提升图像对比度和亮度


注:原创不易,转载请务必注明原作者和出处,感谢支持!

一 提升图像对比度和亮度

一般来说图像的变换可以分成以下两类:
(1)像素变换
在像素变换中,仅仅根据输入的像素值(有时可能加上某些全局信息或者参数)来计算相应的输出像素值。该类变换的常见方法有亮度和对比度调整、颜色校正和颜色变换等等。该类变换可以用下面的通用变换公式来表示。\(f(x,y)\)表示原图像\((x,y)\)处的像素值,\(T\)表示变换函数,\(g(x,y)\)表示输出图像\((x,y)\)处的像素值。可以看到输入仅有当个位置的像素值。

\[g(x, y) = T[f(x, y)] \]

(2)邻域变换
在邻域变换中,变换函数的输入可以是多个像素所构成的一个邻域,然后根据变换函数计算相应的像素值输出。邻域变换最常见的方法有图像卷积操作。对于图像卷积,假设中心锚点位置为\((x,y)\),滤波器大小为\(m \times n\),且\(m\)\(n\)均为奇数(滤波器大小一般均为奇数)。令\(a = (m-1)/2\)\(b = (n-1)/2\),矩阵\(w\)为滤波器系数,原图像为\(f\),输出图像为\(g\),则图像的卷积操作可以用下面的通用公式表示。

\[g(x,y) = \sum_{s=-a}^a \sum_{t=-b}^b w(s,t)f(x+s, y+t) \]

而提升图像对比度和亮度是一个典型的像素变换操作,它的变换公式表示如下。如果你想要提升图像亮度(针对灰度图),那你只需要令\(\alpha = 1\),并使\(\beta\)的值大于0,使图像的亮度得到一个增益即可。此时,图像的每个像素都将提升\(\beta\)个灰度值。如果你想要提升图像对比度,那你只需要令\(\beta = 0\),并使\(\alpha\)的值大于1,使得图像相邻像素差值变大从而提升图像的对比度。如果你既要提升对比度和亮度,则只需要选择\(\alpha\)为大于1的合适值和\(\beta\)为大于0的合适值即可。

\[g(x,y) = \alpha f(x, y) + \beta \]


二 代码实现

OpenCV中提供了一个实现提升图像对比度和亮度的APIconvertTo()如下。

void cv::Mat::convertTo(
	OutputArray m,		// 输出图像
    int rtype,			// 输出图像类型,取-1则表示类型与原图相同
    double alpha = 1,	// alpha值
    double beta = 0		// beta值
) const;

如果不使用convertTo(),则可以将手写代码实现如下。

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{
	// load source image and show it
	Mat src = imread("D:\\IMG\\lena.jpg", IMREAD_UNCHANGED);
	if (!src.data)
	{
		cout << "Error : could not load image." << endl;
		return -1;
	}
	imshow("input", src);

	// parameters for enhance contrast
	double alpha = 2.0;
	double beta = 0.0;

	Mat dst(src.size(), src.type());
	decltype(src.rows) row, col;
	for (row = 0; row < src.rows; ++row)
	{
		for (col = 0; col < src.cols; ++col)
		{
			// single channel (gray image) or three channels (BGR image) only
			if (src.channels() == 1)
			{
				int gray = src.at<uchar>(row, col);
				dst.at<uchar>(row, col) = saturate_cast<uchar>(gray * alpha + beta);
			}
			else if (src.channels() == 3)
			{
				int b = src.at<Vec3b>(row, col)[0];
				int g = src.at<Vec3b>(row, col)[1];
				int r = src.at<Vec3b>(row, col)[2];
				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);
			}
		}
	}
	imshow("output (alpha = 2)", dst);

	waitKey(0);
	return 0;
}

如果使用APIconvertTo(),则可以简单地作如下实现。

src.convertTo(dst, -1, alpha, beta);

三 实现效果

(1) 单纯提升亮度
原图

手写代码实现,\(\beta\)分别为50、100、150

调用API实现,\(\beta\)分别为50、100、150

(2) 单纯提升对比度
原图

手写代码实现,\(\alpha\)分别为2、3、4

调用API实现,\(\alpha\)分别为2、3、4

(3) 同时提升对比度和亮度,\(\alpha = 3\)\(\beta = 50\)

可以看到,手写代码实现的效果与调用API实现的效果略有不同,但大体效果是不差多少的。在熟悉了API实现原理之后,以后就可以直接调用API进行实现而无需自己手动写代码实现。

posted @ 2019-07-27 10:26  wallace-rice  阅读(3724)  评论(0编辑  收藏  举报