dehaze paper reading 1

基础出发点:

I(x) = J(x) * t(x) + (1 - t(x)) *A (1)

I(x):我们观测的图像。

J(x):就是那个客观存在的图像,对于去雾的案例,可以认为是没有被雾挡住的图像。

t:为大气的传输过程。

A:为空气中的光。

x:为像素的位置。

就是说我们看到的图像是空气的光线和真实图像的混合。这个混合的比例为t。

去雾就是在已知观测图像I(x)下恢复真实图像J(x)。

朴素的想法:

J(x) = (I(x)-A)/t(x)  + A

因此问题就变成要估计t(x)和A。

先处理t(x)。

这个是一个随空间位置变化的量。对于一个具体点,可以假设附近的点上t(x)取常数。这个当然是不真实的,但可以解决一些问题。

I(x) = J(x) * t + (1 - t) * A

分成r,g,b三个通道(颜色)的方式:

Ic(x) = Jc(x) * t + (1 - t) * Ac         c = r, g, b

min_omega( Ic(y)) = t * min_omega(Jc(y)) + ( 1 - t) * Ac 


min_omega( Ic(y)) / Ac = t * min_omega(Jc(y))  / Ac + ( 1 - t)

min_omega函数表示,以x为中心的一个矩形区域中所有像素的最小值。可以用OpenCV实现为。论文中patch_size = 15。

double min_omega(Mat& I, int patch_size) {

            Mat tmp_img;

            getRectSubPix(I, Size(patch_size, patch_size), Point(x,y), tmp_img);
            minMaxLoc(tmp_img, &tmpmin);

            return tmpmin;

}

再做一步:

min_c(  min_omega(Ic(y) ) ) /Ac = t * min_c( min_omega(Jc(y))) + (1 - t)                                               (2)

min_c是对三个通道求最小:

uchar min_c(uchar r, uchar g, char b) {

       return std::min(r ,std:: min(g,b));

}

方程(2)是无法编程实现的。因为 Jc(y)是不知道的。

幸运的事情发生了,

min_c( min_omega(Jc(y))) = dark_channle( . ) = 0

所以。t = 1 - min_c(min_omega(Ic(y)))/Ac。这样就得到了t。

dark_channle( .) 是论文中核心和要害。就是说对于自然的图像,尤其是户外的图像,任何一个像素,他附近的的所有通道中总存在一个接近于0的点。

比如说一个单色的图像,其他两个通道都会为0。 在阴影中像素也是这样的。dark_channel是一个刚开始让人觉得很诧异的东西,仔细一想也是自然的。

这样可以定义重新写一下公式:

t = 1 - dark_channel(I)/A

当A已知那么,t就是可以通过原来的图像计算出来。

#include </to/path/opencv>

#include </to/path/std ...>

//using C++ 11 feature, Visual Studio 2013 or GCC

Mat recover(const Mat& I, float A, const Mat& t) {

    Mat J(I.size(), CV_32FC3);
    auto it_dst = J.begin<Vec3f>();
    auto it = t.begin<float>();
    const float t0 = 0.1f;
    for (auto it_src = I.begin<Vec3b>(); it_src != I.end<Vec3b>(); ++it_src, ++it, ++it_dst){
            float tx = std::max(*it, t0);

            float r_tx =  1 /tx;

            float t1 = A *(r_tx - 1)

            
            (*it_dst)[0] = ((*it_src)[0] )*r_tx - t1;
            (*it_dst)[1] = ((*it_src)[1] )*r_tx - t1;
            (*it_dst)[2] = ((*it_src)[2] )*r_tx - t1;
    }

    return std::move(J); //C++ 11 move semantics
}

Mat dark_channel(const Mat& I, int patch_size);


Mat dehaze(const Mat& I, int patch_size, uchar A) {

   auto dc = dark_channel(I, patch_size);

   Mat ones = Mat::ones(I.size(), CV_32F);

    Mat t(ones.size(), ones.type());

     cv::divide(ones, dc/A, t);

   return recover(I, A, t);

}


int main(int argc, char *argv[]) {

    uchar A = 220;

    string in_file (argv[1]);

    auto I = imread(in_file); //C++11 auto

    Mat&& dst = dehaze(I, 15, A);  

    imwrite("out_" + in_file, dst);

}


uchar min_omega(const Mat& dc, int patch_size, int x, int y) {
    double tmp_min;
    Mat tmp_img;
    getRectSubPix(dc, Size(patch_size, patch_size), Point(x, y), tmp_img);
    minMaxLoc(tmp_img, &tmp_min);
    return (uchar)tmp_min;
}

uchar min_c(uchar a, uchar b, uchar c) {
    return std::min(a,min(b,c));
}
    
Mat dark_channel(const Mat& I, int patch_size){
    Mat dc;
    dc.create(I.size(), CV_8U);
    for (int y = 0; y < dc.rows; ++y) {
        const uchar *p = I.ptr<uchar>(y);
        for (int x = 0; x < dc.cols; ++x) {
            dc.at<uchar>(y, x) = min_c(p[3*x],p[3*x+1], p[3*x+2]);
        }
    }
    
    Mat dc2;
    dc2.create(dc.size(), dc.type());
    for (int y = 0; y < dc.row; y++){
        for (int x = 0; x < dc.cols; x++){
            uchar tmpmin = min_omega(dc, patch_size, x, y);
            dc2.at<uchar>(y, x) = (uchar)tmpmin;      
        }
    }
    return std::move(dc2);
}


    


    



Paper download





























你可能感兴趣的:(dehaze paper reading 1)