Computer Science,  算法设计

JEPG图像压缩优化算法

数字图像处理

2020.4.18

摘要

JPEG(Joint Photographic Experts Group)是JPEG标准的产物,该标准由国际标准化组织(ISO)制订,是面向连续色调静止图像的一种压缩标准。JPEG格式是最常用的图像文件格式,后缀名为.jpg或.jpeg。其主要是采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式,它能够将图像压缩在很小的储存空间,一定程度上会造成图像数据的损伤。JPEG是面向连续色调静止图像的一种压缩标准。其最初目的是使用64Kbps的通信线路传输720×576 分辨率压缩后的图像。通过损失极少的分辨率,可以将图像所需存储量减少至原大小的10%。由于其高效的压缩效率和标准化要求,JPG图片已广泛用于彩色传真、静止图像、电话会议、印刷及新闻图片的传送上。                     

关键词:JPEG;图像;压缩效率;编码;离散余弦变换;有损

设计内容

本次设计任务,是以matlab为平台进行开发研究。主要研究的是jpeg方法压缩图片 在matlab中的实现。分析jpeg图像压缩的原理,以及对原理的深入研究。设计旨在利用matlab实现jpeg压缩过程,并比较各种编码方式对原图片的压缩效率,以探索更好的编码方式,实现更高的压缩效率。主要设计任务分为:

  1. 真彩色图片RGB通道转RUV通道,便于jpeg方式对数据的处理。
  2. 对图像信息转为matlab能够处理的矩阵信息。
  3. 按照jpeg要求对图片矩阵进行分块处理,即将矩阵分为8*8矩阵块。此后任务即为对每一个矩阵块的处理。矩阵不满足8*8即填充为8*8,并记录原始矩阵大小。
  4. 对分割后的8*8矩阵进行DCT离散余弦变换,分离高频部分与低频部分。使得矩阵第一个元素为直流分量,其余63个元素为低频分量。
  5. 利用量化表(工业分析的最佳表)对矩阵再处理,可由用户自己选择量化系数,量化系数越大,图像损失越大,压缩后大小越小,具体由操作者自己选择,其中对Y分量矩阵粗处理,其余两个分量矩阵细处理。得到量化后的矩阵。
  6. 采用不同的编码方式,对得到的量化表进行特定编码。并存储编码信息。

为了方便判断压缩是否成功,还需要判断压缩后的图像能否打开,即解压缩过程。实现最后的压缩效率比较。其设计任务为:

  1. 对特定的编码方式进行反编码,得到编码前的矩阵信息。
  2. 利用量化表,反推出量化前的矩阵信息(此处存在压缩损失,即有损压缩)。
  3. 对反量化后的表进行DCT逆运算。得到分割前的8*8矩阵。
  4. 将所有8*8矩阵按分割前的顺序合并,并利用原始矩阵大小切割矩阵,还原回原始信息。
  5. 利用matlab合并各个图层,输出图片。

           除了算法设计部分,还有UI设计部分,即利用c#设计UI,方便用户操作。要求做到,用户自己选择指定路径下的图片,进行压缩,压缩前可自由选择编码方式,以及量化系数,在反向解压缩时也要对应相应得编码方式以及量化系数。

系统功能层次图

操作界面功能
压缩过程
jjijiejie’yjie’ya解压过程

开发重点

  • 图像分割

Jpeg压缩前提是将图像分成8*8的小矩阵,对每个矩阵分别处理,这里用到  blkproc函数进行分块处理,并且图像不为8*8的倍数矩阵时要予以拼接得到倍数矩阵,并保留原大小信息进编码信息中,以便解压时还原为原图片格式

  • Zigzag之字形读取

采用matlab中矩阵搜索下标的方式,一个一个对应读取,再逆读取时还得设计中间变量实现数据交换。达到之字形读取。

  • Dpcm直流分量编码的还原

直流分量采用差分运算存储,因此采用原始方法,先把第一位被减数放入数列第一位,后面依次运算后每隔63个数放入一位,实现所有8*8矩阵直流分量的插入。

  • 矩形变化函数的问题

Matlab算法中,所有矩阵变形,比如reshape等都是按列读取,因此许多地方的还原等需要用到矩阵转置才行。要仔细审查小心错误。

  • 编码信息的存储

由于哈夫曼编码是变长编码,非等长,因此用到自创的编码方法,每凑够8位编入一个unit8型数据中。算是算法的突破,后续实验有效证明此压缩效率的优秀。

  • 不同编程平台之间数据通信

为解决c#与matlab之间数据通信问题,我采用一个信息沟通文档作为传递信息的标志,文档按固定格式编写,方便两边读取正确的配置信息。

设计主要算法分析

c#存取用户配置信息
string newTxtPath ="userdata.txt";//创建配置文件
FileStream fs = new FileStream(newTxtPath, FileMode.OpenOrCreate);
//获取要压缩图片的所在路径
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine("Usermode: ");
if (radioButton1.Checked)
{
    sw.WriteLine(1);
}
else if (radioButton2.Checked)
{
    sw.WriteLine(2);
 }
else (radioButton3.Checked)
{
    sw.WriteLine(3);
}//存取用户选择的编码方式,1为RLE编码,2为哈夫曼编码,3为混合
sw.WriteLine("Quantization coefficient:");
sw.WriteLine(comboBox1.Text);//存取用户选择的量化系数
sw.WriteLine("ExportBMP: ");
if (checkBox1.Checked)
   sw.WriteLine(1);
