首先感谢@浅墨_毛星云,本篇博文是小武通过学习@浅墨_毛星云的博客以及书籍《opencv3.0编程入门》整理的笔记及疑问心得,小武水平有限,欢迎交流。
@浅墨_毛星云博文:https://blog.csdn.net/poem_qianmo/article/category/1923021
一、中值滤波——medianBlur函数
基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像边缘细节。
●中值滤波与均值滤波器比较
中值滤波器与均值滤波器比较的优势:在均值滤波器中,由于噪声成分被放入平均计算中,所以输出受到了噪声的影响,但是在中值滤波器中,由于噪声成分很难选上,所以几乎不会影响到输出。因此同样用3x3区域进行处理,中值滤波消除的噪声能力更胜一筹。中值滤波无论是在消除噪声还是保存边缘方面都是一个不错的方法。
中值滤波器与均值滤波器比较的劣势:中值滤波花费的时间是均值滤波的5倍以上。
Ps: 中值滤波在一定条件下,可以克服线性滤波器(如均值滤波等)所带来的图像细节模糊,而且对滤除脉冲干扰即图像扫描噪声最为有效。在实际运算过程中并不需要图像的统计特性,也给计算带来不少方便。但是对一些细节多,特别是线、尖顶等细节多的图像不宜采用中值滤波。
函数原型:
C++: void medianBlur(InputArray src,OutputArray dst, int ksize)
参数详解:
- 第一个参数,InputArray类型的src,函数的输入参数,填1、3或者4通道的Mat类型的图像;当ksize为3或者5的时候,图像深度需为CV_8U,CV_16U,或CV_32F其中之一,而对于较大孔径尺寸的图片,它只能是CV_8U。
- 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。我们可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
- 第三个参数,int类型的ksize,孔径的线性尺寸(aperture linear size),注意这个参数必须是大于1的奇数,比如:3,5,7,9 ...
二、双边滤波——bilateralFilter函数
双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。具有简单、非迭代、局部的特点。
双边滤波器之所以能够做到在平滑去噪的同时还能够很好的保存边缘(Edge Preserve),是由于其滤波器的核由两个函数生成:
- 一个函数由像素欧式距离决定滤波器模板的系数
- 另一个函数由像素的灰度差值决定滤波器的系数
详情:http://www.cnblogs.com/wangguchangqing/p/6416401.html
函数原型:
C++: void bilateralFilter(InputArray src, OutputArraydst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)
参数详解:
- 第一个参数,InputArray类型的src,输入图像,即源图像,需要为8位或者浮点型单通道、三通道的图像。
- 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
- 第三个参数,int类型的d,表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
- 第四个参数,double类型的sigmaColor,颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
- 第五个参数,double类型的sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
- 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
代码实现:
综合学习OpenCV之五综合案例:
//添加头文件 #include<opencv2/core/core.hpp> #include<opencv2/highgui.hpp> #include<opencv2/imgproc.hpp> #include<iostream> using namespace std; using namespace cv; int kernel_box=3; int kernel_mean=3; int kernel_gaussian=3; int kernel_median=3; int kernel_bilateral=3; Mat Img_in , Img_out1, Img_out2,Img_out3,Img_out4,Img_out5; void callback_box(int ,void*); void callback_mean(int ,void*); void callback_gaussian(int ,void*); void callback_median(int ,void*); void callback_bilateral(int ,void*); //主函数 int main () { Img_in=imread("lenna.jpg"); imshow("【原图】",Img_in); //加跟踪条 namedWindow("【方框滤波】"); createTrackbar("内核大小","【方框滤波】",&kernel_box,25,callback_box); callback_box(kernel_box,0); namedWindow("【均值滤波】"); createTrackbar("内核大小","【均值滤波】",&kernel_mean,25,callback_mean); callback_mean(kernel_mean,0); namedWindow("【高斯滤波】"); createTrackbar("内核大小","【高斯滤波】",&kernel_gaussian,25,callback_gaussian); callback_gaussian(kernel_gaussian,0); namedWindow("【中值滤波】"); createTrackbar("内核大小","【中值滤波】",&kernel_median,25,callback_median); callback_median(kernel_median,0); namedWindow("【双边滤波】"); createTrackbar("内核大小","【双边滤波】",&kernel_bilateral,25,callback_bilateral); callback_bilateral(kernel_bilateral,0); waitKey(0); return 0; } //回调函数 void callback_box(int ,void*) { if (kernel_box%2 ==0) kernel_box=kernel_box+1; boxFilter(Img_in,Img_out1,-1,Size(kernel_box,kernel_box)); imshow("【方框滤波】",Img_out1); } void callback_mean(int ,void*) { if (kernel_mean%2 ==0) kernel_mean=kernel_mean+1; blur(Img_in,Img_out2,Size(kernel_mean,kernel_mean)); imshow("【均值滤波】", Img_out2); } void callback_gaussian(int ,void*) { if (kernel_gaussian%2 ==0) kernel_gaussian=kernel_gaussian+1; GaussianBlur(Img_in,Img_out3,Size(kernel_gaussian*2+1 , kernel_gaussian*2+1),0,0); imshow("【高斯滤波】",Img_out3); } void callback_median(int ,void*) { if (kernel_median%2 ==0) kernel_median=kernel_median+1; medianBlur(Img_in,Img_out4,kernel_median*2+1); imshow("【中值滤波】",Img_out4); } void callback_bilateral(int ,void*) { bilateralFilter(Img_in , Img_out5,kernel_bilateral,kernel_bilateral*2,kernel_bilateral/2); imshow("【双边滤波】",Img_out5); }效果: