热门标签 | 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度偏振图像为:

 

 

 


推荐阅读
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • poj 3352 Road Construction ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 题目描述:牛客网新员工Fish每天早上都会拿着一本英文杂志,在本子上写下一些句子。他的同事Cat对这些句子非常感兴趣,但发现这些句子的单词顺序被反转了。例如,“student. a am I”实际上是“I am a student.”。Cat请求你帮助他恢复这些句子的正常顺序。 ... [详细]
  • Spring Boot 中配置全局文件上传路径并实现文件上传功能
    本文介绍如何在 Spring Boot 项目中配置全局文件上传路径,并通过读取配置项实现文件上传功能。通过这种方式,可以更好地管理和维护文件路径。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 重要知识点有:函数参数默许值、盈余参数、扩大运算符、new.target属性、块级函数、箭头函数以及尾挪用优化《深切明白ES6》笔记目次函数的默许参数在ES5中,我们给函数传参数, ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 在Delphi7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本。定义如下:1234 ... [详细]
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社区 版权所有