How to preserve red color only using ImageMagick command line

Viewed 335

I have the following image:

enter image description here

What I want is to preserve only red color and desaturate every other color into grayscale. Resulting in this:

enter image description here

How can I do that with Imagemagick command line?

I tried this but failed:

convert original.png \( -clone 0  -transparent red -alpha extract -transparent black \) redonly.png
2 Answers

Here is one way, though not perfect, using ImageMagick. I specify hue=0 deg for red and tolerance=25 deg both in range 0 to 360.

Input:

enter image description here

hue=0
tolerance=25
toler=`convert xc: -format "%[fx:(100*$tolerance/360)]" info:`
hueval=`convert xc: -format "%[fx:50-$hue]" info:`
thresh=`convert xc: -format "%[fx:100-$tolerance]" info:`
convert tomato.jpg \
\( -clone 0 -colorspace gray -colorspace sRGB \) \
\( -clone 0 -colorspace HSL -channel 0 -separate +channel \
-evaluate AddModulus ${hueval}% \
-solarize 50% -level 0x50% \
-threshold $thresh% \) \
-swap 0,1 -alpha off -compose over -composite \
result.jpg

enter image description here

Also not perfect, but fairly easy to understand. Basically, you could use the fx operator to inspect the Hue of each pixel and, depending on its Hue/colour, return either the original pixel or its greyscale equivalent.

So, as a first stab, you might do this to replace all pixels exhibiting a high Hue value with their greyscale (lightness) equivalent:

magick ripe.jpg -fx "(u.hue<0.1)? u : u.lightness" result.jpg

enter image description here

Then you might realise that red Hues wrap around at 0/360 degrees on the Hue circle, so you could do:

magick ripe.jpg -fx "(u.hue<0.1)||(u.hue>0.9)? u : u.lightness" result.jpg

enter image description here

Explanation

There are a couple of things going on here. Firstly, the -fx operator is a very low-level, extremely powerful (and sadly rather slow because it is interpreted) way of running a piece of "code" on every pixel in the image. Secondly, I am running a ternary operator with the format:

condition? valueA : valueB

so I am testing a condition for every pixel, and if true I return valueA, and if false I return valueB. When I refer to u and u.hue and u.lightness, the u means the first image in my command - I could load two images and use features of the first to select features of the second, then I would use u and v to differentiate. Finally, the values are scaled on the range [0,1] so I don't test for "Hue>350 in the range [0,360]", instead I test for Hue>0.9 as a sloppy equivalent - I guess I could have used Hue>(350/360). Note that you can make the expression arbitrarily complicated and also put it in a separate file to re-use it like this:

magick ripe.jpg -fx @DeSatNonRed.fx  result.jpg

DeSatNonRed.fx might look something like this:

hueMin=350/360;
hueMax=20/360;
(hue>hueMin) || (hue<hueMax) ? u : u.lightness

Note that, in the general case, you should also really consider Saturation when looking at Hues, which you can add in above, but which I omitted for clarity, and because your image is almost fully saturated anyway.

Keywords: Image processing, ImageMagick, low-level fx operator, ternary, pixel-by-pixel, evaluate, expression.

Related