ImageSharp源碼詳解之JPEG編碼原理(1)JPEG介紹


最近在看GitHub上的一個很火的項目是:ImageSharp。這是一個純.net core的圖像處理庫,沒有使用其他的任何依賴。在看這個項目過程中激發了我對圖像文件編碼解碼的興趣。於是從最簡單的BMP圖像開始看,到GIF格式卡了一段時間(主要卡在lzw編碼過程和數據塊中),到最后的JPEG格式(PNG格式不打算看了),經歷了半個月時間才梳理出個大概。趁着這個熱乎勁,我想寫下關於JPEG格式的系列文章,文章目錄暫定如下:

ImageSharp源碼詳解之JPEG編碼原理(1)JPEG介紹

ImageSharp源碼詳解之JPEG編碼原理(2)采樣

ImageSharp源碼詳解之JPEG壓縮原理(3)DCT變換

ImageSharp源碼詳解之JPEG壓縮原理(4)量化

ImageSharp源碼詳解之JPEG壓縮原理(5)熵編碼

ImageSharp源碼詳解之JPEG壓縮原理(6)C#源碼解析及調試技巧

1.JPEG介紹

JPEG(Joint Photographic Experts Group)是聯合圖像專家小組的英文縮寫。它由國際電話與電報咨詢委員會CCITT(The International Telegraph and Telephone Consultative Committee)與國際標准化組織ISO於1986年聯合成立的一個小組,負責制定靜態數字圖像的編碼標准。

小組一直致力於標准化工作,開發研制出連續色調、多級灰度、靜止圖像的數字圖像壓縮編碼方法,即JPEG算法。JPEG算法被確定為國際通用標准,其適用范圍廣泛,除用於靜態圖像編碼外,還推廣到電視圖像序列的幀內圖像壓縮。而用JPEG算法壓縮出來的靜態圖片文件稱為JPEG文件,擴展名通常為*.jpg、*.jpe*.jpeg。

JPEG專家組開發了兩種基本的壓縮算法、兩種數據編碼方法、四種編碼模式。具體如下:

壓縮算法:

1有損的離散余弦變換(Discrete Cosine Transform,DCT);

2 無損的預測技術壓縮。

數據編碼方法:

1哈夫曼編碼;

2算術編碼;

編碼模式:

1基於DCT順序模式:編/解碼通過一次掃描完成;

2基於DCT遞進模式:編/解碼需要多次掃描完成,掃描效果從粗糙到精細,逐級遞進;

3無損模式:基於DPCM,保證解碼后完全精確恢復到原圖像采樣值;

4層次模式:圖像在多個空間多種分辨率進行編碼,可以根據需要只對低分辨率數據作解碼,放棄高分辨率信息。

在我閱讀的源碼中,我關注的是離散余弦變換、哈夫曼編碼、基於DCT順序模式的編碼,這也是JPRG圖像常用的技術。

整體文件的大致結構如下:

SOI(0xFFD8)

APP0(0xFFE0)

[APPn(0xFFEn)]可選

DQT(0xFFDB)

SOF0(0xFFC0)

DHT(0xFFC4)

SOS(0xFFDA)

壓縮數據

EOI(0xFFD9)

我們解碼的時候大致都是按照上面順序進行解碼,關於上面這些標記,大家可以從文章結尾參考資料中看到他們的詳細信息,這里我不對這些標記展開描述,在后面用到的時候會提到。

2.JPEG壓縮的大致過程

2.1 編碼

對於一副圖像,編碼器首先需要填充這個圖像的一些頭信息,量化表,霍夫曼表。我們可以看ImageSharp中JpegEncoderCore這個類里面的的Encode方法如下:

 1            ...
 2            // Write the Start Of Image marker.
 3             this.WriteApplicationHeader(metadata);
 4             // Write Exif and ICC profiles
 5             this.WriteProfiles(metadata);
 6             // Write the quantization tables.
 7             this.WriteDefineQuantizationTables();
 8             // Write the image dimensions.
 9             this.WriteStartOfFrame(image.Width, image.Height, componentCount);
10             // Write the Huffman tables.
11             this.WriteDefineHuffmanTables(componentCount);
12             // Write the image data.
13             this.WriteStartOfScan(image);
14             // Write the End Of Image marker.
15             this.buffer[0] = JpegConstants.Markers.XFF;
16             this.buffer[1] = JpegConstants.Markers.EOI;
17             stream.Write(this.buffer, 0, 2);
18             stream.Flush();
19         }           
View Code

這一系列都是圍繞着WriteStartOfScan這個方法展開,這個方法就是對於圖像數據進行編碼,過程如下圖:

注意我們看到過程中YUV采樣后面是DCT變換、量化、熵編碼。實際過程中,YUV采樣過程中就包含后面DCT變換、量化、熵編碼這三個過程,只不過我們在描述的時候將其分開。在ImageSharp源碼中我們可以看到它使用的是標准霍夫曼表來進行編碼,這也是一般JPEG編碼器常用的方法,但這樣就和常規的霍夫曼編碼不一致,我們可以通過其他資料學習到霍夫曼編碼需要對原始數據遍歷兩次,一次構建霍夫曼樹,一次進行編碼。我在谷歌的guetzli項目中看到了針對不同圖像數據,構建不同霍夫曼樹結構的方法。

2.2 解碼

作為編碼的互逆過程,大致流程如下:

 

雖然我們了解了如何編碼,就能大致知道如何解碼,但是ImageSharp源碼中,對於解碼和編碼在代碼實現還是有區別的,后續我只分析JPEG的編碼過程。

3.后續

后面計划是先把量化和熵編碼的相關文章寫完,然后把imagesharp這個項目中調試技巧做一下分析解讀,最后再寫DCT變換。DCT涉及到傅里葉變換,如果要深入理解,其背后復雜的數學知識不是我所能講解的,在這一章盡量講解代碼技巧,數學公式不敢牽涉太多。

 參考文獻:

學習JPEG編碼需要參考大量的資料,這些資料側重點都不一樣,需要相互印證。對我來說看代碼比看文獻更快,我的方法就是通過調試代碼來驗證我的猜想,下面是我看過的一些資料,后面文章不在贅述:

1.itu-t81.pdf JPEG文件格式,標准的霍夫曼編碼參考的是這個資料,在ImageSharp源碼中可以下載獲取。

2.impulseadventure 這個網站里面可以找到一個叫JPEGsnoop的開源軟件,可以分析JPEG各個標記的具體數值,同時這個網站里面很多教程都是關於JPEG的編碼和解碼,我文章里面一些圖片都來自上面。

3.JPEG壓縮原理,一位大牛的博客,值得一看。

4.FluxJpeg 這個源碼只針對JPEG格式進行編碼與解析,所以看起來相對ImageSharp簡單很多,我前期就是先對照資料看的這個源碼,后面換成ImageSharp。

5.數據壓縮導論(第4版) 這本書很好,理論講解的很深入,例子舉的也很容易看懂。

6.實用數字信號處理-從原理到應用  這本書關於DFT的講解很深入,值得一看。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM