本文作者:小嗷

微信公众号:aoxiaoji

吹比QQ群:736854977

简书链接:https://www.jianshu.com/u/45da1fbce7d0


1.前言

上篇写道模拟噪声,这篇打算利用中值滤波、双边滤波消除噪声

2.简介及用途

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

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

举一个滤波在我们生活中的应用:美颜的磨皮功能。如果将我们脸上坑坑洼洼比作是噪声的话,那么滤波算法就是来取出这些噪声,使我们自拍的皮肤看起来很光滑。

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

本文你会找到以下问题的答案:

  1. 中值滤波
  2. 双边滤波
  3. exp的含义(以自然常数e为底的指数函数)
  4. 自定义中值滤波代码以及利用中值滤波去椒盐噪声

2.1 中值滤波

值滤波与均值滤波器比较,先来个 3 x 3 矩阵图:

2.3X3图

均值滤波器:就是9个数平均数

(14 + 15 + 16 + 24 + 25 +26 +34 + 35 +36)/9 = ???

即:小学数学求平均数。

在均值滤波器中,由于噪声成分被放入平均计算中,所以输出受到了噪声的影响。

比如:左边(黄色区域)被噪声影响成255,你求平均数是不是影响最终的平均值?

3.噪声

即:你家和当地富豪(富豪代表噪声)平均年收入,你们两家平均年收入1个亿。

缺点:均值滤波在去噪的同时破坏了图像的细节部分,也使得图像变得更加模糊。

中值滤波器

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

4.中值滤波

即:小学数学求9个数的中间值。

比如:100个人有3个是富豪(富豪代表噪声),求中间值,就可以避免和富豪的平均,取中间值起码比起平均,更靠谱。

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

简单介绍实际操作

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

中值滤波在一定条件下,可以克服线性滤波器(如均值滤波等)所带来的图像细节模糊,

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

2.2 双边滤波

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

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

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

5.双边滤波

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

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

6.双边滤波公式

exp,高等数学里以自然常数e为底的指数函数,全称Exponential(指数曲线)。

就是以e(自然对数2.718.)为底的幂函数
exp(x)=e^x 相当于e的x次方

定义域滤波对应图示:

7.双边滤波公式

值域核表示为:

8.双边滤波公式

值域滤波:

9.双边滤波公式

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

10.双边滤波公式

11.3API函数

3.1 medianBlur

Void medianBlur(InputArray src,OutputArray dst,int ksize)

第一个参数:输入图像

第二个参数:输出图像

第三个参数:孔径的尺寸,注意这个数是大于1的奇数

3.2 bilaralFilter

Void bilaralFilter(InputArray src,OutputArray dst,int d,double sigmaColor,double sigmaSpace,int borderType=BORDER_DEFAULT)

第一个参数:输入图像

第二个参数:输出图像

第三个参数:表示在过滤过程中每个像素邻域的直径。如果这个值被设为非正数,那么opencv会从第五个参数sigmaSpace来计算出她。

第四个参数:颜色空间滤波器的sigma值,这个参数的值越大,就表明该像素领域内有越宽广的颜色会被混到一起

第五个参数:坐标空间滤波器的sigma值,坐标空间的标注方差。它的数值越大,意味着越远的像素会相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色,当d>0时,d指定了尺寸,此参数没什么用,否则,d正比于这个参数值。

第六个参数:用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT

官方介绍:

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

中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,其原理是把数字图像或数字序列中的一点的值用该点的一个邻域中各点值的中值替代,让周围的像素值接近真实值,从而消除孤立的噪声点。

中值滤波在一定的条件下可以克服常见线性滤波器带来的图像细节模糊,而且对滤除脉冲干扰及图像扫描噪声非常有效,也用于保护边缘信息。

双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空域信息和灰度相似性,优点是可以做边缘保存。在边缘附近,离得较远的像素不会对边缘上的像素值影响太多,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能很好地滤掉,只能对低频噪声很好地滤波。

12.4代码与任务

任务:

  1. 自定义中值滤波函数和API的中值滤波函数利用椒盐噪声图作对比
  2. 利用双边滤波器处理椒盐噪声图

代码实现

#include "opencv2/imgproc.hpp"  
#include "opencv2/highgui.hpp"  
#include<ctime>  
using namespace cv;
using namespace std;

//求九个数的中值
//先排序,再取它第5个值
uchar Median(uchar n1, uchar n2, uchar n3, uchar n4, uchar n5,
    uchar n6, uchar n7, uchar n8, uchar n9) {
    uchar arr[9];
    arr[0] = n1;
    arr[1] = n2;
    arr[2] = n3;
    arr[3] = n4;
    arr[4] = n5;
    arr[5] = n6;
    arr[6] = n7;
    arr[7] = n8;
    arr[8] = n9;
    //增量gap,并逐步缩小增量
    for (int gap = 9 / 2; gap > 0; gap /= 2)//希尔排序  
        for (int i = gap; i < 9; ++i)
            // 从第gap个元素,逐个对其所在组进行直接插入排序操作
            for (int j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap)
                swap(arr[j], arr[j + gap]);
    return arr[4];//返回中值  
}

//中值滤波函数  
void MedianFlitering(const Mat &src, Mat &dst) {
    if (!src.data)return;
    Mat _dst(src.size(), src.type());
    for (int i = 0; i<src.rows; ++i)
        for (int j = 0; j < src.cols; ++j) {
            //边缘不进行处理  
            if ((i - 1) > 0 && (i + 1) < src.rows && (j - 1) > 0 && (j + 1) < src.cols) {
                //把9个值传进去,在利用希尔排序和直接插入排序操作,搞成从低到高的数组,再取它第4位数(0代表第一位数)
                _dst.at<Vec3b>(i, j)[0] = Median(src.at<Vec3b>(i, j)[0], src.at<Vec3b>(i + 1, j + 1)[0],
                    src.at<Vec3b>(i + 1, j)[0], src.at<Vec3b>(i, j + 1)[0], src.at<Vec3b>(i + 1, j - 1)[0],
                    src.at<Vec3b>(i - 1, j + 1)[0], src.at<Vec3b>(i - 1, j)[0], src.at<Vec3b>(i, j - 1)[0],
                    src.at<Vec3b>(i - 1, j - 1)[0]);
                _dst.at<Vec3b>(i, j)[1] = Median(src.at<Vec3b>(i, j)[1], src.at<Vec3b>(i + 1, j + 1)[1],
                    src.at<Vec3b>(i + 1, j)[1], src.at<Vec3b>(i, j + 1)[1], src.at<Vec3b>(i + 1, j - 1)[1],
                    src.at<Vec3b>(i - 1, j + 1)[1], src.at<Vec3b>(i - 1, j)[1], src.at<Vec3b>(i, j - 1)[1],
                    src.at<Vec3b>(i - 1, j - 1)[1]);
                _dst.at<Vec3b>(i, j)[2] = Median(src.at<Vec3b>(i, j)[2], src.at<Vec3b>(i + 1, j + 1)[2],
                    src.at<Vec3b>(i + 1, j)[2], src.at<Vec3b>(i, j + 1)[2], src.at<Vec3b>(i + 1, j - 1)[2],
                    src.at<Vec3b>(i - 1, j + 1)[2], src.at<Vec3b>(i - 1, j)[2], src.at<Vec3b>(i, j - 1)[2],
                    src.at<Vec3b>(i - 1, j - 1)[2]);
            }
            else
                _dst.at<Vec3b>(i, j) = src.at<Vec3b>(i, j);
        }
    //最终把处理后的_dst拷贝到dst
    _dst.copyTo(dst);
}
//图像椒盐化  
void salt(Mat &image, int num) {
    if (!image.data) return;//防止传入空图  
    int i, j;
    srand(time(NULL));
    for (int x = 0; x < num; ++x) {
        i = rand() % image.rows;
        j = rand() % image.cols;
        image.at<Vec3b>(i, j)[0] = 255;
        image.at<Vec3b>(i, j)[1] = 255;
        image.at<Vec3b>(i, j)[2] = 255;
    }
}
void main() {
    Mat image = imread("D://shenghua.jpg");
    imshow("原图", image);
    Mat Salt_Image;
    image.copyTo(Salt_Image);
    salt(Salt_Image, 3000);
    imshow("图像椒盐化", Salt_Image);
    Mat image3, image4;
    //自定义滤波
    MedianFlitering(Salt_Image, image3);
    //OPenCV自带的
    medianBlur(Salt_Image, image4, 3);
    //进行双边滤波操作  
    Mat out;
    bilateralFilter(Salt_Image, out, 25, 25 * 2, 25 / 2);
    imshow("双边滤波", out);
    imshow("自定义中值滤波处理后", image3);
    imshow("openCV自带的中值滤波", image4);
    waitKey();
}

效果图如下:

13.效果图.PNG

重点看看自带的中值滤波和自定义

13.效果图1.PNG

13.效果图2.PNG

因为不处理边缘所以自定义在边缘部分出现白点

为什么中值滤波可以有效处理的椒盐噪声(随机生成的白点或者黑点)?

其实很简单,我们把官富二代比作白色点或者黑色点(金字塔塔顶),9个人什么情况下,官富二代的软硬实力排到中间位置?

即:9个人当中有5个是官富二代(5个白点或黑点),又是有这种可能。但是,极少。毕竟白点是少数。所以,一般来说白点就被去除。

(吐槽一下:郭嘉就喜欢不去除白点,什么某某人均收入,它真把人命当傻子看)

14.png

  1. 本人是抱着玩一玩的心态,学习opencv(其实深度学习没有外界说的这么高深,小嗷是白板,而且有工作在身并且于代码无关)
  2. 大家可以把我的数学水平想象成初中水平,毕竟小嗷既不是代码靠吃饭又不是靠数学吃饭,毕业N年
  3. 写文章主要是为了后人少走点弯路,多交点朋友,一起学习
  4. 如果有好的图像识别群拉我进去QQ:631821577
  5. 就我一个白板,最后还是成的,你们别怕,慢慢来把

7.二维码

分享可以无数次,转载成自己文章QQ邮箱通知一下,未经授权请勿转载。

  • 邮箱:631821577@qq.com
  • QQ群:736854977
  • 有什么疑问公众号提问,下班或者周六日回答,ths

代码链接:

https://pan.baidu.com/s/1sU2dKQBNnl0J8dGixPvRJg

密码:j6sl

推荐文章:

关注公众号 -> 点击下方“文章分类” -> 编程

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