实验2:编码解码
使用matlab实现对lena灰度图像和二值图像的DPCM编码和解码,以及游长编码的实现。
在进行编码之前,首先应该理解两种编码方式的原理、过程,梳理好之后再落实到代码上。
将图片的像素当做信号,一张图片可以转化为一个一维的信号流,从而利用两种编码方式进行压缩。
DPCM
差分脉冲编码调制简称差值编码,是对模拟信号幅度抽样的差值进行量化编码的调制方式(抽样差值的含义请参见“增量调制”)。这种方式是用已经过去的抽样值来预测当前的抽样值,对它们的差值进行编码。差值编码可以提高编码频率,这种技术已应用于模拟信号的数字通信之中。
DPCM与预测编码类似,只是它有一个量化步骤。量化步骤和PCM中的量化步骤类似,可以是均匀量化,也可以是非均匀量化
对于有些信号(例如图像信号)由于信号的瞬时斜率比较大,很容易引起过载,因此,不能用简单增量调制进行编码,除此之外,这类信号也没有像话音信号那种音节特性,因而也不能采用像音节压扩那样的方法,只能采用瞬时压扩的方法。但瞬时压扩实现起来比较困难,因此,对于这类瞬时斜率比较大的信号,通常采用一种综合了增量调制和脉冲编码调制两者特点的调制方法进行编码,这种编码方式被简称为脉码增量调制,或称差值脉码调制,用DPCM表示。
原理是减少或除去声音信号的多余成分以提高通信的有效性。
DPCM的操作流程是,首先生成预测值,然后用原始信号值减去预测信号生成误差值,接着量化误差值,得到量化后的误差值,并进行传输。重建时,利用量化后的误差值和预测信号来重构信号,从而得到图像。
游长编码
又称为游程编码Run-lengthcoding。是一种统计编码,属于无损压缩,是栅格数据压缩的重要编码方法。
它的原理是,用一个符号值或串长代替具有相同值的连续符号,使符号长度少于原始数据的长度。只在各行或者各列数据的代码发生变化时,一次记录该代码及相同代码重复的个数,从而实现数据的压缩。
游程编码的记录方式有两种:①逐行记录每个游程的重点列号;②逐行记录每个游程的长度。
比如,对于数据
A
A
A
B
B
A
C
C
C
A
第一种方式,记录为
(A,3),(B,5),
(A,1),(C,4),(A,5)
第二种方式记录为
A3,B2,
A1,C3,A1
游程编码是连续精确的编码,在传输过程中,如果其中有一位符号发生错误,就会影响整个编码序列。
游程编码有4位元表示法、8位元表示法等方式,利用n(4 or 8)个位元来存储整数,4位可表现的最大整数值为15,8位可表示的最大整数位255。
为了充分利用其在表示二值数据时的优势,在处理灰度图像时,把0~255范围内的数值都转化为8位二进制,存储在矩阵中,这样灰度图像和二值图像的数据都变成了0和1构成的字符串流。但是这样耗费了大量的空间,运算较为复杂。
DPCM
主函数:得到原图,分别转化为灰度图像和二值图像,调用编码函数my_dpcmEncoder和解码函数my_dpcmDecoder,编码函数返回的是量化后的误差值en,解码函数返回的是重构的信号值,把重构的信号流重新转化为图片的尺寸并输出,得到解码后的图像。
编码函数:
首先进行变量的初始化,重构信号f_rec、预测信号f_pre和误差值e、量化后的误差值en都是1行Num列的零矩阵,num=m*n。预测信号的前两个初始化为原始信号的第一个信号,重构信号的第一个值也置为f(1)。
接着进行主体的计算,结合书本公式:
for i=2:num
if(i~=2)
f_pre(i)=(f_rec(i-1)+f_rec(i-2))/2;
end
e(i)=f(i)-f_pre(i);
en(i)=16*trunc((255+e(i))/16)-256+8;
f_rec(i)=f_pre(i)+en(i);
end
解码函数:初始化f_pre,f_rec为1行num列的零矩阵,重构信号的第一个值为f(1)(单独传过来的原始信号初始值),预测信号的前两个值为f(1)。
循环计算:
for i=2:num
if(i~=2)
f_pre(i)=(f_rec(i-1)+f_rec(i-2))/2;
end
f_rec(i)=f_pre(i)+en(i);
end
Trunc函数其实和fix函数相同,此处单独编写:
if(b>0)
a=floor(b);
elseif (b<0)
a=ceil(b);
else a=0;
end
游程编码
主函数:得到原始灰度图像和二值图像,调用压缩函数和解压函数,分别显示原始图像和编码、解码后的图像。
压缩函数:
将原始图像矩阵转化为1行length列的矩阵,length=m*n。此处可以显示出数据流的长度和相应的值,用于比对。
Run矩阵记录每个元素的游程,data记录具体数值。如果是连续字符,游程+1,否则更新data的值,以及data的数量标记j。游程赋值为1。
j=1;
run(1)=1;
for i=1:(length-1)
%字符变化,更新序号j
if dataStr(i)~=dataStr(i+1)
data(j)=dataStr(i);
j=j+1;
run(j)=1;
else%连续字符,游程+1
run(j)=run(j)+1;
end
end
由于循环时略过了最后一个字符,把data的最后一个值设置为数据流的最后一个值。
得到新的压缩后的数据流后,也可以显示压缩数据流的长度和值,用于比对。另外可以计算压缩比length/runLen。
解压函数:
根据游程和对应的data值还原图像数据。双重循环,外层为游程的个数,内层为游程的长度:
for m=1:runLen
for n=1:run(m)
Image(l)=data(m);
l=l+1;
end
end
Matlab的基本使用方法
Size(f)得到一个一行两列的向量,第一个参数表示行数,第二个参数表示列数。因此[~,n]=size(f)可以得到图像矩阵的列数。Size(f,2)也可以直接得到矩阵的列数。
矩阵行列向量的获取:b=a[:,1];b获得的是a的第一列的列向量,同理b=a[1,:]获得的是第一行的行向量。
A(1)指矩阵A中的第一个元素。Matlab中矩阵的排列顺序是从上往下、从左往右的。
函数的使用:matlab中函数需要单独写在一个函数文件中,调用函数时,函数名与文件名一致。
For循环。
输出语句。
无输出图像:
发现是信号矩阵的尺寸发生了错误,应该全部使用一维矩阵,在得到最终的重构信号后,重新转化为图像矩阵再显示。
灰度图像的矩阵每一个数值表示灰度值,如果是彩色图,则矩阵是3维矩阵,存储的是每个像素的r,g,b值。
显示结果
二值图像显示效果不理想:
在进行预测的时候,误差会很大,产生的二值图像效果也很差。这可能是因为预测器和量化公式有一定的适用范围。
游程编码中二值图像显示不正常:
查看解压后和加压前对应的矩阵,经过比较,是相同的。但是,在进行压缩之前,代码中把所有大于0的像素值都置为1了,而查看原二值图像的矩阵,发现数值有255,14等非0、1数值。而原始的二值图像,是打开事先准备好的黑白图像得到的,因此数值并不为0,1,只是在程序中又进行了一次转化。因此在程序开头再对黑白图像调用一次im2bi函数,这样得到的图像矩阵中数值的格式为logical,非0即1。再次运行程序,发现结果正确。
灰度图像和二值图像的压缩比例分别为:
可以看到,游程编码对二值图像的压缩效果较好。
灰度图像原始信号:
灰度图像压缩后信号
二值图像原始信号
二值图像压缩后信号
差分脉冲编码调制算法对灰度图像的处理效果较好,对于二值图像的预测效果很差,重构的信号与原本的信号相差很大。游程编码适用于二值图像的压缩,作为无损压缩算法能够很好地还原图像,但是对于灰度图像,压缩比接近1,并没有起到压缩的效果,而对于二值图像,压缩比超过了20,性能较好。
通过本次实验,理解了DPCM的信号压缩原理和游程编码的原理,并能用matlab正确实现对图像的编码和解码,在这个过程中解决问题的能力得到了锻炼。