基于中值滤波或双边滤波方式的图像去雾效果的研讨。

一、前言 

 实际上很久以前,当我初次接触图像去雾技术时,最先实现的是基于中值滤波的图像去雾,并且也有一定的效果,在我的Imageshop的集成软件中的去雾方案就是这个的实现,不过那个效果没有本文好。

     而基于双边滤波的方案,也是很早就听说过,前不久有朋友传给我一篇国内的双边滤波去雾的论文,总体思路和基于中值的类似,想想干脆把这两个放在一起做个比较吧。

二、算法的流程

 算法的最基础的原理还是基于大气散射模型的,即:

  已知条件就是输入图像I(X),求J(x);

     在参考论文一种单幅图像去雾方法中是通过中值滤波的方式来去雾的,而论文基于双边滤波的实时图像去雾技术研究选用了双边滤波,如果你要实现代码,可能需要两篇论文结合起来看,因为在论文1中的描述没有讲清楚如何通过获得的雾浓度数据来获取无雾的图像。

     简单的来说,算法的流程可描述如下:

  1、定义F(X)=A(1-t(x)),称之为大气光幕或者为雾浓度。

     2、计算

,并使用和何博士论文中类似的方式计算全局大气光值A。

     3、计算

,即对M(x)进行中值滤波。

     4、计算

,注意式子中的绝对值。

     5、计算

,式中P为控制去雾程度的因子,取值范围[0,1]。

     6、通过式子

获得去雾后的图像。

     上面的很多算式是从不同论文里截图的,因此表达上有些前后不一致,但不影响高手理解其含义。

     如果是采用双边滤波算子,则步骤3和4中的median运算符需修改为bilaterfilter,其他的步骤一样。

  算法的原理我讲不清,反正看的越多越迷糊了。

三、算法的效果

   算法的效果还是有些意外,有些图获得了相当不错的效果。

  原图

去雾图

对应的大气光幕

  原图

去雾图

对应的大气光幕

  原图

去雾图

对应的大气光幕

  原图

去雾图

对应的大气光幕

  原图

去雾图

对应的大气光幕

  原图

去雾图

                           对应的大气光幕

   这里的大气光幕和何凯明的论文中的透射率图不是同一个概念,因此不具有可比性。

   在大气光幕的公式中,我们看到有全局大气光A的影响,但是上述计算F(X)的过程确没有涉及到A,很是无语啊。

   从效果上看,我所列举的这些例子都还是不错的,特别是第一幅图,用何凯明的暗通道我一直没有调出这种效果。

   上述都是用中值滤波做的效果,在部分图像对应大气光幕图上可以看出,图像的边缘处有一些小圆弧,这些都是矩形半径中值滤波的明显痕迹,而基于双边滤波的我也实践过,并没有像参考论文2说的那样有多少改进,感觉彼此彼此,而且有些图还会出现突变,因此我认为写这些论文纯粹是为了发论文。 

四、代码实现细节

    在代码实现上,个人感觉没有什么难点,先求暗通道,然后就是几个中值滤波或者是双边滤波,求全局大气光的过程还涉及到最小值滤波,主要的代码如下:

