音影片入門-11-PNG文件格式詳解

  • 2019 年 10 月 29 日
  • 筆記

* 音影片入門文章目錄 *

PNG 文件格式解析

PNG 影像格式文件由一個 8 位元組的 PNG 文件署名域和 3 個以上的後續數據塊(IHDR、IDAT、IEND)組成。

PNG 文件包括 8 位元組文件署名(89 50 4E 47 0D 0A 1A 0A,十六進位),用來識別 PNG 格式。

用十六進位查看器打開任意一個 PNG 文件,都是可以看到這樣的頭部:

png-hex-file-signture.png

PNG 定義了兩種類型的數據塊:一種是 PNG 文件必須包含、讀寫軟體也都必須要支援的關鍵塊(critical chunk);另一種叫做輔助塊(ancillary chunks),PNG 允許軟體忽略它不認識的附加塊。這種基於數據塊的設計,允許 PNG 格式在擴展時仍能保持與舊版本兼容。

數據塊總覽

下表就是 PNG 中數據塊的類別,關鍵數據塊部分突出顯示以區分:

數據塊符號 數據塊名稱 多數據塊 是否可選 位置限制
IHDR 文件頭數據塊 第一塊
cHRM 基色和白色點數據塊 在PLTE和IDAT之前
gAMA 影像γ數據塊 在PLTE和IDAT之前
sBIT 樣本有效位數據塊 在PLTE和IDAT之前
PLTE 調色板數據塊 在IDAT之前
bKGD 背景顏色數據塊 在PLTE之後IDAT之前
hIST 影像直方圖數據塊 在PLTE之後IDAT之前
tRNS 影像透明數據塊 在PLTE之後IDAT之前
oFFs (專用公共數據塊) 在IDAT之前
pHYs 物理像素尺寸數據塊 在IDAT之前
sCAL (專用公共數據塊) 在IDAT之前
IDAT 影像數據塊 與其他IDAT連續
tIME 影像最後修改時間數據塊 無限制
tEXt 文本資訊數據塊 無限制
zTXt 壓縮文本數據塊 無限制
fRAc (專用公共數據塊) 無限制
gIFg (專用公共數據塊) 無限制
gIFt (專用公共數據塊) 無限制
gIFx (專用公共數據塊) 無限制
IEND 影像結束數據 最後一個數據塊

我們目前只需關注關鍵數據塊即可。

數據塊中有 4 個關鍵數據塊:

  • 文件頭數據塊 IHDR(header chunk):包含有影像基本資訊,作為第一個數據塊出現並只出現一次。
  • 調色板數據塊 PLTE(palette chunk):必須放在影像數據塊之前。
  • 影像數據塊 IDAT(image data chunk):存儲實際影像數據。PNG 數據允許包含多個連續的影像數據塊。
  • 影像結束數據 IEND(image trailer chunk):放在文件尾部,表示 PNG 數據流結束。

數據塊連起來,大概這個樣子:

PNG 標識符 PNG 數據塊(IHDR) PNG 數據塊(其他類型數據塊) PNG 結尾數據塊(IEND)

數據塊結構

PNG 文件中,每個數據塊(比如IHDR,IDAT等)由4個部分組成:

名稱 位元組數 說明
Length (長度) 4 位元組 指定數據塊中數據域的長度,其長度不超過(2^31-1)位元組
Chunk Type Code (數據塊類型碼) 4 位元組 數據塊類型碼由 ASCII 字母(A-Z和a-z)組成
Chunk Data (數據塊數據) 可變長度 存儲按照 Chunk Type Code 指定的數據
CRC (循環冗餘檢測) 4 位元組 存儲用來檢測是否有錯誤的循環冗餘碼
  • CRC(cyclic redundancy check) 域中的值是對 Chunk Type Code 域和 Chunk Data 域中的數據進行計算得到的。
  • 注意:Length 值的是除:length 本身,Chunk Type Code,CRC 外的長度,也就是 Chunk Data 的長度。

數據塊-文件頭數據塊 IHDR

它包含 PNG 文件中存儲的影像數據的基本資訊,並要作為第一個數據塊出現在 PNG 數據流中,而且一個 PNG 數據流中只能有一個文件頭數據塊。

