Convert 32 bit png to 8 bit png with ImageMagick by preserving semi transparent pixels

26,644

Apparently, even though the PNG format actually allows any and all of the colors in an 8-bit indexed color PNG to be fully or partially transparent, ImageMagick's "PNG8" format specifier only supports GIF-style 1-bit transparency.

It's possible to produce indexed 8-bit PNGs using ImageMagick by not using the PNG8: specifier, but simply using -colors 256 or -colors 255* to reduce the number of colors in the image. Unfortunately, at least based on my tests using ImageMagick 6.8.9, the resulting images have some rather weird and quite unnecessarily ugly color quantization artifacts.

Fortunately, there's a much better tool for this specific job: pngquant. Using it, converting a 32-bit RGBA PNG into an 8-bit colormapped PNG with minimal quality loss is as easy as:

pngquant 256 < original.png > output.png

As a quick demonstration, here's a simple test picture (a star with a semitransparent drop shadow) converted to 8-bit PNG using various methods:

Original pngquant 256 ImageMagick convert (-colors 255) ImageMagick convert (PNG8) ImageMagick convert (-colors 255, PNG8)

From left to right:

  1. Original 32-bit RGBA PNG
  2. Quantized with pngquant 256 < input.png > output.png
  3. Quantized with convert input.png -colors 255 output.png*
  4. Quantized with convert input.png PNG8:output.png
  5. Quantized with convert input.png -colors 255 PNG8:output.png*

*) In the comments below, it is suggested that -colors 255 is necessary "to reserve one entry for the 'background' color." Based on my testing, I have not observed this to actually be the case; using -colors 256 will still produce an 8-bit colormapped PNG, with quantization artifacts qualitatively similar to, but differing in details from, the output with -colors 255. Nonetheless, just to play it safe, I've used -colors 255 for the examples above. Reducing the colormap size by one color should not, in itself, significantly affect the quality of the results, as a test with pngquant 255 will demonstrate.

Share:
26,644
Karmar
Author by

Karmar

Updated on June 20, 2020

Comments

  • Karmar
    Karmar about 4 years

    I want to convert 32 bit png to 8 bit png with ImageMagick, but semi transparent pixels are lost. How to solve this problem? The command that I am using is the following

    convert original.png PNG8:output.png
    
  • Karmar
    Karmar over 11 years
    Thank you for the answer. I tried that command, but it doesn't preserve semi transparent pixels.
  • Ilmari Karonen
    Ilmari Karonen over 11 years
    Oh well, it was worth a try. :( (Ps. You did try both of them, right? Someone in the thread I linked to suggested that it's specifically the PNG8: prefix that's breaking the alpha transparency.)
  • Karmar
    Karmar over 11 years
    Yes, I tried both. Second one makes fully transparent pixels black. I started using pngquant command line tool instead of ImageMagick. It preserve alpha channel correctly.
  • Glenn Randers-Pehrson
    Glenn Randers-Pehrson about 7 years
    Try -colors 255 instead of -colors 256. ImageMagick needs to reserve one entry for the "background" color.
  • Ilmari Karonen
    Ilmari Karonen about 7 years
    @GlennRanders-Pehrson: It shouldn't need to do that, since the PNG8 format allows any and all of the 256 colors to be semitransparent. But apparently ImageMagick doesn't support that, and can only output GIF-style 1-bit transparency in colormapped images. Anyway, thanks for prompting me to update this old answer. :)
  • Glenn Randers-Pehrson
    Glenn Randers-Pehrson about 7 years
    "PNG8" wasn't defined by the PNG group. Others have defined it to either mean indexed PNG, or indexed PNG with binary transparency. ImageMagick uses the latter definition.
  • Glenn Randers-Pehrson
    Glenn Randers-Pehrson about 7 years
    ImageMagick does support 8-bit indexed PNGs with semitransparency, but it doesn't call them "PNG8". But whether PNG8 supports semitransparent colors is irrelevant anyhow; the requirement to leave a slot for the background color still holds. Try `convert in.png -colors 255 output.png"
  • Ilmari Karonen
    Ilmari Karonen about 7 years
    @GlennRanders-Pehrson: Huh, looks like you're right. The results still look pretty awful, but it does produce an 8-bit PNG with partial transparency.
  • Glenn Randers-Pehrson
    Glenn Randers-Pehrson about 7 years
    Sometimes -colors 256 works; that's when at least one pixel of the background color is present in the main image, and it's therefore not necessary to reserve an extra slot in the palette.
  • Ilmari Karonen
    Ilmari Karonen about 7 years
    @GlennRanders-Pehrson I still don't see why that should be necessary, unless it's some weird quirk of ImageMagick. If the input image has no fully transparent pixels, why would we need to reserve a colormap entry for them in the first place? But practically speaking it doesn't seem to matter, since ImageMagick's output looks just as awful either way. (I suspect that must be a bug in ImageMagick, since there's no reason why it should look that bad, no matter what quantization algorithm they're using.)
  • Glenn Randers-Pehrson
    Glenn Randers-Pehrson about 7 years
    It isn't necessary to reserve a background color, but ImageMagick reserves one by default. You can avoid that with "-define png:exclude-chunk=bKGD". The quantization probably looks better with "-colors 255" and not specifying PNG8: (PNG8 uses a quick-and-dirty quantization method that simply zeroes out the lower bits). But, in fact, I prefer to use pngquant rather than ImageMagick for palette-generation.
  • Ilmari Karonen
    Ilmari Karonen about 7 years
    @GlennRanders-Pehrson OK, I guess that makes sense. As to whether convert -colors 255 looks better with or without PNG8:, well, you can look at the examples above and decide for yourself. To me, they both look pretty bad.