阅读Real-Time O(1) Bilateral Filtering 一文的相关感受。
研究双边滤波有很长一段时间了,最近看了一篇Real-Time O(1) Bilateral Filtering的论文,标题很吸引人,就研读了一番,经过几天的攻读,基本已理解其思想,现将这一过程做一简单的小结。
论文大于10MB,无法上传至博客园,可以在这个链接下载:http://www.cs.cityu.edu.hk/~qiyang/publications/cvpr-09-qingxiong-yang.pdf。
首先,先给出一个我自己的结论:这篇文章无啥新意,主要的算法思想都来自于另外一篇论文,Fast Bilateral Filtering for the Display of High-Dynamic-Range Images,而且文中的部分实验结果我认为存在较大的水分,但是,其中提到的算法还是比较快的。
论文中对双边模糊的优化思路大概是这样的:
对于双边模糊,离散化后的表达式大概如下所示:
f(s)是空域核函数,f(r)是值域核函数, 难以直接优化上式的原因是 f(r)的存在。
论文中提出一种思路,如果上式中固定I(X)的值,则对于每一个不同的I(y)值,上式的分子就相当于对fR(I(x),I(y))*I(y)进行空域核卷积运算,分母则是对fR(I(x),I(y))进行空域核卷积元算,而这种卷积运算有着快速的算法。 这样,我们在图像的值域范围内选定若干有代表性的I(X)值,分别进行卷积,然后对于图像中的其他的像素值,进行线性插值得到。
算法的主要贡献也就在这里,而这个想法是从Fast Bilateral Filtering for the Display of High-Dynamic-Range Images一文中得到的,并且在此文中还提到了进行subsampleing进行进一步的优化,即这些抽样卷积可以在原图的小图中进行,然后最后的结果在原图中通过双线性插值获取。
关于直接采样然后插值的算法源代码可以参考:http://files.cnblogs.com/Imageshop/qx_constant_time_bilateral_filter.rar
下面为其主要的实现代码:
int qx_constant_time_bilateral_filter::bilateral_filter(unsigned char **image_filtered,unsigned char **image,double sigma_range,unsigned char **texture)
{
unsigned char image_min,image_max;
int y,x,jk_0,jk_1;
if(sigma_range>QX_DEF_THRESHOLD_ZERO)
{
m_sigma_range=sigma_range;
color_weighted_table_update(m_table,m_sigma_range*QX_DEF_CTBF_INTENSITY_RANGE,QX_DEF_CTBF_INTENSITY_RANGE);
}
qx_timer timer;
timer.start();
if(texture==NULL)
{
vec_min_val(image_min,image[],m_h*m_w);
vec_max_val(image_max,image[],m_h*m_w);
}
else
{
vec_min_val(image_min,texture[],m_h*m_w);
vec_max_val(image_max,texture[],m_h*m_w);
}
m_nr_scale=qx_max(,int(double(image_max-image_min)/(*m_sigma_range)+0.5));
//printf("[qx_max,qx_min]:[%5.5f,%5.5f]\n",(float)image_max,(float)image_min);
//printf("[sigma_range: %1.3f]\n",m_sigma_range);
//printf("[nr_scale: %d]\n",m_nr_scale);
m_grayscale[]=(double)image_min;
m_grayscale[m_nr_scale-]=(double)image_max;
double delta_scale=double(image_max-image_min)/(m_nr_scale-);
for(int i=;i<m_nr_scale-;i++) m_grayscale[i]=(double)image_min+delta_scale*i;
for(int i=;i<m_nr_scale;i++)
{
double **jk;
if(i==)
{
jk_0=;
jk_1=;
jk=m_jk[jk_0];
}
else
jk=m_jk[jk_1];
for(y=;y<m_h;y++)
{
for(x=;x<m_w;x++)
{
int index;
if(texture==NULL) index=int(abs(m_grayscale[i]-image[y][x])+0.5f);
else index=int(abs(m_grayscale[i]-texture[y][x])+0.5f); /*cross/joint bilateral filtering*/
jk[y][x]=m_table[index]*image[y][x];
m_wk[y][x]=m_table[index];
}
}
if(m_spatial_filter==QX_DEF_CTBF_BOX_BILATERAL_FILTER)
{
boxcar_sliding_window(jk,jk,m_box,m_h,m_w,m_radius);
boxcar_sliding_window(m_wk,m_wk,m_box,m_h,m_w,m_radius);
}
else if(m_spatial_filter==QX_DEF_CTBF_GAUSSIAN_BILATERAL_FILTER)
{
gaussian_recursive(jk,m_box,m_sigma_spatial*qx_min(m_h,m_w),,m_h,m_w);
gaussian_recursive(m_wk,m_box,m_sigma_spatial*qx_min(m_h,m_w),,m_h,m_w);
}
for(y=;y<m_h;y++)
{
for(x=;x<m_w;x++)
{
jk[y][x]/=m_wk[y][x];
}
}
//image_display(jk,m_h,m_w);
if(i>)
{
for(y=;y<m_h;y++)
{
for(x=;x<m_w;x++)
{
double kf;
if(texture==NULL) kf=double(image[y][x]-image_min)/delta_scale;
else kf=double(texture[y][x]-image_min)/delta_scale; /*cross/joint bilateral filtering*/
int k=int(kf);
if(k==(i-))
{
double alpha=(k+)-kf;
image_filtered[y][x]=(unsigned char)qx_min(qx_max(alpha*m_jk[jk_0][y][x]+(.f-alpha)*m_jk[jk_1][y][x],.f)+0.5f,.f);
}
else if(k==i&&i==(m_nr_scale-)) image_filtered[y][x]=(unsigned char)(m_jk[jk_1][y][x]+0.5f);
}
}
jk_1=jk_0;
jk_0=(jk_0+)%;
}
}
//timer.time_display("bilateral filter");
return();
}
我这里对其中的代码进行简单的描述:
1、第13、14行是获取图像的动态范围,即具有最大亮度和最小亮度的像素值。
2、 第22行的m_nr_scale是计算在原图中的取样数。26-29行中的m_grayscale是用来记录取样点的值的,比如如果动态范围是[0,255],取样数,5,则m_grayscale的值分别为0、64、128、192、255, 即对式(1)中的I(x)先固定为这5个值,计算式(1)的结果。
3、第32到第40行直接的这些代码其实是为了节省内存的,因为如果取样点为5,那么就需要5*2倍原图大小内存的空间来存储取样点的卷积值,但是如果我按取样点的大小顺序计算,那么每计算一个取样点后(第一个除外,这就是70行的判断),就可以把原图中夹子于这个取样点及这个取样点之前那个取样数据之间的像素的结果值通过两者之间的线性插值获取。这种方案就可以只需要2*2倍原图大小的内存。但是这种方案就涉及到插值的顺序,32到40就是处理这样的过程的,实际的写法你可以有很多种,上面的代码写的很烂的。
4、52到61之间的代码是看空域你是用什么类型的卷积函数,这里可以使用任意的其他的卷积函数,而至于的卷积函数也可以时任意的,这个可以参考代码中的color_weighted_table_update函数内的代码。
5、第72到87行的代码就是对其飞取样点的数据进行插值的过程,注意一些边缘的处理过程。
用插值+SubSampleing的代码可以从这里下载:http://files.cnblogs.com/Imageshop/qx_constant_time_bilateral_filter%28%E5%A2%9E%E5%BC%BA%E7%89%88%29.rar
试验结果(SigmaS=10,SigmaR=30,使用高斯卷积核函数):

