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

双边滤波器之所以能够做到在平滑去噪的同时还能够很好的保存边缘(Edge Preserve),是由于其滤波器的核由两个函数生成:

  • 一个函数由像素欧式距离决定滤波器模板的系数

  • 另一个函数由像素的灰度差值决定滤波器的系数

其综合了高斯滤波器(Gaussian Filter)和α" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">αα-截尾均值滤波器(Alpha-Trimmed mean Filter)的特点。高斯滤波器只考虑像素间的欧式距离,其使用的模板系数随着和窗口中心的距离增大而减小;Alpha截尾均值滤波器则只考虑了像素灰度值之间的差值,去掉α%" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">α%α%的最小值和最大值后再计算均值。

双边滤波器使用二维高斯函数生成距离模板,使用一维高斯函数生成值域模板。
距离模板系数的生成公式如下:

其中,(k,l)" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">(k,l)(k,l)为模板窗口的中心坐标;(i,j)" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">(i,j)(i,j)为模板窗口的其他系数的坐标;σd" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">σdσd为高斯函数的标准差。使用该公式生成的滤波器模板和高斯滤波器使用的模板是没有区别的。

值域模板系数的生成公式如下:

其中,函数f(x,y)" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">f(x,y)f(x,y)表示要处理的图像,f(x,y)" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">f(x,y)f(x,y)表示图像在点(x,y)" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">(x,y)(x,y)处的像素值;(k,l)" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">(k,l)(k,l)为模板窗口的中心坐标;(i,j)" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">(i,j)(i,j)为模板窗口的其他系数的坐标;σr" role="presentation" style="font-size: 14px; display: inline; line-height: normal; word-spacing: normal; overflow-wrap: normal; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border-width: 0px; border-style: initial; border-color: initial;">σrσr为高斯函数的标准差。

将上述两个模板相乘就得到了双边滤波器的模板

实现(参考OpenCV源代码)

这里的实现主要参考OpenCV中的bilateralFilter实现,其实现主要有两个优化:

  • 使用查表的方式计算灰度值模板系数

  • 将二维的模板转换为一维,降低算法复杂度。

在滤波之前,首先将灰度值模板系数计算出来。

double color_coeff = -0.5 / (color_sigma * color_sigma);vector<double> _color_weight(channels * 256); // 存放差值的平方double *color_weight = &_color_weight[0];for (int i = 0; i < channels * 256; i++)        color_weight[i] = exp(i * i * color_coeff);

灰度值的模板系数计算公式参见上面的公式,是两个灰度值的差值的平方。这里表的长度是channels * 256没有想通,应该255的长度就足够了。在使用的时候,首先取出模板中心的灰度值val0,然后依次取出模板其他位置的灰度值val,使用abs(val - val0)的差值从color_weight查表得到灰度值模板的系数。

距离的模板是二维的,这里使用的方法就i比较巧妙,将其化为了一维。

vector<double> _space_weight(ksize * ksize); // 空间模板系数vector<int> _space_ofs(ksize * ksize); // 模板窗口的坐标// 生成空间模板    int maxk = 0;    for (int i = -radius; i <= radius; i++)    {        for (int j = -radius; j <= radius; j++)        {            double r = sqrt(i*i + j * j);            if (r > radius)                continue;            space_weight[maxk] = exp(r * r * space_coeff); // 存放模板系数            space_ofs[maxk++] = i * temp.step + j * channels; // 存放模板的位置,和模板系数相对应        }    }

使用一维数组存放空间模板系数,同时使用另一个一维数组存放模板位置,和系数相对应。
整个代码的实现如下:

