在我博文的一系列的文章,有不少算法都于去雾有关,比如限制对比度自适应直方图均衡化算法原理、实现及效果局部自适应自动色阶/对比度算法在图像增强上的应用这两个增强算法都有一定的去雾能力,而最直接的就是《Single Image Haze Removal Using Dark Channel Prior》一文中图像去雾算法的原理、实现、效果及其他 一文,描述了暗通道去雾这一state-of-the-art algorithms的过程和实现,虽几经优化,对于常用的视频1024*768大小的图片,算法处理部分还是需要70MS的时间(I3 笔记本CPU),因此,这一算法用于实时要求时还有一定的难度,并且优化后的算法基本无法并行,而可并行的算法重复计算大,由于不熟悉GPU方面的理念,不晓得使用不优化的算法靠GPU是否能有多大速度的提升。

为此,我一直在找寻相关的论文,这种找寻的踪迹一般就是看到一篇好论文--》看其参考文献--》再看参考文献的参考文献,这样循环下去。 然后有某种机会或巧合,又看到一篇好论文,重复前面的过程,你就会发现很多交集,慢慢的就会有一些好运向你招手。 话说我原本只看英文的文献,所以一直忽略了国内的文章,前几日,一个QQ朋友推荐了一篇清华大学的论文,下载后稍微看了下,觉得其描述的结果还是比较吸引人的,于是就实现了下,实时的效果应该说很不错,这里就简单的介绍并推荐给大家。

原始论文下载:基于单幅图像的快速去雾.pdf  ,作者刘倩,陈茂银,周东华,感谢他们(她们)。

算法原理没有什么复杂的地方,其实说原理,还不如说经验或实验,因为论文中可以用理论来推导的公式确实不多。不过这也没关系,有用的东西就应该拿来用。

算法的执行流程直接贴用原图的文字来说明吧:

先挑点小瑕疵,比如步骤5中的L有个下标,而其他涉及到L的地方去没有,这叫前后不一致。论文中也还有一些其他的地方有些小错误,所以看啊,即使是清华的文章,在编辑审核这一块还是相当的不严谨。

我们先来看看算法,1、2、3、4步都没有什么说的,第五步是求大气透射率的过程,这里ρ是一个用来调节的参数,当ρ值越大时,结果图像整体越暗,去雾的效果更明显,ρ较小时,图像偏白,有明显的雾气。第六步求全局大气光A值,用了很简单的方式,即求原始图像的RGB所有像素分量的最大值(这个估计99%都为255了)何暗通道的最大值的平均值,并且注意到RGB三个通道用的A值都为同一个数字。这个和我在何凯明的文章的分析也有相似的地方。 第七步用了标准的去雾模型来求结果值。

在来看看算法的效率问题, 从算法的初步分析来看,算法的效率取决于第3步和第7步,第三步中,使用了均值模糊,目前已经存在大量的O(1)均值模糊算法,可是O(1)只能表示算法的执行速度和参数的大小无关系,并不表示算法就很快。比如基于积分图的模糊算法是广为认知的O(1)算法,但是他也存在很多问题,最严重的就是数据的溢出,当图像较大和偏白时,对图像积分图的累加和存在超出int.Maxvalue所能表达的范围的问题,解决办法就是积分图内的数据全部使用long类型表示,这将导致程序多占用Width*Height*4字节的大小的内存,且在32位系统还流行的情况进一步降低程序的速度(32位系统64位整数的计算速度要比32位整数慢)。积分图的另外一个问题就是计算积分图的过程难以并行化,因为一个像素的积分值是依赖于其前面一系列像素的相关结果值的。另外一种优化方式就是先计算行方向的平均值,然后再计算列方向的值。这种方式在同一行(列)内,算法依旧必行顺序执行,这也是因为前后影响的原因。但是不同行(列)之间的计算是没有任何关系,因此非常适合GPU这种可大规模并行计算的场合,但不适于CPU这种重量级的线程并发(反而会慢)。这种算法如果为了精度会需要一个和原图一样大小,占用字节Width*Height*4字节大小的的中转区用来保存中间计算的结果。在彩色图像高速模糊之懒惰算法一文中,我采用了另外一种处理方法,利用列直方图相关的技术,只需对每个循环的起始位置处的像素做特殊处理,其他位置的利用简单的一加一简即可获得累加和,从而快速的实现模糊,我实际的编码表明,这种方式比其他的方式都要快。但是有一个缺点,不适合于并行计算,不过在CPU上这个很有优势。

