彩色图像自动色阶调整和自动对比度调整

1.自动色阶调整

算法原理:

(1)分别统计每个通道的灰度直方图;
(2)对每个通道,利用LowCut和HighCut,计算灰度最小值min和最大值max;
(3)对每个通道分别建立分段线性拉伸查找表,
f(g) = 0 g<=min
f(g) = 255 g>=max
f(g) = ((g-min)/(max-min)) * 255 min<g<max
(4)对每个通道利用相应的查找表进行分段线性拉伸,得到效果图。

2.自动对比度调整

算法原理:

(1)分别统计每个通道的灰度直方图;
(2)对每个通道,利用LowCut和HighCut,分别计算灰度最小值和最大值,取三个通道最小值中最小者作为最小值min,取三个通道最大值中最大者为最大值max;
(3)对每个通道建立统一的分段线性拉伸查找表,
f(g) = 0 g<=min
f(g) = 255 g>=max
f(g) = ((g-min)/(max-min)) * 255 min<g<max

(4)对每个通道利用统一的查找表进行分段线性拉伸,得到效果图。


3.区别

两者的区别主要是取最大值最小值的不同,自动色阶每个通道取的是当前通道的最大值最小值,而自动对比度取的是三个通道之最。

根据这个区别,不难推测出它们在效果上的一个区别:自动对比度不会改变RGB之间的大小顺序,而自动色阶有可能改变RGB之间的大小顺序。

参考博客:

http://www.cnblogs.com/Imageshop/archive/2011/11/13/2247614.html

博客中提到这两个算法主要是参考PS中的功能。做图像处理的,不会PS,确实有点说不过去,有时间我还是要学习一下PS。

然后针对其中的一些功能,用opencv实现一下,肯定能有很多收获。(待做事件)


4.OpenCV实现

(1)自动色阶

/*
	*函数功能:自动色阶调整(仿照PS功能)
	*输入参数:src	输入彩色图像
	*输出参数:dst	输出调整色阶之后的彩色图像
	*返回值:void
	*算法步骤:
		(1)分别统计每个通道的灰度直方图;
		(2)对每个通道,利用LowCut和HighCut,计算灰度最小值min和最大值max;
		(3)对每个通道分别建立分段线性拉伸查找表,
			f(g) = 0		g<=min
			f(g) = 255		g>=max
			f(g) = ((g-min)/(max-min)) * 255		min<g<max
		(4)对每个通道利用相应的查找表进行分段线性拉伸,得到效果图。
		http://www.cnblogs.com/Imageshop/archive/2011/11/13/2247614.html
*/
void AutoLevelsAdjust(cv::Mat &src, cv::Mat &dst)
{
	CV_Assert(!src.empty() && src.channels() == 3);

	//统计灰度直方图
	int BHist[256] = { 0 };	//B分离
	int GHist[256] = { 0 };	//G分量
	int RHist[256] = { 0 };	//R分量
	cv::MatIterator_<Vec3b> its, ends;
	for (its = src.begin<Vec3b>(), ends = src.end<Vec3b>(); its != ends; its++)
	{
		BHist[(*its)[0]]++;
		GHist[(*its)[1]]++;
		RHist[(*its)[2]]++;
	}

	//设置LowCut和HighCut
	float LowCut = 0.5;
	float HighCut = 0.5;

	//根据LowCut和HighCut查找每个通道最大值最小值
	int BMax = 0, BMin = 0;
	int GMax = 0, GMin = 0;
	int RMax = 0, RMin = 0;

	int TotalPixels = src.cols * src.rows;
	float LowTh = LowCut * 0.01 * TotalPixels;
	float HighTh = HighCut * 0.01 * TotalPixels;

	//B通道查找最小最大值
	int sumTempB = 0;
	for (int i = 0; i < 256; i++)
	{
		sumTempB += BHist[i];
		if (sumTempB >= LowTh)
		{
			BMin = i;
			break;
		}
	}
	sumTempB = 0;
	for (int i = 255; i >= 0; i--)
	{
		sumTempB += BHist[i];
		if (sumTempB >= HighTh)
		{
			BMax = i;
			break;
		}
	}

	//G通道查找最小最大值
	int sumTempG = 0;
	for (int i = 0; i < 256; i++)
	{
		sumTempG += GHist[i];
		if (sumTempG >= LowTh)
		{
			GMin = i; 
			break;
		}
	}
	sumTempG = 0;
	for (int i = 255; i >= 0; i--)
	{
		sumTempG += GHist[i];
		if (sumTempG >= HighTh)
		{
			GMax = i;
			break;
		}
	}

	//R通道查找最小最大值
	int sumTempR = 0;
	for (int i = 0; i < 256; i++)
	{
		sumTempR += RHist[i];
		if (sumTempR >= LowTh)
		{
			RMin = i;
			break;
		}
	}
	sumTempR = 0;
	for (int i = 255; i >= 0; i--)
	{
		sumTempR += RHist[i];
		if (sumTempR >= HighTh)
		{
			RMax = i;
			break;
		}
	}

	//对每个通道建立分段线性查找表
	//B分量查找表
	int BTable[256] = { 0 };
	for (int i = 0; i < 256; i++)
	{
		if (i <= BMin)
			BTable[i] = 0;
		else if (i > BMin && i < BMax)
			BTable[i] = cvRound((float)(i - BMin) / (BMax - BMin) * 255);
		else
			BTable[i] = 255;
	}

	//G分量查找表
	int GTable[256] = { 0 };
	for (int i = 0; i < 256; i++)
	{
		if (i <= GMin)
			GTable[i] = 0;
		else if (i > GMin && i < GMax)
			GTable[i] = cvRound((float)(i - GMin) / (GMax - GMin) * 255);
		else
			GTable[i] = 255;
	}

	//R分量查找表
	int RTable[256] = { 0 };
	for (int i = 0; i < 256; i++)
	{
		if (i <= RMin)
			RTable[i] = 0;
		else if (i > RMin && i < RMax)
			RTable[i] = cvRound((float)(i - RMin) / (RMax - RMin) * 255);			
		else
			RTable[i] = 255;
	}

	//对每个通道用相应的查找表进行分段线性拉伸
	cv::Mat dst_ = src.clone();
	cv::MatIterator_<Vec3b> itd, endd;
	for (itd = dst_.begin<Vec3b>(), endd = dst_.end<Vec3b>(); itd != endd; itd++)
	{
		(*itd)[0] = BTable[(*itd)[0]];
		(*itd)[1] = GTable[(*itd)[1]];
		(*itd)[2] = RTable[(*itd)[2]];
	}

	dst = dst_;	
}

