文档章节

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

abcijkxyz
 abcijkxyz
发布于 2016/11/22 16:40
字数 1864
阅读 58
收藏 0

一、前言  

  实际上很久以前,当我初次接触图像去雾技术时,最先实现的是基于中值滤波的图像去雾,并且也有一定的效果,在我的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  转载请保留本行信息*************************

 

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

共有 人打赏支持
abcijkxyz
粉丝 64
博文 6196
码字总数 1876
作品 0
深圳
项目经理
基于matlab的图片处理(以图片验证码为例)

在一幅图像中,人们常常只对其中的一部分感兴趣,更重要的是,通常只有部分区域含有我们需要的某些重要的信息,这些部分通常占据一定的区域,并且具有某些特性(如灰度,轮廓,颜色和纹理等)...

qq_40996400
05/23
0
0
滤波反投影重建算法(FBP)实现及应用(matlab)

滤波反投影重建算法实现及应用(matlab) 1. 滤波反投影重建算法原理 滤波反投影重建算法常用在CT成像重建中,背后的数学原理是傅立叶变换:对投影的一维傅立叶变换等效于对原图像进行二维的...

qq_33414271
2017/09/28
0
0
12- OpenCV+TensorFlow 入门人工智能图像处理-磨皮美白

磨皮美白 上节课的+固定值,我们还可以变为乘以一个系数。 只修改b和g的值,然后分别加上系数。 可以看到这次就没有蒙上白色 磨皮美白(双边滤波) 可以看到已经有很大效果了 高斯均值滤波 高斯...

天涯明月笙
05/12
0
0
本科毕设论文——基于Kinect的拖拉机防撞系统

基于Kinect的拖拉机防撞系统电子信息科学与技术专业学生 sukeysun 摘要:随着智能车辆技术的发展,智能导航定位和实时车载监控等技术被更多的应用到日常生活照。在农业领域上,车辆自主感知道...

sukeySun
2017/12/21
0
0
图像与滤波关系

http://www.ruanyifeng.com/blog/2017/12/image-and-wave-filters.html 我对图像处理一直很感兴趣,曾经写过好几篇博客(1,2,3,4)。 前几天读到一篇文章,它提到图像其实是一种波,可以用...

u011001084
2017/12/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

[Hive]JsonSerde使用指南

注意: 重要的是每行必须是一个完整的JSON,一个JSON不能跨越多行,也就是说,serde不会对多行的Json有效。 因为这是由Hadoop处理文件的工作方式决定,文件必须是可拆分的,例如,Hadoop将在...

Mr_yul
13分钟前
0
0
54:mysql修改密码|连接mysql|mysql常用命令

1、mysql修改密码: root用户时mysql的超级管理员,默认mysql的密码是空的,直接可以连接上去的,不过这样不安全; 注释:为了方便的使用mysql,需要把mysql加入到环境变量里; #后续自己输入mys...

芬野de博客
20分钟前
0
0
鼠标单击复制粘贴标签中的内容

<span ref="spanContentOne" id="spanContentOne" style="font-size: 14px;">或许不是最亮眼,总比瞎买强一点</span><!--<input type="button" @click="copyClick('1')" value="复制" />-......

帝子兮
24分钟前
0
0
使用axel多线程疯狂下载

在Linux中比较常见见的下载工具是curl和wget,但是下载比较大的文件两者都不支持多线程, 断点续传的作用不见得能发挥到最大。今天介绍一个axel工具,开启多线程疯狂下载。 安装 Fedora/Cen...

linuxprobe16
26分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部