图像处理之对比度增强

文章目录

  • 对比度增强
  • 一、对比度增强的方法?
  • 二、各方法的原理
    • 1.线性变换
    • 2.直方图正规化
    • 3.伽马变换
    • 4.全局直方图均衡化
    • 5.限制对比度的自适应直方图均衡化
  • 总结


对比度增强

在图像处理中,由于获取的图像质量不好,需要通过对比度增强来提升图片质量,主要解决的是由于图像灰度级范围较小造成的对比度较低的问题,作用是使图像的灰度级范围放大,从而让图像更加清晰。

本文所总结的内容出自张平的《opencv算法精讲》

一、对比度增强的方法?

对比度提升的几种常用方法:

线性变换、直方图正规化、伽马变换、全局直方图均衡化、限制对比度的自适应直方图均衡化等。

二、各方法的原理

1.线性变换

我们知道简单的线性方程公式是 y = a*x+b,对于图像亮度提升来说,此时的x,y都是二维矩阵,通过系数a来调整原始图像中的图像灰度级的范围(指的是图像转换为灰度图后的范围 [最小灰度值,最大灰度值] )

当00时,图像灰度级范围扩大。
当b>0时,亮度增加,当b<0时,亮度减小。

代码如下:

/*
线性变换
变换后的矩阵B = a*原矩阵A+b
image:原图像
rtype:输出矩阵的数据类型,CV_8U等
a:上述公式中的a
b:上述公式中的b
*/
Mat LinearTransform(const Mat& image, int rtype, double a, double b) {
	Mat Out;
	image.convertTo(Out, rtype, a, b);
	return Out;
}

2.直方图正规化

直方图正规化其实是一种自动选取a和b的线性变换的方法。
假设输入矩阵 I,高为H、宽为W,I(r,c)代表第r行第c列的灰度值,将 I 中的最小灰度值记为 I min,最大值记为 I max,即 I(r,c)∈[ I min, I max],为使输出矩阵 ***O***的灰度级范围为[ O min, O max],则 I(r,c)和 O(r,c)的映射关系如下:
O ( r , c ) = O m a x − O m i n I m a x − I m i n ( I ( r , c ) − I m i n ) + O m i n O(r,c) = \frac{O_{max}-O_{min}}{I_{max}-I_{min}}(I{(r,c)}-I_{min})+O_{min} O(r,c)=ImaxIminOmaxOmin(I(r,c)Imin)+Omin
其中 0 ≤ r < H 0\le r 0r<H 0 ≤ c < W 0\le c 0c<W O ( r , c ) O(r,c) O(r,c)代表第r行第c列的灰度值。这个过程就是直方图正规化。因为 0 ≤ I ( r , c ) − I m i n I m a x − I m i n ≤ 1 0 \le \frac{I_{(r,c)}-I_{min}}{I_{max}-I_{min}} \le 1 0ImaxIminI(r,c)Imin1,所以 O ( r , c ) ∈ [ O m i n , O m a x ] O{(r,c)} \in [O_{min},O_{max}] O(r,c)[Omin,Omax],一般令 O m i n = 0 O_{min}=0 Omin=0, O m a x = 255 O_{max}=255 Omax=255。我们将上面的公式展开后花间,可达到线性变换的a和b,
a = O m a x − O m i n I m a x − I m i n , b = O m i n − O m a x − O m i n I m a x − I m i n ∗ I m i n a= \frac{O_{max}-O_{min}}{I_{max}-I_{min}},b=O_{min}-\frac{O_{max}-O_{min}}{I_{max}-I_{min}} * I_{min} a=ImaxIminOmaxOmin,b=OminImaxIminOmaxOminImin

代码如下:

Mat HistgramNormalization(Mat gray_image)
{	
	//获取输入矩阵的最大值和最小值
	double Imax, Imin;
	minMaxLoc(gray_image, &Imin, &Imax, NULL, NULL);

	// 定义输出矩阵的最大值和最小值
	double Omin = 0, Omax = 255;
	// 确定映射关系函数中的系数,其实就是确定线性变换中的系数a和b
	double a = (Omax - Omin) / (Imax - Imin);
	double b = Omin - a * Imin;
	Mat Out;//输出矩阵
	convertScaleAbs(gray_image, Out, a, b);//进行线性变换
	return Out;
}

