在滤波算法中,均值、高斯、双边都是常用的算法,知道他们用途的同时,再了解它们的底层原理,有助于对图像算法的理解和算法的底层优化。
本文介绍这三种算法,并给出底层的实现代码(不同的实现方式),和实现的效果展示,和一些库函数的效果对比。
本文为转载文章:
代码实现参考了以下链接:
https://blog.csdn.net/u013921430/article/details/84532068?spm=1001.2014.3001.5506
https://blog.csdn.net/a435262767/article/details/107115249
均值滤波:
介绍:均值滤波是求邻域内的均值作为新的像素值。
实现:采用c#数组的形式实现均值滤波算法,对于边缘像素不进行填充,而是取有值的点进行平均。
首先生成一个0-255的随机数二维数组,再求每一个像素位置的滤波后的值(针对边缘位置进行分类讨论)
public bool Mean_ilter( int size, int imgsize,out double[,] resImg,out HObject image)
{
//声明一个halcon图像用于显示
image = null;
HOperatorSet.GenEmptyObj(out image);
HOperatorSet.GenImageConst(out image ,"byte", imgsize*1.0, imgsize*1.0);
//默认是滤波器的尺寸是3
size = 3;
//首先生成一个随机数矩阵
Random rd = new Random();
double[,] ImgSource = new double[imgsize, imgsize];
for (int i = 0; i < imgsize; i++)
{
for (int j = 0; j < imgsize; j++)
{
double x = rd.Next(255);
ImgSource[i, j] = x;
HOperatorSet.SetGrayval(image,i,j,x);
// System.Console.Write(" ");
// System.Console.Write(ImgSource[i, j]);
}
System.Console.WriteLine();
}
resImg = new double[imgsize,imgsize];
//进行均值滤波处理
for(int m=0;m<imgsize;m++)
{
for(int n=0;n<imgsize;n++)
{
//对于边界的像素单独处理
if(n==0&&m==0)
{
//左上角
resImg[m, n] = (ImgSource[m, n] +ImgSource[m, n + 1] +
ImgSource[m + 1, n] + ImgSource[m + 1, n + 1]) / 4;
}
else if (n == (imgsize -1)&& m == 0)
{
//右上角
resImg[m, n] = ( ImgSource[m, n - 1] + ImgSource[m, n] +
ImgSource[m + 1, n - 1] + ImgSource[m + 1, n] ) / 4;
}
else if (n == 0 && m == (imgsize - 1))
{
//左下角
resImg[m, n] = (ImgSource[m - 1, n] + ImgSource[m - 1, n + 1] +ImgSource[m, n] +
ImgSource[m, n + 1] ) / 4;
}
else if (m == (imgsize - 1) && n == (imgsize - 1))
{
//右下角
resImg[m, n] = (ImgSource[m - 1, n - 1] + ImgSource[m - 1, n] +
ImgSource[m, n - 1] + ImgSource[m, n] ) / 4;
}
else if ( m == 0)
{
//非角落,第一行
resImg[m, n] = ( ImgSource[m, n - 1] + ImgSource[m, n] +
ImgSource[m, n + 1] + ImgSource[m + 1, n - 1] + ImgSource[m + 1, n] + ImgSource[m + 1, n + 1]) / 6;
}
else if (m == (imgsize - 1))
{
//非角落,最后一行
resImg[m, n] = (ImgSource[m - 1, n - 1] + ImgSource[m - 1, n] + ImgSource[m - 1, n + 1] + ImgSource[m, n - 1] + ImgSource[m, n] +
ImgSource[m, n + 1] ) / 6;
}
else if (n == 0)
{
//非角落,第一列
resImg[m, n] = ( ImgSource[m - 1, n] + ImgSource[m - 1, n + 1] + ImgSource[m, n] +
ImgSource[m, n + 1] + ImgSource[m + 1, n] + ImgSource[m + 1, n + 1]) / 6;
}
else if (n == (imgsize - 1))
{
//非角落,最后一列
resImg[m, n] = (ImgSource[m - 1, n - 1] + ImgSource[m - 1, n] + ImgSource[m, n - 1] + ImgSource[m, n] +
ImgSource[m + 1, n - 1] + ImgSource[m + 1, n] ) / 6;
}
else
{
//中间部分
resImg[m, n] = (ImgSource[m-1,n-1]+ ImgSource[m-1, n]+ ImgSource[m-1, n+1]+ ImgSource[m, n-1]+ ImgSource[m, n]+
ImgSource[m, n+1]+ ImgSource[m+1, n-1] + ImgSource[m+1, n] + ImgSource[m+1, n+1])/ 9;
}
}
}
return true;
}
高斯滤波:
介绍:高斯滤波是求邻域的加权均值作为新的像素值,以到像素点的距离进行权重的分配。当窗口大小确定时,滤波核是固定的
实现:
方法1:采用halcon的方式进行实现,(自实现的边缘填充。
计算卷积核的函数ComPut_Gauss_ConvKernel 参考我之前的文章:
https://blog.csdn.net/qq_53863660/article/details/136842479?spm=1001.2014.3001.5502
此方法实现比较呆,只可以实现 3*3size的滤波,先进行了边缘填充,选择靠近的像素值进行填充,然后循环整个图像,在扩充图像上进行计算,得到每一个位置的滤波像素值
public bool gaussFilter(int _size,HObject image,out HObject GaussImg)
{
// HOperatorSet.Rgb1ToGray(image,out image);只适用于灰度图
// 初始化halcon对象
GaussImg = null; HObject ExpandImage = null;
HOperatorSet.GenEmptyObj(out GaussImg);
HOperatorSet.GenEmptyObj(out ExpandImage);
//根据尺寸,计算卷积核
double[,] matrix = new double[_size,_size];
ComPut_Gauss_ConvKernel(_size, 1.5, out matrix);
//生成结果图和扩充图
HOperatorSet.GetImageSize(image, out HTuple width, out HTuple height);
HOperatorSet.GenImageConst(out GaussImg, "byte", width, height);
HOperatorSet.GenImageConst(out ExpandImage,"byte",width.D+2.0,height.D+2.0);
//原图的宽高
int H = height.I;
int W = width.I;
//扩充图的宽高
int H_Epd = H + 2;
int W_Epd = W + 2;
//填充规则,角落的位置填充角落的像素,非角落的像素填充靠近的像素
double grayValue1=0;
for (int i=0;i< H_Epd; i++)
{
for(int j=0;j< W_Epd; j++)
{
//左上角
if(i==0&&j==0)
{
HOperatorSet.GetGrayval(image,i,j,out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//右下角
else if(i==H_Epd-1&&j==W_Epd-1)
{
HOperatorSet.GetGrayval(image, H-1, W-1, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//左下角
else if(i == H_Epd-1 && j == 0)
{
HOperatorSet.GetGrayval(image, H - 1, 0, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//右上角
else if(i == 0 && j == W_Epd - 1)
{
HOperatorSet.GetGrayval(image, 0, W-1, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//第一行非角落
else if (i == 0 )
{
HOperatorSet.GetGrayval(image, i-1+1, j-1, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//最后一行非角落
else if (i == H_Epd-1)
{
HOperatorSet.GetGrayval(image, i - 1-1 , j - 1, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//第一列非角落
else if (j == 0)
{
HOperatorSet.GetGrayval(image, i - 1 , j - 1 + 1, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//最后一列非角落
else if (j==W_Epd-1)
{
HOperatorSet.GetGrayval(image, i - 1 , j - 1-1, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
//中间位置
else
{
HOperatorSet.GetGrayval(image, i - 1 , j - 1, out HTuple gray);
grayValue1 = gray.D;
HOperatorSet.SetGrayval(ExpandImage, i, j, grayValue1);
}
}
}
//以扩充图为基准,进行滤波计算,得到高斯结果图
double grayValue2;
for(int i=0;i< H; i++)
{
for(int j=0;j< W; j++)
{
//卷积核第一行
HOperatorSet.GetGrayval(ExpandImage, i+1-1, j+1-1, out HTuple gray1);
HOperatorSet.GetGrayval(ExpandImage, i+1-1, j+1, out HTuple gray2);
HOperatorSet.GetGrayval(ExpandImage, i+1-1, j+1+1, out HTuple gray3);
//卷积核第二行
HOperatorSet.GetGrayval(ExpandImage, i+1, j+1-1, out HTuple gray4);
HOperatorSet.GetGrayval(ExpandImage, i+1, j+1, out HTuple gray5);
HOperatorSet.GetGrayval(ExpandImage, i+1, j+1+1, out HTuple gray6);
//卷积核第三行
HOperatorSet.GetGrayval(ExpandImage, i+1+1, j+1-1, out HTuple gray7);
HOperatorSet.GetGrayval(ExpandImage, i+1+1, j+1, out HTuple gray8);
HOperatorSet.GetGrayval(ExpandImage, i+1+1, j+1+1, out HTuple gray9);
//此处可以采用halcon自带的卷积运算算子,可以优化速度
grayValue2 = gray1.D * matrix[0,0] + gray2.D * matrix[0, 1] + gray3.D * matrix[0, 2] + gray4.D * matrix[1, 0] +
gray5.D * matrix[1, 1] + gray6.D * matrix[1, 2] + gray7.D * matrix[2, 0] + gray8.D * matrix[2, 1] + gray9.D * matrix[2, 2];
HOperatorSet.SetGrayval(GaussImg, i, j, grayValue2);
}
}
ExpandImage.Dispose();
return true;
}
方式2:采用CV的方式
直接采用opencv的卷积函数进行计算,方便快捷。
public bool gaussFilterUsingOpencv(int _size,Mat img,out Mat imgGauss)
{
imgGauss = new Mat();
double[,] matrix = new double[_size, _size];
ComPut_Gauss_ConvKernel(_size, 10, out matrix);
//边缘扩充函数
//将c#数组矩阵转换为mat类型
Mat kernel = new Mat(_size, _size, MatType.CV_64F, matrix, 0);
//直接采用cv只带的相关运算,自带了扩充功能
Cv2.Filter2D(img, imgGauss,MatType.CV_8UC1,kernel,new Point(-1,-1),0,BorderTypes.Replicate);
return true;
}
双边滤波:
介绍:双边滤波是也是求邻域的加权均值作为新的像素值,以到像素点的距离和像素值的相似性进行权重的分配。当窗口大小确定时,滤波核也不确定
实现:
此方法可以适应所有的size,
1.先对图像进行扩充
2.循环处理每一个像素
3.针对每一个像素,又循环处理该像素每一个滤波核的位置,分别计算距离和像素值权重(在扩充图进行计算),得到权重,直接计算该位置权重后的值,并进行叠加。
4.计算完并叠加所有滤波核位置的权重后的值之后,进行归一化处理(除以权重和),再赋值给该位置,作为该像素位置滤波后的像素值。
5.循环处理
距离权重:
像素值相似权重:
综合权重:
//采用opencv的库自实现的双边滤波
public bool bilaFilter(Mat img ,int size,double sigmaD,double sigmaV,out Mat imgBila)
{
int imgWidth =img.Cols;
int imgHeight =img.Rows;
//初始化结果图像
imgBila = new Mat(img.Rows,img.Cols,MatType.CV_8UC1,new Scalar(0));
Mat imgExpand = new Mat();
//对原图进行边界扩充处理
Cv2.CopyMakeBorder(img,imgExpand,(int)(size/2), (int)(size / 2), (int)(size / 2), (int)(size / 2),BorderTypes.Replicate);
//循环处理,计算滤波后图像每一个像素点的值
for(int i=0;i<imgHeight;i++)
{
for(int j=0;j<imgWidth;j++)
{
double weightSum = 0;
double pixelValue = 0;
//循环处理,对滤波核的每一个网格进行处理
for(int row_w=-(size/2);row_w<(size/2);row_w++)
{
for(int col_w=-(size/2);col_w<(size/2);col_w++)
{
//计算高斯距离权重
double dist_square = Math.Pow(col_w,2) + Math.Pow(row_w,2);
//计算像素值相似度权重
//采用C# opencv获取像素值的时候,此处的类型必须是byte
double value_square = Math.Pow(img.At<byte>(i, j)
- imgExpand.At<byte>(i+(int)(size/2)- col_w, j+(int)(size/2) + row_w), 2);//此处-row_w,再+col_w应该也可以,看怎么去定义滤波窗口的窗口坐标,但是不论写哪一种,下面p1行(注释处)应该和此处保持一致
//距离和像素权重相乘得到该位置的权重
double weight = Math.Exp(-(dist_square/(2*sigmaD*sigmaD)+value_square/(2*sigmaV*sigmaV)));
//计算权重和
weightSum += weight;
//计算权重计算后的值
pixelValue += weight * imgExpand.Get<byte>(i + (size / 2) - col_w, j + (size / 2) + row_w);//p1
}
}
//归一化,得到该位置滤波后的像素值
imgBila.Set<byte>(i, j,(byte)(pixelValue/weightSum));
}
}
return true;
}
效果和效果:
yi.原图
2.高斯滤波
① 使用opencv自实现的高斯滤波 ② opencv自带的高斯滤波函数
③ 使用halcon自实现的高斯滤波(size 3) ④halcon自带的高斯滤波(size 3)
3.均值滤波
① halcon自带的均值滤波函数 ② 原图 ③均值滤波结果(自实现的c#数组的方式)
4.双边滤波
① 自实现的双边滤波效果(采用opencv) ②opencv自带的双边滤波函数效果