前言
這是OpenCV圖像處理專欄的第十二篇文章,今天為大家介紹一個用于解決光照不均勻的圖像自适應校正算法。光照不均勻其實是非常常見的一種狀況,為了提升人類的視覺感受或者是為了提升諸如深度學習之類的算法準确性,人們在解決光照不均衡方面已經有大量的工作。一起來看看這篇論文使用的算法吧,論文名為:《基于二維伽馬函數的光照不均勻圖像自适應校正算法》。
算法原理
論文使用了Retinex的多尺度高斯濾波求取
「光照分量」,然後使用了二
「維Gamma函數」針對原圖的
「HSV空間的V(亮度)分量」進行亮度改變,得到結果。原理還是蠻簡單的,因為是中文論文,且作者介紹得很清楚,我就不細說了,可以自己看論文,論文位址見附錄。本文的重點在于對算法步驟的解讀和OpenCV複現。
算法步驟

需要注意的點
文中公式5(二維Gamma變換) 有誤,公式5為:
其中
的指數應該是
,而不是
,如果使用後者會得到錯誤結果,應該是作者筆誤了。
OpenCV C++代碼複現
Mat RGB2HSV(Mat src) {
int row = src.rows;
int col = src.cols;
Mat dst(row, col, CV_32FC3);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
float b = src.at<Vec3b>(i, j)[0] / 255.0;
float g = src.at<Vec3b>(i, j)[1] / 255.0;
float r = src.at<Vec3b>(i, j)[2] / 255.0;
float minn = min(r, min(g, b));
float maxx = max(r, max(g, b));
dst.at<Vec3f>(i, j)[2] = maxx; //V
float delta = maxx - minn;
float h, s;
if (maxx != 0) {
s = delta / maxx;
}
else {
s = 0;
}
if (r == maxx) {
h = (g - b) / delta;
}
else if (g == maxx) {
h = 2 + (b - r) / delta;
}
else {
h = 4 + (r - g) / delta;
}
h *= 60;
if (h < 0)
h += 360;
dst.at<Vec3f>(i, j)[0] = h;
dst.at<Vec3f>(i, j)[1] = s;
}
}
return dst;
}
Mat HSV2RGB(Mat src) {
int row = src.rows;
int col = src.cols;
Mat dst(row, col, CV_8UC3);
float r, g, b, h, s, v;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
h = src.at<Vec3f>(i, j)[0];
s = src.at<Vec3f>(i, j)[1];
v = src.at<Vec3f>(i, j)[2];
if (s == 0) {
r = g = b = v;
}
else {
h /= 60;
int offset = floor(h);
float f = h - offset;
float p = v * (1 - s);
float q = v * (1 - s * f);
float t = v * (1 - s * (1 - f));
switch (offset)
{
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
default:
break;
}
}
dst.at<Vec3b>(i, j)[0] = int(b * 255);
dst.at<Vec3b>(i, j)[1] = int(g * 255);
dst.at<Vec3b>(i, j)[2] = int(r * 255);
}
}
return dst;
}
Mat work(Mat src) {
int row = src.rows;
int col = src.cols;
Mat now = RGB2HSV(src);
Mat H(row, col, CV_32FC1);
Mat S(row, col, CV_32FC1);
Mat V(row, col, CV_32FC1);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
H.at<float>(i, j) = now.at<Vec3f>(i, j)[0];
S.at<float>(i, j) = now.at<Vec3f>(i, j)[1];
V.at<float>(i, j) = now.at<Vec3f>(i, j)[2];
}
}
int kernel_size = min(row, col);
if (kernel_size % 2 == 0) {
kernel_size -= 1;
}
float SIGMA1 = 15;
float SIGMA2 = 80;
float SIGMA3 = 250;
float q = sqrt(2.0);
Mat F(row, col, CV_32FC1);
Mat F1, F2, F3;
GaussianBlur(V, F1, Size(kernel_size, kernel_size), SIGMA1 / q);
GaussianBlur(V, F2, Size(kernel_size, kernel_size), SIGMA2 / q);
GaussianBlur(V, F3, Size(kernel_size, kernel_size), SIGMA3 / q);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
F.at <float>(i, j) = (F1.at<float>(i, j) + F2.at<float>(i, j) + F3.at<float>(i, j)) / 3.0;
}
}
float average = mean(F)[0];
Mat out(row, col, CV_32FC1);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
float gamma = powf(0.5, (average - F.at<float>(i, j)) / average);
out.at<float>(i, j) = powf(V.at<float>(i, j), gamma);
}
}
vector <Mat> v;
v.push_back(H);
v.push_back(S);
v.push_back(out);
Mat merge_;
merge(v, merge_);
Mat dst = HSV2RGB(merge_);
return dst;
}
效果
結論
可以看到這個算法對光照不均勻的圖像校正效果還是不錯的,且沒有像Retiex那樣在亮度突變處出現色暈現象。
附錄
- 論文原文:https://wenku.baidu.com/view/3570f2c255270722182ef74e.html
同期文章
- OpenCV圖像處理專欄一 | 盤點常見顔色空間互轉
- OpenCV圖像處理專欄二 |《Local Color Correction 》論文閱讀及C++複現
- OpenCV圖像處理專欄三 | 灰階世界算法原理和實作
- OpenCV圖像處理專欄四 | 自動白平衡之完美反射算法原理及C++實作
- OpenCV圖像處理專欄五 | ACE算法論文解讀及實作
- OpenCV圖像處理專欄六 | 來自何凱明博士的暗通道去霧算法(CVPR 2009最佳論文)
- OpenCV圖像處理專欄七 | 直方圖均衡化算法及代碼實作
- OpenCV圖像處理專欄八 | 《Contrast image correction method》 論文閱讀及代碼實作
- OpenCV圖像處理專欄九 | 基于直方圖的快速中值濾波算法
- OpenCV圖像處理專欄十 | 利用中值濾波進行去霧
- OpenCV圖像處理專欄十一 | IEEE Xplore 2015的圖像白平衡處理之動态門檻值法
歡迎關注GiantPandaCV, 在這裡你将看到獨家的深度學習分享,堅持原創,每天分享我們學習到的新鮮知識。( • ̀ω•́ )✧
有對文章相關的問題,或者想要加入交流群,歡迎添加BBuf微信:
https://u.wechat.com/MPWFDnmCPu6zgf5YUtdpT_U (二維碼自動識别)