研究双边滤波有很长一段时间了,最近看了一篇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 一文的相关感受。的更多相关文章

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

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

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

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

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

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

  4. Computer Vision_33_SIFT:Fast Adaptive Bilateral Filtering——2018

    此部分是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面.对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献.有一些刚刚出版的 ...

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

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

  6. Bilateral Filtering(双边滤波) for SSAO

    原网址:http://blog.csdn.net/bugrunner/article/details/7170471 1. 简介 图像平滑是一个重要的操作,而且有多种成熟的算法.这里主要简单介绍一下B ...

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

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

  8. 【论文阅读】Retrieving Similar Similar Styles to Parse Clothing(相关工作)

    发表于2015年5月PAMI 作者: Kota Yamaguchi, M.Hadi Kiapour, Luis E. Ortiz, Tamara L. Berg 相关工作: [服装检索Clothing ...

  9. Bilateral Filter

    最近在看图像风格化的论文的时候,频繁遇到 Bilateral Filter.google 一波后,发现并不是什么不得了的东西,但它的思想却很有借鉴意义. 简介 Bilateral Filter,中文又 ...

随机推荐

  1. Devexpress WPF Theme Editor 02

    Devexpress WPF Theme Editor 01 对于上次我们生成的主题 开始添加到我们的项目中. 运行Visual Studio和打开一个WPF解决方案,你想申请的主题. 添加的主题集会 ...

  2. Java集合概述

    容器,是用来装东西的,在Java里,东西就是对象,而装对象并不是把真正的对象放进去,而是指保存对象的引用.要注意对象的引用和对象的关系,下面的例子说明了对象和对象引用的关系. String str = ...

  3. 2016 ICPC大连站---F题 Detachment

    题意:输入一个x,将x拆分成一些小的数(这些数不能相同,即x=a1+a2+......   ai!=aj when i!=j),然后这些数相乘得到一个成积(s=a1*a2*......),求最大的乘积 ...

  4. Linux tree命令

    Linux tree命令用于以树状图列出目录的内容. 执行tree指令,它会列出指定目录下的所有文件,包括子目录里的文件. 语法 tree [-aACdDfFgilnNpqstux][-I <范 ...

  5. [翻译]Apache Spark入门简介

    原文地址:http://blog.jobbole.com/?p=89446 我是在2013年底第一次听说Spark,当时我对Scala很感兴趣,而Spark就是使用Scala编写的.一段时间之后,我做 ...

  6. 移动端全兼容的flexbox速成班

    说起flexbox,都算是件陈年旧事了,它是2009年W3C提出的一种全新的可伸缩的CSS布局方式.依赖flexbox,我们可以更简单,高效的完成可伸缩式页面的布局. 业界与flexbox的相关教程文 ...

  7. kmdjs指令大全

    调试 通过下面方式,可以输出kmdjs声称的类: <script src="../dist/kmd.js?debug" data-main="js/main&quo ...

  8. React Native知识1-FlexBox 布局内容

    一:理论知识点 1:什么是FlexBox布局? 弹性盒模型(The Flexible Box Module),又叫Flexbox,意为“弹性布局”,旨在通过弹性的方式来对齐和分布容器中内容的空间,使其 ...

  9. iOS时间个性化设置设置

    现在在很多项目中,不会直接显示时间,很多时候都是显示“刚刚”,”XX分钟前”,等等字样,那么他们是怎么实现的呢 ? .新建一个NSDate的类目:NSDate+XMGExtension NSDate+ ...

  10. HTML5-02 元素

    概述 HTML 文档主要由元素组成,且主要分为两大部分:头部 和 主体.如图: 头部 概述 <head> 元素包含了所有的头部标签.<head> 元素中通常包含脚本(scrip ...