原图 上述算法的结果 标准的结果
上述的取样数是按照第22行的m_nr_scale设置的,可见,视觉上似乎两者之间没有什么差别。
按照m_nr_scale的计算方式,如果SigmaR比较小,m_nr_scale值也会比较大,对于一些工程应用,往往SigmaR就是要取比较小的值才能保护住边缘。因此,我们尝试修改m_nr_scale的值,实际的测试表明,将m_nr_scale的值再该小一半,也能获得很为理想的效果,而速度确可以提高一倍。
另外,上述代码还应对m_nr_scale的最小值做个限制,m_nr_scale必须至少大于等于2的,否则无法插值的。
在速度上,使用这种方式加上一些其他的优化技巧,SigmaR=30(SigmaS对速度没有影响)时,一副640*480的彩色图像,在I3的笔记本上耗时约为75ms(值域使用均值模糊)、125ms(值域使用高斯函数)。
论文中提高的下采样技术进行速度的提升,我的看法看情况取舍。我自己也进行了编程,得出的结论是:
1、下采样的系数越小,结果和准确值偏差越大,并且此时因为下采样造成高斯滤波或者均值滤波的加快已经在整个耗时里占用的比例不大了,此时主要的矛盾是最后的双线性插值以及线性插值了,因此,总体时间上无明显提升。因此,我建议采样倍数不要超过3,即采样图的大小最小为原图的1/9。
2、为速度和效果综合考虑,可以采用下采样系数为2,这是双线程插值其实是求四个相邻像素的平均值,因此可以有较大的优化空间。
同样的640*480的图像,使用2*2下采样时约为40ms(均值模糊)以及55ms(高斯模糊);
在Real-Time O(1) Bilateral Filtering一文中有一下几段话:
As visible, our results are visually very similar to the exact even using very small number of PBFICs. To achieve acceptable PSNR value ( dB) for variance2
R ∈ , our method generally requires to PBFICs, and the running time is about 3.7ms to 15ms for MB image.
For a typical 1MB image, Porikli’s method runs at about second. Our GPU implementation runs at about frames per second using 8 PBFICs (Computation complexity of Recursive Gaussian filtering is about twice the box filtering)......
我对此速度表示严重怀疑,第一论文中说道他的算法占用内存数是大概4倍图像大小,那基本上就是采用上面代码类似的流程,这个流程有个严重的后果就是两个取样点的计算必须按大小的顺序进行,那这个并行就是个难题。另外,我们知道,8个PBFICs的过程就包括16个均值模糊或高斯模糊的过程(1MB大小的图像,就是1024*1024大小的灰度图),就凭这个过程在3.5或者15ms能执行完毕的机器或许还很少见吧。GPU有着能耐?抑或是作者使用的是超级计算机,不知道各位大神同意吗?
因此,论文的标题 Real - Time 是不是值得商榷呢?
相关工程参考:http://files.cnblogs.com/Imageshop/FastBilateralFilterTest.rar
阅读Real-Time O(1) Bilateral Filtering 一文的相关感受。的更多相关文章
- 学习《Hardware-Efficient Bilateral Filtering for Stereo Matching》一文笔记。
个人收藏了很多香港大学.香港科技大学以及香港中文大学里专门搞图像研究一些博士的个人网站,一般会不定期的浏览他们的作品,最近在看杨庆雄的网点时,发现他又写了一篇双边滤波的文章,并且配有源代码,于是下载下 ...
- 基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。
一.引言 本人初次接触HDR方面的知识,有描述不正确的地方烦请见谅. 为方便文章描述,引用部分百度中的文章对HDR图像进行简单的描述. 高动态范围图像(High-Dynamic Range,简称HDR ...
- Tone Mapping算法系列一:基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。
一.引言 本人初次接触HDR方面的知识,有描述不正确的地方烦请见谅. 为方便文章描述,引用部分百度中的文章对HDR图像进行简单的描述. 高动态范围图像(High-Dynamic Range,简称HDR ...
- Computer Vision_33_SIFT:Fast Adaptive Bilateral Filtering——2018
此部分是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面.对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献.有一些刚刚出版的 ...
- Bilateral Filtering(双边滤波) for SSAO(转)
原文链接:http://blog.csdn.net/bugrunner/article/details/7170471 另外一篇相似的英文资料:http://homepages.inf.ed.ac.u ...
- Bilateral Filtering(双边滤波) for SSAO
原网址:http://blog.csdn.net/bugrunner/article/details/7170471 1. 简介 图像平滑是一个重要的操作,而且有多种成熟的算法.这里主要简单介绍一下B ...
- 灰度图像--图像增强 双边滤波 Bilateral Filtering
学习DIP第31天 转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不 ...
- 【论文阅读】Retrieving Similar Similar Styles to Parse Clothing(相关工作)
发表于2015年5月PAMI 作者: Kota Yamaguchi, M.Hadi Kiapour, Luis E. Ortiz, Tamara L. Berg 相关工作: [服装检索Clothing ...
- Bilateral Filter
最近在看图像风格化的论文的时候,频繁遇到 Bilateral Filter.google 一波后,发现并不是什么不得了的东西,但它的思想却很有借鉴意义. 简介 Bilateral Filter,中文又 ...
随机推荐
- C#开发微信门户及应用(44)--微信H5页面开发的经验总结
在我们开发微信页面的时候,需要大量用到了各种呈现的效果,一般可以使用Boostrap的效果来设计不同的页面,不过微信团队也提供很多这方面的资源,包括JSSDK的接口,以及Weui的页面样式和相关功能页 ...
- 背水一战 Windows 10 (21) - 绑定: x:Bind 绑定, x:Bind 绑定之 x:Phase, 使用绑定过程中的一些技巧
[源码下载] 背水一战 Windows 10 (21) - 绑定: x:Bind 绑定, x:Bind 绑定之 x:Phase, 使用绑定过程中的一些技巧 作者:webabcd 介绍背水一战 Wind ...
- html节点属性操作
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- ubuntu 12.04 LTS 如何使用更快的更新源
装好ubuntu系统后的第一见事就是替换自带的更新源,原因是系统自带的源有些在中国访问不了,可以访问的速度又特别慢.幸好国内的一些公司和大学提供了速度不错的更新源.下面介绍如何使用更快的更新源 方法/ ...
- SQL注入—我是如何一步步攻破一家互联网公司的
最近在研究Web安全相关的知识,特别是SQL注入类的相关知识.接触了一些与SQL注入相关的工具.周末在家闲着无聊,想把平时学的东东结合起来攻击一下身边某个小伙伴去的公司,看看能不能得逞.不试不知道,一 ...
- [转载]C#使用Interlocked进行原子操作
原文链接:王旭博客 » C# 使用Interlocked进行原子操作 什么是原子操作? 原子(atom)本意是"不能被进一步分割的最小粒子",而原子操作(atomic operat ...
- JS高程3.基本概念(6)函数
1.ECMAScript中的函数使用function关键字来声明. eg: function sum (num1,num2){ alert(num1+num2); } sum(3,7); 注意: 在有 ...
- 转发:Chrome 控制台console的用法
大家都有用过各种类型的浏览器,每种浏览器都有自己的特色,本人拙见,在我用过的浏览器当中,我是最喜欢Chrome的,因为它对于调试脚本及前端设计调试都有它比其它浏览器有过之而无不及的地方.可能大家对co ...
- 设置statusBarStyle
设置状态栏的样式, typedef NS_ENUM(NSInteger, UIStatusBarStyle) { UIStatusBarStyleDefault ...
- AlertDialog的六种创建方式
AlertDialog的六种创建方式 创建AlertDialog的步骤: 1.创建AlertDialog.Builder对象 2.调用Builder对象的setTitle方法设置标题,setIcon方 ...