(2)自动对比度

/*
	*函数功能:自动对比度调整(仿照PS功能)
	*输入参数:src	输入彩色图像
	*输出参数:dst	输出调整色阶之后的彩色图像
	*返回值:void
	*算法步骤:
	(1)分别统计每个通道的灰度直方图;
	(2)对每个通道,利用LowCut和HighCut,分别计算灰度最小值和最大值,取三个通道最小值中
	最小者作为最小值min,取三个通道最大值中最大者为最大值max;
	(3)对每个通道建立统一的分段线性拉伸查找表,
		f(g) = 0		g<=min
		f(g) = 255		g>=max
		f(g) = ((g-min)/(max-min)) * 255		min<g<max
	(4)对每个通道利用统一的查找表进行分段线性拉伸,得到效果图。
	http://www.cnblogs.com/Imageshop/archive/2011/11/13/2247614.html
*/
void AutoContrastAdjust(cv::Mat &src, cv::Mat &dst)
{
	CV_Assert(!src.empty() && src.channels() == 3);

	//统计灰度直方图
	int BHist[256] = { 0 };
	int GHist[256] = { 0 };
	int RHist[256] = { 0 };
	cv::MatIterator_<Vec3b> its, ends;
	for (its = src.begin<Vec3b>(), ends = src.end<Vec3b>(); its != ends; its++)
	{
		BHist[(*its)[0]]++;
		GHist[(*its)[1]]++;
		RHist[(*its)[2]]++;
	}

	//设置LowCut和HighCut
	float LowCut = 0.5;
	float HighCut = 0.5;

	//根据LowCut和HighCut查找每个通道最大值最小值
	int BMax = 0, BMin = 0;
	int GMax = 0, GMin = 0;
	int RMax = 0, RMin = 0;

	int TotalPixels = src.cols * src.rows;
	float LowTh = LowCut * 0.01 * TotalPixels;
	float HighTh = HighCut * 0.01 * TotalPixels;

	//B通道查找最小最大值
	int sumTempB = 0;
	for (int i = 0; i < 256; i++)
	{
		sumTempB += BHist[i];
		if (sumTempB >= LowTh)
		{
			BMin = i; 
			break;
		}
	}
	sumTempB = 0;
	for (int i = 255; i >= 0; i--)
	{
		sumTempB += BHist[i];
		if (sumTempB >= HighTh)
		{
			BMax = i;
			break;
		}
	}

	//G通道查找最小最大值
	int sumTempG = 0;
	for (int i = 0; i < 256; i++)
	{
		sumTempG += GHist[i];
		if (sumTempG >= LowTh)
		{
			GMin = i;
			break;
		}
	}
	sumTempG = 0;
	for (int i = 255; i >= 0; i--)
	{
		sumTempG += GHist[i];
		if (sumTempG >= HighTh)
		{
			GMax = i;
			break;
		}
	}

	//R通道查找最小最大值
	int sumTempR = 0;
	for (int i = 0; i < 256; i++)
	{
		sumTempR += RHist[i];
		if (sumTempR >= LowTh)
		{
			RMin = i;
			break;
		}
	}
	sumTempR = 0;
	for (int i = 255; i >= 0; i--)
	{
		sumTempR += RHist[i];
		if (sumTempR >= HighTh)
		{
			RMax = i;
			break;
		}
	}

	//获取最大值,最小值,与自动色阶的不同之处主要在此,取的是三通道之最
	int Max = std::max(std::max(BMax, GMax), RMax);
	int Min = std::min(std::min(BMin, GMin), RMin);

	//建立统一的分段线性查找表
	int Table[256] = { 0 };
	for (int i = 0; i < 256; i++)
	{
		if (i <= Min)
			Table[i] = 0;
		else if (i > Min && i < Max)
			Table[i] = (int)((float)(i - Min) / (Max - Min) * 255);
		else
			Table[i] = 255;
	}
	
	//对每个通道用统一的查找表进行分段线性拉伸
	cv::Mat dst_ = src.clone();
	cv::MatIterator_<Vec3b> itd, endd;
	for (itd = dst_.begin<Vec3b>(), endd = dst_.end<Vec3b>(); itd != endd; itd++)
	{
		(*itd)[0] = Table[(*itd)[0]];
		(*itd)[1] = Table[(*itd)[1]];
		(*itd)[2] = Table[(*itd)[2]];
	}
	
	dst = dst_;
}

