littleworth
littleworth

Reputation: 5169

How to preserve red color only using ImageMagick command line

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

Upvotes: 3

Views: 664

Answers (2)

Mark Setchell
Mark Setchell

Reputation: 207345

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.

Upvotes: 2

fmw42
fmw42

Reputation: 53089

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

Upvotes: 6

Related Questions