JPEG圖像密寫研究(一) JPEG圖像檔案結構
【轉載】轉載自http://www.cnblogs.com/leaven/archive/2010/04/06/1705846.html
JPEG壓縮編碼算法的主要計算步驟如下:
(0) 8*8分塊。
(1) 正向離散餘弦變換(FDCT)。
(2) 量化(quantization)。
(3) Z字形編碼(zigzag scan)。
(4) 使用差分脈沖編碼調制(DPCM)對直流系數(DC)進行編碼。
(5) 使用行程長度編碼(RLE)對交流系數(AC)進行編碼。
(6) 熵編碼。
一、JPEG檔案格式介紹
JPEG檔案使用的資料存儲方式有多種。最常用的格式稱為JPEG檔案交換格式(JPEG File Interchange Format,JFIF)。而JPEG檔案大體上可以分成兩個部分:标記碼(Tag)和壓縮資料。
标記碼由兩個位元組構成,其前一個位元組是固定值0xFF,後一個位元組則根據不同意義有不同數值。在每個标記碼之前還可以添加數目不限的無意義的0xFF填充,也就說連續的多個0xFF可以被了解為一個0xFF,并表示一個标記碼的開始。而在一個完整的兩位元組的标記碼後,就是該标記碼對應的壓縮資料流,記錄了關于檔案的諸種資訊。
常用的标記有SOI、APP0、DQT、SOF0、DHT、DRI、SOS、EOI。
注意,SOI等都是标記的名稱。在檔案中,标記碼是以标記代碼形式出現。例如SOI的标記代碼為0xFFD8,即在JPEG檔案中的如果出現資料0xFFD8,則表示此處為一個SOI标記。
SOI,Start of Image,圖像開始
标記代碼 2位元組 固定值0xFFD8
APP0,Application,應用程式保留标記0
标記代碼 2位元組 固定值0xFFE0
包含9個具體字段:
① 資料長度 2位元組 ①~⑨9個字段的總長度
即不包括标記代碼,但包括本字段
② 辨別符 5位元組 固定值0x4A46494600,即字元串“JFIF0”
③ 版本号 2位元組 一般是0x0102,表示JFIF的版本号1.2
可能會有其他數值代表其他版本
④ X和Y的密度機關 1位元組 隻有三個值可選
0:無機關;1:點數/英寸;2:點數/厘米
⑤ X方向像素密度 2位元組 取值範圍未知
⑥ Y方向像素密度 2位元組 取值範圍未知
⑦ 縮略圖水準像素數目 1位元組 取值範圍未知
⑧ 縮略圖垂直像素數目 1位元組 取值範圍未知
⑨ 縮略圖RGB位圖 長度可能是3的倍數 縮略圖RGB位圖資料
本标記段可以包含圖像的一個微縮版本,存為24位的RGB像素。如果沒有微縮圖像(這種情況更常見),則字段⑦“縮略圖水準像素數目”和字段⑧“縮略圖垂直像素數目”的值均為0。
APPn,Application,應用程式保留标記n,其中n=1~15(任選)
标記代碼 2位元組 固定值0xFFE1~0xFFF
包含2個具體字段:
① 資料長度 2位元組 ①~②2個字段的總長度
即不包括标記代碼,但包括本字段
② 詳細資訊 資料長度-2位元組 内容不定
例如,Adobe Photoshop生成的JPEG圖像中就用了APP1和APP13兩個标記段分别存儲了一幅圖像的副本。
DQT,Define Quantization Table,定義量化表
标記代碼 2位元組 固定值0xFFDB
包含9個具體字段:
① 資料長度 2位元組 字段①和多個字段②的總長度
即不包括标記代碼,但包括本字段
② 量化表 資料長度-2位元組
a)精度及量化表ID 1位元組 高4位:精度,隻有兩個可選值
0:8位;1:16位
低4位:量化表ID,取值範圍為0~3
b)表項 (64×(精度+1))位元組 例如8位精度的量化表
其表項長度為64×(0+1)=64位元組
本标記段中,字段②可以重複出現,表示多個量化表,但最多隻能出現4次。
SOF0,Start of Frame,幀圖像開始
标記代碼 2位元組 固定值0xFFC0
包含9個具體字段:
① 資料長度 2位元組 ①~⑥六個字段的總長度
即不包括标記代碼,但包括本字段
② 精度 1位元組 每個資料樣本的位數
通常是8位,一般軟體都不支援 12位和16位
③ 圖像高度 2位元組 圖像高度(機關:像素),如果不支援 DNL 就必須 >0
④ 圖像寬度 2位元組 圖像寬度(機關:像素),如果不支援 DNL 就必須 >0
⑤ 顔色分量數 1位元組 隻有3個數值可選
1:灰階圖;3:YCrCb或YIQ;4:CMYK
而JFIF中使用YCrCb,故這裡顔色分量數恒為3
⑥顔色分量資訊 顔色分量數×3位元組(通常為9位元組)
a)顔色分量ID 1位元組
b)水準/垂直采樣因子 1位元組 高4位:水準采樣因子
低4位:垂直采樣因子
(曾經看到某資料把這兩者調轉了)
c) 量化表 1位元組 目前分量使用的量化表的ID
本标記段中,字段⑥應該重複出現,有多少個顔色分量(字段⑤),就出現多少次(一般為3次)。
DHT,Difine Huffman Table,定義哈夫曼表
标記代碼 2位元組 固定值0xFFC4
包含2個具體字段:
①資料長度 2位元組 字段①和多個字段②的總長度
即不包括标記代碼,但包括本字段
② 哈夫曼表 資料長度-2位元組
a)表ID和表類型 1位元組 高4位:類型,隻有兩個值可選
0:DC直流;1:AC交流
低4位:哈夫曼表ID,
注意,DC表和AC表分開編碼
b)不同位數的碼字數量 16位元組
c)編碼内容 16個不同位數的碼字數量之和(位元組)
本标記段中,字段②可以重複出現(一般4次),也可以緻出現1次。例如,Adobe Photoshop 生成的JPEG圖檔檔案中隻有1個DHT标記段,裡邊包含了4個哈夫曼表;而Macromedia Fireworks生成的JPEG圖檔檔案則有4個DHT标記段,每個DHT标記段隻有一個哈夫曼表。
DRI,Define Restart Interval,定義差分編碼累計複位的間隔
标記代碼 2位元組 固定值0xFFDD
包含2個具體字段:
①資料長度 2位元組 固定值0x0004,①~②兩個字段的總長度
即不包括标記代碼,但包括本字段
②MCU塊的單元中的重新開始間隔
2位元組 設其值為n,則表示每n個MCU塊就有一個
RSTn标記。第一個标記是RST0,第二個是
RST1等,RST7後再從RST0重複。
如果沒有本标記段,或間隔值為0時,就表示不存在重開始間隔和标記RST
SOS,Start of Scan,掃描開始 12位元組
标記代碼 2位元組 固定值0xFFDA
包含2個具體字段:
①資料長度 2位元組 ①~④兩個字段的總長度
即不包括标記代碼,但包括本字段
②顔色分量數 1位元組 應該和SOF中的字段⑤的值相同,即:
1:灰階圖是;3: YCrCb或YIQ;4:CMYK。
而JFIF中使用YCrCb,故這裡顔色分量數恒為3
③顔色分量資訊
a) 顔色分量ID 1位元組
b) 直流/交流系數表号 1位元組 高4位:直流分量使用的哈夫曼樹編号
低4位:交流分量使用的哈夫曼樹編号
④ 壓縮圖像資料
a)譜選擇開始 1位元組 固定值0x00
b)譜選擇結束 1位元組 固定值0x3F
c)譜選擇 1位元組 在基本JPEG中總為00
本标記段中,字段③應該重複出現,有多少個顔色分量(字段②),就出現多少次(一般為3次)。本段結束後,緊接着就是真正的圖像資訊了。圖像資訊直至遇到一個标記代碼就自動結束,一般就是以EOI标記表示結束。
EOI,End of Image,圖像結束 2位元組
标記代碼 2位元組 固定值0xFFD9
這裡補充說明一下,由于在JPEG檔案中0xFF具有标志性的意思,是以在壓縮資料流(真正的圖像資訊)中出現0xFF,就需要作特别處理。具體方法是,在資料0xFF後添加一個沒有意義的0x00。換句話說,如果在圖像資料流中遇到0xFF,應該檢測其緊接着的字元,如果是
1)0x00,則表示0xFF是圖像流的組成部分,需要進行譯碼;
2)0xD9,則與0xFF組成标記EOI,則圖像流結束,同時圖像檔案結束;
3)0xD0~0xD7,則組成RSTn标記,則要忽視整個RSTn标記,即不對目前0xFF和緊接的0xDn兩個位元組進行譯碼,并按RST标記的規則調整譯碼變量;
3)0xFF,則忽視目前0xFF,對後一個0xFF再作判斷;
4)其他數值,則忽視目前0xFF,并保留緊接的此數值用于譯碼。
為了造福大衆,我把自己定義的檔案頭結構體給他家參考一下,歡迎讨論~
typedef struct APP0 //應用程式保留标記0
{
//char value[2]; //标記代碼
unsigned char d_length[2]; //資料長度
unsigned char symbol[5]; //辨別符
unsigned char version[2]; //版本号
unsigned char density; //X和Y的密度機關
unsigned char x_density[2]; //X方向像素密度
unsigned char y_density[2]; //Y方向像素密度
unsigned char x_num; //縮略圖水準像素數目
unsigned char y_num; //縮略圖垂直像素數目
unsigned char* RGB_bitmap; //縮略圖RGB位圖
}APP0; APP0 app0;
typedef struct APPn //應用程式保留标記n,其中n=1~15(任選)
{
//char value[2]; //标記代碼
unsigned char d_length[2]; //資料長度
unsigned char* info; //詳細資訊,資料長度-2位元組
}APPn; APPn appn;
typedef struct DQT //定義量化表
{
//char value[2]; //标記代碼
unsigned char d_length[2]; //資料長度
unsigned char* table; //量化表,資料長度-2位元組
//a.精度及量化表ID 1位元組
//高4位:精度,隻有兩個可選值,0:8位;1:16位
//低4位:量化表ID,取值範圍為0~3
//b.表項(64×(精度 + 1))位元組
//例如8位精度的量化表,其表項長度為64×(0 + 1) = 64位元組
}DQT; DQT dqt;
typedef struct SOF0 //幀圖像開始
{
//char value[2]; //标記代碼
unsigned char d_length[2]; //資料長度
unsigned char accuracy; //精度
unsigned char height[2]; //圖像高度
unsigned char width[2]; //圖像寬度
unsigned char color; //顔色分量數
unsigned char* color_info; //顔色分量資訊
}SOF0; SOF0 sof0;
typedef struct DHT //定義哈夫曼表
{
//char value[2]; //标記代碼
unsigned char d_length[2]; //資料長度
unsigned char* H_table; //哈夫曼表,資料長度-2位元組
//a.表ID和表類型,1位元組,
//高4位:0:DC直流;1:AC交流
//低4位:哈夫曼表ID,注意,DC表和AC表分開編碼
//b.不同位數的碼字數量,16位元組
//c.編碼内容,16個不同位數的碼字數量之和(位元組)
}DHT; DHT dht;
typedef struct DRI //定義差分編碼累計複位的間隔
{
//char value[2]; //标記代碼
unsigned char d_length[2]; //資料長度
unsigned char interval[2]; //MCU塊的單元中的重新開始間隔
//設其值為n,則表示每n個MCU塊就有一個RSTn标記。
}DRI; DRI dri;
typedef struct SOS //掃描開始,12位元組
{
//char value[2]; //标記代碼
unsigned char d_length[2]; //資料長度
unsigned char color; //顔色分量數
unsigned char* color_info; //顔色分量資訊
unsigned char image_data[3]; //壓縮圖像資料
}SOS; SOS sos;