1. 背景
直方圖均衡化在圖像增強方面有着很重要的應用。一些拍攝得到的圖片,我們從其直方圖可以看出,它的分布是集中於某些灰度區間,這導致人在視覺上感覺這張圖的對比度不高。所以,對於這類圖像,我們可以通過直方圖均衡技術,將圖像的灰度分布變得較為均勻,從而使得圖像對比度增大,視覺效果更佳。
2. 原理
直方圖均衡化的作用是圖像增強。
有兩個問題比較難懂,一是為什么要選用累積分布函數,二是為什么使用累積分布函數處理后像素值會均勻分布。
第一個問題。均衡化過程中,必須要保證兩個條件:①像素無論怎么映射,一定要保證原來的大小關系不變,較亮的區域,依舊是較亮的,較暗依舊暗,只是對比度增大,絕對不能明暗顛倒;②如果是八位圖像,那么像素映射函數的值域應在0和255之間的,不能越界。綜合以上兩個條件,累積分布函數是個好的選擇,因為累積分布函數是單調增函數(控制大小關系),並且值域是0到1(控制越界問題),所以直方圖均衡化中使用的是累積分布函數。
第二個問題。累積分布函數具有一些好的性質,那么如何運用累積分布函數使得直方圖均衡化?比較概率分布函數和累積分布函數,前者的二維圖像是參差不齊的,后者是單調遞增的。直方圖均衡化過程中,映射方法是
其中,n是圖像中像素的總和,$n_k$是當前灰度級的像素個數,L是圖像中可能的灰度級總數。
來看看通過上述公式怎樣實現的拉伸。假設有如下圖像:
得圖像的統計信息如下圖所示,並根據統計信息完成灰度值映射:
映射后的圖像如下所示:
3. C++實現
直方圖均衡化的代碼實現有以下幾個步驟:
- 遍歷全圖,先統計每個灰度級下的像素點個數(為此我們開辟了256大小的數組);
- 計算每個灰度級的像素點占總像素的點的比例;
- 按照第二步求出的比例重新計算每個灰度級下的新的灰度值,即均衡化;
- 依照新的灰度值表遍歷更新圖像的灰度值。
int gray[256] = { 0 }; //記錄每個灰度級別下的像素個數
double gray_prob[256] = { 0 }; //記錄灰度分布密度
double gray_distribution[256] = { 0 }; //記錄累計密度
int gray_equal[256] = { 0 }; //均衡化后的灰度值
int gray_sum = 0; //像素總數
Mat equalize_hist(Mat& input)
{
Mat output = input.clone();
gray_sum = input.cols * input.rows;
//統計每個灰度下的像素個數
for (int i = 0; i < input.rows; i++)
{
uchar* p = input.ptr<uchar>(i);
for (int j = 0; j < input.cols; j++)
{
int vaule = p[j];
gray[vaule]++;
}
}
//統計灰度頻率
for (int i = 0; i < 256; i++)
{
gray_prob[i] = ((double)gray[i] / gray_sum);
}
//計算累計密度
gray_distribution[0] = gray_prob[0];
for (int i = 1; i < 256; i++)
{
gray_distribution[i] = gray_distribution[i-1] +gray_prob[i];
}
//重新計算均衡化后的灰度值,四舍五入。參考公式:(N-1)*T+0.5
for (int i = 0; i < 256; i++)
{
gray_equal[i] = (uchar)(255 * gray_distribution[i] + 0.5);
}
//直方圖均衡化,更新原圖每個點的像素值
for (int i = 0; i < output.rows; i++)
{
uchar* p = output.ptr<uchar>(i);
for (int j = 0; j < output.cols; j++)
{
p[j] = gray_equal[p[j]];
}
}
return output;
}