【问题标题】:Compression performance on certain types of data某些类型数据的压缩性能
【发布时间】: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,但将其他行留在%256ppmz 压缩率略微上升到 94.65%,RAR 产生了 30.9% 的比率。所以看起来好像它们可以很好地处理线性增加的序列,即使它们不同步,但是有一些非常奇怪的事情发生,算术 mod 2^8 对我们的压缩算法比地狱友好得多其他值。

【问题讨论】:

  • 非常有趣的问题/问题,感谢分享!

标签: performance compression


【解决方案1】:

嗯,首先,计算机喜欢 2 的幂。 :)

大多数此类压缩算法都使用通常与 2 的大幂对齐的压缩块。当您的循环与这些块完美对齐时,只有一个“唯一序列”可以压缩。如果您的数据未对齐,您的序列将在每个块中稍微移动,并且算法可能无法将其识别为一个“序列”。

编辑:(从 cmets 更新)

第二个原因是i*i*i 存在整数溢出。结果是一个双模:一个超过2^32,然后一个超过255。这种双模大大增加了循环的长度,使其接近随机,压缩算法难以找到“模式”。

【讨论】:

  • 我同意肯定有一些与 2 的幂有关的东西是有利的,但我不相信最后一个组件有任何对齐,即带有 i*i*i 的那个。对计算机来说,无论怎么看,它都应该是一团乱麻。
  • @Steven Lu:i*i*i % 255 有 255 个循环。i*i*i % 256 有 256 个循环。实际上并不是那么随机。
  • 是的,那为什么它的性能不会差 1/255 倍,而不是好 20 倍呢?
  • i*i*i 也有整数溢出。所以双模(一个超过 2^32,一个超过 255)肯定会使这个周期更长......给我一秒钟检查
  • 哦,可能就是这样。是的,因为 256 会有点“网格”,整数溢出。想知道这是否能说明所有问题。
【解决方案2】:

神秘有很大一部分答案,但查看数据本身的数学属性也是值得的,尤其是蓝色通道。

(i * i * i) % 255 以 255 的周期重复,同样频繁地采用 255 个不同的值。一个简单的编码器(忽略不同像素之间或 R 和 B 像素之间的模式)需要 7.99 位/像素来编码蓝色通道。

(i * i * i) % 256 是 0,只要 i 是 8 的倍数(8 的三次方是 512,当然是 0 mod 256);
每当i 比 8 的倍数大 4 时,它就是 64;
每当i 比 8 的倍数小 4 时,它就是 192(这些一起涵盖了 4 的所有倍数);
只要i 是 4 的偶数非倍数,它就是 16 个不同值之一,具体取决于i 的余数 mod 64。
只要i 是奇数,它就会采用 128 个不同的值之一。

这使得蓝色像素只有 147 种不同的可能性,其中一些发生的频率比其他的高得多,而蓝色通道的天真熵为 6.375 位/像素。

【讨论】:

  • 每当 (iii) % 16 = 8 时,它是 16 个不同值之一,即每 4 个像素。此外,低位严格地在 0 和 1 之间波动。因此,虽然有 147 个不同的值,但四分之一是三个值之一,四分之一是 16 个值之一,一半是 128 个奇数值之一。
猜你喜欢
  • 2021-01-21
  • 1970-01-01
  • 2018-12-14
  • 1970-01-01
  • 2022-01-02
  • 2013-09-14
  • 2017-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多