OpenCV 图像滤波:双边滤波算法

最近在看浅墨前辈的OpenCV教程来做一次复习,其中发现了一个挺有趣的之前没见过的算法,叫双边滤波算法。这个算法可以对图像进行平滑的同时尽量对高频信息进行保留(比如边缘和边角),而相对低频的区域则会被平滑。感觉浅墨一书中讲的还是有点不直观,这里给出我自己的一些理解。

直观理解

双边滤波算法本质是基于高斯滤波,目的是解决高斯滤波造成的边缘模糊。那么算法的做法就是想办法去“推断”出当前像素是否是边缘点或者接近边缘的点。

我们都知道,对图像进行空间域滤波的方法是使用一个结构元素(核)来对原图像进行卷积。比如说高斯核像是这样的:


13248131-35fab90a488203c5.png
高斯核

而这个结构元素就会对原图像进行卷积操作,从而得到一个新的图像,即输出图像。我们知道,这个结构元素是不会变的。但是!但是!但是!在双边滤波算法中就不是如此了。

为了使图像的边缘得到保留,就要根据当前被卷积像素的邻域进行观察,“推断”是否是边缘点和接近边缘的点。因此,结构元素就会改变,从而保留边缘点。

下面的一组图中,图a是原图像,图c是输出。而中间的图像是什么呢?显然,这是原图中根据某个点的邻域生成的,专属于这个点的结构元素!


13248131-5bfbbb87e7affed5.png
作者给出的一幅图片

可以看到,原图中显然有一个灰度的突变,这就表示是边缘。灰度值高的地方不应该和灰度低的区域进行混合,所以,图像中接近边缘的一个点就会生成图b这样的结构元素。那么这个接近边缘的点在哪里呢?大概就在我标出的这个区域。


13248131-659465f9147c5883.jpg
红色圆圈中的像素点大概会生成图b这样的结构元素

而生成这样的结构元素的方法,是将我们原本的高斯核,与一个能“推断”出是否在边缘点的结构元素相乘,如下图中间的结构元素


13248131-f919f1c6148d1ff3.png
下面就给出数学定义

数学定义

双边滤波器的输出像素依赖于当前被卷积像素的邻域。i和j是当前被卷积像素的坐标点,k和l是邻域像素的坐标点。


13248131-2c53758acbaadb2f.png
双边滤波公式

加权系数ω由定义域核和值域核决定,是它们的乘积。定义域核就是高斯核,不解释


13248131-4bb0f6ca1a04e152.png
定义域核

而值域核就是用于“推断”是否是边缘点的方法,公式如下


13248131-42d0fe1113f5dae7.png
值域核

可以看到,它取决于被卷积像素的灰度值和邻域像素的灰度值的差。我们知道,边缘会有较大的灰度变化,而这个公式就会使边缘和边缘另一边的区域生成比较小的权值,与被卷积像素的灰度值类似的区域会生成比较大的权值,就像之前图中的一个“断崖”。

13248131-7201e98b3fc19884.png
"断崖"

相乘就得到加权系数ω


13248131-6f71b1f0c7785585.png
加权系数ω

编程实现

这里使用滑动条来观察一下参数对输出的变化。

/********************************************************************
 * Created by 杨帮杰 on 1/25/2019
 * Right to use this code in any way you want without
 * warranty, support or any guarantee of it working
 * E-mail: [email protected]
 * Association: SCAU 华南农业大学
 ********************************************************************/

#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

#define IMG1 "/home/jacob/图片/77.jpg"

using namespace cv;
using namespace std;

int g_d = 15;
int g_sigmaColor = 20;
int g_sigmaSpace = 50;

Mat image1;
Mat image2;

void on_Trackbar(int, void*)
{
    bilateralFilter(image1, image2, g_d, g_sigmaColor, g_sigmaSpace);
    imshow("output", image2);
}

int main()
{
    image1= imread(IMG1);
    //resize(image1,image1,Size(image1.cols/2, image1.rows/2));

    if (!image1.data)
    {
        cout << "img1 没读到" <<endl;
        return 0;
    }

    image2 = Mat::zeros(image1.rows, image1.cols, image1.type());
    bilateralFilter(image1, image2, g_d, g_sigmaColor, g_sigmaSpace);

    namedWindow("output");

    createTrackbar("核直径","output", &g_d, 50, on_Trackbar);
    createTrackbar("颜色空间方差","output", &g_sigmaColor, 100, on_Trackbar);
    createTrackbar("坐标空间方差","output", &g_sigmaSpace, 100, on_Trackbar);

    imshow("input", image1);
    imshow("output", image2);

    waitKey();
    return 0;
}

13248131-26d769902d9b5fc8.png
输入
13248131-b412588f27f8e28c.png
输出

明显,粗糙的皮肤得到了平滑。这个算法对皮肤不好的妹子有奇效→_→!

References:
Bilateral Filtering for Gray and Color Images
OpenCV学习笔记(七)中值、双边滤波

猜你喜欢

转载自blog.csdn.net/weixin_33949359/article/details/87354989
今日推荐