自定义图像格式:如何针对压缩算法



在过去的几天里,我对PNG做了一些摆弄,我对自己的发现感到不安。我得出的结论是,我的大多数结果都与压缩有关。所以这个周末我将深入研究高级压缩文章。到目前为止,我想分享我的发现。看看是否有人对实现我的目标有任何建议,也许可以为我指明正确的方向。

我目前正在做一个项目,我需要在不到15秒的时间内获得尽可能小的文件大小。

我使用的大多数图像都是PNG-8ppp,带有完整的256色调色板。我可以用5bpp(32种颜色)准确地表示这些图像中的大多数。

然而,PNG索引仅支持1,2,4和8ppp。因此,我的想法是将PNG格式剥离到我需要的最小信息,并编写一个编码器/解码器来支持具有3,5,6或7bpp的IDAT部分。

Test 1:
Original File: 61.5KB, 750 * 500, 8pp Palette, 256 colors, No tRNS
After Optimizations (Reductions to 4bpp, Strip Anx Chunks, & PNGOUT): 49.2KB 4bpp, 16 Colors
Human Interpretation: I can see 6 distinguishable colors.

由于我只需要六种颜色来表示图像,我决定使用3bpp对IDAT进行编码,以给我最多8种颜色的调色板。首先,我对IDAT进行了解压缩,得到了368KB的新文件大小。在对IDAT应用3pp之后,我的新未压缩文件大小为274KB。我开始了一个似乎很好的开始。。。接下来,我将deflate应用到我的新IDAT部分。后果59KB。

比使用4bpp大10KB。

Test 2:
Original File: 102KB, 1000 * 750, 8bpp, 256 Colors, tRNS 1 fully transparent color
After Optimization: 79KB, 8bpp, 193 colors, tRNS 1 full transparent color
Human Interpretation: I need about 24 colors to represent this picture.

24种颜色可以在5bpp中以32种颜色表示。使用上面的相同技术,我能够获得比未压缩更好的结果,但我再次在压缩时失败了。压缩的最终大小。。。84KB。然后我试了6,7便士。。。与8bpp时的压缩效果相同。

为了确保我保存了所有未压缩的图像,并尝试了其他几种压缩算法。。。LZMA、BZIP2、PAQ8……同样的结果是,8ppp时的压缩尺寸比5,6时更小,或7bpp时更小,4bpp时的压缩大小比3ppp时更小。

为什么会发生这种情况?我可以调整/修改压缩算法,以针对使用5、6或7bpp格式的类似PNG的格式,而不是8pp压缩吗?值得花时间吗。。。是的,再节省10KB是值得的。

您看到的是,通过使用奇数像素大小,由于PNG压缩的工作方式,您的有效压缩会降低。与直接使用FLATE/ZIP压缩相比,PNG压缩的优势在于过滤。PNG压缩试图利用水平和垂直对称性和少量预处理过滤器。这些滤波器在字节边界上工作,对4/8/16/24/32/48/64位的像素大小有效。当你移动到一个奇数大小的像素(3/5/6/7位)时,你会破坏过滤,因为在8位边界上过滤时,相同颜色的像素不会水平"相互抵消"。

即使滤波不是问题,FLATE压缩的工作方式,将像素大小从8位减少到7位或6位也不会有太大影响,因为它还假设符号大小为8位。

最后。。。使用奇数大小的像素可以获得的唯一好处是,未压缩的数据会更小。通过打破像素的字节边界对称性,您将失去PNG压缩的大部分好处。

GIF压缩支持从1到8位的所有像素大小。它将符号大小定义为像素大小,并且不使用任何预过滤。如果压缩为7位像素,8位GIF图像不会受到更少的压缩,但也不会受益,因为压缩更多地取决于像素的重复,而不是符号大小。

PNG使用的DEFLATE压缩有两种主要技术:
  • 查找重复的字节序列,并将其编码为反向引用
  • 使用霍夫曼编码对字节进行编码

通过将像素长度从8位更改为与字节边界不同步,DEFLATE将无法将重复的像素运行编码为重复的字节。

多亏了霍夫曼编码,8位像素有未使用的位并不重要,因为编码将用可变宽度代码对字节进行编码,将最短的值分配给最频繁出现的值。

最新更新