#40 OptiPNG refuses to compress small images

v1.0 (example)

I noticed that OptiPNG does not seem to work as expected with small files. There seems to be some logic in place, that is the image reduction is very small in bytes, then OptiPNG just outputs that the image is already optimized and will not optimize it further:

** Processing: mapmarker_red.png
18x30 pixels, 4x8 bits/pixel, RGB+alpha
Reducing image to 8 bits/pixel, 137 colors (98 transparent) in palette
Input IDAT size = 603 bytes
Input file size = 758 bytes

zc = 9 zm = 9 zs = 0 f = 0 IDAT size = 274
zc = 9 zm = 8 zs = 0 f = 0 IDAT size = 274
zc = 8 zm = 9 zs = 0 f = 0 IDAT size = 274
zc = 8 zm = 8 zs = 0 f = 0 IDAT size = 274
zc = 7 zm = 9 zs = 0 f = 0 IDAT size = 274
zc = 7 zm = 8 zs = 0 f = 0 IDAT size = 274
zc = 6 zm = 9 zs = 0 f = 0 IDAT size = 274
zc = 6 zm = 8 zs = 0 f = 0 IDAT size = 274
zc = 9 zm = 9 zs = 3 f = 0 IDAT size = 257
zc = 9 zm = 8 zs = 3 f = 0 IDAT size = 257

mapmarker_red.png is already optimized.

However other tools like pngcrush happily optimize the image with 10-15%.

1 Attachments


  • Ian

    This is a known design flaw, currently documented as feature request #27. optipng assumes that a paletted image is always best, but in fact for small images (and some other cases) it usually isn't. For the same reason, optipng will never convert a paletted PNG file to RBG even if it would produce a smaller output.

    What your output shows is the optipng has gone straight into palette mode because there are only 137 colours. It then goes through its optimisation tests to find the smallest image chunk with that palette, which is 257 bytes. Then it compares the file size with that 257-byte chunk with the existing file size. Because of the overhead of the palette that is required, the file size ends up larger than it was before and so it says "already optimised".

    You can work around this issue by using the -nc command line option. This will prevent optipng converting your image to have a palette. You will see larger numbers for the IDAT size (due to now being 32 bits per pixel), but because a palette is no longer required to be stored you should see a reduction in the overall file size. Just for good measure, you can then run optipng on the resulting optimally-compressed RGB image and see if it can do better by using a palette (in this case, we already know it won't).

    This is how I process all my files:
    - force them to RGB mode (or grey-scale) either direct from GIMP or using mogrify to strip any palette chunks;
    - optipng -o7 -nc to compress in RGB mode;
    - optipng -o7 to see if a palette might be even better.

    Hopefully the need for this two-pass approach will be fixed soon by improvements to the colour type selection algorithm.

    Just for reference, if you want to make images of this type small you should reduce the number of colours. In this case, the image will be indistinguishable from the original when reduced to perhaps 16 colours. Interactive tools are indispensable, just slide down the number of colours until you start to see speckles or banding in the colours.

    For further optimisation, you can also reduce the number of transparency levels which is something that many tools don't do even when the number of colours is being reduced. Your image has 79 levels of transparency in an image only 18 pixels wide (note that optiping reports the number of different colours that have any partial transparency, so not quite the same thing, and even a single value of partial transparency can produce a large number of "transparent colours"). For large areas of graduated transparency like a shadow, you might need that many levels, but for anti-alisaing you can reduce the number of shades of partial transparency to a very small number (typically 8-16) and not see any difference in quality. The image size will reduce dramatically due to improved compression.