编码大小超过15000 * 15000像素的PNG文件时,LodePNG崩溃

时间:2014-08-25 16:28:27

标签: c png

我一直在使用LodePNG的lodepng_encode24_file对一些24位RGB图像文件进行编码,到目前为止它运行得非常好。但是,我注意到当我向它提供大于15360 * 15360像素的数据集(14336 * 14336像素图像编码正确)时,它似乎崩溃。

32位情况(崩溃前的最大大小略低)的这种行为的最小示例可以通过简单地替换行来获得

  unsigned width = 512, height = 512;

  unsigned width = 1024*14, height = 1024*14;
LodePNG's example_encode.c file

并执行它。

我之前遇到过C代码崩溃的问题,因为我正在将大型数组分配给堆栈内存(其最大大小通常约为2MB)而不是堆内存,所以作为C的新用户,我的第一直觉就是看堆内存大小有一个上限。

但是,according to this answer,堆内存没有限制,所以其他一些必须出错。

我的第二个猜测是崩溃是由于PNG格式本身支持的最大图像尺寸的固有限制。但是,according to this answer及其下方的评论,PNG支持的最大文件大小约为4,000,000,000 * 4,000,000,000像素,因此这也不是罪魁祸首。

有没有人猜测可能会出现什么问题?是否有其他人在尝试时能够重现此错误?

编辑:就RAM消耗而言,我有8GB RAM,并减去硬件保留,使用中,修改和待机内存(Windows资源监视器实用程序使用的术语)I在进行计算时有大约4GB的RAM空闲。对于15000 * 15000大小的32位图像,需要不到1GB。同样,当我成功编码14000 * 14000个24位图像时,我的空闲RAM在编码过程的任何时候都不会低于3GB,所以我不认为RAM耗尽是问题。

1 个答案:

答案 0 :(得分:4)

我相信你低估了程序使用的内存量。假设您使用的是Windows,那么您可能只有2 GB的内存可用于进程(请参阅here)。然后,为14336x14336映像分配一个882 MB的大块。然后,LodePNG代码会进行更多可能等于或大于此图像大小的分配。

我只是手动跟踪LodePNG code,但似乎在内存中创建了一个缓冲区并以块的形式写入该缓冲区。它在重新分配时执行最小调整大小(在lodepng_chunk_append()中,仅足以保存数据加上12个字节),这意味着它将不得不进行大量重新分配。这可能(或最终将)将存储器分段到一个非常大的缓冲区不可用的点。

即使堆内存没有碎片,也要考虑当你尝试将realloc() 800MB缓冲区放到801MB缓冲区时会发生什么。如果堆管理器是“智能”的话可能会有效,但是一个天真的管理器需要总共1601MB ...如果你的堆大小是2000MB并且你已经使用了822MB它就不可用。

很多(大多数)这是猜想,但您可以自己做一些测试来模拟分配和重新分配几个大块内存,看看是否可以重现内存不足的情况。

附录:虽然上述情况可能属实,但实际上并不是导致此类情况崩溃的原因。从实际运行和跟踪代码开始,问题是LodePng.c中的第5558行:

size_t size = (w * h * lodepng_get_bpp(&info.color) + 7) / 8;

w * h返回24时lodepng_get_bpp()大于178,956,970时,此计算会导致整数溢出。对于方形图像,这是13378 x 13378.这会导致输出缓冲区分配不正确的大小因此,稍后写入时缓冲区溢出。

快速解决方法是将此行更改为:

 size_t size = (size_t) ((unsigned long long) w * h * lodepng_get_bpp(&info.color) / 8 + 1);

虽然我不确定这对所有BPP值都能正常工作,但它仍然存在溢出问题,尽管规模较大。我建议联系LodePNG的作者来实施适当的修复。