原网址:http://blog.csdn.net/bugrunner/article/details/7170471

1. 简介

图像平滑是一个重要的操作,而且有多种成熟的算法。这里主要简单介绍一下Bilateral方法(双边滤波),这主要是由于前段时间做了SSAO,需要用bilateral blur 算法进行降噪。Bilateral blur相对于传统的高斯blur来说很重要的一个特性即可可以保持边缘(Edge Perseving),这个特点对于一些图像模糊来说很有用。一般的高斯模糊在进行采样时主要考虑了像素间的空间距离关系,但是却并没有考虑像素值之间的相似程度,因此这样我们得到的模糊结果通常是整张图片一团模糊。Bilateral blur的改进就在于在采样时不仅考虑像素在空间距离上的关系,同时加入了像素间的相似程度考虑,因而可以保持原始图像的大体分块进而保持边缘。在于游戏引擎的post blur算法中,bilateral blur常常被用到,比如对SSAO的降噪。

2. 原理

滤波算法中,目标点上的像素值通常是由其所在位置上的周围的一个小局部邻居像素的值所决定。在2D高斯滤波中的具体实现就是对周围的一定范围内的像素值分别赋以不同的高斯权重值,并在加权平均后得到当前点的最终结果。而这里的高斯权重因子是利用两个像素之间的空间距离(在图像中为2D)关系来生成。通过高斯分布的曲线可以发现,离目标像素越近的点对最终结果的贡献越大,反之则越小。其公式化的描述一般如下所述:

其中的c即为基于空间距离的高斯权重,而用来对结果进行单位化。

高斯滤波在低通滤波算法中有不错的表现,但是其却有另外一个问题,那就是只考虑了像素间的空间位置上的关系,因此滤波的结果会丢失边缘的信息。这里的边缘主要是指图像中主要的不同颜色区域(比如蓝色的天空,黑色的头发等),而Bilateral就是在Gaussian blur中加入了另外的一个权重分部来解决这一问题。Bilateral滤波中对于边缘的保持通过下述表达式来实现:

其中的s为基于像素间相似程度的高斯权重,同样用来对结果进行单位化。对两者进行结合即可以得到基于空间距离、相似程度综合考量的Bilateral滤波:

上式中的单位化分部综合了两种高斯权重于一起而得到,其中的cs计算可以详细描述如下:

且有

且有

上述给出的表达式均是在空间上的无限积分,而在像素化的图像中当然无法这么做,而且也没必要如此做,因而在使用前需要对其进行离散化。而且也不需要对于每个局部像素从整张图像上进行加权操作,距离超过一定程度的像素实际上对当前的目标像素影响很小,可以忽略的。限定局部子区域后的离散化公就可以简化为如下形式:

上述理论公式就构成了Bilateral滤波实现的基础。为了直观地了解高斯滤波与双边滤波的区别,我们可以从下列图示中看出依据。假设目标源图像为下述左右区域分明的带有噪声的图像(由程序自动生成),蓝色框的中心即为目标像素所在的位置,那么当前像素处所对应的高斯权重与双边权重因子3D可视化后的形状如后边两图所示:      

左图为原始的噪声图像;中间为高斯采样的权重;右图为Bilateral采样的权重。从图中可以看出Bilateral加入了相似程度分部以后可以将源图像左侧那些跟当前像素差值过大的点给滤去,这样就很好地保持了边缘。为了更加形象地观察两者间的区别,使用Matlab将该图在两种不同方式下的高度图3D绘制出来,如下:

  

上述三图从左到右依次为:双边滤波,原始图像,高斯滤波。从高度图中可以明显看出Bilateral和Gaussian两种方法的区别,前者较好地保持了边缘处的梯度,而在高斯滤波中,由于其在边缘处的变化是线性的,因而就使用连累的梯度呈现出渐变的状态,而这表现在图像中的话就是边界的丢失(图像的示例可见于后述)。

3. 代码实现

有了上述理论以后实现Bilateral Filter就比较简单了,其实它也与普通的Gaussian Blur没有太大的区别。这里主要包括3部分的操作: 基于空间距离的权重因子生成;基于相似度的权重因子的生成;最终filter颜色的计算。

