1
votes

I have a 32-bit PNG image like this:

32-bit

Displayed here with a checkerboard background for visibility, but instead of the checkerboard the image is actually transparent (for your reference: the original 32-bit image). As you can see, around the edges and towards the right, the red pixels are gradually fading out from opaque to transparent.

If I would display this 32-bit image on top of a blue background, the pixels around the edges and towards the right would gradually fade from red to blue.

Now I need to convert this image to an 8-bit PNG with binary alpha. I want the fully transparent area to remain fully transparent, but the partially transparent pixels to gradually blend with the intended background/matte color, in this example blue.

So something like this:

8-bit blended with background color

However, I can't seem to figure out how to do this with ImageMagick (using ImageMagick 7.0.8-26 on Mac). I tried as follows:

magick original.png -background 'rgba(0,0,255,0)' png8:output.png

But this results in:

8-bit with background color ignored

It seems to ignore the blue background color altogether, and just convert the transparency to binary (probably taking fully opaque for ≥128 or fully transparent for <128).

Also if I specifiy rgb(0,0,255) as background color (i.e. no alpha) the result is the same.

Does ImageMagick have some smart 'background blending for partially transparent pixels' option that I don't know about?

Otherwise, I'm guessing I should somehow extract a binary bitmap where the original image is fully transparent (i.e. has alpha value or opacity 0), then flatten the image on a background of my choosing, and then re-apply the boolean alpha bitmap again. But not quite sure how to do this.

4

4 Answers

1
votes

Here is one more way in ImageMagick.

enter image description here

convert img.png \
\( -clone 0 -alpha extract -channel rgba \
    \( xc:none xc:blue +append \) -clut +channel \
        -channel alpha -threshold 0 +channel \) \
+swap -compose over -composite PNG8:result.png


OR FOR Imagemgick 7

magick img.png \
\( -clone 0 -alpha extract -alpha copy -channel rgba \
\( xc:none xc:blue +append \) -clut \
-channel alpha -threshold 0 +channel \) \
+swap -compose over -composite PNG8:result.png

enter image description here

Read the image.

Clone it, extract the alpha channel, create transparent blue color map and apply to the alpha channel, then threshold the alpha channel.

Swap the two images and composite the original over the processed alpha channel.

Save the output.
2
votes

I can think of two techniques, but I'm sure folks have better answers.

First Option

Composite the blue background, and then copy the alpha channel from the original source.

magick original.png \( \
  +clone \( +clone -fill BLUE -draw 'color 0,0 reset' \) \
  -compose DstOver -composite \) \
  -swap 0,1 -compose CopyAlpha -composite  PNG8:output.png

output

Second Option

Replace transparent with blue color, but then call -transparent without any -fuzz operation.

magick original.png \( \
  +clone -fill BLUE -draw 'color 0,0 reset' \) \
  -compose DstOver -composite -transparent BLUE \
  PNG8:output.png

output

Again, I'm sure the above examples can be improved on. YMMV

2
votes

One way to do that with ImageMagick is to extract the alpha channel, colorize essentially everything except the totally transparent pixels, and composite the original image over the result.

convert input.png \( +clone -alpha extract -transparent black -fill blue \
   -channel RGB -colorize 100 +channel \) +insert -composite output.png

That reads the input image, clones it inside the parentheses, and extracts the alpha channel to a black and white mask.

The pure black is made transparent with "-transparent black".

The white, everything except pure black, is changed to blue with "-colorize 100". Setting "-channel RGB" applies that fill color to only the white part of the mask and ignores the transparent.

Then after the parentheses the "+insert" moves that blue-on-transparent image to the back, and "-composite" places the input image over that blue and transparent one.

If you're using ImageMagick version 7, use "magick" instead of "convert". If you're running on Windows, change the end of line backslash "\" to a caret "^", and remove the backslashes that escape the parentheses "\(...\)" to simply "(...)".

0
votes

Thanks to the posted suggestions, however the problem is I don't want to mask any particular color (like black or blue) as transparent. That exact same color may also appear in the non-transparent parts.

After some tweaking, I got the exact result I need:

magick original.png -write MPR:foo -background 'rgb(0,0,255)' -flatten \( MPR:foo -alpha extract -threshold 0 \) -compose copy_opacity -composite png8:result.png

Explanation:

  • I start with original.png and use -write MPR:foo to save a temporary copy in memory called 'foo'
  • Then I flatten the image on a blue background, giving the correct color values everywhere (including blended with the background for partially transparent pixels) but losing the opacity data.
  • Then I side-process a 2nd layer, opening the same original input image (restoring from memory using MPR:foo) and extract its alpha channel which now becomes a grayscale image, containing the original alpha data.
  • I apply -threshold 0 on that layer, which changes all channel values that were >0 to the maximum (i.e. alpha 255 or fully opaque) and ≤0 remains 0 (fully transparent). So this is now my 'binary alpha' channel.
  • Then finally I merge these two layers by using a copy_opacity composite which just copies the 2nd layer (the binary alpha) and writes that as the resulting layer's alpha data.
  • Saving the end result gives me the exact image I need.