学习 opencv---(8)非线性滤波:中值滤波,双边滤波

   正如我们上一篇文章中讲到的,线性滤波可以实现很多种不同的图像变换。然而非线性滤波,如中值滤波器和双边滤波器,有时可以达到更好的实现效果。

  邻域算子的其他一些例子还有对 二值图像进行操作的形态学算子,用于计算距离变换和寻找连通量的半全局算子

    

    一、理论与概念讲解——从现象到本质

   

    1.1 非线性滤波概述

    之前的那篇文章里,我们所考虑的滤波器都是线性的,即俩个信号之和的响应和他们各自响应之和相等。换句话说,每个像素的输出值是一些输入像素的加权和,线性滤波器易于构造,并且易于从频率响应角度来进行分析。

   其实在很多情况下,使用邻域像素的非线性滤波也许会得到更好的效果。比如在噪声是散粒噪声而不是高斯噪声,即图像偶尔会出现很大的值的时候。在这种情况下,用高斯滤波器对图像进行模糊的话,噪声像素是不会被去除的,它们只是转换为更为柔和但仍然可见的散粒。

    这就到了中值滤波登场的时候了。

  

   1.2 中值滤波

  中值滤波(Median filter)是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像边缘细节,.

  中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,其基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点,对于斑点噪声(speckle noise)和椒盐噪声(salt-and-pepper noise)来说尤其有用,因为它不依赖于邻域内那些与典型值差别很大的值。中值滤波器在处理连续图像窗函数时与线性滤波器的工作方式类似,但滤波过程却不再是加权运算。

  中值滤波在一定的条件下可以克服常见线性滤波器如最小均方滤波、方框滤波器、均值滤波等带来的图像细节模糊,而且对滤除脉冲干扰及图像扫描噪声非常有效,也常用于保护边缘信息, 保存边缘的特性使它在不希望出现边缘模糊的场合也很有用,是非常经典的平滑噪声处理方法。

  

    ●中值滤波与均值滤波器比较:

           中值滤波器与均值滤波器比较的优势:在均值滤波器中,由于噪声成分被放入平均计算中,所以输出受到了噪声的影响,但是在中值滤波器中,由于噪                                                           声成分很难选上,所以几乎不会影响到输出。因此同样用3x3区域进行处理,中值滤波消除的噪声能力更胜一筹。中                                                        值滤波无论是在消除噪声还是保存边缘方面都是一个不错的方法。 

            中值滤波器与均值滤波器比较的劣势:中值滤波花费的时间是均值滤波的5倍以上。

  顾名思义,中值滤波选择每个像素的邻域像素中的中值作为输出,或者说中值滤波将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。

   例如,取3 x 3的函数窗,计算以点[i,j]为中心的函数窗像素中值步骤如下:

                    (1) 按强度值大小排列像素点.

                   (2) 选择排序像素集的中间值作为点[i,j]的新值.

   这一过程如图下图所示.

    

   一般采用奇数点的邻域来计算中值,但如果像素点数为偶数时,中值就取排序像素中间两点的平均值.采用大小不同邻域的中值滤波器的结果如图。

  

  中值滤波在一定条件下,可以克服线性滤波器(如均值滤波等)所带来的图像细节模糊,而且对滤除脉冲干扰即图像扫描噪声最为有效。在实际运算过程中并不需要图像的统计特性,也给计算带来不少方便。但是对一些细节多,特别是线、尖顶等细节多的图像不宜采用中值滤波。

    

   1.3 双边滤波

   双边滤波(Bilateral Filter)是一种非线性的滤波方法,是结合图像的空间邻近度和 像素值相似度的一种折中处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。具有简单,非迭代,局部的特点。

  双边滤波器的好处是可以做边缘保存(edge preserving),一般过去用的维纳滤波或者高斯滤波去降噪,都会较明显地模糊边缘,对于高频细节的保护效果并不明显。双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。

  在双边滤波器中,输出像素的值依赖于邻域像素值的加权值组合:

 

   

而加权系数w(i,j,k,l)取决于定义域核和值域核的乘积。

其中定义域核表示如下(如图):

  

定义域滤波对应图示:

 值域核表示为:

 

 值域滤波:

 

两者相乘后,就会产生依赖于数据的双边滤波权重函数:

 

    二、深入——OpenCV源码分析溯源

 (1)首先让我们一起领略medianBlur()函数的源码,。。

 1 //-----------------------------------【medianBlur()函数中文注释版源代码】-------------------------  
 2 //    代码作用:进行中值滤波操作的函数   
 3 //    说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码   
 4 //    OpenCV源代码版本:3.1.0   
 5 //    源码路径:…opencvsourcesmodulesimgprocsrcsmooth.cpp   
 6 //    源文件中如下代码的起始行数:2760行  
 7 //    中文注释by hehhehhehhe  
 8 //--------------------------------------------------------------------------------------------------------  
 9 
10 void cv::medianBlur(InputArray _src0, OutputArray _dst, int ksize)
11 {
12     //处理特定的ksize值的不同情况 
13     CV_Assert((ksize %2 ==1) && (_src0.dims()<=2));   //CV_Assert()若括号中的表达式值为false,则返回一个错误信息。 
14     if (ksize <= 1)
15     {
16         _src0.copyTo(_dst);
17         return;
18     }
19 
20     /*下面的语句还需时日来理解*/
21     CV_OCL_RUN(_dst.isUMat(),
22         ocl_medianFilter(_src0, _dst, ksize))
23         
24 
25     //拷贝形参Mat数据到临时变量,用于稍后的操作
26     Mat _src0 = _src0.getMat();
27     _dst.create(src0.size(),src0.type());
28     Mat dst = _dst.getMat();
29 
30     CV_IPP_RUN(IPP_VERSION_X100 >= 810 && ksize <= 5, ipp_medianFilter(_src0, _dst, ksize));
31 
32 //若之前有过HAVE_TEGRA_OPTIMIZATION优化选项的定义,则执行宏体中的tegra优化版函数并返回 
33 #ifdef HAVE_TEGRA_OPTIMIZATION
34     if (tegra::useTegra() && tegra::medianBlur(src0, dst, ksize))
35         return;
36 #endif
37 
38     bool useSortNet = ksize == 3 || (ksize == 5
39 #if !(CV_SSE2 || CV_NEON)
40         && src0.depth() > CV_8U
41 #endif
42         );
43 
44 
45     //开始正式进行滤波操作
46     Mat src;
47     if (useSortNet)
48     {
49         if (dst.data != src0.data)
50             src = src0;
51         else
52             src0.copyTo(src);
53 
54         //根据不同的景深,给medianBlur_SortNet函数取不同的模板类型值
55         if (src.depth() == CV_8U)
56             medianBlur_SortNet<MinMax8u, MinMaxVec8u>(src, dst, ksize);
57         else if (src.depth() == CV_16U)
58             medianBlur_SortNet<MinMax16u, MinMaxVec16u>(src, dst, ksize);
59         else if (src.depth() == CV_16S)
60             medianBlur_SortNet<MinMax16s, MinMaxVec16s>(src, dst, ksize);
61         else if (src.depth() == CV_32F)
62             medianBlur_SortNet<MinMax32f, MinMaxVec32f>(src, dst, ksize);
63         else
64             CV_Error(CV_StsUnsupportedFormat, "");
65 
66         return;
67     }
68     else
69     {
70         cv::copyMakeBorder(src0, src, 0, 0, ksize / 2, ksize / 2, BORDER_REPLICATE);
71 
72         int cn = src0.channels();
73         CV_Assert(src.depth() == CV_8U && (cn == 1 || cn == 3 || cn == 4));
74 
75         double img_size_mp = (double)(src0.total()) / (1 << 20);
76         if (ksize <= 3 + (img_size_mp < 1 ? 12 : img_size_mp < 4 ? 6 : 2)*
77             (MEDIAN_HAVE_SIMD && (checkHardwareSupport(CV_CPU_SSE2) || checkHardwareSupport(CV_CPU_NEON)) ? 1 : 3))
78             medianBlur_8u_Om(src, dst, ksize);
79         else
80             medianBlur_8u_O1(src, dst, ksize);
81     }
82 }

 仔细阅读源码我们可以发现,正式进入滤波操作时,根据图像不同的位深,我们会给medianBlur_SortNet函数模板取不同的模板类型值,或者调用medianBlur_8u_Om或medianBlur_8u_O1来进行操作。

 上面我们刚说到,medianBlur_SortNet 是一个函数模板,其源码于smooth.cpp的1439行开始,由于其函数体很长,我们在此只贴出它的函数声明。

1 template<class Op, class VecOp>  
2 static void medianBlur_SortNet( constMat& _src, Mat& _dst, int m );  

 (2)bilateralFilter函数的源码也比较冗长

  从3137行到3506行都是.。大家有兴趣自己去看源代码。,时间紧迫就不看了。。。。。。。。。。。。

 再提一点,smooth.cpp源码的第2275行到2552行是OpenCV中自适应双边滤波器(adaptive BilateralFilter)的源代码,有兴趣和精力的童鞋可以去探究探究。(不过我在smooth.cpp没找到。。。。。。。。。。。。。。好尴尬)

 

   三、浅出——API函数快速上手

       3.1  中值滤波——medianBlur函数

 medianBlur函数使用中值滤波器来平滑(模糊)处理一张图片,从src输入,而结果从dst输出。

且对于多通道图片,每一个通道都单独进行处理,并且支持就地操作(In-placeoperation)。

1 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 ...

   图像深度是指存储每个像素所用的位数,也用于量度图像的色彩分辨率。

    孔径的线性尺寸这个是什么东东。。。。。。。。。?????????????????????????

  

 调用范例:

1 //载入原图
2 Mat image = imread("1.jpg");
3 //进行中值滤波操作
4 Mat out;
5 medianBlur(image, out, 7);

  3.2  双边滤波——bilateralFilter函数

   用双边滤波器来处理一张图片,由src输入图片,结果于dst输出。

   

1 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。

 调用代码示范如下:

  

1 //载入原图  
2 Mat image=imread("1.jpg");  
3 //进行双边滤波操作  
4 Mat out;  
5 bilateralFilter(image, out, 25, 25*2, 25/2);

     3.3. 完整的实例

 1 #include <opencv2/core/core.hpp>
 2 #include <opencv2/highgui/highgui.hpp>
 3 #include <opencv2/imgproc/imgproc.hpp>
 4 
 5 
 6 using namespace cv;
 7 
 8 /*--------------------------------------------------------
 9    【1】 说明:中值滤波medianBlur
10 -----------------------------------------------------------*/
11 
12 /*
13 void main()
14 {
15     //载入原图
16     Mat image = imread("1.jpg");
17 
18     //创建窗口
19     namedWindow("中值滤波【原图】");
20     namedWindow("中值滤波【效果图】");
21 
22     //显示原图
23     imshow("中值滤波【原图】",image);
24 
25     //进行中值滤波操作
26     Mat out;
27     medianBlur(image,out,1);
28 
29     //显示效果图
30     imshow("中值滤波【效果图】",out);
31 
32     waitKey();
33 }
34 */
35 
36 
37 /*--------------------------------------------------------
38     【2】 说明:双边滤波bilatralFilter
39 -----------------------------------------------------------*/
40 /*
41 void main()
42 {
43     //载入原图
44     Mat image = imread("1.jpg");
45 
46     //创建窗口
47     namedWindow("双边滤波【原图】");
48     namedWindow("双边滤波【效果图】");
49 
50     //显示原图
51     imshow("双边滤波【原图】",image );
52 
53     //进行双边滤波操作
54     Mat out;
55     bilateralFilter(image, out, 25,25*2,25/2 );
56 
57     //显示效果图
58     imshow("双边滤波【效果图】",out);
59 
60     waitKey();
61 }
62 */

     四、综合示例——在实战中熟稔

   依然是每篇文章都会配给大家的一个详细注释的博文配套示例程序,把这篇文章中介绍的知识点以代码为载体,展现给大家。

