Log entry (14/10/2023)
Since my arrival on Planet Zopfli, I have been beholden to a tepidly persuasive contract: Marginally improved output in exchange for lengthy processing times, each yield requiring up to four samples of differing origins. Thankfully, this dull endeavour was disrupted by a notification which painted itself on my terminal:
OxiPNG v9.0.0 firmware now available. Restart mining subsystem to apply updates.
Important update
OxiPNG v9.0.0 has been released, which naturally means some of the results from before have changed because of new optimisations and updated dependencies, here’s hoping on some improvements!
Please eliminate the variants.
Please eliminate the variants.
Come on! Come on! Come—
Data analysis
Typical case
Ah, screw it, you know the drill by now.
The interesting stuff
Results for the series-deciphering_hugo.png image, with the best-to-worst encoding order being: OxiPNG-ZF or Oxi-ZF-Quant, OxiPNG or Oxi-Quant, CWebP, PNGQuant, then Original.
Filename: 'series-deciphering_hugo.png' Original Filesize: 3819 bytes PNGQuant Filesize: 1990 bytes OxiPNG Filesize: 1885 bytes OxiPNG-ZF Filesize: 1849 bytes Oxi-Quant Filesize: 1885 bytes Oxi-ZF-Quant Filesize: 1849 bytes CWebP Filesize: 1918 bytes Smallest encoding: 1849 bytes, OxiPNG-ZF
Okay, we saved one byte for the Zopfli variants, same story though.
Results for the tag-css.png image, with the best-to-worst encoding order being: CWebP, OxiPNG or Oxi-Quant or OxiPNG-ZF or Oxi-ZF-Quant, PNGQuant, then Original.
Filename: 'tag-css.png' Original Filesize: 4780 bytes PNGQuant Filesize: 2474 bytes OxiPNG Filesize: 2261 bytes OxiPNG-ZF Filesize: 2261 bytes Oxi-Quant Filesize: 2261 bytes Oxi-ZF-Quant Filesize: 2261 bytes CWebP Filesize: 2054 bytes Smallest encoding: 2054 bytes, CWebP
With the OxiPNG update, a lot of the size differences between the variants have been flattened out, however, like in this case, the results can be worse compared to the previous release (e.g. “OxiPNG-ZF” output size from the previous release: 2256 bytes, which is five bytes smaller).
Of course, “CWebP” was more effective here anyways, so the actual smallest encoding didn’t increase at all, I just thought this point was noteworthy.
Results for the Invert.png image, with the best-to-worst encoding order being: CWebP, Oxi-ZF-Quant, OxiPNG-ZF, Oxi-Quant, OxiPNG, Original, then PNGQuant.
Filename: 'Invert.png' Original Filesize: 1542935 bytes PNGQuant Filesize: 1586364 bytes OxiPNG Filesize: 1506472 bytes OxiPNG-ZF Filesize: 1501598 bytes Oxi-Quant Filesize: 1504355 bytes Oxi-ZF-Quant Filesize: 1501474 bytes CWebP Filesize: 1356018 bytes Smallest encoding: 1356018 bytes, CWebP
And as you can see by the result for the Invert.png image, the PNGQuant combos are, annoyingly, still effective. Sure, “CWebP” absolutely destroys the OxiPNG variants here, but if we encounter a series-deciphering_hugo.png-esque result, we’ll need to try all the OxiPNG variants regardless to ensure the smallest encoding is found.
Strangely, this specific pre-release build of OxiPNG achieved an even smaller output at 1500130 bytes (Oxi-ZF-Quant variant).
…Why? Well now, I think I have an answer.
Indexed colours
The pre-release build and the “PNGQuant” variants share a difficult problem which is (most likely) the cause of the output size variance: Colour palette optimisation.
Each index in an indexed colour palette consists of a single byte, however, not every byte is equally compressible, 00000010 is a lot simpler than 01110011 (and that’s not even considering when the bytes are chained together), therefore, the frequency of each colour and thus its index drastically affects the image data’s compressibility.
Unfortunately, palette optimisation isn’t trivial to solve nor implement, although algorithms focused on this problem do exist as found by Kornel (the creator of pngquant and Gifski).
https://scholar.google.com/scholar?q=palette+reordering PDFs
kornelski
Addendum
OxiPNG v9.1.0 added a palette sorting method which has improved the situation, however, do note that this method is not guaranteed to find the smallest result possible; it’s not a brute-force algorithm.
Heuristics
In hopes of reducing the amount of processing time needed per image, I’m proposing a new heuristic for avoiding the OxiPNG variants in most cases:
- Typically, the normal OxiPNG output is more than 10% larger than the “CWebP” output.
- And usually, the OxiPNG variants are less than 10% smaller than the normal OxiPNG output.
- Therefore, if the OxiPNG output isn’t within 110% of the “CWebP” output, you can skip running the OxiPNG variants as it’s unlikely any of them would find a smaller image encoding.
Obviously, these percentages are based solely on my image set, which is only about twenty images and it’s biased towards my own pixel art, so to create a more generic heuristic, I’ll need to expand my dataset. Until then…