JPEG格式研究——(2)JPEG檔案格式

迷路的鹿1202發表於2024-09-04

JPEG檔案除了影像資料之外,還儲存了與圖片相關的各種資訊,這些資訊透過不同型別的TAG儲存在檔案中。

TAG

JPEG透過TAG標記壓縮書記之外的資訊。所有的TAG都包含一個TAG型別,TAG型別大小為兩個位元組,位於一個TAG的最前面。TAG型別的第一個位元組一定為0xFF

以下是部分常見的TAG型別

TAG型別 數值 備註
SOF0 ~ SOF3
SOF5 ~ SOF7
SOF8 ~ SOF11
SOF13 ~ SOF15
FF C0 ~ FF C3
FF C5 ~ FF C7
FF C8 ~ FF CB
FF CD ~ FF CF
不同的編碼模式對應不同的SOF,詳見JPEG標準Table B.1。本文以常見的SOF0為研究物件
DHT FF C4 Define Huffman table(s),定義瞭解碼所需的哈夫曼表
RSTm FF D0 ~ FF D7 遇到時重置DC係數,數字會遞增,具體含義不清楚
SOI FF D8 Start of Image,表示影像檔案的開始
EOI FF D9 End of Image,表示影像檔案結束
SOS FF DA Start of scan,表示這一個TAG後就是壓縮的影像資料,記錄了DHT、DQT與影像不同部分的對應關係
DQT FF DB Define quantization table(s),定義瞭解碼所需的量化表
APP0 ~ APP15 FF E0 ~ FF EF 應用儲存的圖片相關資訊(如相機資訊等)

JPEG檔案中各種資料的分佈

壓縮資料中也是存在TAG的,雖然大部分TAG都在檔案開頭,但是也有少部分是例外。如EOI就在檔案的末尾,RSTm會出現在壓縮資料當中。

那麼問題來了,如果壓縮資料中有一個位元組本身就是0xFF怎麼辦?JPEG的做法是在0xFF後面再加一個位元組0x00,用於表示這不是一個TAG。

因為不知道解碼需要哪些TAG,我在嘗試寫JPEG解碼器的時候耗費了大量的時間在研究TAG上。總結出對於常見的JPEG圖片解碼需要的TAG:

1.SOI和EOI:用於確定檔案的開頭和結尾

2.DQT和DHT:儲存瞭解碼時需要用到的哈夫曼表和量化表

3.SOS:儲存了圖片不同部分的需要用哪個哈夫曼表

4.SOF:圖片的長和寬、取樣精度等、使用哪個量化表都儲存在SOF中

5.RST:部分圖片存在RST,遇到RST時要重置DC係數才能得到正確的影像

哈夫曼表與量化表

這裡有個小坑,我原先一直以為DQT和DHT都是一個TAG對應一個表,後來發現一個TAG可以不只一個表

哈夫曼表

哈夫曼表的儲存格式如下:

DHT格式

名稱 長度(bit) 備註
$Lh$ 16 表示這一個TAG的長度(包括TAG型別的兩個位元組)
$Tc$ 4 Table class,0=DC表,1=AC表
$Th$ 4 Huffman table destination identifier,表示該哈夫曼表的id
$L_i$ 8 表示這一長度的編碼個數
$V_{i,j}$ 8 表示編碼前的原始資料

一個DHT中的哈夫曼表個數可以透過長度Lh算出:

$$
Lh = 2 + \sum_{t=1}^{n} (17+m_t)
$$

其中

$$
m_t=\sum_{1}^{16} L_i
$$

除了TAG型別和Lh一個TAG只有一個外,其餘的都是每個哈夫曼表都有的。

Th之後是一個長度為16位元組的陣列,分別對應長度從1bit到16bit的編碼個數。

再之後存的是各個編碼對應的原始資料(以位元組為單位)。JPEG採用的正規化哈夫曼編碼,可以這些資訊推匯出資料編碼前後的對應關係。

量化表

DQT的結構與DHT結構相似,比DHT還稍微簡單一些
DQT格式

名稱 長度(bit) 備註
Lq 16 與Lh意義相同,表示這一TAG的長度
Pq 4 量化表的精度,0=8bit,1=16bit
Pq 4 量化表的id
Qk 8 量化表中的資料

量化表大小固定為8x8,也就是一個表有64個數,DQT長度與量化表個數也有類似的關係:

$$
Pq = 2 + \sum_{t=1}^n (65 + 64 \times Pq(t))
$$

SOF

SOF(Start of Frame) TAG的結構如下:

SOF格式

名稱 長度(bit) 備註
Lf 16 這一TAG的長度
P 8 取樣精度
Y 16 圖片的高度
X 16 圖片的寬度
Nf 4 Component的個數

Component的結構如下:

名稱 長度(bit) 備註
Ci 8 Compoenent的id
Hi 4 水平縮放因子
Vi 4 垂直縮放因子
Tqi 8 對應的量化表id

根據我的理解,這裡的Component個數相當於色度分量的個數,比如RGB和YUV都是3,灰度影像則是1.

SOS

SOS格式

名稱 長度(bit) 備註
Ls 16 這一TAG的長度
Ns 8 一個scan內的component數量
Ss 8 沒用
Se 8 沒用
Ah 4 沒用
Al 4 沒用

Scan中還描述了這一Scan內不同Component中哈夫曼表和量化表的對應關係:

名稱 長度(bit) 備註
Csi 8 透過id選擇Component
Tdi 4 透過id選擇DC哈夫曼表
Tai 4 透過id選擇AC哈夫曼表

到這裡JPEG解碼所需要的幾個重要TAG的結構就介紹完了,接下來就做好準備工作就可以開始解碼了。


參考資料

JPEG解碼系列部落格:多媒體-編解碼 - 隨筆分類 - OnlyTime_唯有時光 - 部落格園 (cnblogs.com)

JPEG標準:Microsoft Word - T081E.DOC (w3.org)

一個Rust寫的JPEG解碼器:MROS/jpeg_tutorial: 跟我寫 JPEG 解碼器 (Write a JPEG decoder with me) (github.com)

友情連結

我學習過程中寫的JPEG圖片檢視器:Ryan1202/my-tiny-jpeg-viewer: A Tiny Jpeg Viewer (github.com)

相關文章