从去年开始学习Matlab,主要用它来处理图像,虽然也写了一些代码,但大多还是得依靠众多博友和度娘的帮助,然后现在得学习怎么融会贯通地使用写出自己的代码,希望通过发博客互相沟通交流能让自己的进步更快一点(●’◡’●)。
今天要做的是类似均值滤波,通过使用5×5的滑动窗来求取该滑动窗内25个像素的均值和标准差。主要涉及到sum函数、二维矩阵卷积函数conv2、ones函数、imfilter函数、标准差的计算原理,以及在这个过程中遇到的一些问题。
利用滑动窗和sum函数求和,边界两行两列的像素不参与计算,维持原值,也就是说,从第三行第三列的像素值开始计算。要同时进行行和列的计算,也就是以某一像素点M(i,j)为中心,对它周围及自身的像素求和,如下所示,同时写出了sum函数的示例。此外,sum函数有时会遇到“下标索引必须为正整数类型或逻辑类型”的问题,这个时候就要检查设定的下标的起始和结尾的值。
5×5滑动窗 | Matlab代码示例 | |||||
---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | a=sum(sum(M(i-2:i+2,j-2:j+2)));%滑动窗求和,N=5,则半径r=2 | |
1 | 1 | 1 | 1 | 1 | a=sum(x);%对矩阵每一列求和,生成行向量 | |
1 | 1 | M(i,j) | 1 | 1 | a=sum(x,1);%对矩阵每一列求和,生成行向量 | |
1 | 1 | 1 | 1 | 1 | a=sum(x,2);%对矩阵每一行求和,生成列向量 | |
1 | 1 | 1 | 1 | 1 | a=sum(x(: ));%对矩阵内所有元素求和,生成一个值 |
总的Matlab代码如下:
img=imread('LS.png');%读入图像
[h, w]=size(img);
mean=zeros(h, w);
for i=1:h
for j=1:w
if i>=3 & i<=(h-2) & j>=3 & j<=(w-2) %不对边界进行处理
mean(i,j)=(sum(sum(img(i-2:i+2,j-2:j+2))))/25;%求和后均值
else
mean(i,j)=img(i,j);
end
end
end
mean=uint8(mean);%转换数值类型显示图像
imshow(mean);
除了用sum函数来进行5×5滑动窗求和之外,还可以利用conv2函数来求和,因为这边用的卷积核和上面的滑动窗一样,是全1矩阵,并且180°旋转后不改变大小。Matlab进行卷积操作时的具体步骤是:(1)先对原始图像矩阵A(M1×N1)根据卷积核B(M2×N2)进行补零,第一行之前和最后一行之后都补(M2-1)行,第一列之前和最后一列之后则补(N2-1)列;(2)然后将卷积核绕其中心进行180°的旋转,并将卷积核的中心与图像矩阵的中心元素(也就是上面的M(i,j))相对应;(3)最后滑动卷积核遍历图像,对应位置相乘并相加。
函数调用的方式很简单,就是conv2(A,B),其中A为原始图像矩阵,B则为卷积核。和sum函数相比,利用conv2函数更简单一点,不会出现下标索引的问题,重点是要设定shape的值,这边选择shape=same,也就是conv2(A,B,‘same’)。并且,记得卷积前先将数值类型改为double或者single。另一个问题是,不能对三维图像矩阵进行二维卷积,所以要先对图像进行灰度化。另外,要得到5×5的全1矩阵,可以利用ones函数,两者的代码示例如下:
conv2(A,B,‘full’) | conv2(A,B,‘same’) | conv2(A,B,‘valid’) |
---|---|---|
返回完整卷积,(M1+M2-1)×(N1+N2-1) | 返回与矩阵A行列相同的卷积部分,(M1×N1) | 返回没有补零边缘的卷积部分,(M1-M2+1)×(N1-N2+1) |
ones(N) | ones(M×N) | ones(size(A)) |
生成N×N的全1矩阵 | 生成M×N的全1矩阵 | 生成与矩阵A行列相同的全1矩阵 |
总的Matlab代码如下:
img=imread('LS.png');%读入图像
img=rgb2gray(img);%灰度化
B=ones(5)/25;
mean=conv2(double(img),double(B),'same');%用均值后的卷积核求和
mean=uint8(mean);%转换数值类型显示图像
imshow(mean);
与conv2函数相似的,还有相关滤波filter2函数和imfilter函数。相关滤波filter2函数和卷积函数conv2函数的具体步骤几乎完全相同,区别在于它的卷积核不用旋转180°,在滑动窗模板权值均为1的情况下,效果和conv2函数应该完全一致,调用方式是filter2(h,A,shape),其中h为相关核,A为输入图像。
而imfilter函数能够实现3通道的RGB图像和单通道的滤波器的卷积,并且返回3通道的图像,调用方式为imfilter(A,h,option1,option2,option3),其中option1为边界选项,可选的有补充常数(默认补零)、symmetric、replicate、circular;option2为输出图像大小的选项,可选的有same(默认)和full;option3用来决定采用与filter2相同的相关滤波(corr)还是与conv2相同的卷积滤波(conv)。
总的来说,filter2函数对输入类型无限制,在边界只能补零,只能对二维图像-灰度图进行滤波;conv2函数的输入类型只能是double或者single,也只能对二维图像-灰度图进行滤波;imfilter函数有灵活的边界补充选项,可以进行多维图像的滤波。所以最终选择conv2函数或者imfilter函数来计算均值。
总的Matlab代码如下:
img=imread('LS.png');%读入图像
B=ones(5)/25;
mean=imfilter(img,B,'corr');%用均值后的卷积核求和
imshow(mean);
标准差又常称均方差,是方差的算术平方根,用σ表示,此处通过方差的公式来计算标准差,方差是每个样本值与全体样本值的均值之差的平方值的平均数,以下是常用来计算标准差的公式,其中(N-1)为自由度。
标准差公式 | 均值公式 |
总的Matlab代码如下:
img=imread('LS.png');%读入图像
B=ones(5)/25;
C=ones(5)/24;
mean=imfilter(img,B,'corr');%用均值后的卷积核求和
S=sqrt(double(imfilter((img-mean).^2,C,'corr')));%求方差的平方根-标准差
S=uint8(S);%转换数值类型显示图像
imshow(S);
总的Matlab代码如下:
img=imread('LS.png');%读入图像
img=rgb2gray(img);%灰度化
B=ones(5)/25;
C=ones(5)/24;
mean=conv2(double(img),double(B),'same');%用均值后的卷积核求和
S=sqrt(conv2((double(img)-double(mean)).^2,double(C),'same'));%求方差的平方根-标准差
S=uint8(S);%转换数值类型显示图像
imshow(S);
最后代码应用到图像上,个人认为还是用conv2函数得到的效果比较明显,只是要注意数值类型的变换,大部分时候将数值类型转化为double就可以成功运行。这只是在一张图像上利用滑动窗来进行的均值和标准差的计算,之后还会尝试做多张图像的。大家有什么问题,欢迎一起讨论哦(●’◡’●)。