Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert an RGB image to grayscale but keep one color?

I am trying to create an effect similar to Sin City or other movies where they remove all colors except one from an image.

I have an RGB image which I want to convert to grayscale but I want to keep one color.

This is my picture:

alt text

I want to keep the red color. The rest should be grayscale.

This is what my code outputs so far (you can see that the areas are correct, I don't know why they are white instead of red though):

alt text

Here is my code so far:

filename = 'roses.jpg';  [cdata,map] = imread( filename ); % convert to RGB if it is indexed image if ~isempty( map )     cdata = idx2rgb( cdata, map );  end  %imtool('roses.jpg');  imWidth = 685; imHeight = 428;  % RGB ranges of a color we want to keep redRange = [140 255]; greenRange = [0 40]; blueRange = [0 40];  % RGB values we don't want to convert to grayscale redToKeep = zeros(imHeight, imWidth); greenToKeep = zeros(imHeight, imWidth); blueToKeep = zeros(imHeight, imWidth);  for x=1:imWidth      for y=1:imHeight          red = cdata( y, x, 1 );         green = cdata( y, x, 2 );         blue = cdata( y, x, 3 );          if (red >= redRange(1) && red <= redRange(2) && green >= greenRange(1) && green <= greenRange(2) && blue >= blueRange(1) && blue <= blueRange(2))             redToKeep( y, x ) = red;             greenToKeep( y, x ) = green;             blueToKeep( y, x ) = blue;         else             redToKeep( y, x ) = 999;             greenToKeep( y, x ) = 999;             blueToKeep( y, x ) = 999;         end      end   end   im = rgb2gray(cdata); [X, map] = gray2ind(im); im = ind2rgb(X, map);  for x=1:imWidth      for y=1:imHeight          if (redToKeep( y, x ) < 999)             im( y, x, 1 ) = 240;         end         if (greenToKeep( y, x ) < 999)             im( y, x, 2 ) = greenToKeep( y, x );         end         if (blueToKeep( y, x ) < 999)             im( y, x, 3 ) = blueToKeep( y, x );         end      end   end   imshow(im); 
like image 798
Richard Knop Avatar asked Oct 31 '10 16:10

Richard Knop


People also ask

How an RGB image can be converted to GREY?

You just have to take the average of three colors. Since its an RGB image, so it means that you have add r with g with b and then divide it by 3 to get your desired grayscale image. Its done in this way.

Can convert images from RGB mode directly to grayscale?

I = rgb2gray( RGB ) converts the truecolor image RGB to the grayscale image I . The rgb2gray function converts RGB images to grayscale by eliminating the hue and saturation information while retaining the luminance. If you have Parallel Computing Toolbox™ installed, rgb2gray can perform this conversion on a GPU.

Do grayscale images have Colour?

A grayscale (or graylevel) image is simply one in which the only colors are shades of gray. The reason for differentiating such images from any other sort of color image is that less information needs to be provided for each pixel.


2 Answers

One option which greatly improves the quality of the resulting image is to convert to a different color space in order to more easily select your colors. In particular, the HSV color space defines pixel colors in terms of their hue (the color), saturation (the amount of color), and value (the brightness of the color).

For example, you can convert your RGB image to HSV space using the function rgb2hsv, find pixels with hues that span what you want to define as "non-red" colors (like, say, 20 degrees to 340 degrees), set the saturation for those pixels to 0 (so they are grayscale), then convert the image back to RGB space using the function hsv2rgb:

cdata = imread('EcyOd.jpg');       % Load image hsvImage = rgb2hsv(cdata);         % Convert the image to HSV space hPlane = 360.*hsvImage(:, :, 1);   % Get the hue plane scaled from 0 to 360 sPlane = hsvImage(:, :, 2);        % Get the saturation plane nonRedIndex = (hPlane > 20) & ...  % Select "non-red" pixels               (hPlane < 340); sPlane(nonRedIndex) = 0;           % Set the selected pixel saturations to 0 hsvImage(:, :, 2) = sPlane;        % Update the saturation plane rgbImage = hsv2rgb(hsvImage);      % Convert the image back to RGB space 

And here is the resulting image:

alt text

Notice how, compared to the solution from zellus, you can easily maintain the light pink tones on the flowers. Notice also that brownish tones on the stem and ground are gone as well.

For a cool example of selecting objects from an image based on their color properties, you can check out Steve Eddins blog post The Two Amigos which describes a solution from Brett Shoelson at the MathWorks for extracting one "amigo" from an image.


A note on selecting color ranges...

One additional thing you can do which can help you select ranges of colors is to look at a histogram of the hues (i.e. hPlane from above) present in the pixels of your HSV image. Here's an example that uses the functions histc (or the recommended histcounts, if available) and bar:

binEdges = 0:360;    % Edges of histogram bins hFigure = figure();  % New figure  % Bin pixel hues and plot histogram: if verLessThan('matlab', '8.4')   N = histc(hPlane(:), binEdges);  % Use histc in older versions   hBar = bar(binEdges(1:end-1), N(1:end-1), 'histc'); else   N = histcounts(hPlane(:), binEdges);   hBar = bar(binEdges(1:end-1), N, 'histc'); end  set(hBar, 'CData', 1:360, ...            % Change the color of the bars using           'CDataMapping', 'direct', ...  %   indexed color mapping (360 colors)           'EdgeColor', 'none');          %   and remove edge coloring colormap(hsv(360));                      % Change to an HSV color map with 360 points axis([0 360 0 max(N)]);                  % Change the axes limits set(gca, 'Color', 'k');                  % Change the axes background color set(hFigure, 'Pos', [50 400 560 200]);   % Change the figure size xlabel('HSV hue (in degrees)');          % Add an x label ylabel('Bin counts');                    % Add a y label 

And here's the resulting pixel color histogram:

alt text

Notice how the original image contains mostly red, green, and yellow colored pixels (with a few orange ones). There are almost no cyan, blue, indigo, or magenta colored pixels. Notice also that the ranges I selected above (20 to 340 degrees) do a good job of excluding most everything that isn't a part of the two large red clusters at either end.

like image 72
gnovice Avatar answered Oct 11 '22 15:10

gnovice


figure pic = imread('EcyOd.jpg');  for mm = 1:size(pic,1)     for nn = 1:size(pic,2)         if pic(mm,nn,1) < 80 || pic(mm,nn,2) > 80 || pic(mm,nn,3) > 100             gsc = 0.3*pic(mm,nn,1) + 0.59*pic(mm,nn,2) + 0.11*pic(mm,nn,3);             pic(mm,nn,:) = [gsc gsc gsc];         end     end end imshow(pic) 

alt text

like image 38
zellus Avatar answered Oct 11 '22 15:10

zellus