去年11月发布了一系列有关小波变换和图像处理的文章,把学习小波过程中的心得体会和编写的程序放在网上和大家共享交流。半年来,感谢大家的关注和帮助,在相互的讨论交流中,我不断地从大家提出的问题中拓展自己的知识面,对小波的理论及其应用有了更深入的了解和掌握。根据和大家讨论交流中发现的问题,对博客中的程序进行修正。有关小波图像分解和重构的两篇文章中分享的程序,存在下列问题:
(1)程序所用的小波函数只有非标准的Haar小波,其滤波器组为 Lo_D=[1/2 1/2], Hi_D=[-1/2 1/2],是固化在 mydwt2.m 的程序中的,不能选择其他的小波函数;
(2)非标准的Haar小波,其分解出来的系数矩阵中,高频系数的细节内容(轮廓、边缘等特征)不明显;
(3)函数 mydwt2 中列变换的矩阵对象为输入矩阵,这是错误的,其矩阵对象应该是行变换后的缓存矩阵;
(4)函数 mydwt2 的输出用[LL,HL,LH,HH]表示,不是很规范,应改为[cA,cV,cH,cD]来表示,即一级小波变换输出的系数矩阵有4个部分:平均部分、垂直细节部分、水平细节部分和对角线细节部分。
(5)函数 mywavedec2 的输出 y 是与输入矩阵 x 相同大小的矩阵,并且已将N级分解后所有的平均、细节系数组合成一体的。实际上,这种定义只对Haar小波有效。
(6)原程序中要调用 modmat 函数对图像矩阵进行修剪,使之能被 2 的 N 次方整除,主要是为了生成塔式结构图像而设的,对上述问题修正后,这个 modmat 函数已不需使用了。
针对上述问题,我对程序作了修正,发布在今天的3篇文章里,请大家点击查看。新修正的程序更为简洁易懂,功能也有所增强,可以用任意的小波函数进行小波分解,可根据小波分解系数矩阵重构出指定分解级的低频系数和原始图像。
1、《小波图像分解与重构程序存在的问题与解决办法》
http://blog.csdn.net/chenyusiyuan/archive/2008/06/05/2513126.aspx
2、《小波图像分解 Matlab 程序 - V2.0版》
http://blog.csdn.net/chenyusiyuan/archive/2008/06/05/2513865.aspx
3、《小波图像重构 Matlab 程序 - V2.0版》
http://blog.csdn.net/chenyusiyuan/archive/2008/06/05/2514119.aspx
上一篇文章中我们实现了小波的一维、二维信号分解与重构,其中的二维信号分解与重构,只要稍作修改,就可以实现图像的分解和重构了。修改的工作,主要是对图像信号进行规范化处理、数据格式转换和绘图细节处理等。
简单起见,我们从黑白(灰度)图像的分解、重构说起,因为彩色图像的处理要复杂一点。在本文中,我们使用著名的Lena图作为原始图像。
图1
首先,为了实现图像的N层分解,对一幅m行n列的黑白图像,我们要对其进行规范化处理,使其能被2的N次方整除。以下的modmat() 函数实现此功能:
function y=modmat(x,dim)
% 函数 MODMAT() 对输入矩阵x进行规范化,使其行列数均能被 2^dim 整除
% 输入参数:x —— r*c 维矩阵;
% dim —— 矩阵重构的维数
% 输出参数:y —— rt*ct 维矩阵,mod(rt,2^dim)=0,mod(ct,2^dim)=0
[row,col]=size(x); % 求出输入矩阵的行列数row,col
rt=row - mod(row,2^dim); % 将row,col分别减去本身模 2^dim 得到的数
ct=col - mod(col,2^dim); % 所得的差为rt、ct,均能被 2^dim 整除
y=x(1:rt,1:ct); % 输出矩阵 y 为输入矩阵 x 的 rt*ct 维子矩阵
然后,将规范化后的图像的数据格式由适合显示图像的uint8格式转换为适合数值处理的double格式,再调用二维小波分解函数进行图像分解,最后为了清晰地显示分解图像的塔式结构,在图像的相应区域绘制若干分界线。具体程序如下:
function y=mywavedec2(x,dim)
% 函数 MYWAVEDEC2() 对输入矩阵 x 进行 dim 层分解,得到相应的分解系数矩阵 y
% 输入参数:x —— 输入矩阵;
% dim —— 分解层数。
% 输出参数:y —— 分解系数矩阵。
x=modmat(x,dim); % 首先规范化输入矩阵,使其行列数均能被 2^dim 整除
subplot(121);imshow(x);title('原始图像'); % 画出规范化后的源图像
[m,n]=size(x); % 求出规范化矩阵x的行列数
xd=double(x); % 将矩阵x的数据格式转换为适合数值处理的double格式
for i=1:dim
xd=modmat(xd,1);
[dLL,dHL,dLH,dHH]=mydwt2(xd); % 矩阵小波分解
tmp=[dLL,dHL;dLH,dHH]; % 将分解系数存入缓存矩阵
xd=dLL; % 将缓存矩阵左上角部分的子矩阵作为下一层分解的源矩阵
[row,col]=size(tmp); % 求出缓存矩阵的行列数
y(1:row,1:col)=tmp; % 将缓存矩阵存入输出矩阵的相应行列
end
yd=uint8(y); % 将输出矩阵的数据格式转换为适合显示图像的uint8格式
for i=1:dim % 对矩阵 yd 进行分界线处理,画出分解图像的分界线
m=m-mod(m,2);
n=n-mod(n,2);
yd(m/2,1:n)=255;
yd(1:m,n/2)=255;
m=m/2;n=n/2;
end
subplot(122);imshow(yd);title([ num2str(dim) ' 维小波分解图像']);
以下是程序的运行结果。
图2
上述的图像分解程序,其输出数据是double格式的,以便作为重构程序的输入。
接下来我们讨论图像的重构。我编写的重构程序中,为了比较分解图像和重构图像,首先绘出经过小波分解的图像,然后再进行重构。在绘制分解图像和重构图像的过程中,要注意数据格式的转换。
function y=mywaverec2(x,dim)
% 函数 MYWAVEREC2() 对输入的分解系数矩阵x进行 dim 层重构,得到重构矩阵 y
% 输入参数:x ——分解系数矩阵;
% dim ——重构层数;
% 输出参数:y ——重构矩阵。
% 绘制分解图像
xd=uint8(x); % 将输入矩阵的数据格式转换为适合显示图像的uint8格式
[m,n]=size(x); % 求出输入矩阵的行列数
for i=1:dim % 对转换矩阵xd进行分界线处理
m=m-mod(m,2);
n=n-mod(n,2);
xd(m/2,1:n)=255;
xd(1:m,n/2)=255;
m=m/2;n=n/2;
end
figure;
subplot(121);imshow(xd);title([ num2str(dim) ' 层小波分解图像']); % 画出带有分界线的分解图像
% 重构图像
xr=double(x); % 将输入矩阵的数据格式转换回适合数值处理的double格式
[row,col]=size(xr); % 求出转换矩阵xr的行列数
for i=dim:-1:1 % 重构次序是从内层往外层进行,所以先抽取矩阵 xr 的最内层分解矩阵进行重构
tmp=xr(1:floor(row/2^(i-1)),1:floor(col/2^(i-1))); % 重构的内层矩阵的行列数均为矩阵xr的2^(i-1)
[rt1,ct1]=size(tmp); % 读取待重构矩阵 tmp 的行列数
rt=rt1-mod(rt1,2);ct=ct1-mod(ct1,2);
rLL=tmp(1:rt/2,1:ct/2); % 将待重构矩阵 tmp 分解为四个部分
rHL=tmp(1:rt/2,ct/2+1:ct);
rLH=tmp(rt/2+1:rt,1:ct/2);
rHH=tmp(rt/2+1:rt,ct/2+1:ct);
tmp(1:rt,1:ct)=myidwt2(rLL,rHL,rLH,rHH); % 将重构结果返回到矩阵 tmp
xr(1:rt1,1:ct1)=tmp; % 把矩阵 tmp 的数据返回到矩阵 xr 的相应区域,准备下一个外层的重构
end
y=xr; % 重构结束后得到的矩阵xr即为输出矩阵 y
yu=uint8(xr); % 将矩阵xr的数据格式转换为适合显示图像的uint8格式
subplot(122);imshow(yu);title('小波重构图像
');
图3