【发布时间】:2011-09-19 00:47:12
【问题描述】:
我正在测试我的新图像文件格式,它包含通过 zlib 的压缩流发送的每像素 24 位 PPM RGB 格式和附加到前面的 8 字节标头。
当我编写测试来评估实现它的相应代码的性能时,我有一个测试用例产生了非常糟糕的结果。
unsigned char *image = new unsigned char[3000*3000*3];
for(int i=0;i<3000*3000;++i) {
image[i*3] = i%255;
image[i*3+1] = (i/2)%255;
image[i*3+2] = (i*i*i)%255;
}
现在我在这里所做的是创建一个 3000x3000 完全打包的每像素 3 字节的图像,其中红色和绿色条纹稳步增加,但蓝色分量会发生很大变化。
当我使用 zlib 流将其压缩为 .ppmz 格式时,它能够将大小从 27,000,049 字节(它不是偶数 2700 万的原因是标题中有 49 字节)减少到 25,545,520 字节。这个压缩文件是原始大小的 94.6%。
这让我一开始有点慌张,因为我认为即使蓝色成分如此混乱也无济于事,至少红色和绿色成分会重复很多次。一个足够聪明的压缩机应该能够缩小到大约 1/3 的大小......
为了测试这一点,我提取了原始的 27MB 未压缩文件并对其进行了 RAR 处理,结果为 8,535,878 字节。这是相当不错的,31.6%,甚至超过三分之一!
然后我意识到我在定义测试图像时犯了一个错误。当我应该钳制到 255 时,我使用的是 mod 255,即 mod 256:
unsigned char *image = new unsigned char[3000*3000*3];
for(int i=0;i<3000*3000;++i) {
image[i*3] = i%256;
image[i*3+1] = (i/2)%256;
image[i*3+2] = (i*i*i)%256;
}
问题是,现在我的像素可以再取一个值,我之前跳过了这个值。但是当我再次运行我的代码时,ppmz 变成了一个 145797 字节的文件。 WinRAR 将其压缩为 62K。
为什么这个微小的变化会造成如此巨大的差异?即使是强大的 WinRAR 也无法获得 8MB 以下的原始文件。每 256 步重复一次值而每 255 步完全改变的原因是什么?我通过%255 得到了这一点,它使前两个颜色分量的图案略微异相,但行为几乎不是随机的。然后只是疯狂的模运算被转储到最后一个通道。但我不明白它是如何解释如此巨大的性能差距的。
我想知道这是否更像是一个数学问题而不是一个编程问题,但我真的不知道原始数据如何包含比我新修改的数据更多的熵。我认为 2 依赖的力量表明与算法有关。
更新:我进行了另一项测试:我将第三行切换回(i*i*i)%255,但将其他行留在%256。 ppmz 压缩率略微上升到 94.65%,RAR 产生了 30.9% 的比率。所以看起来好像它们可以很好地处理线性增加的序列,即使它们不同步,但是有一些非常奇怪的事情发生,算术 mod 2^8 对我们的压缩算法比地狱友好得多其他值。
【问题讨论】:
-
非常有趣的问题/问题,感谢分享!