3.伽马变换

伽马变换的第一步是将灰度值归一化带 [ 0 , 1 ] [0,1] [0,1]的范围。归一化后的图像矩阵记为 I I I,高为H,宽为W,第r行第c列的值记为 I ( r , c ) I{(r,c)} I(r,c),输出矩阵击为 O O O,伽马变换就是:
O ( r , c ) = I ( r , c ) γ , 0 ≤ r < H , 0 ≤ c < W O_{(r,c)} = I{(r,c)}^\gamma,0\le rO(r,c)=I(r,c)γ0r<H,0c<W
γ = 1 \gamma = 1 γ=1时,图像不变,当 0 < γ < 1 0<\gamma<1 0<γ<1时,可以增加对比度,当 γ > 1 \gamma>1 γ>1 时,可以降低对比度,接下来我们在看一下Gamma变换的的图示,这样理解起来容易点。下图中从左上角到右下角的曲线为 y = x a y=x^a y=xa,a∈[0.125,0.25,0.5,1,2,4,8],其实就是高中的幂函数,只是定义域在[0,1]之间,
图像处理之对比度增强_第1张图片

代码如下:

Mat GammaTransform(Mat gray_image, double gamma)
{
	//1.归一化
	Mat fI;
	gray_image.convertTo(fI, CV_64F, 1 / 255, 0);
	//2.伽马变换,其实就是幂运算,gamma的区间为(0,1)
	Mat Out;
	pow(fI, gamma, Out);
	//3.再次进行线性变换,转换为0,255之间的像素值
	Out.convertTo(Out, CV_8U, 255, 0);
	return Out;
}

4.全局直方图均衡化

假设输入的图像为 I I I,高为 H H H,宽为 W W W h i t s I hits_I hitsI代表 I I I的灰度直方图, h i s t I ( k ) hist_I(k) histI(k)代表灰度值等于k的像素点的个数,其中 k ∈ [ 0 , 255 ] k\in[0,255] k[0,255]。全局直方图均衡化的操作就是对图像 I I I进行改变,使得输出图像 O O O的灰度直方图 h i s t O hist_O histO是‘平的’,即每一个像素值的个数是‘相等’的。这里的相等不是严格意义上的相等,而是‘约等于’,即 h i s t O ( k ) ≈ H ∗ W 256 , k ∈ [ 0 , 255 ] hist_O(k)\approx\frac{H*W}{256},k\in[0,255] histO(k)256HW,k[0,255],那么对于任意灰度级p, 0 ≤ p ≤ 255 0\le p \le 255 0p255,总能找到q, 0 ≤ q ≤ 255 0\le q \le 255 0q255,使得
∑ k = 0 p h i s t I ( k ) = ∑ k = 0 q h i s t O ( k ) \sum_{k=0}^phist_I(k)=\sum_{k=0}^qhist_O(k) k=0phistI(k)=k=0qhistO(k)
其中 ∑ k = 0 p h i s t I ( k ) \sum_{k=0}^{p}hist_I(k) k=0phistI(k) ∑ k = 0 q h i s t I ( k ) \sum_{k=0}^{q}hist_I(k) k=0qhistI(k)称为 I I I O O O的累加直方图,又因为 h i s t O ( k ) ≈ H ∗ W 256 hist_O(k)\approx\frac{H*W}{256} histO(k)256HW,所以
∑ k = 0 p h i s t I ( k ) ≈ ( q + 1 ) H ∗ W 256 \sum_{k=0}^phist_I(k)\approx(q+1)\frac{H*W}{256} k=0phistI(k)(q+1)256HW
化简上式得
q ≈ ∑ k = 0 p h i s t I ( k ) H ∗ W ∗ 256 − 1 q\approx\frac{\sum_{k=0}^{p}hist_I(k)}{H*W}*256-1 qHWk=0phistI(k)2561
上式给出了一个从亮度级为p的输入像素到亮度级为q的输出像素的映射关系,p代表的是I(r,c),是原矩阵第r行第c列的像素值,此时令p=I(r,c);q代表的是O(r,c),是输出矩阵的第r行第c列的像素值,将两个进行替换得到最终的映射关系
O ( r , c ) ≈ ∑ k = 0 I ( r , c ) h i s t I ( k ) H ∗ W ∗ 256 − 1 O(r,c)\approx\frac{\sum_{k=0}^{I(r,c)}hist_I(k)}{H*W}*256-1 O(r,c)HWk=0I(r,c)histI(k)2561
代码如下:

