JPEG(Joint Photographic Experts Group)是聯合圖像專家小組的英文縮寫。它由國際電話與電報咨詢委員會CCITT(The International Telegraph and Telephone Consultative Committee)與國際標准化組織ISO於1986年聯合成立的一個小組,負責制定靜態數字圖像的編碼標准。
小組一直致力於標准化工作,開發研制出連續色調、多級灰度、靜止圖像的數字圖像壓縮編碼 方法,即JPEG算法。JPEG算法被確定為國際通用標准,其適用范圍廣泛,除用於靜態圖像編碼外,還推廣到電視圖像序列的幀內圖像壓縮。而用JPEG算 法壓縮出來的靜態圖片文件稱為JPEG文件,擴展名通常為*.jpg、*.jpe*.jpeg。
JPEG專家組開發了兩種基本的壓縮算法、兩種數據編碼方法、四種編碼模式。具體如下:
壓縮算法:
l 有損的離散余弦變換(Discrete Cosine Transform,DCT);
l 無損的預測技術壓縮。
數據編碼方法:
l 哈夫曼編碼;
l 算術編碼;
編碼模式:
l 基於DCT順序模式:編/解碼通過一次掃描完成;
l 基於DCT遞進模式:編/解碼需要多次掃描完成,掃描效果從粗糙到精細,逐級遞進;
l 無損模式:基於DPCM,保證解碼后完全精確恢復到原圖像采樣值;
l 層次模式:圖像在多個空間多種分辨率進行編碼,可以根據需要只對低分辨率數據作解碼,放棄高分辨率信息。
在實際應用中,JPEG圖像使用的是離散余弦變換、哈夫曼編碼、順序模式。
JPEG壓縮編碼算法的主要計算步驟如下:
(0) 8*8分塊。
(1) 正向離散余弦變換(FDCT)。
(2) 量化(quantization)。
(3) Z字形編碼(zigzag scan)。
(4) 使用差分脈沖編碼調制(DPCM)對直流系數(DC)進行編碼。
(5) 使用行程長度編碼(RLE)對交流系數(AC)進行編碼。
(6) 熵編碼。
筆者在實踐過程中查閱了大量的資料,發現大多數書籍資料和網上資料都是從編碼角度分析 JPEG的編/解碼方式,並且都只是介紹編碼過程中的主要方法。所以,本文從解碼角度詳細分析JPEG的編/解碼過程,並且加入許多筆者實踐過程中遇到的 問題和解決方法,希望從另一個角度說明問題,以更好幫助讀者結合其他資料解決問題。
不過,介紹解碼過程之前,首先要了解JPEG文件中數據的存儲格式。
一、JPEG文件格式介紹
JPEG文件使用的數據存儲方式有多種。最常用的格式稱為JPEG文件交換格式(JPEG File Interchange Format,JFIF)。而JPEG文件大體上可以分成兩個部分:標記碼(Tag)和壓縮數據。
標記碼由兩個字節構成,其前一個字節是固定值0xFF,后一個字節則根據不同意義有不同 數值。在每個標記碼之前還可以添加數目不限的無意義的0xFF填充,也就說連續的多個0xFF可以被理解為一個0xFF,並表示一個標記碼的開始。而在一 個完整的兩字節的標記碼后,就是該標記碼對應的壓縮數據流,記錄了關於文件的諸種信息。
常用的標記有SOI、APP0、DQT、SOF0、DHT、DRI、SOS、EOI。
注意,SOI等都是標記的名稱。在文件中,標記碼是以標記代碼形式出現。例如SOI的標記代碼為0xFFD8,即在JPEG文件中的如果出現數據0xFFD8,則表示此處為一個SOI標記。
本文附錄列出一張完整的JPEG定義的標記表,供讀者查閱。這里僅列出幾個常用標記的標記代碼、占用字節長度和表示的意義。
u 標記代碼 2字節 固定值0xFFD8
u 標記代碼 2字節 固定值0xFFE0
u 包含9個具體字段:
① 數據長度 2字節 ①~⑨9個字段的總長度
即不包括標記代碼,但包括本字段
② 標識符 5字節 固定值0x4A46494600,即字符串“JFIF0”
③ 版本號 2字節 一般是0x0102,表示JFIF的版本號1.2
可能會有其他數值代表其他版本
④ X和Y的密度單位 1字節 只有三個值可選
0:無單位;1:點數/英寸;2:點數/厘米
⑤ X方向像素密度 2字節 取值范圍未知
⑥ Y方向像素密度 2字節 取值范圍未知
⑦ 縮略圖水平像素數目 1字節 取值范圍未知
⑧ 縮略圖垂直像素數目 1字節 取值范圍未知
⑨ 縮略圖RGB位圖 長度可能是3的倍數 縮略圖RGB位圖數據
本標記段可以包含圖像的一個微縮版本,存為24位的RGB像素。如果沒有微縮圖像(這種情況更常見),則字段⑦“縮略圖水平像素數目”和字段⑧“縮略圖垂直像素數目”的值均為0。
l APPn,Application,應用程序保留標記n,其中n=1~15(任選)
u 標記代碼 2字節 固定值0xFFE1~0xFFF
u 包含2個具體字段:
① 數據長度 2字節 ①~②2個字段的總長度
即不包括標記代碼,但包括本字段
② 詳細信息 數據長度-2字節 內容不定
例如,Adobe Photoshop生成的JPEG圖像中就用了APP1和APP13兩個標記段分別存儲了一幅圖像的副本。
l DQT,Define Quantization Table,定義量化表
u 標記代碼 2字節 固定值0xFFDB
u 包含9個具體字段:
① 數據長度 2字節 字段①和多個字段②的總長度
即不包括標記代碼,但包括本字段
② 量化表 數據長度-2字節
a) 精度及量化表ID 1字節 高4位:精度,只有兩個可選值
0:8位;1:16位
低4位:量化表ID,取值范圍為0~3
b) 表項 (64×(精度+1))字節 例如8位精度的量化表
其表項長度為64×(0+1)=64字節
本標記段中,字段②可以重復出現,表示多個量化表,但最多只能出現4次。
u 標記代碼 2字節 固定值0xFFC0
u 包含9個具體字段:
① 數據長度 2字節 ①~⑥六個字段的總長度
即不包括標記代碼,但包括本字段
② 精度 1字節 每個數據樣本的位數
通常是8位,一般軟件都不支持 12位和16位
③ 圖像高度 2字節 圖像高度(單位:像素),如果不支持 DNL 就必須 >0
④ 圖像寬度 2字節 圖像寬度(單位:像素),如果不支持 DNL 就必須 >0
⑤ 顏色分量數 1字節 只有3個數值可選
1:灰度圖;3:YCrCb或YIQ;4:CMYK
而JFIF中使用YCrCb,故這里顏色分量數恆為3
⑥顏色分量信息 顏色分量數×3字節(通常為9字節)
a) 顏色分量ID 1字節
b) 水平/垂直采樣因子 1字節 高4位:水平采樣因子
低4位:垂直采樣因子
(曾經看到某資料把這兩者調轉了)
c) 量化表 1字節 當前分量使用的量化表的ID
本標記段中,字段⑥應該重復出現,有多少個顏色分量(字段⑤),就出現多少次(一般為3次)。
l DHT,Difine Huffman Table,定義哈夫曼表
u 標記代碼 2字節 固定值0xFFC4
u 包含2個具體字段:
①數據長度 2字節 字段①和多個字段②的總長度
即不包括標記代碼,但包括本字段
② 哈夫曼表 數據長度-2字節
a) 表ID和表類型 1字節 高4位:類型,只有兩個值可選
0:DC直流;1:AC交流
低4位:哈夫曼表ID,
注意,DC表和AC表分開編碼
b) 不同位數的碼字數量 16字節
c) 編碼內容 16個不同位數的碼字數量之和(字節)
本標記段中,字段②可以重復出現(一般4次),也可以致出現1次。例如,Adobe Photoshop 生成的JPEG圖片文件中只有1個DHT標記段,里邊包含了4個哈夫曼表;而Macromedia Fireworks生成的JPEG圖片文件則有4個DHT標記段,每個DHT標記段只有一個哈夫曼表。
l DRI,Define Restart Interval,定義差分編碼累計復位的間隔
u 標記代碼 2字節 固定值0xFFDD
u 包含2個具體字段:
①數據長度 2字節 固定值0x0004,①~②兩個字段的總長度
即不包括標記代碼,但包括本字段
②MCU塊的單元中的重新開始間隔
2字節 設其值為n,則表示每n個MCU塊就有一個
RSTn標記。第一個標記是RST0,第二個是
RST1等,RST7后再從RST0重復。
如果沒有本標記段,或間隔值為0時,就表示不存在重開始間隔和標記RST
u 標記代碼 2字節 固定值0xFFDA
u 包含2個具體字段:
①數據長度 2字節 ①~④兩個字段的總長度
即不包括標記代碼,但包括本字段
②顏色分量數 1字節 應該和SOF中的字段⑤的值相同,即:
1:灰度圖是;3: YCrCb或YIQ;4:CMYK。
而JFIF中使用YCrCb,故這里顏色分量數恆為3
③顏色分量信息
a) 顏色分量ID 1字節
b) 直流/交流系數表號 1字節 高4位:直流分量使用的哈夫曼樹編號
低4位:交流分量使用的哈夫曼樹編號
④ 壓縮圖像數據
a)譜選擇開始 1字節 固定值0x00
b)譜選擇結束 1字節 固定值0x3F
c)譜選擇 1字節 在基本JPEG中總為00
本標記段中,字段③應該重復出現,有多少個顏色分量(字段②),就出現多少次(一般為3次)。本段結束后,緊接着就是真正的圖像信息了。圖像信息直至遇到一個標記代碼就自動結束,一般就是以EOI標記表示結束。
u 標記代碼 2字節 固定值0xFFD9
這里補充說明一下,由於在JPEG文件中0xFF具有標志性的意思,所以在壓縮數據流 (真正的圖像信息)中出現0xFF,就需要作特別處理。具體方法是,在數據0xFF后添加一個沒有意義的0x00。換句話說,如果在圖像數據流中遇到 0xFF,應該檢測其緊接着的字符,如果是
1)0x00,則表示0xFF是圖像流的組成部分,需要進行譯碼;
2)0xD9,則與0xFF組成標記EOI,則圖像流結束,同時圖像文件結束;
3)0xD0~0xD7,則組成RSTn標記,則要忽視整個RSTn標記,即不對當前0xFF和緊接的0xDn兩個字節進行譯碼,並按RST標記的規則調整譯碼變量;
3)0xFF,則忽視當前0xFF,對后一個0xFF再作判斷;
4)其他數值,則忽視當前0xFF,並保留緊接的此數值用於譯碼。
二、 JPEG解碼過程詳解
下面來詳細講述JPEG文件的解碼過程。
1.讀入文件的相關信息
按照上述的JPEG文件數據存儲方式,把要解碼的文件的相關信息一一讀出,為接下來的解 碼工作做好准備。參考方法是,設計一系列的結構體對應各個標記,並存儲標記內表示的信息。其中圖像長寬、多個量化表和哈夫曼表、水平/垂直采樣因子等多項 信息比較重要。以下給出讀取過程中的兩個問題。
1)整個文件的大體結構
JFIF格式的JPEG文件(*.jpg)的一般順序為:
SOI(0xFFD8)
APP0(0xFFE0)
[APPn(0xFFEn)]可選
DQT(0xFFDB)
SOF0(0xFFC0)
DHT(0xFFC4)
SOS(0xFFDA)
壓縮數據
EOI(0xFFD9)
2)字的高低位問題
JPEG文件格式中,一個字(16位)的存儲使用的是 Motorola 格式, 而不是 Intel 格式。也就是說, 一個字的高字節(高8位)在數據流的前面, 低字節(低8位)在數據流的后面,與平時習慣的Intel格式不一樣。.
3)讀出哈夫曼表數據
a)理論說明
在標記段DHT內,包含了一個或者多個的哈夫曼表。對於單一個哈夫曼表,應該包括了三部分:
l 哈夫曼表ID和表類型
這個字節的值為一般只有四個0x00、0x01、0x10、0x11。
0x00表示DC直流0號表;
0x01表示DC直流1號表;
0x10表示AC交流0號表;
0x11表示AC交流1號表。
l 不同位數的碼字數量
JPEG文件的哈夫曼編碼只能是1~16位。這個字段的16個字節分別表示1~16位的編碼碼字在哈夫曼樹中的個數。
l 編碼內容
這個字段記錄了哈夫曼樹中各個葉子結點的權。所以,上一字段(不同位數的碼字數量)的16個數值之和就應該是本字段的長度,也就是哈夫曼樹中葉子結點個數。
b)舉例說明
以下面一段哈夫曼表數據舉例說明(數據全部以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
紅色部分(第1字節)為哈夫曼表ID和表類型,其值0x11表示此部分數據描述的是AC交流1號表。
藍色部分(2~17字節)為不同位數的碼字的數量。這16個數值實際意義為:沒有1位和4位的哈夫曼碼字;2位和3位的碼字各有2個;5位碼字有5個;6位和8位碼字各有1個;7位碼字各有6個;沒有9位或以上的碼字。
綠色部分(18~34字節)為編碼內容。由藍色部分數據知道,此哈夫曼樹有0+2+2+0+5+1+6+1=17個葉子結點,即本字段應該有17個字節。這段數據表示17個葉子結點按從小到大排列,其權值依次為0、1、11、2、21、3、31、41……
4)建立哈夫曼樹
a)理論說明
在讀出哈夫曼表的數據后,就要建立哈夫曼樹。具體方法為:
1)第一個碼字必定為0。
如果第一個碼字位數為1,則碼字為0;
如果第一個碼字位數為2,則碼字為00;
如此類推。
2)從第二個碼字開始,
如果它和它前面的碼字位數相同,則當前碼字為它前面的碼字加1;
如果它的位數比它前面的碼字位數大,則當前碼字是前面的碼字加1后再在后邊添若干個0,直至滿足位數長度為止。
b)舉例說明
繼續以上邊的例子說明問題。
n 由於沒有1位的碼字,所以第一個碼字的位數為2,即碼字為00;
n 由於2位的碼字有兩個,所以第二個碼字位數仍為2,即碼字為00+1=01;
n 第三個碼字為3位,比第二個碼字長1位,所以第三個碼字為:01+1=10,然后再添1個“0”,得100;
n ……
如此類推,最后得到這個哈夫曼樹如下:
序號 |
碼字長度 |
碼字 |
權值 |
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后,添加“00”補足5位,形成下一個5位碼字。
在准備好所有的圖片信息后,就可以對圖片數據進行解碼了。
1)理論說明
分析圖像數據流的結構,筆者准備以一個從宏觀到微觀的順序為讀者詳細剖析,即:
數據流à最小編碼單元à數據單元與顏色分量。
a) 在圖片像素數據流中,信息可以被分為一段接一段的最小編碼單元(Minimum Coded Unit,MCU)數據流。所謂MCU,是圖像中一個正方矩陣像素的數據。
矩陣的大小是這樣確定的:
查閱標記SOF0,可以得到圖像不同顏色分量的采樣因子,即Y、Cr、Cb三個分量各自 的水平采樣因子和垂直采樣因子。大多圖片的采樣因子為4:1:1或1:1:1。其中,4:1:1即(2*2):(1*1):(1*1));1:1:1即 (1*1):(1*1):(1*1)。記三個分量中水平采樣因子最大值為Hmax,垂直采樣因子最大值為Vmax,那么單個MCU矩陣的寬就是 Hmax*8像素,高就是Vmax*8像素。
如果,整幅圖像的寬度和高度不是MCU寬度和高度的整數倍,那么編碼時會用某些數值填充進去,保證解碼過程中MCU的完整性(解碼完成后,可直接忽視圖像寬度和高度外的數據)。
在數據流中,MCU的排列方法是從左到右,從上到下。
b) 每個MCU又分為若干個數據單元。數據單元的大小必定為8*8,所以每個MCU的數據單元個數為Hmax*Vmax。
另外JPEG的壓縮方法與BMP文件有所不同,它不是把每個像素的顏色分量連續存儲在一起的,而是把圖片分成Y,Cr,Cb三張子圖,然后分別壓縮。而三個顏色分量的采樣密度(即采樣因子)可能一樣(例如1:1:1)也可能不一樣(例如4:1:1)。
每個MCU內部,數據的順序是Y、Cr、Cb。如果一個顏色分量有多個數據單元,則順序是從左到右,從上到下。
2)舉例說明
下面通過一幅32*35的圖像,對上面兩個問題列出兩種采樣因子的具體說明。
圖 1 整張完整的圖像(4:1:1) 圖 2 將圖像的MCU1放大
圖1及圖3中灰色部分為實際圖像大小(32px*35px);粗虛線表示各個MCU的分界;細虛線表示MCU內部數據單元的分界。
a) 采樣因子為4:1:1
此時,Hmax=max(2,1,1)=2,Vmax=max(2,1,1)=2。所以,MCU的寬為Hmax*8=16像素,高為Vmax*8=16像素。圖像實際的寬剛好是2個MCU,但高則稍稍大於2個MCU的高度,所以要補足3行MCU。
在數據流中,MCU的順序是MCU1àMCU2àMCU3àMCU4àMCU5àMCU6。
每個MCU又分為Hmax*Vmax=2*2=4個數據單元。由於采樣因子是 4:1:1,即(2*2):(1*1):(1*1),所以Y分量的水平和垂直方向都是每2個像素采樣2次;Cr分量和Cb分量的水平和垂直方向都是每2個 像素采樣1次。因此,在一個MCU來里邊,Y分量有256個采樣點,即4個完整的數據單元;Cr分量和Cb分量各自只有64個采樣點。
如果以MCU1說明MCU數據的次序,則依次為Y1 、Y2 、Y5 、Y6 、Cr1256 、Cb1256 。圖2中全部256個點均是Y的采樣點,紅色部分為Cr分量和Cr分量的采樣點。
換句話說,對於整張圖片來說,數據流的數據依次是:
[Y1 、Y2 、Y5 、Y6 、Cr1256 、Cb1256] 、[Y3 、Y4 、Y7 、Y8 、Cr3478 、Cb3478] 、[Y9 、Y10 、Y13 、Y14 、Cr9101314、Cb9101314 ]、……
圖 3 整張完整的圖像(1:1:1)
b) 采樣因子為1:1:1
如圖3。Hmax=max(1,1,1)=1,Vmax=max(1,1,1)=2。所以,MCU的寬為Hmax*8=8像素,高為Vmax*8=8像素。圖像實際的寬剛好是4個MCU,但高則稍稍大於4個MCU的高度,所以要補足5行MCU。
在數據流中,MCU的順序是:
MCU1àMCU2àMCU3àMCU4à ………… àMCU18àMCU19àMCU20。
每個MCU又分為Hmax*Vmax=1*1=1個數據單元,也就是一個數據單元就是一 個MCU。由於采樣因子是1:1:1,即(1*1):(1*1):(1*1),所以Y分量、Cr分量和Cb分量的水平和垂直方向都是每1個像素采樣1次, 也就是圖象的每一個像素都是采樣點。因此,在一個MCU來里邊,Y分量、Cr分量和Cb分量各自有64個采樣點。有
因此,對於整張圖片來說,數據流的數據依次是:
[Y1 、Cr1、Cb1] 、[Y2 、Cr2 、Cb2] 、[Y3 、Cr3、Cb3] 、………… [Y19 、Cr19、Cb19]、[Y20 、Cr20、Cb20]。
3.顏色分量單元的內部解碼
1)理論說明
“顏色分量單元”是筆者為說明問題而建立的概念,指的是MCU中某個顏色分量中的一個8*8數據塊,例如上面提到的Y1 、Cr1、Cb1 都是一個顏色分量單元。
圖像數據流是以位(bit)為單位存儲信息的。並且內部的數據都是在編碼時通過正向離散余弦變換(FDCT)進行時空域向頻率域變換而得到的結果,所以對於每個顏色分量單元都應該由兩部分組成:1個直流分量和63個交流分量。
解碼的過程其實就是哈夫曼樹的查找過程。
首先查閱標記段SOS中的顏色分量信息,可以得出各個顏色分量對應使用的直流分量和交流分量使用的哈夫曼樹編號。一般來說,
Y分量:直流分量:直流0號哈夫曼樹,交流分量:交流0號哈夫曼樹;
Cr分量:直流分量:直流1號哈夫曼樹,交流分量:交流1號哈夫曼樹;
Cb分量:直流分量:直流1號哈夫曼樹,交流分量:交流1號哈夫曼樹。
顏色分量單元內部綜合運用了RLE行程編碼和哈夫曼編碼來壓縮數據。每個像素的數據流由兩部分構成:編碼和數值,並且兩者基本以互相隔開方式出現(除非該編碼的權值為零)。具體讀入單個顏色分量單元的步驟如下:
a)從此顏色分量單元數據流的起點開始一位一位的讀入,直到讀入的編碼與該分量直流哈夫曼樹的某個碼字(葉子結點)一致,然后用直流哈夫曼樹查得該碼字對應的權值。權值(共8位)表示該直流分量數值的二進制位數,也就是接下來需要讀入的位數。
b)繼續讀入位數據,直到讀入的編碼與該分量交流哈夫曼樹的某個碼字(葉子結點)一致,然后用交流哈夫曼樹查得該碼字對應的權值。權值的高4位表示當前數值前面有多少個連續的零,低4位表示該交流分量數值的二進制位數,也就是接下來需要讀入的位數。
c)不斷重復步驟b,直到滿足交流分量數據結束的條件。而結束條件有兩個,只要滿足其中一個即可:
①當讀入碼字的權值為零,表示往后的交流變量全部為零;
②已經讀入63個交流分量。
d)各個數值的譯碼是按下表進行的:
實際數值 |
編碼長度 |
編碼 |
0 |
0 |
- |
-1,1 |
1 |
0,1 |
-3,-2,2,3 |
2 |
00,01,10,11 |
-7,-6,-5,-4,4,5,6,7 |
3 |
000,001,010,011,100,101,110,111 |
-15,……,-8,8,……,15 |
4 |
0000,……,0111,1000,……,1111 |
-31,……,-16,16,……,31 |
5 |
00000,……,01111,10000,……,11111 |
-63,……,-32,32,……,63 |
6 |
…… |
-127,……,-64,64,……,127 |
7 |
…… |
-255,……,-128,128,……,255 |
8 |
…… |
-511,……,-256,256,……,511 |
9 |
…… |
-1023,……,-512,512,……,1023 |
10 |
…… |
-2047,……,-1024,1024,……,2047 |
11 |
…… |
-4095,……,-2048,2048,……,4095 |
12 |
…… |
-8191,……,-4096,4096,……,8191 |
13 |
…… |
-16383,……,-8192,8192,……,16383 |
14 |
…… |
-32767,……,-16384,16384,……,32767 |
15 |
…… |
2)舉例說明
下面舉例說明以上幾點。某個顏色分量單元數據如下:
D3 5E 6E 4D 35 f5 8A
若以二進制表示,則為:
1101 0011 0101 1110 0110 1110 0100 1101 0011 0101 1111 0101 1000 1010
假設該顏色分量單元對應以下直流哈夫曼樹和交流哈夫曼樹,則可將各個以位為單位的數據流拆分如下:
110 1001101 01 1 11001 101 11001 001 101 00 11010 1 1111010 11 00 01010
直流哈夫曼樹 交流哈夫曼樹
序號 |
碼字長度 |
碼字 |
權值 |
1 |
2 |
00 |
0x00 |
2 |
2 |
01 |
0x01 |
3 |
2 |
10 |
0x02 |
4 |
3 |
110 |
0x07 |
5 |
4 |
1110 |
0x1e |
6 |
5 |
11110 |
0x2e |
序號 |
碼字長度 |
碼字 |
權值 |
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 |
詳細說明一下:
讀入數據流並對照直流哈夫曼樹,第一個哈夫曼編碼為110,其權值為6,所以往后讀入6位數據“1001101”,譯碼成數值為77。因為每個顏色分量單元只有一個直流分量,所以下一個就是第一個交流分量了。
繼續讀入數據流並對照交流哈夫曼樹,得哈夫曼編碼為01,其權值為1,所以它的前面沒有 零,並往后讀如1位數據“1”,譯碼成數值為1。如此往復,最后讀到哈夫曼編碼“00”,其權值為0,所以滿足交流變量結束條件(最后剩余的 “01010”對本顏色分量單元來說是冗余的,它可能屬於下一個顏色分量單元)。
實際上,這段數據譯碼為:
77,(0,1),(0,5),(0,-6),(0,-3),(5,1),(2,3)
因此,把它置於1個8*8的矩陣中應為:
77 |
1 |
5 |
-6 |
-3 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
3 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
4.直流系數的差分編碼
把所有的顏色分量單元按顏色分量(Y、Cr、Cb)分類。每一種顏色分量內,相鄰的兩個 顏色分量單元的直流變量是以差分來編碼的。也就是說,通過步驟3解碼出來的直流變量數值只是當前顏色分量單元的實際直流變量減去前一個顏色分量單元的實際 直流變量。也就是說,當前直流變量要通過前一個顏色分量單元的實際(非解碼)直流分量來校正:
DCn=DCn-1+Diff
其中Diff為差分校正變量,也就是直接解碼出來的直流系數。但如果當前顏色分量單元是第一個單元,則解碼出來的直流數值就是真正的直流變量。
再次提醒的是,3個顏色分量的直流變量是分開進行差分編碼的。也就是說,為1張圖片解碼時應設置3個獨立的直流校正變量。另一個問題是,當數據流中出現標記RSTn,則3個顏色分量的直流差分校正變量Diff都需要重新復位到0。
5.反量化
不同的顏色分量使用不同的量化表,這個可以從標記段SOF中的顏色分量信息字段查得。一般是Y分量使用量化表0,而Cr、Cb兩個分量共同使用量化表1。
反量化的過程比較簡單。只需要對8*8的顏色分量單元的64個值逐一乘以對應的量化表內位置相同的值則可。圖像內全部的顏色分量單元都要進行反量化。
6.反Zig-zag編碼
如果將反量化后的每個8*8顏色分量單元的每個元素編號,如下圖4,那么各反Zig-zag編碼的過程就是把矩陣元素按圖5重新排列。
圖 4 將顏色分量單元元素編碼 圖 5 反Zig-zag編碼
關於量化和反Zig-zag編碼的先后順序,筆者查閱的幾份資料有不同的見解。經過實踐試驗,解碼的過程中,是應該直接用文件提供的量化表反量化矩陣數據,再將其反Zig-zag編碼才能正確解碼。
7.隔行的正負糾正
這個問題比較特別,因為在筆者認真閱讀的幾份資料中都沒有提及此問題。而是筆者通過對已知圖像進行JPEG編碼壓縮,然后和該圖的JPEG文件數據對比發現的問題。具體原因不明。
實際上,就是必須對每個顏色分量單元的奇數行(每個顏色分量單元有8行,假設把它按0、1、……、6、7編出行號),即1、3、5、7行,進行取相反數操作(正的變負,負的變正)。
8.反離散余弦變換
之前提到,文件中的數據是在編碼時通過正向離散余弦變換(FDCT)進行時空域向頻率域 變換而得到的結果,所以現在解碼就必須將其反向離散余弦變換(IDCT),就是把顏色分量單元矩陣中的頻率域數值向時空域轉換。並且,原來的頻率域的矩陣 大小為8*8,則經過反向離散余弦變換后,時空域的矩陣仍然是8*8。
設正負糾正后的頻率域矩陣為F[u][v],而反向離散余弦變換后的矩陣為f[i][j],其中0≤u,v,i,j≤7。具體使用的公式如下:
,其中
C(u)= (當u=0),C(u)=1(當u≠0);
C(v)= (當v=0),C(u)=1(當v≠0);
另外補充一下正向離散余弦變換的公式,用於編碼:
9.YCrCb向RGB轉換
要在屏幕上顯示圖像,就必須以RGB模式表示圖像的顏色。所以,解碼時需要把YCrCb模式向RGB模式轉換。
正如前面提到,並不是每種顏色分量的采樣因子都一樣,所以轉換時需要注意。如果采樣因子是1:1:1,則每一個像素點的3個顏色分量都被采樣,所以沒有問題。但4:1:1的采樣因子就不一樣了。由“初步了解圖像數據流的結構”一 節中對4:1:1的采樣因子的分析,可以知道一個MCU里有4個Y分量單元,而Cr分量和Cb分量各自只有1個分量單元。以圖2為例,僅有的一個Cr分量 單元(紅色的64個采樣點)應該平鋪用於4個Y分量單元,即左上角16個值用於Y1,右上角16個值用於Y2,左下角16個值用於Y5,右下角16個值用 於Y6。換句話說,一個Cr采樣點服務於4個Y采樣點。對於Cb分量,道理一樣。
另外,由於離散余弦變化要求定義域的對稱,所以在編碼時把RGB的數值范圍從[0,255]統一減去128偏移成[-128,127]。因此解碼時必須為每個分量加上128。具體公式如下:
R=Y +1.402*Cb +128;
G=Y-0.34414*Cr -0.71414*Cb +128;
B=Y +1.772*Cb +128;
還有一個問題,通過變換得出的R、G、B值可能超出了其定義域,所以要作出檢查。如果大於255,則截斷為255;如果小於0,則截斷為0。
下面補充RGB模式向YCrCb模式的公式:
Y =0.299*R +0.587*G +0.114*B ;
Cr= -0.1687*R - 0.3313*G +0.5*B +128;
Cb=0.5 *R - 0.4187*G - 0.0813*B+128;
至此,每個MCU的解碼已經完成。而每一個MCU如何組成一幅完整的圖像,請參考“初步了解圖像數據流的結構”分析。
參考文獻
[1] 李才偉,中山大學計算機系多媒體課程教學課件.
[2] 張益貞,Visual C++實現MPEG/JPEG編解碼技術.北京:人民郵電出版社
[3] CCIT,Information Technology-digital Compression and Conding of Continuous-ton Still Images-requirements and Guidelines.http://www.wotsit.org/download.asp?f=itu-1150PDF (訪問日期:2007-1-1)
[4] 公子御風,JFIF文件格式即JPEG文件交換格式(JPEG File Interchonge Format).http://cat1226.bokee.com/4574350.html (訪問日期:2006-12-29)
[5] 雲風,JPEG 簡易文檔 V2.11.http://rtornados.bokee.com/2442419.html (訪問日期:2006-12-30)
標記名 |
標記代碼 |
說明 |
幀開始標記,Start of Frame,非層次哈夫曼編碼 |
||
SOF0 |
0xFFC0 |
基線離散余弦變換 |
SOF1 |
0xFFC1 |
擴展順序離散余弦變換 |
SOF2 |
0xFFC2 |
遞進離散余弦變換 |
SOF3 |
0xFFC3 |
空間順序無損 |
幀開始標記,Start of Frame,層次哈夫曼編碼 |
||
SOF5 |
0xFFC5 |
差分離散余弦變換 |
SOF6 |
0xFFC6 |
差分層次離散余弦變換 |
SOF7 |
0xFFC7 |
差分空間無損 |
幀開始標記,Start of Frame,非層次算術編碼 |
||
JPEG |
0xFFC8 |
為JPEG擴展保留 |
SOF9 |
0xFFC9 |
擴展順序離散余弦變換 |
SOF10 |
0xFFCA |
遞進離散余弦變換 |
SOF11 |
0xFFCB |
空間順序無損 |
幀開始標記,Start of Frame,層次算術編碼 |
||
SOF13 |
0xFFCD |
差分離散余弦變換 |
SOF14 |
0xFFCE |
差分層次離散余弦變換 |
SOF15 |
0xFFCF |
差分空間無損 |
其他標記 |
||
DHT |
0xFFC4 |
定義哈夫曼樹表 |
DAC |
0xFFCC |
定義算術編碼表 |
RST0 |
OxFFD0 |
差分編碼累計復位,共8個 |
…… |
…… |
|
RST7 |
OxFFD7 |
|
SOI |
OxFFD8 |
圖像開始 |
EOI |
OxFFD9 |
圖像結束 |
SOS |
0xFFDA |
開始掃描,圖像數據開始 |
DQT |
0xFFDB |
定義量化表 |
DNL |
0xFFDC |
定義線數 |
DRI |
0xFFDD |
定義差分編碼累計復位的間隔 |
DHP |
0xFFDE |
定義層次級數 |
EXP |
0xFFDF |
展開參考圖像 |
APP0 |
0xFFE0 |
為應用程序保留,共15個 |
…… |
…… |
|
APP15 |
0xFFEE |
|
JPG0 |
0xFFF0 |
為JPEG擴展保留,共14個 |
…… |
…… |
|
JPG13 |
0xFFFD |
|
COM |
0xFFFE |
注釋 |
TEM |
0xFF01 |
算術編碼中作臨時之用 |
RES |
0xFF02 |
保留,共189個 |
…… |
…… |
|
RES |
0xFFBF |