else
    sw.WriteLine(0);
    //存取用户是否选择导出图片的源位图
sw.WriteLine("Userfilepath: ");
sw.WriteLine(PATH);//存取要压缩图片的路径信息
sw.Close();

压缩过程:
1.读取配置信息,将真彩色图片RGB数据转为YUV数据
ini =fopen('userdata.txt','rb');
filemode = fgetl(ini);
filemode = fgetl(ini);//读取用户选择的编码方式
filequant = fgetl(ini);
filequant = fgetl(ini);//读取用户选择的量化系数
filedata = fgetl(ini);
filedata = fgetl(ini);//读取用户是否选择输出原位图
filename = fgetl(ini);
filename = fgetl(ini);//读取要压缩的文件路径
fclose(ini);
R = data(:,:,1);//R通道读取
G = data(:,:,2);//G通道读取
B = data(:,:,3);//B通道读取
Y = 0.299*R + 0.587*G + 0.114*B;//转换Y分量
U = - 0.1687*R - 0.3313*G + 0.5*B + 128;//转换U分量
V = 0.5*R - 0.4187*G - 0.0813*B + 128;//转换V分量

2.将矩阵扩充为8*8矩阵的倍数,方便切割
[len,wid]=size(a);//获取原始矩阵的行数列数
n=ceil(len/8);//扩充为8*8倍数矩阵的最少行数
m=ceil(wid/8);//扩充为8*8倍数矩阵的最少列数
decn = n*8-len;//需要补充的行数
decm = m*8-wid;//需要补充的列数
for i=1:decn
a(end+1,:)=128;
end//循环补充行数,元素值为128
for i=1:decm
a(:,end+1)=128;
end//循环补充列数,元素值为128

3.对得到的矩阵进行分割,分成8*8形式
利用blkproc函数,blkproc(Y,[8,8],…,…)即为将Y矩阵按8*8的方式分割,  对每一个8*8矩阵采用后续函数处理。

4.将分割矩阵元素分别减去128,方便DCT及量化处理
	  Y = Y - 128;//对Y中每个元素减去128

