热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于偏振差分成像的图像去雾算法

Polarization-basedvisionthroughhazeYoavY.Schechner,SrinivasaG.Narasimhan,andShreeK.NayarC

Polarization-based vision through haze

Yoav Y.Schechner,Srinivasa G.Narasimhan,and Shree K.Nayar

Columbia University

1,介绍

这篇文章在分析图像形成的过程中,将偏振对大气散射的影响考虑在内,然后设计从图像中去雾的算法。这个方法可以通过旋转偏振片到不同的角度或者两张偏振图像来实现。这个方法效果很好,不需要依赖天气情况的变化。

2,基本原理

 

 

把从光源到分布粒子的光线和从分布粒子到相机的光线构成的平面作为标准平面。将一束光线分成两个偏振分量,平行的量和垂直的量。光线和此平面平行的偏振量为,光线和此平面垂直的偏振量为,可以定义偏振度为:

 

其中A定义为: 

 

根据大气散射模型,直接传输定义为:

 

其中表示无雾图像,t(z)可以表达为:

 

从而没有经过偏振片的含雾图像可以表达为:

 

当旋转偏振片时,是随偏振角度变化的函数,其函数曲线如下:

 

由上图可见,当偏振角度为90度时,为“最坏的状态”。当偏振角度为0度时,为“最好的状态”。其表达式如下:

 

 

 

其中通过反解上式可得:

 

 

3,去雾过程

   首先,应该求得大气光值和其对应的偏振度p。由于得到的0度和90度偏振图像中含有白色区域,不能直接选取图像中0.1%最亮的点的平均值作为大气光值。本文的做法如下,将90度偏振图像与0度偏振图像相减,得到的图像取其最亮的0.1%的点,在分别在0度和90度的偏振图像中找到作差得到的图像的最亮的0.1%的点的位置,取这些点的平均值,分别得到0度和90度偏振图像的大气光值。

   现在定义每一个像素点的大气光为:

 

无偏振图像为:

 

传输函数为:

 

由以上各式,得到去雾图像为:

 

4,结果分析

     依据上述的理论过程,利用MATLAB/C++实现的结果如下:

 

可以很明显的看到,通过上述理论过程去雾,在景物部分的效果很好,而在天空区域出现了很严重的噪声。为了解决这个问题,文章引入了一个参数,然后实现如下替换:,就可以解决天空区域噪声很大的问题。去噪的结果如下:

 

5,结论

      通过以上的两个结果进行比较发现:不引入参数时天空区域出现严重的噪声现象;引入参数后,景物部分又出现了去雾效果不好的结果。总之,应适当调整引入的参数的大小,才能达到更好的去雾效果。

附录:

本文的C++代码如下:

 

#include 
#include 
#include 
#include 
#include 
#include 

using namespace cv;
using namespace std;

typedef struct Pixel 
{
    int x, y;
    float data;
}Pixel;

bool structCmp(const Pixel &a, const Pixel &b) 
{
    return a.data > b.data;//descending降序
}

Mat getAdd(Mat& parimg, Mat& perimg);
Mat getSubstract(Mat& parimg, Mat& perimg);
Mat getA(Mat& parimg, Mat& perimg, float *array);
Mat recover(Mat& total, Mat& a, float *array);

int main() 
{
    string loc1 = "E:\\polarization\\firstopencv\\0degree.tiff";
    string loc2 = "E:\\polarization\\firstopencv\\90degree.tiff";
    string name = "forest";
    clock_t start, finish;
    double duration;

    cout <<"A defog program" << endl
        <<"----------------" << endl;

    Mat src1 = imread(loc1);
    Mat src2 = imread(loc2);
    Mat parimage = Mat(src1.size(), CV_16UC3);
    Mat perimage = Mat(src2.size(), CV_16UC3);
    src1.convertTo(parimage, CV_16UC3, 255.0 );
    src2.convertTo(perimage, CV_16UC3, 255.0 );
    imshow("0degree", parimage);
    imshow("90degree", perimage);
    cout <<"input hazy image" << endl;

    int rows = parimage.rows;
    int cols = parimage.cols;
    Mat newparimage; Mat newperimage;
    parimage.convertTo(newparimage, CV_32FC3, 1.0/65535.0, 0);
    perimage.convertTo(newperimage, CV_32FC3, 1.0/65535.0, 0);


    Mat totalimage(rows, cols, CV_32FC3);
    Mat diffimage(rows, cols, CV_32FC3);
    Mat A(rows, cols, CV_32FC3);
    Mat recoverimg(rows, cols, CV_32FC3);
    totalimage = getAdd(newparimage,newperimage);
    diffimage = getSubstract(newparimage, newperimage);
    int nr = rows;int nl = cols;
    start = clock();

    //estimate Airlight
    //开一个结构体数组存暗通道,再sort,取最大0.1%,利用结构体内存储的原始坐标在原图中取点
    cout <<"estimating airlight." << endl;
    vector planes;
    split(diffimage, planes);
    Mat rchannel = planes[2];
    int pixelTot = rows * cols * 0.001;
    float *Apar = new float[3]; float *Aper = new float[3];
    Pixel *toppixels, *allpixels;
    toppixels = new Pixel[pixelTot];
    allpixels = new Pixel[rows * cols];


    for (unsigned int r = 0; r ) 
    {
        const uchar *data = rchannel.ptr(r);
        for (unsigned int c = 0; c ) 
        {
            allpixels[r*cols + c].data = *data;
            allpixels[r*cols + c].x = r;
            allpixels[r*cols + c].y = c;
        }
    }
    
    std::sort(allpixels, allpixels + rows * cols, structCmp);
    memcpy(toppixels, allpixels, pixelTot * sizeof(Pixel));
    
    float tmp_par_A[3]; float tmp_per_A[3];
    float A_par_r, A_par_g, A_par_b, avg1, maximum1 = 0;
    int idx1, idy1, max_x1, max_y1;
    for (int i = 0; i ) 
    {
        float A_par_sum_r = 0, A_par_sum_g = 0, A_par_sum_b = 0;
        idx1 = allpixels[i].x; idy1 = allpixels[i].y;
        const uchar *pardata = newparimage.ptr(idx1);
        pardata += 3 * idy1;
        A_par_b = *pardata++;
        A_par_g = *pardata++;
        A_par_r = *pardata++;

        A_par_sum_b += A_par_b;
        A_par_sum_g += A_par_g;
        A_par_sum_r += A_par_r;
        
        tmp_par_A[0] = A_par_sum_b / pixelTot;
        tmp_par_A[1] = A_par_sum_g / pixelTot;
        tmp_par_A[2] = A_par_sum_r / pixelTot;
    }

    float A_per_r, A_per_g, A_per_b, avg2, maximum2 = 0;
    int idx2, idy2, max_x2, max_y2;
    for (int i = 0; i )
    {
        float A_per_sum_r = 0, A_per_sum_g = 0, A_per_sum_b = 0;
        idx2 = allpixels[i].x; idy2 = allpixels[i].y;
        const uchar *perdata = newperimage.ptr(idx2);
        perdata += 3 * idy2;
        A_per_b = *perdata++;
        A_per_g = *perdata++;
        A_per_r = *perdata++;
        
        A_per_sum_b += A_per_b;
        A_per_sum_g += A_per_g;
        A_per_sum_r += A_per_r;

        
        tmp_per_A[0] = A_per_sum_b / pixelTot;
        tmp_per_A[1] = A_per_sum_g / pixelTot;
        tmp_per_A[2] = A_per_sum_r / pixelTot;
    }

    delete[] toppixels;
    delete[] allpixels;

    float P[3]; float Ainf[3];
    for (int i = 0; i <3; i++)
    {
        P[i] = (tmp_per_A[i] - tmp_par_A[i]) / (tmp_per_A[i] + tmp_par_A[i]);
        Ainf[i] = tmp_per_A[i] + tmp_par_A[i];
    }

    A = getA(newparimage, newperimage, P);

    cout <<"start recovering." << endl;
    recoverimg = recover(totalimage, A, Ainf);
    cout <<"recovering finished." << endl;

    finish = clock();
    duration = (double)(finish - start);
    cout <<"defog used " <"ms time;" << endl;
    imshow("result", recoverimg);
    waitKey(0);

    
    imwrite(name + "_refined.png", recoverimg);
    destroyAllWindows();
    parimage.release();
    perimage.release();
    recoverimg.release();
    return 0;
}

Mat getAdd(Mat& parimg, Mat& perimg)
{
    int nr = parimg.rows; int nl = perimg.cols;
    Mat addimg = Mat::zeros(nr, nl, CV_32FC3);
    for (unsigned int r = 0; r )
    {
        const float* parPtr = parimg.ptr<float>(r);
        const float* perPtr = perimg.ptr<float>(r);
        float* addPtr = addimg.ptr<float>(r);
        for (unsigned int c = 0; c )
        {
            for (int i = 0; i <3; i++)
            {
                *addPtr++ = *parPtr++ + *perPtr++;
            }
        }
    }
    return addimg;
}

Mat getSubstract(Mat& parimg, Mat& perimg)
{
    int nr = parimg.rows; int nl = perimg.cols;
    Mat subimg = Mat::zeros(nr, nl, CV_32FC3);
    for (unsigned int r = 0; r )
    {
        const float* parPtr = parimg.ptr<float>(r);
        const float* perPtr = perimg.ptr<float>(r);
        float* subPtr = subimg.ptr<float>(r);
        for (unsigned int c = 0; c )
        {
            for (int i = 0; i <3; i++)
            {
                *subPtr++ = *perPtr++ - *parPtr++;
            }
        }
    }
    return subimg;
}

Mat getA(Mat& parimg, Mat& perimg, float *array)
{
    int nr = parimg.rows; int nl = perimg.cols;
    Mat A= Mat::zeros(nr, nl, CV_32FC3);
    for (unsigned int r = 0; r )
    {
        const float* parPtr = parimg.ptr<float>(r);
        const float* perPtr = perimg.ptr<float>(r);
        float* APtr = A.ptr<float>(r);
        for (unsigned int c = 0; c )
        {
            for (int i = 0; i <3; i++)
            {
                *APtr++ = (*perPtr++ - *parPtr++) / *array;
            }
        }
    }
    return A;
}

Mat recover(Mat& total,Mat& a,float *array)
{
    int nr = total.rows;int nl=total.cols;
    Mat recoverimage = Mat::zeros(nr, nl, CV_32FC3);
    for (unsigned int r = 0; r )
    {
        const float* totalPtr = total.ptr<float>(r);
        const float* aPtr = a.ptr<float>(r);
        float* recoverPtr = recoverimage.ptr<float>(r);
        for (unsigned int c = 0; c )
        {
            for (int i=0;i<3;i++)
            {
                *recoverPtr = (*totalPtr - *aPtr) /(1-(*aPtr / *array));
                recoverPtr++;
                totalPtr++;
                aPtr++;
            }
        }
    }
    return recoverimage;
}

 

0度偏振图像为:

90度偏振图像为:

 

 

 


推荐阅读
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了三种方法来实现在Win7系统中显示桌面的快捷方式,包括使用任务栏快速启动栏、运行命令和自己创建快捷方式的方法。具体操作步骤详细说明,并提供了保存图标的路径,方便以后使用。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有