views:

197

answers:

4

So 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);
+2  A: 

I don't really know how matlab works so I can't really comment on the code, but maybe this will help explain a bit how RGB colors work.

When using RGB colors a gray scale can be made by making sure the values for R,G and B are all the same. So basically what you want to do is detect if a pixel is red, when not just make R,G and B the same (you can use an average of the 3 for a rudimentary result).

Harder part is how to detect if a pixel is actually red, you can't just check if a pixel is high in the R value since it can still be another color, and a low R value can just mean a darker red.

so you could do something like this: (I don't have matlab, so assuming syntax):

red = cdata( y, x, 1 );
green = cdata( y, x, 2 );
blue = cdata(y, x, 3);

if (red < (blue * 1.4) || red < (green * 1.4) )
{
    avg = (red + green + blue) / 3;
    cdata(y, x, 1) = avg;
    cdata(y, x, 2) = avg;
    cdata(y, x, 3) = avg;
}

There are probably better ways to detect red and to get an average gray, but it's a start ;)

Doggett
Thanks. I have changed my code a bit and I already get some output but the areas that should be red are white instead. Check my updated question.
Richard Knop
Your colors are white because you removed the original values for green and blue for the pixels you want to keep. That's why in the example it only modifies the matrix for pixels you want to make gray and just leaves the rest alone.
Doggett
@Doggett: Kind of implemented your algorithm without noticing your post. I hope you don't mind.
zellus
@Zellus Not at all, I see my total lack of knowledge of matlab made my example a bit longer than necessary anyhow ;)
Doggett
+12  A: 

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:

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.

gnovice
Thanks. I will try that. Meanwhile, I have updated my question. Could you check it out? :)
Richard Knop
+3  A: 
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

zellus
Thanks, that's a lot easier way to do it. How did you get those coefficients (0.3, 0.59, 0.11) though? I don't understand that.
Richard Knop
@Richard Knop: That's the formula used by [RGB2GRAY](http://www.mathworks.com/help/toolbox/images/ref/rgb2gray.html), as listed in the documentation.
gnovice
@ Richard Knop: http://www.mathworks.com/help/toolbox/images/ref/rgb2gray.html scroll a bit down until you reach paragraph *'Algorithm'*. But there are others on the web as well.
zellus
Thanks very much.
Richard Knop