轉至:http://blog.csdn.net/carson2005/article/details/7875416
(1)讀入JPEG/JFIF文件的相關信息
按照JFIF文件格式,將JPEG文件相關的字段信息一一讀取出來,並進行相應的解析。例如,圖像的寬度、高度、量化表、Huffman表、水平/垂直採樣因子等。一般而言,JFIF格式文件的讀取順序依次爲:
SOI字段;
APP0字段;
APPn字段;
DQT字段;
SOFO字段;
DHT字段;
SOS字段;
壓縮數據字段;
EOI字段;
讀取JPEG文件相關信息的時候,有兩點需要特別注意:
(a)由於JPEG中以0XFF來做爲特殊標記符,因此,如果某個像素的取值爲0XFF,那麼實際在保存的時候,是以0XFF00來保存的,從而避免其跟特殊標記符0XFF之間產生混淆。所以,在讀取文件信息的時候,如果遇0XFF00,就必須去除後面的00;即,將0XFF00當做0XFF;
(b)JPEG文件中,一個字(16位)的存儲是採用了Motorola格式(big-endian),而不是我們常用的Intel格式(little-endian)。因此,如果需要的話,請在處理之間進行依次高低字節的轉換。
(2) 讀取Huffman表
在標記碼DHT之後,包含了一個或者多個Huffman表(通常是4個表)。對於一個Huffman表而言,它包含了以下三部分內容:
(a)表ID和表類型;1個字節;僅有4個可選的取值,0X00,0X01,0X10,0X11,分別表示DC直流0號表,DC直流1號表,AC交流0號表,AC交流1號表;
(b)不同位數的碼字數量;前面提到,JPEG中的Huffman編碼表是按照編碼長度的位數以表格的形式保存的,而且,Huffman編碼表的位數只能是1--16位,因此,這裏用16個字節來分別表示1--16位的每種位長的編碼在Huffman樹中的個數。
(c)編碼內容;該字段記錄了Huffman樹中各個葉子節點的權重,上一個字段(不同位數的碼字數量)的16個數值之和,就是本字段的長度,也就是Huffman樹中葉子節點的個數。
這裏,我們不妨以下面一段Huffman表的數據爲例來說明情況(均以16進製表示):
11 00 02 02 00 05 01 06 01 00 00 00 00 00 00 00 00
00 01 11 02 21 03 31 41 12 51 61 71 81 91 22 13 32
以上數據串中第一行代表了Huffman表ID、表類型、不同位數的碼字數量信息;
第一行的第一個字節0X11代表了表的ID和類型是AC交流1號表;
第一行的第2到第17字節代表了不同位數碼字的數量。即,第2個字節00表示沒有位數爲1的編碼;第3個和第4個字節的02表示位數爲2和位數爲3的編碼各有兩個;第5個字節的00表示沒有位數爲5的編碼。。。。此外,通過這些數據我們發現,此Huffman樹有0+2+2+0+5+1+6+1=17個葉子節點。
第二行爲編碼的內容,表明17個葉子節點按照從小到大的順序排列,即,權值依次爲0,1,11,2,21,3,31,41...
(3) 構建Huffman樹
讀取到Huffman表的數據之後,就需要構建Huffman樹了。其具體規則如下
(a)第一個編碼的數字必定爲0;如果第一個編碼的位數爲1,就被編碼爲0;如果第一個編碼的位數爲2,就被編碼爲00;如果第一個編碼的位數爲3,就被編碼爲000。。。
(b)從第二個編碼開始,如果它和它前面編碼具有相同的位數,則當前編碼是它前面的編碼加1;如果它的編碼位數比它前面的編碼位數大,則當前編碼時它前面的編碼加1之後再在後面添加若干個0,直到滿足編碼位數的長度爲止。
還是以上面的數據爲例:
第一行的第2個字節00表示沒有位數爲1的編碼;
第一行的第3個字節02表示位數爲2的編碼有2個;由於沒有位數爲1的編碼,因此這裏位數爲2的編碼中的第一個爲00,第二個爲00+1=01;
第一行的第4個字節02表示位數爲3的編碼有2個;因此,這裏位數爲3的編碼中的第一個爲01+1=10,然後添加1個“0”,得到100;位數爲3的編碼中的第二個爲100+1=101;
依次類推,可以得到如下的Huffman樹
序號 |
碼字長度 |
碼字 |
權值 |
1 |
2 |
00 |
0x00 |
2 |
2 |
01 |
0x01 |
3 |
3 |
100 |
0x11 |
4 |
3 |
101 |
0x02 |
5 |
5 |
11000 |
0x21 |
6 |
5 |
11001 |
0x03 |
7 |
5 |
11010 |
0x31 |
8 |
5 |
11011 |
0x41 |
9 |
5 |
11100 |
0x12 |
10 |
6 |
111010 |
0x51 |
11 |
7 |
1110110 |
0x61 |
12 |
7 |
1110111 |
0x71 |
13 |
7 |
1111000 |
0x81 |
14 |
7 |
1111001 |
0x91 |
15 |
7 |
1111010 |
0x22 |
16 |
7 |
1111011 |
0x13 |
17 |
8 |
11111000 |
0x32 |
特別提醒的是,如果中間有某個位數的編碼缺失,例如,沒有4位的編碼,則應該在3位的編碼後面加1,添加2個“00”補足5位,形成下一個5位編碼。
(4) DC係數的Huffman解碼
JPEG編碼階段我們講到,DC係數是以(A,B)的中間形式進行編碼的。其中的A代表了B的二進制編碼位數,B則利用VLI進行編碼。另外,8*8的圖像塊經過DCT變換之後得到的8*8的係數矩陣,經過Huffman編碼及RLE編碼之後,寫入編碼數據的時候,DC係數也是被寫在數據流最前面的。因此,解碼的時候,DC係數也是最先被讀取出來,假設,我們一次性讀入了若干個字節長度的數據。其中的第一個字節代表了DC係數的Huffman編碼,通過查找DC係數的Huffman表(亮度表或色度表),得到該Huffman編碼所在的組編號,該編號就是DC係數中間格式(A,B)中的A,也就是B的位數。例如,A=2,就代表B採用2位二進制數進行編碼。這樣一來,讀取接下來的A位二進制數,將其譯碼爲十進制,就得到了DC係數的差值。將該差值與上一個DC係數值相加,就得到了真正的當前DC係數的值。
(5) AC係數的Huffman解碼
處理完DC係數之後,接下來進行AC係數的譯碼工作,顯然,這裏依然需要讀取一個Huffman編碼,通過查找AC係數的Huffman編碼表,進行解碼,我們得到(A,B)的數據對,其中的A代表了0的個數,而B則代表了後面數據的位數。例如,(2,3)就代表了當前AC係數之前有2個0,下一個需要讀取的二進制數據是3位。需要提醒的是,(0,0)代表EOB,即8*8塊的編碼結束。接着,讀取B位二進制數據,進行譯碼,我們就得到了AC係數的值。如此反覆循環,直到遇到EOB,或者讀取了63個AC係數,我們就完成了一個8*8塊的係數矩陣的譯碼工作。
(6) 反量化
在譯碼得到了8*8的係數矩陣之後,我們需要進行反量化工作。該步驟,就是將前一個步驟得到的8*8係數矩陣分別乘以8*8的量化矩陣即可。
(7) 反Zig-zag掃描
JPEG編碼過程中,爲了編碼方便,採用了Zig-zag掃描,因此,這裏需要進行反Zig-zag掃描,重新排列8*8的反量化係數矩陣。反Zig-zag掃描的輸入時8*8矩陣,輸出依然是8*8矩陣,只不過,數據的排列方式有所不同而已。
(8) DCT逆變換
DCT變換,將原始圖像變換到頻域,而DCT逆變換,就是要將數據從頻域變換回時域。
DCT逆變換的計算公式爲:
DCT逆變換的公式,可以改寫爲:
其中A爲矩陣:
左邊爲未轉秩的數據順序,右邊爲轉秩之後的數據順序。
(9)顏色模式轉換
BMP圖片是以RGB顏色空間進行保存的,因此,將JPEG解碼爲BMP必須進行顏色模式的轉換。另外,由於DCT要求的定義域對稱,所以,在編碼的時候將RGB的數值範圍從[0,255]統一減去128,將數值範圍轉換到[-128,127]的範圍內。因此,解碼的時候,必須爲每個顏色分量加上128。另外需要注意的是,通過解碼變換之後得到的RGB的值有可能超過255或者小於0;如果小於0,就截斷爲0,如果大於255,就截取爲255;