1. 程式人生 > >使用C++實現彩色影象直方圖均衡化的三種方法

使用C++實現彩色影象直方圖均衡化的三種方法

引言

本文主要介紹如何實現彩色影象的直方圖均衡化,達到影象增強效果的三種方法:

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;
}