轉載自 www.thecodeway.com http://www.ibm.com/developerworks/cn/linux/l-cn-jpeg/
JPEG壓縮算法之前已有很多前輩詳細講解過,我就不在這裡畫蛇添足了(主要是我懶。。),轉載兩篇JPEG壓縮算法介紹,拼為一篇。侵删。
JPEG圖像壓縮算法
圖檔壓縮有多重要,可能很多人可能并沒有一個直覺上的認識,舉個例子,一張800X800大小的普通圖檔,如果未經壓縮,大概在1.7MB左右,這個體積如果存放文本檔案的話足夠儲存一部92萬字的鴻篇巨著《紅樓夢》,現如今網際網路上絕大部分圖檔都使用了JPEG壓縮技術,也就是大家使用的jpg檔案,通常JPEG檔案相對于原始圖像,能夠得到1/8的壓縮比,如此高的壓縮率是如何做到的呢?
JPEG能夠獲得如此高的壓縮比是因為使用了有損壓縮技術,所謂有損壓縮,就是把原始資料中不重要的部分去掉,以便可以用更小的體積儲存,這個原理其實很常見,比如485194.200000000001這個數,如果我們用485194.2來儲存,就是一種“有損”的儲存方法,因為小數點後面的那個“0.000000000001”屬于不重要的部分,是以可以被忽略掉。JPEG整個壓縮過程基本上也是遵循這個步驟:
1. 把資料分為“重要部分”和“不重要部分”
2. 濾掉不重要的部分
3. 儲存
步驟一:圖像分割
JPEG算法的第一步,圖像被分割成大小為8X8的小塊,這些小塊在整個壓縮過程中都是單獨被處理的。後面我們會以一張非常經典的圖為例,這張圖檔名字叫做Lenna,據說是世界上第一張JPG圖檔,這張圖檔自從誕生之日開始,就和圖像處理結下淵源,陪伴了無數理工宅男度過了的一個個不眠之夜,可謂功勳卓著,感興趣的朋友可以在 這裡了解到這張圖檔的故事。
步驟二:顔色空間轉換RGB->YCbCr
所謂“ 顔色空間”,是指表達顔色的數學模型,比如我們常見的“RGB”模型,就是把顔色分解成紅綠藍三種分量,這樣一張圖檔就可以分解成三張灰階圖,數學表達上,每一個8X8的圖案,可以表達成三個8X8的矩陣,其中的數值的範圍一般在[0,255]之間。
R | > | |||
---|---|---|---|---|
G | > | |||
B | > |
不同的顔色模型各有不同的應用場景,例如RGB模型适合于像顯示器這樣的自發光圖案,而在印刷行業,使用油墨列印,圖案的顔色是通過在反射光線時産生的,通常使用CMYK模型,而在JPEG壓縮算法中,需要把圖案轉換成為YCbCr模型,這裡的Y表示亮度(Luminance),Cb和Cr分别表示綠色和紅色的“色內插補點”。
“色差”這個概念起源于電視行業,最早的電視都是黑白的,那時候傳輸電視信号隻需要傳輸亮度信号,也就是Y信号即可,彩色電視出現之後,人們在Y信号之外增加了兩條色差信号以傳輸顔色資訊,這麼做的目的是為了相容黑白電視機,因為黑白電視隻需要處理信号中的Y信号即可。
根據三基色原理,人們發現紅綠藍三種顔色所貢獻的亮度是不同的,綠色的“亮度”最大,藍色最暗,設紅色所貢獻的亮度的份額為KR,藍色貢獻的份額為KB,那麼亮度為
(1.1) |
---|
根據經驗,KR=0.299,KB=0.114,那麼
(1.2) |
---|
藍色和紅色的色差的定義如下
(1.3) |
---|
(1.4) |
---|
最終可以得到RGB轉換為YCbCr的數學公式為
(1.5) |
---|
YCbCr模型廣泛應用在圖檔和視訊的壓縮傳輸中,比如你可以留意一下電視或者DVD後面的接口,就可以發現色差接口。
這是有道理的,還記得我們在文章開始時提到的有損壓縮的基本原理嗎?有損壓縮首先要做的事情就是“把重要的資訊和不重要的資訊分開”,YCbCr恰好能做到這一點。對于人眼來說,圖像中明暗的變化更容易被感覺到,這是由于人眼的構造引起的。視網膜上有兩種感光細胞,能夠感覺亮度變化的視杆細胞,以及能夠感覺顔色的視錐細胞,由于視杆細胞在數量上遠大于視錐細胞,是以我們更容易感覺到明暗細節。比如說下面這張圖
Y | Cb | Cr |
---|
可以明顯看到,亮度圖的細節更加豐富。JPEG把圖像轉換為YCbCr之後,就可以針對資料得重要程度的不同做不同的處理。這就是為什麼JPEG使用這種顔色空間的原因。
步驟三:離散餘弦變換
這次我們來介紹JPEG算法中的核心内容,離散餘弦變換( Discrete cosine transform ),簡稱DCT。
離散餘弦變換屬于 傅裡葉變換 的另外一種形式,沒錯,就是大名鼎鼎的傅裡葉變換。傅裡葉是法國著名的數學家和實體學家,1807年,39歲的傅裡葉在他的一篇論文裡提出了一個想法,他認為 任何周期性的函數,都可以分解為為一系列的三角函數的組合 ,這個想法一開始并沒有得到當時科學界的承認,比如當時著名的數學家拉格朗日提出質疑,三角函數無論如何組合,都無法表達帶有“尖角”的函數,一直到1822年拉格朗日死後,傅裡葉的想法才正式在他的著作《熱的解析理論》一書中正式發表。
金子總會閃光,傅裡葉變換如今廣泛應用于數學、實體、信号處理等等領域,變換除了它在數學上的意義外,還有其哲學上的偉大意義,那就是,世上任何複雜的事物,都可以分解為簡單的事物的組合,而這個過程隻需要借助數學工具就可以了。但是當年拉格朗日的質疑是正确的,三角函數的确無法表達出尖角形狀的函數,不過隻要三角函數足夠多,可以無限逼近最終結果。比如下面這張動圖,就動态描述了一個矩形方波,是如何做傅裡葉分析的。
當我們要處理的不再是函數,而是一堆離散的資料時,并且這些資料是對稱的話,那麼傅裡葉變化出來的函數隻含有餘弦項,這種變換稱為離散餘弦變換。舉個例子,有一組一維資料[x0,x1,x2,…,xn-1],那麼可以通過DCT變換得到n個變換級數Fi
(2.1) |
---|
此時原始資料Xi可以通過離散餘弦變換變化的逆變換(IDCT)表達出來
(2.2) |
---|
也就是說,經過DCT變換,可以把一個數組分解成數個數組的和,如果我們數組視為一個一維矩陣,那麼可以把結果看做是一系列矩陣的和
(2.3) |
---|
舉個例子,我們有一個長度為8的數字,内容為50,55,67,80,-10,-5,20,30,經過DCT轉換,得到8個級數為287.0,106.3,14.2,-110.8,9.2,65.7,-8.2,-43.9,根據公式2.3把這個數組轉換為8個新的數組的和,如果我們使用圖像來表達的話,就可以發現DCT轉換的有趣之處了
[50,55,67,80,-10,-5,20,30] | |
---|---|
數組0 | |
[35.9,35.9,35.9,35.9,35.9,35.9,35.9,35.9] | |
數組1 | |
[26.0,22.1,14.8,5.2,-5.2,-14.8,-22.1,-26.1] | |
數組2 | |
[3.3,1.4,-1.4,-3.3,-3.3,-1.4,1.4,3.3] | |
數組3 | |
[-23.0,5.4,27.2,15.4,-15.4,-27.2,-5.4,23.0] | |
數組4 | |
[1.6,-1.6,-1.6,1.6,1.6,-1.6,-1.6,1.6] | |
數組5 | |
[9.1,-16.1,3.2,13.6,-13.6,-3.2,16.1,-9.1] | |
數組6 | |
[-0.8,1.9,-1.9,0.8,0.8,-1.9,1.9,-0.8] | |
數組7 | |
[-2.1,6.1,-9.1,10.8,-10.8,9.1,-6.1,2.1] |
奧妙之處在于,經過DCT,資料中隐藏的規律被發掘了出來,雜亂的資料被轉換成幾個工整變化的資料。DCT轉換後的數組中第一個是一個直線資料,是以又被稱為“直流資料”,簡稱DC,後面的資料被稱為“交流資料”,簡稱AC,這個稱呼起源于信号分析中的術語。
在JPEG壓縮過程中,經過顔色空間的轉換,每一個8X8的圖像塊,在資料上表現為3個8X8的矩陣,緊接着我們對這三個矩陣做一個二維的DCT轉換,二維的DCT轉換公式為
(2.1) |
---|
DCT的威力究竟有多大,我們可以做一個實際的測試,比如一個所有數值都一樣的矩陣,經過DCT轉換後,将所有級數組合成一個新的矩陣
可以看到,經過DCT轉換,矩陣的“能量”被全部集中在左上角上的直流分量F(0,0)上,其他位置都變成了0。
在實際的JPEG壓縮過程中,由于圖像本身的連貫性,一個8X8的圖像中的數值一般不會出現大的跳躍,經過DCT轉換會有類似的效果,左上角的直流分量儲存了一個大的數值,其他分量都接近于0,我們以Lenna左上角第一塊圖像的Y分量為例,經過變換的矩陣為
可以看到,資料經過DCT變化後,被明顯分成了直流分量和交流分量兩部分,為後面的進一步壓縮起到了充分的鋪墊作用,可以說是整個JPEG中最重要的一步,後面我們會介紹資料量化。
步驟四:資料量化
經過上一節介紹的離散餘弦變換,圖像資料雖然已經面目全非,但仍然是處于“可逆”的狀态,也就是說我們還沒有進入“有損”的那一步。這次我們來玩真的,看一下資料中的細節是如何被濾去的。先來考察一下要對付的問題是什麼,經過顔色空間轉換和離散餘弦變換,每一個8X8的圖像塊都變成了三個8X8的浮點數矩陣,分别表示Y,Cr,Cb資料,比如以其中某個亮度資料矩陣舉例,它的資料如下
我們的問題是,在可以損失一部分精度的情況下,如何用更少的空間存儲這些浮點數?答案是使用量子化( Quantization ),簡稱量化。“量子”這個概念來自于實體學,意思是說連續的能量可以看做是一個個單元體的組合,看起來高端大氣,其實很簡單,比如遊戲中在處理角色面朝方向時,一般并不是使用0到2π這樣的浮點數,而是把方向分成16個區間,用0到16這樣的整數來表示,這樣隻用4個bit就足夠了。JPEG提供的量子化算法如下:
(3.1) |
---|
其中G是我們需要處理的圖像矩陣,Q稱作量化系數矩陣(Quantization matrices),JPEG算法提供了兩張标準的量化系數矩陣,分别用于處理亮度資料Y和色差資料Cr以及Cb。
标準亮度量化表 | 标準色差量化表 |
---|
其中round函數是取整函數,但考慮到了四舍五入,也就是說
(3.2) |
---|
比如上面資料,以左上角的-415.38為例,對應的量子化系數是16,那麼round(-415.38/16)=round(-25.96125)=-26。最終得到的量子化後的結果為
可以看到,一大部分資料變成了0,這非常有利于後面的壓縮存儲。這兩張神奇的量化表也是有講究的,還記得我們在第一節中所講的有損壓縮的基本原理嗎,有損壓縮就是把資料中重要的資料和不重要的資料分開,然後分别處理。DCT系數矩陣中的不同位置的值代表了圖像資料中不同頻率的分量,這兩張表中的資料時人們根據人眼對不不同頻率的敏感程度的差别所積累下的經驗制定的,一般來說人眼對于低頻的分量必高頻分量更加敏感,是以兩張量化系數矩陣左上角的數值明顯小于右下角區域。在實際的壓縮過程中,還可以根據需要在這些系數的基礎上再乘以一個系數,以使更多或更少的資料變成0,我們平時使用的圖像處理軟體在省城jpg檔案時,在控制壓縮品質的時候,就是控制的這個系數。
在進入下一節之前,矩陣的量化還有最後一步要做,就是把量化後的二維矩陣轉變成一個一維數組,以友善後面的霍夫曼壓縮,但在做這個順序轉換時,需要按照一個特定的取值順序。
這麼做的目的隻有一個,就是盡可能把0放在一起,由于0大部分集中在右下角,是以才去這種由左上角到右下角的順序,經過這種順序變換,最終矩陣變成一個整數數組
-26,-3,0,-3,-2,-6,2,-4,1,-3,0,1,5,,1,2,-1,1,-1,2,0,0,0,0,0,-1,-1,0,0,0,0,…,0,0
後面的工作就是對這個數組進行再一次的哈夫曼壓縮,已得到最終的壓縮資料。
步驟五:哈弗曼編碼
JPEG壓縮的最後一步是對資料進行哈弗曼編碼( Huffman coding ),哈弗曼幾乎是所有壓縮算法的基礎,它的基本原理是根據資料中元素的使用頻率,調整元素的編碼長度,以得到更高的壓縮比。
舉個例子,比如下面這段資料
“AABCBABBCDBBDDBAABDBBDABBBBDDEDBD”
這段資料裡面包含了33個字元,每種字元出現的次數統計如下
字元 | A | B | C | D | E |
---|---|---|---|---|---|
次數 | 6 | 15 | 2 | 9 | 1 |
如果我們用我們常見的定長編碼,每個字元都是3個bit。
字元 | A | B | C | D | E |
---|---|---|---|---|---|
編碼 | 001 | 010 | 011 | 100 | 101 |
那麼這段文字共需要3*33 = 99個bit來儲存,但如果我們根據字元出現的機率,使用如下的編碼
字元 | A | B | C | D | E |
---|---|---|---|---|---|
編碼 | 110 | 1110 | 10 | 1111 |
那麼這段文字共需要3*6 + 1*15 + 4*2 + 2*9 + 4*1 = 63個bit來儲存,壓縮比為63%,哈弗曼編碼一般都是使用二叉樹來生成的,這樣得到的編碼符合字首規則,也就是較短的編碼不能夠是較長編碼的字首,比如上面這個編碼,就是由下面的這顆二叉樹生成的。
我們回到JPEG壓縮上,回顧上一節的内容,經過資料量化,我們現在要處理的資料是一串一維數組,舉例如下:
①原始資料 | 35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0 |
---|
在實際的壓縮過程中,資料中的0出現的機率非常高,是以首先要做的事情,是對其中的0進行處理,把資料中的非零的資料,以及資料前面0的個數作為一個處理單元。
①原始資料 | 35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0 | ||||||
---|---|---|---|---|---|---|---|
②RLE編碼 | 35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0,8 | 0,0,…,0 |
如果其中某個單元的0的個數超過16,則需要分成每16個一組,如果最後一個單元全都是0,則使用特殊字元“EOB”表示,EOB意思就是“後面的資料全都是0”,
①原始資料 | 35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0 | ||||||
---|---|---|---|---|---|---|---|
②RLE編碼 | 35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0,8 | 0,0,…,0 |
35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0 | 0,0,8 | 0,0,…,0 |
(0,35) | (0,7) | (3,-6) | (0,-2) | (2,-9) | (15,0) | (2,8) | EOB |
* RLE - Run-Length Encoding,即行程編碼,對于連續重複出現的字元有很好的壓縮率。編碼後為(字元出現次數,字元)。
* JPEG 中實際使用的是(字元出現次數 - 1,字元)
其中(15,0)表示16個0,接下來我們要處理的是括号裡右面的數字,這個數字的取值範圍在-2047~2047之間,JPEG提供了一張标準的碼表用于對這些數字編碼:
Value | Size | Bits | ||
---|---|---|---|---|
– | ||||
-1 | 1 | 1 | 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 | 0 0000,…,0 1111 | 1 0000,…,1 1111 |
-63,…,-32 | 32,…,63 | 6 | 00 0000,… | …,11 1111 |
-127,…,-64 | 64,…,127 | 7 | 000 0000,… | …,111 1111 |
-255,…,-128 | 128,…,255 | 8 | 0000 0000,… | …,1111 1111 |
-511,…,-256 | 256,…,511 | 9 | 0 0000 0000,… | …,1 1111 1111 |
-1023,…,-512 | 512,…,1023 | 10 | 00 0000 0000,… | …,11 1111 1111 |
-2047,…,-1024 | 1024,…,2047 | 11 | 000 0000 0000,… | …,111 1111 1111 |
舉例來說,第一個單元中的“35”這個數字,在表中的位置是長度為6的那組,所對應的bit碼是“100011”,而“-6”的編碼是”001″,由于這種編碼附帶長度資訊,是以我們的資料變成了如下的格式。
①原始資料 | 35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0 | |||||||
---|---|---|---|---|---|---|---|---|
②RLE編碼 | 35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0,8 | 0,0,…,0 | |
35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0 | 0,0,8 | 0,0,…,0 | |
(0,35) | (0,7) | (3,-6) | (0,-2) | (2,-9) | (15,0) | (2,8) | EOB | |
③BIT編碼 | (0,6, 100011) | (0,3, 111) | (3,3, 001) | (0,2, 01) | (2,4, 0110) | (15,-) | (2,4, 1000) | EOB |
括号中前兩個數字分都在0~15之間,是以這兩個數可以合并成一個byte,高四位是前面0的個數,後四位是後面數字的位數。
①原始資料 | 35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0 | |||||||
---|---|---|---|---|---|---|---|---|
②RLE編碼 | 35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0,8 | 0,0,…,0 | |
35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0 | 0,0,8 | 0,0,…,0 | |
(0,35) | (0,7) | (3,-6) | (0,-2) | (2,-9) | (15,0) | (2,8) | EOB | |
③BIT編碼 | (0,6, 100011) | (0,3, 111) | (3,3, 001) | (0,2, 01) | (2,4, 0110) | (15,-) | (2,4, 1000) | EOB |
(0x6,100011) | (0x3,111) | (0x33,001) | (0x2,01) | (0x24,0110) | (0xF0,-) | (0x24,1000) | EOB |
對于括号前面的數字的編碼,就要使用到我們提到的哈弗曼編碼了,比如下面這張表,就是一張針對資料中的第一個單元,也就是直流(DC)部分的哈弗曼表,由于直流部分沒有前置的0,是以取值範圍在0~15之間。
Length | Value | Bits |
---|---|---|
3 bits | 04 05 03 02 06 01 00 (EOB) | 000 001 010 011 100 101 110 |
4 bits | 07 | 1110 |
5 bits | 08 | 1111 0 |
6 bits | 09 | 1111 10 |
7 bits | 0A | 1111 110 |
8 bits | 0B | 1111 1110 |
舉例來說,示例中的DC部分的資料是0x06,對應的二進制編碼是“100”,而對于後面的交流部分,取值範圍在0~255之間,是以對應的哈弗曼表會更大一些
Length | Value | Bits |
---|---|---|
2 bits | 01 02 | 00 01 |
3 bits | 03 | 100 |
4 bits | 00 (EOB) 04 11 | 1010 1011 1100 |
5 bits | 05 12 21 | 1101 0 1101 1 1110 0 |
6 bits | 31 41 | 1110 10 1110 11 |
… | … | … |
12 bits | 24 33 62 72 | 1111 1111 0100 1111 1111 0101 1111 1111 0110 1111 1111 0111 |
15 bits | 82 | 1111 1111 1000 000 |
16 bits | 09 … FA | 1111 1111 1000 0010 … 1111 1111 1111 1110 |
這樣經過哈弗曼編碼,并且序列化後,最終資料成為如下形式
①原始資料 | 35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0 | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
②RLE編碼 | 35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0,8 | 0,0,…,0 | |||||||
35 | 7 | 0,0,0,-6 | -2 | 0,0,-9 | 0,0,…,0 | 0,0,8 | 0,0,…,0 | |||||||
(0,35) | (0,7) | (3,-6) | (0,-2) | (2,-9) | (15,0) | (2,8) | EOB | |||||||
③BIT編碼 | (0,6, 100011) | (0,3, 111) | (3,3, 001) | (0,2, 01) | (2,4, 0110) | (15,-) | (2,4, 1000) | EOB | ||||||
(0x6,100011) | (0x3,111) | (0x33,001) | (0x2,01) | (0x24,0110) | 0xF0 | (0x24,1000) | EOB | |||||||
④哈弗曼編碼 | 100 | 100011 | 100 | 111 | 1111 1111 0101 | 001 | 01 | 01 | 1111 1111 0100 | 0110 | 1111 1111 001 | 1111 1111 0100 | 1000 | 1010 |
⑤序列化 | 100100011100111111111110101001010111111111010001101111111100111111111010010001010 | |||||||||||||
91 CF FE A5 7F D1 BF CF FA 45 |
最終我們使用了10個位元組的空間儲存了原本長度為64的數組,至此JPEG的主要壓縮算法結束,這些資料就是儲存在jpg檔案中的最終資料。
一個JPEG圖檔執行個體分析
我們可以打開 JPEG 檔案檢視裡面的内容,即可看到上面的各個标記段:
在頭部有 FFD8 ,表示圖像的開始;結束部分有 FFD9 ,表示圖像的結束。
在中間有兩個量化表 DQT 對應的标記 FFDB ;
還有圖像大小資訊對應的 FFC0
再後面有四個 Haffman 表對應的 FFC4 ;
一般一個 JPEG 檔案裡會有 2 類 Haffman 表:一個用于 DC 一個用于 AC ,也即實際有 4個表,亮度的 DC,AC 兩個,色度的 DC,AC 兩個。
然後是圖像資料段标記 FFDA;
我們再來看看各個标記的細部,具體分析一下各個部分的含義。
1、圖檔的識别資訊
上面的内容,在标記 FFE0 後,即為長度16。
然後是5位元組的 JFIF 辨別符号,說明這是一個 JPEG 壓縮的檔案。
然後是主/次版本号碼。下一個為 XY 像素的機關,這裡為1,表示機關為點數/英寸。
然後是 XY 方向的像素密度,這裡是 96DPI,最後是縮略圖有關資訊,這裡為0。
2、量化表的執行個體
上面這個内容,FFDB 标記後的長度值為67,接下來的是 QT 資訊,占一個位元組;
這裡是0,表示這個 QT 表編号為0,并且精度是8bit。然後後面就是64個8x8的 QT 表的各個 item 了。
也即第一個 DQT 量化表的内容表示為十進制是:
這個表即為 JPEG 亮度量化表。
第二個量化表的内容為:
這個表的内容即為 JPEG 色度量化表。
當你打開不同的 JPEG 檔案,你會看到這兩個表可能也是會有差別的。這個主要是使用了不同的量化方式的結果。
3、圖像資訊段
上面這個内容,FFC0 标記後即是長度,為17;
然後是一個位元組的資料精度,通常是為8,代表樣本位數。
接下來是圖檔的高度,占兩位元組,這裡即為8,然後是圖檔的寬度,也為8,這也就是我們定義的8x8的内容。
然後是 component 的個數,這裡是3,表示 YUV。接下來是三組資料,每組資料裡,第一個是 component ID,第二個是采樣系數,這裡 Y 的采樣系數為22,說明垂直是2,水準是2。
再後面就是量化表的編号了。
4、Haffman 表的執行個體
上面這個内容,FFC4 标記後的内容為資料長度,再接着的1位元組為 Huffman Table 的資訊,低4位是 HT ID 号,第5位是 HT 表類型标記,再高三位是為0。
第一個 DHT 表,00,類型為 DC table,HT ID 号為 0;
第二個 DHT 表,10,類型為 AC table,HT ID 号也為 0;
第三個 DHT 表,01,類型為 DC table,HT ID 号為 1;
第四個 DHT 表,11,類型為 AC table,HT ID 号為 1;
即前兩個表為Y亮度分量的 DC/AC 表,後兩個為 UV 色度分量的 DC/AC 表。
以第一個表為例,因為長度隻有 31,那麼 00 後面的 16 位元組,即綠色部分:
組号為 1 的組中,代碼有 0 個;
組号為 2 的,代碼有 1 個;
組号為 3 的代碼有 5 個;
組号為 4/5/6/7/8/9 的代碼各 1 個。
總共 12 個。
再看後續的資料:
00 01 02 03 04 05 06 07 08 09 0A 0B
即對應:
其他未出現的組号,對應的資料未使用到。也就是說前面提到過的範式 Huffman 編碼裡,目前隻使用部分資料即可,原因是這個 8x8 的圖像資料很小。
第二個 DHT 表就更複雜些了,長度有 181。
5、圖像資料段
這裡 SOS 段,長度為 12,後面所含有的 component 數量為 3 個,也即 Y UV。然後後面是各 component 的編号,及對應所使用的 Huffman 表的 ID 是多少。
在這個段的後面就是所有壓縮後的資料。直到結束的問題,即 FFD9,EOI(End Of Image)。
轉載自:
http://thecodeway.com/blog/
http://www.ibm.com/developerworks/cn/linux/l-cn-jpeg/