(3)效果


原图


自动色阶效果图


自动对比度效果图

单从这幅图的测试效果来看,没法说自动色阶和自动对比度哪个效果更好。但是两者之间确实存在区别,为了对比一下两者的不同,利用ImageWatch观察一下内部像素值的变化


原图,位置(0000,0416)处的像素值 B |G|R = 213|231|160,    大小关系 G>B>R


自动色阶,位置(0000,0416)处的像素值 B |G|R = 255|254|157,    大小关系 B>G>R,改变了B和G的大小关系


自动对比度,位置(0000,0416)处的像素值 B |G|R = 226|248|160,    大小关系 G>B>R,RGB大小关系不变

小结:如果在图像处理的过程中比较关心RGB之间的大小关系,就不能用自动色阶,而应该用自动对比度。如果不关心RGB的大小关系,就根据实际测试对后续的影响来判断使用哪种处理。

5.扩展

在查询相关资料的时候,看到很多资料提到了彩色图像的直方图均衡化。于是,决定也了解一下彩色图像直方图均衡化的效果,并且与自动对比度进行一下比对。

参考博客:

https://blog.csdn.net/frank_xu_0818/article/details/39232157

(1)RGB空间下分通道直方图均衡化

/*
	 *函数功能:对彩色图像进行直方图均衡化
	 *输入参数:src	输入彩色图像
	 *输出参数:dst	输出均衡化之后的图像
	 *返回值:void
	 *备注:先通道分离,然后对每一通道进行直方图均衡化,最后融合
 */
void ColorEqualizeHist(cv::Mat &src, cv::Mat &dst)
{
	CV_Assert(!src.empty() && src.channels() == 3);
	
	//通道分离
	cv::Mat channels[3];
	cv::split(src, channels);

	//每个通道进行直方图均衡化
	cv::Mat equalizeHistImg[3];
	for (int i = 0; i < 3; i++)
		cv::equalizeHist(channels[i], equalizeHistImg[i]);

	//通道融合
	cv::Mat dst_;
	cv::merge(equalizeHistImg, 3, dst_);

	dst = dst_;
}

(2)转到YCbCr空间下单独对Y通道直方图均衡化

/*
	*函数功能:对彩色图像进行直方图均衡化
	*输入参数:src	输入彩色图像
	*输出参数:dst	输出均衡化之后的图像
	*返回值:void
	*备注:先转到YCbCr颜色空间,然后通道分离,只对灰度通道进行直方图均衡化,
	进行通道融合,最后转回到BGR空间
*/
void EqualizeHistByYCbCr(cv::Mat &src, cv::Mat &dst)
{
	CV_Assert(!src.empty() && src.channels() == 3);

	//BGR颜色空间转换到YCbCr颜色空间
	cv::Mat srcYCbCr;
	cv::cvtColor(src, srcYCbCr, CV_BGR2YCrCb);

	//对YCbCr进行通道分离
	cv::Mat channels[3];
	cv::split(srcYCbCr, channels);

	//对亮度通道进行直方图均衡化
	cv::equalizeHist(channels[0], channels[0]);

	//图像融合
	cv::Mat dst_;
	cv::merge(channels, 3, dst_);

	//将YCbCr颜色空间转换回BGR颜色空间
	cv::cvtColor(dst_, dst_, CV_YCrCb2BGR);

	dst = dst_;
}

(3)效果


RGB直方图均衡化


YCbCr直方图均衡化

小结:RGB分通道均衡化再融合,颜色容易失真,实用性很低。转换为YCbCr再进行对亮度通道均衡化,融合后转回RGB,效果不够稳定。参考博客中的lena图我自己也测了,效果确实不错,但是我测gakki还有别的一幅图,效果不好。

根据我测得一些图,可以发现自动对比度与直方图均衡化相比,自动对比度效果更好,更稳定。


猜你喜欢

转载自blog.csdn.net/weixin_42142612/article/details/80928270