研究双边滤波有很长一段时间了,最近看了一篇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. C#新手常犯的错误

    虽然这篇post的标题是新手常犯的错误,实际上很多有经验的程序员也经常犯这些错误,我整理了一下,就当是笔记.1.遍历List的错误,比如如下代码: List<String> strList ...

  2. iOS学习笔记——AutoLayout的约束

    iOS学习笔记——AutoLayout约束 之前在开发iOS app时一直以为苹果的布局是绝对布局,在IB中拖拉控件运行或者直接使用代码去调整控件都会发上一些不尽人意的结果,后来发现iOS在引入了Au ...

  3. python中global 和 nonlocal 的作用域

    python引用变量的顺序: 当前作用域局部变量->外层作用域变量->当前模块中的全局变量->python内置变量 . 一 global global关键字用来在函数或其他局部作用域 ...

  4. Linux-安装Oracle(CentOS-Oracle 12c)

    第一步:网络连接,在我的上一篇博客中有介绍,不再多说. 网络连接的目的:为了能使用yum命令,在网上直接下载文件. 第二步:前往oracle官网下载12c database服务器端的两个文件:(安装在 ...

  5. 【工匠大道】将项目同时托管到Github和Git@OSC

    原文地址 摘要: Github是最大的git代码托管平台,​GIT@OSC是国内最大的git代码托管平台,支持免费私有库,支持SVN操作,用户众多.很多用户需要同时将代码托管到两个平台,这篇文章的主要 ...

  6. 学习.NET是因为热爱 or 兴趣 or 挣钱?

    看到最近园子里掀起了“.NET快不行了”.“.NET工资太低了”.“转行做XX”等一系列之风,不由得想说点什么,我只是基于自己的观点,你认同或者不认同,我就是这样认为,无所谓对与错,写文章就是为了交流 ...

  7. iOS隐私政策

    隐私政策 本应用尊重并保护所有使用服务用户的个人隐私权.为了给您提供更准确.更有个性化的服务,本应用会按照本隐私权政策的规定使用和披露您的个人信息.但本应用将以高度的勤勉.审慎义务对待这些信息.除本隐 ...

  8. SharePoint项目实践中如何实现非打破继承看上去很美的权限控制

    首先对于比较机密的数据,打破继承还是必须要的. 但是对于一些普通申请单据,虽然客户也希望用户不要看到其他人的申请单据,但是我还是不推荐打破继承,应为打破继承一方面会造成网站的权限管理特别的凌乱,另一方 ...

  9. xp系统下硬盘安装centos6.5

    引言: 电脑系统是Windows XP,电脑没有光驱.手头没有U盘.没有移动硬盘.电脑主板不支持U盘启动,在这种情况下想安装CentOS 6.0,有木有办法? 答案:有办法,请看下面教程! 必备工具: ...

  10. Linux系统安装NoSQL(MongoDB和Redis)步骤及问题解决办法

    ➠更多技术干货请戳:听云博客 如下是我工作中的记录,介绍的是linux系统下NoSQL:MongoDB和Redis的安装过程和遇到的问题以及解决办法: 需要的朋友可以按照如下步骤进行安装,可以快速安装 ...