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度偏振图像为: