【opencv4.3.0教程】13之调整图像的亮度与对比度

目录

前言

一、亮度与对比度

1、什么是亮度和对比度

2、亮度和对比度调整原理

3、亮度和对比度调整代码实现

4、API-convertScaleAbs

二、伽马校正

1、伽马校正引入

2、伽马校正原理

3、伽马校正代码实现


前言

我们在拍照的时候,经常需要通过调整图像的亮度及对比度来调整图像的美感,让我们拍出的照片更有意境。特别是女孩子,喜欢拍一些美美哒皂片!

但如果你的男朋友掌握不好角度,亮度和对比度,那可能就是大型拍照翻车现场了。

你眼中的自己和男友眼中的你

 好吧,说回正题,如果我们不考虑拍的丑不丑,我们只想让图片亮度对比度调整一下,还是可以做到滴!接下来让我们走进今天的内容吧!

一、亮度与对比度

1、什么是亮度和对比度

亮度与对比度其实是图像处理中的概念。我们在调整台式电脑的显示界面,就可以调整亮度与对比度。

亮度是指画面的明亮程度;

对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,即指一幅图像灰度反差的大小。差异范围越大代表对比越大,差异范围越小代表对比越小,好的对比率120:1就可容易地显示生动、丰富的色彩,当对比率高达300:1时,便可支持各阶的颜色。

上面是百科的定义,我们怎么去理解呢?

亮度比较容易理解,阳光下的图片一般都亮,夜晚的图片一般都暗。我们也经常调整手机屏幕和电脑屏幕的亮度。

对比度我们常常听到,从对比二字来看,我们可以理解为两个像素差距大不大,对比起来差距明显不明显。对比度越高,那两个像素的差距越大。其实就是色彩空间中能够表示的范围,范围越广,对比越强烈,对比度越大,同样的,展现出的图像的效果也就越好。如果我们拍景色,在一个伸手不见五指的黑夜去拍,还不开闪光灯,拍出一个近乎纯黑的景色,但如果是白天,晴空万里,阳光明媚,我们再去拍摄,景物分明,而且非常迷人。这就是对比度小和对比度大的区别。

2、亮度和对比度调整原理

那如果我们想要调整亮度和对比度,我们其实就是调整像素值。

亮度其实调整的就是每个像素的大小,像素值越大,越接近白色,就越亮,像素值越小,越接近黑色图像就越黑。所以调整亮度其实就是给像素值增加一个变量值,变量值为正,就是调亮,变量值为负,就是调暗。公式表示如下:

对比度调整的是像素区别,一般来说,图像中相邻位置的像素差别较小,我们想增强对比度,那就需要将差别按倍数扩大,要降低对比度,就要按比例缩小。所以调整对比度就是给像素值添加一个倍数,倍数大于1,就是增强对比度,倍数小于1,就是减少对比度。公式表示如下:

我们使用一个统一的公式来表示,就可以同时调整亮度和对比度,公式如下:

3、亮度和对比度调整代码实现

了解了原理,我们就使用代码实现一下吧!上面公式中的f(x)表示原始像素,g(x)表示调整后的像素。所以我们还需要用到我们之前讲的图像像素指针,来修改像素值,我们要考虑,我们修改的是三通道彩色图像,还是灰度图像。

如果是灰度图像,我们修改方式如下:

	float f = src.at<uchar>(row, col);
	dst.at<uchar>(row, col) = saturate_cast<uchar>(f*alpha + beta);

如果是彩色图像,我们就需要修改每一个通道的值:


	float b = m1.at<Vec3f>(row, col)[0];// blue
	float g = m1.at<Vec3f>(row, col)[1]; // green
	float r = m1.at<Vec3f>(row, col)[2]; // red
 	
	dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
	dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
	dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);

而对于一幅图像,我们需要根据其类型来选择不同的方式,所以我们需要判断一下图像的通道数:

if (src.channels() == 3) {
	float b = m1.at<Vec3f>(row, col)[0];// blue
	float g = m1.at<Vec3f>(row, col)[1]; // green
	float r = m1.at<Vec3f>(row, col)[2]; // red

	dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
	dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
	dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
}
else if (src.channels() == 1) {
	float f = src.at<uchar>(row, col);
	dst.at<uchar>(row, col) = saturate_cast<uchar>(f*alpha + beta);
}