再观察下第七步。第七步存在两个需要优化的地方,第一,存在除法;第二,有浮点运算。如果直接编码必然会带来性能损失,但是,观察下在第七步的公式中,只有两个自变量,H(X)和L(X),并且自变量的取值都为[0,255]之间的整数,因此,如果事先建议一个查找表,由于这个查找表的计算量只有 256*256次,要远远的小于直接计算的次数,必然能提高程序的速度。256这个数字还有个好处,就是可以用移位来辅助计算查表表的下标,部分参考代码如下所示:

unsigned char * Table = (unsigned char  *) malloc (**sizeof(unsigned char));
for (Y = ; Y < ; Y++)
{
Index=Y<<;
for (X = ; X < ; X++)
{
Value = (Y-X) /(-X*InvA);
if (Value > )
Value = ;
else if (Value < )
Value = ;
Table[Index++]= Value;
}
}

  其中InvA = 1 / A;A为全局大气光值。 Value需为double类型的变量。

当我们需要计算F(x)时,查表的方式为 F(X)=Table[(H[X]<<8 )+ L[X]];

实际的效果表明,这样的方式对于1024*768的图,可以提速10ms。

那么对于其他步骤也有很多优化的注意事项,比如计算M(X)中所有元素的平均值Mav这一块,完全没有必要在开一个循环,而是可以在进行步骤3的时候同步进行,大家知道,循环楚了要计算循环体内部的东西外,还要有个循环计数器的更新的,何必浪费这个时间呢。

for (Y = , DarkPt = DarkChannel; Y < Height; Y++)
{
ImgPt = Src + Y * Stride;
for (X = ; X < Width; X++)
{
Min = *ImgPt;
if (Min > *(ImgPt + )) Min = *(ImgPt + );
if (Min > *(ImgPt + )) Min = *(ImgPt + );
*DarkPt = Min; // 三通道的最小值
Sum += Min; // 累积以方便后面求平均值
ImgPt += ;
DarkPt++;
}
}
Mean =(double)Sum /(Width*Height*);

还有比如第6步中,分别求原图RGB三像素最大值以及安通道中的最大值的过程,传统的过程如下代码:

int Max1 = ;
for (Y = ; Y < Height; Y++)
{
ImgPt = Src + Y * Stride;
for (X = ; X < Width; X++)
{
if (Max1 < *(ImgPt)) Max1 = *(ImgPt);
if (Max1 < *(ImgPt + )) Max1 = *(ImgPt + );
if (Max1 < *(ImgPt + )) Max1 = *(ImgPt + );
ImgPt += ;
}
}

  特别是对于求原图的最大值,实际上很多情况下这个值都为255,因此如果Max1变量已经是255,则循环完全没有必要进行下去了,因此,如果改为下述代码,必然可以减少计算量:

for (Y = ; Y < Height; Y++)
{
ImgPt = Src + Y * Stride;
for (X = ; X < Width; X++)
{
if (Max1 < *(ImgPt)) Max1 = *(ImgPt);
if (Max1 < *(ImgPt + )) Max1 = *(ImgPt + );
if (Max1 < *(ImgPt + )) Max1 = *(ImgPt + );
ImgPt += ;
}
if (Max1==) break;
}

注意,这个break语句必须放在Y循环中,如果放在X循环中,虽然提前退出循环的可能性会增加,但是判断的工作量带来的损失更多。
      综合上述优化,我用C++写了个DLL,对于1024*768的图像在我的I3的机器上平均能达到18ms每副图像的计算速度,相当于56fps,只占用了单核的资源,考虑解码、显示等等其他过程所占用的时间,应该是能够靠CPU实现20fps的实时速度的。