void myBilateralFilter(const Mat &src, Mat &dst, int ksize, double space_sigma, double color_sigma){    int channels = src.channels();    CV_Assert(channels == 1 || channels == 3);    double space_coeff = -0.5 / (space_sigma * space_sigma);    double color_coeff = -0.5 / (color_sigma * color_sigma);    int radius = ksize / 2;    Mat temp;    copyMakeBorder(src, temp, radius, radius, radius, radius, BorderTypes::BORDER_REFLECT);    vector<double> _color_weight(channels * 256); // 存放差值的平方    vector<double> _space_weight(ksize * ksize); // 空间模板系数    vector<int> _space_ofs(ksize * ksize); // 模板窗口的坐标    double *color_weight = &_color_weight[0];    double *space_weight = &_space_weight[0];    int    *space_ofs = &_space_ofs[0];    for (int i = 0; i < channels * 256; i++)        color_weight[i] = exp(i * i * color_coeff);    // 生成空间模板    int maxk = 0;    for (int i = -radius; i <= radius; i++)    {        for (int j = -radius; j <= radius; j++)        {            double r = sqrt(i*i + j * j);            if (r > radius)                continue;            space_weight[maxk] = exp(r * r * space_coeff); // 存放模板系数            space_ofs[maxk++] = i * temp.step + j * channels; // 存放模板的位置,和模板系数相对应        }    }    // 滤波过程    for (int i = 0; i < src.rows; i++)    {        const uchar *sptr = temp.data + (i + radius) * temp.step + radius * channels;        uchar *dptr = dst.data + i * dst.step;        if (channels == 1)        {            for (int j = 0; j < src.cols; j++)            {                double sum = 0, wsum = 0;                int val0 = sptr[j]; // 模板中心位置的像素                for (int k = 0; k < maxk; k++)                {                    int val = sptr[j + space_ofs[k]];                    double w = space_weight[k] * color_weight[abs(val - val0)]; // 模板系数 = 空间系数 * 灰度值系数                    sum += val * w;                    wsum += w;                }                dptr[j] = (uchar)cvRound(sum / wsum);            }        }        else if (channels == 3)        {            for (int j = 0; j < src.cols * 3; j+=3)            {                double sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;                int b0 = sptr[j];                int g0 = sptr[j + 1];                int r0 = sptr[j + 2];                for (int k = 0; k < maxk; k++)                {                    const uchar *sptr_k = sptr + j + space_ofs[k];                    int b = sptr_k[0];                    int g = sptr_k[1];                    int r = sptr_k[2];                    double w = space_weight[k] * color_weight[abs(b - b0) + abs(g - g0) + abs(r - r0)];                    sum_b += b * w;                    sum_g += g * w;                    sum_r += r * w;                    wsum += w;                }                wsum = 1.0f / wsum;                b0 = cvRound(sum_b * wsum);                g0 = cvRound(sum_g * wsum);                r0 = cvRound(sum_r * wsum);                dptr[j] = (uchar)b0;                dptr[j + 1] = (uchar)g0;                dptr[j + 2] = (uchar)r0;            }        }    }}

需要注意图像像素值的获取,首先获取到每行的坐标指针

const uchar *sptr = temp.data + (i + radius) * temp.step + radius * channels;uchar *dptr = dst.data + i * dst.step;

在滤波循环中,从space_ofs中取出每个模板位置偏移地址

int val = sptr[j + space_ofs[k]];

这种实现方法,大大的降低滤波的时间复杂度。

结果对比:

实现的结果和OpenCV的实现相差无几。sigma = 80,模板大小为20

总结

双边滤波器,在平滑图像的同时,还能够很好的保护图像的边缘信息,例如上图中,图像的平滑效果非常明显了,但是头发的发丝还是很明显的。
双边滤波器的最重要参数仍然是标准差sigma,其值小于10时,平滑效果不是很明显。

能使曲线变平滑的一维滤波器_双边滤波器的原理及实现相关推荐

  1. 能使曲线变平滑的一维滤波器_音响电源滤波器有什么作用?有人说能提升音质!你信不信?...

    我们日常使用音响听音乐,讨论了很多关于喇叭.箱体.音源等影响音质的话题.今天我们来谈谈音响电源滤波器,可能有些人还很陌生,它到底有什么作用?其实,音响电源滤波器在音响系统里面发挥着非常重要的角色. 现 ...

  2. 抽取_内插_半带滤波器_多相滤波器

    文章目录 半带滤波器 多相抽取滤波器 多相内插滤波器 半带抽取器和半带内插器 参考资料:Xilinx FIR Compiler v7.2 LogiCORE IP Product Guide PG149 ...

  3. Python机器视觉--OpenCV进阶(核心)--常用低通滤波器(方盒滤波器,均值滤波器,高斯滤波器,中值滤波器,双边滤波器)

    1.常用低通滤波器介绍 1.1低通滤波器与高通滤波器的区别: 1.低通滤波主要用于噪点的消除或者是用于图像的降噪 2.高通滤波器主要于图像边缘的寻找 1.2 方盒滤波 boxFilter(src, d ...

  4. 并行加速实战 双边滤波器

    之前分析了 二维中值滤波器的并行加速 由于二维中值滤波器是控制密集型的滤波器(排序操作),所以SSE加速不太明显 这次选用了计算密集型的双边滤波器 针对双边滤波器在5*5的滤波核下的运算速度做优化和分 ...

  5. python函一维聚类_聚类实战:一维数组数据聚类

    大部分聚类方法针对的是多维数据,现实场景中还有可能存在以为数据的情况,针对以为数组的聚类和多维的数据有很大的不同,今天就来实战演练下: 需求内容:分析订单的价格分布 常见方案:按照100为梯度,分析不 ...

  6. 设计一个三阶巴特沃斯滤波器_巴特沃斯滤波器matlab实现

    巴特沃斯滤波器的特点是通频带内的频率响应曲线最大限度平坦,没有起伏,而在阻频带则逐渐下降为零. 在振幅的对数对角频率的波特图上,从某一边界角频率开始,振幅随着角频率的增加而逐步减少,趋向负无穷大. 一 ...

  7. Udacity机器人软件工程师课程笔记(三十二) - 卡尔曼滤波器 - 一维卡尔曼滤波器 - 多维卡尔曼滤波器 - 拓展卡尔曼滤波器(EKF)

    卡尔曼滤波器 一.概述 二.一维高斯分布 均值和方差 三.一维卡尔曼滤波器 变量命名约定 卡尔曼滤波循环 1.测量值更新 (1)平均值计算 (2)程序实现 2.位置预测 位置预测公式 3.一维卡尔曼滤 ...

  8. Java平滑处理什么意思_几种平滑处理方法

    平滑,也可叫滤波,或者合在一起叫平滑滤波,平滑滤波是低频增强的空间域滤波技术.它的目的有两类:一类是模糊:另一类是消除噪音.空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值.邻域的 ...

  9. Matlab对含噪声图像的滤波操作_两种噪声_三种滤波器_两种方法

    注释很重要 Matlab对含噪声图像的滤波操作. 噪声: 高斯噪声(正态分布) 均匀噪声 用到的滤波器: 高斯滤波器 盒型滤波器 中值滤波器 用到的两种方法: 直接conv2 fft2 %%C1 fi ...

最新文章

  1. GitLab安装说明
  2. 阿里AI labs发布两大天猫精灵新品,将与平头哥共同定制智能语音芯片
  3. 【大数据-Hadoop】Hadoop架构
  4. CodeForces - 1547G How Many Paths?(强联通缩点+拓扑)
  5. 35个非主流开源数据库
  6. 关于谷歌云,你应该知道的一切! | 技术头条
  7. 阿里文娱技术专家战獒: 领域驱动设计详解之What, Why, How?
  8. DNN Mail功能介绍 [DNN 3.2.2]
  9. 源码安装yui compressor
  10. 携号转网移动用户转出最多;微软称 8 万台电脑感染病毒;TensorFlow 2.1 rc0 发布 | 极客头条...
  11. Kubernetes系列之理解K8s Service的几种模式
  12. 20151024-1025-威海-第5届全国高校软件工程专业教育年会参会总结
  13. 无向图java_Java实现无向图的建立与遍历
  14. 简易CPU的C++实现
  15. java getbytes_JAVA的getBytes()方法
  16. 深入理解JAVA中的JNDI注入
  17. html前端简单页面,web前端制作一个简单的登录页面
  18. 天创速盈:拼多多商家提升投产比有什么技巧?
  19. 3ds Max 2014的preRender脚本bug
  20. 视频文件太大如何压缩变小?

热门文章

  1. java ssm 项目案例,亲测可用的JAVA SSM项目
  2. r23中文测试软件,Maxon公布最新的CINEBENCH R23测试软件
  3. java中throws用法_java中throws实例用法详解
  4. photos怎么改成中文_Win10怎么设置中文语言?Win10设置语言为中文的方法图解
  5. ios 百度地图指定区域_ios百度地图的使用(普通定位、反地理编码)
  6. /usr/bin/ld: skipping incompatible /usr/lib/mysql/libmysqlclient.so when searching for -lmysqlclient
  7. Django学习手册 - ORM数据类型
  8. Spring4基础 学习笔记(5) ---- Spring与Dao
  9. .NET 4.5 Task异步编程学习资料
  10. SOE服务的地址构建注意点_大小写