PNG 解压缩 IDAT 块。如何阅读?

PNG decompressed IDAT chunk. How to read?

我已经阅读了太多次 PNG 规范,但仍然对如何解释 IDAT 块感到困惑。我使用 zlib 对其进行了解压缩,并获得了我的 IDAT 块获得的所有字节。

我使用 krita 制作了示例图像。这是一个 3x2 PNG 图像,每个像素包含不同的颜色。 See the 3 by 2 PNG image here

根据关于过滤器的PNG specification,它说当 IDAT 块的第一个字节为 1 时,应用的过滤器方法是

过滤(字节)=原始(字节)-原始(previous_byte)

考虑到该公式,我解压缩了我的 IDAT 块(长度为 29 个字节,仅存储 6 个像素)。第一个字节(字节编号 0)包含值 1。这就是公式的来源。

Byte#    Vaue
0        1
1        224
2        215
3        200
4        227
5        241
6        48
7        2
8        36
9        225
10       1
11       253
12       255
13       195
14       245
15       182
16       244
17       232
18       245
19       57
20       0
21       0
22       0
23       0
24       0
25       0
26       0
27       0
28       0

第一个像素应该是我用 RGB to color converter 重建的 RGB(224, 215, 200)。这看起来与图像中原始像素的颜色几乎相同。这是我对所有颜色像素的看法。

Pixel 1: RGB(224, 215, 200) [read from byte 1, byte2 and byte3]
Pixel 2: RGB(195, 200, 248) [because byte 4:227 byte5:241 byte6:48]
Pixel 3: RGB(197, 236, 217) [because byte 7:2 byte8:36 byte9:225]
Pixel 4: RGB(198, 233, 217) [because byte10:1 byte11:253 byte12:255]
Pixel 5: RGB(137, 222, 142) [because byte13:195 byte14:245 byte15:182]
Pixel 6: RGB(107, 198, 131) [because byte16:244 byte17:232 byte18:245]

我已经使用公式从像素中获取所有值。 重建像素 1、2 和 3 看起来几乎相同,但像素 4、5 和 6 不是我所期望的。我想我没有以正确的方式阅读 IDAT 块。这可以解释为什么只有 6 个像素的 RGB 有 29 个字节。我期望 19 个字节,因为 3 乘以 6 是 18 和 1 个字节用于过滤方法。

IHDR说bit depth是8,color type是2。从table中规格说明每个像素都是 R、G 和 B 三元组。有人能给我指出正确的方向来阅读 IDAT 块并解释它的长度吗?

您的解压结果长度29不正确,可能导致您混淆。

您的图像是 3x2 RGB 像素。那将是 3*3 * 2 = 18 个字节的数据,加上每行 1 个额外的字节;一共20个字节。不知何故你得到了额外的 9 个虚拟字节,而不是压缩数据的一部分。

(我从较大的图像重建了你的小图像,很高兴得到了完全相同的数字,否则解释必然是纯粹的理论。为方便起见,我用十六进制查看器确定了压缩数据的偏移量。)

>>> with open ('3x2b.png','rb') as f:
...   result = f.seek (0x6a)
...   data = f.read()
... 
>>> d = zlib.decompress(data)
>>> print ([x for x in d])
[1, 224, 215, 200, 227, 241, 48, 2, 36, 225, 1, 253, 255, 195, 245, 182, 244, 232, 245, 57]

这'unpacks'到以下两行,每行3个RGB像素值:

filter  RGB          RGB           RGB
1      (224,215,200) (227,241,48)  (2,36,225)
1      (253,255,195) (245,182,244, (232,245,57)

所有这些值可能相对于较早的结果:在它之前读取的最后一个完整行,或其左侧的像素。对于第一行,您必须假定一行全为零;第一个像素的值 "left" 也必须假定为 0

你看到标记为 'filter' 的两个字节了吗?那是你出错的地方。每行都有自己的过滤字节。您使用过滤器字节本身来计算第二行。

添加("Sub" 过滤器的逆运算,如过滤器 1 所示)产生

; start of row 0, filter is 1 and 'initial pixel' is (0,0,0)
(224,215,200) (224+227,215+241,200+48)
             =(195,200,248)
                            (195+2,200+36,248+225)
                           =(197,236,217)
; restart for row 1, filter is 1 again and start value (0,0,0):
(253,255,195) (253+245,255+182,195+244)
             =(242,181,183)
                            (242+232,181+245,183+57)
                           =(218,170,240)

...正是我开始使用的颜色。

这是过滤器 1 ("Sub"),因此使用其左侧的值;对于过滤器 2 ("Up"),您需要使用先前解码的 行中的相应字节,对于 Average 和 Paeth,您需要两者。