本篇是該系列的第四篇,主要介紹霍夫曼解碼相關內容。編碼
承接上篇,文件頭解析完畢後,就進入了編碼數據區域,即SOS的tag後的區域,也是圖片數據量的大頭所在。spa
1. 解碼過程規則描述blog
a)今後顏色份量單元數據流的起點開始一位一位的讀入,直到讀入的編碼與該份量直流哈夫曼樹的某個碼字(葉子結點)一致,而後用直流哈夫曼樹圖片
查得該碼字對應的權值。權值(共8位)表示該直流份量數值的二進制位數,也就是接下來須要讀入的位數。變量
b)繼續讀入位數據,直到讀入的編碼與該份量交流哈夫曼樹的某個碼字(葉子結點)一致,而後用交流哈夫曼樹查得該碼字對應的權值。權值的高4位二進制
表示當前數值前面有多少個連續的零,低4 位表示該交流份量數值的二進制位數,也就是接下來須要讀入的位數。im
c)不斷重複步驟b,直到知足交流份量數據結束的條件。d3
而結束條件有兩個,只要知足其中一個便可:數據
①當讀入碼字的權值爲零,表示日後的交流變量所有爲零;
②已經讀入63個交流份量。img
2. 待處理的數據區域
上面的規則描述過於抽象,須要一個例子來實戰說明,仍使用那張animal_park.jpg的圖片。
其二進制數據顯示以下(FFDA所表明的SOS以後深色標註區域):
截取到的二進制數據爲:F9 96 8B FA 71 EA 5B 24 B5 ...
3. 準備好霍夫曼表
先列出四張霍夫曼表——DC0,AC0, DC1,AC1,待後面查找使用。
DC0——Y份量的直流部分
AC0——Y份量的交流部分(表太長,沒列全)
DC1——UV份量的直流部分
AC1——UV份量的交流部分
4. 解碼步驟
這是難點所在,解碼的過程其實就是霍夫曼樹的查找過程。mcu單元內部使用了RLE行程編碼和霍夫曼編碼來壓縮數據。
例子:F9 96 8B FA 71 EA 5B 24 B5。。。
對應的二進制位展開:1111 1001, 1001 0110, 1000 1011, 1111 1010, 0111 0001, 1110 1010, 0101 1011, 0010 0100, 1011 0101。。。
step1. 先讀入若干位與DC0表的Code進行匹配。
讀取2位的11時, 無匹配的Code,由於2位寬的Code只有0b00和0b01
3位的111 無 3 0b100,0b101和0b110。
4位的1111 無 4 0b1110。
5位的11111 無 5 0b11110。
6位的111110 有 6 0b111110,剛好匹配!其對應的CodeVal爲0x7
step2. 利用上面獲得的CodeVal進行拆分,並讀取後面若干位。
0x7=0x07,高四位爲0,低四位爲7,則再讀取後面的7位二進制,爲:01, 1001 0。
後面讀取的值,這樣算:若是開頭爲1則爲正數,若是開頭爲0,則爲負數,而後對各位求反獲得數值,便可。
01, 1001 0這個值,因爲開頭爲0,則爲負數,多少呢?取反獲得:10, 01101 = 0x4D = 77,最後獲得最終值爲:-77。
step3. 經過上面兩步驟的第一次掃描,獲得的爲Y份量的DC值,後面還需通過63次掃描獲得剩餘的AC值(通常掃描幾回就結束了)。
上面DC值標記爲-77。
step4. 繼續經過相似step1和step2來取得AC值,注意要查找AC0表。
讀取5位的110, 10時,有匹配的Code:0b11010=0x1a,其對應的CodeVal=0x04;
取得後四位的值——4,表示還需讀取的二進制位數量,來表示真正的信源值——0b0010,經(step2中描述)變換後值爲-13;
那麼能夠RLE標記爲(0,-13),其中0來自於CodeVal的高4位,-13爲另讀入的數據值。可也記爲key-val對。
step5. 重複step4的操做,直到獲得(0,0)(位置爲5B那個字節的最高四位)。
後面的依次爲:
Code CodeVal RLE_val RLE
11, 1111 1010(0x3FA) 0x34 0111(-8) (3, -8)
00 0x1 0 (-1) (0, -1)
1, 1110 10(0x7A) 0x71 1(1) (7, 1)
00 0x1 1(1) (0, 1)
01 0x0 -- (0, 0) -> 結束於5B的高4位
step6. 經過step1-step5的掃描,獲得數據:-77, (0, -13), (3, -8),(0, -1),(7, 1), (0, 1), (0, 0)
step7. step1到step6結束後,表示一個mcu的霍夫曼解碼結束。
RLE中的(m,n),m表示前面填充0的個數,n表示實際值。
其解碼結果以下: