JPEG文件格式介紹及解碼算法

JPEG文件的存儲格式有很多種肋拔,但最常用的是JFIF格式,即JPEG File Interchange Format。JPEG文件大體可以分為兩個部分:

(1)標記碼鸟蜡;由兩個字節(jié)構成乘粒,其中豌注,前一個字節(jié)是固定值0XFF代表了一個標記碼的開始,后一個字節(jié)不同的值代表著不同的含義灯萍。需要提醒的是轧铁,連續(xù)的多個0XFF可以理解為一個0XFF,并表示一個標記碼的開始旦棉。另外齿风,標記碼在文件中一般是以標記代碼的形式出現的。例如绑洛,SOI的標記代碼是0XFFD8救斑,即,如果JPEG文件中出現了0XFFD8真屯,則代表此處是一個SOI標記系谐。

(2)壓縮數據;一個完整的兩字節(jié)標記碼的后面讨跟,就是該標記碼對應的壓縮數據了纪他,它記錄了關于文件的若干信息。

一些典型的標記碼晾匠,及其所代表的含義如下所示:

SOI茶袒,Start Of Image, 圖像開始凉馆,標記代碼為固定值0XFFD8薪寓,用2字節(jié)表示;

APP0澜共,Application 0, 應用程序保留標記0向叉,標記代碼為固定值0XFFE0,用2字節(jié)表示嗦董;該標記碼之后包含了9個具體的字段:

(1)數據長度:2個字節(jié)母谎,用來表示(1)--(9)的9個字段的總長度,即不包含標記代碼但包含本字段京革;

(2)標示符:5個字節(jié)奇唤,固定值0X4A6494600幸斥,表示了字符串“JFIF0”;

(3)版本號:2個字節(jié)咬扇,一般為0X0102甲葬,表示JFIF的版本號為1.2;但也可能為其它數值懈贺,從而代表了其它版本號经窖;

(4)X,Y方向的密度單位:1個字節(jié),只有三個值可選梭灿,0:無單位钠至;1:點數每英寸;2:點數每厘米胎源;

(5)X方向像素密度:2個字節(jié),取值范圍未知屿脐;

(6)Y方向像素密度:2個字節(jié)涕蚤,取值范圍未知;

(7)縮略圖水平像素數目:1個字節(jié)的诵,取值范圍未知万栅;

(8)縮略圖垂直像素數目:1個字節(jié),取值范圍未知西疤;

(9)縮略圖RGB位圖:長度可能是3的倍數烦粒,保存了一個24位的RGB位圖;如果沒有縮略位圖(這種情況更常見)代赁,則字段(7)(8)的取值均為0扰她;

APPn, Application n, 應用程序保留標記n(n=1---15),標記代碼為2個字節(jié),取值為0XFFE1--0XFFFF芭碍;包含了兩個字段:

(1)數據長度徒役,2個字節(jié),表示(1)(2)兩個字段的總長度窖壕;即忧勿,不包含標記代碼,但包含本字段瞻讽;

(2)詳細信息:數據長度-2個字節(jié)鸳吸,內容不定;

DQT速勇,Define Quantization Table, 定義量化表晌砾;標記代碼為固定值0XFFDB;包含9個具體字段:

(1)數據長度:2個字節(jié)烦磁,表示(1)和多個(2)字段的總長度贡羔;即廉白,不包含標記代碼,但包含本字段乖寒;

(2)量化表:數據長度-2個字節(jié)猴蹂,其中包括以下內容:

(a)精度及量化表ID,1個字節(jié)楣嘁,高4位表示精度磅轻,只有兩個可選值,0:8位逐虚;1:16位聋溜;低4位表示量化表ID,取值范圍為0--3叭爱;

(b)表項撮躁,64(精度取值+1)個字節(jié),例如买雾,8位精度的量化表把曼,其表項長度為64(0+1)=64字節(jié);

本標記段中漓穿,(2)可以重復出現嗤军,表示多個量化表,但最多只能出現4次晃危;

SOFO叙赚,Start Of Frame, 幀圖像開始,標記代碼為固定值0XFFC0僚饭;包含9個具體字段:

(1)數據長度:2個字節(jié)震叮,(1)--(6)共6個字段的總長度;即鳍鸵,不包含標記代碼冤荆,但包含本字段;

(2)精度:1個字節(jié)权纤,代表每個數據樣本的位數钓简;通常是8位;

(3)圖像高度:2個字節(jié)汹想,表示以像素為單位的圖像高度外邓,如果不支持DNL就必須大于0;

(4)圖像寬度:2個字節(jié)古掏,表示以像素為單位的圖像寬度损话,如果不支持DNL就必須大于0;

(5)顏色分量個數:1個字節(jié),由于JPEG采用YCrCb顏色空間丧枪,這里恒定為3光涂;

(6)顏色分量信息:顏色分量個數*3個字節(jié),這里通常為9個字節(jié)拧烦;并依此表示如下一些信息:

(a)顏色分量ID: 1個字節(jié)忘闻;

(b)水平/垂直采樣因子:1個字節(jié),高4位代表水平采樣因子恋博,低4位代表垂直采樣因子齐佳;

(c)量化表:1個字節(jié),當前分量使用的量化表ID债沮;

本標記段中炼吴,字段(6)應該重復出現3次,因為這里有3個顏色分量疫衩;

DHT硅蹦,Define Huffman Table定義Huffman表,標記碼為0XFFC4闷煤;包含2個字段:

(1)數據長度童芹,2個字節(jié),表示(1)--(2)的總長度曹傀,即,不包含標記代碼饲宛,但包含本字段皆愉;

(2)Huffman表,數據長度-2個字節(jié)艇抠,包含以下字段:

(a)表ID和表類型幕庐,1個字節(jié),高4位表示表的類型家淤,取值只有兩個异剥;0:DC直流;1:AC交流絮重;低4位冤寿,Huffman表ID;需要提醒的是青伤,DC表和AC表分開進行編碼督怜;

(b)不同位數的碼字數量,16個字節(jié)狠角;

(c)編碼內容号杠,16個不同位數的碼字數量之和(字節(jié));

本標記段中,字段(2)可以重復出現姨蟋,一般需要重復4次屉凯。

DRI,Define Restart Interval眼溶,定義差分編碼累計復位的間隔悠砚,標記碼為固定值0XFFDD;

包含2個具體字段:

(1)數據長度:2個字節(jié)偷仿,取值為固定值0X0004哩簿,表示(1)(2)兩個字段的總長度;即酝静,不包含標記代碼节榜,但包含本字段;

(2)MCU塊的單元中重新開始間隔:2個字節(jié)别智,如果取值為n宗苍,就代表每n個MCU塊就有一個RSTn標記;第一個標記是RST0薄榛,第二個是RST1,RST7之后再從RST0開始重復讳窟;如果沒有本標記段,或者間隔值為0敞恋,就表示不存在重開始間隔和標記RST丽啡;

SOS,Start Of Scan硬猫,掃描開始补箍;標記碼為0XFFDA,包含2個具體字段:

(1)數據長度:2個字節(jié)啸蜜,表示(1)--(4)字段的總長度坑雅;

(2)顏色分量數目:1個字節(jié),只有3個可選值衬横,1:灰度圖裹粤;3:YCrCb或YIQ;4:CMYK蜂林;

(3)顏色分量信息:包括以下字段遥诉,

(a)顏色分量ID:1個字節(jié);

(b)直流/交流系數表ID噪叙,1個字節(jié)突那,高4位表示直流分量的Huffman表的ID;低4位表示交流分量的Huffman表的ID构眯;

(4)壓縮圖像數據

(a)譜選擇開始:1個字節(jié)愕难,固定值0X00;

(b)譜選擇結束:1個字節(jié),固定值0X3F猫缭;

(c)譜選擇:1個字節(jié)葱弟,固定值0X00;

本標記段中猜丹,(3)應該重復出現芝加,有多少個顏色分量,就重復出現幾次射窒;本段結束之后藏杖,就是真正的圖像信息了;圖像信息直到遇到EOI標記就結束了脉顿;

EOI蝌麸,End Of Image,圖像結束艾疟;標記代碼為0XFFD9来吩;

另外,需要說明的是蔽莱,在JPEG中0XFF具有標記的意思弟疆,所以在壓縮數據流(真正的圖像信息)中,如果出現了0XFF盗冷,就需要做特別處理了怠苔。方法是,如果在圖像數據流中遇到0XFF仪糖,應該檢測其緊接著的字符柑司,如果是:

(1)0X00,表示0XFF是圖像流的組成部分乓诽;需要進行譯碼帜羊;

(2)0XD9咒程,表示與0XFF組成標記EOI鸠天,即,代表圖像流的結束帐姻,同時稠集,圖像文件結束;

(3)0XD0--0XD7饥瓷,組成RSTn標記剥纷,需要忽視整個RSTn標記,即不對當前0XFF和緊接著的0XDn兩個字節(jié)進行譯碼呢铆,并按RST標記的規(guī)則調整譯碼變量晦鞋;

(4)0XFF,忽略當前0XFF,對后一個0XFF進行判斷悠垛;

(5)其它數值线定,忽然當前0XFF,并保留緊接著此數值用于譯碼确买;

需要說明的是斤讥,JPEG文件格式中,一個字(16位)的存儲使用的是Motorola格式湾趾,而不是Intel格式芭商。也就是說,一個字的高字節(jié)(高8位)在數據流的前面搀缠,低字節(jié)(低8位)在數據流的后面铛楣,與平時習慣的Intel格式有所不同。這種字節(jié)順序問題的起因在于早期的硬件發(fā)展上胡嘿。在8位CPU的時代蛉艾,許多8位CPU都可以處理16位的數據,但它們顯然是分兩次進行處理的衷敌。這個時候就出現了先處理高位字節(jié)還是先處理低位字節(jié)的問題勿侯。以Intel為代表的廠家生產的CPU采用先低字節(jié)后高字節(jié)的方式;而以Motorola,IBM為代表的廠家生產的CPU則采用了先高字節(jié)后低字節(jié)的方式缴罗。Intel的字節(jié)順序也稱為little-endian助琐,而Motorola的字節(jié)順序就叫做big-endian。而JPEG/JFIF文件格式則采用了big-endian格式面氓。下面的函數兵钮,實現了從intel格式到motolora格式的轉換
USHORT Intel2Moto(USHORT val)
{
BYTE highBits = BYTE(val / 256);
BYTE lowBits = BYTE(val % 256);
return lowBits * 256 + highBits;
}

解碼

1)讀入JPEG/JFIF文件的相關信息

按照JFIF文件格式,將JPEG文件相關的字段信息一一讀取出來舌界,并進行相應的解析掘譬。例如,圖像的寬度呻拌、高度葱轩、量化表、Huffman表藐握、水平/垂直采樣因子等靴拱。一般而言,JFIF格式文件的讀取順序依次為:

SOI字段猾普;

APP0字段袜炕;

APPn字段;

DQT字段初家;

SOFO字段偎窘;

DHT字段乌助;

SOS字段;

壓縮數據字段陌知;

EOI字段眷茁;

讀取JPEG文件相關信息的時候,有兩點需要特別注意:

(a)由于JPEG中以0XFF來做為特殊標記符纵诞,因此上祈,如果某個像素的取值為0XFF,那么實際在保存的時候浙芙,是以0XFF00來保存的登刺,從而避免其跟特殊標記符0XFF之間產生混淆。所以嗡呼,在讀取文件信息的時候纸俭,如果遇0XFF00,就必須去除后面的00南窗;即揍很,將0XFF00當做0XFF;

(b)JPEG文件中万伤,一個字(16位)的存儲是采用了Motorola格式(big-endian)窒悔,而不是我們常用的Intel格式(little-endian)。因此敌买,如果需要的話简珠,請在處理之間進行依次高低字節(jié)的轉換。

(2) 讀取Huffman表

在標記碼DHT之后虹钮,包含了一個或者多個Huffman表(通常是4個表)聋庵。對于一個Huffman表而言,它包含了以下三部分內容:

(a)表ID和表類型芙粱;1個字節(jié)祭玉;僅有4個可選的取值,0X00,0X01,0X10,0X11春畔,分別表示DC直流0號表脱货,DC直流1號表,AC交流0號表拐迁,AC交流1號表蹭劈;

(b)不同位數的碼字數量疗绣;前面提到,JPEG中的Huffman編碼表是按照編碼長度的位數以表格的形式保存的线召,而且,Huffman編碼表的位數只能是1--16位多矮,因此缓淹,這里用16個字節(jié)來分別表示1--16位的每種位長的編碼在Huffman樹中的個數哈打。

(c)編碼內容;該字段記錄了Huffman樹中各個葉子節(jié)點的權重讯壶,上一個字段(不同位數的碼字數量)的16個數值之和料仗,就是本字段的長度,也就是Huffman樹中葉子節(jié)點的個數伏蚊。

這里立轧,我們不妨以下面一段Huffman表的數據為例來說明情況(均以16進制表示):

11 00 02 02 00 05 01 06 01 00 00 00 00 00 00 00 00

00 01 11 02 21 03 31 41 12 51 61 71 81 91 22 13 32

以上數據串中第一行代表了Huffman表ID、表類型躏吊、不同位數的碼字數量信息氛改;

第一行的第一個字節(jié)0X11代表了表的ID和類型是AC交流1號表;

第一行的第2到第17字節(jié)代表了不同位數碼字的數量比伏。即胜卤,第2個字節(jié)00表示沒有位數為1的編碼;第3個和第4個字節(jié)的02表示位數為2和位數為3的編碼各有兩個赁项;第5個字節(jié)的00表示沒有位數為5的編碼葛躏。。悠菜。舰攒。此外,通過這些數據我們發(fā)現悔醋,此Huffman樹有0+2+2+0+5+1+6+1=17個葉子節(jié)點芒率。

第二行為編碼的內容,表明17個葉子節(jié)點按照從小到大的順序排列篙顺,即偶芍,權值依次為0,1,11,2,21,3,31,41...

(3) 構建Huffman樹

讀取到Huffman表的數據之后,就需要構建Huffman樹了德玫。其具體規(guī)則如下

(a)第一個編碼的數字必定為0匪蟀;如果第一個編碼的位數為1,就被編碼為0宰僧;如果第一個編碼的位數為2材彪,就被編碼為00;如果第一個編碼的位數為3琴儿,就被編碼為000段化。。造成。

(b)從第二個編碼開始显熏,如果它和它前面編碼具有相同的位數,則當前編碼是它前面的編碼加1晒屎;如果它的編碼位數比它前面的編碼位數大喘蟆,則當前編碼時它前面的編碼加1之后再在后面添加若干個0缓升,直到滿足編碼位數的長度為止。

還是以上面的數據為例:

第一行的第2個字節(jié)00表示沒有位數為1的編碼蕴轨;

第一行的第3個字節(jié)02表示位數為2的編碼有2個港谊;由于沒有位數為1的編碼,因此這里位數為2的編碼中的第一個為00橙弱,第二個為00+1=01歧寺;

第一行的第4個字節(jié)02表示位數為3的編碼有2個;因此棘脐,這里位數為3的編碼中的第一個為01+1=10成福,然后添加1個“0”,得到100荆残;位數為3的編碼中的第二個為100+1=101奴艾;

依次類推,可以得到如下的Huffman樹

Snip20161124_1.png

特別提醒的是内斯,如果中間有某個位數的編碼缺失蕴潦,例如,沒有4位的編碼俘闯,則應該在3位的編碼后面加1潭苞,添加2個“00”補足5位,形成下一個5位編碼真朗。

(4) DC系數的Huffman解碼

JPEG編碼階段我們講到此疹,DC系數是以(A,B)的中間形式進行編碼的。其中的A代表了B的二進制編碼位數遮婶,B則利用VLI進行編碼蝗碎。另外,88的圖像塊經過DCT變換之后得到的88的系數矩陣旗扑,經過Huffman編碼及RLE編碼之后蹦骑,寫入編碼數據的時候,DC系數也是被寫在數據流最前面的臀防。因此眠菇,解碼的時候,DC系數也是最先被讀取出來袱衷,假設捎废,我們一次性讀入了若干個字節(jié)長度的數據。其中的第一個字節(jié)代表了DC系數的Huffman編碼致燥,通過查找DC系數的Huffman表(亮度表或色度表)登疗,得到該Huffman編碼所在的組編號,該編號就是DC系數中間格式(A,B)中的A篡悟,也就是B的位數谜叹。例如,A=2搬葬,就代表B采用2位二進制數進行編碼荷腊。這樣一來,讀取接下來的A位二進制數急凰,將其譯碼為十進制,就得到了DC系數的差值。將該差值與上一個DC系數值相加羽杰,就得到了真正的當前DC系數的值瞬测。

(5) AC系數的Huffman解碼

處理完DC系數之后,接下來進行AC系數的譯碼工作床三,顯然一罩,這里依然需要讀取一個Huffman編碼,通過查找AC系數的Huffman編碼表撇簿,進行解碼聂渊,我們得到(A,B)的數據對,其中的A代表了0的個數四瘫,而B則代表了后面數據的位數汉嗽。例如,(2,3)就代表了當前AC系數之前有2個0找蜜,下一個需要讀取的二進制數據是3位饼暑。需要提醒的是,(0,0)代表EOB洗做,即88塊的編碼結束弓叛。接著,讀取B位二進制數據诚纸,進行譯碼邪码,我們就得到了AC系數的值。如此反復循環(huán)咬清,直到遇到EOB闭专,或者讀取了63個AC系數,我們就完成了一個88塊的系數矩陣的譯碼工作旧烧。

(6) 反量化

在譯碼得到了88的系數矩陣之后影钉,我們需要進行反量化工作。該步驟掘剪,就是將前一個步驟得到的88系數矩陣分別乘以8*8的量化矩陣即可平委。

(7) 反Zig-zag掃描

JPEG編碼過程中,為了編碼方便夺谁,采用了Zig-zag掃描廉赔,因此肉微,這里需要進行反Zig-zag掃描,重新排列88的反量化系數矩陣蜡塌。反Zig-zag掃描的輸入時88矩陣碉纳,輸出依然是8*8矩陣,只不過馏艾,數據的排列方式有所不同而已劳曹。

(8) DCT逆變換

DCT變換,將原始圖像變換到頻域琅摩,而DCT逆變換铁孵,就是要將數據從頻域變換回時域。

DCT逆變換的計算公式為:

![Uploading 1345131501_6624_203673.png . . .]

DCT逆變換的公式房资,可以改寫為:

1345131501_6624.png

其中A為矩陣:

1345131617_8476.png
1345131629_6848.png
1345131650_4581.png

左邊為未轉秩的數據順序蜕劝,右邊為轉秩之后的數據順序。

(9)顏色模式轉換

BMP圖片是以RGB顏色空間進行保存的轰异,因此熙宇,將JPEG解碼為BMP必須進行顏色模式的轉換。另外溉浙,由于DCT要求的定義域對稱烫止,所以,在編碼的時候將RGB的數值范圍從[0,255]統(tǒng)一減去128戳稽,將數值范圍轉換到[-128,127]的范圍內馆蠕。因此,解碼的時候惊奇,必須為每個顏色分量加上128互躬。另外需要注意的是,通過解碼變換之后得到的RGB的值有可能超過255或者小于0颂郎;如果小于0吼渡,就截斷為0,如果大于255乓序,就截取為255寺酪;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市替劈,隨后出現的幾起案子寄雀,更是在濱河造成了極大的恐慌,老刑警劉巖陨献,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盒犹,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機急膀,發(fā)現死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門沮协,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人卓嫂,你說我怎么就攤上這事慷暂。” “怎么了命黔?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵呜呐,是天一觀的道長就斤。 經常有香客問我悍募,道長,這世上最難降的妖魔是什么洋机? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任坠宴,我火速辦了婚禮,結果婚禮上绷旗,老公的妹妹穿的比我還像新娘喜鼓。我一直安慰自己,他們只是感情好衔肢,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布庄岖。 她就那樣靜靜地躺著,像睡著了一般角骤。 火紅的嫁衣襯著肌膚如雪隅忿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天邦尊,我揣著相機與錄音背桐,去河邊找鬼。 笑死蝉揍,一個胖子當著我的面吹牛链峭,可吹牛的內容都是我干的。 我是一名探鬼主播又沾,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼弊仪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了杖刷?” 一聲冷哼從身側響起撼短,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挺勿,沒想到半個月后曲横,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年禾嫉,在試婚紗的時候發(fā)現自己被綠了灾杰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡熙参,死狀恐怖艳吠,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情孽椰,我是刑警寧澤昭娩,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站黍匾,受9級特大地震影響栏渺,放射性物質發(fā)生泄漏。R本人自食惡果不足惜锐涯,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一磕诊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纹腌,春花似錦霎终、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涎劈,卻和暖如春广凸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背责语。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工炮障, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坤候。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓胁赢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親白筹。 傳聞我的和親對象是個殘疾皇子智末,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內容