I'm currently using GPUImage to change the color(RGB) channels of images to produce "filter like" effects. The values I use for GPUImage are the X,Y values of the points of the color curve tool in photoshop. So for example I might have these points:
Photoshop curve tool !please note that the image doesn't represent
the current data points:
Red:
0,0
0.1176,0.0196
0.2275,0.098
0.3294,0.3333
0.4392,0.549
0.7451,0.8627
1,1
Green:
0,0
0.0784,0.0196
0.1961,0.2431
0.5176,0.5882
0.7451,0.8039
1,1
Blue:
0,0.2549
0.1569,0.3529
0.3333,0.451
0.8314,0.7255
1,0.8039
GPUImage handles these points without problems, however since Imagemagick doesn't support direct input of these values I have to convert each channel points to the curve's expression like this:
Red: -237.407*u^6 + 634.277*u^5 - 615.934*u^4 + 261.501*u^3 - 44.054*u^2 + 2.617*u
Green: -32.880*u^5 + 80.544*u^4 - 68.222*u^3 + 22.707*u^2 - 1.148*u
Blue: -0.382*u^4 + 0.744*u^3 - 0.498*u^2 + 0.686*u + 0.255
I am using the im_fx_curves.pl script found here to convert the points to the expression.
So my process would normally look like this:
convert test_image.png \
\( -channel Red -fx "`perl im_fx_curves.pl 0,0 0.1176,0.0196 0.2275,0.098 0.3294,0.3333 0.4392,0.549 0.7451,0.8627 1,1`" \) \
\( -channel Green -fx "`perl im_fx_curves.pl 0,0 0.0784,0.0196 0.1961,0.2431 0.5176,0.5882 0.7451,0.8039 1,1`" \) \
\( -channel Blue -fx "`perl im_fx_curves.pl 0,0.2549 0.1569,0.3529 0.3333,0.451 0.8314,0.7255 1,0.8039`" \) result.png
However the results are slightly different. And I'm confused on why this is happening. I would gladly take any tips on what I might be doing wrong!
Related
I would like to create a hotfolder for my motion camera, into which I can drop images marking areas, which should be excluded in motion recognition via a *pgm mask. On these images, there is a small area marked with a transparent box with a magenta colored outline. My aim is to replace this box and outline with a black solid box and the rest of the image with white. (Tried to post samples here, but not enough reputation to do so.)
I know how to do this "by foot" using gimp, but I cannot figure out a clever and simple way achieving this with imagemagick.
I tried googling for solutions with -trim and -virtual-pixel, but no luck. Any help would be appreciated.
I'll do this step-by-step so you can see the intermediate parts in case you are on Windows and bash doesn't work.
First, let's make make everything that is not within 10% of your magenta colour, namely rgb(225,75,130), into lime green:
magick source.jpg -fill lime -fuzz 10% +opaque "rgb(225,75,130)" result.png
Ok, now let's get the trim box - i.e. all the constant junk that ImageMagick could trim off to focus on the magenta bit.
magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info:
14x66+426+118
So your magenta box is 14x66pixels and located at offset 426,118 from the top-left. Now we want to get those in bash variables w,h,x,y. We need to change x and + into spaces using tr:
read w h x y < <(magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info: | tr 'x+' ' ')
If we print this we get:
echo $w, $h, $x, $y
14, 66, 426, 118
Now we want to draw a rectangle, but that needs top-left and bottom-right, so we need to do some maths:
((x1=x+w))
((y1=y+h))
Ok, now we can load the original image, make it fully white, then draw our black rectangle:
magick source.jpg -threshold -1 -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm
So, the whole thing boils down to:
#!/bin/bash
read w h x y < <(magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info: | tr 'x+' ' ')
echo $w, $h, $x, $y
((x1=x+w))
((y1=y+h))
magick source.jpg -threshold -1 -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm
There are other (maybe more elegant) ways of doing it, using flood-fills and/or connected components but I didn't want it to rely on your magenta box being "watertight", i.e. not rely on the sides being continuous and complete.
Also, if the size of your images is known and constant, you can avoid reloading the original and making it white by thresholding like I do in the last line and just create a canvas of the known dimensions, i.e.:
magick -size ${W}x${H} xc:white -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm
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 an image created in a Affinity Designer, which has the following channels:
R, G and B:
Alpha:
When I use this image in other places (Unreal Engine), I get small black artifacts on the edge of the shape due to the black part of the RGB channels. The only important information I need is the alpha channel.
How can I use ImageMagick to replace the RGB channels with white, while retaining the alpha channel?
Here is a simple way to do the in ImageMagick:
First, since you did not provide the actual image, I will reconstruct it from the rgb.png and alpha.png
convert rgb.png alpha.png -alpha off -compose copy_opacity -composite image.png
Now, to make the rgb part white, simply do:
convert image.png -fill white -colorize 100 result.png
Now to see that the rgb parts are fully white, we do:
identify -verbose result.png
.
.
.
Channel statistics:
Pixels: 512
Gray:
min: 255 (1)
max: 255 (1)
mean: 255 (1)
standard deviation: 0 (0)
kurtosis: -4.9152e+52
skewness: 9.375e+35
entropy: 0
Alpha:
min: 0 (0)
max: 255 (1)
mean: 187.152 (0.733931)
standard deviation: 109.252 (0.42844)
kurtosis: -0.822401
skewness: -1.05578
entropy: 0.336307
So the rgb components are a constant 255 (white) and the image is now white with an alpha component.
Try the following...
magick \( input.png -fill white -draw 'color 0,0 reset' \) \
\( input.png -alpha extract \) \
-compose Copy_Alpha -composite \
output.png
I'm sure there's better ways to do the above. Reading the input.png twice, and extracting the alpha channel is bit redundant.
The first part replaces all RGB channels with white, but also clears the alpha channel.
input.png -fill white -draw 'color 0,0 reset'
The second part grabs the alpha channel.
input.png -alpha extract
Finally, we copy the extracted alpha values back over the white image's alpha channel.
-compose Copy_Alpha -composite
If your using ImageMagick-6, then replace magick with convert, and Copy_Alpha with Copy_Opacity.
Best of Luck!
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 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