void _stdcall HazeRemovalBasedOnMedianBlur(unsigned char * Scan0, int Width,int Height,int Stride,int DarkRadius,int MedianRadius,int P)
{
    int  X, Y, Diff,Min,F;
    unsigned char* Pointer, *DarkP, *FilterP,* FilterPC;
    unsigned char * DarkChannel = (unsigned char*)malloc(Width * Height);
    unsigned char * Filter = (unsigned char*)malloc(Width * Height);
    unsigned char * FilterClone = (unsigned char*)malloc(Width * Height);

    for (Y = 0; Y < Height; Y++)
    {
        Pointer = Scan0 + Y * Stride;
        DarkP = DarkChannel + Y * Width;             // 由实际图像计算得到的图像暗通道     
        for (X = 0; X < Width; X++)
        {
            Min = *Pointer;
            if (Min > *(Pointer + 1)) Min = *(Pointer + 1);
            if (Min > *(Pointer + 2)) Min = *(Pointer + 2);
            *DarkP = (unsigned char)Min;
            DarkP++;
            Pointer += 3;
        }
    }
    memcpy(Filter, DarkChannel, Width * Height);                        // 求全局大气光A时会破坏DarkChannel中的数据

    MinValue(DarkChannel, Width, Height,Width,DarkRadius);                // 求取暗通道值

    // 利用暗通道来估算全局大气光值A
    int Sum, Value,Threshold = 0;
    int SumR = 0, SumG = 0, SumB = 0, AtomR, AtomB, AtomG, Amount = 0;
    int* Histgram = (int*)calloc(256 , sizeof(int));    
    for (Y = 0; Y < Width * Height; Y++) Histgram[DarkChannel[Y]]++;
    for (Y = 255, Sum = 0; Y >= 0; Y--)
    {
        Sum += Histgram[Y];
        if (Sum > Height * Width * 0.01)
        {
            Threshold = Y;                                        // 选取暗通道值中前1%最亮的像素区域为候选点
            break;
        }
    }
    AtomB = 0; AtomG = 0; AtomR = 0;
    for (Y = 0, DarkP = DarkChannel; Y < Height; Y++)
    {
        Pointer = Scan0 + Y * Stride;
        for (X = 0; X < Width; X++)
        {
            if (*DarkP >= Threshold)                            //    在原图中选择满足候选点的位置的像素作为计算全局大气光A的信息                        
            {
                SumB += *Pointer;
                SumG += *(Pointer + 1);
                SumR += *(Pointer + 2);
                Amount++;
            }
            Pointer += 3;
            DarkP++;
        }
    }
    AtomB = SumB / Amount;
    AtomG = SumG / Amount;
    AtomR = SumR / Amount;

    memcpy(DarkChannel,Filter, Width * Height);                        // 恢复DarkChannel中的数据
    MedianBlur(Filter,Width,Height,Width,MedianRadius,50);          // 步骤1:使用中值滤波平滑,这样处理的重要性是在平滑的同时保留了图像中的边界部分,但是实际这里用中值滤波和用高斯滤波效果感觉差不多
    memcpy(FilterClone, Filter, Width * Height);

    DarkP = DarkChannel;
    FilterP = Filter;
    for (Y = 0; Y < Height * Width; Y++)              //利用一重循环来计算提高速度
    {
        Diff = *DarkP - *FilterP;                    //通过对|DarkP -FilterP |执行中值滤波来估计的局部标准差,这样可以保证标准差估计的鲁棒性
        if (Diff < 0) Diff = -Diff;
        *FilterP = (unsigned char)Diff;
        DarkP++;
        FilterP++;
    }
    MedianBlur(Filter,Width,Height,Width,MedianRadius,50);

    FilterPC = FilterClone;
    FilterP = Filter;
    for (Y = 0; Y < Height * Width; Y++)
    {
        Diff = *FilterPC - *FilterP;                    // 步骤2:然后考虑到有较好对比度的纹理区域可能没有雾, 这部分区域就不需要做去雾处理
        if (Diff < 0) Diff = 0;                            // 这里可以这样做是因为在最后有个max(....,0)的过程,
        *FilterP = (unsigned char)Diff;
        FilterPC++;
        FilterP++;
    }

    DarkP = DarkChannel;
    FilterP = Filter;

    for (Y = 0; Y < Height * Width; Y++)
    {
        Min = *FilterP * P / 100;
        if (*DarkP > Min) 
            *FilterP = Min;                                // 获得满足约束条件的大气光幕
        else
            *FilterP = *DarkP;
        DarkP++;
        FilterP++;
    }

    FilterP = Filter;
    for (Y = 0;Y < Height; Y++)
    {
        Pointer = Scan0 + Y * Stride;
        for (X = 0; X < Width; X++)
        {
            F = *FilterP++;
            if (AtomB != F) 
                Value = AtomB *(*Pointer - F) /( AtomB - F);
            else
                Value=*Pointer;
            *Pointer++ = Clamp(Value);
            if (AtomG != F) 
                Value =  AtomG * (*Pointer - F) /( AtomG-F);
            else
                Value =  *Pointer;
            *Pointer++ = Clamp(Value);
            if (AtomR != F) 
                Value =  AtomR *(*Pointer - F) /( AtomR-F);
            else
                Value =  *Pointer;
            *Pointer++ = Clamp(Value);
        }
    }
    free(Histgram);
    free(Filter);
    free(DarkChannel);
    free(FilterClone);
}

  关于中值滤波或者双边滤波的快速算法,可以在本人博客中找到大量的相关信息。

     在程序的耗时上,主要还是2次中值处理上,借助于C++的一些优化(比如内嵌SSE代码,C#做不到)中值的速度也相当快了,我用1024*768的灰度图测试耗时约为60ms(未考虑用多线程,因为那个程序用多线程编码上会复杂不少),对彩色图用这种方式去雾,I3CPU上1024*768的总耗时约为140ms,想要实时,换换I7的CPU试试吧(传说中我的那篇实时去雾的文章的算法在I3上20ms,I7上有测试表明只要3到4ms)。

     由于算法的最后一步的公式问题,在某些参数情况下图像会出现黑快或者白块,目前该问题尚未解决。 有兴趣对改算法进行进一步测试的同学可自己研究下。

     相关测试代码下载:

 http://files.cnblogs.com/Imageshop/HazeRemovalBasedOnMedianBlur.rar

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CNN

Tensorflow MobileNet移植到Android

在柯林斯原始网络中使用函数tf.nn.top_k电子杂志概率最大的3类,函数将tf.nn.top_k作为网络中的一个计算节点。

2235
来自专栏Python中文社区

遗传算法框架GAFT优化小记

專 欄 ❈PytLab,Python 中文社区专栏作者。主要从事科学计算与高性能计算领域的应用,主要语言为Python,C,C++。熟悉数值算法(最优化方法,...

1798
来自专栏鸿的学习笔记

写给开发者的机器学习指南(八)

Ranking emails based on their content(Recommendation system)

812
来自专栏Pulsar-V

SLAM初探(四)

OpenCV基础 这里我就不做过多的描述性问题,现在OpenCV在许多有关计算机视觉方面得到许多的应用。 OpenCV获取视频的方法及其图像转化问题 获取视频及...

3077
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

SSE图像算法优化系列十二:多尺度的图像细节提升。

无意中浏览一篇文章,中间提到了基于多尺度的图像的细节提升算法,尝试了一下,还是有一定的效果的,结合最近一直研究的SSE优化,把算法的步骤和优化过程分享给大家。...

1978
来自专栏落影的专栏

使用AudioToolbox编码AAC

前言 使用VideoToolbox硬编码H.264 使用VideoToolbox硬解码H.264 这次在编码H.264视频流的同时,录制并编码AAC音频流。...

3627
来自专栏何俊林

MediaCodec进行音频合成

2133
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

标准的基于欧式距离的模板匹配算法优源码化和实现(附源代码)。

     很久没有出去溜达了,今天天气好,就放松放松去,晚上在办公室没啥事,把以前写的一个基于标准的欧式距离的模板匹配代码共享吧。      opencv有模板...

21610
来自专栏数据小魔方

R语言可视化——折线图、平滑曲线及路径图

今天跟大家讲关于路径图、平滑曲线与折线图及其美化。 这里涉及到三个设计线条的特殊图层函数: geom_smooth()、geom_path()、geom_li...

34110
来自专栏null的专栏

优化算法——拟牛顿法之DFP算法

一、牛顿法    image.png image.png 二、DFP拟牛顿法 1、DFP拟牛顿法简介         DFP拟牛顿法也称为DFP校正方法,DFP...

2856

扫码关注云+社区