# Filtering pixels of a certain color

What is the best way to filter an image so that only the pixels close to a certain color are left?

I've downloaded some satellite images from the web where a specific pixel color corresponds to a some type of crop. Thus I would like to filter out with Mathematica those pixels that are specifically for that color so I can next run ComponentMeasurements to obtain the area covered by those pixels, so I was trying to figure out what function to use or build that would just return an image with the pixels that match or are truly similar to the color provided.

-
Is your question a general image-processing question or specific to Mathematica? –  rm -rf May 9 '12 at 17:53
See this post, it might give you some ideas where to start. –  István Zachar May 9 '12 at 18:05
What definition of "color" do you want to use? –  belisarius May 9 '12 at 20:41
Here's another implementation (an earlier answer of mine). –  Szabolcs May 11 '12 at 7:23

i = Import["http://i.stack.imgur.com/zzUdB.gif"];

{h, s, b} = ColorSeparate[i, "HSB"];

Manipulate[
Column[
{
Graphics[{Hue[c], Rectangle[]}, ImageSize -> 40],
ImageMultiply[i,
Binarize[Abs[Mod[ImageData[h] - c, 1, -1/2]] // Image,
tolerance] // ColorNegate]
}
],
{c, 0, 1},
{{tolerance, 0.1}, 0, 0.5}
]


-
Very nice. Version 7 users will need: {h, s, b} = ColorSeparate@ColorConvert[i, "HSB"]; –  Mr.Wizard May 9 '12 at 21:47
@Mr.Wizard Good catch! The second argument (the color space) of ColorSeparate is a version 8 addition. –  Sjoerd C. de Vries May 9 '12 at 21:51
@mr.wizard Didn't cross my mind, but makes sense. Done. –  Sjoerd C. de Vries May 9 '12 at 22:07
Note that since Hue is periodic with period 1 you might want to apply a function to the image data of h that reflects this periodicity. For example you could do something like Abs[Mod[ImageData[h] - c, 1, -1/2]]. This way, when filtering on Hue[0] (which is equal to Hue[1]) you'll not only catch the points with hue 0 <= hue < tol but also 1-tol < hue <= 1. –  Heike May 9 '12 at 22:19
+1 Great, but your current code has only 1 slider while images of interfaces have 2. –  Vitaliy Kaurov May 9 '12 at 22:38

I investigated an alternative approach - a simple brute force technique, not as good as Sjoerd's.

satImage = ImageResize[
/a/ac/Bananal-Island-North-End-Landsat-25-07-1992.gif"], 300]


find[pixel_, color_, tolerance_] := If[
And[
Abs[color[[1]] - pixel[[1]]] < tolerance ,
Abs[color[[2]] - pixel[[2]]] < tolerance ,
Abs[color[[3]] - pixel[[3]]] < tolerance ], pixel, {0, 0, 0}];

Manipulate[
im = ImageApply[find[#, col, tolerance] &, satImage],
{col, Blue},
{{tolerance, 0.2}, 0.1, 1, Appearance -> "Labeled"}]


-

An answer to this question also appears in this other answer, albeit somewhat hidden. So I've taken the relevant part of my answer there and put it into this function which extracts the desired color by setting all other colors transparent:

colorExpose[im_, col_, tol_: .005] := Module[
{
color = Apply[List, ColorConvert[col, "RGB"]],
r = If[
ImageColorSpace[im] != "RGB",
ColorConvert[im, "RGB"], im
]
},
SetAlphaChannel[r,
Binarize[r, (Norm[# - color] < tol) &]
]
]


Here are some examples of how the function can be used:

ra = Import["http://i.stack.imgur.com/zzUdB.gif"]
colorExpose[ColorConvert[ra, "CMYK"], Yellow]
colorExpose[ra, Hue[.5]]


These forms show that the colorspace doesn't matter for the supplied image or the color, because the function always converts to RGBColor before doing the comparisons. The tolerance for the color matching is supplied as a third (optional) argument.

-