--- ###### tags: `DataCompression` --- # JPEG Decoder (2/3) 在上一部份處理完MCU的DCT及量化,這篇要介紹如何進行對係數進行編碼。 一個8x8 MCU經過量化後可分為 - 1個在左上角雙向上頻率為0的**DC coefficient** - 63個**AC coefficient** 從2-D DCT可發現DC coefficient代表區塊內像素平均值,由於鄰近區塊的DC coefficient有相似性,以DPCM方式編碼使值縮小,可以在之後的壓縮取得不錯成效。 - $DiffDC(i) = DC(i) - DC(i-1), i=1,2,...$ - $DC(0)=0$ 例如 - DC(1)=-512 - DC(2)=1024 代表 - MCU 1: DC Coef=-512 - MCU 2: DC Coef=512。 AC coefficient藉由zig-zag順序來將低頻率的係數放在前面,有助於entropy coding。又因區塊小,跨區塊變化也小,高頻部份趨近於0,適合以RLE方式編碼。 接者編碼AC/DC coefficient成Bit-stream。 # 1. Huffman Coding DC/AC coefficient在檔案中的存放方式 ``` | MCU 1 | | Y | Cb | Cr | | DC | AC | DC | AC | DC | AC | ... ``` DC/AC coefficient需經過Huffman encoding才寫入。 下表範例是經過Huffman encoding的資料集以及其codeword Symbol|a|b|c|d|e -|-|-|-|-|- weights|0.10|0.15|0.30|0.16|0.29 codeword|0b010|0b011|0b11|0b00|0b10 ### 1.1 Huffman Table JPEG分兩部份存Huffman table,第一個部份存codeword length,第二個存decoded value,[實作](https://www.impulseadventure.com/photo/jpeg-decoder.html)上可使用兩個迴圈建出Huffman tree。[Expansion of DHT Table into binary bit strings](https://www.impulseadventure.com/photo/jpeg-huffman-coding.html)敘述如何利用codeword length及decoded value重建code word: ``` Class = 1 (AC Table) Destination ID = 0 Codes of length 01 bits (000 total): Codes of length 02 bits (002 total): 01 02 Codes of length 03 bits (001 total): 03 Codes of length 04 bits (003 total): 11 04 00 Codes of length 05 bits (003 total): 05 21 12 ... ``` ![](https://www.impulseadventure.com/photo/images/huffman/huff_tree1.gif) 步驟如下: 1. 0 bit (root)沒有value,生成左右node 2. 1 bit 沒有value,同樣生成左右node 3. 2 bit 有 0x01, 0x02,從左至右填上分支出來的位置,成為leaf node。剩下的node繼續分支。 4. 3 bit 有 0x03,填上剛生成的node,以此類推。 了解上述流程才能理解Huffman table在JPEG內所佔空間為何是`16+n` bytes > In JPEG all Huffman code tables are defined in the image header. Each table requires 16 + n bytes, where n is the number of codewords in the table. **The first 16 bytes list the number of codewords of each length from 1 to 16 bits (codewords longer than 16 bits are forbidden).** The remaining n bytes list the decoded output values of the n codewords in ascending codeword order (n < 256). 以下方HEX Code為例,嘗試解出Huffman table: ``` 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 | 01 02 03 04 05 06 07 08 09 0A 0B codeword length | decoded value ``` Codeword length (bits)|1|2|3|4|5|6|7|8|9|...|16 -|-|-|-|-|-|-|-|-|-|-|- Decoded value| - | 0x00 | 0x01, 0x02, 0x03, 0x04, 0x05 | 0x06 | 0x07 | 0x08 | 0x09 | 0x0A | 0x0B | - | - Symbol|Codeword|Bit-length -|-|- 0x00|00|2 0x01|010|3 0x02|011|3 0x03|100|3 0x04|101|3 0x05|110|3 0x06|1110|4 0x07|11110|5 0x08|111110|6 0x09|1111110|7 0x0A|11111110|8 0x0B|111111110|9 :::info 當採用Optimized Luminance/Chrominance的DC/AC coefficient分別個一張表,共4張表 ::: ### 1.2 Optimized JPEG 一般來說,JPEG不會對各種係數各建一張表,除非儲存時選擇`Optimized`。 Optimized JPEG指的是對Y(Luminance), CbCr(Chrominance)不同DC/AC係數各算一張Huffman table,雖然在檔案中需額外定義,但整體空間省下[2.4~6.8%](https://www.impulseadventure.com/photo/optimized-jpeg.html)。現代高階相機具有輸出Optimized JPEG的能力,但考慮到耗時與節省的空間不成比例,多採用[JPEG ITU-T81D standard, 149-157](https://www.w3.org/Graphics/JPEG/itu-t81.pdf)提供的Huffman table。 計算Optimized JPEG的Huffman table方式不只一種,最簡單的方式是採用先前提到的作法:將係數化成`Size`和`Additional bits`,再對`Size`進行編碼。也有的軟體有自家處理的方法,例如Photoshop CS2限制DC chrominance table不得有`0xA-0xB`、`EOB`為在DC luminance table內為5位元長度的codeword。 ### 1.3 Standard Huffman Tables 由於JPEG對於Huffman table的限制,DC table的`Size`為`0x0-0xB`,也就是最多12個code word;AC table的`Run`與`Size`分別為`0x0-0xF`、`0x1-0xA`,再加上`EOB (0x00)`與`ZRL (0xF0)`共162個code word。Standard huffman tables必須涵蓋所有code word。 使用方式如下,拿Table K3的category對應到Luminance的DC係數的Size。例如Size = 3, 對應的code word就是`0b100`。 ![](https://i.imgur.com/E3pgEON.png) 但即使存儲影像時選擇`Standard`也未必採用Standard huffman table,例如Photoshop CS2輸出的檔案,其Huffman table會依據quaility而定。 # 2. DC coefficient Encoding DC coeffiecient並非直接經過Huffman encoding直接寫入檔案,先前提過由於相鄰的區塊DC coefficient有所關係,使用DPCM可降低entropy。 ![](https://i.imgur.com/WIxYdbV.png) 但經過DPCM轉換有著Difference過大的問題,想像一下原始圖片數值介於`[0,255]`,假設經過DCT放大8倍,Difference同樣擴大8倍,這會造成code table非常巨大。因此進一步採用floating-point representation。 ### 2.1 Floating-point representation **Decode** Huffman table解碼出的值,對應到下方表格的`Size`、。`Size=5`代表要讀取5 bits的`Additional bits`,再對應到實際的值。在給定`Size`下,每個`Additional bits`出現的機率相近,使得entropy coding效率高。 DC Value (Size) | Additional bits | Value (DC Coef Difference) -|-|- 0x05|0b00101|-26 - `Size = 0` 具有特殊意義,代表該MCU結束(End of block) ![](https://i.imgur.com/5uo3nnx.png) ```python3= # https://github.com/yasoob/Baseline-JPEG-Decoder/blob/master/decoder.py def DecodeNumber(code, bits): l = 2 ** (code - 1) if bits >= l: return bits else: return bits - (2 * l - 1) DecodeNumber(0x05,0b00101) # -26 DecodeNumber(0x04,0b0101) # -10 ``` **Encode** ```python3= import math def EncodeNumber(value): code = int(math.log(abs(value))/math.log(2)) + 1 if value > 0: return code, value else: return code, value+2**code-1 EncodeNumber(15) # 0x4, 0b1111 EncodeNumber(-26) # 0x5, 0b00101 ``` **Example** 以一小段hex作為[範例](https://www.impulseadventure.com/photo/jpeg-huffman-coding.html),MCU 1, 2的DC Coef分別是 -512、+508 ![](https://i.imgur.com/HFcc6NH.png) --- # 3. AC coefficient Encoding Run-length encoding(RLE)具有以下特性: - 容易實作 - 無損壓縮 - 對資料內有連續出現的數值壓縮率佳 影像經過DCT及Quantization,數值集中在左上角,AC coefficient以zig-zag順序排成一維陣列,進行 (RLE)可以得到不錯的效果。 ### 3.1 Zig-zag sequence ![](https://i.imgur.com/Ag1pmP1.png) 觀察zig-zag matrix - 每條斜線上的點座標x+y均同,左上到右下x+y是遞增的 - 在同一條斜線上,x+y是奇數方向為右上至左下,反之亦然 我們可以拿[zigzag matrix](https://rosettacode.org/wiki/Zig-zag_matrix#Python)生成函式的一部份,窮舉所有座標對應的值,依照這些值進行遞增排序即為zig-zag order。 預先生成8x8 zig-zag matrix: ```python= zigzag = [ [0, 1, 5, 6, 14, 15, 27, 28], [2, 4, 7, 13, 16, 26, 29, 42], [3, 8, 12, 17, 25, 30, 41, 43], [9, 11, 18, 24, 31, 40, 44, 53], [10, 19, 23, 32, 39, 45, 52, 54], [20, 22, 33, 38, 46, 51, 55, 60], [21, 34, 37, 47, 50, 56, 59, 61], [35, 36, 48, 49, 57, 58, 62, 63], ] ``` ### 3.2 Run-length encoding [JPEG Entropy Coding](http://www.mee.tcd.ie/~sigmedia/pmwiki/uploads/Teaching.4S1b/handout6_4s1.pdf): > The codeword represents the **run-length of zeros before a non-zero coef and the Size of that coef.** This is then followed by the Additional Bits which define the coef amplitude and sign precisely. Size and Additional Bits are defined just as for DC coefs. 編碼方式: ``` (Run, Size) codeword + coefficient additional bits ``` - Run: run-length of zeros - Size & Additional bits: 剛才提到的Floating-point representation 步驟如下: 1. 拿 `(Run, Size)` 組合而成的位元組進行Huffman encoding得到codeword,例如 `Run=0x0A, Size=0x03, (Run, Size)=0xA3` 2. 後面放上`Additional bit` 之所以將`Run`和`Size`放在一起進行編碼,是因為`Run`和`Size`關聯性高(較小的係數通常前面0多,較大的係數前面0較少)。 :::info 有些特殊的組合: - `EOB=(0,0)` - `ZRL=(15,0)` > The procedure “Append EHUFSI(X’F0’) bits of EHUFCO(X’F0’)” codes a run of 16 zero coefficients (ZRL code of Figure F.1). The procedure “Code EHUFSI(0) bits of EHUFCO(0)” codes the end-of-block (EOB code). If the last coefficient (K = 63) is not zero, the EOB code is bypassed. - [ISO/IEC 10918-1:19, 90-91](https://www.w3.org/Graphics/JPEG/itu-t81.pdf) `EOB`代表後續無係數,`ZRL`用來與其他`(Run, Size)`使用表示更長的run,例如: ``` (ZRL) (4,3) 010 ``` 等同 ``` |----- x16-----| |- 4 -| AC 0 0 0 .... 0 0 0 0 0 0 0 -5 ``` ::: 如果不想對`(Run, Size)`進行Huffman encoding,可以使用上方的Table K.3。 **Example** ![](https://i.imgur.com/u3HYSac.png) # 4. Conclusion ![](https://i.imgur.com/M8PEMAq.png) 這篇弄清了係數如何進行編碼AC/DC coeffieient,下篇介紹如何將這些東西塞進檔案。此外,我們並未進行down-sampling,但後續仍需處理這部份的JPEG header。 # 5. Reference - :star: [Understanding and Decoding a JPEG Image using Python ](https://yasoob.me/posts/understanding-and-writing-jpeg-decoder-in-python/) - [JPEG Huffman Coding Tutorial](https://www.impulseadventure.com/photo/jpeg-huffman-coding.html)