文件頭數據塊由 13 位元組組成:

域的名稱 位元組數 說明
Width 4 bytes 影像寬度,以像素為單位
Height 4 bytes 影像高度,以像素為單位
Bit depth 1 byte 影像深度: 索引彩色影像:1,2,4或8 灰度影像:1,2,4,8或16 真彩色影像:8或16
ColorType 1 byte 顏色類型:0:灰度影像, 1,2,4,8或16 2:真彩色影像,8或16 3:索引彩色影像,1,2,4或8 4:帶α通道數據的灰度影像,8或16 6:帶α通道數據的真彩色影像,8或16
Compression method 1 byte PNG Spec 規定此處總為 0,表示使用壓縮方法(LZ77派生演算法)
Filter method 1 byte PNG Spec 規定此處總為 0,濾波器方法
Interlace method 1 byte 隔行掃描方法:0:非隔行掃描 1: Adam7(由Adam M. Costello開發的7遍隔行掃描方法)

用十六進位查看器打開一個 PNG 文件:

png-hex-ihdr.png

十六進位 說明
00 00 00 0D 數據塊長度 13 位元組
49 48 44 52 數據塊類型碼 「IHDR」 的 ASCII 字母
00 00 04 1D 影像寬度 1053
00 00 02 B3 影像高度 691
08 影像深度 8
06 帶α通道數據的真彩色圖
00 壓縮方法
00 濾波器方法
00 隔行掃描方法:00非隔行掃描
52 C3 75 3A CRC (循環冗餘檢測)

數據塊-調色板數據塊 PLTE

包含有與索引彩色影像(indexed-color image)相關的彩色變換數據,它僅與索引彩色影像有關,而且要放在影像數據塊(image data chunk)之前。

PLTE 數據塊是定義影像的調色板資訊,PLTE 可以包含 1~256 個調色板資訊,每一個調色板資訊由 3 個位元組組成:

顏色 位元組 意義
Red 1 byte 0 = 黑色, 255 = 紅
Green 1 byte 0 = 黑色, 255 = 綠色
Blue 1 byte 0 = 黑色, 255 = 藍色
  • 調色板的長度應該是 3 的倍數,否則,這將是一個非法的調色板。

  • 對於索引影像,調色板資訊是必須的,調色板的顏色索引從 0 開始編號,然後是 1、2……,調色板的顏色數不能超過色深中規定的顏色數(如影像色深為 4 的時候,調色板中的顏色數不可以超過 2^4=16),否則,這將導致 PNG 影像不合法。

  • 真彩色影像和帶 α 通道數據的真彩色影像也可以有調色板數據塊,目的是便於非真彩色顯示程式用它來量化影像數據,從而顯示該影像。

用十六進位查看器打開一個索引影像 PNG 文件:

png-indexed-color-hex-plte.png

十六進位 說明
00 00 00 27 數據塊長度 39 位元組
50 4C 54 45 數據塊類型碼 「PLTE」 的 ASCII 字母
B7 00 34 FF 99 00 60 00 73 FF 0F 00 FF ED 00 09 00 B2 FF 66 00 FF 3B 00 E2 00 15 8B 00 54 FF C1 00 33 00 99 FF FF 00 調色板顏色 13 個
48 29 75 2C CRC (循環冗餘檢測)

預覽調色板中的顏色:

indexed-color-plte-colors.png

數據塊-影像數據塊 IDAT

它存儲實際的數據,在數據流中可包含多個連續順序的影像數據塊。

IDAT 存放著影像真正的數據資訊,因此,如果能夠了解 IDAT 的結構,我們就可以很方便的生成 PNG 影像。

用十六進位查看器打開一個索引影像 PNG 文件:

png-hex-idat.png

十六進位 說明
00 00 00 D3 數據塊長度 211 位元組
49 44 41 54 數據塊類型碼 「IDAT」 的 ASCII 字母
78 9C …… 壓縮的數據 211 位元組,LZ77 派生壓縮方法
52 98 5D 9D CRC (循環冗餘檢測)

影像數據塊 IDAT 細節在本文下半部分有詳細分析

數據塊-影像結束數據 IEND

