原标題:專欄 | 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;
}
效果
均是原圖和算法處理後的結果的順序,可以看到這個算法得到了還不錯的結果。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CM2YTYmdzMlZWO0YGO4IjZ3MGMmlTYkJmZxAjM5MGN28CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
附錄
責任編輯: