在前面的章節,我們已經將 JPEG 圖檔的所有數據讀取出來了。接下來,只要再進行一些簡單的轉換,就能夠還原出原始圖像數據。
這些轉換依序爲
- 反量化
- 反 zigzag (因爲不產生數據,所以沒有在第一章列出來)
- 反向 DCT 變換
- 升採樣
- YCbCr 轉 RGB
其中的反量化、反 zigzag 、反向 DCT 變換都是對一個 block 執行,升採樣跟 YCbCr 將多個 block 合併回 MCU。
從 DQT 區段中,讀出了量化表,在 SOF0 區段,知道了各個顏色分量所對應的量化表 id 。
一個 block 跟一個量化表都是 8 * 8,將它們對應的位置相乘,就完成這個轉換了。虛擬碼如下:
let result[8][8];
for i in 0..8 {
for j in 0..8 {
result[i][j] = block[i][j] * quant_table[i][j];
}
}
zigzag 時,會將一個 block 以下圖(截自標準書)的順序重新排列:
反 zigzag 就是把這個過程還原回來。
虛擬碼:
let ZZ = [
[ 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 ]
];
let result[8][8];
for i in 0..8 {
for j in 0..8 {
let order = ZZ[i][j];
result[i][j] = block[order / 8][order % 8];
}
}
$ result[i][j] = \frac{1}{4}\sum{{x=0} ^7}\sum{{y=0} ^7}C_x C_y cos(\frac{(2i + 1)x\pi}{16}) cos (\frac{(2j + 1)y\pi}{16}) block[x][y] $
其中的
參考 mcu 與各分量如何對應一節,就可以得到 MCU 的任一個像素所對應的 Y, Cb, Cr 分量。
照公式變換而已,直接看虛擬碼:
# chomp 會將大於 255 的值變爲 255 、小於 0 的值變爲 0
let R = chomp(Y + 1.402*Cr + 128.0);
let G = chomp(Y - 0.34414*Cb - 0.71414*Cr + 128.0);
let B = chomp(Y + 1.772*Cb + 128.0);
解碼過程都放在 decoder.rs 裡。
此外,jpeg_tutorial 的 mcu 子命令可以打印出特定 MCU 解碼過程的各個階段,你可以善用它來除錯。
至此已經解出一個 MCU 的 RGB 值了,將圖像的各個 MCU 拼起來就能得到原圖啦!
你可以嘗試用能夠顯示圖形的函式庫把解碼完成的圖像顯示出來,或是輸出到格式簡單的圖檔後再用看圖軟體打開,就可以知道自己的解碼器是否正確運作。我推薦輸出成 ppm 格式,它真的超級簡單。