在C语言中使用libpng读写png文件而不改变文件大小

4
我想读取png文件,查看图像数据并在不改变文件大小的情况下重新写入。根据libpng文档,png是无损的,并使用deflatelz77进行压缩。在libpng中有一个示例项目声称可以读取和写入图像而不会丢失任何信息,它在像素值方面是正确的,但会更改文件结构(例如IDAT的数量,可选块等),从而更改png文件大小。
我的明确问题是:如何从压缩流(在libpng中为Zstream)中提取编码参数(例如deflate参数或lz77参数),并使用这些参数对原始图像进行编码,以创建与输入文件相同且没有任何更改的图像文件?
这是我的代码。我尝试将参数保存在info_ptr中以便写入图像,但不起作用。该怎么办?
int main(int argc, char *argv[])
{
inname = argv[1];
outname = argv[2];

png_structrp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(read_ptr);



if (!info_ptr)
{
    png_destroy_read_struct(&read_ptr, (png_infopp)NULL, (png_infopp)NULL);
}

png_FILE_p imageFile, imageFile2, imageFileW;
    imageFile=fopen(inname, "rb");  imageFileW = fopen(outname, "wb"); imageFile2 = fopen(inname, "rb");

int fileSize=fsize(imageFile2);
unsigned char* bufImWrite = malloc(sizeof(char)*fileSize);
fread(bufImWrite, 1, fileSize, imageFile2);

png_init_io(read_ptr, imageFile);

png_read_info(read_ptr, info_ptr);


png_uint_32 height;
height = info_ptr->height;


png_bytep * row_pointers;

row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
for (int y = 0; y < height; y++)
    row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(read_ptr, info_ptr));


png_read_image(read_ptr, row_pointers);


png_read_end(read_ptr, info_ptr);


png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);;
png_init_io(png_ptr, imageFileW);
png_write_info(png_ptr, info_ptr);
//png_set_compression_level(png_ptr, 9);
//png_set_compression_window_bits(png_ptr, 15);
//png_set_compression_strategy(png_ptr, 3);
//png_set_compression_mem_level(png_ptr, 8);

png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);


}

这是一个奇怪的请求。如果您不希望更改文件,请只复制它。如果您想要更改图像内容,则很可能无法实现完全相同的压缩率。如果您想更改一些标签,请直接在原地更改它们。 - user1196549
1
这个问题是正确的@YvesDaoust(踩?)。他需要获取原始图像数据,并再次压缩此数据以生成相同的文件。这与复制(在压缩科学中)不同。你对这个问题有什么解决方案? - Mohammad Shokouhi Gol
2个回答

7

PNG文件不包含用于压缩的确切deflate/zlib参数记录,因此您想要的并不是真正可能的。如果您需要保留原始编码,则应该保留原始PNG文件的副本,而不是破坏性地读取它。

如果您想要更改附加的PNG块而不更改图像数据,请使用一些应用程序,例如tweakpng将原始PNG文件中的IHDR、PLTE、IDAT和IEND块复制到新文件中。

如果如评论所示,您想要添加水印而不更改压缩数据流,则这是不可能的,因为图像数据是不同的。如果您真的需要可移除的水印,请使用一些可编辑格式(例如SVG)将水印分开,或者使用APNG等格式,在其中可以将水印存储在单独的部分中。缺点是任何人都可以从其副本中删除水印。


谢谢@Glenn Randers-Pehrson,根据您的说明,我无法解码和编码PNG文件并生成相同的文件。我正在进行图像水印处理,并且我的方法受到文件大小的限制。换句话说,我希望在PNG的压缩数据流中嵌入水印位,同时考虑对数据进行操作后的adlercrc的更改。您能告诉我在什么位置是最佳的压缩zlib流进行操作的位置,以保存数据结构和图像质量吗? - Ali Mahdavi
如果要添加水印,那么就会改变图像数据,因此绝对不可能保持相同的压缩数据流,因为正在压缩不同的图片。 - Glenn Randers-Pehrson
我想使用APNG格式作为第二帧来添加水印并覆盖在第一帧上可能是可行的,但这可能超出了这个问题的范围。我建议你放弃这个想法,接受重新压缩的图像数据。 - Glenn Randers-Pehrson
感谢Glenn Randers-Pehrson再次关注,我知道带水印的图像和输入图像不同,但生成相同的图像是第一步。因为如果我不能生成相同的图像,就无法在另一侧提取水印。所以我的想法是改变压缩数据并生成与输入图像几乎相似的图像。在JPEG中,我通过更改压缩数据中的DCT系数来实现这一点,并寻找在PNG中实现它的方法(更改deflate结果)。无论如何,还是要感谢你。 - Ali Mahdavi

5
除了Glenn的正确回答之外,即使您以某种方式拥有用于压缩的参数,压缩可能是由不再可用的代码执行的,或者由专有代码执行而不可用于您。在任何情况下,都无法复制完全相同的压缩数据。如果您不打算对图像进行处理,请保留原始压缩数据。

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