3.1 Spatial Weight

这就是通常的Gaussian Blur中使用的计算高斯权重的方法,其主要通过两个pixel之间的距离并使用如下公式计算而来:

其中的就表示两个像素间的距离,比如当前像素与其右边紧邻的一个像素之间的距离我们就可以用来计算,也即两个二维向量{0 , 0}以及{0 , 1}之间的欧氏距离。直接计算一个区域上的高斯权重并单位化后就可以进行高斯模糊了。

3.2 Similarity Weight

与基于距离的高斯权重计算类似,只不过此处不再根据两个pixel之间的空间距离,而是根据其相似程度(或者两个pixel的值之间的距离)。

其中的表示两个像素值之间的距离,可以直接使用其灰度值之间的差值或者RGB向量之间的欧氏距离。

3.3 Color Filtering

有了上述两部分所必需的权重因子之后,那么具体的双边滤波的实现即与普通的高斯滤波无异。主要部分代码如下述:

  1. UCHAR3 BBColor(int posX , int posY)
  2. {
  3. int centerItemIndex = posY * picWidth4 + posX * 3 , neighbourItemIndex;
  4. int weightIndex;
  5. double gsAccumWeight = 0;
  6. double accumColor = 0;
  7. // 计算各个采样点处的Gaussian权重,包括closeness,similarity
  8. for(int i = -number ; i <= number ; ++i)
  9. {
  10. for(int j = -number ; j <= number ; ++j)
  11. {
  12. weightIndex = (i + number) * (number * 2 + 1) + (j + number);
  13. neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +
  14. min(noiseImageWidth - 1  , max(0 , posX + i * radius)) * 3;
  15. pCSWeight[weightIndex] = LookupGSWeightTable(pSrcDataBuffer[neighbourItemIndex] , pSrcDataBuffer[centerItemIndex]);
  16. pCSWeight[weightIndex] = pGSWeight[weightIndex] * pGCWeight[weightIndex];
  17. gsAccumWeight += pCSWeight[weightIndex];
  18. }
  19. }
  20. // 单位化权重因子
  21. gsAccumWeight = 1 / gsAccumWeight;
  22. for(int i = -number ; i <= number ; ++i)
  23. {
  24. for(int j = -number ; j <= number ; ++j)
  25. {
  26. weightIndex = (i + number) * (number * 2 + 1) + (j + number);
  27. pCSWeight[weightIndex] *= gsAccumWeight;
  28. }
  29. }
  30. // 计算最终的颜色并返回
  31. for(int i = -number ; i <= number ; ++i)
  32. {
  33. for(int j = -number ; j <= number ; ++j)
  34. {
  35. weightIndex = (i + number) * (number * 2 + 1) + (j + number);
  36. neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +
  37. min(noiseImageWidth - 1  , max(0 , posX + i * radius)) * 3;
  38. accumColor += pSrcDataBuffer[neighbourItemIndex + 0] * pCSWeight[weightIndex];
  39. }
  40. }
  41. return UCHAR3(accumColor , accumColor , accumColor);
  42. }
