1. 程式人生 > >讀取JPEG標頭檔案中的huffman表

讀取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