使用C++實現彩色影象直方圖均衡化的三種方法
阿新 • • 發佈:2018-12-16
引言
本文主要介紹如何實現彩色影象的直方圖均衡化,達到影象增強效果的三種方法:
1. 對RGB三個通道影象分別進行直方圖均衡化,然後再合併三個通道;
2. 提取RGB三個通道影象,計算其平均直方圖結果,然後再進行均衡化;
3. RGB空間轉為HSI空間影象,對I(亮度,Intensity)通道進行直方圖均衡化,再轉為RGB影象。
第一種方法不推薦,會破壞色彩結構;根據情況選擇第2,3種方法。
先導知識
直方圖
首先必須瞭解灰度直方圖是什麼東西。直方圖就是統計一個影象各灰度級值0~255有多少各畫素點。
單通道均衡化
在實現之前先了解單通道影象(灰度圖)直方圖均衡化和其過程。
上述過程就是求出一個原畫素點到均衡化的畫素點的一個對映函式 f(x)。利用該對映函式就可以迴圈掃描影象的每一個畫素x,然後得到f(x)替代原來的畫素值x,達到均衡化的結果。
在實際均衡化過程中,核心步驟如下:
1. 傳入單通道影象和該的灰度直方圖。
2. 遍歷灰度直方圖,對值 I 從 0~255 累計求和,對累計求和,對和結果除以影象的素數,然後乘以 L-1得到函式結果 得到函式結果 f(xi )。
3. 將每一個灰度值 i累積求和的結果 累積求和的結果 f(xi )儲存在一個大小為 256 的 int 陣列中,儲存的位置為 i;
4. 遍歷輸入影象每一個素,記點值為 x,修改其畫素值使等於 f(x)。
5. 均衡化結束。
演算法實現
單通道均衡化
主要步驟如下,EqualizedResult就是均衡化的對映函式,下一步驟就是將畫素點值作為對映函式的輸入,將輸出結果替代原來的畫素值。
// 功能:對灰度直方圖進行均衡化 // 輸入:單通道CImg物件, 灰度圖物件 // 輸出:均衡化結果 CImg<unsigned int> HistogramEqualization::HistogramEqualizationMethod (CImg<unsigned int> InputImage,CImg<unsigned int> Histogram) { int L = 256; //灰度級 int NumOfPixels = (InputImage)._width * (InputImage)._height; double CumulativeDistributionFunction[256] = { 0 }; // 灰度直方圖的累積分佈 double EqualizedResult[256] = { 0 }; // 均衡化結果對映函式 /*直方圖就對應於概率密度函式pdf, 而概率分佈函式cdf就是直方圖的累積和, 即概率密度函式的積分 積分*(L(=255)-1)*/ /*均衡化關鍵步驟*/ int count = 0; cimg_forX(Histogram, x) { count += Histogram[x]; // 累計求和 CumulativeDistributionFunction[x] = 1.0 * count / NumOfPixels; //計算概率 EqualizedResult[x] = round(CumulativeDistributionFunction[x] * (L - 1)); // 計算概率密度,round四捨五入 } /*輸出均衡化結果*/ CImg<unsigned int> OutputImage((InputImage)._width, (InputImage)._height, 1, 1, 0); cimg_forXY(OutputImage, x, y) // calculate histogram equalization result OutputImage(x, y, 0) = EqualizedResult[(InputImage)(x, y)]; return OutputImage; }
獨立RGB通道均衡化
/*功能: 分別對彩色圖的三個通道分開求解均衡化結果,然後合併*/
/*輸入: 均衡化影象檔名*/
/*輸出: 彩色圖均衡化結果*/
CImg<unsigned int> HistogramEqualization::Hist_Equal_ColorImage_OneColorChannel(string ImageFileName)
{
CImg<unsigned int> ColorImage;
ColorImage.load_bmp(ImageFileName.c_str());
CImg<unsigned int> R_Channel = HistogramEqualizationMethod(ColorImage.get_channel(0),
GetHistogram(ColorImage.get_channel(0)));
CImg<unsigned int> G_Channel = HistogramEqualizationMethod(ColorImage.get_channel(1),
GetHistogram(ColorImage.get_channel(1)));
CImg<unsigned int> B_Channel = HistogramEqualizationMethod(ColorImage.get_channel(2),
GetHistogram(ColorImage.get_channel(2)));
CImg<unsigned int> EqualizedImage = ColorImage;
cimg_forXY(ColorImage, x, y) {
EqualizedImage(x, y, 0) = R_Channel(x, y, 0);
EqualizedImage(x, y, 1) = G_Channel(x, y, 0);
EqualizedImage(x, y, 2) = B_Channel(x, y, 0);
}
if (show) EqualizedImage.display("通道獨立均衡化結果");
char FileName[100] = {};
sprintf_s(FileName, "%d_OneChannels_equalize_%s", number++, ImageFileName.c_str());
EqualizedImage.save(FileName);
return EqualizedImage;
}
三通道直方圖平均後均衡化
/*傳入三個通道影象計算平均亮度直方圖*/
CImg<unsigned int> HistogramEqualization::GetAverageHistogram
(CImg<unsigned int> img1, CImg<unsigned int> img2, CImg<unsigned int> img3)
{
CImg<unsigned int> histogram(256, 1, 1, 1, 0);
cimg_forXY(img1, x, y) {
++histogram[(int)img1(x, y)];
++histogram[(int)img2(x, y)];
++histogram[(int)img3(x, y)];
}
cimg_forX(histogram, pos) histogram(pos) /= 3;
return histogram;
}
// 功能:對彩色圖進行直方圖均衡化,先求三個通道的平均直方圖
// 輸入: 彩色圖檔名
// 輸出: 均衡化結果
CImg<unsigned int> HistogramEqualization::Hist_Equal_ColorImage_ThreeColorChannels(string ImageFileName)
{
CImg<unsigned int> ColorImage;
ColorImage.load_bmp(ImageFileName.c_str());
CImg<unsigned int> R_Channel = ColorImage.get_channel(0);
CImg<unsigned int> G_Channel = ColorImage.get_channel(1);
CImg<unsigned int> B_Channel = ColorImage.get_channel(2);
CImg<unsigned int> AverageHistogram = GetAverageHistogram(R_Channel, G_Channel, B_Channel);
double CumulativeDistributionFunction[256] = { 0 }; // 灰度直方圖的累積分佈
double EqualizedResult[256] = { 0 }; // 均衡化結果圖
int count = 0;
int L = 256;
int NumOfPixels = ColorImage._height * ColorImage._width;
cimg_forX(AverageHistogram, pos) {
count += AverageHistogram[pos];
CumulativeDistributionFunction[pos] = 1.0 * count / NumOfPixels;
EqualizedResult[pos] = round(CumulativeDistributionFunction[pos] * (L - 1));
}
CImg<unsigned int> EqualizedImage = ColorImage;
cimg_forXY(EqualizedImage, x, y) {
EqualizedImage(x, y, 0) = EqualizedResult[R_Channel(x, y)];
EqualizedImage(x, y, 1) = EqualizedResult[G_Channel(x, y)];
EqualizedImage(x, y, 2) = EqualizedResult[B_Channel(x, y)];
}
if (show) EqualizedImage.display("三通道直方圖平均後進行均衡化的結果");
char FileName[100] = {};
sprintf_s(FileName, "%d_Threechannels_equalize_%s", number++, ImageFileName.c_str());
EqualizedImage.save(FileName);
return EqualizedImage;
}
HSI空間下均衡化
這個略複雜。。
RGB轉HSI空間公式:
HSI轉RGB空間公式:
/*功能:在HSI空間下進行均衡化,然後再轉回RGB空間*/
CImg<double> HistogramEqualization::Hist_Equal_ColorImage_HSISpace(string ImageFileName)
{
CImg<unsigned int> ColorImage;
ColorImage.load(ImageFileName.c_str());
// 獲取RGB通道影象,並歸一化
CImg<double> R_Channel = ColorImage.get_channel(0)* 1.0 / 255.0;
CImg<double> G_Channel = ColorImage.get_channel(1) * 1.0 / 255.0;
CImg<double> B_Channel = ColorImage.get_channel(2) * 1.0 / 255.0;
int w = ColorImage._width;
int h = ColorImage._height;
// HSI
CImg<double> Hue(w, h, 1, 1, 0);
CImg<double> Saturation(w, h, 1, 1, 0);
CImg<double> Intensity(w, h, 1, 1, 0);
// for calculate Hue
CImg<double> theta(w, h, 1, 1, 0);
cimg_forXY(Hue, x, y) {
double numerator = R_Channel(x, y, 0) - G_Channel(x, y, 0) + R_Channel(x, y, 0) - B_Channel(x, y, 0);
double denominator = 2.0 * sqrt((R_Channel(x, y, 0) - G_Channel(x, y, 0)) * (R_Channel(x, y, 0) - G_Channel(x, y, 0))
+ (R_Channel(x, y, 0) - B_Channel(x, y, 0))*(G_Channel(x, y, 0) - B_Channel(x, y, 0)));
theta(x, y, 0) = acos(numerator / denominator);
}
cimg_forXY(Hue, x, y) {
if (G_Channel(x, y, 0) >= B_Channel(x, y, 0)) {
Hue(x, y, 0) = theta(x, y, 0);
}
else {
Hue(x, y, 0) = 2 * cimg::PI - theta(x, y, 0);
}
}
// calculate saturation and intensity
cimg_forXY(Intensity, x, y) {
double deno = (R_Channel(x, y, 0) + G_Channel(x, y, 0) + B_Channel(x, y, 0));
if (deno == 0) deno = 1e-12f; // 取極小,近似0
Saturation(x, y, 0) = 1.0 - 3.0 * cimg::min(R_Channel(x, y, 0), B_Channel(x, y, 0), G_Channel(x, y, 0))
/ deno;
Intensity(x, y, 0) = 1.0 * (R_Channel(x, y, 0) + G_Channel(x, y, 0) + B_Channel(x, y, 0)) / 3.0;
if (Saturation(x, y, 0) == 0) Hue(x, y, 0) = 0; // 黑色
}
CImg<unsigned int> tmp_Intensity(w, h, 1, 1, 0); // 亮度影象 0~255
cimg_forXY(tmp_Intensity, x, y) {
tmp_Intensity(x, y, 0) = floor(Intensity(x, y, 0) * 255);
}
CImg<unsigned int> I_Histogram = GetHistogram(tmp_Intensity); // 亮度直方圖,由於rgb通道值/255,需要擴大255
tmp_Intensity = HistogramEqualizationMethod(tmp_Intensity, I_Histogram); // 亮度直方圖均衡化
cimg_forXY(Intensity, x, y) {
Intensity(x, y, 0) = tmp_Intensity(x, y, 0) * 1.0 / 255.0;
}
CImg<double> EqualizedImage(w, h, 1, 3, 0);
cimg_forXY(Intensity, x, y) {
if (Hue(x, y, 0) < 2 * cimg::PI / 3 && Hue(x, y, 0) >= 0) { //RG sector
EqualizedImage(x, y, 2) = Intensity(x, y, 0) * (1 - Saturation(x,y,0));
EqualizedImage(x, y, 0) = Intensity(x, y, 0) *(1 + Saturation(x, y, 0) * cos(Hue(x, y, 0)) / cos(cimg::PI / 3 - Hue(x,y,0)));
EqualizedImage(x, y, 1) = 3 * Intensity(x, y, 0) - (EqualizedImage(x,y,2) + EqualizedImage(x, y, 0));
}
else if (Hue(x, y, 0) < 4 * cimg::PI / 3 && Hue(x, y, 0) >= 2 * cimg::PI / 3) { //GB sector
EqualizedImage(x, y, 0) = Intensity(x, y, 0) * (1 - Saturation(x, y, 0));
EqualizedImage(x, y, 1) = Intensity(x, y, 0) *(1 + Saturation(x, y, 0) * cos(Hue(x, y, 0) - 2*cimg::PI/3) / cos(cimg::PI - Hue(x, y, 0)));
EqualizedImage(x, y, 2) = 3 * Intensity(x, y, 0) - (EqualizedImage(x, y, 1) + EqualizedImage(x, y, 0));
}
else if (Hue(x, y, 0) <= 2 * cimg::PI && Hue(x, y, 0) >= 4 * cimg::PI / 3) { // BR sector
EqualizedImage(x, y, 1) = Intensity(x, y, 0) * (1 - Saturation(x, y, 0));
EqualizedImage(x, y, 2) = Intensity(x, y, 0) *(1 + Saturation(x, y, 0) * cos(Hue(x, y, 0) - 4 * cimg::PI / 3) / cos(5 * cimg::PI / 3 - Hue(x, y, 0)));
EqualizedImage(x, y, 0) = 3 * Intensity(x, y, 0) - (EqualizedImage(x, y, 1) + EqualizedImage(x, y, 2));
}
}
CImg<unsigned int> resultImage(w, h, 1, 3, 0);
cimg_forXY(EqualizedImage, x, y) {
resultImage(x, y, 0) = 255 * min(max(EqualizedImage(x, y, 0), 0.0), 1.0);
resultImage(x, y, 1) = 255 * min(max(EqualizedImage(x, y, 1), 0.0), 1.0);
resultImage(x, y, 2) = 255 * min(max(EqualizedImage(x, y, 2), 0.0), 1.0);
}
//resultImage = resultImage.normalize(0, 255);
if (show) resultImage.display("HSI空間下均衡化的結果");
char FileName[100] = {};
//sprintf_s(FileName, "%d_HSI_equalize_%s", number++, ImageFileName.c_str());
//resultImage.save(FileName);
return resultImage;
}