Mat equalHist(Mat image)
{
	//检查图像数据类型是否符合要求,如不符合,则抛出异常
	// CV_Assert的官方简要说明如下
	// Checks a condition at runtime and throws exception if it fails
	CV_Assert(image.type() == CV_8UC1);
	// 灰度图像的宽和高
	int rows = image.rows;
	int cols = image.cols;
	//第一步:计算图像的灰度直方图
	Mat gray_hist = CalcGrayHist(image);
	//第二步:计算累加灰度直方图
	Mat zero_comu_moment = Mat::zeros(Size(256, 1), CV_32SC1); //生成一行256列的零矩阵

	for (int p = 0; p < 256; p++)
	{
		if (p==0)
		{
			zero_comu_moment.at<int>(0, p) = gray_hist.at<int>(0, 0);
		}
		else
		{
			zero_comu_moment.at<int>(0, p) = zero_comu_moment.at<int>(0, p - 1) + gray_hist.at<int>(0, p);
		}
	}

	//第三步:根据累加直方图得到输入灰度级和输出灰度级直方图的映射关系
	Mat output_q = Mat::zeros(Size(256, 1), CV_8UC1);

	float cofficient = 256.0 / (rows * cols);

	for (int p = 0; p < 256; p++)
	{
		float q = cofficient * zero_comu_moment.at<int>(0, p) - 1;
		if (q>=0)
		{
			output_q.at<uchar>(0, p) = uchar(floor(q));
		}
		else
		{
			output_q.at<uchar>(0, p) = 0;
		}
	}
	//第四步:得到直方图均衡化后的图像
	Mat equal_hist_image = Mat::zeros(image.size(), CV_8UC1);

	for (int r = 0; r < rows; r++)
	{
		for (int c = 0; c < cols; c++)
		{
			int	p = image.at<uchar>(r, c);
			//根据原像素值p获得新的像素值q重新赋值给对应位置
			equal_hist_image.at<uchar>(r, c) = output_q.at<uchar>(0, p);
		}
	}
	return equal_hist_image;
}

5.限制对比度的自适应直方图均衡化

自适应直方图均衡化首先将图像划分为不重叠的区域块,然后对每一个快进行直方图均衡化,同时为了避免噪声被放大,提出了“限制对比度”,即如果某个像素点的统计数据超过某个界限,便将多余的数量平均分配到每一个像素点上。

由此我们可以大致总结其流程:
1.将图像分割成不重叠的区域块;
2.对每个区域块进行直方图均衡化;
3.为避免噪声的出现,设置一个像素点出现的最高数量,如果超过这个数量,将多出来的平均分配到每个像素点上。
代码如下:

Mat AdaptEqualHist(Mat gray_image)
{
	//构建CLAHE对象,其默认设置“限制对比度”为40,块的大小为8*8
	Ptr<CLAHE> clahe = createCLAHE(2.0, Size(8, 8));
	Mat dst;
	//限制对比度的自适应直方图均衡化
	clahe->apply(gray_image, dst);
	return dst;

}

总结

以上为亮度提升的一些基础算法,个人认为亮度提升的关键是要提升图像像素级的范围和亮度值,最基础的就是一个的线性变换。以上内容为自己学习总结,有理解不到位之处,欢迎在评论区指出。

你可能感兴趣的:(图像处理,计算机视觉,opencv,c++)