轉載: http://blog.csdn.net/shelldon/article/details/54234433
圖片壓縮有多重要,可能很多人可能並沒有一個直觀上的認識,舉個例子,一張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]之間。
不同的顏色模型各有不同的應用場景,例如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恰好能做到這一點。對於人眼來說,圖像中明暗的變化更容易被感知到,這是由於人眼的構造引起的。視網膜上有兩種感光細胞,能夠感知亮度變化的視桿細胞,以及能夠感知顏色的視錐細胞,由於視桿細胞在數量上遠大於視錐細胞,所以我們更容易感知到明暗細節。比如說下面這張圖
可以明顯看到,亮度圖的細節更加豐富。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轉換公式為
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大部分集中在右下角,所以才去這種由左上角到右下角的順序,經過這種順序變換,最終矩陣變成一個整數數組
后面的工作就是對這個數組進行再一次的哈夫曼壓縮,已得到最終的壓縮數據。
步驟五:哈弗曼編碼
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 | 0 | 1110 | 10 | 1111 |
那么這段文字共需要3*6 + 1*15 + 4*2 + 2*9 + 4*1 = 63個bit來保存,壓縮比為63%,哈弗曼編碼一般都是使用二叉樹來生成的,這樣得到的編碼符合前綴規則,也就是較短的編碼不能夠是較長編碼的前綴,比如上面這個編碼,就是由下面的這顆二叉樹生成的。
我們回到JPEG壓縮上,回顧上一節的內容,經過數據量化,我們現在要處理的數據是一串一維數組,舉例如下:
①原始數據 |
|
---|
在實際的壓縮過程中,數據中的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”,
①原始數據 |
|
|||||||
---|---|---|---|---|---|---|---|---|
②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 | ||
---|---|---|---|---|
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 | 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″,由於這種編碼附帶長度信息,所以我們的數據變成了如下的格式。
①原始數據 |
|
|||||||
---|---|---|---|---|---|---|---|---|
②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的個數,后四位是后面數字的位數。
①原始數據 |
|
|||||||
---|---|---|---|---|---|---|---|---|
②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 |
這樣經過哈弗曼編碼,並且序列化后,最終數據成為如下形式
①原始數據 |
|
|||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
②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 |
⑤序列化 |
|
|||||||||||||
|
最終我們使用了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、圖片的識別信息
SOI,Start of Image,圖像開始,2字節,固定值0xFFD8
JEPG APP0 應用程序保留標記0
字段名稱 |
字段長度 |
Comments |
標記代碼 marker |
2 bytes |
固定值0xFF, 0xE0 |
數據長度 length |
2 bytes |
APP0總長度,不過括marker,但是包括length本身 |
標識符 identifier |
5 bytes |
固定的字符串"JFIF\0" |
版本號 version |
2 bytes |
一般為0x0101或者0x0102,表示JFIF的版本號1.1 或 1.2 |
像素單位 unit |
1 bytes |
坐標單位,0沒有單位; 1 pixel/inch;2 pixel/cm |
水平像素數目 Xdensity |
2 bytes |
取值范圍未知 |
垂直像素數目 YDensity |
2 bytes |
取值范圍未知 |
縮略圖水平像素數目 |
1 byte |
取值范圍未知 |
縮略圖垂直像素數目 |
1 byte |
取值范圍未知 |
縮略圖RGB位圖 |
3n bytes |
n = Xthumbnail * Ythumbnail, 這是一個24bits/piexl的RGB位圖 |
上面的內容,在標記 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)。