快速编写PNG图片

25

概述

我希望尽可能快地写入一个 .png 文件,不考虑压缩。也就是说,我不太关心文件大小,但我确实关心尽可能快地进行写入。

动机

我正在使用 OpenLayers(客户端)和 python/C++(后端)创建基于 Web 的地图应用程序。该应用程序需要能够快速绘制动态内容,以便用户在地图上移动时能立即看到更新。我已经制作了基于瓦片的(256x256 像素)和单图像的("单个瓦片")版本,但无论是哪种情况,后端渲染最慢的部分都是将图像保存为 png 文件(无论是在磁盘上还是在内存中)。例如,我可以在大约 200 毫秒内生成某个视图的“原始”、“tga”或“tiff”版本,但生成 .png 版本需要更长的时间,因为 .png 保存几乎需要整整一秒钟,而保存其他格式的时间少于 100 毫秒(即使“原始”文件的大小是 .png 文件的五倍以上)。而这个文件保存时间也显着大于将结果图像从服务器传输到客户端的时间。(我的应用程序的一个重要属性是,“后端”通常会在与浏览器相同的计算机上运行,因此即使对于较大的文件,传输时间也可以忽略不计。)

我认为通过调用 C++ 中的 libpng 库,可以快速地对 .png 进行写入:

    png_set_compression_level( png_ptr, 0 );

在调用任何png_write_...函数之前,请确保先调用png_set_compression_level(png_structp,int)。然而,虽然这个调用确实停止了libpng对文件的压缩(生成的文件大小与.raw文件大致相同),但它并没有使得保存.png文件明显更快。

请帮帮我

我需要将这些图像使用.png格式,因为我需要它们作为透明覆盖层放置在基础地图上,并且需要比GIF提供的256种颜色更多。OpenLayers只是使用html的img标签,因此我的理解是我只能使用有效的img格式。

我认为应该有一种方法可以通过不进行任何真正的压缩来快速编写.png文件(我知道.png“总是被压缩”,但我想象这可能包括“空压缩”)。看起来你应该能够编写一个简单的固定头部,后跟未压缩的数据,再跟一些固定的页脚。或者按行执行同样的想法。重点是我可以非常快地在C++中循环遍历这2.5 MB的原始数据,并且可以非常快地将其转储到各种文件格式中,因此似乎我也应该能够快速转储为固定的未压缩的.png格式。

听起来对吗?你知道我可以在哪里找到执行此操作的代码示例吗?


2
您的硬盘具有固定/最大写入速度,通常比处理器进行数学运算的能力慢。 - Brian Roach
2
我给出+1的评价,主要是因为这可能是我最近看到的第一个关于“快速完成此操作”的问题,它(1)有一个关心速度的好理由,(2)包括硬性数字,(3)在发布之前实际上已经进行了诚实的尝试。 - user395760
1
@ Brian Roach:不对。请再次阅读帖子。磁盘速度根本不是这里的限制因素。我可以在100毫秒内写入2.5 MB的.raw文件。当我写一个五倍更小的.png文件时,需要整整一秒钟。而且,无论是将其实际写入磁盘还是写入内存文件,都无关紧要。由于实际构建/压缩数据的速度而导致的相同缓慢。 - M Katz
如果您已经关闭了压缩,文件的大小仍然比以前大并且速度仍然很慢,您是否考虑过压缩可能不是罪魁祸首呢? - Pascal Cuoq
@ Pascal Cuoq:嗯,它可能不是压缩本身。但我不认为这仅仅是字节数量的问题。我需要了解更多关于.png的知识,但我知道有“过滤”和其他一些东西正在进行。我相信这是一个过程,libpng花费时间安排字节,无论是“压缩”还是其他什么。 - M Katz
你成功完成了吗?我也遇到了同样的问题,我想快速将PNG写入RAM。 - Phil
2个回答

33
你希望的是一种专为你的目的而定制的实现;你需要编写自己的编码器。实际上并不难,规格说明是免费的。
格式并不太复杂,应该很容易实现一个编码器。
注意:所有值都是无符号的。多字节整数采用“网络字节顺序”(最高有效字节在前)。

该格式由chunks组成。 块结构:

  • 块内容的长度,4个字节
  • 块标识符(ASCII),4个字节
  • 块内容,“块内容的长度”个字节
  • 标识符和内容的CRC(即不包括长度),4个字节

您的实现只需要魔数和三个chunks


详细布局:
  • { 137, 80, 78, 71, 13, 10, 26, 10 } (魔数), 8 字节
  • IHDR 块的字节数,4 字节 (值: 13)
  • { 73, 72, 68, 82 } ("IHDR"),4 字节
  • 宽度,4 字节
  • 高度,4 字节
  • 每种颜色的位深度, 1 字节 (8 = 24/32 位色深)
  • 颜色类型, 1 字节 (2 = RGB)
  • 压缩方法, 1 字节 (0 = DEFLATE 算法,不进行压缩)
  • 过滤方法, 1 字节 (0 = 不过滤)
  • 交错方法,1 字节 (0 = 不交错)
  • IHDR 块的 CRC, 4 字节
  • IDAT 块内容的字节数,4 字节
  • { 73, 68, 65, 84 } ("IDAT"),4 字节
  • 使用 DEFLATE 算法封装的原始图像数据,"IDAT 块内容的字节数" 字节
  • IDAT 块的 CRC, 4 字节
  • IEND 块的字节数,4 字节 (值: 0)
  • { 73, 69, 78, 68 } ("IEND"),4 字节
  • IEND 块的 CRC, 4 字节 (可预先计算)

DEFLATE数据编码,不使用压缩

您的数据将被分成每个65535字节的块 格式很简单:

  • 前X个块
    • 头部,1字节(值:0)
    • 数据,65535字节
  • 最终块
    • 头部,1字节(值:1)
    • 数据,65535字节或更少

就是这样


这就是如何制作快速的PNG文件。


1
我需要相同的东西时,有一些细节。 - Řrřola
1
IDAT的内容看起来像这样:字节0x78,字节0x01,块,图像数据的Adler32校验和。块有一个5字节的小尾数头:字节(1=最后一块,否则为0),2个字节的块大小,2个字节的补码。 - Řrřola
1
此外,每个图像行首先需要以字节0(=无过滤器)为前缀。 - Řrřola
1
crc32("IEND") == 0xAE426082。 - Michaelangel007
PNG文件规范写得非常好,读起来很愉快。该文件格式非常灵活,通常设计得很好。 - Drew Noakes
显示剩余5条评论

15

PNG的压缩速度主要受两个参数影响:

  1. ZLIB压缩的压缩级别。将其设置为0,通过png_set_compression_level实质上相当于禁用了该压缩。

  2. 像素过滤。这可以针对每一行进行变化,并且选择通常是通过某些启发式方法完成的,这可能对大小很有效,但可能耗费时间。如果速度是主要考虑因素(更重要的是,如果ZLIB压缩被禁用),您应该选择单个过滤器:PNG_FILTER_NONE。请参见png_set_filterpng_set_filter_heuristics

我认为没有太多可以优化速度的地方。与像BMP或TGA这样的“原始”格式相比,非压缩PNG仍然具有每个块计算CRC32的(小)负担,以及ZLIB的内部Adler CRC。就这些了。

(为记录,我编写了一个完整的纯Java编码器/解码器,旨在实现内存和CPU效率:PNGJ


1
同时将3.png_set_compression_strategy设置为HUFFMAN_ONLY。 - Glenn Randers-Pehrson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接