有了这些实现的核心代码,我们就可以遍历所有的像素点,来调整图像了。全部代码如下:

	Mat YT = imread("./image/YiTian1.jpg");
	if (!YT.data)
	{
		cout << "ERROR : could not load image.\n";
		return -1;
	}
	imshow("倚天屠龙记", YT);

	Mat new_YT = YT.clone();
	float alpha = 1.0; // 调整对比度
	float beta = -50;   // 调整亮度
	YT.convertTo(YT, CV_32F);
	for (int row = 0; row < YT.rows; row++) {
		for (int col = 0; col < YT.cols; col++) {
			if (YT.channels() == 3) {
				float b = YT.at<Vec3f>(row, col)[0];// blue
				float g = YT.at<Vec3f>(row, col)[1]; // green
				float r = YT.at<Vec3f>(row, col)[2]; // red
	
				new_YT.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
				new_YT.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
				new_YT.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
			}
			else if (YT.channels() == 1) {
				float f = YT.at<uchar>(row, col);
				new_YT.at<uchar>(row, col) = saturate_cast<uchar>(f*alpha + beta);
			}
		}
	}
	imshow("倚天-调整亮度", new_YT);

执行结果如下:

调整对比度:alpha = 1.2,beta = 0
调整对比度:alpha = 0.8,beta = 0
调整亮度:alpha = 1.0,beta = 50
调整亮度:alpha = 1.0,beta = -50

4、API-convertScaleAbs

OpenCV中提供了API-convertScaleAbs,可以实现类似的功能,这个API的功能是:

缩放、计算绝对值并将结果转换为8位。

API如下:

void convertScaleAbs(
    InputArray src, 
    OutputArray dst,                                  
    double alpha = 1, 
    double beta = 0
);

参数含义如下:

(1)InputArray类型的src,输入图像。

(2)OutputArray类型的dst,输出图像。

(3)double类型的alpha,可选比例因子。。

(4)double类型的beta,添加到缩放值的可选增量。

举个栗子:

	Mat YT = imread("./image/YiTian1.jpg");
	if (!YT.data)
	{
		cout << "ERROR : could not load image.\n";
		return -1;
	}
	imshow("倚天屠龙记", YT);

        Mat YT_convertScaleAbs;

	convertScaleAbs(YT, YT_convertScaleAbs,1.9,-50);
	imshow("倚天屠龙记-convertScaleAbs", YT_convertScaleAbs);

调整后的图像

二、伽马校正

1、伽马校正引入

我们前面通过线性的方式调整图像的亮度和对比度,调整之后,效果可能不是那么理想,毕竟“直男”太直了!

所以我们需要考虑非线性调整方式,看看如果是非线性,会有什么样的效果呢?

这就是我们接下来要讲的伽马校正。

2、伽马校正原理

伽马校正数学公式很简单:

I表示的是输入图像,O表示输出图像,γ是参数,我们可以通过调整不同的γ得到不同的结果:

不同伽马值的绘图

3、伽马校正代码实现

理解了公式,我们实现也很简单:

if (YT.channels() == 3) {
	float b = YT.at<Vec3f>(row, col)[0];// blue
	float g = YT.at<Vec3f>(row, col)[1]; // green
	float r = YT.at<Vec3f>(row, col)[2]; // red

	new_YT.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(pow(b / 255.0, gamma) * 255.0);
	new_YT.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(pow(g / 255.0, gamma) * 255.0);
	new_YT.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(pow(r / 255.0, gamma) * 255.0);
}
else if (YT.channels() == 1) {
	float f = YT.at<uchar>(row, col);
	new_YT.at<uchar>(row, col) = saturate_cast<uchar>(pow(f / 255.0, gamma) * 255.0);
}

有了这些实现的核心代码,我们就可以遍历所有的像素点,来调整图像了。全部代码如下:

	Mat YT = imread("./image/YiTian1.jpg");
	if (!YT.data)
	{
		cout << "ERROR : could not load image.\n";
		return -1;
	}
	imshow("倚天屠龙记", YT);	

        Mat new_YT = YT.clone();
	YT.convertTo(YT, CV_32F);
	float gamma = 1.6;

	for (int row = 0; row < YT.rows; row++) {
		for (int col = 0; col < YT.cols; col++) {
			if (YT.channels() == 3) {
				float b = YT.at<Vec3f>(row, col)[0];// blue
				float g = YT.at<Vec3f>(row, col)[1]; // green
				float r = YT.at<Vec3f>(row, col)[2]; // red

				new_YT.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(pow(b / 255.0, gamma) * 255.0);
				new_YT.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(pow(g / 255.0, gamma) * 255.0);
				new_YT.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(pow(r / 255.0, gamma) * 255.0);
			}
			else if (YT.channels() == 1) {
				float f = YT.at<uchar>(row, col);
				new_YT.at<uchar>(row, col) = saturate_cast<uchar>(pow(f / 255.0, gamma) * 255.0);
			}
		}
	}

	imshow("倚天-伽马校正", new_YT);

执行结果如下:

今天我们讲的就到这里啦,大家要多做练习才能掌握更深哦!

猜你喜欢

转载自blog.csdn.net/shuiyixin/article/details/106669332