文档章节

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

abcijkxyz
 abcijkxyz
发布于 2016/11/22 16:39
字数 2335
阅读 10
收藏 0
点赞 0
评论 0

  研究双边滤波有很长一段时间了,最近看了一篇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

      下面为其主要的实现代码:

 1 int qx_constant_time_bilateral_filter::bilateral_filter(unsigned char **image_filtered,unsigned char **image,double sigma_range,unsigned char **texture)
 2 {
 3     unsigned char image_min,image_max; 
 4     int y,x,jk_0,jk_1;
 5     if(sigma_range>QX_DEF_THRESHOLD_ZERO) 
 6     {
 7         m_sigma_range=sigma_range;
 8         color_weighted_table_update(m_table,m_sigma_range*QX_DEF_CTBF_INTENSITY_RANGE,QX_DEF_CTBF_INTENSITY_RANGE);
 9     }
10     qx_timer timer;
11     timer.start();
12     if(texture==NULL)
13     {
14         vec_min_val(image_min,image[0],m_h*m_w);
15         vec_max_val(image_max,image[0],m_h*m_w);
16     }
17     else
18     {
19         vec_min_val(image_min,texture[0],m_h*m_w);
20         vec_max_val(image_max,texture[0],m_h*m_w);
21     }
22     m_nr_scale=qx_max(1,int(double(image_max-image_min)/(255*m_sigma_range)+0.5));
23     //printf("[qx_max,qx_min]:[%5.5f,%5.5f]\n",(float)image_max,(float)image_min);
24     //printf("[sigma_range: %1.3f]\n",m_sigma_range);
25     //printf("[nr_scale: %d]\n",m_nr_scale);
26     m_grayscale[0]=(double)image_min;
27     m_grayscale[m_nr_scale-1]=(double)image_max;
28     double delta_scale=double(image_max-image_min)/(m_nr_scale-1);
29     for(int i=1;i<m_nr_scale-1;i++) m_grayscale[i]=(double)image_min+delta_scale*i;
30     for(int i=0;i<m_nr_scale;i++)
31     {
32         double **jk;
33         if(i==0)
34         {
35             jk_0=0;
36             jk_1=1;
37             jk=m_jk[jk_0];
38         }
39         else 
40             jk=m_jk[jk_1];
41         for(y=0;y<m_h;y++)
42         {
43             for(x=0;x<m_w;x++)
44             {
45                 int index;
46                 if(texture==NULL) index=int(abs(m_grayscale[i]-image[y][x])+0.5f);
47                 else index=int(abs(m_grayscale[i]-texture[y][x])+0.5f); /*cross/joint bilateral filtering*/
48                 jk[y][x]=m_table[index]*image[y][x];
49                 m_wk[y][x]=m_table[index];
50             }
51         }
52         if(m_spatial_filter==QX_DEF_CTBF_BOX_BILATERAL_FILTER)
53         {
54             boxcar_sliding_window(jk,jk,m_box,m_h,m_w,m_radius);
55             boxcar_sliding_window(m_wk,m_wk,m_box,m_h,m_w,m_radius);
56         }
57         else if(m_spatial_filter==QX_DEF_CTBF_GAUSSIAN_BILATERAL_FILTER)
58         {
59             gaussian_recursive(jk,m_box,m_sigma_spatial*qx_min(m_h,m_w),0,m_h,m_w);
60             gaussian_recursive(m_wk,m_box,m_sigma_spatial*qx_min(m_h,m_w),0,m_h,m_w);
61         }
62         for(y=0;y<m_h;y++)
63         {
64             for(x=0;x<m_w;x++)
65             {
66                 jk[y][x]/=m_wk[y][x];
67             }
68         }
69         //image_display(jk,m_h,m_w);
70         if(i>0)
71         {
72             for(y=0;y<m_h;y++)
73             {
74                 for(x=0;x<m_w;x++)
75                 {
76                     double kf;
77                     if(texture==NULL) kf=double(image[y][x]-image_min)/delta_scale;
78                     else kf=double(texture[y][x]-image_min)/delta_scale; /*cross/joint bilateral filtering*/
79                     int k=int(kf); 
80                     if(k==(i-1))
81                     {
82                         double alpha=(k+1)-kf;
83                         image_filtered[y][x]=(unsigned char)qx_min(qx_max(alpha*m_jk[jk_0][y][x]+(1.f-alpha)*m_jk[jk_1][y][x],0.f)+0.5f,255.f);
84                     }
85                     else if(k==i&&i==(m_nr_scale-1)) image_filtered[y][x]=(unsigned char)(m_jk[jk_1][y][x]+0.5f);
86                 }
87             }
88             jk_1=jk_0;
89             jk_0=(jk_0+1)%2;
90         }
91     }
92     //timer.time_display("bilateral filter");
93     return(0);
94 }

   我这里对其中的代码进行简单的描述:

      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

    

 

 

 

 

   

 

 

 

本文转载自:http://www.cnblogs.com/Imageshop/p/3416527.html

共有 人打赏支持
abcijkxyz
粉丝 60
博文 6196
码字总数 1876
作品 0
深圳
项目经理
OpenCV 图像处理——平滑操作

关键函数: 关键函数: Smooth 各种方法的图像平滑 void cvSmooth( const CvArr src, CvArr dst, src 输入图像. dst 输出图像. smoothtype 平滑方法: CVBLURNO_SCALE (简单不带尺度变换的模糊...

晨曦之光
2012/05/23
1K
0
计算机视觉、机器学习相关领域论文和源代码大集合

一、特征提取Feature Extraction: · SIFT [1] [Demo program][SIFT Library] [VLFeat] · PCA-SIFT [2] [Project] · Affine-SIFT [3] [Project] · SURF [4] [OpenSURF] [Matlab Wrapper]......

wangdy
2016/08/02
213
0
linux文件系统系统调用实测的一个方法

通过几个C代码来检测一下文件系统相关系统调用时间,以及和标准I/O库函数的性能差异。主要以read和fread函数为例。 1.1.1 read系统调用开销 我们来模拟一下read系统调用的开销情况, 代码如...

binarydady
04/23
0
0
计算机视觉、机器学习相关领域论文和源代码大集合

注:下面有project网站的大部分都有paper和相应的code。Code一般是C/C++或者Matlab代码。 最近一次更新:2013-3-17 一、特征提取Feature Extraction: · SIFT [1] [Demo program][SIFT Lib...

moki_oschina
2015/01/15
0
0
11 Common Web Use Cases Solved In Redis

11 Common Web Use Cases Solved In Redis WEDNESDAY, JULY 6, 2011 AT 9:16AM In How to take advantage of Redis just adding it to your stack Salvatore 'antirez' Sanfilippo shows how......

平江夜弹
2015/12/11
5
0
一文读懂 AVL 树

背景 AVL 树是一棵平衡的二叉查找树,于 1962 年,G. M. Adelson-Velsky 和 E. M. Landis 在他们的论文《An algorithm for the organization of information》中发表。 所谓的平衡之意,就是...

超级数学建模
01/02
0
0
Nide 0.2 发布,Node.js 的集成开发环境

Nide 0.2 发布,该版本最大的新闻就是可跟一个原生的 Mac OS X 应用一样安装;其他包括版本管理、自动保存等等。 还有以下改进: Project Tree Display Syntax Highlighted Code Editing for...

红薯
2012/01/17
1K
5
性能测试知多少---了解前端性能

我的上一篇博文中讲到了响应时间,我们在做性能测试时,能过工具可以屏蔽客户端呈现时间,通过局域网的高宽带可以忽略数据传输速度的障碍。这并不是说他们不会对系统造成性能影响。相反,从用...

长平狐
2013/03/12
78
0
vsftpd日志(xferlog格式)的含义

原帖: http://bbs.chinaunix.net/viewthread.php?tid=697954&page=1&extra=page%3D1#pid4725282 引用:Thu Mar 4 08:12:30 2004 1 202.114.40.242 37 /incoming/index.html a _ o a [email......

红薯
2009/05/06
640
0
如何选择合适的 GC 时间 —— USER, SYS, REAL?

在 GC 日志文件中,每个 GC 事件有三种类型的时间: user sys real 例如: 在我们开始分析 GC 日志的时候,会遇到两个问题: 1、、 和 三者之间有什么不同? 2、我们该使用哪一个时间来分析日...

丢失的羊羔
2016/07/19
837
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

用 Python 实现打飞机,让子弹飞吧!

所用技术和软件 python 2.7 pygame 1.9.3 pyCharm 准备工作 安装好 pygame 在第一次使用 pygame 的时候,pyCharm 会自动 install pygame。 下载好使用的素材。 技术实现 初始化 pygame 首先要...

猫咪编程
7分钟前
0
0
MySQL的行锁和表锁

简单总结一下行锁和表锁。 行锁 每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 表锁 每次操作锁住整张表。开销小,加锁快;不会出...

to_ln
9分钟前
0
0
Java IO类库之字节数组输入流ByteArrayInputStream

一、ByteArrayInputStream字节数组输入流介绍 ByteArrayInputStream是字节数组输入流,继承自InputStream。它的内部包含一个缓冲区,是一个字节数组,缓冲数组用于保存从流中读取的字节数据,...

老韭菜
11分钟前
0
0
iOS安全应该做哪些事情

1. 尽量使用HTTPS协议。 2. 密码提交的时候,密码使用SHA256加密后传输,MD5等经过哈希碰撞已经可以推算出原文。 3. 密码提交的时候,可以加盐。 4. 密码保存在本地的时候,尽量使用钥匙串保...

HOrange
17分钟前
0
0
react native 注意事项

1. 环境参考官网 android studio 必装 java jdk安装 1.8版本(环境建议自己一步一步配置,切记不要 apt ) 2.有改变编译内容发现 会白屏,然后APP消失,请卸载原来的测试 appinfo (连续两次...

304158
23分钟前
0
0
FOMO游戏代码解析

源代码在此处

怎当她临去时秋波那一转
28分钟前
1
0
EOS智能合约与DApp开发入门

EOS的是Block.One主导研发的一个区块链底层公链系统,它专门为支撑商业去中心化 应用(Decentralized Application)而设计,其代码开源。 比特币被称为区块链1.0,因为它开辟了数字加密货币的...

笔阁
41分钟前
1
0
编译cjson到dll

https://blog.csdn.net/mengzhisuoliu/article/details/52203724 编译完成后 是纯lua实现的json decode 的10倍以上...

梦想游戏人
51分钟前
0
0
JS基础- Date 对象

Date 对象 Date 对象用于处理日期和时间。 创建 Date 对象的语法: var myDate=new Date() 注释:Date 对象会自动把当前日期和时间保存为其初始值。 Date 对象属性 属性 描述 constructor 返...

ZHAO_JH
53分钟前
0
0
Python数据分析numpy(1)

Python开源的科学计算基础库 1.表示N维数组对象ndarray 2.线性代数、傅里叶变换、随机数生成 3.广播函数,整合c++、c 一.数据的维度 1.数据 2.数据维度 3.一维数据 (1)特点 (2)Python中的...

十年磨一剑3344
55分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部