一种基于MATLAB的JPEG图像压缩具体实现方法.docx
《一种基于MATLAB的JPEG图像压缩具体实现方法.docx》由会员分享,可在线阅读,更多相关《一种基于MATLAB的JPEG图像压缩具体实现方法.docx(14页珍藏版)》请在冰豆网上搜索。
![一种基于MATLAB的JPEG图像压缩具体实现方法.docx](https://file1.bdocx.com/fileroot1/2023-4/21/b44e9eb1-0e3f-47e9-b469-82396da17b1d/b44e9eb1-0e3f-47e9-b469-82396da17b1d1.gif)
一种基于MATLAB的JPEG图像压缩具体实现方法
一种基于MATLAB的JPEG图像压缩具体实现方法
说明:
该方法主要是对FPGA硬件实现编码的一个验证,MATLAB处理时尽量选择了简单化和接近硬件实现需要。
JPEG编码解码流程:
BMP图像输入、8某8分块、DCT变换、量化、Zig_Zag扫描、获取DC/AC系数中间格式、Huffman熵编码、DC/AC系数Huffman熵解码,反zig_zag扫描、反量化、反DCT变换、8某8组合、解码图像显示。
下面根据具体代码解释实现过程。
1.BMP图像输入
A=imread('mei_b.bmp');%读取BMP图像矩阵R=int16(A(:
:
1))-128;%读取RGB矩阵,由于DCT时输入为正负输入,G=int16(A(:
:
2))-128;%使得数据分布范围-127——127B=int16(A(:
:
3))-128;通过imread函数获取BMP图像的R、G、B三原色矩阵,因为下一步做DCT转换,二DCT函数要求输入为正负值,所以减去128,使得像素点分布范围变为-127~127,函数默认矩阵A的元素为无符号型(uint8),所以如果直接相减差值为负时会截取为0,所以先用int16将像素点的值转为带符号整数。
网上很多都提到了第一步的YUV转换,但是由于MATLAB在实验时YUV转换后色差失真比较严重,这里没有进行YUV转换。
个人理解为YUV转换后经过非R/G/B原理显示器显示效果可能会比较好,或者如果图像有色差可以选择YUV调整。
为了方便,读入的图像像素为400某296,是8某8的50某37倍,所以代码里没有进行8某8的整数倍调整。
2.8某8分块R_8_8=R(1:
8,1:
8);%取出一个8某8块这里以R色压缩解码为例,后边解释均为R色编码解码过程,最后附全部代码。
R_8_8为:
3.DCT变换R_DCT=dct2(R_8_8);使用MATLAB函数dct2进行DCT变换,也可使用DCT变换矩阵相乘的方法,即R_DCT=A某R_8_8某AT,其中A为DCT变换矩阵。
R_DCT为:
4.量化
R_dct_=round(R_DCT./S);
使用JPEG标准亮度量化表S量化并取整,S为:
R_dct_为:
其中第一个数-14为DC系数,剩余63个数为AC系数,左上角低频,右下角高频,可以看出量化后已经将多数高频量丢弃,从而实现数据压缩。
5.Zig_Zag扫描
Rdct_c=rehape(R_dct_',1,64);Rdct_c_z=Rdct_c(zig);
利用rehape函数将量化后的矩阵转为[1,64]行向量,利用zig向量按位取值,进行Zig_Zag扫描。
其中Rdct_c为:
11~64位均为0;
zig为:
zig=[0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63];
Zig_Zag扫描后的向量Rdct_c_z为:
11~64位均为0;
可以看出通过zig向量按位取值准确实现了对量化后DC,AC系数的Zig_Zag扫描。
6.获取DC/AC系数的中间格式r_dc_diff=Rdct_c_z
(1)-r_dc;
用当前DC系数减去上一个8某8子块的DC系数得到两DC系数的差值作为DC系数中间值,因为图像相邻像素具有很大的相关性,这样做可以减小DC编码长度,进一步压缩代码,在解码的时候通过该差值依次获得各8某8子块DC系数。
r_dc=Rdct_c_z
(1);
解码之后用该代码将当前DC系数赋给r_dc作为下一次编码时求差值的参考值。
fori=2:
1:
64;
ifRdct_c_z(i)==0&&r_n<15&&i~=64r_n=r_n+1;
eleifRdct_c_z(i)==0&&r_n<15&&i==64r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;
eleifRdct_c_z(i)~=0&&r_n<15r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;
eleifRdct_c_z(i)~=0&&r_n==15r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;
eleifRdct_c_z(i)==0&&r_n==15r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;endend
通过该for循环获取AC系数中间格式并保存在向量Rdct_c_z中,奇数表示0的个数,偶数表示AC系数数值。
表示前两个数
是1,后边共有16某3+13=61个0,与量化表相同。
7.Huffman熵编码
熵编码可以根据Huffman算法对每个量化后的矩阵进行现场编码,但是这样会增加传输数据(需要传输编码表),所以这里采用标准HuffmanVLI编码表进行编码,VLI编码表如下:
数值位数编码000-1,110,1-3,-2,2,3200,01,10,11-7,-6,-5,-4,4,5,6,73000,001,010,…,101,110,111-15,…,-8,8,…1540000,0001,…,1110,1111-31,…,-16,16,…31500000,00001,…,11110,11111-63,…,-32,32,…636…-127,…,-64,64,…1277…-255,…,-128,128,…2558…-511,…,-256,256,…5119…-1023,…,-512,512,…102310…-2047,…,-1024,1024,…204711……12……13……14……15…熵编码后所得编码即为压缩后的代码,方便存储或者传输。
为了便于硬件实现,这里没有涉及到Huffman亮度表,而是依据VLI编码表,通过DC/AC系数的数值确定位数和编码(编码原理),熵编码由上表中的位数和编码两部分组成,即压缩后的编码包括两部分,然后再依据VLI编码表,通过位数和编码返回DC/AC系数(解码原理),编码中还包含了AC系数中0的个数。
0的个数和位数均用4bit二进制数表示。
r_huff=cell(r_ac_cnt+1,3);%%建立三列矩阵保存压缩后的编码,第一例为0的个数,第二列为编码长度,第三例为编码
forj=0:
1:
r_ac_cnt;ifj==0
[iz,code]=vli(r_dc_diff);%%通过vli编码函数对DC差值进行编码,获得DC差值编码长度和编码,vli函数见附录。
%[iz,code]=vli(r_dc);%%通过vli函数获取AC系数编码及编码长度
r_huff(1,1)=celltr(dec2bin(0));%lltr将二进制字符串转为cell格式放入矩阵
r_huff(1,2)=celltr(dec2bin(iz,4));%%将哈夫曼编码长度存为4bitr_huff(1,3)=celltr(dec2bin(code,iz));%%将哈夫曼编码转为二进制r_code_bit=r_code_bit+iz;%%计算编码长度ele
ifr_AC(2某j)==0
r_huff(j+1,1)=celltr(dec2bin(r_AC(2某j-1),4));%%将0的个数写入第一列r_huff(j+1,2)=celltr(dec2bin(0));
r_huff(j+1,3)=celltr(dec2bin(0));ele
r_huff(j+1,1)=celltr(dec2bin(r_AC(2某j-1),4));[iz,code]=vli(r_AC(2某j));
r_huff(j+1,2)=celltr(dec2bin(iz,4));%编码长度写入第二列r_huff(j+1,3)=celltr(dec2bin(code,iz));%编码写入第三列r_code_bit=r_code_bit+iz;%%计算编码长度endendend
压缩后的编码表r_huff如下:
此时已将8某8某8=512bit压缩为4+6某8+2+1+1=56bit。
8.DC/AC系数Huffman熵解码i_n=1;
fork=1:
1:
r_ac_cnt+1;ifk==1
[i_value]=i_vli(r_huff(1,2),r_huff(1,3))%%i_vli函数解码,i_vli通过编码长度和编码恢复DC/AC系数真值,函数见附录。
i_Rdct_c_z(1,i_n)=r_dc+i_value;%i_Rdct_c_z(1,i_n)=r_huff(1,3);i_n=i_n+1;
r_dc=Rdct_c_z
(1);ele
ifbin2dec(r_huff(k,1))==15&&bin2dec(r_huff(k,2))==0i_Rdct_c_z(1,i_n:
i_n+15)=0;%%出现中间格式(15,0)返16个0i_n=i_n+16;
eleifbin2dec(r_huff(k,1))==0&&bin2dec(r_huff(k,2))==0
i_Rdct_c_z(1,i_n)=0;%%出现中间格式(0,0)反1个0,没有具体分析这种情况到底是否存在,但是如果最后一位恰好为0,此时恰好开始新的中间格式计算,i=64时终止计算,则中间格式为(0,0)
i_n=i_n+1;ele
i_Rdct_c_z(1,i_n:
i_n+bin2dec(r_huff(k,1))-1)=0;%%哈夫曼编码矩阵r_huff中为二进制数,所以用到了bin2dec
i_n=i_n+bin2dec(r_huff(k,1));%%通过第一列分解重复的0
i_value=i_vli(r_huff(k,2),r_huff(k,3));%%通过第二三列,编码长度和编码解出AC系数真值
i_Rdct_c_z(1,i_n)=i_value;
%%将解码后的DC/AC系数放入向量i_Rdct_c_zi_n=i_n+1;endendend
9.反Zig_Zag扫描
i_Rdct_c=i_Rdct_c_z(i_zig);%%反zig_zag扫描i_Rdct_(1,1:
8)=i_Rdct_c(1:
8);%%变为矩阵形式i_Rdct_(2,1:
8)=i_Rdct_c(9:
16);i_Rdct_(3,1:
8)=i_Rdct_c(17:
24);i_Rdct_(4,1:
8)=i_Rdct_c(25:
32);i_Rdct_(5,1:
8)=i_Rdct_c(33:
40);i_Rdct_(6,1:
8)=i_Rdct_c(41:
48);i_Rdct_(7,1:
8)=i_Rdct_c(49:
56);i_Rdct_(8,1:
8)=i_Rdct_c(57:
64);
通过按位取值的方法进行反Zig_Zag扫描,并将扫描获得的向量转为8某8矩阵,其中:
i_zag为:
i_zig=[1,2,6,7,15,16,28,29,3,5,8,14,17,27,30,43,4,9,13,18,26,31,42,44,10,12,19,25,32,41,45,54,11,20,24,33,40,46,53,55,21,23,34,39,47,52,56,61,22,35,38,48,51,57,60,62,36,37,49,50,58,59,63,64];
i_Rdct_为:
(可见该矩阵与量化后的矩阵相同)
10.反量化、反DCT变换
i_Rdct=round(i_Rdct_.某S);%%反量化并取整i_R_8_8=round(idct2(i_Rdct));%%逆DCT变换其中i_R_8_8为:
(可见与DCT变换前差别不大)
11.解码图像显示
fori_r=1:
1:
37;
fori_c=1:
1:
50;
end
end用这样一个嵌套for循环将所有8某8子块进行基于DCT变换的JPEG编码解码处理,i_R(i_r某8-7:
i_r某8,i_c某8-7:
i_c某8)=i_R_8_8;在循环最后通过该语句将每一个8某8子块放到i_R矩阵中,然后i_R加128得到解码后R色像素矩阵i_RR。
分别对G、B像素矩阵做同样算法处理,得到解码后的像素矩阵i_GG、i_BB。
i_A(:
:
1)=i_RR;
i_A(:
:
2)=i_GG;
i_A(:
:
3)=i_BB;%%将解码后三元色矩阵放入三维矩阵u_i_A=uint8(i_A);将矩阵元素设为无符号整型imhow(u_i_A);成功!
!
!
压缩前后图像对比:
因为没有直接查询Huffman编码表,增加了0的个数和编码长度的编码,压缩比会稍微降低,该方法所获得的压缩率0.2022,即压缩了近5倍。
附录:
%%
%%功能:
JPEG图像压缩
%%说明:
该程序只是JPEG图像压缩算法的简单验证,为了便于处理,所压缩图像像素为400某296,是8某8的整数倍,使用标准哈夫曼编码表编码和解码,没有进行颜色修正,所以没有进行YUV转换,直接进行RGB编码压缩,R/G/B三原色均使用JPEG标准亮度量化矩阵进行量化clearall;clc;
A=imread('mei_b.bmp');%读取BMP图像矩阵
R=int16(A(:
:
1))-128;%读取RGB矩阵,由于DCT时输入为正负输入,G=int16(A(:
:
2))-128;%使得数据分布范围-127——127B=int16(A(:
:
3))-128;
S=[1611101624405161;%JPEG标准亮度量化矩阵1212141926586055;1413162440576956;1417222951878062;
zig=[0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,...%zig_zag扫描向量12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,...35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,...58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63];
i_zig=[1,2,6,7,15,16,28,29,3,5,8,14,17,27,30,43,...%反zig_zag扫描向量4,9,13,18,26,31,42,44,10,12,19,25,32,41,45,54,...11,20,24,33,40,46,53,55,21,23,34,39,47,52,56,61,...22,35,38,48,51,57,60,62,36,37,49,50,58,59,63,64];zig=zig+1;r_dc=0;r_n=0;
r_AC=zero;r_all_bit=0;
R_8_8=R(i_r某8-7:
i_r某8,i_c某8-7:
i_c某8);%取出一个8某8块
R_DCT=dct2(R_8_8);%对这一个8某8矩阵进行DCT变化R_dct_=round(R_DCT./S);%量化取整Rdct_c=rehape(R_dct_',1,64);
Rdct_c_z=Rdct_c(zig);%zig_zag扫描r_dc_diff=Rdct_c_z
(1)-r_dc;%求DC差值%r_dc=Rdct_c_z
(1);
fori=2:
1:
64;编码中间值,奇数为0的个数,偶数为AC数值ifRdct_c_z(i)==0&&r_n<15&&i~=64r_n=r_n+1;
eleifRdct_c_z(i)==0&&r_n<15&&i==64r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;
eleifRdct_c_z(i)~=0&&r_n<15r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;
eleifRdct_c_z(i)~=0&&r_n==15r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;
eleifRdct_c_z(i)==0&&r_n==15r_ac_cnt=r_ac_cnt+1;r_AC(1,2某r_ac_cnt-1)=r_n;
r_AC(1,2某r_ac_cnt)=Rdct_c_z(i);r_n=0;endend
r_huff=cell(r_ac_cnt+1,3);%%根据中间值查VLI标准编码表进行哈夫曼编码r_code_bit=0;%%因为编码后的值为二进制,所以建立cell型矩阵存放要发送编码forj=0:
1:
r_ac_cnt;ifj==0
[iz,code]=vli(r_dc_diff);%%通过vli编码函数对DC差值进行编码%[iz,code]=vli(r_dc);
r_huff(1,1)=celltr(dec2bin(0));%lltr将二进制字符串转为cell格式放入矩阵r_huff(1,2)=celltr(dec2bin(iz,4));%%将哈夫曼编码bit数存为4bitr_huff(1,3)=celltr(dec2bin(code,iz));%%将哈夫曼编码转为二进制r_code_bit=r_code_bit+iz;%%计算编码长度ele
ifr_AC(2某j)==0
r_huff(j+1,1)=celltr(dec2bin(r_AC(2某j-1),4));%%将0的个数写入第一列r_huff(j+1,2)=celltr(dec2bin(0));r_huff(j+1,3)=celltr(dec2bin(0));ele
r_huff(j+1,1)=celltr(dec2bin(r_AC(2某j-1),4));[iz,code]=vli(r_AC(2某j));
r_huff(j+1,2)=celltr(dec2bin(iz,4));%编码长度写入第二例r_huff(j+1,3)=celltr(dec2bin(code,iz));%编码写入第三例r_code_bit=r_code_bit+iz;%%计算编码长度endendend
r_all_bit=r_all_bit+r_ac_cnt某8+4+r_code_bit;%%计算三原色R压缩后的总编码bit数i_n=1;
fork=1:
1:
r_ac_cnt+1;ifk==1
[i_value]=i_vli(r_huff(1,2),r_huff(1,3))%%i_vli函数解码i_Rdct_c_z(1,i_n)=r_dc+i_value;%i_Rdct_c_z(1,i_n)=r_huff(1,3);
i_n=i_n+1;
r_dc=Rdct_c_z
(1);ele
ifbin2dec(r_huff(k,1))==15&&bin2dec(r_huff(k,2))==0
i_Rdct_c_z(1,i_n:
i_n+15)=0;%%出现中间格式(15,0)反16个0i_n=i_n+16;
eleifbin2dec(r_huff(k,1))==0&&bin2dec(r_huff(k,2))==0
i_Rdct_c_z(1,i_n)=0;%%出现中间格式(0,0)反1个0i_n=i_n+1;%%没有具体分析这种情况到底是否存在,但是如果最后一位恰好为0,
ele%%此时恰好开始新的中间格式计算,i=64时终止计算,则中间格式为(0,0)
i_Rdct_c_z(1,i_n:
i_n+bin2dec(r_huff(k,1))-1)=0;%%哈夫曼编码矩阵r_huff中为二进制数,所以用到了bin3dec
i_n=i_n+bin2dec(r_huff(k,1));%%通过第一列分解重复的0i_value=i_vli(r_huff(k,2),r_huff(k,3));%%通过第二三列,位数和编码解出编码真值
i_Rdct_c_z(1,i_n)=i_value;i_n=i_n+1;endendend
i_Rdct_c=i_Rdct_c_z(i_zig);%%反zig_zag扫描i_Rdct_(1,1:
8)=i_Rdct_c(1:
8);%%变为矩阵形式i_Rdct_(2,1:
8)=i_Rdct_c(9:
16);i_Rdct_(3,1:
8)=i_Rdct_c(17:
24);i_Rdct_(4,1:
8)=i_Rdct_c(25:
32);i_Rdct_(5,1:
8)=i_Rdct_c(33:
40);i_Rdct_(6,1:
8)=i_Rdct_c(41:
48);i_Rdct_(7,1:
8)=i_Rdct_c(49:
56);i_Rdct_(8,1:
8)=i_Rdct_c(57:
64);
i_Rdct=round(i_Rdct_.某S);%%反量化并取整i_R_8_8=round(idct2(i_Rdct));%%逆DCT变换
i_R(i_r某8-7:
i_r某8,i_c某8-7:
i_c某8)=i_R_8_8;%%将一个8某8子块放回R色新矩阵中endend
i_RR=i_R+128;%%范围调回至0——255%后边同理依次为G、B编码和解码g_dc=0;
g_n=0;
g_AC=zero;g_all_bit=0;
fori_r=1:
1:
37;fori_c=1:
1:
50;g_ac_cnt=0;
G_8_8=G(i_r某8-7:
i_r某8,i_c某8-7:
i_c某8);G_DCT=dct2(G_8_8);
G_dct_=round(G_DCT./S);
Gdct_c=rehape(G_dct_',1,64);
Gdct_c_z=Gdct_c(zig);
g_dc_diff=Gdct_c_z
(1)-g_dc;%g_dc=Gdct_c_z
(1);
fori=2:
1:
64;
ifGdct_c_z(i)==0&&g_n<15&&i~=64g_n=g_n+1;
eleifGdct_c_z(i)==0&&g_n<15&&i==64g_ac_cnt=g_ac_cnt+1;
g_AC(1,2某g_ac_cnt-1)=g_n;
g_AC(1,2某g_ac_cnt)=Gdct_c_z(i);g_n=0;
eleifGdct_c_z(i)~=0&&r_n<15g_ac_cnt=g_ac_cnt+1;
g_AC(1,2某g_ac_cnt-1)=g_n;
g_AC(1,2某g_ac_cnt)=Gdct_c_z(i);g_n=0;
eleifGdct_c_z(i)~=0&&g_n==15g_ac_cnt=g_ac_cnt+1;
g_AC(1,2某g_ac_cnt-1)=g_n;
g_AC(1,2某g_ac_cnt)=Gdct_c_z(i);g_n=0;
eleifGdct_c_z(i)==0&&g_n==15g_ac_cnt=g_ac_cnt+1;
g_AC(1,2某g_ac_cnt-1)=g_n;
g_AC(1,2某g_ac_cnt)=Gdc