美文网首页iOS 模式识别
三 (3.2 imgproc) 平滑处理 - 滤波器

三 (3.2 imgproc) 平滑处理 - 滤波器

作者: 交大小丑 | 来源:发表于2018-10-24 21:04 被阅读33次

    滤波作用:

    滤波的目的有两个:
    1.抽出对象的特征作为图像识别的特征模式;
    2.为适应图像处理要求,消除数字图像所混入的噪声 .
    对图像滤波有两个要求:
    1.不能损坏图像的轮廓和边缘等重要信息;
    2.使图像清晰视觉效果更好.

    三种常见线性邻域滤波:“方框滤波”,“均值滤波”,“高斯滤波” 。
    非线性滤波:“中值滤波”,“双边滤波”。

    • 方框滤波——boxblur函数
    • 均值滤波(邻域平均滤波)——blur函数
    • 高斯滤波——GaussianBlur函数
    • 中值滤波——medianBlur函数
    • 双边滤波——bilateralFilter函数

    线性滤波

    一、 方框滤波(box Filter)

    1.1. 原理

    先给出内核,用内核各点的值与其对应的图像像素值相乘


    卷积计算示意图
    卷积计算公式

    可以看出通过滤波后,图片的边缘信息会丢失。

    方框滤波(box Filter)被封装在一个名为boxFilter的函数中。
    void boxFilter( InputArray src, OutputArray dst, int ddepth,
                    Size ksize, Point anchor = Point(-1,-1),
                    bool normalize = true,
                    int borderType = BORDER_DEFAULT );
    
    • InputArray src :输入的图像
    • OutputArray dst:输出的图像
    • int ddepth 输出图像的深度,-1 代表使用原图深度,即src.depth()
      Size ksize 内核的大小。一般这样写Size(w, h)来表示内核的大小,Size(3, 3)就表示 3x3 的核大小
    • Point anchor = Point(-1,-1) 表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1) 如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
    • bool normalize = true 默认值为true,一个标识符,表示内核是否被其区域归一化(normalized)了
    • int borderType = BORDER_DEFAULT 用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
    参数可以省略:
    boxFilter(src, dst, -1, Size(10, 10)); // 后面3个参数都用默认值
                                           // Point anchor = Point(-1,-1)
                                           // bool normalize = true
                                           // int borderType = BORDER_DEFAULT
    

    1.2. 方框滤波实例

    方框滤波函数使用的卷积核
    • normalize = true 时,方框滤波就变成了均值滤波。也就是说,均值滤波是方框滤波归一化(normalized)后的特殊情况。其中,归一化就是把要处理的量都缩放到一个范围内,比如(0,1),以便统一处理和直观量化。
    • normalize = false 时,方框滤波用于计算每个像素邻域内的积分特性,比如密集光流算法(dense optical flow algorithms)中用到的图像倒数的协方差矩阵(covariance matrices of image derivatives)
      如果我们要在可变的窗口中计算像素总和,可以使用integral()函数。

    1.3. 代码实例

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    
    using namespace cv;
    
    int main() {
    
        Mat src = imread("../pics/pig.jpg");
    
        namedWindow("原图");
        imshow("原图", src);
    
        Mat dst;
    
        // 方框滤波
        boxFilter(src, dst, -1, Size(10, 10)); // 后面3个参数都用默认值
    
        namedWindow("方框滤波");
        imshow("方框滤波", dst);
    
        waitKey(0);
    }
    

    二、 均值滤波(blur函数)

    2.1. 原理

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

    均值滤波封装在一个名为blur的函数中:
    void blur( InputArray src, OutputArray dst,
               Size ksize, Point anchor = Point(-1,-1),
               int borderType = BORDER_DEFAULT );
    
    均值滤波的核:
    均值滤波的核

    2.2. 实例

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    
    using namespace cv;
    
    int main() {
    
        Mat src = imread("../pics/pig.jpg");
    
        namedWindow("原图");
        imshow("原图", src);
    
        Mat dst;
    
        // 均值滤波
        blur(src, dst, Size(10, 10));
    
        namedWindow("均值滤波");
        imshow("均值滤波", dst);
    
        waitKey(0);
    }
    

    2.3. 缺陷

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

    三、 高斯滤波(GaussianBlur函数)

    3.1. 原理

    从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积,由于正态分布也被称为高斯分布,因此这项技术被称为高斯模糊。

    由于高斯函数的傅立叶变换是另外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波操作。

    具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的 加权平均灰度值 去替代模板中心像素点的值。

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

    1. 正态分布 --- 一维

    正态分布

    2. 正态分布 --- 二维

    上面的正态分布是一维的,图像都是二维的,所以我们需要二维的正态分布。


    二维正态分布

    3.2. 函数推导

    3.2.1 一维函数推导:

    正态分布的密度函数叫做“高斯函数”(Gaussian function)。
    正态分布也成为高斯分布,二位正态分布也成为二维高斯分布、
    一维高斯分布(正态分布)公式

    一维高斯函数
    对于任意的实数a,b,c,是以著名数学家Carl Friedrich Gauss的名字命名的。高斯的一维图是特征对称“bell curve”形状,a是曲线尖峰的高度,b是尖峰中心的坐标,c称为标准方差,表征的是bell钟状的宽度。
    一维高斯函数

    高斯函数广泛应用于统计学领域,用于表述正态分布,在信号处理领域,用于定义高斯滤波器,在图像处理领域,二维高斯核函数常用于高斯模糊Gaussian Blur,在数学领域,主要是用于解决热力方程和扩散方程,以及定义Weiertrass Transform。

    当函数积分面积为1时,是我们的常用的正态分布,使用积分求出面积为1时,各个参数大小:

    从上图可以看出,高斯函数是一个指数函数,其log函数是对数凹二次函数

    高斯函数的积分是误差函数error function,尽管如此,其在整个实线上的反常积分能够被精确的计算出来,使用如下的高斯积分


    image.png image.png image.png
    当且仅当:
    image.png

    上式积分为1,在这种情况下,高斯是正态分布随机变量的概率密度函数,期望值μ=b,方差delta^2 = c^2,即


    image.png
    • 参数u 是对称轴
    • 高斯分布参数Sigma ( δ )决定了高斯函数的宽度。也就高斯函数的聚合程度。


      image.png

    对于图像处理来说,常用二维零均值离散高斯函数作平滑滤波器

    3.2.2 二维高斯函数推导:

    二维高斯函数,形如:

    二维高斯函数
    当函数在的X,Y方向上对称时:
    对称形式

    A是幅值,x。y。是中心点坐标,σx σy是方差,图示如下,A = 1, xo = 0, yo = 0, σx = σy = 1


    二维高斯函数图像

    该函数各向同性,其曲线是草帽状的对称图,该曲线对整个覆盖面积求积分为1。高斯滤波的思路就是:对高斯函数进行离散化,以离散点上的高斯函数值为权值,对我们采集到的灰度矩阵的每个像素点做一定范围邻域内的加权平均,即可有效消除高斯噪声。

    四、 中值滤波

    高斯模糊(高斯滤波)的原理与算法 - lsh呵呵的专栏 - CSDN博客
    https://blog.csdn.net/nima1994/article/details/79776802

    高斯函数及其各阶导数 - 文森vincent - 博客园 http://www.cnblogs.com/vincent2012/archive/2012/08/29/2662907.html

    高斯函数的详细分析 - ZuckGogo - CSDN博客 https://blog.csdn.net/jorg_zhao/article/details/52687448

    图像滤波之高斯滤波介绍 - 淇淇宝贝 - 博客园 https://www.cnblogs.com/qiqibaby/p/5289977.html

    3.3 高斯滤波离散化算法实现

    可以发现很多教材以及网上的资料都描述高斯滤波的原理是采用高斯算子对图像进行卷积运算。其实在各个算法库如Matlab、OpenCV等,在实现的时候,就是采用一个矩阵模板进行加权运算,拿图像的八连通区域来说,中间点的像素值就等于八连通区的像素值的均值,这样达到平滑的效果,该模板我们常成为高斯核。

    根据上述分析可知,高斯核是整个求解的关键。很显然,它是通过二维高斯函数计算得来的。这里给出离散高斯核矩阵的计算公式。
    离散的高斯卷积核H: (2k+1)×(2k+1)维,其元素计算方法为:


    离散的高斯卷积核计算公式 离散的高斯卷积核计算公式

    其中Sigma为方差,k确定核矩阵的维数,关于这两个取值,在下文进行分析。

    与matlab函数运行结果对比

    这里对高斯卷积核的矩阵算法进行验证。
    1)在matlab中用以下代码可以产生一个3×3的核。

     filter=fspecial('gaussian',3,1);
    

    所产生的核矩阵为:

                  filter =
                             0.0751    0.1238   0.0751
                             0.1238    0.2042   0.1238
                             0.0751   0.1238    0.0751
    

    2)同样对于上述的公式,我们取k=1,Sigma=1,即可得到3×3的高斯卷积核如下:


    image.png

    上文说到,我们进行加权滤波,权系数之和必须为1,上面所求出的高斯滤波核函数同样的必须进行归一化:


    image.png
    由此可以证明该核函数计算方法的正确性。

    openCV中使用的高斯滤波

    1. OpenCv中cvSmooth函数的用法

    该函数原型为:
    void cvSmooth(const CvArr* src, CvArr* dst,
                  int smoothtype=CV_GAUSSIAN,
                  int param1=3, int param2=0,double param3=0, double param4=0 );
    
    • 该函数前三个参数很容易理解,至于后四个参数以下进行分析。
    • 如果指定param1和param2,则代表核函数的行列,即为滤波窗口的宽度和高度;
    • Param3:高斯卷积的Sigma值
    • 如果用户希望采用非对称的高斯核,则引入param4,最后两个参数分别代表水平核以及垂直核维数;
    • 如果param3没有给出,则有前两个参数param1和param2计算出Sigma。这里的根据是高斯分布的特点(如图所示,数值分布在(μ—3σ,μ+3σ)中的概率为0.9974),如果核矩阵更大,那么相应的Sigma也更大,相反,如果Sigma更大,那么核矩阵覆盖范围也更大。具体到OpenCv下,用如下公式进行计算(根据其源代码显示)。


      image.png
    • 同样的根据这个公式可知,如果param1和param2为0(或者没有给出),那么滤波窗口的尺寸,则有后两个参数代表的Sigma来确定。


      image.png

    2. 高斯滤波封装在一个名为GaussianBlur的函数中。

    void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                       double sigmaX, double sigmaY = 0,
                       int borderType = BORDER_DEFAULT );
    
    • Size ksize ksize.width 和 ksize.height 可以不同,但他们都必须为正数和奇数,或者为0,可由 sigma 计算而来
    • double sigmaX 高斯核函数在 X 方向的的标准差
    • double sigmaY 高斯核函数在 Y 方向的的标准差
    • 若 sigmaY 为零,就将它设为 sigmaX;
    • 若 sigmaX 和 sigmaY 都是0,那么就由 ksize.width 和 ksize.height 计算出来

    3.4 实例

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    
    using namespace cv;
    
    int main() {
    
        Mat src = imread("../pics/pig.jpg");
    
        namedWindow("原图");
        imshow("原图", src);
    
        Mat dst;
    
        // 高斯滤波
        // sigmaX 和 sigmaY 都是0,就由 ksize.width 和 ksize.height 计算出来
        // Size w,h 必须为奇数
        GaussianBlur(src, dst, Size(5, 5), 0, 0);
    
        namedWindow("高斯滤波");
        imshow("高斯滤波", dst);
    
        waitKey(0);
    }
    
    核心代码
    // 高斯滤波
    // sigmaX 和 sigmaY 都是0,就由 ksize.width 和 ksize.height 计算出来
    // Size w,h 必须为奇数
    GaussianBlur(src, dst, Size(5, 5), 0, 0);
    

    高斯滤波:
    opencv学习(二十)之高斯滤波GaussianBlur() - 烟雨博客 - CSDN博客 https://blog.csdn.net/keith_bb/article/details/54412493?fps=1&locationNum=11
    opencv源码解析之(4):GaussianBlur() - tornadomeet - 博客园 http://www.cnblogs.com/tornadomeet/archive/2012/03/10/2389617.html


    非线性滤波概述

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

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

    四、 中值滤波(medianBlur函数)

    4.1 原理

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

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

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

    椒盐噪声
    椒盐噪声是由图像传感器,传输信道,解码处理等产生的黑白相间的亮暗点噪声。椒盐噪声是指两种噪声,一种是盐噪声(salt noise)盐=白色(255),另一种是胡椒噪声(pepper noise),椒=黑色(0)。前者是高灰度噪声,后者属于低灰度噪声。一般两种噪声同时出现,呈现在图像上就是黑白杂点。对于彩色图像,则表现为在单个像素BGR三个通道随机出现的255与0,如下图所示。

    中值滤波执行过程:
    (1) 按强度值大小排列像素点.
    (2) 选择排序像素集的中间值作为点[i,j]的新值.

    image.png

    一般采用奇数点的邻域来计算中值,但如果像素点数为偶数时,中值就取排序像素中间两点的平均值。

    3.2 中值滤波与均值滤波器比较

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

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

    3.2 函数讲解

    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

    3.3 代码

    //-----------------------------------【程序说明】----------------------------------------------
    //            说明:【中值滤波medianBlur函数的使用示例程序】
    //            开发所用OpenCV版本:2.4.8
    //            2014年4月3 日 Create by 浅墨
    //------------------------------------------------------------------------------------------------
     
    //-----------------------------------【头文件包含部分】---------------------------------------
    //     描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include "opencv2/core/core.hpp"
    #include"opencv2/highgui/highgui.hpp"
    #include"opencv2/imgproc/imgproc.hpp"
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //     描述:包含程序所使用的命名空间
    //----------------------------------------------------------------------------------------------- 
    using namespace cv;
     
    //-----------------------------------【main( )函数】--------------------------------------------
    //     描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原图
           Mat image=imread("1.jpg");
     
           //创建窗口
           namedWindow("中值滤波【原图】" );
           namedWindow("中值滤波【效果图】");
     
           //显示原图
           imshow("中值滤波【原图】", image );
     
           //进行中值滤波操作
           Mat out;
           medianBlur( image, out, 7);
     
           //显示效果图
           imshow("中值滤波【效果图】" ,out );
     
           waitKey(0 );    
    }
    
    核心代码
     medianBlur( image, out, 7);
    

    五、 双边滤波(Bilateral filter)

    5.1 原理

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

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


    双边滤波核示意图 高斯核/定义域核 值域核

    值域核的就是的双边滤波与高斯滤波的差别,值域核使得边缘信息得保留,只有满足特点值域范围内,才会进行的高斯模糊。

    原理还需要补充

    Bilateral Filters(双边滤波算法)原理及实现 - 梅子黄时雨 - CSDN博客 https://blog.csdn.net/piaoxuezhong/article/details/78302920

    5.2 函数讲解

    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。

    5.3 实例

    //-----------------------------------【程序说明】----------------------------------------------
    //            说明:【双边滤波bilateralFilter函数的使用示例程序】
    //            开发所用OpenCV版本:2.4.8
    //            2014年4月3 日 Create by 浅墨
    //------------------------------------------------------------------------------------------------
     
    //-----------------------------------【头文件包含部分】---------------------------------------
    //     描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include "opencv2/core/core.hpp"
    #include"opencv2/highgui/highgui.hpp"
    #include"opencv2/imgproc/imgproc.hpp"
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //     描述:包含程序所使用的命名空间
    //----------------------------------------------------------------------------------------------- 
    using namespace cv;
     
    //-----------------------------------【main( )函数】--------------------------------------------
    //     描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原图
           Mat image=imread("1.jpg");
     
           //创建窗口
           namedWindow("双边滤波【原图】" );
           namedWindow("双边滤波【效果图】");
           namedWindow("高斯滤波【效果图】");
           //显示原图
           imshow("双边滤波【原图】", image );
     
           //进行双边滤波操作
           Mat out;
           bilateralFilter( image, out, 25, 50, 25/2 );
           Mat outgaosi;
           GaussianBlur( image, outgaosi,  Size(25, 25), 25/2, 25/2 );
           //显示效果图
           imshow("双边滤波【效果图】" ,out );
           imshow("高斯滤波【效果图】" ,out );
           waitKey(0 );    
    }
    
    
    核心代码:
           bilateralFilter( image, out, 25, 25*2, 25/2 );
    
    

    【OpenCV入门教程之八】线性邻域滤波专场:方框滤波、均值滤波与高斯滤波 - 【浅墨的游戏编程Blog】毛星云(浅墨)的专栏 - CSDN博客 https://blog.csdn.net/poem_qianmo/article/details/22745559

    【OpenCV入门教程之九】 非线性滤波专场:中值滤波、双边滤波 - 【浅墨的游戏编程Blog】毛星云(浅墨)的专栏 - CSDN博客 https://blog.csdn.net/poem_qianmo/article/details/23184547

    opencv各类滤波器详解 - 当你努力到把自己都感动的时候,你离成功就不远了。 - CSDN博客 https://blog.csdn.net/xuhang0910/article/details/47087939

    opencv几种常见滤波器使用方法_百度文库 https://wenku.baidu.com/view/f3f49ac5f5335a8103d2207b

    写个程序体会各种滤波器效果
    https://blog.csdn.net/feitian944/article/details/80287265

    滤波器的使用
    https://blog.csdn.net/xingchenbingbuyu/article/details/79170764

    相关文章

      网友评论

        本文标题:三 (3.2 imgproc) 平滑处理 - 滤波器

        本文链接:https://www.haomeiwen.com/subject/kefftqtx.html