参考文献:数字图像处理(第三版)(第四版) 美Rafael,C.,Gonzalez(拉斐尔,C.,冈萨雷斯) 著
参考博客:https://www.cnblogs.com/wangguchangqing/p/6407717.html
全代码地址:https://mp.csdn.net/postedit/100053767
平均滤波与高斯滤波,他们都是低通滤波的一种(使图像模糊化,原理就是是相邻像素变化没那么剧烈,使你看到的相互邻接的像素就比较接近了)。我个人认为高斯滤波中心权重比较大,所以模糊效果相对来说不如均值滤波。网上的很多原理教程讲的真的很好。在下水平不够,只会用一句简单的话来说,从数学角度,其实就是拿卷积内核,对每个像素做卷积运算。那么就涉及到几个步骤。
步骤一:内核的产生
这个非常简单,平均滤波就是用1.0除总大小就行。(注意1.0,浮点数)
for(int i=0;i<kernel_side_length;++i)
for (int j = 0; j < kernel_side_length; ++j)
{
kernel[i][j] =1.0/(kernel_side_length*kernel_side_length);
//printf("% f", kernel[i][j]);
}
高斯滤波,这个也不难,如果你不去推高斯分布公式的话。简单说一下就是,归一化的高斯分布,这是个权重问题。还是看上文的播客吧。不建议直接代工式产生内核,还是大概查一下资料。
void Bmp::produce_gauss_martix(double **gauss_martix,const int kernel_side_length,const double variance)const
{
double index;
double sum=0.0;
index=1.0/(2*PI*variance);
for(int i=0;i<kernel_side_length;++i)
for(int j=0;j<kernel_side_length;++j)
{
gauss_martix[i][j]=index*exp((-1.0)*((i-kernel_side_length/2)*(i-kernel_side_length/2)+(j-kernel_side_length/2)*(j-kernel_side_length/2))/2*variance);
sum += gauss_martix[i][j];
}//代高斯分布公式
for (int i = 0; i < kernel_side_length; ++i)
for (int j = 0; j < kernel_side_length; ++j)
{
gauss_martix[i][j] /=sum;
}//归一化
}
步骤二:零扩展
有点像迷宫老鼠的思想,比如你(0,0)这个位置的像素,没有邻居,拿卷积内核怎么卷积呢?这时候就往外拓展n圈层0,这个n圈层,是根据你的内核大小来的。比如如果你5x5的内核,那你就拓展两层0。(这里就是说你要开辟一段新的内存空间了)这里要提醒一下对外圈赋值0的问题,最简单粗暴的方法是先把申请的空间元素全部弄成0,然后再填像素。我没这么干,然后调试了好久。(注意要找映射关系,你可以拿笔画画,也可边调试边推)
空间申请:
pixel_zero=new byte**[heigth+kernel_side_length-1];
for(int i=0;i< heigth+kernel_side_length-1;++i)
pixel_zero[i]=new byte*[width+kernel_side_length-1];
for(int i=0;i< heigth+kernel_side_length-1;++i)
for(int j=0;j< width+kernel_side_length-1;++j)
pixel_zero[i][j]=new byte[byte_count];//为补0像素申请空间
补0:
void Bmp::supply_zero(byte ***pixel_zero,const int kernel_side_length)const
{
for(int i=0;i<heigth+ kernel_side_length-1;++i)
{
for(int j=0;j<kernel_side_length/2;++j)
for(int k=0;k<byte_count;++k)
{
pixel_zero[i][j][k]=0;
}
}//左边列
for(int i=0;i<heigth+kernel_side_length - 1;++i)
{
for(int j=width+ kernel_side_length / 2;j<width+kernel_side_length-1;++j)
for(int k=0;k<byte_count;++k)
{
//printf("%d\n",j);
pixel_zero[i][j][k]=0;
}
}//右边列
for(int i=0;i<width + kernel_side_length - 1;++i)
{
for(int j=0;j<kernel_side_length/2;++j)
for(int k=0;k<byte_count;++k)
{
pixel_zero[j][i][k]=0;
}
}//上边行.注意j代表行
for(int i=0;i<width + kernel_side_length - 1;++i)
{
for(int j=heigth+ kernel_side_length / 2;j<heigth+kernel_side_length-1;++j)
for(int k=0;k<byte_count;++k)
{
pixel_zero[j][i][k]=0;
}
}//下边行.注意j代表行
for(int i=0;i<heigth;++i)
for (int j = 0; j < width; ++j)
for(int k=0;k<byte_count;++k)
{
pixel_zero[i+kernel_side_length/2][j+ kernel_side_length / 2][k]=pixel[i][j][k];
}//为0扩展像素矩阵赋值
}
步骤三:卷积运算
卷积运算,就是把一堆乘法加起来。(你要特别注意,RGB是三通道的,你要分开来算)。
void Bmp::convolution (byte***pixel_zero,double **kernel,const int kernel_side_length)const
{
double red_sum;
double green_sum;
double blue_sum;
for(int i=0;i<heigth;++i)
for (int j = 0; j < width;++j)
{
red_sum=green_sum=blue_sum=0;
for(int m=0;m<kernel_side_length;++m)
{
for(int n=0;n<kernel_side_length;++n)
{
red_sum+=kernel[m][n]*pixel_zero[i+m][j+n][0];
green_sum+=kernel[m][n]*pixel_zero[i+m][j+n][1];
blue_sum+=kernel[m][n]*pixel_zero[i+m][j+n][2];
}
}
pixel_temp[i][j][0]=red_sum;
pixel_temp[i][j][1]=green_sum;
pixel_temp[i][j][2]=blue_sum;
}
}
展示:
均值滤波
原图像
9x9均值滤波后
9x9方差为1.0高斯滤波后