它用來標記 PNG 文件或者數據流已經結束,並且必須要放在文件的尾部。

如果我們仔細觀察 PNG 文件,我們會發現,文件的結尾 12 個字元看起來總應該是這樣的:

00 00 00 00 49 45 4E 44 AE 42 60 82

用十六進位查看器打開一個 PNG 文件:

png-hex-iend.png

由於數據塊結構的定義,IEND 數據塊的長度總 是 0(00 00 00 00,除非人為加入資訊),數據標識總是 IEND(49 45 4E 44),因此,CRC 碼也總是 AE 42 60 82。

影像數據塊 IDAT 細節

IDAT 壓縮數據細節

PNG Spec 壓縮演算法部分:

PNG compression method 0 is deflate/inflate compression with a sliding window (which is an upper bound on the distances appearing in the deflate stream) of at most 32768 bytes. Deflate compression is an LZ77 derivative [ZL].    Deflate-compressed datastreams within PNG are stored in the "zlib" format, which has the structure:    - zlib compression method/flags code    1 byte  - Additional flags/check bits   1 byte  - Compressed data blocks    n bytes  - Check value   4 bytes    Further details on this format are given in the zlib specification [RFC-1950].
  • PNG 使用 DEFLATE 壓縮演算法。
  • DEFLATE 是同時使用了 LZ77 演算法與哈夫曼編碼(Huffman Coding)的一個無損數據壓縮演算法。
  • DEFLATE 壓縮的數據以 zlib 格式存儲。
    zlib(RFC1950):一種格式,是對 deflate 進行了簡單的封裝,他也是一個實現庫(delphi中有zlib,zlibex)      gzip(RFC1952):一種格式,也是對 deflate 進行的封裝。        gzip = gzip 頭 + deflate 編碼的實際內容 + gzip 尾      zlib = zlib 頭 + deflate 編碼的實際內容 + zlib 尾

提取&解壓 IDAT 中壓縮數據

