I(x) = J(x) * t(x) + (1 - t(x)) *A (1)
編程
I(x):咱們觀測的圖像。ide
J(x):就是那個客觀存在的圖像,對於去霧的案例,能夠認爲是沒有被霧擋住的圖像。函數
t:爲大氣的傳輸過程。spa
A:爲空氣中的光。
get
x:爲像素的位置。
string
就是說咱們看到的圖像是空氣的光線和真實圖像的混合。這個混合的比例爲t。it
去霧就是在已知觀測圖像I(x)下恢復真實圖像J(x)。io
樸素的想法:opencv
J(x) = (I(x)-A)/t(x) + A
pdf
所以問題就變成要估計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);
}