• 【OpenCV】—线性滤波:方框滤波、均值滤波、高斯滤波


    一、线性滤波:方框滤波、均值滤波、高斯滤波

    1、平滑处理

    平滑处理(smoothing)也称模糊处理(bluring),是一种简单且使用频率很高的图像处理方法。平滑处理的用途有很多,最常见的是用来减少图像上的噪点或者失真。处理降低图像分辨率时,平滑处理是好用的方法。

    2、图像滤波与滤波器

    (1)图像噪声:指存在于图像数据中的不必要的或多余的干扰信息。噪声的存在严重影响了遥感图像的质量,因此在图像增强处理和分类处理之前,必须予以纠正。[1]图像中各种妨碍人们对其信息接受的因素即可称为图像噪声 。噪声在理论上可以定义为“不可预测,只能用概率统计方法来认识的随机误差”。

    (2)图像滤波:指在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。

    消除图像中的噪声成分叫作图像平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段,而在较高频段,有用的信息经常被噪声淹没。

    (3)图像滤波的目的有两个:

    • 抽出对象的特征作为图像识别的特征模式;
    • 为了适应图像处理的要求,消除图像数字化时所混入的噪声。

    对于滤波处理的要求也有两条:

    • 不能损坏图像的轮廓及边缘等重要信息;
    • 使图像清晰视角效果好。

    (4)平滑滤波:低频增强的空间域滤波技术。目的有两类:一类是模糊;另一类是消除噪音;

    空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点(像素点)的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑也会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。

    (5)滤波器:

    关于滤波器,一种形象的比喻是:可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。

    滤波器种类很多,都分别被封装在单独的函数中:

    • 方框滤波——BoxBlur函数
    • 均值滤波——也叫邻域平均滤波——Blur函数
    • 高斯滤波——GaussianBlur函数
    • 中值滤波——medianBlur函数
    • 双边滤波——bilateralFilter函数

    3、线性滤波器的简介

    说明:线性滤波器经常用于剔除输入信号中不想要的频率或者从许多频率中选择一个想要的频率。

    • 低通滤波器:允许低频率通过
    • 高通滤波器:允许高频率通过
    • 带通滤波器:允许一定范围频率通过
    • 带阻滤波器:阻止一定范围频率通过并且允许其他频率通过
    • 全通滤波器:允许所有频率通过,仅仅改变相位关系
    • 陷波滤波器(Band-Stop Filter):阻止一个狭窄频率范围通过,是一种特殊的带阻滤波器

    4、滤波和模糊

    以高斯滤波举例:滤波可分为低通滤波和高通滤波两种:高斯滤波指用高斯函数作为滤波函数的滤波操作,至于是不是模糊,要看高斯低通还是高斯高通,低通是模糊,高通就是锐化。

    5、邻域算子与线性领域滤波

    邻域算子(局部算子)是利用给定像素周围的像素值的决定此像素的最终输出值的一种算子。而线性邻域滤波就是一种常用的邻域算子,像素的输出值取决于输入像素的加权和,具体如下:

    补充:

    图像平滑:是指用于突出图像的宽大区域、低频成分、主干部分或抑制图像噪声和干扰高频成分,使图像亮度平缓渐变,减小突变梯度,改善图像质量的图像处理方法。

    图像锐化:意思是补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰,分为空间域处理和频域处理两类。图像锐化是为了突出图像上地物的边缘、轮廓,或某些线性目标要素的特征。

    邻域算子除了用于局部色调调整以外,还可以用于图像滤波,以实现图像的平滑和锐化,图像边缘增强或者图像噪声去除。

    线性邻域滤波算子:即用不同的权重去结合一个小邻域内的像素,来得到处理效果:

    在这里插入图片描述

    图注:邻域滤波(卷积)——左边的图像与中间图像的卷积产生右边图像。目标图像中蓝色标记的像素是利用原图像中红色标记的像素计算得到的。

    线性滤波处理的输出像素值g(i,j)是输入像素值f(i+k,j+I)的加权和,其中的h(k,l)我们称为”核",就是滤波器的加权系数,即滤波器的”滤波系数“。如下:
    在这里插入图片描述

    简写:f为输入像素值,h表示加权系数的”核“,g表示输出像素值

    在这里插入图片描述

    6、方框滤波(box Filter)

    说明:方框滤波被封装在一个名为boxblur的函数中,即boxblur函数的作用是使用方框滤波器(box
    filter)来模糊一张图片,从src输入,从dst输出。

    void boxFileter(InputArray src,OutputArray dst,int ddepth,Size ksize,Point
    anchor=Point(-1,-1),boolnormalize=true,int borderType=BORDER_DEFAULT)
    
    • 1
    • 2
    • 第一个参数:输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片。但是需要注意的是待处理的图片深度应该为CV_8U,CV_16U,CV_16S,CV_32F,以及CV_64F之一。

      补充:像素深度和图像深度是两个相互关联但又有所不同的两个概念。像素深度是指存储每个像素所需要的比特数。假定存储每个像素需要8bit,则图像的像素深度为8。图像深度是指像素深度中实际用于存储图像的灰度或色彩所需要的比特位数。假定图像的像素深度为16bit,但用于表示图像的灰度或色彩的位数只有15位,则图像的图像深度为15。图像深度决定了图像的每个像素可能的颜色数,或可能的灰度级数。例如,彩色图像每个像素用R,G,B三个分量表示,每个分量用8位,像素深度为24位。

    • 第二个参数:目标图像,需要和源图片有一样的尺寸和类型

    • 第三个参数:输出图像的深度,-1代表使用原图像深度,即src.depth()

    • 第四个参数:内核的大小。一般Size(w,h)来表示内核的大小,其中w为像素宽度,h表示像素高度。Size(3x3)表示3x3的核大小,Size(5,5)表示5x5核大小

    • 第五个参数:表示锚点(即被平滑的那个点)。注意:它有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心

    • 第六个参数:默认值为true,一个标识符,表示内核是否被其他区域归一化(normalized)了

    • 第七个参数:用于推断图像外部像素的某种边缘模式。有默认值BORDER_DEFAULT,一般不需要考虑

    BoxFileter()函数方框滤波所用的核表示如下:

    上式中 f 表示原图,h 表示核,g 表示目标图,当normalize=true的时候,方框滤波就变成了我们熟悉的均值滤波。也就是说,均值滤波是方框滤波归一化(normalized)后的特殊情况。其中,归一化就是把要处理的量都缩放到一个范围内,比如(0,1),以便统一处理核直观量化。而非归一化(Unnormalized)的方框滤波用于计算每个像素邻域内的积分特性,比如密集光流算法(dense optical flow algorithms)中用到的图像倒数的协方差矩阵(covariance matrices of image derivatives)

    调用代码示例如下:

    Mat image=imread("2.jpg");
    //进行均值滤波操作
    Mat out;
    boxFilter(image,out,-1,Size(5,5));
    
    • 1
    • 2
    • 3
    • 4

    示例程序:使用方框滤波来模糊一张图片

    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    using namespace cv;
    int main()
    {
    	//载入图像
    	Mat image = imread("E:\\dong.jpg");
    	namedWindow("方框滤波【原图】");
    	namedWindow("方框滤波【效果图】");
    	//显示原图
    	imshow("方框滤波【原图】", image);
    	//进行滤波操作
    	Mat out;
    	boxFilter(image, out, -1, Size(5, 5));
    	//显示效果图
    	imshow("方框滤波【效果图】", out);
    	waitKey(0);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    7、均值滤波

    说明:均值滤波,是最简单的一种滤波操作,输出图像的每一个像素是核窗口内输入图像对应像素的平均值(所有像素加权系数相等),其中说白了它就是归一化后的方框滤波。

    7.1 均值滤波的理论简析

    均值滤波是典型的线性滤波算法,主要方法为邻域平均法,即用一片图像区域的各个像素的均值来代替原图像中的各个像素值。一般需要在图像上对目标像素给出一个模板(内核),该模板包括了其周围的临近像素(比如以目标像素为中心的周围8(3x3-1)个像素,构成一个滤波模板,即去掉目标像素本身)。再用模板中全体像素的平均值来代替原来像素值。即对待处理的当前像素点(x,y),选择一个模板,该模板由其邻近的若干像素组成,求模板中所有像素的均值,再把该均值赋予当前像素点(x,y),作为处理后图像在该点上的灰度点g(x,y),即g(x,y)=1/m求和f(x,y),其中m为该模板中包含当前像素在内的像素总个数。

    7.2 均值滤波的缺陷

    均值滤波本身存在着固有的缺陷,即它不能很好地保护图像细节,在图像去噪的同时也破坏了图像的细节部分,从而使图像变得模糊,不能很好地去除噪声点。

    7.3 在OpenCV中使用均值滤波——blur函数

    blur函数的作用:对输入的图像src进行均值滤波后用dst输出

    blur函数在OpenCV官方文档中,给出的其核是这样的:

    在这里插入图片描述

    这一内核一看就明了,就是在求平均值,即blur函数封装的就是均值滤波。函数原型如下:

    void blur(InputArray src,OutputArray dst,Size kize,Point anchor=Point(-1,-1),
    int borderType=BORDER_DEFAULT)
    
    • 1
    • 2
    • 第一个参数:输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片。但是需要注意的是待处理的图片深度应该为CV_8U,CV_16U,CV_16S,CV_32F,以及CV_64F之一。
    • 第二个参数:目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
    • 第三个参数:内核的大小。一般Size(w,h)来表示内核的大小,其中w为像素宽度,h表示像素高度。Size(3x3)表示3x3的核大小,Size(5,5)表示5x5核大小
    • 第四个参数:表示锚点(即被平滑的那个点)。注意:它有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
    • 第五个参数:用于推断图像外部像素的某种边缘模式。有默认值BORDER_DEFAULT,一般不需要考虑。

    调用代码示例如下:

    Mat image=imread("1.jpg");
    Mat out;
    blur(image,out,Size(7,7));
    
    • 1
    • 2
    • 3

    示例程序:使用Blur均值滤波

    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    using namespace cv;
    int main()
    {
    	//载入图像
    	Mat image = imread("E:\\tangsan.jpg");
    	namedWindow("均值滤波【原图】");
    	namedWindow("均值滤波【效果图】");
    	//显示原图
    	imshow("均值滤波【原图】", image);
    	//进行滤波操作
    	Mat out;
    	blur(image, out, Size(7, 7));
    	//显示效果图
    	imshow("均值滤波【效果图】", out);
    	waitKey(0);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    8、高斯滤波

    8.1 高斯滤波的理论简析

    高斯滤波是一种线性平滑滤波,可以消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩膜)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。

    高斯滤波是最有用的滤波操作,虽然它用效率往往不是最高的。高斯模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像的效果散景以及普通照明阴影中的效果都明显不同。高斯平滑用于计算机视觉算法中的预处理阶段,以增强图像在不同比例大小下的图像效果(参与尺度空间表示以及尺度空间实现)。高斯模糊:图像的高斯模糊过程就是图像与正态分布做卷积,正态分布也叫做高斯分布。

    图像与圆形方框模糊做卷积将会生成更加精确的焦外成像效果。由于高斯函数的傅里叶变换是另外一个高斯函数,所以高斯模糊对于图像处理来说就是一个低通滤波操作。

    高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。一维零均值高斯函数如下:

    在这里插入图片描述

    其中Sigma决定了高斯函数的宽度。对于图像处理来说,常用的二维零均值离散高斯函数作平滑滤波器。

    二维高斯函数如下:

    在这里插入图片描述

    8.2 高斯滤波:GaussianBlur函数

    说明:GaussianBlur函数的作用是用高斯滤波器来模糊一张图片,对输入的图像src进行高斯滤波后用dst输出。它将源图像和指定的高斯核函数做卷积运算,并且支持就地过滤(In-placefiltering)

    void GaussianBlur(InputArray src,OutArray dst,Size ksize,double sigmaX,double sigmaY=0,
    intborderType=BORDER_DEFAULT)
    
    • 1
    • 2
    • 第一个参数:输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片。但是需要注意的是待处理的图片深度应该为CV_8U,CV_16U,CV_16S,CV_32F,以及CV_64F之一。
    • 第二个参数:目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
    • 第三个参数:内核的大小。其中ksize.width和ksize.height可以不同,但它们都必须为正数和奇数,或者为零,这都是由sigma计算的。
    • 第四个参数:表示高斯核函数在X方向的标准偏差
    • 第五个参数:表示高斯核函数在Y方向的标准偏差。若sigmaY为0,就将它设为sigmaX;如果sigmaX和sigmaY都是0,则由ksize.width和ksize.height计算
    • 第六个参数:用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT

    调用示例如下:

    Mat image=imread("1.jpg");
    Mat out;
    GaussianBlur(image,out,Size(5,5),0,0);
    
    • 1
    • 2
    • 3

    GaussianBlur函数完整示例程序:

    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    using namespace cv;
    int main()
    {
    	//载入图像
    	Mat image = imread("E:\\tangsan.jpg");
    	namedWindow("高斯滤波【原图】");
    	namedWindow("高斯滤波【效果图】");
    	//显示原图
    	imshow("高斯滤波【原图】", image);
    	//进行滤波操作
    	Mat out;
    	GaussianBlur(image, out, Size(3, 3), 0, 0);
    	//显示效果图
    	imshow("高斯滤波【效果图】", out);
    	waitKey(0);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    9、线性滤波相关OpenCV源码解析

    9.1OpenCV中boxFilter函数源码解析

    boxFilter函数的源代码如下:

    void cv::boxFilter(InputArray _src, OutputArray _dst, int ddepth,
    	Size ksize, Point anchor,
    	bool normalize, int borderType)
    {
    	Mat src = _src.getMat();//复制源图的形参Mat数据到临时变量,
    	int sdepth=src.depth(),cn=src.channels();//定义int临时变量,代表源图深度的sdepth,源图通道的引用cn
        //处理ddepth小于0的情况
    	if (ddepth < 0)
    		ddepth = sdepth;
    	_dst.create(src.size(), CV_MAKETYPE(ddepth, cn));//初始化目标图
    	Mat dst = _dst.getMat();//复制目标图的形参Mat数据到临时变量,用于稍后的操作
    	//处理borderType不为BORDER_CONSTANT 且normalize为真的情况
        if (borderType != BORDER_CONSTANT && normaliz)
    	{
    		if (src.rows == 1)
    			ksize.height = 1;
    		if (src.cols == 1)
    			ksize.width = 1;
    	}
    //若之前有过HAVE_TEGRA_OPTIMIZATION优化选项的定义,则执行宏体中的tegra优化版函数并返回
        #define HAVE_TEGRA_OPTIMIZATION
        if(tegra::box(src,dst,ksize,anchor,normalize,borderType))
            return;
        #endif
        	//调用FilteEngine滤波引擎,正式开始滤波操
    	Ptr<FilterEngine> f = createBoxFilter(src.type(), dst.type(),
    		ksize, anchor, normalize, borderType);
    	f->apply(src, dst, wsz, ofs);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    上述,Ptr 是用来动态分配的对象的智能指针模板类。可以发现,函数的内部代码思路很清晰:先复制源图像的形参Mat数据到临时变量,定义一些临时变量,再处理ddepth小于零的情况,接着处理borderType不为BORDER_CONSTANT且normalize为真的情况,最终调用FileEngine滤波引擎创建一个BoxFilter,正式滤波操作。

    9.2 FilterEngine类解析:OpenCV图像滤波核心引擎

    说明:FilterEngine类是OpenCV关于图像滤波的主力军类,是OpenCV图像滤波功能的核心引擎。各种滤波函数如blur、GaussianBlur,其实就是在函数的末尾处定义一个Ptr类型的f,然后f->apply(src,dst).

    这个类可以把几乎所有的滤波操作施加到图像上,它包含了所有必要的中间缓冲器。其中有很多滤波相关的create系函数的返回值直接就是Ptr,如:

    cv::createSeparableLinearFiter()
    cv::createLinearFilter(),cv::createGaussianFilter(),cv::createDerivFilter()
    cv::createBoxFilter()
    cv::createMorphologyFileter()
    
    • 1
    • 2
    • 3
    • 4

    下面给出其中一个函数的原型:

    Ptr<FilterEngine>createLinearFilter(int srcType,int dstType,InputArray kernel,Point_anchor=Point(-1,-1),
    double delta=0,int rowBorderType=BORDER_DEFAULT,intcolumBorderType=-1,const Scalar& borderValue=Saclar())
    
    • 1
    • 2

    Ptr是用来动态分配的对象的智能指针模板类,而尖括号里的模板参数是FilterEngine:

    使用FilterEngine类可以分块处理大量的图像,构成复杂的管线,其中就包含一些进行滤波阶段。如果使用预先定义好的滤波操作,有cv::filter2D()、cv::erode()和cv::dilate()可以选择,它们不依赖于FilterEngine,在自己函数体内部就实现了FilterEngine提供的功能;不像其他诸如blur系列函数,依赖与FilterEngine引擎。

    接下来看其类声明经过详细注释的源码,如下:

    //-----------------------------------【FilterEngine类中文注释版源代码】----------------
    //     源码路径:…\opencv\sources\modules\imgproc\include\opencv2\imgproc\imgproc.hpp
    //     源文件中如下代码的起始行数:222行
    //     中文注释by浅墨
    //-----------------------------------------------------------------------------------
    class CV_EXPORTS FilterEngine
    {
    public:
       //默认构造函数
       FilterEngine();
       //完整的构造函数。 _filter2D 、_rowFilter 和 _columnFilter之一,必须为非空
       FilterEngine(const Ptr<BaseFilter>& _filter2D,
                     constPtr<BaseRowFilter>& _rowFilter,
                    constPtr<BaseColumnFilter>& _columnFilter,
                     int srcType, int dstType, intbufType,
                     int_rowBorderType=BORDER_REPLICATE,
                     int _columnBorderType=-1,
                     const Scalar&_borderValue=Scalar());
       //默认析构函数
       virtual ~FilterEngine();
       //重新初始化引擎。释放之前滤波器申请的内存。
       void init(const Ptr<BaseFilter>& _filter2D,
                  constPtr<BaseRowFilter>& _rowFilter,
                  constPtr<BaseColumnFilter>& _columnFilter,
                  int srcType, int dstType, intbufType,
                  int_rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1,
                  const Scalar&_borderValue=Scalar());
       //开始对指定了ROI区域和尺寸的图片进行滤波操作
       virtual int start(Size wholeSize, Rect roi, int maxBufRows=-1);
        //开始对指定了ROI区域的图片进行滤波操作
       virtual int start(const Mat& src, const Rect&srcRoi=Rect(0,0,-1,-1),
                          bool isolated=false, intmaxBufRows=-1);
       //处理图像的下一个srcCount行(函数的第三个参数)
       virtual int proceed(const uchar* src, int srcStep, int srcCount,
                            uchar* dst, intdstStep);
       //对图像指定的ROI区域进行滤波操作,若srcRoi=(0,0,-1,-1),则对整个图像进行滤波操作
       virtual void apply( const Mat& src, Mat& dst,
                            const Rect&srcRoi=Rect(0,0,-1,-1),
                            Point dstOfs=Point(0,0),
                            bool isolated=false);
     
       //如果滤波器可分离,则返回true
    boolisSeparable() const { return (const BaseFilter*)filter2D == 0; }
     
       //返回输入和输出行数
       int remainingInputRows() const;
    intremainingOutputRows() const;
     
       //一些成员参数定义
       int srcType, dstType, bufType;
       Size ksize;
       Point anchor;
       int maxWidth;
       Size wholeSize;
       Rect roi;
       int dx1, dx2;
       int rowBorderType, columnBorderType;
       vector<int> borderTab;
       int borderElemSize;
       vector<uchar> ringBuf;
       vector<uchar> srcRow;
       vector<uchar> constBorderValue;
       vector<uchar> constBorderRow;
       int bufStep, startY, startY0, endY, rowCount, dstY;
       vector<uchar*> rows;
     
       Ptr<BaseFilter> filter2D;
       Ptr<BaseRowFilter> rowFilter;
       Ptr<BaseColumnFilter> columnFilter;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    9.3 OpenCV中blur函数源码剖析

    说明:blur函数内部调用了一个boxFilter函数,且第6个参数为true,也就是上文说的normalize=true,即均值滤波是均一化后的方框滤波。

    void cv::blur(InputArray src,OutputArray dst,Size ksize,Point anchor,int borderType)
    {
        //调用boxFilter函数进行处理
        boxFilter(src,dst,-1,ksize,anchor,true,borderType);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    10、OpenCV中GaussianBlur函数源码剖析

    void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
                       double sigma1, double sigma2,
                       int borderType )
    {
      //初始化及边界类型等的判断
        CV_INSTRUMENT_REGION()
    
        int type = _src.type();
        Size size = _src.size();
        _dst.create( size, type );
    
        if( borderType != BORDER_CONSTANT && (borderType & BORDER_ISOLATED) != 0 )
        {
            if( size.height == 1 )
                ksize.height = 1;
            if( size.width == 1 )
                ksize.width = 1;
        }
    //容易理解,高斯滤波器如果尺寸为1,根据高斯函数可以知道该系数为1,所以就是将输入复制到输出
        if( ksize.width == 1 && ksize.height == 1 )
        {
            _src.copyTo(_dst);
            return;
        }
    //OpenCV中针对一些ksize = 3和5的情况做了OpenCL优化,所以初始化OpenCL相关函数
        bool useOpenCL = (ocl::isOpenCLActivated() && _dst.isUMat() && _src.dims() <= 2 &&
                   ((ksize.width == 3 && ksize.height == 3) ||
                   (ksize.width == 5 && ksize.height == 5)) &&
                   _src.rows() > ksize.height && _src.cols() > ksize.width);
        (void)useOpenCL;
    
        int sdepth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
    //获取gaussianKernels
        Mat kx, ky;
        createGaussianKernels(kx, ky, type, ksize, sigma1, sigma2);
    //调用opencl进行计算
        CV_OCL_RUN(useOpenCL, ocl_GaussianBlur_8UC1(_src, _dst, ksize, CV_MAT_DEPTH(type), kx, ky, borderType));
    //如果不是ksize=3或者5的情况,考虑使用filter2D的opencl优化程序计算
        CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && (size_t)_src.rows() > kx.total() && (size_t)_src.cols() > kx.total(),
                   ocl_sepFilter2D(_src, _dst, sdepth, kx, ky, Point(-1, -1), 0, borderType))
    //如果OpenCL版的filter2D依然不能计算,则选择cpu版本的gaussianBlur
        Mat src = _src.getMat();
        Mat dst = _dst.getMat();
    
        Point ofs;
        Size wsz(src.cols, src.rows);
        if(!(borderType & BORDER_ISOLATED))
            src.locateROI( wsz, ofs );
    
        CALL_HAL(gaussianBlur, cv_hal_gaussianBlur, src.ptr(), src.step, dst.ptr(), dst.step, src.cols, src.rows, sdepth, cn,
                 ofs.x, ofs.y, wsz.width - src.cols - ofs.x, wsz.height - src.rows - ofs.y, ksize.width, ksize.height,
                 sigma1, sigma2, borderType&~BORDER_ISOLATED);
    
        CV_OVX_RUN(true,
                   openvx_gaussianBlur(src, dst, ksize, sigma1, sigma2, borderType))
    
        CV_IPP_RUN_FAST(ipp_GaussianBlur(src, dst, ksize, sigma1, sigma2, borderType));
    //若CPU版本的gaussianBlur仍然不能计算,则选择CPU版本的filter2D
        sepFilter2D(src, dst, sdepth, kx, ky, Point(-1, -1), 0, borderType);
    }       
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    11、图像线性滤波综合示例

    补充:计算机视觉里面的“内核”就是用来圈定用来计算某一个像素的新值所用到的其周围像素点的一个框(圆,或者任意形状)。

    #include<opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include<iostream>
    using namespace cv;
    using namespace std;
    //---------------------------------
    //全局变量声明
    //-------------------------------
    Mat g_srcIamge, g_dstImage1, g_dstImage2, g_dstImage3;//存储图片的Mat类型
    int g_nBoxFilterValue = 3;//方框滤波参数值
    int g_nMeanBlurValue = 3;//均值滤波参数值
    int g_nGaussianBlurValue = 3;//高斯滤波参数值
    //---------------------------------
    //全局函数声明
    //-------------------------------
    static void on_BoxFilter(int, void *);//方框滤波
    static void on_MeanBlur(int, void *);//均值滤波
    static void on_GaussianBlur(int, void *);//高斯滤波
    int main()
    {
    	//改变console字体颜色
    	system("color 5E");
    	//载入原图
    	g_srcIamge = imread("E:\\tangsan.jpg", 1);
    	if (!g_srcIamge.data)
    	{
    		printf("读取图片错误\n");
    		return false;
    	}
    	//复制原图到三个Mat类型中
    	g_dstImage1 = g_srcIamge.clone();
    	g_dstImage2 = g_srcIamge.clone();
    	g_dstImage3 = g_srcIamge.clone();
    	//显示原图
    	namedWindow("【<0>原图窗口】", 1);
    	imshow("【<0>原图窗口】", g_srcIamge);
    
    	//=========1、方框滤波===========
    	//创建窗口
    	namedWindow("【<1>方框滤波】",1);
    	//创建轨迹条
    	createTrackbar("内核值:", "【<1>方框滤波】", &g_nBoxFilterValue, 40, on_BoxFilter);
    	on_MeanBlur(g_nBoxFilterValue, 0);
    
    
    	//======2、均值滤波===========
    	namedWindow("【<2>均值滤波】", 1);
    	//创建轨迹条
    	createTrackbar("内核值:", "【<2>均值滤波】", &g_nMeanBlurValue, 40, on_BoxFilter);
    	on_MeanBlur(g_nMeanBlurValue, 0);
    
    
    	//=====3、高斯滤波===============
    	namedWindow("【<3>高斯滤波】", 1);
    	//创建轨迹条
    	createTrackbar("内核值:", "【<3>高斯滤波】", &g_nGaussianBlurValue, 40, on_BoxFilter);
    	on_GaussianBlur(g_nGaussianBlurValue, 0);
    
    
    	//输出一些帮助信息
    	cout << endl << "请调整滚动条观察图像效果~\n\n" << "\t按下“q”键时,程序退出~\n";
    	//在waitKey(1)之后输入一个值
    	while(char(waitKey(1)!='q')){}
    	return 0;
    }
    //----------------------
    //方框滤波操作的回调函数
    //---------------------------
    static void on_BoxFilter(int, void *)//方框滤波
    {
    	boxFilter(g_srcIamge, g_dstImage1, -1, Size(g_nBoxFilterValue + 1, g_nBoxFilterValue + 1));
    	imshow("【<1>方框滤波】", g_dstImage1);
    }
    static void on_MeanBlur(int, void *)//均值滤波
    {
    	blur(g_srcIamge, g_dstImage2, Size(g_nMeanBlurValue + 1, g_nMeanBlurValue + 1), Point(-1, -1));
    	imshow("【<2>均值滤波】", g_dstImage2);
    }
    static void on_GaussianBlur(int, void *)//高斯滤波
    {
    	GaussianBlur(g_srcIamge, g_dstImage3, 
    	Size(g_nGaussianBlurValue * 2 + 1, g_nGaussianBlurValue * 2 + 1), 0, 0);
    	imshow("【<3>高斯滤波】", g_dstImage3);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    示例截图:

    (1)图像原图
    在这里插入图片描述
    (2)方框滤波
    在这里插入图片描述
    (3)均值滤波
    在这里插入图片描述
    (4)高斯滤波
    在这里插入图片描述

  • 相关阅读:
    tensorboard报错解决:No dashboards are active for the current data set
    SpringCloud_OAuth 2.0 实现单点登录
    详解python的集合
    【21天学习挑战赛】冒泡排序与插入排序
    出去重复的列值(关键词:distinct)
    【Docker与微服务】高级篇
    【JavaWeb】
    docker 搭建rknn转换环境
    libvirt 使用UEFI 设置 edk2-ovmf
    Python实现批量采集美女shipin<无水印>
  • 原文地址:https://blog.csdn.net/qq_44859533/article/details/125503636