5.对得到矩阵进行DCT离散余弦变换,再进行量化处理得到频率处理后的信息
T = dctmtx(8);//获取离散余弦变换系数矩阵,可以增加数据处理效率
DCTTRS = blkproc(Y,[8,8],'P1*x*P2',T,T');//对每个8*8矩阵进行DCT 变换
QUANT = int8(blkproc(DCTTRS,[8,8],'x./P1',lighttable*qc));//对DCT变换后的矩阵进行量化,这里以Y分量为例,按位与Lighttable量化表相乘,qc为选择的量化系数,系数越大压缩比越大,损失越大,量化后数据采用int8格式存储,即近似为整数,方便编码存取。

6.对8*8矩阵进行Zigzag读取
Zsort = blkproc(QUANT,[8,8],'ZIGZAGING');//对8*8矩阵进行Zigzag
function ZIGZAG1 = ZIGZAGING(x)//ZIGZAGING函数
c = [2,9,17,10,3,4,11,18,25,33,26,19,12,5,6,13,20,27,34,41,49,42,35,28 ,21,14,7,8,15,22,29,36,43,50,57,58,51,44,37,30,23,16,24,31,38,45,  52,59,60,53,46,39,32, 40,47,54,61,62,55,48,56,63,64]; //按位读取
block1d=reshape(x',1,64); //把8*8矩阵按行排成一排
ZIGZAG1=block1d(c);利用C表按位读取指定位置元素形成Zigzag读取结果

7. 对矩阵信息进行编码处理
DPCM编码:
DPCM = blkproc(QUANT,[8,8],'dpcm');
%
function DC = dpcm(x)//函数dpcm
DC = int16([]);//转换为int16数据类型,存取数据。  
DC = [DC,x(1)];//将每个8*8矩阵的第一位(直流分量)存取
%
DPCM = reshape(DPCM',1,total);//将读取的dpcm按行排成一排
afDPCM(1) = DPCM(1);//预存取第一位,作为被减标志量
for i = 2 : total
    afDPCM(i) = DPCM(i) - DPCM(i-1);//直流分量做差存取减小数据大小
end
RLE编码:
Rle =RLEcode(Zsort);
function ret_dat = RLEcode(in_dat) //RLE编码函数
ret_dat = double([]);//定义RLE编码存取为double型 
in_dat = in_dat';//行转为列 
in_dat = in_dat(:) ;//按列合并
len = length(in_dat)  ;//获取Zigzag读取后的长度
c = 1;
while c <= len 
    pix_dat = in_dat(c);//预读取第一位
    count = 0;  
    while (c <= len) && (in_dat(c) == pix_dat)//统计连续同元素个数          
        count = count + 1;//连续相同则记录          
        c = c + 1;遍历量加一      
    end;  
    ret_dat = [ret_dat, pix_dat, count];RLE方式存取编码信息  
end;  
end
哈夫曼编码:
Zsort = Zsort';//行转为列 
Zsort = Zsort(:)' ;//按列合并
Min = min(Zsort);
Max = max(Zsort);
f = histc(Zsort(:), Min:Max); //求频率,既而求概率P
f = f(:)/sum(f);
symbols = find(f ~= 0);//由前面求f可知symbols中字符是升序排列的
p = f(symbols);读取存在概率的编码存取
symbols = symbols + Min - 1;恢复初始值
[dict, avglen] = huffmandict(symbols, p);获得编码字典和平均码长 
[ zipped, pad ] = HuffEnco(Zsort,dict);
%霍夫曼编码函数
function [ zipped, pad ] = HuffEnco( data, dict )
string = huffmanenco(data, dict);//Huffman编码结果:0-1串
string = uint8(string);//直接使用uint8型存储,1字节,大大节约空间
len = length(string);
pad = 8-mod(len,8);//补零: 便于每次取8个数的处理,存储
if pad>0,//如果string长度不能被8整除,则在其右端补零直到能被8整除为止
	string = [string uint8(zeros(1, pad))];
end
% 每8位存储为一个uint8型整数(0~255)
cols = length(string) / 8;//获取列数
string = reshape(string, 8, cols);//将string改编成8*cols的矩阵
weights = 2.^(0:7);形成1*8的矩阵
zipped = uint8(weights*double(string)); //矩阵相乘,得1*cols的矩  阵,unit8型
%霍夫曼编码得到的长度不一致,采用这种存取方法直接忽略长度参差问题
直接按满8位存取,能大大节省空间,这也是本项目最得力之处

save('codetableY.mat','dict');//存取霍夫曼编码表
混合编码:
即先RLE编码,在对RLE编码后的数据进行霍夫曼编码,本质是两段代码的前后拼接

8. 编码信息存储
	RLE编码存储方式
	l = length(Rle);//获取rle编码后长度
name = ['Export.rleV',filequant];//将量化系数存储进文件名
fid=fopen(name,'wb');//创建文件
fwrite(fid ,l,'int');//存取rle编码长度
fwrite(fid,len,'int');//存取原始图片行数
fwrite(fid,wid,'int');//存取原始图片列数
fwrite(fid,total,'int');//存取原始图片元素数目
fwrite(fid,afDPCM,'int8');//存取dpcm编码信息
fwrite(fid,Rle,'int16');//存取RLE编码信息
fwrite(fid,mm,'int');//存取Zigzag读取的行数
fwrite(fid,nn,'int'); //存取Zigzag读取的列数

哈夫曼编码存储方式
lz = length(zipped);//读取哈夫曼编码长度
name = ['Export.hfmV',filequant]//将量化系数存储进文件名
fid=fopen(name,'wb');//创建文件
fwrite(fid ,lz,'int');//存储编码长度
fwrite(fid,len,'int'); //存取原始图片行数
fwrite(fid,wid,'int'); //存取原始图片列数
fwrite(fid,total,'int'); //存取原始图片元素数目
fwrite(fid,afDPCM,'int8'); //存取dpcm编码信息
fwrite(fid,zipped,'uint8');//存取哈夫曼编码信息
fwrite(fid,mm,'int'); //存取Zigzag读取的行数
fwrite(fid,nn,'int'); //存取Zigzag读取的列数
fwrite(fid,pad,'int8');//存取按位存取补零标志位

混合编码
lz = length(zipped);//读取哈夫曼编码长度
name = ['Export.gpjV',filequant]//将量化系数存储进文件名
fid=fopen(name,'wb');//创建文件
fwrite(fid ,lz,'int');//存储编码长度
fwrite(fid,len,'int'); //存取原始图片行数
fwrite(fid,wid,'int'); //存取原始图片列数
fwrite(fid,total,'int'); //存取原始图片元素数目
fwrite(fid,afDPCM,'int8'); //存取dpcm编码信息
fwrite(fid,zipped,'uint8');//存取哈夫曼编码信息
fwrite(fid,mm,'int'); //存取Zigzag读取的行数
fwrite(fid,nn,'int'); //存取Zigzag读取的列数
fwrite(fid,pad,'int8');//存取按位存取补零标志位

	解压过程:
1.读取用户配置
ini =fopen('userdata.txt','rb');
filemode = fgetl(ini);
filemode = fgetl(ini);//读取反编码方式
filequant = fgetl(ini);
filequant = fgetl(ini);//读取反量化系数
fclose(ini);

2.读取编码信息
通用信息读取(不分先后)
t = fread(fid,2,'int');//读取原始图像行数和列数
hang = t(1);
lie = t(2)+total/hang;
if (filemode == '2')|(filemode == '3')
    pad = fread(fid,1,'int8');//如果涉及哈夫曼编码,读取补零标志
end

RLE编码信息读取
b = fread(fid,total,'int8');//按指定格式读取原始图片元素数目
 b = b';//数据转置
 a = fread(fid,l,'int16');//按指定格式读取编码信息长度
 a = a';//数据转置
c = 1;
for i = 1 : 2 : l
    count = a(i+1);
 for j = 1 : count
        deZsort(c) = a(i);
        c = c + 1;
 end 
end//将RLE编码折叠的信息展开

哈夫曼编码信息读取
b = fread(fid,total,'int8');//按指定格式读取原始图片元素数目
 b = b';//数据转置
 a = fread(fid,l,'uint8');//按指定格式读取编码信息长度
 a = a';//数据转置
dict =  load("codetableY.mat");//读取哈夫曼编码表
dict = struct2cell(dict);//转化为cell类型
dict = dict{1,1};//转化为矩阵参与运算
deZsort =HuffDeco( a, pad, dict );//还原信息
%解码函数
function [ decosig ] = HuffDeco( zipped, pad, dict )
len = length(zipped);//获取编码信息长度
string = repmat(uint8(0), 1, len.*8);//预先创建空串
bitindex = 1:8;
for index = 1:len //还原为0-1串 1*N
string(bitindex+8.*(index-1)) = uint8(bitget(zipped(index),bitindex));
end
string = logical( string(:)' ); 
len = length(string);
string( (len-pad+1) : end ) = [];// 去除pad位置的数
string = double(string);
decosig = huffmandeco(string, dict);初步还原后Huffman解码
%

混合编码信息读取
c = fread(fid,l,'uint8');//读取混合编码长度
c = c';//数据转置
dict =  load("MixcodetableY.mat");//读取哈夫曼编码表
   dict = struct2cell(dict);//转化为cell型
   dict = dict{1,1};//转化为数据矩阵
   shang =HuffDeco( c, pad, dict );//霍夫曼反编码
   b = shang(:,1:total);//交流分量值
   a = shang(:,total+1:end);//直流分量值
   c = 1;
   for i = 1 : 2 : length(a)
       count = a(i+1);
        for j = 1 : count
            deZsort(c) = a(i);
            c = c + 1;
        end 
  end //RLE反编码展开图像信息

3.DPCM逆编码还原直流分量
for i = 2 : total
    b(i) = b(i) + b(i-1);
end//反向求和还原直流分量
c = 64;
deZsort = [b(1),deZsort];预插入第一位
for i = 2 : total
   deZsort = [deZsort(:,1:c),b(i),deZsort(:,c+1:end)];
   c = c + 64;
end//每隔63位插入直流分量,还原矩阵信息

4.逆Zigzag读取还原矩阵
deZsort = reshape(deZsort,lie,hang);//利用reshape还原矩阵结构
deZsort = deZsort';//reshape是按照列取数据
deZigzag = blkproc(deZsort,[1,64],'deZigzaging');//8*8还原信息
%逆Zigzag读取函数
function DEZIGZAG = deZigzaging(x)
c = [1,2,9,17,10,3,4,11, 18,25,33,26,19,12,5,6,13,20,27,34,41,49,42,35,28,21,14,7,8,15,22,29,36,43,50,57,58,51,44,37,30,23,16,24,31,38,45,52,59,60,53,46,39,32,40,47,54,61,62,55,48,56,63,64];//读取顺序表  
for i = 1 : 64
    dez(i) = find(c == i);
end//转换中间表
DEZIGZAG = x(dez);//按正常顺序重新排序
DEZIGZAG = DEZIGZAG';//matlab为按列读取,故需要转置
DEZIGZAG = reshape(DEZIGZAG,8,8);//转换为8*8矩阵
DEZIGZAG = DEZIGZAG';//同理再次转置
%
5.利用量化表和反离散余弦变换函数还原信息
deQUANT = blkproc(deZigzag,[8,8],'x.*P1',lighttable*qc);
//利用读取的量化表,按位相乘有损还原量化前的信息
T = dctmtx(8);//读取DCT系数矩阵
deDCTTRS = blkproc(deQUANT,[8,8],'P1*x*P2',T',T)+128;
//进行DCT逆变换,还原位图信息,并恢复128的值
finalY =deDCTTRS(1:m,1:n);//切割初始添加的行数和列数,彻底还原

6.输出解压缩后的图片
R = finalY + 1.40* (finalV-128);//还原R通道矩阵
G = finalY - 0.34414*(finalU-128) - 0.71414*(finalV-128); //还原G通道矩阵
B = finalY + 1.772*(finalU-128); //还原B通道矩阵
data = cat(3,R,G,B);合并RGB通道
data = uint8(data);转换double为Uint8类型
imwrite(data,'final.bmp');//输出解压后的图片

系统测试分析

  • 标准图片压缩:

Rle编码压缩

Rle编码解压缩

哈夫曼编码压缩

哈夫曼编码解压缩

混合编码压缩

混合编码解压缩

  • 设计结果及结论

1.图像压缩纵向测试:

以标准图为例统计量化系数从1-10压缩效率变化(展示前4个压缩直观对比)

从统计数据可以看到随着量化系数的增大,其压缩比例不断增大。

从变化量增量来看,随着量化系数增大,压缩比例的增加率逐渐减小,预估其量化系数在4左右达到压缩比性最佳。

量化系数为1时压缩前后对比

量化系数为2时压缩前后对比

量化系数为3时压缩前后对比

量化系数为4时压缩前后对比

从纵向压缩损失直观图可见,到量化系数为4时,其图像损失已明显,但尚处于可接受范围。

2.横向对比标准jpeg压缩:

BMP图片  768KB
  标准jpg图片   100KB
量化系数1压缩图片 50.5KB
量化系数为4压缩图片 19.4KB

从直观反映看,量化系数1压缩图片质量与标准质量相当,但大小只有其一半,而量化系数为4时压缩损失尚在接受范围,且压缩大小为标准jog图片的5分之一,可见此压缩存储算法在一定程度上具有优越性。与之对应的压缩与解压缩时间的损失。

3.横向对比不同类型图片的压缩

系统界面型图片
   自然风景型图片
         设计海报型图片
动画漫画型图片
 人物人像型图片

压缩效率对比分析

从图中数据对比可以直观看出此jpeg算法在对系统性图片,海报及漫画等非自然类图像压缩的效率要远高于人像,风景等自然类图像。且图像中相邻像素越相近,压缩效果越好,对于非自然类图像,大部分像素的相近率要高于自然类图像,故此种情况下,压缩效率有明显区别。

总结

  1. 哈夫曼编码压缩效果普遍要好于RLE编码压缩效果,二者混合编码效果为三者最好。切使用此软件自创的编码信息存储方式,其1倍量化下压缩效果比标准jpg压缩效果好一倍,且图像损失相当,4倍量化下压缩效果是标准jpg图片大小的20%,且图像损失处于接受范围。
  2. 量化系数越大,压缩效果越好,图像损失越大,随着量化系数增大,压缩效率的增长率减小,其在4倍量化时效果最佳。
  3. 对于不同类型图片压缩,其在非自然类图像上的压缩率要高于非自然类,本质上即为连续像素点变化不大的图片压缩效果要优于相对变化大的图片。算法核心即为此。

源代码

操作界面:
Form1:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;

namespace imagecomp
{
    public partial class Form1 : Form
    {
        string PATH;
        string output;
        public Form1()
        {
            InitializeComponent();
            pictureBox1.Enabled = false;
            textBox1.ReadOnly = true;
            textBox2.ReadOnly = true;
            button2.Enabled = false;
            button3.Enabled = false;
            button4.Enabled = false;
            comboBox1.Items.Add(1);
            comboBox1.Items.Add(2);
            comboBox1.Items.Add(3);
            comboBox1.Items.Add(4);

        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.Multiselect = false;
            fileDialog.Title = "请选择文件";
            fileDialog.Filter = "图片(*.bmp,*.jpg,*.png)|*.bmp;*.jpg;*.png";
            if(fileDialog.ShowDialog() == DialogResult.OK)
            {
                Path.Text = fileDialog.FileName;
                textBox1_TextChanged(sender, e);
                PATH = fileDialog.FileName;
                button2.Enabled = true;
                button3.Enabled = true;
                button4.Enabled = true;
          }
        }
            public void textBox1_TextChanged(object sender, EventArgs e)
        {
            button2.Enabled = true;
            button3.Enabled = true;
            button4.Enabled = true;
        }
        void pictureBox1_LoadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            pictureBox1.UseWaitCursor = false;
            if (e.Error != null)
            {
                pictureBox1.Image=null;
                textBox2.Text = "加载失败请检查文件路径是否正确";
                textBox2_TextChanged(sender, e);
                return;
            }
            else
            {
                textBox2.Text = "加载成功";
                textBox2_TextChanged(sender, e);
            }
        }
        private void button2_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(Path.Text))
            {
                MessageBox.Show("未找到指定文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            pictureBox1.LoadCompleted += new AsyncCompletedEventHandler(pictureBox1_LoadCompleted);
            //图片加载时,显示等待光标
            pictureBox1.UseWaitCursor = true;
            //采用异步加载方式
            pictureBox1.WaitOnLoad = false;
            pictureBox1.LoadAsync(Path.Text);
        }
        private void button3_Click(object sender, EventArgs e)
        {
            string newTxtPath ="userdata.txt";
            FileStream fs = new FileStream(newTxtPath, FileMode.OpenOrCreate);
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine("Usermode: ");
            if (radioButton1.Checked)
            {
                sw.WriteLine(1);
            }
            else if (radioButton2.Checked)
            {
                sw.WriteLine(2);
            }
            else if (radioButton3.Checked)
            {
                sw.WriteLine(3);
            }
            else
            {
                sw.WriteLine(4);
            }
            sw.WriteLine("Quantization coefficient:");
            sw.WriteLine(comboBox1.Text);
            sw.WriteLine("ExportBMP: ");
            if (checkBox1.Checked)
                sw.WriteLine(1);
            else
                sw.WriteLine(0);
            sw.WriteLine("Userfilepath: ");
            sw.WriteLine(PATH);
            sw.Close();
            if (File.Exists("repack.exe"))
            {
                textBox4.Text = "压缩中";
                textBox4_TextChanged(sender, e);
                Process p = Process.Start("repack.exe");
                p.WaitForExit();
                textBox4.Text = "压缩完成";
                textBox4_TextChanged(sender, e);
                System.IO.FileInfo Origin = new FileInfo(PATH);
                origin.Text = Origin.Length.ToString();
                origin_TextChanged(sender, e);
                if (radioButton1.Checked)
                {
                    output = "Export.rleV"+ comboBox1.Text;
                }
                if(radioButton2.Checked)
                {
                    output = "Export.hfmV" + comboBox1.Text;
                }
                if (radioButton3.Checked)
                {
                    output = "Export.gpjV" + comboBox1.Text;
                }
                System.IO.FileInfo Packed = new FileInfo(output);
                packed.Text = Packed.Length.ToString();
                packed_TextChanged(sender, e);
                double P = double.Parse(Packed.Length.ToString());
                double O = double.Parse(Origin.Length.ToString());
                double Rant = P / O;
                rant.Text = Rant.ToString();
                rant_TextChanged(sender, e);
            }
            else
                MessageBox.Show("压缩程序丢失"+"\n"+"请确认程序完整性","错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        } 
        }
        private void button4_Click(object sender, EventArgs e)
        {
            if (File.Exists("unpack.exe"))
            {
                textBox4.Text = "解压中";
                textBox4_TextChanged(sender, e);
                Process p = Process.Start("unpack.exe");
                p.WaitForExit();
                textBox4.Text = "解压完成";
                textBox4_TextChanged(sender, e);
                if (checkBox2.Checked)
                {
                    Form2 fshow = new Form2();
                    fshow.Show();
                }
            }
            else
                MessageBox.Show("解压程序丢失" + "\n" + "请确认程序完整性", "错误",MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
Form2:
namespace imagecomp
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
            TopLevel = true;
            if (string.IsNullOrWhiteSpace("final.bmp"))
            {
                MessageBox.Show("未找到指定文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            pictureBox1.LoadCompleted += new AsyncCompletedEventHandler(pictureBox1_LoadCompleted);
            //图片加载时,显示等待光标
            pictureBox1.UseWaitCursor = true;
            //采用异步加载方式
            pictureBox1.WaitOnLoad = false;
            pictureBox1.LoadAsync("final.bmp");
        }
        void pictureBox1_LoadCompleted(object sender, AsyncCompletedEventArgs e)
        {
            pictureBox1.UseWaitCursor = false;
            if (e.Error != null)
            {
                pictureBox1.Image = null;
                MessageBox.Show("加载失败", "错误",MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
        }
}
压缩模块设计:
Mian:
clc
clear
ini =fopen('userdata.txt','rb');
filemode = fgetl(ini);
filemode = fgetl(ini);
filequant = fgetl(ini);
filequant = fgetl(ini);
filedata = fgetl(ini);
filedata = fgetl(ini);
filename = fgetl(ini);
filename = fgetl(ini);
fclose(ini);
qc = str2double(filequant);
data = imread(filename);
if filedata == '1'
   imwrite(data,'data.bmp'); 
end
data = double(data);
R = data(:,:,1);
G = data(:,:,2);
B = data(:,:,3);
Y = 0.299*R + 0.587*G + 0.114*B;
U = - 0.1687*R - 0.3313*G + 0.5*B + 128;
V = 0.5*R - 0.4187*G - 0.0813*B + 128;
load('lighttable.mat');
load('colortable.mat');
[len,wid]=size(Y);
n=ceil(len/8);
m=ceil(wid/8);
decn = n*8-len;
decm = m*8-wid;
for i=1:decn
Y(end+1,:)=128;
end
for i=1:decm
    Y(:,end+1)=128;
end
total = n*m;
T = dctmtx(8);
Y = Y - 128;
DCTTRS = blkproc(Y,[8,8],'P1*x*P2',T,T');
QUANT = int8(blkproc(DCTTRS,[8,8],'x./P1',lighttable*qc));
%Zigzag读取
Zsort = blkproc(QUANT,[8,8],'ZIGZAGING');
[mm,nn] = size(Zsort);
 %DC
 DPCM = blkproc(QUANT,[8,8],'dpcm');
 DPCM = reshape(DPCM',1,total);
 afDPCM(1) = DPCM(1);
 for i = 2 : total
     afDPCM(i) = DPCM(i) - DPCM(i-1);
 end 
 %AC
if filemode == '2' 
    Zsort = Zsort';%行转为列 
    Zsort = Zsort(:)' ;%按列合并
    Min = min(Zsort);
    Max = max(Zsort);
    f = histc(Zsort(:), Min:Max); % 求频率,既而求概率P
    f = f(:)/sum(f);
    symbols = find(f ~= 0); % 由前面求f可知symbols中字符是升序排列的
    p = f(symbols);
    symbols = symbols + Min - 1;
    [dict, avglen] = huffmandict(symbols, p); 
    [ zipped, pad ] = HuffEnco(Zsort,dict);
     save('codetableY.mat','dict');
    lz = length(zipped);
    name = ['Export.hfmV',filequant];
    fid=fopen(name,'wb');
    fwrite(fid ,lz,'int');
    fwrite(fid,len,'int');
    fwrite(fid,wid,'int');
    fwrite(fid,total,'int');
    fwrite(fid,afDPCM,'int8');
    fwrite(fid,zipped,'uint8');
    fwrite(fid,mm,'int');
    fwrite(fid,nn,'int');
    fwrite(fid,pad,'int8');
end
 if filemode == '1'
Rle =RLEcode(Zsort);
l = length(Rle);
 name = ['Export.rleV',filequant];
fid=fopen(name,'wb');
fwrite(fid ,l,'int');
fwrite(fid,len,'int');
fwrite(fid,wid,'int');
fwrite(fid,total,'int');
fwrite(fid,afDPCM,'int8');
fwrite(fid,Rle,'int16');
fwrite(fid,mm,'int');
fwrite(fid,nn,'int');
 end
  if filemode == '3'
      Rle = RLEcode(Zsort);
      shang = [afDPCM,Rle];
       shang = shang';%行转为列 
     shang = shang(:)' ;%按列合并
    Min = min(shang);
    Max = max(shang);
    f = histc(shang(:), Min:Max); % 求频率,既而求概率P
    f = f(:)/sum(f);
    symbols = find(f ~= 0); % 由前面求f可知symbols中字符是升序排列的
    p = f(symbols);
    symbols = symbols + Min - 1;
    [dict, avglen] = huffmandict(symbols, p); 
    [ zipped, pad ] = HuffEnco(shang,dict);
     save('MixcodetableY.mat','dict');
    lz = length(zipped);
     name = ['Export.gpjV',filequant];
    fid=fopen(name,'wb');
    fwrite(fid ,lz,'int');
    fwrite(fid,len,'int');
    fwrite(fid,wid,'int');
    fwrite(fid,total,'int');
    fwrite(fid,zipped,'uint8');
    fwrite(fid,mm,'int');
    fwrite(fid,nn,'int');
    fwrite(fid,pad,'int8');   
  end
fclose(fid);

Zigzag:
function ZIGZAG1 = ZIGZAGING(x)
c = [2,9,17,10,3,4,11,18,25,33,26,19,12,5,6,13,20,27,34,41,49,42,35,28,21,14,7,8,15,22,29,36,43,50,57,58,51,44,37,30,23,16,24,31,38,45,52,59,60,53,46,39,32,40,47,54,61,62,55,48,56,63,64]; 
block1d=reshape(x',1,64); 
ZIGZAG1=block1d(c);

RLEencode:
function ret_dat = RLEcode(in_dat) 
ret_dat = double([]);  
in_dat = in_dat';%行转为列 
in_dat = in_dat(:) ;%按列合并
len = length(in_dat)  ;
c = 1;
while c <= len 
    pix_dat = in_dat(c);  
    count = 0;  
    while (c <= len) && (in_dat(c) == pix_dat)          
        count = count + 1;          
        c = c + 1;      
    end;  
    ret_dat = [ret_dat, pix_dat, count];  
end;  
end

huffmanencode:
function [ zipped, pad ] = HuffEnco( data, dict )
string = huffmanenco(data, dict);  % Huffman编码结果:0-1串
string = uint8(string);            % uint8型,1字节
len = length(string);
pad = 8-mod(len,8);    !
if pad>0,   % 如果string长度不能被8整除,则在其右端补零直到能被8整除
	string = [string uint8(zeros(1, pad))];
end
% 每8位存储为一个uint8型整数(0~255)
cols = length(string) / 8;          % 列数
string = reshape(string, 8, cols);  % 将string改编成8*cols的矩阵
weights = 2.^(0:7);                 % 1*8的矩阵
zipped = uint8(weights*double(string)); % 矩阵相乘,得1*N的矩阵

DPCM:
function DC = dpcm(x)
DC = int16([]);   
DC = [DC,x(1)];


解压缩模块设计:
Main:
clc
clear
load('lighttable.mat');
load('colortable.mat');
ini =fopen('userdata.txt','rb');
filemode = fgetl(ini);
filemode = fgetl(ini);
filequant = fgetl(ini);
filequant = fgetl(ini);
fclose(ini);
qc = str2double(filequant);
if filemode == '1'
    name = ['Export.rleV',filequant];
fid = fopen(name,'rb');
end
if filemode == '2'
    name = ['Export.hfmV',filequant];
fid = fopen(name,'rb');
end
if filemode == '3'
    name = ['Export.gpjV',filequant];
    fid = fopen(name,'rb');
end
data = fread(fid,4,'int');
l = data(1);
m = data(2);
n = data(3);
total =data(4);
if filemode == '1'
    b = fread(fid,total,'int8');
    b = b';
    a = fread(fid,l,'int16');
    a = a';
end
if filemode == '2'
    b = fread(fid,total,'int8');
    b = b';
    a = fread(fid,l,'uint8');
    a = a';
end  
if filemode == '3'
    c = fread(fid,l,'uint8');
    c = c';
end   
t = fread(fid,2,'int');
hang = t(1);
lie = t(2)+total/hang;
if (filemode == '2')|(filemode == '3')
    pad = fread(fid,1,'int8');
end
if filemode == '1'
c = 1;
for i = 1 : 2 : l
    count = a(i+1);
 for j = 1 : count
        deZsort(c) = a(i);
        c = c + 1;
 end 
end
end
if filemode == '2'
    dict =  load("codetableY.mat");
    dict = struct2cell(dict);
    dict = dict{1,1};
    deZsort =HuffDeco( a, pad, dict );
end
if filemode == '3'
    dict =  load("MixcodetableY.mat");
    dict = struct2cell(dict);
    dict = dict{1,1};
    shang =HuffDeco( c, pad, dict );
    b = shang(:,1:total);
    a = shang(:,total+1:end);
    c = 1;
    for i = 1 : 2 : length(a)
        count = a(i+1);
         for j = 1 : count
             deZsort(c) = a(i);
             c = c + 1;
         end 
    end  
end
for i = 2 : total
    b(i) = b(i) + b(i-1);
end
c = 64;
deZsort = [b(1),deZsort];
for i = 2 : total
   deZsort = [deZsort(:,1:c),b(i),deZsort(:,c+1:end)];
   c = c + 64;
end
deZsort = reshape(deZsort,lie,hang);
deZsort = deZsort';%reshape是按照列取数据
deZigzag = blkproc(deZsort,[1,64],'deZigzaging');
deQUANT = blkproc(deZigzag,[8,8],'x.*P1',lighttable*qc);
T = dctmtx(8);
deDCTTRS = blkproc(deQUANT,[8,8],'P1*x*P2',T',T)+128;
finalY =deDCTTRS(1:m,1:n);
clear a b c count deDCTTRS deQUANT deZigzag deZsort hang lie i j l t
l = fread(fid,1,'int');
if filemode == '1'
    b = fread(fid,total,'int8');
    b = b';
    a = fread(fid,l,'int16');
    a = a';
end
if filemode == '2'
    b = fread(fid,total,'int8');
    b = b';
    a = fread(fid,l,'uint8');
    a = a';
end  
if filemode == '3'
    c = fread(fid,l,'uint8');
    c = c';
end   
t = fread(fid,2,'int');
hang = t(1);
lie = t(2)+total/hang;
if (filemode == '2')|(filemode == '3')
    pad = fread(fid,1,'int8');
end
if filemode == '1'
c = 1;
    for i = 1 : 2 : l
    count = a(i+1);
    for j = 1 : count
        deZsort(c) = a(i);
        c = c + 1;
    end  
    end
end
if filemode == '2'
    dict =  load("codetableU.mat");
    dict = struct2cell(dict);
    dict = dict{1,1};
    deZsort =HuffDeco( a, pad, dict );   
end
if filemode == '3'
    dict =  load("MixcodetableU.mat");
    dict = struct2cell(dict);
    dict = dict{1,1};
    shang =HuffDeco( c, pad, dict );
    b = shang(:,1:total);
    a = shang(:,total+1:end);
    c = 1;
    for i = 1 : 2 : length(a)
        count = a(i+1);
         for j = 1 : count
             deZsort(c) = a(i);
             c = c + 1;
         end 
    end  
end
for i = 2 : total
    b(i) = b(i) + b(i-1);
end
c = 64;
deZsort = [b(1),deZsort];
for i = 2 : total
   deZsort = [deZsort(:,1:c),b(i),deZsort(:,c+1:end)];
   c = c + 64;
end
deZsort = reshape(deZsort,lie,hang);
deZsort = deZsort';%reshape是按照列取数据
deZigzag = blkproc(deZsort,[1,64],'deZigzaging');
deQUANT = blkproc(deZigzag,[8,8],'x.*P1',colortable*qc);
T = dctmtx(8);
deDCTTRS = blkproc(deQUANT,[8,8],'P1*x*P2',T',T)+128;
finalU = deDCTTRS(1:m,1:n);
clear a b c count deDCTTRS deQUANT deZigzag deZsort hang lie i j l t
l = fread(fid,1,'int');
if filemode == '1'
    b = fread(fid,total,'int8');
    b = b';
    a = fread(fid,l,'int16');
    a = a';
end
if filemode == '2'
    b = fread(fid,total,'int8');
    b = b';
    a = fread(fid,l,'uint8');
    a = a';
end  
if filemode == '3'
    c = fread(fid,l,'uint8');
    c = c';
end   
t = fread(fid,2,'int');
hang = t(1);
lie = t(2)+total/hang;
if (filemode == '2')|(filemode == '3')
    pad = fread(fid,1,'int8');
end
if filemode == '1'
c = 1;
for i = 1 : 2 : l
    count = a(i+1);
 for j = 1 : count
        deZsort(c) = a(i);
        c = c + 1;
 end 
end
end
if filemode == '2'
    dict =  load("codetableV.mat");
    dict = struct2cell(dict);
    dict = dict{1,1};
    deZsort =HuffDeco( a, pad, dict );
end
if filemode == '3'
    dict =  load("MixcodetableV.mat");
    dict = struct2cell(dict);
    dict = dict{1,1};
    shang =HuffDeco( c, pad, dict );
    b = shang(:,1:total);
    a = shang(:,total+1:end);
    c = 1;
    for i = 1 : 2 : length(a)
        count = a(i+1);
         for j = 1 : count
             deZsort(c) = a(i);
             c = c + 1;
         end 
    end  
end
qqq = deZsort;
for i = 2 : total
    b(i) = b(i) + b(i-1);
end
c = 64;
deZsort = [b(1),deZsort];
for i = 2 : total
   deZsort = [deZsort(:,1:c),b(i),deZsort(:,c+1:end)];
   c = c + 64;
end
deZsort = reshape(deZsort,lie,hang);
deZsort = deZsort';%reshape是按照列取数据
deZigzag = blkproc(deZsort,[1,64],'deZigzaging');
deQUANT = blkproc(deZigzag,[8,8],'x.*P1',colortable*qc);
T = dctmtx(8);
deDCTTRS = blkproc(deQUANT,[8,8],'P1*x*P2',T',T)+128;
finalV = deDCTTRS(1:m,1:n);
R = finalY + 1.40* (finalV-128);
G = finalY - 0.34414*(finalU-128) - 0.71414*(finalV-128);
B = finalY + 1.772*(finalU-128);
data = cat(3,R,G,B);
data = uint8(data);
imwrite(data,'final.bmp');
fclose(fid);

deZigzag:
function DEZIGZAG = deZigzaging(x)
c = [1,2,9,17,10,3,4,11,... 
18,25,33,26,19,12,5,6,... 
13,20,27,34,41,49,42,35,... 
28,21,14,7,8,15,22,29,... 
36,43,50,57,58,51,44,37,... 
30,23,16,24,31,38,45,52,... 
59,60,53,46,39,32,40,47,... 
54,61,62,55,48,56,63,64];  
for i = 1 : 64
    dez(i) = find(c == i);
end
DEZIGZAG = x(dez);
DEZIGZAG = DEZIGZAG';
DEZIGZAG = reshape(DEZIGZAG,8,8);
DEZIGZAG = DEZIGZAG';

Huffmandecode:
function [ decosig ] = HuffDeco( zipped, pad, dict )
len = length(zipped);
string = repmat(uint8(0), 1, len.*8); % 先创建空串
bitindex = 1:8;
for index = 1:len % 还原为0-1串 1*N
	string(bitindex+8.*(index-1)) = uint8(bitget(zipped(index),bitindex));
end
string = logical( string(:)' ); 
len = length(string);
string( (len-pad+1) : end ) = []; % 去除pad位置的数
string = double(string);
decosig = huffmandeco(string, dict);  % Huffman解码

留言

您的邮箱地址不会被公开。 必填项已用 * 标注