Python 如何获取 PNG 格式的图像数据

在学习 PNG 的编码规范,看了 PNG文件结构分析,大概知道图像的数据储存在 IDAT 数据块里,不过不是原始数据,而是对其采用 Zlib 压缩。
于是我在 Python 里 import zlib,对 PNG 的图像数据进行解压,似乎不能够100%还原图像数据。下面是我的操作过程。

这是我测试的 PNG 图像:
PNG图像

图像大小是2*3像素,像素 RGBA 值如下:
#eb6100ff #22ac38ff #ea68a2ff
#22ac38ff #ea68a2ff #eb6100ff

对应的十六进制数据:

89504e470d0a1a0a0000000d49484452000000030000000208060000009d74661a0000002349444154089963789dc8f05f698dc5ff57198bfe332aadb1f87f624f1603e3cf380600af940c90637dc1550000000049454e44ae426082

根据 PNG 规范定义,IDAT 数据库除去数据块长度码、数据块类型码、CRC码,剩下的就是 IDAT 的内容数据块:089963789dc8f05f698dc5ff57198bfe332aadb1f87f624f1603e3cf380600af940c90
然后在 Python 2.7 用 zlib 对其进行解压:

import zlib
bitData = '089963789dc8f05f698dc5ff57198bfe332aadb1f87f624f1603e3cf380600af940c90'
print zlib.decompress(bitData.decode('hex')).encode('hex')

得到输出:
00eb6100ff22ac38ffea68a2ff0122ac38ffc8bc6a0001f95e00

除去扫描行序列0001得到每一个像素的 RGBA 值:
#eb6100ff #22ac38ff #ea68a2ff
#22ac38ff #c8bc6a00 #01f95e00

对比前面像素的 RGBA 值,可见第2行的最后2个像素(c8bc6a0001f95e00)的 RGBA 不是原来的值。不知是哪个环节出了问题,希望 SF 的大神解答下,先谢过了,跪~

阅读 11.7k
1 个回答

在查看对比这个PNG解压源码与题主的操作流程,发现问题在于少了undo_filter这一步。

在源码png.py中

...
            while len(a) >= rb + 1:
                filter_type = a[0]
                scanline = a[1:rb+1]
                del a[:rb+1]
                recon = self.undo_filter(filter_type, scanline, recon)
...

可以看出,每行的第一个字节是代表filter_type, 不是行号!(因为也不需要!)
00eb6100ff22ac38ffea68a2ff0122ac38ffc8bc6a0001f95e00
这里的00是没有用滤波器,01是指用了减法滤波器

减法滤波器的定义:

 def sub():
            """Undo sub filter."""

            ai = 0
            # Loops starts at index fu.  Observe that the initial part
            # of the result is already filled in correctly with
            # scanline.
            for i in range(fu, len(result)):
                x = scanline[i]
                a = result[ai]
                result[i] = (x + a) & 0xff
                ai += 1

c8bc6a00+22ac38ff=ea68a2ff (注意忽略字节进位,即 &ff的作用)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题