在内存占用上,约需要> 3*Width*Height+256*256字节的空间(不包括图像本身的),如果用在连续的视频处理上,这部分内存就不需要频繁的分配和释放,可能也对速度的保证有好处。

程序下载地址: http://files.cnblogs.com/Imageshop/FastHazeRemovalTest.rar

 参数的选取:

    为了获得好的效果,该算法需要选择恰当的参数,我们为此做了一些测试。对于半径参数,我的个人建议是取值不要小于50或图像宽度和高度最大值的的1/20,比如对下面的图像(原图大小不是下图中的大小,这里是为了方便浏览缩小显示的),ρ取1.28时(对于上图中的去雾程序选择为128),半径取不同参数时的效果:

  

          原图                               r=16                          r=50

注意上面中间的图,一群飞鸟的周边明显有格格不入的白色雾气,而在右侧的图中,飞鸟则自然的融入了背景图像中。

另外,当半径足够大时,半径的大小对输出的结果的影响不大。

ρ参数的大小控制了图像去雾能力的大小,越大,雾气越少,图像越显得暗,越小,图像偏白,雾气越浓,下面给出了在半径R=50,取不同ρ值的效果。

  

  

  

          原图                               ρ=0.75                          ρ=1.3

ρ值如何取才能获得最佳效果,这个没有理论依据,需要根据具体图像进行测试,不过一般在1.2到1.5之间的效果能综合去雾和保持图像清晰的能力。

从更多的测试图看,该去雾算法的效果都是较为理想的,而且对于填充部位出现瑕疵的情况也出现的很少,速度上更是没的说,因此,作为一种实时去雾工业化也应该是可行的。

试过将过程中的均值模糊更改为高斯模糊,在速度上会稍有下降,也能达到实时要求,但去雾的效果似乎还没有均值好。

*****************************基本上我不提供源代码,但是我会尽量用文字把对应的算法描述清楚或提供参考文档*********************

*************************************因为靠自己的努力和实践写出来的效果才真正是自己的东西,人一定要靠自己****************************

*********************************作者: laviewpbt   时间: 2013.11.4   联系QQ:  33184777  转载请保留本行信息************************

一种可实时处理 O(1)复杂度图像去雾算法的实现。的更多相关文章

  1. <<一种基于δ函数的图象边缘检测算法>>一文算法的实现。

    原始论文下载: 一种基于δ函数的图象边缘检测算法. 这篇论文读起来感觉不像现在的很多论文,废话一大堆,而是直入主题,反倒使人觉得文章的前后跳跃有点大,不过算法的原理已经讲的清晰了.     一.原理 ...

  2. C++基础代码--20余种数据结构和算法的实现

    C++基础代码--20余种数据结构和算法的实现 过年了,闲来无事,翻阅起以前写的代码,无意间找到了大学时写的一套C++工具集,主要是关于数据结构和算法.以及语言层面的工具类.过去好几年了,现在几乎已经 ...

  3. 任何国家都无法限制数字货币。为什么呢? 要想明白这个问题需要具备一点区块链的基础知识: 区块链使用的大致技术包括以下几种: a.点对点网络设计 b.加密技术应用  c.分布式算法的实现 d.数据存储技术 e.拜占庭算法 f.权益证明POW,POS,DPOS 原因一: 点对点网络设计 其中点对点的P2P网络是bittorent ,由于是点对点的网络,没有中心化,因此在全球分布式的网

    任何国家都无法限制数字货币.为什么呢? 要想明白这个问题需要具备一点区块链的基础知识: 区块链使用的大致技术包括以下几种: a.点对点网络设计 b.加密技术应用  c.分布式算法的实现 d.数据存储技 ...

  4. 杨辉三角(Pascal Triangle)的几种C语言实现及其复杂度分析

    说明 本文给出杨辉三角的几种C语言实现,并简要分析典型方法的复杂度. 本文假定读者具备二项式定理.排列组合.求和等方面的数学知识. 一  基本概念 杨辉三角,又称贾宪三角.帕斯卡三角,是二项式系数在三 ...

  5. 分布式机器学习:同步并行SGD算法的实现与复杂度分析(PySpark)

    1 分布式机器学习概述 大规模机器学习训练常面临计算量大.训练数据大(单机存不下).模型规模大的问题,对此分布式机器学习是一个很好的解决方案. 1)对于计算量大的问题,分布式多机并行运算可以基本解决. ...

  6. MVC web api 返回JSON的几种方式,Newtonsoft.Json序列化日期时间去T的几种方式。

    原文链接:https://www.muhanxue.com/essays/2015/01/8623699.html MVC web api 返回JSON的几种方式 1.在WebApiConfig的Re ...

  7. 几种经典的Hash算法的实现(源代码)

    来源声明: http://blog.minidx.com/2008/01/27/446.html 先保存下来,以备后面研究,现在还看不懂! 哈希算法将任意长度的二进制值映射为固定长度的较小二进制值,这 ...

  8. 基于python的几种排序算法的实现

    #!usr/bin/python3 # -*- coding: utf-8 -*- # @Time : 2019/3/28 10:26 # @Author : Yosef-夜雨声烦 # @Email ...

  9. 一种算法的实现,几个相同大小的div组合在一起,判断是不是矩形

