使用 C++、libpng 和 OpenMP 并行化 PNG 文件创建

2024-02-26

我目前正在尝试在 C++ 中实现一个基于 libpng 的 PNG 编码器,它使用 OpenMP 来加速压缩过程。 该工具已经能够从各种图像格式生成 PNG 文件。 我将完整的源代码上传到pastebin.com,这样你就可以看到我到目前为止所做的事情:http://pastebin.com/8wiFzcgV http://pastebin.com/8wiFzcgV

到目前为止,一切都很好!现在,我的问题是找到一种方法来并行生成包含压缩图像数据的 IDAT 块。通常,libpng 函数 png_write_row 在 for 循环中调用,其中包含一个指向包含有关 PNG 文件的所有信息的结构的指针和一个包含单个图像行的像素数据的行指针。

(Pastebin 文件中的第 114-117 行)

//Loop through image
for (i = 0, rp = info_ptr->row_pointers; i < png_ptr->height; i++, rp++) {
    png_write_row(png_ptr, *rp);
}

然后,Libpng 逐行压缩并用压缩数据填充内部缓冲区。一旦缓冲区已满,压缩数据就会以 IDAT 块的形式刷新到图像文件中。

我的方法是将图像分成多个部分,让一个线程将第 1 行压缩到第 10 行,另一个线程将第 11 行压缩到第 20 行,依此类推。但由于 libpng 使用内部缓冲区,这并不像我首先想象的那么容易:) 我必须以某种方式让 libpng 将压缩数据写入每个线程的单独缓冲区。之后,我需要一种方法以正确的顺序连接缓冲区,以便我可以将它们全部写入输出图像文件。

那么,有人知道如何使用 OpenMP 并对 libpng 进行一些调整来做到这一点吗?非常感谢!


对于评论来说这太长了,但也不是真正的答案——

我不确定您是否可以在不修改 libpng (或编写自己的编码器)的情况下做到这一点。无论如何,如果您了解 PNG 压缩是如何实现的,将会有所帮助:

在高层,图像是一组像素行(通常是表示 RGBA 元组的 32 位值)。

每行可以独立有一个filter http://www.w3.org/TR/PNG-Filters.html应用于它——过滤器的唯一目的是使行更加“可压缩”。例如,“子”过滤器使每个像素的值与其左侧的值之间的差值。乍一看,这种增量编码可能看起来很愚蠢,但如果相邻像素之间的颜色相似(往往是这种情况),那么无论它们代表的实际颜色如何,结果值都非常小。压缩此类数据更容易,因为它的重复性更高。

往下一层,图像数据可以看作是字节流(行之间不再区分)。这些字节被压缩,产生另一个字节流。压缩数据被任意分成段(任何您想要的位置!),每个段写入一个 IDAT 块(每个块还有一点簿记开销,包括 CRC 校验和)。

最低级别将我们带到有趣的部分,即压缩步骤本身。 PNG 格式使用zlib http://www.ietf.org/rfc/rfc1950.txt压缩数据格式。 zlib 本身只是围绕真实压缩数据格式的包装器(具有更多簿记功能,包括 Adler-32 校验和),deflate http://www.ietf.org/rfc/rfc1951.txt(zip 文件也使用这个)。 deflate 支持两种压缩技术:霍夫曼编码(根据字符串中每个不同字节出现的频率,将表示某些字节字符串所需的位数减少到最佳数量)和 LZ77 编码(允许重复的字符串已经存在)发生被引用而不是写入输出两次)。

并行化 deflate 压缩的棘手部分是,一般来说,压缩输入流的一部分要求前一部分也可用,以防需要引用。But,就像 PNG 可以有多个 IDAT 块一样,deflate 被分解为多个“块”。一个块中的数据可以引用另一块中先前编码的数据,但它不能have到(当然,不加的话可能会影响压缩比)。

因此,并行 deflate 的一般策略是将输入分解为多个large部分(以便压缩比保持较高),将每个部分压缩为一系列块,然后将这些块粘合在一起(这实际上很棘手,因为块并不总是以字节边界结束 - 但您可以放置​​一个空的非-压缩块(类型00),它将与字节边界对齐,位于各部分之间)。然而,这并不是小事,需要控制最低级别的压缩(手动创建 deflate 块),创建跨越所有块的正确 zlib 包装器,并将所有这些填充到 IDAT 块中。

如果您想自己实施,我建议您阅读我自己的 zlib/deflate 实现 https://github.com/cameron314/PNGEncoder2/blob/master/DeflateStream.hx (and 我如何使用它 https://github.com/cameron314/PNGEncoder2/blob/ee76bd0a7b2bca5f8dab11e96bfb361c37ad2fcf/PNGEncoder2.hx#L918),这是我专门为压缩 PNG 而创建的(它是用 Haxe for Flash 编写的,但应该相对容易移植到 C++)。由于 Flash 是单线程的,所以我不进行任何并行化,但我确实将编码分成多个帧上几乎独立的部分(“实际上”,因为部分之间保留了小数字节状态),这在很大程度上相当于一样。

祝你好运!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 C++、libpng 和 OpenMP 并行化 PNG 文件创建 的相关文章

随机推荐