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

 

 

  • 3
    点赞
  • 26
    收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:黑客帝国 设计师:我叫白小胖 返回首页
评论 3

打赏作者

qyhyzard

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值