重磅乾貨,第一時間送達
1. 前言今天為大家帶來一篇之前看到的用於單幅圖像去霧的算法,作者來自清華大學,論文原文見附錄。
2. 霧天退化模型在計算機視覺領域,通常使用霧天圖像退化模型來描述霧霾等惡劣天氣條件對圖像造成的影響,該模型是McCartney首先提出。該模型包括衰減模型和環境光模型兩部分。模型表達式為:
其中,
公式(1)中的
由此可見,圖像去霧過程就是根據
對於暗通道去霧算法來說,先從暗原色通道中選取最亮的0.1%比例的像素點,然後選取原輸入圖像中這些像素具有的最大灰度值作為全局大氣光值
然後根據公式(2)可以得出:
首先可以確定的是
根據(4)和(5)推出:
因此初略估計透射率的計算公式:
最後為了保證圖片的自然性,增加一個參數
好了,上面複習完了何凱明博士的暗通道去霧,我們一起來看看清華大學這篇論文。
3. 算法流程算法流程實際上有了這個算法流程就可以寫出代碼了,不過為了加深理解可以看下面的一些推導。
4. 一些推導我們知道去霧的步驟主要就是估計全局大氣光值
從第二節的介紹我們知道
然後這篇論文使用了
。
我們取
所以公式2變換為
對公式(4)右邊進行均值濾波:
其中
均值濾波後的結果可以反映
其中,因此
為了防止去霧後圖像出現整體畫面偏暗,這裡根據圖像的均值來調整
其中
因此可以得到透射率的計算公式:
結合公式(1)推出:
。
4.2 估計全球大氣光值公式(5)中第一個等式左側的表達式取值範圍為
一般情況下又存在
(KaiMing He的暗通道先驗理論)。這樣就初步確定了全局大氣光的範圍,為了能快速獲取全局大氣光,文章直接取兩者的平均值作為全局大氣光值,即:
...(9)。
然後大氣光值
下面是代碼實現。
#include <opencv2/opencv.hpp>#include <iostream>#include <algorithm>#include <vector>using namespace cv;using namespace std;
int getMax(Mat src) { int row = src.rows; int col = src.cols; int temp = 0; for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { temp = max((int)src.at<uchar>(i, j), temp); } if (temp == 255) return temp; } return temp;}
Mat dehaze(Mat src) { double eps; int row = src.rows; int col = src.cols; Mat M = Mat::zeros(row, col, CV_8UC1); Mat M_max = Mat::zeros(row, col, CV_8UC1); Mat M_ave = Mat::zeros(row, col, CV_8UC1); Mat L = Mat::zeros(row, col, CV_8UC1); Mat dst = Mat::zeros(row, col, CV_8UC3); double m_av, A; //get M double sum = 0; for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { uchar r, g, b, temp1, temp2; b = src.at<Vec3b>(i, j)[0]; g = src.at<Vec3b>(i, j)[1]; r = src.at<Vec3b>(i, j)[2]; temp1 = min(min(r, g), b); temp2 = max(max(r, g), b); M.at<uchar>(i, j) = temp1; M_max.at<uchar>(i, j) = temp2; sum += temp1; } } m_av = sum / (row * col * 255); eps = 0.85 / m_av; boxFilter(M, M_ave, CV_8UC1, Size(51, 51)); double delta = min(0.9, eps*m_av); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { L.at<uchar>(i, j) = min((int)(delta * M_ave.at<uchar>(i, j)), (int)M.at<uchar>(i, j)); } } A = (getMax(M_max) + getMax(M_ave)) * 0.5; for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { int temp = L.at<uchar>(i, j); for (int k = 0; k < 3; k++) { int val = A * (src.at<Vec3b>(i, j)[k] - temp) / (A - temp); if (val > 255) val = 255; if (val < 0) val = 0; dst.at<Vec3b>(i, j)[k] = val; } } } return dst;}
int main() { Mat src = imread("F:\\fog\\1.jpg"); Mat dst = dehaze(src); cv::imshow("origin", src); cv::imshow("result", dst); cv::imwrite("F:\\fog\\res.jpg", dst); waitKey(0); return 0;}6. 結果原圖1結果圖1原圖2結果圖2原圖3結果圖3原圖4結果圖4原圖5結果圖57. 結論算法裡面有2個參數可以自己調節,濾波的半徑和
8. 參考https://blog.csdn.net/u013684730/article/details/76640321https://www.cnblogs.com/Imageshop/p/3410279.html。具體如何調節?我就不放在這裡說了,這個算法後面會在我的新專題裡面進行一遍優化,到時候再來回答這個問題。如果你迫切需要這個算法的實現或者對它感興趣,可以自己嘗試調整這兩個參數獲得想要的效果。這裡的均值濾波也可以換成我們之前講的Side Window Filter說不定可以獲得更好的效果。