Windows 上可以用 [hexeditor](https://www.hhdsoftware.com/free-hex-editor)  Mac 上可以用 [hexfiend](http://ridiculousfish.com/hexfiend/)、[Hopper Disassembler](https://www.hopperapp.com/)

png-hex-idat-data-extract.png

使用 zlib 解壓 78 9C ...... 壓縮的數據位元組:

#include <stdio.h>  #include <stdlib.h>  #include <stdint.h>  #include "zlib.h"    int main() {      FILE *inFile = fopen("/Users/staff/Desktop/indexed-color-image.data", "rb");      FILE *outFile = fopen("/Users/staff/Desktop/indexed-color-image-uncompress.data", "wb");        fseek(inFile, 0L, SEEK_END);      long size = ftell(inFile);      fseek(inFile, 0L, SEEK_SET);        uint8_t dataBuf[size];      fread(dataBuf, size, 1, inFile);      printf("壓縮文件大小:%ldn", size);        uint8_t destBuf[1500000]={0};      uint32_t destLen = 0;        uncompress(destBuf, &destLen, dataBuf, size);      printf("解壓後大小:%dn", destLen);        fwrite(destBuf, destLen, 1, outFile);        fflush(outFile);      fclose(inFile);      fclose(outFile);        return 0;  }

png-hex-idat-uncompress.png
png-hex-idat-uncompress-contrast.png

分析解壓後的數據

PNG Spec 7.1 Integers and byte order

All integers that require more than one byte shall be in network byte order (as illustrated in figure 7.1): the most significant byte comes first, then the less significant bytes in descending order of significance (MSB LSB for two-byte integers, MSB B2 B1 LSB for four-byte integers). The highest bit (value 128) of a byte is numbered bit 7; the lowest bit (value 1) is numbered bit 0. Values are unsigned unless otherwise noted. Values explicitly noted as signed are represented in two's complement notation.
  • PNG 使用網路位元組序 大端位元組序(Big Endian)

PNG Spec 7.2 Scanlines

In PNG images of colour type 0 (greyscale) each pixel is a single sample, which may have precision less than a byte (1, 2, or 4 bits). These samples are packed into bytes with the leftmost sample in the high-order bits of a byte followed by the other samples for the scanline.    In PNG images of colour type 3 (indexed-colour) each pixel is a single palette index. These indices are packed into bytes in the same way as the samples for colour type 0.
  • PNG 影像深度小於 1 位元組,將會被 packed into bytes

PNG Spec 7.3 Filtering

PNG allows the scanline data to be filtered before it is compressed. Filtering can improve the compressibility of the data. The filter step itself results in a sequence of bytes of the same size as the incoming sequence, but in a different representation, preceded by a filter type byte. Filtering does not reduce the size of the actual scanline data. All PNG filters are strictly lossless.    Different filter types can be used for different scanlines, and the filter algorithm is specified for each scanline by a filter type byte. The filter type byte is not considered part of the image data, but it is included in the datastream sent to the compression step. An intelligent encoder can switch filters from one scanline to the next. The method for choosing which filter to employ is left to the encoder.
  • 每一掃描行前有一位元組用於指定過濾器類型

PNG Spec Filters:

Filtering transforms the PNG image with the goal of improving compression. PNG allows for a number of filter methods. All the reduced images in an interlaced image shall use a single filter method. Only filter method 0 is defined by this International Standard. Other filter methods are reserved for future standardization (see 4.9 Extension and registration). Filter method 0 provides a set of five filter types, and individual scanlines in each reduced image may use different filter types.  。。。。。。
  • 文件頭數據塊 IHDR 中 Filter method 過濾方法只能是 0。
  • Filter method=0 定義了 5 種 Filter Type 過濾器類型: 0:None1:Sub2:Up3:Average4:Paeth

當 PNG 圖片是索引影像時(下圖數據:影像深度: 4 尺寸 256X256 過濾器類型: 0:None 隔行掃描方法:0:非隔行掃描):

indexed-color-image.png

png-indexed-color-uncompress-data.png

  • 每個高亮區域前面一個位元組 00 代表 過濾器類型 : 0:NonePNG Spec 7.3 Filtering】【 PNG Spec Filters】。
  • 如果高亮區域前面一個位元組不是 00,高亮區將不是掃描行索引數據,需要參考 【PNG Spec 9.2 Filter types for filter method 0
  • 每種顏色高亮顯示的部分 128 位元組 是一個掃描行顏色索引數據,因為 影像深度:4,所以每個位元組代表兩個顏色索引 【PNG Spec 7.2 Scanlines】。
  • 如位元組 55 是十六進位,二進位為 01010101,前四 bit 位代表一個顏色索引 0101 十進位為 5,後四 bit 位代表一個顏色索引 0101 十進位為 5 。

當 PNG 圖片是真彩影像時(下圖數據:影像深度: 8 尺寸 70X70 過濾器類型: 0:None 隔行掃描方法:0:非隔行掃描):

true-color-image.png

png-true-color-uncompress-data.png

  • 每個高亮區域前面一個位元組 00 代表 過濾器類型 : 0:NonePNG Spec 7.3 Filtering】【 PNG Spec Filters】。
  • 如果高亮區域前面一個位元組不是 00,高亮區將不是掃描行顏色數據,需要參考 【PNG Spec 9.2 Filter types for filter method 0
  • 每種顏色高亮顯示的部分 210 位元組 是一個掃描行顏色數據,因為 真彩圖片 影像深度:8,所以每三個位元組代表一個像素顏色。
  • 如位元組 FF 00 00,代表一個像素 RGB 顏色。

More

下一步,將用程式碼手動生成一張 PNG 圖片,文章目錄:* 音影片入門文章目錄 *

所有可能存在問題的答案:PNG Specification


程式碼:
demos/demos-zlib

參考資料:

PNG 文件格式詳解

PNG、JPEG、BMP等幾種圖片格式詳解(一)—— PNG

詳解PNG文件結構

《PNG文件格式》(二)PNG文件格式分析

圖片知識梳理(一): PNG文件結構

隱寫技巧——利用PNG文件格式隱藏Payload

Portable Network Graphics (PNG) Specification and Extensions

gzip,deflate,zlib辨析

Zlib庫的安裝與使用

內容有誤?聯繫作者:

聯繫作者


本文由部落格一文多發平台 OpenWrite 發布!