天天看點

opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧

原标題:專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧

前言

這是OpenCV圖像處理專欄的第十篇文章,介紹一種利用中值濾波來實作去霧的算法。這個方法發表于國内的一篇論文,連結我放附錄了。

算法原理

這個算法和之前He Kaiming的暗通道去霧都基于大氣散射模型即: 其中 就是輸入圖像,需要求去霧後的輸出圖像 ,是以我們隻要計算出全局大氣光值 和透射率 就可以了。其他的一些介紹和背景交代可以去看原文,這裡我直接給出論文的算法核心步驟。

1、定義 ,焦作大氣光幕或者霧濃度。

2、計算,即是求暗通道,這一點在 OpenCV圖像處理專欄六 | 來自何凱明博士的暗通道去霧算法(CVPR 2009最佳論文) 我已經詳細說明了。

3、計算,即對 進行中值濾波得到 。

4、計算,注意式子中取了絕對值。

5、計算,式子中 是控制去霧濃度的系數,取值為 。

6、通過式子獲得去霧後的圖像,這個式子就是把原始子移項變形得到的。

7、自此,算法結束,得到了利用中值濾波實作的去霧後的結果。

int rows, cols;

//擷取最小值矩陣

int **getMinChannel(cv::Mat img) {

rows = img.rows;

cols = img.cols;

if (img.channels != 3) {

fprintf(stderr, "Input Error!");

exit(-1);

}

int **imgGray;

imgGray = new int *[rows];

for (int i = 0; i < rows; i++) {

imgGray[i] = new int[cols];

}

for (int i = 0; i < rows; i++) {

for (int j = 0; j < cols; j++) {

int loacalMin = 255;

for (int k = 0; k < 3; k++) {

if (img.at(i, j)[k] < loacalMin) {

loacalMin = img.at(i, j)[k];

}

}

imgGray[i][j] = loacalMin;

}

}

return imgGray;

}

//求暗通道

int **getDarkChannel(int **img, int blockSize = 3) {

if (blockSize % 2 == 0 || blockSize < 3) {

fprintf(stderr, "blockSize is not odd or too small!");

exit(-1);

}

//計算pool Size

int poolSize = (blockSize - 1) / 2;

int newHeight = rows + poolSize - 1;

int newWidth = cols + poolSize - 1;

int **imgMiddle;

imgMiddle = new int *[newHeight];

for (int i = 0; i < newHeight; i++) {

imgMiddle[i] = new int[newWidth];

}

for (int i = 0; i < newHeight; i++) {

for (int j = 0; j < newWidth; j++) {

if (i < rows && j < cols) {

imgMiddle[i][j] = img[i][j];

}

else {

imgMiddle[i][j] = 255;

}

}

}

int **imgDark;

imgDark = new int *[rows];

for (int i = 0; i < rows; i++) {

imgDark[i] = new int[cols];

}

int localMin = 255;

for (int i = poolSize; i < newHeight - poolSize; i++) {

for (int j = poolSize; j < newWidth - poolSize; j++) {

for (int k = i - poolSize; k < i + poolSize + 1; k++) {

for (int l = j - poolSize; l < j + poolSize + 1; l++) {

if (imgMiddle[k][l] < localMin) {

localMin = imgMiddle[k][l];

}

}

}

imgDark[i - poolSize][j - poolSize] = localMin;

}

}

return imgDark;

}

Mat MedianFilterFogRemoval(Mat src, float p = 0.95, int KernelSize = 41, int blockSize=3, bool meanModel = false, float percent = 0.001) {

int row = src.rows;

int col = src.cols;

int** imgGray = getMinChannel(src);

int **imgDark = getDarkChannel(imgGray, blockSize = blockSize);

//int atmosphericLight = getGlobalAtmosphericLightValue(imgDark, src, meanModel = meanModel, percent = percent);

int Histgram[256] = { 0 };

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

Histgram[imgDark[i][j]]++;

}

}

int Sum = 0, atmosphericLight = 0;

for (int i = 255; i >= 0; i--) {

Sum += Histgram[i];

if (Sum > row * col * 0.01) {

atmosphericLight = i;

break;

}

}

int SumB = 0, SumG = 0, SumR = 0, Amount = 0;

//printf("%dn", atmosphericLight);

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

if (imgDark[i][j] >= atmosphericLight) {

SumB += src.at(i, j)[0];

SumG += src.at(i, j)[1];

SumR += src.at(i, j)[2];

Amount++;

}

}

}

SumB /= Amount;

SumG /= Amount;

SumR /= Amount;

Mat Filter(row, col, CV_8UC1);

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

Filter.at(i, j) = imgDark[i][j];

}

}

Mat A(row, col, CV_8UC1);

medianBlur(Filter, A, KernelSize);

Mat temp(row, col, CV_8UC1);

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

int Diff = Filter.at(i, j) - A.at(i, j);

if (Diff < 0) Diff = -Diff;

temp.at(i, j) = Diff;

}

}

medianBlur(temp, temp, KernelSize);

Mat B(row, col, CV_8UC1);

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

int Diff = A.at(i, j) - temp.at(i, j);

if (Diff < 0) Diff = 0;

B.at(i, j) = Diff;

}

}

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

int Min = B.at(i, j) * p;

if (imgDark[i][j] > Min) {

B.at(i, j) = Min;

}

else {

B.at(i, j) = imgDark[i][j];

}

}

}

Mat dst(row, col, CV_8UC3);

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

int F = B.at(i, j);

int Value;

if (SumB != F) {

Value = SumB * (src.at(i, j)[0] - F) / (SumB - F);

}

else {

Value = src.at(i, j)[0];

}

if (Value < 0) Value = 0;

else if (Value > 255) Value = 255;

dst.at(i, j)[0] = Value;

if (SumG != F) {

Value = SumG * (src.at(i, j)[1] - F) / (SumG - F);

}

else {

Value = src.at(i, j)[1];

}

if (Value < 0) Value = 0;

else if (Value > 255) Value = 255;

dst.at(i, j)[1] = Value;

if (SumR != F) {

Value = SumR * (src.at(i, j)[2] - F) / (SumR - F);

}

else {

Value = src.at(i, j)[2];

}

if (Value < 0) Value = 0;

else if (Value > 255) Value = 255;

dst.at(i, j)[2] = Value;

}

}

return dst;

}

效果

均是原圖和算法處理後的結果的順序,可以看到這個算法得到了還不錯的結果。

opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
opencv 圖像霧檢測_專欄 | OpenCV圖像處理專欄十 | 利用中值濾波進行去霧

附錄

責任編輯: