【影象處理】彩色影象自適應對比度增強(OpenCV實現)
提到影象增強,第一印象就是直方圖均衡與直方圖規定化,這是最常見的也是非常有效的全域性影象增強方法。在前不久的一次組會討論中,課題組的一位同學提到了“自適應影象增強”,雖然自己以前也用過,但是一時間忘記了原理,就去複習了一下,其實他使用的方法的全稱應該叫自適應直方圖均衡,對應的是Matlab 中的adapthisteq 函式;
我在複習的過程中,偶然發現了另一個影象增強的演算法,也就是這篇文章中要提到的——自適應對比度增強(Adaptive Contrast Enhancement,ACE),下面就從原理到實現,來好好聊一下這種方法。
自適應對比度增強
在影象處理的方法中,自適應方法是與影象本身資訊相關,根據影象對圖特徵對影象進行處理的一系列方法,這些方法往往具有更好的魯棒性、普適性。而本文中提到的這種ACE方法由等人在《Real-Time Adaptive Contrast Enhancement》中提到,原理簡單易懂,有興趣的朋友可以點選連結去閱讀。
對於影象中的每一個點,分別計算其區域性均值與區域性標準差;
上述式子中,代表座標為的點的畫素值,為以點為中心,視窗大小為的區域的區域性均值,對應的為區域性的方差,為區域性影象的標準差。
在求得區域性均值與標準差後,就可以對影象進行增強了,具體的公式如下;
上式中,為增強後的畫素值,為全域性均值(你也可以把它設為某一合理數值),是一個係數引數,一般取小於1大於0的小數。
再來分析一下,上面式子的含義;如果將每個點的區域性均值構成一張圖,其實就是均值濾波的結果,而在《數字影象傅立葉變換的物理意義及簡單應用》中,我提到過均值濾波是一種低通濾波,獲得的是影象的低頻部分,也就是背景部分,就可以用來量化影象中的一個點是高頻還是低頻。而在一般情況下, 都是大於1的,所以通過 可以實現對影象的高頻部分的放大,進而對影象進行增強。
再來看看引數,經過上面的過程可以看出,如果 是一個固定引數,比如都取5,通過式子中的區域性均值,我們已經能夠將影象實現一定程度上的自適應增強了。那麼為什麼還要在引數G中引入標準差呢?
我們回憶一下對比度增強的初衷,對比度增強是為了讓本身對比度不強的影象的對比度變得明顯,而對本身對比度很強的影象,是沒必要做增強的。那麼在同一影象中,我們尤其需要增強對比度不強的部分。而方差表示的是影象的畫素值的均勻性,我們可以認為方差越大的區域性區域,其畫素值越不均勻,對比度越強;反之,方差越小的區域性區域,其畫素值越均勻,對比度越弱。因此,在引數中除以了局部標準差,可以讓影象中對比度較弱的部分的增強效果更加明顯。
其次,如果對整張影象中所有點進行等比例增強,影象中本身就是高頻的部分出現過增強的現象,影象看起來十分奇怪。
彩色影象的ACE
在網上看到有人說,對彩色影象增強可以分別對RGB三通道進行增強後進行合併。這個觀點是錯誤的,因為分別對各個通道進行增強,會引起影象色相的變化,影象會變的不是其原來的顏色了。
所以需要將影象轉到HSI色彩空間,或者是YCrCb顏色空間。前者只需要對I亮度通道進行增強,而H、S分別代表的色調和飽和度通道不需要變化。後者用Y通道表示亮度,只需要對Y通道進行增強即可。增強之後再合併通道,轉換回RGB空間便完成了對彩色影象的增強。
程式碼
在這裡我只提供一個adaptContrastEnhancement函式的程式碼,完整的程式碼也很簡單,可以點選下載。無C幣的可以直接在部落格留言,我也會發送到你的郵箱。
//--------------------------
//Adaptive Contrast Enhancement(自適應對比度增強,ACE)
//不用先生,2018.11.08
//
//函式功能:獲取影象的區域性均值與區域性標準差的圖
//函式名稱:adaptContrastEnhancement
//函式引數:Mat &scr:輸入影象,為三通道RGB影象;
//函式引數:Mat &dst:增強後的輸出影象,為三通道RGB影象;
//函式引數:int winSize:區域性均值的視窗大小,應為單數;
//函式引數:int maxCg:增強幅度的上限;
//返回型別:bool
//--------------------
bool adaptContrastEnhancement(Mat &scr, Mat &dst, int winSize,int maxCg)
{
if (!scr.data) //判斷影象是否被正確讀取;
{
cerr << "自適應對比度增強函式讀入圖片有誤";
return false;
}
Mat ycc; //轉換空間到YCrCb;
cvtColor(scr, ycc, COLOR_RGB2YCrCb);
vector<Mat> channels(3); //分離通道;
split(ycc, channels);
Mat localMeansMatrix(scr.rows , scr.cols , CV_32FC1);
Mat localVarianceMatrix(scr.rows , scr.cols , CV_32FC1);
if (!getVarianceMean(channels[0], localMeansMatrix, localVarianceMatrix, winSize)) //對Y通道進行增強;
{
cerr << "計算影象均值與標準差過程中發生錯誤";
return false;
}
Mat temp = channels[0].clone();
Scalar mean;
Scalar dev;
meanStdDev(temp, mean, dev);
float meansGlobal = mean.val[0];
Mat enhanceMatrix(scr.rows, scr.cols, CV_8UC1);
for (int i = 0; i < scr.rows; i++) //遍歷,對每個點進行自適應調節
{
for (int j = 0; j < scr.cols; j++)
{
if (localVarianceMatrix.at<float>(i, j) >= 0.01)
{
float cg = 0.2*meansGlobal / localVarianceMatrix.at<float>(i, j);
float cgs = cg > maxCg ? maxCg : cg;
cgs = cgs < 1 ? 1 : cgs;
int e = localMeansMatrix.at<float>(i, j) + cgs* (temp.at<uchar>(i, j) - localMeansMatrix.at<float>(i, j));
if (e > 255){ e = 255; }
else if (e < 0){ e = 0; }
enhanceMatrix.at<uchar>(i, j) = e;
}
else
{
enhanceMatrix.at<uchar>(i, j) = temp.at<uchar>(i, j);
}
}
}
channels[0] = enhanceMatrix; //合併通道,轉換顏色空間回到RGB
merge(channels, ycc);
cvtColor(ycc, dst, COLOR_YCrCb2RGB);
}
執行結果
可以明顯看出,影象中原先對比度不強的部分,如樹幹、江面、遠景。經過增強後,都有了明顯的改善,而本身對比度已經明顯的區域,就沒有太大的改變。
已完。。