2008 年 9 月 04 日 作為一個基本的圖像壓縮方式,JPEG 已經(jīng)得到了廣泛的運用,但 JPEG 相關(guān)的基本原理,卻經(jīng)常被忽視,或解釋得很不確切。這里我們詳細(xì)討論一下 JPEG 的編碼原理,并結(jié)合實例來給出一個更加感性的認(rèn)識。JPEG 編碼的詳細(xì)過程有著諸多的信息可以給我們巨大的啟發(fā),我們在這里討論的就是要對這些信息做一個具體細(xì)致的分析,通過我們的討論,大家會對 JPEG 編碼過程中出現(xiàn)的內(nèi)容有一個確切的了解,并且能了解到這些內(nèi)容的來龍去脈。 本文以一個實際的產(chǎn)品為例,來說明 JPEG 在其中的應(yīng)用。 ![]() 本系統(tǒng)為一個嵌入式 Linux 網(wǎng)絡(luò)播放器,主要的功能為播放家庭網(wǎng)絡(luò)中的多媒體文件,在家庭客廳等環(huán)境中有著大量的應(yīng)用,它可以給用戶提供更方便快捷的媒體文件的播放方式,并能充分利用家庭音響系統(tǒng)的巨大功能,而非 PC 環(huán)境下有限的外部設(shè)備,大大改善了媒體文件的播放體驗。 系統(tǒng)主要的功能包括: ![]() 本系統(tǒng)架構(gòu)如下圖: ![]() 本系統(tǒng)是基于嵌入式 Linux 的一個應(yīng)用,使用的是 ucLinux 2.4.22,并使用了 microwindows 作為 GUI 界面,底層使用了 Linux kernel 的 FrameBuffer 作為顯示輸出。 此系統(tǒng)在兩個方面使用到了 JPEG 庫: 1、 UI 的顯示,即各種人機(jī)交互界面,考慮到用戶體驗,所以大量使用了貼圖來美化 UI 2、 JPEG 圖片文件的全屏播放,包括用戶手中的各種照片等 JPEG 是 Joint Photographic Experts Group 的縮寫,即 ISO 和 IEC 聯(lián)合圖像專家組,負(fù)責(zé)靜態(tài)圖像壓縮標(biāo)準(zhǔn)的制定,這個專家組開發(fā)的算法就被稱為 JPEG 算法,并且已經(jīng)成為了大家通用的標(biāo)準(zhǔn),即 JPEG 標(biāo)準(zhǔn)。 JPEG 壓縮是有損壓縮,但這個損失的部分是人的視覺不容易察覺到的部分,它充分利用了人眼對計算機(jī)色彩中的高頻信息部分不敏感的特點,來大大節(jié)省了需要處理的數(shù)據(jù)信息。 人眼對構(gòu)成圖像的不同頻率成分具有不同的敏感度,這個是由人眼的視覺生理特性所決定的。如人的眼睛含有對亮度敏感的柱狀細(xì)胞1.8億個,含有對色彩敏感的椎狀細(xì)胞0.08億個,由于柱狀細(xì)胞的數(shù)量遠(yuǎn)大于椎狀細(xì)胞,所以眼睛對亮度的敏感程度要大于對色彩的敏感程度。 總體來說,一個原始圖像信息,要對其進(jìn)行 JPEG 編碼,過程分兩大步: 1、 去除視覺上的多余信息,即空間冗余度 2、 去除數(shù)據(jù)本身的多余信息,即結(jié)構(gòu)(靜態(tài))冗余度 當(dāng)你拿到一個原始未經(jīng)處理的圖像,是由各種色彩組成的,即在一個平面上,有各種色彩,而這個平面是由水平和垂直方向上的很多點組成的。實際上,每個點的色彩,也即計算機(jī)能表示的每個像素點的色彩,能分解成紅、綠、藍(lán),即 RGB 三元色來表示,即這三種顏色的一定比例的混合就能得到一個實際的色彩值。 ![]() 所以,實際上,這個平面的圖像,可以理解為除了水平 X 和垂直 Y 以外,還有一個色彩值的 Z 的三維的系統(tǒng)。Z 代表了三元色中各個分支 R/G/B 的混合時所占的具體數(shù)值大小,每個像素的 RGB 的混合值可能都有所不同,各個值有大有小,但臨近的兩個點的 R/G/B 三個值會比較接近。 ![]() 由于這個原始圖像是由很多個獨立的像素點組成的,也就是說它們都是分散的,離散的。比如有些圖像的尺寸為640X480,就表示水平有640個像素點,垂直有480個像素點。 從上面的內(nèi)容,我們可以知道兩個相鄰的點,會有很多的色彩是很接近的,那么如何能在最后得到的圖片中,盡量少得記錄這些不需要的數(shù)據(jù),也即達(dá)到了壓縮的效果。 這個就要涉及到圖像信號的頻譜特性了。 圖像信號的頻譜線一般在0-6MHz范圍內(nèi),而且一幅圖像內(nèi),包含了各種頻率的分量。但包含的大多數(shù)為低頻頻譜線,只在占圖像區(qū)域比例很低的圖像邊緣的信號中才含有高頻的譜線。這個是對 JPEG 圖像壓縮的理論依據(jù)。 因此具體的方法就是,在對圖像做數(shù)字處理時,可根據(jù)頻譜因素分配比特數(shù):對包含信息量大的低頻譜區(qū)域分配較多的比特數(shù),對包含信息量低的高頻譜區(qū)域分配較少的比特數(shù),而圖像質(zhì)量并沒有可察覺的損傷,達(dá)到數(shù)據(jù)壓縮的目的。 將原始圖像這個色彩空間域,轉(zhuǎn)換為頻譜域,怎么轉(zhuǎn)呢,這個就用到了數(shù)學(xué)上的離散余弦變換,即 DCT(Discrete Cosine Transform) 變換。 DCT 是可逆的、離散的正交變換。變換過程本身雖然并不產(chǎn)生壓縮作用,但是變換后的頻率系數(shù)卻非常有利于碼率壓縮。即這個變換過程得到一個 DCT 變換系數(shù),而對這個系數(shù)可以再進(jìn)行更進(jìn)一步的處理,即所謂的量化。經(jīng)過量化,就能達(dá)到數(shù)據(jù)壓縮的作用了。 總體說來,這第一步,對圖像進(jìn)行編碼,去除多余的信息,要用到 DCT 變換中的正向 DCT(FDCT),然后再對變換的系數(shù)做量化(Quantization),這個過程就是依據(jù)的經(jīng)驗值,來處理人眼視覺系統(tǒng)所不敏感的高頻數(shù)據(jù),從而極大地減少了需要處理的數(shù)據(jù)量,這個是結(jié)合數(shù)學(xué)方法與經(jīng)驗值而做的處理。 利用 Huffman 編碼,來將最后的數(shù)據(jù)用無損的方式做壓縮,這個是純數(shù)學(xué)上的處理方式。 總體來說,上面的兩步即: 如果處理的是彩色圖像,JPEG 算法首先將 RGB 分量轉(zhuǎn)化成亮度分量和色差分量,同時丟失一半的色彩信息(空間分辨率減半)。然后,用 DCT 來進(jìn)行塊變換編碼,舍棄高頻的系數(shù),并對余下的系數(shù)進(jìn)行量化以進(jìn)一步減小數(shù)據(jù)量。最后,使用 RLE 行程編碼和 Huffman 編碼來完成壓縮任務(wù)。 下面將更加詳細(xì)地介紹這兩步中的各個細(xì)節(jié)。 JPEG 編碼中主要涉及到的內(nèi)容主要包括: 1. Color Model Conversion (色彩模型) 2. DCT (Discrete Cosine Transform 離散余弦變換) 3. 重排列 DCT 結(jié)果 4. 量化 5. RLE 編碼 6. 范式 Huffman 編碼 7. DC 的編碼 ![]() 在圖像處理中,為了利用人的視角特性,從而降低數(shù)據(jù)量,通常把 RGB 空間表示的彩色圖像變換到其他色彩空間。 現(xiàn)在采用的色彩空間變換有三種:YIQ,YUV 和 YCrCb。 每一種色彩空間都產(chǎn)生一種亮度分量信號和兩種色度分量信號,而每一種變換使用的參數(shù)都是為了適應(yīng)某種類型的顯示設(shè)備。 ![]() YUV 不是哪個英文單詞的縮寫,而只是符號,Y 表示亮度,UV 用來表示色差,U、V 是構(gòu)成彩色的兩個分量; YUV 表示法的重要性是它的亮度信號(Y)和色度信號(U、V)是相互獨立的,也就是 Y 信號分量構(gòu)成的黑白灰度圖與用 U、V 信號構(gòu)成的另外兩幅單色圖是相互獨立的。由于 Y、U、V 是獨立的,所以可以對這些單色圖分別進(jìn)行編碼。此外,黑白電視能接收彩色電視信號也就是利用了 YUV 分量之間的獨立性。 舉例來說明一下: 要存儲 RGB 8∶8∶8的彩色圖像,即 R、G 和 B 分量都用8位二進(jìn)制數(shù)(1個字節(jié))表示,圖像的大小為640×480像素,那么所需要的存儲容量為640×480×(1+1+1)=921 600字節(jié),即900KB,其中(1+1+1)表示 RGB 各占一個字節(jié)。 ![]() 如果用 YUV 來表示同一幅彩色圖像,Y 分量仍然為640×480,并且 Y 分量仍然用8位表示,而對每四個相鄰像素(2×2)的 U、V 值分別用相同的一個值表示,那么存儲同樣的一幅圖像所需的存儲空間就減少到640×480×(1+1/(2*2)+1/(2*2))=460 800字節(jié),即450KB。也就是把數(shù)據(jù)壓縮了一半。 ![]() 無論是用 YIQ、YUV 和 YCrCb 還是其他模型來表示的彩色圖像,由于現(xiàn)在所有的顯示器都采用 RGB 值來驅(qū)動,這就要求在顯示每個像素之前,須要把彩色分量值轉(zhuǎn)換成 RGB 值。 對電視機(jī),在考慮人的視覺系統(tǒng)和電視陰極射線管(CRT)的非線性特性之后,RGB 和 YUV 的對應(yīng)關(guān)系可以近似地用下面的方程式表示: ![]() 即: Y=0.3R+0.59G+0.11B U=B-Y V=R-Y 對計算機(jī)而言,計算機(jī)用的數(shù)字域的色彩空間變換與電視模擬域的色彩空間變換不同,它們的分量使用 Y、Cr 和 Cb 來表示,與 RGB 空間的轉(zhuǎn)換關(guān)系如下: ![]() 從這里,就可以看出,計算出來的 Y、Cr 和 Cb 分量,會出現(xiàn)大量的小數(shù),即浮點數(shù),從而導(dǎo)致了在 JPEG 編碼過程中會出現(xiàn)大量的浮點數(shù)的運算,當(dāng)然經(jīng)過一定的優(yōu)化,這些浮點數(shù)運算可以用移位與加法這些計算機(jī)能更快速處理的方式來對其進(jìn)行編碼。 RGB 與 YCrCb 之間的逆變換關(guān)系可寫成如下的形式: ![]() 總體來說,上面講的這些內(nèi)容,主要就是對原始圖片,可以先進(jìn)行色彩空間的處理,使采集到的圖像數(shù)據(jù)有所減少。 請注意,實際上,JPEG 算法與色彩空間無關(guān),色彩空間是涉及到圖像采樣的問題,它和數(shù)據(jù)的壓縮并沒有直接的關(guān)系。 因此“RGB 到 YUV 變換”和“YUV 到 RGB 變換”不包含在 JPEG 算法中。JPEG 算法處理的彩色圖像是單獨的彩色分量圖像,因此它可以壓縮來自不同色彩空間的數(shù)據(jù),如 RGB,YcbCr 和 CMYK。 在圖像中,它是由很多個點來組成的,那么存儲每個像素點所用的位數(shù)就叫做像素深度。對一個圖片,這個值是可以有所不同的,從而會使得圖片的數(shù)據(jù)有多和少的區(qū)別。 一幅彩色圖像的每個像素用 R,G,B 三個分量表示,若每個分量用8位,那么一個像素共用3X8=24位表示,就說像素的深度為24 bit,每個像素可以是2的24次方=16 777 216種顏色中的一種。表示一個像素的位數(shù)越多,它能表達(dá)的顏色數(shù)目就越多。 在用二進(jìn)制數(shù)表示彩色圖像的像素時,除 R,G,B 分量用固定位數(shù)表示外,往往還增加1位或幾位作為屬性(Attribute)位。例如,RGB 5∶5∶5表示一個像素時,用2個字節(jié)共16位表示,其中 R,G,B 各占5位,剩下一位作為屬性位。在這種情況下,像素深度為16位,而圖像深度為15 位。 在用32位表示一個像素時,若 R,G,B 分別用8位表示,剩下的8位常稱為 alpha 通道(alpha channel)位,或稱為覆蓋(overlay)位、中斷位、屬性位。它的用法可用一個預(yù)乘 α 通道(premultiplied alpha)的例子說明。假如一個像素(A,R,G,B)的四個分量都用歸一化的數(shù)值表示,(A,R,G,B)為(1,1,0,0)時顯示紅色。當(dāng)像素為 (0.5,1,0,0)時,預(yù)乘的結(jié)果就變成(0.5,0.5,0,0),這表示原來該像素顯示的紅色的強(qiáng)度為1,而現(xiàn)在顯示的紅色的強(qiáng)度降了一半。 這個 alpha 值,在這里就用來表示該像素如何產(chǎn)生特技效果。 總體來說,圖像的寬高、分辨率越高,就是組成一幅圖的像素越多,則圖像文件越大;像素深度越深,就是表達(dá)單個像素的顏色和亮度的位數(shù)越多,圖像文件就越大。 只有黑白兩種顏色的圖像稱為單色圖像(monochrome),每個像素的像素值用1位存儲,它的值只有“0”或者“1”,一幅640×480的單色圖像需要占據(jù)37.5 KB的存儲空間。 而灰度圖像,即有色深的黑白圖像,如果每個像素的像素值用一個字節(jié)表示,而不是僅僅只有一位,那么灰度值級數(shù)就等于256級,每個像素可以是0~255之間的任何一個值,一幅640×480的灰度圖像就需要占用300 KB的存儲空間,類似上面說到過的 Y 分量。 將圖像從色彩域轉(zhuǎn)換到頻率域,常用的變換方法有: ![]() DCT變換的公式為: ![]() f(i,j) 經(jīng) DCT 變換之后,F(xiàn)(0,0) 是直流系數(shù),其他為交流系數(shù)。 還是舉例來說明一下。 8x8的原始圖像: ![]() 推移128后,使其范圍變?yōu)?-128~127: ![]() 使用離散余弦變換,并四舍五入取最接近的整數(shù): ![]() 上圖就是將取樣塊由時間域轉(zhuǎn)換為頻率域的 DCT 系數(shù)塊。 DCT 將原始圖像信息塊轉(zhuǎn)換成代表不同頻率分量的系數(shù)集,這有兩個優(yōu)點:其一,信號常將其能量的大部分集中于頻率域的一個小范圍內(nèi),這樣一來,描述不重要的分量只需要很少的比特數(shù);其二,頻率域分解映射了人類視覺系統(tǒng)的處理過程,并允許后繼的量化過程滿足其靈敏度的要求。 當(dāng)u,v = 0 時,離散余弦正變換(DCT)后的系數(shù)若為F(0,0)=1,則離散余弦反變換(IDCT)后的重現(xiàn)函數(shù) f(x,y)=1/8,是個常 數(shù)值,所以將 F(0,0) 稱為直流(DC)系數(shù);當(dāng) u,v≠0 時,正變換后的系數(shù)為 F(u,v)=0,則反變換后的重現(xiàn)函數(shù) f(x,y) 不是常數(shù),此時 正變換后的系數(shù) F(u,v) 為交流(AC)系數(shù)。 DCT 后的64個 DCT 頻率系數(shù)與 DCT 前的64個像素塊相對應(yīng),DCT 過程的前后都是64個點,說明這個過程只是一個沒有壓縮作用的無損變換過程。 單獨一個圖像的全部 DCT 系數(shù)塊的頻譜幾乎都集中在最左上角的系數(shù)塊中。 DCT 輸出的頻率系數(shù)矩陣最左上角的直流 (DC)系數(shù)幅度最大,圖中為-415;以 DC 系數(shù)為出發(fā)點向下、向右的其它 DCT 系數(shù),離 DC 分量越遠(yuǎn),頻率越高,幅度值越小,圖中最右下角為2,即圖像信息的大部分集中于直流系數(shù)及其附近的低頻頻譜上,離 DC 系數(shù)越來越遠(yuǎn)的高頻頻譜幾乎不含圖像信息,甚至于只含雜波。 DCT 本身雖然沒有壓縮作用,卻為以后壓縮時的"取"、"舍" 奠定了必不可少的基礎(chǔ)。 量化過程實際上就是對 DCT 系數(shù)的一個優(yōu)化過程。它是利用了人眼對高頻部分不敏感的特性來實現(xiàn)數(shù)據(jù)的大幅簡化。 量化過程實際上是簡單地把頻率領(lǐng)域上每個成份,除以一個對于該成份的常數(shù),且接著四舍五入取最接近的整數(shù)。 這是整個過程中的主要有損運算。 以這個結(jié)果來說,經(jīng)常會把很多高頻率的成份四舍五入而接近0,且剩下很多會變成小的正或負(fù)數(shù)。 整個量化的目的是減小非“0”系數(shù)的幅度以及增加“0”值系數(shù)的數(shù)目。 量化是圖像質(zhì)量下降的最主要原因。 因為人眼對亮度信號比對色差信號更敏感,因此使用了兩種量化表:亮度量化值和色差量化值。 ![]() 使用這個量化矩陣與前面所得到的 DCT 系數(shù)矩陣: ![]() 如,使用?415(DC系數(shù))且四舍五入得到最接近的整數(shù) ![]() 總體上來說,DCT 變換實際是空間域的低通濾波器。對 Y 分量采用細(xì)量化,對 UV 采用粗量化。 量化表是控制 JPEG 壓縮比的關(guān)鍵,這個步驟除掉了一些高頻量;另一個重要原因是所有圖片的點與點之間會有一個色彩過渡的過程,大量的圖像信息被包含在低頻率中,經(jīng)過量化處理后,在高頻率段,將出現(xiàn)大量連續(xù)的零。 量化后的數(shù)據(jù),有一個很大的特點,就是直流分量相對于交流分量來說要大,而且交流分量中含有大量的0。這樣,對這個量化后的數(shù)據(jù)如何來進(jìn)行簡化,從而再更大程度地進(jìn)行壓縮呢。 這就出現(xiàn)了“Z”字形編排,如圖: ![]() 對于前面量化的系數(shù)所作的 “Z”字形編排結(jié)果就是: 底部 ?26,?3,0,?3,?3,?6,2,?4,1 ?4,1,1,5,1,2,?1,1,?1,2,0,0,0,0,0,?1,?1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 頂部 這樣做的特點就是會連續(xù)出現(xiàn)多個0,這樣很有利于使用簡單而直觀的行程編碼(RLE:Run Length Coding)對它們進(jìn)行編碼。 8×8圖像塊經(jīng)過 DCT 變換之后得到的 DC 直流系數(shù)有兩個特點,一是系數(shù)的數(shù)值比較大,二是相鄰8×8圖像塊的 DC 系數(shù)值變化不大。根據(jù)這個特點,JPEG 算法使用了差分脈沖調(diào)制編碼(DPCM)技術(shù),對相鄰圖像塊之間量化 DC 系數(shù)的差值(Delta)進(jìn)行編碼。即充分利用相鄰兩圖像塊的特性,來再次簡化數(shù)據(jù)。 即上面的 DC 分量-26,需要單獨處理。 而對于其他63個元素采用zig-zag(“Z”字形)行程編碼,以增加行程中連續(xù)0的個數(shù)。 Run Length Coding,行程編碼又稱“運行長度編碼”或“游程編碼”,它是一種無損壓縮編碼。 例如:5555557777733322221111111 這個數(shù)據(jù)的一個特點是相同的內(nèi)容會重復(fù)出現(xiàn)很多次,那么就可以用一種簡化的方法來記錄這一串?dāng)?shù)字,如 (5,6)(7,5)(3,3)(2,4)(l,7) 即為它的行程編碼。 行程編碼的位數(shù)會遠(yuǎn)遠(yuǎn)少于原始字符串的位數(shù)。 對經(jīng)過“Z”字形編排過的數(shù)據(jù),即可以用行程編碼來對其進(jìn)行大幅度的數(shù)據(jù)壓縮。 我們來用一個簡單的例子來詳細(xì)說明一下: 57,45,0,0,0,0,23,0,-30,-16,0,0,1,0,0,0,0 ,0 ,0 ,0,..,0 可以表示為 (0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-16) ; (2,1) ; EOB 即每組數(shù)字的頭一個表示0的個數(shù),而且為了能更有利于后續(xù)的處理,必須是 4 bit,就是說,只能是 0~15,這是的這個行程編碼的一個特點。 在直流 DC 系數(shù)經(jīng)過上面的 DPCM 編碼,交流 AC 系數(shù)經(jīng)過 RLE 編碼后,得到的數(shù)據(jù),還可以再進(jìn)一補(bǔ)壓縮,即使用 Huffman 編碼來處理。 范式 Huffman 編碼即 Canonical Huffman Code,現(xiàn)在流行的很多壓縮方法都使用了范式哈夫曼編碼技術(shù),如 GZIB、ZLIB、PNG、JPEG、MPEG 等。 對上面的例子中 RLC 后的結(jié)果,對它的存儲,JPEG 里并不直接保存這個數(shù)值,這樣主要是為了提高效率。 ![]() 對上面的例子內(nèi)容,就可以得到: 57 為第 6 組的,實際保存值為 111001,編碼為 (6,111001) 45編碼為 (6,101101) 23為(5,10111) -30為(5,00001) 這個時候前面的例子就變?yōu)椋?/p> (0,6),111001 ; (0,6),101101 ; (4,5),10111; (1,5),00001; (0,4) ,0111 ; (2,1),1 ; (0,0) 這樣,括號里的數(shù)值正好再合成一個字節(jié),高4位是前面0的個數(shù),低4位描述了后面數(shù)字的位數(shù);后面被編碼的數(shù)字表示范圍是 -32767..32767。 使用上面這個表簡化后的內(nèi)容,再到 Huffman 編碼表里去查詢,從而得到最后的編碼。 如06對應(yīng) Huffman 表的111000,那么 69 = (4,5) --- 1111111110011001 (69=0x45=4*16+5 ) 21 = (1,5) --- 11111110110 從而得到最后的結(jié)果: 111000 111001 ; 111000 101101 ; 1111111110011001 10111 ; 11111110110 00001… 使用范式 Huffman 編碼表的好處就是使得出現(xiàn)頻率高的數(shù)字小于8位,而出現(xiàn)頻率低的數(shù)字大于8位,這樣對整體而言,就會極大地減少數(shù)據(jù)量。 需要注意的是,在 JPG 文件中,一般有兩個 Huffman 表,一個是 DC 用,一個是 AC 用,它們是類似的。 對 DC 編碼的部分是單獨來處理的,并且是放在上面這個串的最前面。 總體來說,到目前為止,我們就得到了最后需要真正存儲用的簡化后,也即壓縮后的數(shù)據(jù)了。 介紹了 JPEG 的原理,我們再來結(jié)合一個具體的實例來詳細(xì)討論上面所涉及到的細(xì)節(jié)。 我們先來制作一個簡單的8X8大小的像素圖,然后把它存成JPEG格式。 方法是用 windows 的畫圖工具,定義一個8X8大小的圖,用一些色塊填充進(jìn)去,然后另存為 JPEG 格式,如 test8x8.jpg。如下圖所示: ![]() 保存成的文件后綴為 jpg,但按標(biāo)準(zhǔn)來說,它是一種 JFIF 格式標(biāo)準(zhǔn)的文件,里面的圖像的壓縮方式是 JPEG。 JFIF 是一個文件格式標(biāo)準(zhǔn),JPEG 是一個壓縮標(biāo)準(zhǔn),總體來說它們不是一個概念。 JFIF 是 JPEG File Interchange Format 的縮寫,也即 JPEG 文件交換格式。JFIF 是一個圖片文件格式標(biāo)準(zhǔn),它是一種使用 JPEG 圖像壓縮技術(shù)存儲攝影圖像的方法。JFIF 代表了一種"通用語言"文件格式,它是專門為方便用戶在不同的計算機(jī)和應(yīng)用程序間傳輸 JPEG 圖像而設(shè)計的語言。 JFIF 文件格式定義了一些內(nèi)容是 JPEG 壓縮標(biāo)準(zhǔn)未定義的,如 resolution/aspect ratio,color space 等。 ![]() 我們可以打開 JPEG 文件查看里面的內(nèi)容,即可看到上面的各個標(biāo)記段: ![]() 從圖上可以看出: 在頭部有 FFD8 ,表示圖像的開始;結(jié)束部分有 FFD9 ,表示圖像的結(jié)束。 在中間有兩個量化表 DQT 對應(yīng)的標(biāo)記 FFDB ; 還有圖像大小信息對應(yīng)的 FFC0 再后面有四個 Haffman 表對應(yīng)的 FFC4 ; 一般一個 JPG 文件里會有 2 類 Haffman 表:一個用于 DC 一個用于 AC ,也即實際有 4個表,亮度的 DC,AC 兩個,色度的 DC,AC 兩個。 然后是圖像數(shù)據(jù)段標(biāo)記 FFDA; 我們再來看看各個標(biāo)記的細(xì)部,具體分析一下各個部分的含義。 ![]() 上面的內(nèi)容,在標(biāo)記 FFE0 后,即為長度16。然后是5字節(jié)的 JFIF 標(biāo)識符號,說明這是一個 JPEG 壓縮的文件。然后是主/次版本號碼。下一個為 XY 像素的單位,這里為1,表示單位為點數(shù)/英寸。然后是 XY 方向的像素密度,這里是 96DPI,最后是縮略圖有關(guān)信息,這里為0。 ![]() 上面這個內(nèi)容,F(xiàn)FDB 標(biāo)記后的長度值為67,接下來的是 QT 信息,占一個字節(jié);這里是0,表示這個 QT 表編號為0,并且精度是8bit。然后后面就是64個8x8的 QT 表的各個 item 了。 也即第一個 DQT 量化表的內(nèi)容表示為十進(jìn)制是: ![]() 這個表即為 JPEG 亮度量化表。 第二個量化表的內(nèi)容為: ![]() 這個表的內(nèi)容即為 JPEG 色度量化表。 當(dāng)你打開不同的 JPEG 文件,你會看到這兩個表可能也是會有區(qū)別的。這個主要是使用了不同的量化方式的結(jié)果。 ![]() 上面這個內(nèi)容,F(xiàn)FC0 標(biāo)記后即是長度,為17,然后是一個字節(jié)的數(shù)據(jù)精度,通常是為8,代表樣本位數(shù)。接下來是圖片的高度,占兩字節(jié),這里即為8,然后是圖片的寬度,也為8,這也就是我們定義的8x8的內(nèi)容。然后是 component 的個數(shù),這里是3,表示 YUV。接下來是三組數(shù)據(jù),每組數(shù)據(jù)里,第一個是 component ID,第二個是采樣系數(shù),這里 Y 的采樣系數(shù)為22,說明垂直是2,水平是2。再后面就是量化表的編號了。 ![]() 上面這個內(nèi)容,F(xiàn)FC4 標(biāo)記后的內(nèi)容為數(shù)據(jù)長度,再接著的1字節(jié)為 Huffman Table 的信息,低4位是 HT ID 號,第5位是 HT 表類型標(biāo)記,再高三位是為0。 第一個 DHT 表,00,類型為 DC table,HT ID 號為 0; 第二個 DHT 表,10,類型為 AC table,HT ID 號也為 0; 第三個 DHT 表,01,類型為 DC table,HT ID 號為 1; 第四個 DHT 表,11,類型為 AC table,HT ID 號為 1; 即前兩個表為Y亮度分量的 DC/AC 表,后兩個為 UV 色度分量的 DC/AC 表。 以第一個表為例,因為長度只有 31,那么 00 后面的 16 字節(jié),即綠色部分: ![]() 組號為 1 的組中,代碼有 0 個; 組號為 2 的,代碼有 1 個; 組號為 3 的代碼有 5 個; 組號為 4/5/6/7/8/9 的代碼各 1 個。 總共 12 個。 再看后續(xù)的數(shù)據(jù): 00 01 02 03 04 05 06 07 08 09 0A 0B 即對應(yīng): ![]() 其他未出現(xiàn)的組號,對應(yīng)的數(shù)據(jù)未使用到。也就是說前面提到過的范式 Huffman 編碼里,目前只使用部分?jǐn)?shù)據(jù)即可,原因是這個 8x8 的圖像數(shù)據(jù)很小。 第二個 DHT 表就更復(fù)雜些了,長度有 181。 ![]() 這里 SOS 段,長度為 12,后面所含有的 component 數(shù)量為 3 個,也即 Y UV。然后后面是各 component 的編號,及對應(yīng)所使用的 Huffman 表的 ID 是多少。 在這個段的后面就是所有壓縮后的數(shù)據(jù)。直到結(jié)束的問題,即 FFD9,EOI(End Of Image)。 JPEG 在目前的應(yīng)用范圍是非常廣泛的,各種嵌入式系統(tǒng)中也大量地使用了 JPEG 壓縮,如 IPCAM 攝像頭、數(shù)字相機(jī)、移動存貯等。在這些領(lǐng)域由于傳輸數(shù)據(jù)的帶寬限制或者是存貯數(shù)據(jù)的容量的限制,常常需要使用圖像壓縮技術(shù)來將原始大量的圖像數(shù)據(jù)壓縮后在進(jìn)行傳輸或存貯,以充分利用帶寬與存貯空間,達(dá)到更好的利用效率。這樣,在嵌入系統(tǒng)中,就會使用到 JPEG 壓縮。而且由于嵌入系統(tǒng)的資源有限的特點,在很多情況下,很需要再對 JPEG 編碼壓縮的過程做更進(jìn)一步的優(yōu)化,我們這里詳細(xì)討論一下如何實現(xiàn)這些優(yōu)化。 我們回頭查看一下 JPEG 壓縮中的 DCT 變換過程,公式: ![]() 由于公式中有兩個 i/j=0~7 的部分,這樣要獲得一個 DCT 系數(shù),需要做 8 x 8=64 次乘法和 8 x 8=64 次加法, 而完成整個 8 x 8 像素的 DCT 需要 8 x 8 x 8 x 8=4096 次乘法和 8 x 8 x 8 x 8=4096 次加法. 計算量是相當(dāng)?shù)拇蟆?/p> 對于有些無浮點運算的嵌入式系統(tǒng)或無專門的數(shù)學(xué)運算協(xié)處理器的系統(tǒng),會造成大量的運算,極大地占用CPU的資源。 上面的公式屬于 DCT 的二維計算方式,經(jīng)過簡化,可以將其簡化為兩個一維的公式: ![]() 這樣,上面的過程就可以簡化為分別計算行和列的 DCT 變換。 對于一行來說需要計算的是 (8 x 8) 次乘法和 (8 x 8) 次加法,8 行就是 8 x (8 x 8) 次乘法和 8 x (8 x 8) 次加法,然后列也是相同,那么總數(shù)就為 2 x (8 x (8 x 8))=1024 次乘法和 2 x (8 x (8 x 8))=1024 次加法, 運算量變?yōu)槎S計算的1/4。 但是這樣的運算數(shù)量還是太大,還需要進(jìn)一步優(yōu)化。 在很多嵌入系統(tǒng)中,很多情況下需要不使用浮點運算,這樣就需要再找出一維 DCT 的一些規(guī)律,然后對其進(jìn)行進(jìn)一步的優(yōu)化。 在對一維 DCT 的運算中,還可以分為奇數(shù)列/行和偶數(shù)列/行 ![]() 對上面的處理,就又出現(xiàn)了多種優(yōu)化:ChenDCT,LeeDCT,AAN 算法和 LLM 算法。 其中 AAN 算法只需要 29 次加法和 5 次乘法。(注意,它是指每次一維運算要 29 次加法和 5 次乘法,一共是需要 29*8*2 次加法和 5*8*2 次乘法的)。 ![]() 其中 Y[0]-Y[7] 都是 1*8 的矩陣,X[1]-X[7] 也都是 1*8 的矩陣。 {a, b, c, d, e, f, g} = 1/2 { cos(pi/4), cos(pi/16), cos(pi/8), cos(3pi/16), cos(5pi/16), cos(3pi/8), cos(7pi/16) } 再對上面的含有 pi 的系數(shù)進(jìn)行整數(shù)優(yōu)化,從而避免浮點運算,就會得到: ![]() 其中: 3/8=1/4+1/8 5/8=1/2+1/8 7/8=1-1/8 上面的除以 2,除以 8,都可以通過移位來實現(xiàn),即右移一位和右移三位。即總數(shù)為 30 次加法,12 次移位即可。 這樣就在很大程度上將原本需要使用乘法,浮點運算的過程全部轉(zhuǎn)換成了簡單的加法和移位處理了,這樣使用數(shù)學(xué)的方法,用近似的值來完成整個轉(zhuǎn)換過程,會有很好的性能和處理效果。 在處理上面的數(shù)據(jù)中,可以使用一些中間變量來記錄中間結(jié)果,這樣就可以減少反復(fù)計算中間值,而直接使用已經(jīng)計算得到了的中間值。 tmp0 = x[0] + x[7]; tmp7 = x[0] - x[7]; tmp1 = x[1] + x[6]; tmp6 = x[1] - x[6]; tmp2 = x[2] + x[5]; tmp5 = x[2] - x[5]; tmp3 = x[3] + x[4]; tmp4 = x[3] - x[4];
tmp10 = tmp0 + tmp3; tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; /* 對偶數(shù)項進(jìn)行運算 X 0,4,6,2 */ X[0]=tmp1+tmp11; X[4]=tmp10 /2 - tmp11 X[6]=tmp12-(tmp13/4+ tmp13/8); X[2]=tmp12/4+tmp12/8-tmp13; 其他的各個值也是類似處理的。 六、JPEG 在本嵌入式 Linux 應(yīng)用中遇到的問題 在本系統(tǒng)中,提供給用戶一些播放圖片和預(yù)覽圖片的功能,在這個過程中就需要使用到對 JPEG 的處理。 1、JPEG 出錯的處理 在對圖片做預(yù)覽處理的時候,有些圖片原始尺寸很大,那么就需要將其轉(zhuǎn)換成較小的縮略圖,在轉(zhuǎn)換為縮略圖進(jìn)行顯示時,遇到了一個問題,即有時需要顯示的圖片,會導(dǎo)致系統(tǒng)無響應(yīng)。 后來查找原因,定位到 JPEG 文件的數(shù)據(jù)不完整,才導(dǎo)致 jpeg decoder 出現(xiàn)無響應(yīng)。 在前面的部分,有說到 JPEG 文件的格式中,JPEG 結(jié)束的標(biāo)記 EOI (End Of Image) 為“FFD9”。 如果需要顯示的圖片,在傳輸過程中,或轉(zhuǎn)換過程中,出現(xiàn)了沒有 EOI 數(shù)據(jù),那么應(yīng)該在程序中,將其廢棄,避免出現(xiàn)系統(tǒng)無響應(yīng)。 2、JPEG 解碼的效率優(yōu)化 在解碼 JPEG 時,可以使用 software decode 或 hardware decode 來處理。Hardware decode 的優(yōu)點是充分利用 DSP 所提供的硬件解碼功能,其解碼速度會較 software decode 有數(shù)量級的提高。但有時使用 hardware decode 有一些限制,如各種 DSP 提供的 SDK 會是直接訪問硬件,將 jpeg 直接輸出到顯示設(shè)備,從而會導(dǎo)致 hardware decode 與應(yīng)用系統(tǒng)集成的麻煩。 而使用 software decode,就能在應(yīng)用層完全掌握 jpeg decode 的數(shù)據(jù)緩沖結(jié)果,并可做一些圖片的疊加效果或?qū)ζ溥M(jìn)行半透明混合處理,從而會有較高的靈活性。并且使用 decode buffer cache,來將已經(jīng)解碼的數(shù)據(jù)進(jìn)行保存,在 UI 后續(xù)的使用中,就可不必反復(fù)去解碼 JPEG 圖片,從而也能有效提高繪圖效率。
上面的內(nèi)容是本人對 JPEG 原理做的一個詳細(xì)的實例分析,還介紹了 JPEG 編碼過程中對浮點運算的優(yōu)化處理,它特別適用于在資源有限的嵌入系統(tǒng)中避免大量的浮點運算。 在對 JPEG 原理做了一個詳細(xì)的分析后,大家會對 JPEG 涉及到的各個細(xì)節(jié)有了一個更加明確的認(rèn)識。當(dāng)你自己動手結(jié)合 JPEG 編碼過程來分析時,將會有一個清楚的全局觀。 本文結(jié)合應(yīng)用實例,對在嵌入式 Linux 應(yīng)用中遇到的 JPEG 有關(guān)的問題,做了一個說明,大家在自己的設(shè)計過程中也可以作為參考。 現(xiàn)在,你如果再回頭去看 JPEG 的原理,你現(xiàn)在應(yīng)該能看懂它整個過程的來龍去脈了。 |