美文网首页嵌牛IT观察
Bilateral Filters(双边滤波算法)原理及实现(二

Bilateral Filters(双边滤波算法)原理及实现(二

作者: 东Rain | 来源:发表于2020-01-14 15:59 被阅读0次

姓名:张右润

学号:19021210648

转载自:http://www.pianshen.com/article/2047263046/

【嵌牛导读】双边滤波是一种非线性滤波器,它可以达到保持边缘、降噪平滑的效果。和其他滤波原理一样,双边滤波也是采用加权平均的方法,用周边像素亮度值的加权平均代表某个像素的强度,所用的加权平均基于高斯分布[1]。最重要的是,双边滤波的权重不仅考虑了像素的欧氏距离(如普通的高斯低通滤波,只考虑了位置对中心像素的影响),还考虑了像素范围域中的辐射差异(例如卷积核中像素与中心像素之间相似程度、颜色强度,深度距离等),在计算中心像素的时候同时考虑这两个权重。

【嵌牛鼻子】数字图像处理 图像滤波

【嵌牛提问】双边滤波如何用opencv实现?

【嵌牛正文】

双边滤波(Bilateral Filter)是非线性滤波中的一种。这是一种结合图像的空间邻近度与像素值相似度的处理办法。在滤波时,该滤波方法同时考虑空间临近信息与颜色相似信息,在滤除噪声、平滑图像的同时,又做到边缘保存。

双边滤波采用了两个高斯滤波的结合。一个负责计算空间邻近度的权值,也就是常用的高斯滤波器原理。而另一个负责计算像素值相似度的权值。在两个高斯滤波的同时作用下,就是双边滤波。

如果对于高斯滤波(空间邻近)原理不清晰的,建议先阅读这两篇文章

OpenCV高斯滤波器详解及代码实现

快速高斯滤波、高斯模糊、高斯平滑(二维卷积分步为一维卷积)

看到这里,博主认为你对高斯滤波(空间临近)已经理解并掌握了,那么我们开始对双边滤波进行讲解。

在上面两篇文章中提到,高斯滤波(空间临近)是将二维高斯正态分布放在图像矩阵上做卷积运算。考虑的是邻域内像素值的空间距离关系。通过在核大小范围内,各个点到中心点的空间临近度计算出对应的权值,并将计算好的核与图像矩阵作卷积。最后,图像经过滤波后达到平滑的效果,而图像上的边缘也会有一定程度的平滑,使得整个图像变得模糊,边缘得不到保存。

双边滤波的基本思想是:将高斯滤波(空间临近)的原理中,通过各个点到中心点的空间临近度计算的各个权值进行优化,将其优化为空间临近度计算的权值 和 像素值相似度计算的权值的乘积,优化后的权值再与图像作卷积运算。从而达到保边去噪的效果。

一、双边滤波的公式

g(i, j)代表输出点;

S(i, j)的是指以(i,j)为中心的(2N+1)(2N+1)的大小的范围;

f(k, l)代表(多个)输入点;

w(i, j, k, l)代表经过两个高斯函数计算出的值(这里还不是权值)

上述公式我们进行转化,假设公式中w(i,j,k,l)为m,则有

设 m1+m2+m3 … +mn = M,则有

此时可以看到,这明显是图像矩阵与核的卷积运算了。其中m1/M代表的第一个点(或最后一个点,看后面如何实现)的权值,而图像矩阵与核通过卷积算子作加权和,最终得到输出值。

接下来我们来讨论最关键的w(i, j, k, l)

ws为空间临近高斯函数,wr为像素值相似度高斯函数

可以看到,w是ws和wr的乘积。对于ws来说,这就是普通的高斯滤波函数,其代入的坐标,sigmas是程序输入值,该函数是在空间临近度上计算的。

而wr是计算像素值相似度(颜色空间),注意,这就是高斯函数代入坐标值,2sigmar^2的上方是范数,在这里的值为 |f(i,j)-f(k,l)|^2。也就是两个点像素值差值的绝对值的平方。其中,彩色图片计算差值时应将(i,j)点的RGB三通道值之和减去(k,l)点的RGB三通道值之和。这里是颜色空间计算,不能当成单通道,但是在最后矩阵卷积时,是单通道与权值相乘而不是三个通道之和。

二、双边滤波权值分布(图片来源于网络)

(1)当图像在变化程度平缓的区域时,邻域中的像素值(RGB值)差距相差不大。此时wr无限接近于1,因此此时的双边就是普通的高斯滤波,达到对图像平滑的效果。

(2)  当图像在变化程度剧烈的区域,比如在边缘区域时,邻域中的像素值(RGB值)差距相差很大。此时wr朝0值趋近,颜色差值越大,wr越逼近0,最终整个式子的值逼近于0。最终的结果是权值为0。因此在最终计算时,该处将不影响输出值。

通过此种方式,其既能平滑图像,又保持了图像的边缘。

三、双边滤波实现(图片来源于网络)

首先来看下双边滤波的效果

首先来看下双边滤波的效果

该函数有三个参数,滤波板(核)半径N、参数sigmas和sigmar.

(1) main函数实现

int main(void)

{

// [1] src读入图片

cv::Mat src = cv::imread("pic3.jpg");

// [2] dst目标图片

cv::Mat dst;

// [3] 滤波 N越大越平越模糊(2*N+1) sigmas空间越大越模糊sigmar相似因子

myBialteralFilter(&src, &dst, 25, 12.5, 50);

// [4] 窗体显示

cv::imshow("src 1006534767", src);

cv::imshow("dst 1006534767", dst);

cv::waitKey(0);

cv::destroyAllWindows();

return 0;

}

(2)双边滤波主函数,注意,现在的权值并没有进行归一化,归一化需要在计算时,根据第一大点中讲解的公式进行计算

/* 双边滤波函数 */

void myBialteralFilter(cv::Mat *src, cv::Mat *dst, int N, double sigmas, double sigmar)

{

// [1] 初始化

*dst = (*src).clone();

int _size = 2 * N + 1;

// [2] 分别计算空间权值和相似度权值

int channels = (*dst).channels();

double *_colorArray = NULL;

double **_spaceArray = NULL;

_colorArray = get_color_Array( _size, channels, sigmar);

_spaceArray = get_space_Array(_size, channels, sigmas);

// [3] 滤波

doBialteral(dst, N, _colorArray, _spaceArray);

return;

}

(3)高斯函数带入坐标(空间临近)

/* 计算空间权值 */

double **get_space_Array( int _size, int channels, double sigmas)

{

// [1] 空间权值

int i, j;

// [1-1] 初始化数组

double **_spaceArray = new double*[_size+1];  //多一行,最后一行的第一个数据放总值

for (i = 0; i < _size+1; i++) {

_spaceArray[i] = new double[_size+1];

}

// [1-2] 高斯分布计算

int center_i, center_j;

center_i = center_j = _size / 2;

_spaceArray[_size][0] = 0.0f;

// [1-3] 高斯函数

for (i = 0; i < _size; i++) {

for (j = 0; j < _size; j++) {

_spaceArray[i][j] =

exp(-(1.0f)* (((i - center_i)*(i - center_i) + (j - center_j)*(j - center_j)) /

(2.0f*sigmas*sigmas)));

_spaceArray[_size][0] += _spaceArray[i][j];

}

}

return _spaceArray;

}

(5)滤波

/* 双边 扫描计算 */

void doBialteral(cv::Mat *_src, int N, double *_colorArray, double **_spaceArray)

{

int _size = (2 * N + 1);

cv::Mat temp = (*_src).clone();

// [1] 扫描

for (int i = 0; i < (*_src).rows; i++) {

for (int j = 0; j < (*_src).cols; j++) {

// [2] 忽略边缘

if (i > (_size / 2) - 1 && j > (_size / 2) - 1 &&

i < (*_src).rows - (_size / 2) && j < (*_src).cols - (_size / 2)) {

// [3] 找到图像输入点,以输入点为中心与核中心对齐

//    核心为中心参考点 卷积算子=>高斯矩阵180度转向计算

//    x y 代表卷积核的权值坐标  i j 代表图像输入点坐标

//    卷积算子    (f*g)(i,j) = f(i-k,j-l)g(k,l)          f代表图像输入 g代表核

//    带入核参考点 (f*g)(i,j) = f(i-(k-ai), j-(l-aj))g(k,l)  ai,aj 核参考点

//    加权求和  注意:核的坐标以左上0,0起点

double sum[3] = { 0.0,0.0,0.0 };

int x, y, values;

double space_color_sum = 0.0f;

// 注意: 公式后面的点都在核大小的范围里

// 双边公式 g(ij) =  (f1*m1 + f2*m2 + ... + fn*mn) / (m1 + m2 + ... + mn)

                // space_color_sum = (m1 + m12 + ... + mn)

for (int k = 0; k < _size; k++) {

for (int l = 0; l < _size; l++) {

x = i - k + (_size / 2);  // 原图x  (x,y)是输入点

y = j - l + (_size / 2);  // 原图y  (i,j)是当前输出点

values = abs((*_src).at<cv::Vec3b>(i, j)[0] + (*_src).at<cv::Vec3b>(i, j)[1] + (*_src).at<cv::Vec3b>(i, j)[2]

- (*_src).at<cv::Vec3b>(x, y)[0] - (*_src).at<cv::Vec3b>(x, y)[1] - (*_src).at<cv::Vec3b>(x, y)[2]);

space_color_sum += (_colorArray[values] * _spaceArray[k][l]);

}

}

// 计算过程

for (int k = 0; k < _size; k++) {

for (int l = 0; l < _size; l++) {

x = i - k + (_size / 2);  // 原图x  (x,y)是输入点

y = j - l + (_size / 2);  // 原图y  (i,j)是当前输出点

values = abs((*_src).at<cv::Vec3b>(i, j)[0] + (*_src).at<cv::Vec3b>(i, j)[1] + (*_src).at<cv::Vec3b>(i, j)[2]

- (*_src).at<cv::Vec3b>(x, y)[0] - (*_src).at<cv::Vec3b>(x, y)[1] - (*_src).at<cv::Vec3b>(x, y)[2]);

for (int c = 0; c < 3; c++) {

sum[c] += ((*_src).at<cv::Vec3b>(x, y)[c]

* _colorArray[values]

* _spaceArray[k][l])

/ space_color_sum;

}

}

}

for (int c = 0; c < 3; c++) {

temp.at<cv::Vec3b>(i, j)[c] = sum[c];

}

}

}

}

// 放入原图

(*_src) = temp.clone();

return ;

}

相关文章

网友评论

    本文标题:Bilateral Filters(双边滤波算法)原理及实现(二

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