直方圖均衡化在圖像增強方面有着很重要的應用。一些拍攝得到的圖片,我們從其直方圖可以看出,它的分布是集中於某些灰度區間,這導致人在視覺上感覺這張圖的對比度不高。所以,對於這類圖像,我們可以通過直方圖均衡技術,將圖像的灰度分布變得較為均勻,從而使得圖像對比度增大,視覺效果更佳。
直方圖均衡化的代碼實現有以下幾個步驟:
- 遍歷全圖,先統計每個灰度級下的像素點個數(為此我們開辟了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;
}
這里還分享一段代碼,就是如何畫灰度直方圖
void show_histogram(Mat& img)
{
//為計算直方圖配置變量
//首先是需要計算的圖像的通道,就是需要計算圖像的哪個通道(bgr空間需要確定計算 b或g貨r空間)
int channels = 0;
//然后是配置輸出的結果存儲的 空間 ,用MatND類型來存儲結果
MatND dstHist;
//接下來是直方圖的每一個維度的 柱條的數目(就是將數值分組,共有多少組)
int histSize[] = { 256 }; //如果這里寫成int histSize = 256; 那么下面調用計算直方圖的函數的時候,該變量需要寫 &histSize
//最后是確定每個維度的取值范圍,就是橫坐標的總數
//首先得定義一個變量用來存儲 單個維度的 數值的取值范圍
float midRanges[] = { 0, 256 };
const float *ranges[] = { midRanges };
calcHist(&img, 1, &channels, Mat(), dstHist, 1, histSize, ranges, true, false);
//calcHist 函數調用結束后,dstHist變量中將儲存了 直方圖的信息 用dstHist的模版函數 at<Type>(i)得到第i個柱條的值
//at<Type>(i, j)得到第i個並且第j個柱條的值
//開始直觀的顯示直方圖——繪制直方圖
//首先先創建一個黑底的圖像,為了可以顯示彩色,所以該繪制圖像是一個8位的3通道圖像
Mat drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
//因為任何一個圖像的某個像素的總個數,都有可能會有很多,會超出所定義的圖像的尺寸,針對這種情況,先對個數進行范圍的限制
//先用 minMaxLoc函數來得到計算直方圖后的像素的最大個數
double g_dHistMaxValue;
minMaxLoc(dstHist, 0, &g_dHistMaxValue, 0, 0);
//將像素的個數整合到 圖像的最大范圍內
//遍歷直方圖得到的數據
for (int i = 0; i < 256; i++)
{
int value = cvRound(dstHist.at<float>(i) * 256 * 0.9 / g_dHistMaxValue);
line(drawImage, Point(i, drawImage.rows - 1), Point(i, drawImage.rows - 1 - value), Scalar(255, 255, 255));
}
imshow("【原圖直方圖】", drawImage);
}
測試圖片以及其灰度直方圖:
直方圖均衡化后,圖像對比度顯著增加,圖像增強效果明顯。