【OpenCV学习笔记】2.对比度增强

1.灰度直方图

  • 灰度直方图是图像灰度级的函数,描述每个灰度级在图像矩阵中的像素个数或占有率。灰度级范围可以用来度量图像对比度,灰度级范围越大代表图像对比度越强,图像给人的感觉越清晰。
Mat calcGrayHist(const Mat & image)
{
    
    
	Mat histogram = Mat::zeros(Size(256, 1), CV_32SC1);//存储256个灰度级的像素个数
	int rows = image.rows;
	int cols = image.cols;//图像的宽和高
	//计算灰度级个数
	for(int r = 0; r < rows; r++)
	{
    
    
		for(int c = 0; c < cols; c++)
		{
    
    
			int index = int(image.at<uchar>(r,c));
			histogram.at<int>(0,index) += 1;
		}
	}
	return histogram;
}

2.线性变换

  • 假设输入图像为I,宽为W、高为H,输出图像为O,图像线性变换为:
    O ( r , c ) = a ∗ I ( r , c ) + b , 0 ≤ r < H , 0 ≤ c < W O(r,c)=a*I(r,c)+b, 0 \leq r < H, 0 \leq c < W O(r,c)=aI(r,c)+b,0r<H,0c<W
  • a = 1, b = 0 时,O为I的一个副本
  • a > 1, O的对比度比I大
  • 0 < a < 1, O的对比度比I小
  • b影响输出图像亮度,b>0亮度增加,b<0亮度减小
Mat::convertTo(Outputarray m, int rtype, double alpha = 1, double beta = 0)
//使用Mat的成员函数,alpha和beta分别代表a,b
convertScaleAbs(InputArray src, OutputArray dst, double alpha = 1, double beta = 0)
//使用OpenCV提供的函数
//examples
Mat I = (Mat_<uchar>(2,2) << 0, 200, 23, 4);
Mat O;
I.convertTo(O, CV_8UC1, 2.0, 0);//方法1
//输出结果为[[0,255],[46,8]]
Mat O = 3.5*I;//方法2
//输出结果为[[0,255],[80,14]]
convertScaleAbs(I, O, 2.0, 0);//方法3
//输出结果为[[0,255],[46,8]]

3.直方图正规化

  • 线性变换的参数需要根据不同应用及图形自身信息进行合理选择,可能需要进行多次测试。而直方图正规化可以基于当前图像情况自动选取a、b。
  • 假设输入图像为I,宽为W、高为H,I(r,c)代表I第r行第c列灰度值,将I的最小灰度级记为Imin,最大灰度级记为Imax,为使输出图像O的灰度级范围为[Omin,Omax],做如下映射:
    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{Omax-Omin}{Imax-Imin}(I(r,c)-Imin)+Omin O(r,c)=ImaxIminOmaxOmin(I(r,c)Imin)+Omin
    0 ≤ r < H , 0 ≤ c < W 0 \leq r < H, 0 \leq c < W 0r<H,0c<W
    其中, 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{Omax-Omin}{Imax-Imin},b=Omin-\frac{Omax-Omin}{Imax-Imin}*Imin a=ImaxIminOmaxOmin,b=OminImaxIminOmaxOminImin
//计算原图出现的最小灰度级和最大灰度级
void minMaxLoc(InputArray src, double* minVal, double* maxVal = 0, Point* minLoc = 0, Point* maxLoc = 0, InputArray mask = noArray())
//src-输入矩阵
//minVal-最小值,maxVal-最大值
//minLoc-最小值位置,maxLoc-最大值位置
minMaxLoc(src, &minval, &maxVal, NULL, NULL)//值计算最大值和最小值
//exampls
Mat I = imread(argv[1], LOAD_IMAGE_GRAYSCALE);//输入图像矩阵
double Imax, Imin;
minMaxLoc(I, &Imin, &Imax, NULL, NULL);//找到最大、最小值
double Omin = 0, Omax = 255;
double a = (Omax - Omin)/(Imax - Imin);
double b = Omin - a * Imin;//计算a和b
Mat O;
convertScaleAbs(I, O, a, b);//线性变换
正规化函数
  • 首先了解范数的概念
  • 1范数:计算矩阵中值的绝对值的和
    ∣ ∣ s r c ∣ ∣ 1 = ∑ r = 1 M ∑ c = 1 N ∣ s r c ( r , c ) ∣ ||src||_1=\sum_{r=1}^{M}\sum_{c=1}^{N}|src(r,c)| src1=r=1Mc=1Nsrc(r,c)
  • 2范数:计算矩阵中值的平方和的开方
    ∣ ∣ s r c ∣ ∣ 2 = ∑ r = 1 M ∑ c = 1 N ∣ s r c ( r , c ) ∣ 2 ||src||_2=\sqrt{\sum_{r=1}^{M}\sum_{c=1}^{N}|src(r,c)|^2} src2=r=1Mc=1Nsrc(r,c)2
  • ∞范数:计算矩阵中值的平方和的开方
    ∣ ∣ s r c ∣ ∣ ∞ = m a x ∣ s r c ( r , c ) ∣ ||src||_∞=max|src(r,c)| src=maxsrc(r,c)
void normalize(InputArray src, OutputArray dst, double alpha=1, doubla beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray())
//src-输入矩阵,dst-输出矩阵
//alpha-结构元的锚点,beta-腐蚀操作次数
//norm_type-边界扩充类型,dtype-边界扩充值
  • 当参数norm_type=NORM_L1:
    d s t = a l p h a ∗ s r c ∣ ∣ s r c ∣ ∣ 1 + b e t a dst=alpha*\frac{src}{||src||_1}+beta dst=alphasrc1src+beta
  • 当参数norm_type=NORM_L2:
    d s t = a l p h a ∗ s r c ∣ ∣ s r c ∣ ∣ 2 + b e t a dst=alpha*\frac{src}{||src||_2}+beta dst=alphasrc2src+beta
  • 当参数norm_type=NORM_INF:
  • d s t = a l p h a ∗ s r c ∣ ∣ s r c ∣ ∣ ∞ + b e t a dst=alpha*\frac{src}{||src||_∞}+beta dst=alphasrcsrc+beta
  • 当参数norm_type=NORM_MINMAX:
  • d s t = a l p h a ∗ s r c ( r , c ) − s r c m i n s r c m a x − s r c m i n + b e t a dst=alpha*\frac{src(r,c)-src_{min}}{src_{max}-src_{min}}+beta dst=alphasrcmaxsrcminsrc(r,c)srcmin+beta

4.伽马变换

  • 伽马变换是一种非线性变换。假设输入图像为I,宽为W、高为H,首先将其灰度值归一化到[0,1]范围, O ( r , c ) = I ( r , c ) γ , 0 ≤ r < H , 0 ≤ c < W O(r,c)=I(r,c)^γ,0 \leq r < H, 0 \leq c < W O(r,c)=I(r,c)γ0r<H,0c<W即为伽马变换。
    在这里插入图片描述
  • 当γ=1,图像不变;
  • 0 < γ < 1,增加对比度
  • γ > 1,降低对比度
void pow(InputArray src, double power, OutputArray dst)

5.全局直方图均衡化

  • 假设输入图像为I,宽为W、高为H, h i s t I hist_I histI代表I的灰度直方图, h i s t I ( k ) hist_I(k) histI(k)代表灰度值等于k的像素点个数, k ∈ [ 0 , 255 ] k\in[0,255] k[0,255]。全局直方图均衡化是对I进行改变,使得输出图像O的 h i s t O hist_O histO每一个灰度级像素点相等,即
    h i s t O ( k ) ≈ H ∗ W 256 hist_O(k) \approx \frac{H*W}{256} histO(k)256HW
    那么对于任意灰度级p, 0 ≤ p < 255 0 \leq p < 255 0p<255,总能找到q, 0 ≤ q < 255 0 \leq q < 255 0q<255,使得
    ∑ k = 0 p h i s t I ( k ) = ∑ k = 0 q h i s t O ( k ) \sum_{k=0}^{p}hist_I(k)=\sum_{k=0}^{q}hist_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 O ( k ) \sum_{k=0}^{q}hist_O(k) k=0qhistO(k)成为I和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}^{p}hist_I(k)=(q+1)\frac{H*W}{256} k=0phistI(k)=(q+1)256HW
    O ( r , c ) = ∑ k = 0 I ( r , c ) h i s t I ( k ) H ∗ W ∗ 256 − 1 O(r,c)=\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(image.type() == CV_8UC1);
	int rows = image.rows;
	int cols = image.cols;
	//1.计算图像灰度直方图
	Mat grayHist = calaGrayHist(image);
	//2.计算累加灰度直方图
	Mat zeroCumuMoment = Mat::Zeros(Size(256,1),CV_32SC1);
	for(int p = 0; p < 256; p++)
	{
    
    
		if(P == 0)
			zeroCumuMoment.at<int>(0,p) = grayHist.at<int>(0,0);
		else
			zeroCumuMoment.at<int>(0,p) = zeroCumuMoment.at<int>(0,p-1) +grayHist.at<int>(0,p);
	}
	//3.根据累加直方图获得映射关系
	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 * zeroCumuMoment.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;
	}
	//4.得到直方图均衡化后的图像
	Mat equalHistImage = 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);
			equalHistImage .at<uchar>(r,c) = outPut_q.at<uchar>(0,p);
		}
	}
	return equalHistImage;
}
  • 虽然全局直方图均衡化提高对比度很有效,但是也可能使噪声被放大,为了解决该问题,提出了自适应直方图均衡化。

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

  • 自适应直方图均衡化首先将图像划分为不重叠区域快,然后对每一区域进行直方图均衡化。
  • 为避免噪声出现,突出限制对比度。如果直方图的bin超出提前设置好的限制对比度,超出部分将被剪裁均匀分布到其他bin。
    在这里插入图片描述
  • OpenCV提供createCLANE构建指向CLANE对象的指针,默认设置限制对比度40,块大小8X8。
Ptr<CLANE> clahe = createCLANE(2.0,Size(8,8));
clahe->apply(src,dst);

猜你喜欢

转载自blog.csdn.net/weixin_44496838/article/details/102882992