UCHAR3 BBColor(int posX , int posY)
{
int centerItemIndex = posY * picWidth4 + posX * 3 , neighbourItemIndex;
int weightIndex;
double gsAccumWeight = 0;
double accumColor = 0; // 计算各个采样点处的Gaussian权重,包括closeness,similarity
for(int i = -number ; i <= number ; ++i)
{
for(int j = -number ; j <= number ; ++j)
{
weightIndex = (i + number) * (number * 2 + 1) + (j + number);
neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +
min(noiseImageWidth - 1 , max(0 , posX + i * radius)) * 3; pCSWeight[weightIndex] = LookupGSWeightTable(pSrcDataBuffer[neighbourItemIndex] , pSrcDataBuffer[centerItemIndex]);
pCSWeight[weightIndex] = pGSWeight[weightIndex] * pGCWeight[weightIndex];
gsAccumWeight += pCSWeight[weightIndex];
}
} // 单位化权重因子
gsAccumWeight = 1 / gsAccumWeight;
for(int i = -number ; i <= number ; ++i)
{
for(int j = -number ; j <= number ; ++j)
{
weightIndex = (i + number) * (number * 2 + 1) + (j + number);
pCSWeight[weightIndex] *= gsAccumWeight;
}
} // 计算最终的颜色并返回
for(int i = -number ; i <= number ; ++i)
{
for(int j = -number ; j <= number ; ++j)
{
weightIndex = (i + number) * (number * 2 + 1) + (j + number);
neighbourItemIndex = min(noiseImageHeight - 1 , max(0 , posY + j * radius)) * picWidth4 +
min(noiseImageWidth - 1 , max(0 , posX + i * radius)) * 3;
accumColor += pSrcDataBuffer[neighbourItemIndex + 0] * pCSWeight[weightIndex];
}
} return UCHAR3(accumColor , accumColor , accumColor);
}

其中的相似度分部的权重s主要根据两个Pixel之间的颜色差值计算面来。对于灰度图而言,这个差值的范围是可以预知的,即[-255, 255],因而为了提高计算的效率我们可以将该部分权重因子预计算生成并存表,在使用时快速查询即可。使用上述实现的算法对几张带有噪声的图像进行滤波后的结果如下所示:

  

  

上图从左到右分别为:双边滤波;原始图像;高斯滤波。从图片中可以较为明显地看出两种算法的区别,最直观的感受差别就是使用高斯算法后整张图片都是一团模糊的状态;而双边滤波则可以较好地保持原始图像中的区域信息,看起来仍然嘴是嘴、眼是眼(特别是在第一张美女图像上的效果!看来PS是灰常重要啊~~^o^)。

4. 在SSAO中的使用

在上述实现中的边缘判定函数主要是通过两个像素值之间的差异来决定,这也是我们观察普通图片的一种普遍感知方式。当然,也可以根据使用的需求情况来使用其它的方式判断其它定义下的边缘,比如使用场景的depth或是normal。比如在对SSAO进行滤波时可以直接使用Depth值来行边缘判断。首先,设置一个深度的阈值,在作边缘检测时比较两点间的depth差值,如果差值大于阈值,则认为其属于不同的区域,则此处就应为边界。使用此方法得到的效果可见于下图所示:

高斯滤波

双边滤波

在得到滤波之后的SSAO图像之后,与原始图像进行直接的整合就可以得到最终的渲染效果,如下图所示:

SSAO关闭

SSAO开启

后记: 崭新的2012年自己以一篇博文开始,感觉也不错,加油~!~!


 

8

