在数字图像处理常见的变换核及其用途中,已经说明了线性滤波。线性滤波是算术运算,有固定的模板,即:变换核。
现在来介绍非线性滤波技术。
中值滤波
中值滤波是非线性滤波,没有固定的变换核。它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。
百度百科中是这样描述中值滤波的原理:
中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。方法是用某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序,生成单调上升(或下降)的为二维数据序列。二维中值滤波输出为g(x,y)=med{f(x-k,y-l),(k,l∈W)} ,其中,f(x,y),g(x,y)分别为原始图像和处理后图像。W为二维模板,通常为3*3,5*5区域,也可以是不同的的形状,如线状,圆形,十字形,圆环形等。
在OpenCV中,我们都是去取一个奇数的正方形模板,例如:3*3,5*5,7*7等。
中值滤波对脉冲噪声(椒盐噪声)有良好的滤除作用,特别是在滤除噪声的同时,能够保护信号的边缘,使之不被模糊。这些优良特性是线性滤波方法所不具有的。此外,中值滤波的算法比较简单,也易于用硬件实现。(DSP芯片实现)
椒盐噪声:椒盐噪声也称为脉冲噪声,是图像中经常见到的一种噪声,它是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)。椒盐噪声的成因可能是影像讯号受到突如其来的强烈干扰而产生、类比数位转换器或位元传输错误等。例如失效的感应器导致像素值为最小值,饱和的感应器导致像素值为最大值。
中值滤波在一定的条件下可以克服常见线性滤波器如最小均方滤波、方框滤波器、均值滤波等带来的图像细节模糊,而且对滤除脉冲干扰及图像扫描噪声非常有效,也常用于保护边缘信息, 保存边缘的特性使它在不希望出现边缘模糊的场合也很有用,是非常经典的平滑噪声处理方法。
双边滤波
双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。
双边滤波器的好处是可以做边缘保存(edge preserving),一般过去用的维纳滤波或者高斯滤波去降噪,都会较明显地模糊边缘,对于高频细节的保护效果并不明显。
双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。
对于高斯滤波,仅用空间距离的权值系数核与图像卷积后,确定中心点的灰度值。即认为离中心点 越近的点,其权重系数越大。双边滤波中加入了对灰度信息的权重,即在邻域内,灰度值越接近中心点灰度值的点的权重更 大,灰度值相差大的点权重越小。此权重大小,则由值域高斯函数确定。 两者权重系数相乘,得到最终的卷积模板。由于双边滤波需要每个中心点邻域的灰度信息来确 定其系数,所以其速度与比一般的滤波慢很多,而且计算量增长速度为核大小的平方。
空间域sigma选取
其中核大小通常为sigma的6*sigma 1。因为离中心点3*sigma大小之外的系数与中点的系数只比 非常小,可以认为此之外的点与中心点没有任何联系,及权重系数为0.OpenCV中默认的计算公式也 是如此,OpenCV参考文档内容如下:“对应高斯参数的 Gaussian sigma (标准差). 如果为零,则 标准差由下面的核尺寸计算: sigma = (n/2 - 1)*0.3 0.8, 其中 n=param1 对应水平 核,n=param2对应垂直核.”
值域sigma选取
值域sigma选取:灰度差△g = abs(gray(xi,yi)- gray(xc,yc))忽略常数的影响,因此其函数可以简化为:
Sigma越大,边缘越模糊,极限情况为simga无穷大,值域系数近似相等(忽略常数时,将近为 exp(0)= 1),与高斯模板(空间域模板)相乘后可认为等效于高斯滤波。 Sigma越小,边缘越清晰,极限情况为simga无限接近0,值域系数近似相等(接近exp(-∞) = 0),与高斯模板(空间域模板)相乘后,可近似为系数皆相等,等效于源图像。
中值滤波函数medianBlur
代码语言:javascript复制//函数原型
void medianBlur( InputArray src, OutputArray dst, int ksize );
参数1:输入图像;
参数2:输出图像;
参数3:掩膜的大小,只能是奇数。它决定了邻域是3*3,还是5*5的。
双边滤波函数bilateralFilter
代码语言:javascript复制//函数原型
void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace,
int borderType = BORDER_DEFAULT );
参数1:输入图像;
参数2:输出图像;
参数3:过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
参数6:默认值BORDER_DEFAULT
下面来看一下具体的实例:
代码语言:javascript复制#include<opencv2/opencv.hpp>
using namespace cv;
//全局变量
Mat src1 = imread("C:/Users/zhou_/Desktop/2.jpg");
Mat src2 = imread("C:/Users/zhou_/Desktop/3.jpg");
Mat dst1, dst2;
int kmin = 1;
int kmax = 50;
//创建回调函数
void myMedianBlur(int min, void*);
void myBilateralFilter(int min, void*);
int main()
{
if (!src1.data)
{
perror("load failed:");
exit(0);
}
namedWindow("中值滤波");
namedWindow("双边滤波");
myMedianBlur(kmin, NULL);
createTrackbar("卷积核大小", "中值滤波", &kmin, kmax, myMedianBlur);
myBilateralFilter(kmin, NULL);
createTrackbar("卷积核大小", "双边滤波", &kmin, kmax, myBilateralFilter);
waitKey(0);
return 0;
}
void myMedianBlur(int min, void*)
{
medianBlur(src2, dst2, kmin*2 1);
imshow("中值滤波", dst2);
}
void myBilateralFilter(int min, void*)
{
bilateralFilter(src1, dst1, min, kmin * 2, kmin / 2);
imshow("双边滤波", dst1);
}
可以看到中值滤波的效果还是非常显著的,它有效的去除了图中的椒盐噪声。
参数一定要合理,不然效果是不好的,如果改变参数为4,如下所示:
需要说明的一点是邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘和轮廓信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。如果在这个程序中选择的邻域范围是3*3,那么几乎不会有什么效果。
双边滤波的效果很难看出来,上图是把参数拉到 41的时候,效果类似于美颜中的磨皮,然后有点过度了的感觉。可以看到琪亚娜的脸色和发色非常相近了。