这个示例程序中可以用轨迹条来控制各种滤波(方框滤波、均值滤波、高斯滤波、中值滤波、双边滤波)的参数值,通过滑动滚动条,就可以控制图像在各种平滑处理下的模糊度,有一定的可玩性。废话不多说,上代码吧:

  1 Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4, g_dstImage5;
  2 
  3 //即图片的大小
  4 int g_nBoxFilterValue = 6;     //方框滤波内核值
  5 int g_nMeanBlurValue = 10;     //均值滤波内核值
  6 int g_nGaussianBlurValue = 6;       //高斯滤波内核值
  7 int g_nMedianBlurValue = 10;    //中值滤波内核值
  8 int g_nBilateralFilterValue = 10;    //双边滤波内核值
  9 
 10 
 11 static void on_BoxFilter(int, void *);   //方框滤波
 12 static void on_MeanBlur(int,void *);      //均值滤波
 13 static void on_GaussianBlur(int,void *);    //高斯滤波
 14 static void on_MedianBlur(int,void *);     //中值滤波
 15 static void on_BilateralFilter(int,void *);    //双边滤波
 16 
 17 
 18 
 19 
 20 int main()
 21 {
 22     system("color 5e");
 23 
 24     //载入原图
 25     g_srcImage = imread("1.jpg",1);
 26     if (!g_srcImage.data)
 27     {
 28         printf("Oh,no,读取srcImage错误~! 
");
 29         return false;
 30     }
 31 
 32     //克隆原图到5个Mat类型中
 33     g_dstImage1 = g_srcImage.clone();
 34     g_dstImage2 = g_srcImage.clone();
 35     g_dstImage3 = g_srcImage.clone();
 36     g_dstImage4 = g_srcImage.clone();
 37     g_dstImage5 = g_srcImage.clone();
 38 
 39 
 40     //显示原图
 41     namedWindow("【<0>原图窗口】",1);
 42     imshow ("【<0>原图窗口】",g_srcImage);
 43 
 44 
 45     /*----------【1】方框滤波--------------*/
 46     //创建窗口
 47     namedWindow("【<1>方框滤波】",1);
 48     //创建轨迹条
 49     createTrackbar("内核值:","【<1>方框滤波】",&g_nBoxFilterValue,60,on_BoxFilter);
 50     on_BoxFilter(g_nBoxFilterValue, 0);
 51     //imshow("【<1>方框滤波】",g_dstImage1);
 52 
 53 
 54     /*----------【2】均值滤波--------------*/
 55     //创建窗口
 56     namedWindow("【<1>方框滤波】",1);
 57     //创建轨迹条
 58     createTrackbar("内核值:", "【<2>均值滤波】", &g_nMeanBlurValue, 60, on_MeanBlur);
 59     on_MeanBlur(g_nMeanBlurValue, 0);
 60     //imshow("【<2>方框滤波】", g_dstImage2);
 61 
 62     
 63     /*----------【3】高斯滤波--------------*/
 64     //创建窗口
 65     namedWindow("【3】高斯滤波", 1);
 66     //创建轨迹条
 67     createTrackbar("内核值:", "【3】高斯滤波", &g_nGaussianBlurValue, 60, on_GaussianBlur);
 68     on_GaussianBlur(g_nGaussianBlurValue, 0);
 69     //imshow("【3】高斯滤波", g_dstImage3);
 70 
 71     
 72     /*----------【4】中值滤波--------------*/
 73     //创建窗口
 74     namedWindow("【4】中值滤波", 1);
 75     //创建轨迹条
 76     createTrackbar("内核值:", "【4】中值滤波", &g_nMedianBlurValue, 60, on_MedianBlur);
 77     on_MedianBlur(g_nMedianBlurValue, 0);
 78     //imshow("【4】中值滤波", g_dstImage4);
 79 
 80     
 81     /*----------【5】双边滤波--------------*/
 82     //创建窗口
 83     namedWindow("【5】双边滤波", 1);
 84     //创建轨迹条
 85     createTrackbar("内核值:", "【5】双边滤波", &g_nBilateralFilterValue, 60, on_BilateralFilter);
 86     on_BilateralFilter(g_nBilateralFilterValue, 0);
 87     //imshow("【5】双边滤波", g_dstImage4);
 88 
 89     
 90     //输出一些帮助信息
 91     cout <<endl<<"	嗯。好了,请调整滚动条观察图像效果~

"
 92         << "	按下“q”键时,程序退出~!
"
 93         << "

				 by hehehhehhe";
 94     while (char(waitKey(1)) != 'q'); {}
 95     
 96 
 97     return 0;
 98 }
 99 
100 
101 /*方框滤波操作的回调函数*/
102 static void on_BoxFilter(int, void *)
103 {
104     //方框滤波操作
105     boxFilter(g_srcImage, g_dstImage1, -1, Size(g_nBoxFilterValue + 1, g_nBoxFilterValue + 1));
106     //显示窗口
107     imshow("【<1>方框滤波】", g_dstImage1);
108 }
109 
110 
111 /*均值滤波操作的回调函数*/
112 static void on_MeanBlur(int, void *)
113 {
114     blur(g_srcImage, g_dstImage2, Size(g_nMeanBlurValue + 1, g_nMeanBlurValue + 1), Point(-1, -1));
115     imshow("【<2>均值滤波】", g_dstImage2);
116 }
117 
118 /*高斯滤波操作的回调函数*/
119 static void on_GaussianBlur(int, void *)
120 {
121     GaussianBlur(g_srcImage, g_dstImage3, Size(g_nGaussianBlurValue + 1, g_nGaussianBlurValue + 1), 0, 0);
122     imshow("【<3>高斯滤波】", g_dstImage3);
123 }
124 
125 
126 /*中值滤波操作的回调函数*/
127 static void on_MedianBlur(int, void *)
128 {
129     medianBlur(g_srcImage, g_dstImage4, g_nMedianBlurValue * 2 + 1);
130     imshow("【<4>中值滤波】", g_dstImage4);
131 }
132 
133 
134 /*双边滤波操作的回调函数*/
135 static void on_BilateralFilter(int, void *)
136 {
137     bilateralFilter(g_srcImage, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);
138     imshow("【<5>双边滤波】", g_dstImage5);
139 }

 

 调试的时候有问题。。。。。。。。。。。。。。。。。。。。但是源码一模一样。。。。。。。。。。。。。。。。。。待我有时间去解决

  下面是改过的代码。。。。。。。。。。读者可自行查找区别。。。。。。。

  1 #include <opencv2/core/core.hpp>
  2 #include <opencv2/highgui/highgui.hpp>
  3 #include <opencv2/imgproc/imgproc.hpp>
  4 #include <iostream>
  5 
  6 
  7 using namespace std;
  8 using namespace cv;
  9 
 10 
 11 
 12 Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4, g_dstImage5;
 13 
 14 //即图片的大小
 15 int g_nBoxFilterValue = 6;     //方框滤波内核值
 16 int g_nMeanBlurValue = 10;     //均值滤波内核值
 17 int g_nGaussianBlurValue = 6;       //高斯滤波内核值
 18 int g_nMedianBlurValue = 10;    //中值滤波内核值
 19 int g_nBilateralFilterValue = 10;    //双边滤波内核值
 20 
 21 
 22 static void on_BoxFilter(int, void *);   //方框滤波
 23 static void on_MeanBlur(int,void *);      //均值滤波
 24 static void on_GaussianBlur(int,void *);    //高斯滤波
 25 static void on_MedianBlur(int,void *);     //中值滤波
 26 static void on_BilateralFilter(int,void *);    //双边滤波
 27 
 28 
 29 
 30 
 31 int main()
 32 {
 33     system("color 5e");
 34 
 35     //载入原图
 36     g_srcImage = imread("1.jpg",1);
 37     if (!g_srcImage.data)
 38     {
 39         printf("Oh,no,读取srcImage错误~! 
");
 40         return false;
 41     }
 42 
 43     //克隆原图到5个Mat类型中
 44     g_dstImage1 = g_srcImage.clone();
 45     g_dstImage2 = g_srcImage.clone();
 46     g_dstImage3 = g_srcImage.clone();
 47     g_dstImage4 = g_srcImage.clone();
 48     g_dstImage5 = g_srcImage.clone();
 49 
 50 
 51     //显示原图
 52     namedWindow("【<0>原图窗口】",1);
 53     imshow ("【<0>原图窗口】",g_srcImage);
 54 
 55 
 56     /*----------【1】方框滤波--------------*/
 57     //创建窗口
 58     namedWindow("【1 方框滤波】",1);
 59     //创建轨迹条
 60     createTrackbar("内核值:","【1 方框滤波】",&g_nBoxFilterValue,60,on_BoxFilter);
 61     on_BoxFilter(g_nBoxFilterValue, 0);
 62     //imshow("【<1>方框滤波】",g_dstImage1);
 63 
 64 
 65     /*----------【2】均值滤波--------------*/
 66     //创建窗口
 67     namedWindow("【2】均值滤波",1);
 68     //创建轨迹条
 69     createTrackbar("内核值:", "【2】均值滤波", &g_nMeanBlurValue, 60, on_MeanBlur);
 70     on_MeanBlur(g_nMeanBlurValue, 0);
 71     //imshow("【<2>方框滤波】", g_dstImage2);
 72 
 73     
 74     /*----------【3】高斯滤波--------------*/
 75     //创建窗口
 76     namedWindow("【3】高斯滤波", 1);
 77     //创建轨迹条
 78     createTrackbar("内核值:", "【3】高斯滤波", &g_nGaussianBlurValue, 60, on_GaussianBlur);
 79     on_GaussianBlur(g_nGaussianBlurValue, 0);
 80     //imshow("【3】高斯滤波", g_dstImage3);
 81 
 82     
 83     /*----------【4】中值滤波--------------*/
 84     //创建窗口
 85     namedWindow("【4】中值滤波", 1);
 86     //创建轨迹条
 87     createTrackbar("内核值:", "【4】中值滤波", &g_nMedianBlurValue, 60, on_MedianBlur);
 88     on_MedianBlur(g_nMedianBlurValue, 0);
 89     //imshow("【4】中值滤波", g_dstImage4);
 90 
 91     
 92     /*----------【5】双边滤波--------------*/
 93     //创建窗口
 94     namedWindow("【5】双边滤波", 1);
 95     //创建轨迹条
 96     createTrackbar("内核值:", "【5】双边滤波", &g_nBilateralFilterValue, 60, on_BilateralFilter);
 97     on_BilateralFilter(g_nBilateralFilterValue, 0);
 98     //imshow("【5】双边滤波", g_dstImage4);
 99 
100     
101     //输出一些帮助信息
102     cout <<endl<<"	嗯。好了,请调整滚动条观察图像效果~

"
103         << "	按下“q”键时,程序退出~!
"
104         << "

				 by hehehhehhe";
105     while (char(waitKey(1)) != 'q'); {}
106     
107 
108     return 0;
109 }
110 
111 
112 /*方框滤波操作的回调函数*/
113 static void on_BoxFilter(int, void *)
114 {
115     //方框滤波操作
116     boxFilter(g_srcImage, g_dstImage1, -1, Size(g_nBoxFilterValue + 1, g_nBoxFilterValue + 1));
117     //显示窗口
118     imshow("【1 方框滤波】", g_dstImage1);
119 }
120 
121 
122 /*均值滤波操作的回调函数*/
123 static void on_MeanBlur(int, void *)
124 {
125     blur(g_srcImage, g_dstImage2, Size(g_nMeanBlurValue + 1, g_nMeanBlurValue + 1), Point(-1, -1));
126     imshow("【2】均值滤波", g_dstImage2);
127 }
128 
129 /*高斯滤波操作的回调函数*/
130 static void on_GaussianBlur(int, void *)
131 {
132     GaussianBlur(g_srcImage, g_dstImage3, Size(g_nGaussianBlurValue*2 + 1, g_nGaussianBlurValue*2 + 1), 0, 0);
133     imshow("【3】高斯滤波", g_dstImage3);
134 }
135 
136 
137 /*中值滤波操作的回调函数*/
138 static void on_MedianBlur(int, void *)
139 {
140     medianBlur(g_srcImage, g_dstImage4, g_nMedianBlurValue * 2 + 1);
141     imshow("【4】中值滤波", g_dstImage4);
142 }
143 
144 
145 /*双边滤波操作的回调函数*/
146 static void on_BilateralFilter(int, void *)
147 {
148     bilateralFilter(g_srcImage, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);
149     imshow("【5】双边滤波", g_dstImage5);
150 }

  如果你俩个代码都试过的话,就会发现粗心是多么的不好,

  不过话说回来,这个问题希望opencv团队可以解决,这对一个粗心的人来说是一大幸事

   

原文地址:https://www.cnblogs.com/wyuzl/p/6246381.html