随机推荐

  1. C# 判断字符串是否是int/double

    using System.Text.RegularExpressions; /// <summary> /// 判断字符串是否是int/double /// </summary> ...

  2. html5跟随鼠标炫酷网站引导页动画特效

    html5跟随鼠标炫酷网站引导页动画特效一款非常不错的引导页,文字效果渐变,鼠标跟随出绚丽的条纹.html5炫酷网站引导页,鼠标跟随出特效. 体验效果:http://hovertree.com/tex ...

  3. 【C#进阶系列】29 混合线程同步构造

    上一章讲了基元线程同步构造,而其它的线程同步构造都是基于这些基元线程同步构造的,并且一般都合并了用户模式和内核模式构造,我们称之为混合线程同步构造. 在没有线程竞争时,混合线程提供了基于用户模式构造所 ...

  4. sql server 查看表的死锁和Kill 死锁进程

    查询出来 select        request_session_id spid,       OBJECT_NAME(resource_associated_entity_id) tableNa ...

  5. powershell脚本,命令行参数传值,并绑定变量的例子

    这是小技巧文章,所以文章不长.但原创唯一,非常重要.我搜了下,还真没有人发 powershell怎样 [命令行 参数 绑定],所以我决定写成博客. 搜索关键字如下: powershell 命令行 参数 ...

  6. Maven命令行使用:mvn clean install(安装)

    先把命令行切换到Maven项目的根目录,比如:/d/xxxwork/java/maven-test,然后执行命令: $ mvn clean install 执行结果如下: [INFO] Scannin ...

  7. Hibernate总结(一)

    Hibernate为了提高性能,提供了缓存与快照机制. 它的缓存分为一级缓存与二级缓存. Hibernate一级缓存:当一个事务中执行一次Sql语句时,就将返回的结果存储在Session中的Map集合 ...

  8. Atitit.atiagent  agent分销系统 代理系统 设计文档

    Atitit.atiagent  agent分销系统 代理系统 设计文档 1. 启动项目1 2. 首也2 3. 登录功能2 4. 用户中心2 5. 充值查询3 6. 授权下级代理4 7. 我的提成5 ...

  9. javascript关于继承

    上一篇已介绍了组合继承,现在讲讲剩余的几种继承. 原型式继承 调用一个函数,接收这个函数返回来的对象,这个对象的原型就是传入函数的参数对象. 如: function personObject(o){ ...

  10. 浅谈HTML5单页面架构(三)—— 回归本真:自定义路由 + requirejs + zepto + underscore

    本文转载自:http://www.cnblogs.com/kenkofox/p/4650310.html 不过,这一篇,我想进一步探讨一下这两个框架的优缺点,另外,再进一步,抛开这两个框架,回到本真, ...