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

问题描述 投票:1回答:1

我已经阅读了太多次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(224,215,200),我用RGB to color converter重建。这看起来与图像中的原始像素几乎相同。以下是我对所有彩色像素的看法。

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表示位深度为8,颜色类型为2.从规格表中可以看出每个像素都是R,G和B三倍。有人能指出我正确的方向来阅读IDAT块并解释它的长度吗?

png zlib chunks
1个回答
3
投票

您的解压缩结果长度为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]

这将'解包'到以下两行,每行有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

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

添加(由过滤器1指示的“Sub”过滤器的反转)产生

; 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(“向上”),您需要在先前解码的行中使用相应的字节,对于Average和Paeth,您需要两者。

© www.soinside.com 2019 - 2024. All rights reserved.