ImageMagick: Replace RGB channels with white, retain Alpha - image-processing

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!

Related

ImageMagick convert grey shading only fuzz

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.

Replace/repaint all pixels with one color in image with interpolated from neighboring pixels

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.

Imagemagick: How does RGB work with this software?

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

How to select all grayscale colors?

In ImageMagick convert, I can select a specific color with e.g. -opaque blue. How can I select all grayscale colors (e.g. #000000, #707070, #ffffff)?
Not sure what you are trying to do, but this may help. The greyscale pixels will have a saturation of zero, so that is probably the easiest way to identify them.
First, make a funky sample image:
convert -size 400x100 gradient:black-white -bordercolor red -border 80 image.png
Now make all grey areas (those with very low saturation) transparent:
convert image.png -alpha on -channel A -fx "saturation<0.01?0:1" result.png
Note
Note that the -fx operator is extremely powerful but notoriously slow because it is actually interpolated for each and every pixel. If your images are large, the following technique may be more appropriate.
Basically, I clone the image and convert the whole thing to HSL colorspace and separate the channels. Then I discard the Hue and Lightness channels so I am left with just the Saturation. I then threshold that and copy that back to the original image as the alpha channel. On a 2000x2000 pixel image, this method will run in under a second whereas the -fx method will require 5-6 seconds.
convert image.png \( +clone -colorspace hsl -separate -delete 0,2 -threshold 1% \) -compose copy-opacity -composite result.png

How to output white png version of my image ?**

With ImageMagick shell command (convert?)...
Given a colorful input.png image.
How to us input.png to produce a white output.jpg version with similar dimensions an opacity of 100% ?
I will later on use this layer in my workflow.
This should work:
convert input.png -threshold -1 output.jpg
This transforms any pixel with an intensity greater than (-1), i.e., all of them, to white.
It does not work with GraphicsMagick, though, because in GM the threshold value is unsigned (in ImageMagick it's a signed "double"). Neither of the applications documents exactly what is supposed to happen when the threshold is negative.
Here's a command that works on both ImageMagick and GraphicsMagick, and is documented:
[gm] convert input.png -fuzz 100% -fill white -opaque gray output.jpg
You could use fill, like this:
convert input.png -fill "#ffffff" output.jpg
or
convert input.png -fill white output.jpg
Or you can convert all three channels (red, green and blue) to "1" which is full intensity, like this:
convert input.png -channel red -fx "1" -channel green -fx "1" -channel blue -fx "1" output.jpg

Resources