I had been searching for good algorithm for green screen chroma key using ImageMagick, but no satisfactory answer so far.
I would like to explore a simple method of using range selection along the HSV/HSB color space (similar to -fuzz) to select the green color I want and make them transparent. The -fuzz seems to apply only in RGB space, which is not desired.
Can anyone teach me how to select color with seperate range for H, S, V component, for example, 115-125 degree Hue, 40%-60% Saturation and 30-80% Value.
If there is other better chroma-key algorithm, your advice is highly appreciated too.
Thanks in advance.
Imagemagick's FX can be used to generate a alpha channel. The hue, saturation, lightness, & luma keywords exists, but you'll need to calculate the color value by max(r, g, b).
hueMin=115/360;
hueMax=125/360;
saturationMin=0.40;
saturationMax=0.60;
valueMin=0.30;
valueMax=0.80;
value = max( r, max( g, b ) );
(
( hue > hueMin && hue < hueMax ) && (
( saturation > saturationMin && saturation < saturationMax ) ||
( value > valueMin && value < valueMax ))) ? 0.0 : 1.0
Saving the above into a file named hsl-greenscreen.fx and execute it against an image with:
convert source.png -channel alpha -fx #hsl-greenscreen.fx out.png
The FX script will probably need additional tweaking to match expected results. You'll also notice this will take a bit of CPU to complete, but that can be improved on.
Another option would be to apply the same -fuzz options, but on each HSV channel. Simply split & clone each channel, apply -fuzz against a target grey, and compose an image mask.
convert source.png -colorspace HSV -separate +channel \
\( -clone 0 -background none -fuzz 5% +transparent grey32 \) \
\( -clone 1 -background none -fuzz 10% -transparent grey50 \) \
\( -clone 2 -background none -fuzz 20% -transparent grey60 \) \
-delete 0,1,2 -alpha extract -compose Multiply -composite \
-negate mask.png
Then assign the mask as the images alpha channel
convert source.png mask.png -alpha Off -compose CopyOpacity -composite out.png
Related
I want to convert to white any light shades of grey. I am using '-fuzz 10% -fill white -opaque #efeaee'. The problem is that the fuzz converts other colours other than grey, not just shades of grey (ie light yellow and light green get converted). I have tried to reduce the fuzz and repeat for various versions of grey but it really doesn't work and I'm sure there's a better way?
You can do that in ImageMagick, by converting to HSV colorspace. Then threshold the S and V channels. You want low saturation and high value combined as a mask. Then blend the input and a white image using the mask.
sthresh=10
vthresh=10
convert zelda1.jpg \
\( -clone 0 -fill white -colorize 100 \) \
\( -clone 0 -colorspace HSV -channel 1 -separate +channel -threshold $sthresh% -negate \) \
\( -clone 0 -colorspace HSV -channel 2 -separate +channel -negate -threshold $vthresh% -negate \) \
\( -clone 2,3 -compose multiply -composite -blur 0x1 \) \
-delete 2,3 \
-compose over -composite \
result.png
sthresh is the fuzz value for how close in color it is to white
vthresh is the fuzz value for how close it is in brightness to white
Values of zero mean exactly pure white.
I have the following image:
What I want is to preserve only red color and desaturate
every other color into grayscale. Resulting in this:
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
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:
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
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
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
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.
I have a GIF image generated by a program where each output value is represented by its color via attached color palette. I need to replace one value, i.e. color from image with interpolated from neighboring pixels. Since I don't have possibility to alter programs output, I need to modify the output image. The resulting image will be saved in the PNG or GIF format.
I can easily extract (mask) all pixels that need repainting, since they have fixed color, but I was unable to find solution on how to replace a color of one/all pixels in imagemagick with interpolated color from neighboring pixels.
Is there a way to do this in imagemagick?
The raw values of the pixels are proportional to the physical value, so it would be great if the interpolation could be done on raw values that are then later transformed to the color via supplied color palette.
Attached image shows the original (left) and processed manually in GIMP (right).
One technique is to replace the offending color with the background, and then use a combination of erode & dilate morphology to remove the paths.
Given...
convert input.png \
-fill white -fuzz 10% -opaque black \
-morphology Erode Diamond \
-morphology Dilate Diamond \
output.png
It's not a true interpolate from nearest neighbors, but close. Also note the rounding errors across edges.
Updated
Or as Fred pointed out in the comments, just use -morphology Smooth Diamond instead of Erode + Dilate
convert input.png \
-fill white -fuzz 10% -opaque black \
-morphology Smooth Diamond \
output.png
Adding a bit to xenoid's suggestion, you want to create a mask image and use that to composite the median filter with the original, so that only the region about the line is changed. Using emcconville's image and ImageMagick:
convert img.png \
\( -clone 0 -statistic median 3x3 \) \
\( -clone 0 -fuzz 10% -fill white +opaque black -fill black +opaque white -negate \) \
-compose over -composite \
result.png
An alternate, but slightly longer approach, is to put the mask into the alpha channel of the filtered image and then composite it over the original, which produces exactly the same result:
convert img.png \
\( -clone 0 -statistic median 3x3 \) \
\( -clone 0 -fuzz 10% -fill white +opaque black -fill black +opaque white -negate \) \
\( -clone 1 -clone 2 -alpha off -compose copy_opacity -composite \) \
-delete 1,2 \
-compose over -composite \
result.png
Unfortunately, there is a slight dark residual to the upper left between the red and green. I tried increasing both the fuzz value and the filter size, but that did not seem to help. I am not sure why.
I want to change all colors #FF00FF in an image to #0000FF while keeping shades, ideally. So I figured I should at least get it to change colors to begin with to see if the software is even capable of doing things like that.
However its only changing a bit of the color to white and only with a high Fuzz. So it's obvious that RGB in Imagemagick doesn't work like it does anywhere else and I can't find anything to explain how it works.
It seems to replace some off-white with pure white.
Using PHP I do:
exec("convert ".$dir."".$file." -channel RGB -fuzz 30% -opaque rgb\(255,0,255\) -fill rgb\(0,0,255\) ".$dir."".$file);
I am not 100% certain what you mean as you haven't provided a sample of what other software does, but I'll have a try and see if we can get there.
So, if we make a starting image, including your presumed shades of magenta on the left and some test colours on the right:
convert -size 256x256 gradient:black-magenta -size 50x256 \
xc:black xc:white xc:red xc:lime xc:blue +append start.png
And, you want to change magenta shades into blue. I would call that a hue modulation, so I would want to find out the hue angle between blue and magenta, so I would create a 2x1 image with one magenta and one blue pixel and get their HSI values:
convert xc:magenta xc:blue -append -colorspace hsi txt:
Output
# ImageMagick pixel enumeration: 1,2,65535,hsi
0,0: (54612.5,65535,43690) #D555FFFFAAAA hsi(300,100%,66.6667%)
0,1: (43690,65535,21845) #AAAAFFFF5555 hsi(240,100%,33.3333%)
And I can see their hues are 60 degrees apart (300-240). So I would use the -modulate operator, which takes a Brightness, Saturation and Hue, leave the first two unchanged at 100%, and modify the Hue by 60 degrees:
convert start.png -modulate 100,100,60 result.png
Or maybe that is not what you mean? Maybe you only mean to affect specific colour. If so, it gets harder... but not that hard :-)
First, extract the Hue, Saturation and Brightness layers to separate files:
convert start.png -colorspace HSL -separate -colorspace gray HSL-%d.png
That will give us the Hue as a single channel greyscale image in HSL-0.png, the Saturation in HSL-1.png and the Lightness in HSL-2.png.
Now we want to make a new LUT (Lookup Table) for the Hue channel, so we make a 360 pixel long LUT that maps 1:1, i.e. everything maps to normal.
convert -size 1x360 gradient: -rotate 90 greyscale.png
Then we want to dink with the lookups around magenta (300) and make them blue (240). So we want to subtract 60 degrees (which is 0.16 if you scale 0-360 degrees onto the range 0-1) from all pixels in the range 280-320 so there is some tolerance:
convert -size 1x360 gradient: -rotate 90 -colorspace gray -fx "i<280||i>320?u:u-0.16" hueCLUT.png
Now apply that LUT to the Hue of the original image and rebuild it...
convert HSL-0.png -colorspace gray hueCLUT.png -clut HSL-1.png HSL-2.png -set colorspace HSL -combine -colorspace RGB result.png
So, as a simpler script, that might become:
#!/bin/bash
# Make a hue CLUT, transforming magenta hues to blue
convert -size 1x360 gradient: -rotate 90 -colorspace gray -fx "i<295||i>305?u:u-0.16" -resize 256x1! hueclut.png
# Apply to the hue channel
convert start.png -colorspace HSL -write MPR:HSL \
-channel R -separate hueclut.png -clut \
\( MPR:HSL -channel G -separate \) \
\( MPR:HSL -channel B -separate \) \
-set colorspace HSL -combine -colorspace RGB result.png
I would like to convert a set of color images to grayscale. I use ImageMagick:
convert colorimage.png -colorspace Gray bwimage.png
But for each image there are some features that vanish or become imperceptible after the conversion. For example, with an image of two colors:
convert -size 50x100 xc:lightgray xc:pink +append lgraypink.png
that is then converted to grayscale:
convert lgraypink.png -colorspace Gray lgraypink-gray.png
it becomes difficult to see the distinction.
I am looking for a color-to-B&W conversion that produces, say, three images corresponding to different mappings. The three images would then be offered to the user and they would be asked to choose one. How can I tweak the mapping to generate different B&W images?
You could "ring the changes" on the proportions of the various R,G and B components like this:
convert image.png -channel R -evaluate multiply .2 \
-channel G -evaluate multiply .5 \
-channel B -evaluate multiply .3 \
+channel -separate -compose add -flatten gray_253.png
convert image.png -channel R -evaluate multiply .3 \
-channel G -evaluate multiply .2 \
-channel B -evaluate multiply .5 \
+channel -separate -compose add -flatten gray_325.png
convert image.png -channel R -evaluate multiply .5 \
-channel G -evaluate multiply .3 \
-channel B -evaluate multiply .2 \
+channel -separate -compose add -flatten gray_532.png
So the first one creates a greyscale made of 20% of the Red, 50% of the Green and 30% of the Blue etc...
Another idea, if you are interested in differentiating colours (i.e. hues), may be to convert to HSL colorspace and discard the Saturation and Lightness, leaving just the Hue and then contrast-stretch that to the full range of black to white tones:
convert image.png -colorspace hsl -separate -delete 1,2 -auto-level result.png