PNG文件識別
之前寫過《JPEG/Exif/TIFF格式解讀(1):JEPG圖片壓縮與存儲原理分析》,JPEG文件是以,FFD8開頭,FFD9結尾,中間存儲着以0xFFE0~0xFFEF 為標志的數據段。
對於一個PNG文件來說,其文件頭總是由位固定的字節來描述的,HEX: 89 50 4E 47 0D 0A 1A 0A
其中第一個字節0x89超出了ASCII字符的范圍,這是為了避免某些軟件將PNG文件當做文本文件來處理。文件中剩余的部分由3個以上的PNG的數據塊(Chunk)按照特定的順序組成,因此,一個標准的PNG文件結構應該如下:
PNG文件標志 |
PNG數據塊 |
…… |
PNG數據塊 |
與JPEG格式如下
SOI Marker | Marker XX size=SSSS | Marker YY size=TTTT | SOS Marker size=UUUU | Image stream | EOI Marker | ||||||
FFD8 | FFXX | SSSS | DDDD...... | FFYY | TTTT | DDDD...... | FFDA | UUUU | DDDD.... | I I I I.... | FFD9 |
何其相似,但卻相差甚遠。png數據結構個人覺得比jpeg復雜
jpeg:段標識(FF)+段類型(1字節)+段長度(2字節)+數據塊
png:段長度(4字節)+段類型/類型標志(4字節)+數據塊+校驗碼(4字節)
png的每一段,稱之為數據塊。
PNG數據塊(Chunk)
PNG定義了兩種類型的數據塊,
-
關鍵數據塊(critical chunk),這是標准的數據塊
-
輔助數據塊(ancillary chunks),這是可選的數據塊。
關鍵數據塊定義了4個標准數據塊,每個PNG文件都必須包含它們,PNG讀寫軟件也都必須要支持這些數據塊。雖然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的所有數據,就需要解讀每個數據塊
PNG數據塊結構
PNG文件中,每個數據塊由4個部分組成,如下:
名稱 | 字節數 | 說明 |
Length (長度) | 4字節 | 指定數據塊中數據域的長度,其長度不超過(231-1)字節 |
Chunk Type Code (數據塊類型碼) | 4字節 | 數據塊類型碼由ASCII字母(A-Z和a-z)組成 |
Chunk Data (數據塊數據) | 可變長度 | 存儲按照Chunk Type Code指定的數據 |
CRC (循環冗余檢測) | 4字節 | 存儲用來檢測是否有錯誤的循環冗余碼 |
-
Length 值的是除:length 本身,Chunk Type Code,CRC 外的長度,也就是 Chunk Data 的長度。
-
CRC: 一種校驗算法。僅僅用來校驗數據的正確性的
CRC(cyclic redundancy check)域中的值是對Chunk Type Code域和Chunk Data域中的數據進行計算得到的。
CRC具體算法定義在ISO 3309和ITU-T V.42中,其值按下面的CRC碼生成多項式進行計算:
x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
下面,我們依次來了解一下各個關鍵數據塊的結構吧。
文件頭數據塊IHDR
文件頭數據塊IHDR(header chunk):它包含有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 |
Comdivssion method | 1 byte | 壓縮方法(LZ77派生算法) |
Filter method | 1 byte | 濾波器方法 |
Interlace method | 1 byte | 隔行掃描方法: 0:非隔行掃描 1: Adam7(由Adam M. Costello開發的7遍隔行掃描方法) |
由於我們研究的是手機上的PNG,因此,首先我們看看MIDP1.0對所使用PNG圖片的要求吧:
-
在MIDP1.0中,我們只可以使用1.0版本的PNG圖片。並且,所以的PNG關鍵數據塊都有特別要求:IHDR
-
文件大小:MIDP支持任意大小的PNG圖片,然而,實際上,如果一個圖片過大,會由於內存耗盡而無法讀取。
-
ColorType/顏色類型:所有顏色類型都有被支持,雖然這些顏色的顯示依賴於實際設備的顯示能力。同時,MIDP也能支持alpha通道,但是,所有的alpha通道信息都會被忽略並且當作不透明的顏色對待。
-
Bit depth/色深:所有的色深都能被支持。
-
Comdivssion method/壓縮方法:僅支持壓縮方式0(deflate壓縮方式),這和jar文件的壓縮方式完全相同,所以,PNG圖片數據的解壓和jar文件的解壓可以使用相同的代碼。(其實這也就是為什么J2ME能很好的支持PNG圖像的原因:))
-
Filter method/濾波器方法:盡管在PNG的白皮書中僅定義了方法0,然而所有的5種方法都被支持!
-
隔行掃描:雖然MIDP支持0、1兩種方式,然而,當使用隔行掃描時,MIDP卻不會真正的使用隔行掃描方式來顯示。
-
PLTE chunk:支持
-
IDAT chunk:圖像信息必須使用5種過濾方式中的方式0 (None, Sub, Up, Average, Paeth)
-
IEND chunk:當IEND數據塊被找到時,這個PNG圖像才認為是合法的PNG圖像。
-
可選數據塊:MIDP可以支持下列輔助數據塊,然而,這卻不是必須的。
關於更多的信息,可以參考http://www.w3.org/TR/REC-png.html
用十六進制查看器打開一個 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
調色板數據塊PLTE(palette chunk)包含有與索引彩色圖像(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 文件:
分析如下:
十六進制 | 說明 |
---|---|
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 (循環冗余檢測) |
pHYs
物理像素數據塊,它表示了圖片的像素尺寸,或者是高寬比,它的結果如下
Pixels per unit, X axis | 4 bytes (PNG unsigned integer) |
---|---|
Pixels per unit, Y axis |
4 bytes (PNG unsigned integer) |
Unit specifier |
1 byte |
unit specifier的定義如下:
-
0:unit is unknown
-
1:unit is the metre
數據塊-圖像數據塊 IDAT
圖像數據塊IDAT(image data chunk):它存儲實際的數據,在數據流中可包含多個連續順序的圖像數據塊。
IDAT存放着圖像真正的數據信息,因此,如果能夠了解IDAT的結構,我們就可以很方便的生成PNG圖像。
用十六進制查看器打開一個索引圖像 PNG 文件:
十六進制 | 說明 |
---|---|
00 00 00 D3 | 數據塊長度 211 字節 |
49 44 41 54 | 數據塊類型碼 “IDAT” 的 ASCII 字母 |
78 9C ...... | 壓縮的數據 211 字節,LZ77 派生壓縮方法 |
52 98 5D 9D | CRC (循環冗余檢測) |
數據塊-圖像結束數據IEND
圖像結束數據IEND(image trailer chunk):它用來標記PNG文件或者數據流已經結束,並且必須要放在文件的尾部。
如果我們仔細觀察PNG文件,我們會發現,文件的結尾12個字符看起來總應該是這樣的:
00 00 00 00 49 45 4E 44 AE 42 60 82
不難明白,由於數據塊結構的定義,IEND數據塊的長度總是0(00 00 00 00,除非人為加入信息),數據標識總是IEND(49 45 4E 44),因此,CRC碼也總是AE 42 60 82。
IHDR cHRM pHYs IEND
至此,我們已經能夠從一個PNG文件中識別出各個數據塊了。由於PNG中規定除關鍵數據塊外,其它的輔助數據塊都為可選部分,因此,有了這個標准后,我們可以通過刪除所有的輔助數據塊來減少PNG文件的大小。(當然,需要注意的是,PNG格式可以保存圖像中的層、文字等信息,一旦刪除了這些輔助數據塊后,圖像將失去原來的可編輯性。)
刪除了輔助數據塊后的PNG文件,現在文件大小為147字節,原文件大小為261字節,文件大小減少后,並不影響圖像的內容。
其實,我們可以通過改變調色板的色值來完成一些又趣的事情,比如說實現雲彩/水波的流動效果,實現圖像的淡入淡出效果等等,在此,給出一個鏈接給大家看也許更直接:http://blog.csdn.net/flyingghost/archive/2005/01/13/251110.aspx,我寫此文也就是受此文的啟發的。
如上說過,IDAT數據塊是使用了LZ77壓縮算法生成的,由於受限於手機處理器的能力,因此,如果我們在生成IDAT數據塊時仍然使用LZ77壓縮算法,將會使效率大打折扣,因此,為了效率,只能使用無壓縮的LZ77算法,關於LZ77算法的具體實現,此文不打算深究,如果你對LZ77算法的JAVA實現有興趣,可以參考以下兩個站點:
-
http://jazzlib.sourceforge.net/
參考文章:
音視頻入門-11-PNG文件格式詳解 https://www.cnblogs.com/binglingziyu/p/audio-video-basic-11-png-file-format-detail.html
PNG文件結構 https://www.cnblogs.com/Yuuki-/p/7868858.html
http://read.newbooks.com.cn/info/173555.html
轉載本站文章《PNG文件解讀(2):PNG格式文件結構與數據結構解讀—解碼PNG數據》,
請注明出處:https://www.zhoulujun.cn/html/theory/multimedia/CG-CV-IP/8410.html