讀取JPEG標頭檔案中的huffman表
讀取JPEG標頭檔案中的huffman表
實驗工具:UltrEdit、Matlab
實驗目的:提取出JPEG標頭檔案中的huffman表段,並構建huffman對映。
在講實驗前,先了解一下JPEG的格式。
一.JPEG格式
JPEG格式是一種常見的影象檔案壓縮格式,是一些未壓縮影象(如Tiff格式、bmp格式、png格式等),在經過JPEG壓縮後形成的影象檔案。影象的內容上會有一些肉眼不可見的失真。
JPEG壓縮的過程為:
1.將畫素點分塊(8x8);2.將塊內的畫素點做DCT變換,每個塊內得到1個DC係數和63個AC係數;3.將DC係數和AC係數量化;4.將DC係數和AC係數根據定義的Huffman表編碼成二進位制的壓縮資料(在編碼之前,還要對DC係數和AC係數做一些變換,如DC係數是按差值儲存,而AC係數是按行程長度碼儲存)。
因此,JPEG格式的圖象檔案可以分為兩個部分,即標頭檔案部分和壓縮資料部分。標頭檔案中儲存著一些影象的基本資訊,如影象大小、量化表、Huffman表等。JPEG的位元流結構如下所示:
影象以FFD8開始,以FFD9結尾。壓縮資料段以FFDA開始,即FFD8-FFDA之間的是JPEG影象的標頭檔案。Huffamn碼段則以FFC4為開頭。JPEG定義了一些標準的Huffman表(也可以使用自定義的Huffman表)。
一般來說,在彩色影象中,針對DC係數和AC係數一共定義了4張Huffman表,即亮度DC表、色度DC表、亮度AC表和色度AC表,而在灰度影象中,則只需要兩張表,即亮度DC表和亮度AC表。
二、實驗步驟
實驗使用灰度JPEG影象。
1.將一張灰度的JPEG影象,用UltraEdit軟體開啟。
可以直接看到影象的十六進位制格式。
可以看到影象以FFD8開頭,也可以找到壓縮資料段的開始標誌FFDA。在標頭檔案段中,可以找到兩個FFC4。
第一段FFC4是DC係數的Huffman表,而第二段則是AC係數的Huffman表。
以AC係數的Huffman表段為例,在FFC4之後,00 B5 10是表資訊。後面的16個,即00 02...一直到7D,表示編碼成相應長度的碼字數量,我們記為碼段1。由00 02...7D可知,編碼長度為1的有0個,編碼長度為16的有125個(AC係數一共有162個編碼)。在7D之後,即01 02 ...FA則是AC係數的VLC值,記為碼段2。在構建huffman對映時,就是根據碼段1和碼段2形成的。
2.將由UltraEdit開啟的這段十六進位制檔案儲存為.TXT檔案
這樣就可以方便Python、MATLAB、C等讀取。
3.下面開始介紹程式碼。
新建指令碼檔案,命名為read_huff.m,先將儲存好的.TXT檔案讀入,並將其儲存為cell。
clc;
clear;
filename='Lena_90.txt';
code=textread(filename,'%s');%讀取影象十六進位制檔案
之後便可以讀取其標頭檔案。分別讀取出DC係數和AC係數的huffman表段(捨棄了表資訊),儲存為huff_dc和huff_ac。
%讀取標頭檔案
for i=1:length(code)
if (char(code(i))=='FF')&(char(code(i+1))=='C4')&(char(code(i+4))=='00')
dc_first=i+5;
end
if (char(code(i))=='FF')&(char(code(i+1))=='C4')&(char(code(i+4))=='10')
dc_last=i-1;
end
end
for i=1:length(code)
if (char(code(i))=='FF')&(char(code(i+1))=='C4')&(char(code(i+4))=='10')
ac_first=i+5;
end
if (char(code(i))=='FF')&(char(code(i+1))=='DA')
ac_last=i-1;
end
end
huff_dc=code(dc_first:dc_last);%讀出的dc係數huffman表段
huff_ac=code(ac_first:ac_last);%讀出的ac係數huffman表段
新建一個函式檔案,命名為huff_table.m,用來構建huffman對映(程式碼稍後再貼)。
呼叫huff_table.m,可以分別得到DC係數和AC係數的對映結果,即dc_code,dc_length,ac_code和ac_length,以AC係數的對映結果為例,ac_code表示VLC值對映成的編碼(10進位制),ac_length則表示編碼長度。根據這兩個結果,可以將ac_code轉化為二進位制形式。
huffval_dc和huffval_ac則是直接從表中讀出來的原始值。huffval_ac是AC係數的VLC值,可以和ac_code_b形成一一對應。
huffval_dc=huff_dc(17:end);%dc係數的Huffman值
[dc_code,dc_length]=huff_table(huff_dc);
%將dc係數的Huffman程式碼轉換為二進位制
for i=1:length(dc_code)
dc_code_b(i)=string(dec2base(dc_code(i),2,dc_length(i)));
end
huffval_ac=huff_ac(17:end);%ac係數的Huffman值,即VLC值
%將ac係數的Huffman程式碼轉換為二進位制
[ac_code,ac_length]=huff_table(huff_ac);
for i=1:length(ac_code)
ac_code_b(i)=string(dec2base(ac_code(i),2,ac_length(i)));
end
%儲存標準的Huffman表
save huffman.mat ac_code_b huffval_ac dc_code_b huffval_dc;
最終的結果(ac_code_b):
下面給出huff_table.m的程式碼:
function [ehufco, ehufsi] = huff_table(huffman)
%將JPEG中的huffman錶轉化為huffman_table,
%返回程式碼表ehufco和程式碼長度表ehufsi
bits=huffman(1:16);
huffval=huffman(17:end);
k=0;
j=1;
for i=1:16
for j=1:hex2dec(bits{i})
huffsize(k+1)=i;
k=k+1;
end
end
huffsize(k+1)=0;
lastk=k;
code=0;
k=1;
si=huffsize(1);
while huffsize(k)>0
huffcode(k)=code;
code=code+1;
k=k+1;
while huffsize(k)==si
huffcode(k)=code;
code=code+1;
k=k+1;
end
if huffsize(k)==0
break;
end
code=code*2;
si=si+1;
while huffsize(k)~=si
code=code*2;
si=si+1;
end
end
for k=1:lastk
% i=hex2dec(huffval{k});
ehufco(k)=uint32(huffcode(k));
ehufsi(k)=uint32(huffsize(k));
end
end