读取JPEG头文件中的huffman表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangcheng_95/article/details/83245053

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

猜你喜欢

转载自blog.csdn.net/zhangcheng_95/article/details/83245053