直方圖均衡
1 原理
首先定義幾個變量,其實這幾個變量在前面幾篇關於直方圖變換的文章中同樣應該定義。
變量 | 意義 |
---|---|
灰度級爲的像素的個數 | |
第級灰度 | |
灰度級爲的像素的個數 | |
灰度級爲的概率估計值 | |
總像素數 |
由此可見:
直方圖均衡化是一種灰度變換,經過該變換之後,圖像的直方圖分佈基本符合均勻分佈。
設變換的輸出爲灰度,那麼直方圖均衡的變換可以表達爲:
每一個灰度級經過函數變換之後都對應一個輸出灰度級,同時滿足兩個條件:
- 在區間上是單值且單調遞增函數;
- 當時,;
第一條約束要求輸入越高的灰度級,變換之後仍然是越高的灰度級,不會出現亮暗翻轉的情況。第二條約束要求變換之後,灰度級仍然在有效範圍之內。
用和表示灰度級爲和的概率密度,
其中是積分變量。公式右邊是概率密度函數的積分,總爲正,因此滿足條件一,當到達積分上限時,函數的值爲1,滿足條件二。
這就說明了經過變換之後,輸出灰度級在上服從均勻分佈。
將變換離散化之後,得到離散形式下的變換關係:
其中,爲圖像的寬高,爲圖像的總灰度級。
2 Matlab實現
2.1 Matlab已有函數
Matlab中使用函數histeq
進行直方圖均衡化。注意,histeq
默認情況下,直方圖均衡時灰度等級會被劃分到64個子級中,這導致直接使用默認參數調用時結果的直方圖與手動計算的結果差異較大。
2.2 手動實現
clc;
clear;
close all;
% 對灰度圖進行灰度線性變換
ori_img = imread('../images/18.jpg');
ori_img1 = rgb2gray(ori_img);
[oriHist,oriX] = imhist(ori_img1);
p = oriHist;
all = sum(oriHist);
for i=1:1:256
p(i) = sum(oriHist(1:i));
end
% p = 255*p;
% p = uint8(p);
[width,height] = size( ori_img1);
gray1 = ori_img1;
for i=1:1:width
for j = 1:1:height
gray1(i,j) = 255*p(gray1(i,j)+1)/all;
end
end
[g1Hist,g1X] = imhist(gray1);
g2 = histeq(ori_img1,256);
[g2Hist,g2X] = imhist(g2);
figure(1),subplot(1,3,1),imshow(ori_img1),title('原圖');subplot(1,3,2),imshow(gray1),title('手動實現直方圖均衡化');subplot(1,3,3),imshow(g2),title('histeq');
figure(3),subplot(1,3,1),stem(oriX,oriHist),title('原圖直方圖');subplot(1,3,2),stem(g1X,g1Hist),title('手動實現直方圖均衡化');subplot(1,3,3),stem(g2X,g2Hist),title('histeq');
3 OpenCV實現
3.1 OpenCV已有函數
直方圖均衡化在OpenCV
中使用函數equalizeHist
函數完成。
/** @brief Equalizes the histogram of a grayscale image.
The function equalizes the histogram of the input image using the following algorithm:
- Calculate the histogram \f$H\f$ for src .
- Normalize the histogram so that the sum of histogram bins is 255.
- Compute the integral of the histogram:
\f[H'_i = \sum _{0 \le j < i} H(j)\f]
- Transform the image using \f$H'\f$ as a look-up table: \f$\texttt{dst}(x,y) = H'(\texttt{src}(x,y))\f$
The algorithm normalizes the brightness and increases the contrast of the image.
@param src Source 8-bit single channel image.
@param dst Destination image of the same size and type as src .
*/
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );
3.2 C++ 手動實現
創建灰度映射表,具體變換過程使用opencv的LUT函數執行。
void CreateHELT(cv::Mat& hist, cv::Mat&lut)
{
cv::Mat data(1,256,CV_32FC1,cv::Scalar(0));
lut = cv::Mat(1, 256, CV_8UC1);
for (int i = 0; i < data.cols; i++)
{
if (i == 0)
{
data.at<float>(i) = hist.at<float>(i);
}
else
{
data.at<float>(i) = data.at<float>(i-1)+hist.at<float>(i);
}
float f = 255 * data.at<float>(i);
if (f < 0)
f = 0;
else if (f > 255)
f = 255;
lut.at<uchar>(i) = (uchar)(f);
}
}
4 效果圖
histeq
使用256級灰度時的結果:
histeq
使用默認參數調用時的計算結果:
從結果可以看出,histeq的灰度級明顯少。
5 參考
https://blog.csdn.net/timeless_2014/article/details/80389433
https://www.cnblogs.com/whw19818/p/5790680.html