Bilateral Filtering(双边滤波) for SSAO的更多相关文章

  1. Bilateral Filtering(双边滤波) for SSAO(转)

    原文链接:http://blog.csdn.net/bugrunner/article/details/7170471 另外一篇相似的英文资料:http://homepages.inf.ed.ac.u ...

  2. opencv-12-高斯滤波-双边滤波(附C++代码实现)

    开始之前 这几天由于自己的原因没有写, 一个是因为自己懒了, 一个是感觉这里遇到点问题不想往下写了, 我们先努力结束这个章节吧, 之前介绍了比较常用而且比较好理解的均值和中值滤波, 但是呢,在例程Sm ...

  3. 灰度图像--图像增强 双边滤波 Bilateral Filtering

    学习DIP第31天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不 ...

  4. 学习《Hardware-Efficient Bilateral Filtering for Stereo Matching》一文笔记。

    个人收藏了很多香港大学.香港科技大学以及香港中文大学里专门搞图像研究一些博士的个人网站,一般会不定期的浏览他们的作品,最近在看杨庆雄的网点时,发现他又写了一篇双边滤波的文章,并且配有源代码,于是下载下 ...

  5. 基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。

    一.引言 本人初次接触HDR方面的知识,有描述不正确的地方烦请见谅. 为方便文章描述,引用部分百度中的文章对HDR图像进行简单的描述. 高动态范围图像(High-Dynamic Range,简称HDR ...

  6. 阅读Real-Time O(1) Bilateral Filtering 一文的相关感受。

    研究双边滤波有很长一段时间了,最近看了一篇Real-Time O(1) Bilateral Filtering的论文,标题很吸引人,就研读了一番,经过几天的攻读,基本已理解其思想,现将这一过程做一简单 ...

  7. 快速双边滤波 附完整C代码

    很早之前写过<双边滤波算法的简易实现bilateralFilter>. 当时学习参考的代码来自cuda的样例. 相关代码可以参阅: https://github.com/johng12/c ...

  8. Tone Mapping算法系列一:基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。

    一.引言 本人初次接触HDR方面的知识,有描述不正确的地方烦请见谅. 为方便文章描述,引用部分百度中的文章对HDR图像进行简单的描述. 高动态范围图像(High-Dynamic Range,简称HDR ...

  9. 【OpenCV】邻域滤波:方框、高斯、中值、双边滤波

    原文:http://blog.csdn.net/xiaowei_cqu/article/details/7785365 邻域滤波(卷积)   邻域算子值利用给定像素周围像素的值决定此像素的最终输出.如 ...

随机推荐

  1. Python中的collections模块

    Python中内置了4种数据类型,包括:list,tuple,set,dict,这些数据类型都有其各自的特点,但是这些特点(比如dict无序)在一定程度上对数据类型的使用产生了约束,在某些使用场景下效 ...

  2. 【Python爬虫学习笔记(1)】urllib2库相关知识点总结

    1. urllib2的opener和handler概念 1.1 Openers: 当你获取一个URL你使用一个opener(一个urllib2.OpenerDirector的实例).正常情况下,我们使 ...

  3. 求整数A和B的二进制表示中有多少位是不同?

    如果有题目要求整数A和B二进制表示中多少位是不同的? 那我们要先考虑一个unsigned类型中变量1的个数?我们可以考虑简单的移位运算,向右移位,我们进行判断如果不是1直接丢掉,使用&运算符即 ...

  4. Flask第二篇——服务器相关

    web服务器.应用服务器和web应用框架 web服务器:负责处理http请求.响应静态文件,常见的有Apache,Nginx以及微软的IIS 应用服务器:负责处理逻辑的服务器.比如php.python ...

  5. 转发 Java火焰图在Netflix的实践

    为了分析不同软件或软件的不同版本使用CPU的情况,相关设计人员通常需要进行函数的堆栈性能分析.相比于定期采样获得数据的方式,利用定时中断来收集程序运行时的PC寄存器值.函数地址以及整个堆栈轨迹更加高效 ...

  6. 【转】linux中shell变量$#,$@,$0,$1,$2的含义解释

    原文网址:http://www.cnblogs.com/fhefh/archive/2011/04/15/2017613.html linux中shell变量$#,$@,$0,$1,$2的含义解释: ...

  7. Virsh中创建虚拟机两种方式define和create的区别

    本质上两者一样的,都是从xml配置文件创建虚拟机 define  丛xml配置文件创建主机但是不启动 create  同样是丛xml配置文件创建主机,但是可以指定很多选项,比如是否启动,是否连接控制台 ...

  8. gitlab安装、配置与阿里云产品集成

    https://www.ilanni.com/?p=12819 一.gitlab安装与部署 gitlab的安装可以分为源码安装和通过安装包进行安装,要是按照我以前的写作习惯的话,我也会把源码安装在本文 ...

  9. 执行npm install报错:npm ERR! code EINTEGRITY

    命令行执行npm install报错如下: D:\frontend\viewsdev>npm install npm ERR! code EINTEGRITY npm ERR! sha512-8 ...

  10. RK3288 USB触摸屏无法使用,需要添加PID和VID

    RK3288  Android5.1 现象:USB 接口触摸屏插到板子上,触摸屏无法使用,有可能出现更奇葩的,同一套代码,有的板子可以用,有的板子不能用. 1.打开串口调试,插上触摸屏,读取触摸屏的 ...