OpenCV改变图像的对比度和亮度
目标
在本教程中,您将学习如何:
- 访问像素值
- 用零初始化矩阵
- 了解cv :: saturate_cast是什么,为什么它是有用的
- 获取有关像素变换的一些很酷的信息
- 在一个实际的例子中提高图像的亮度
理论
- 注意
- 下面的解释属于Richard Szeliski 的“ 计算机视觉:算法与应用 ”一书
图像处理
- 一般的图像处理算子是采用一个或多个输入图像并产生输出图像的函数。
- 图像变换可以看作:点运算符(像素变换)邻里(区域)运营商
像素变换
- 在这种图像处理变换中,每个输出像素的值仅取决于相应的输入像素值(可能是一些全局采集的信息或参数)。
- 这些操作者的示例包括亮度和对比度调整以及颜色校正和变换。
亮度和对比度调整
- 两个常用的点处理是乘法和加法:
- 参数和 beta通常称为增益和偏置参数; 有时这些参数被分别控制对比度和亮度。α>0β
- 您可以将视为源图像像素,g(x)作为输出图像像素。然后,我们可以更方便地将表达式写成:f(x)g(x)
其中和j表示像素位于第i行和第j列。ij
Code
以下代码执行:g(i,j)=α⋅f(i,j)+β
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main( int argc, char** argv )
{
double alpha = 1.0; /*< Simple contrast control */
int beta = 0; /*< Simple brightness control */
String imageName("../data/lena.jpg"); // by default
if (argc > 1)
{
imageName = argv[1];
}
Mat image = imread( imageName );
Mat new_image = Mat::zeros( image.size(), image.type() );
cout << " Basic Linear Transforms " << endl;
cout << "-------------------------" << endl;
cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;
cout << "* Enter the beta value [0-100]: "; cin >> beta;
for( int y = 0; y < image.rows; y++ ) {
for( int x = 0; x < image.cols; x++ ) {
for( int c = 0; c < 3; c++ ) {
new_image.at<Vec3b>(y,x)[c] =
saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
}
}
}
namedWindow("Original Image", WINDOW_AUTOSIZE);
namedWindow("New Image", WINDOW_AUTOSIZE);
imshow("Original Image", image);
imshow("New Image", new_image);
waitKey();
return 0;
}
说明
- 我们首先创建参数来保存用户要输入的和 beta:αβ
double alpha = 1.0; /*< Simple contrast control */
int beta = 0; /*< Simple brightness control */
- 我们使用cv :: imread加载图像并将其保存在Mat对象中:
String imageName("../data/lena.jpg"); // by default
if (argc > 1)
{
imageName = argv[1];
}
Mat image = imread( imageName );
- 现在,由于我们会对这个图像进行一些转换,所以我们需要一个新的Mat对象来存储它。此外,我们希望这具有以下功能:
- 初始像素值等于零
- 与原始图像相同的大小和类型
Mat new_image = Mat :: zeros(image.size(),image.type());
我们观察到cv :: Mat :: zeros返回基于image.size()和image.type()的Matlab风格的零初始化器,
- 现在,要执行我们将访问图像中的每个像素。由于我们使用BGR图像,我们将每像素(B,G和R)有三个值,因此我们也将分别访问它们。这是代码片段:g(i,j)=α⋅f(i,j)+β
for( int y = 0; y < image.rows; y++ ) { for( int x = 0; x < image.cols; x++ ) { for( int c = 0; c < 3; c++ ) { new_image.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta ); } } }
请注意以下事项:
- 初始像素值等于零
- 与原始图像相同的大小和类型
- 最后,我们创建窗口并显示图像,通常的方式。
namedWindow("Original Image", WINDOW_AUTOSIZE); namedWindow("New Image", WINDOW_AUTOSIZE); imshow("Original Image", image); imshow("New Image", new_image); waitKey();
image.convertTo(new_image,-1,alpha,beta);
其中cv :: Mat :: convertTo将有效地执行* new_image = a * image + beta *。但是,我们想告诉你如何访问每个像素。在任何情况下,两种方法都给出相同的结果,但是convertTo是更优化的,并且工作得更快。
结果
运行我们的代码并使用和 beta = 50α=2.2β=50
$ ./BasicLinearTransforms lena.jpg
Basic Linear Transforms
-------------------------
* Enter the alpha value [1.0-3.0]: 2.2
* Enter the beta value [0-100]: 50
我们得到这个:
实际例子
在本段中,我们将通过调整图像的亮度和对比度来实践我们学到的来校正曝光不足的图像。我们还将看到另一种技术来校正称为伽马校正的图像的亮度。
亮度和对比度调整
增加(/减少)值将为每个像素添加(/减)一个常量值。像素值超出[0; 255]范围将饱和(即像素值高于(/小于255)(/ 0)将被钳位到255(/ 0))。β
在浅灰色,原始图像的直方图,在深灰色当亮度= 80在Gimp
直方图表示每个颜色级别具有该颜色级别的像素数。黑暗的图像将具有许多具有低颜色值的像素,因此直方图将在其左部分呈现峰值。当添加恒定偏差时,直方图向右移动,因为我们向所有像素添加了一个恒定的偏置。
该参数将修改水平如何传播。如果 alpha <1,颜色级别将被压缩,结果将是对比度较小的图像。αα<1
在浅灰色,原始图像的直方图,在Gimp中对比度<0时,呈深灰色
请注意,这些直方图是使用Gimp软件中的“亮度 - 对比度”工具获得的。亮度工具应该与偏置参数相同,但是对比度工具似乎与 alpha增益不同,其中输出范围似乎以Gimp为中心(您可以注意到先前的直方图)。βα
可以发现,使用偏置可以提高亮度,但是同时,由于对比度会降低,图像会出现轻微的面纱。该阿尔法收益可以用来diminue这种效果,但由于饱和,我们将失去原有的明亮区域的一些细节。βα
伽玛矫正
伽马校正可以用于通过使用输入值和映射的输出值之间的非线性变换来校正图像的亮度:
由于该关系是非线性的,因此对于所有像素的效果将不同,并且将取决于它们的原始值。
绘制不同的伽玛值
当,原始的暗区域会更亮,直方图将向右移动,而 gamma> 1则会相反。γ<1γ>1
更正曝光不足的图像
以下图像已更正:和 beta = 40。α=1.3β=40
维基百科,自由的百科全书[CC BY-SA 3.0]
整体亮度得到改善,但您可以注意到,由于实施的数字饱和度,云彩现在已经饱和了(突出显示拍摄中的剪辑)。
以下图像已更正:。γ=0.4
维基百科,自由的百科全书[CC BY-SA 3.0]
伽马校正应倾向于增加较少的饱和效应,因为映射是非线性的,并且没有像以前的方法那样的数值饱和。
左:直方图alpha,β校正; 中心:原始图像的直方图; 右:伽马校正后的直方图
上图比较了三个图像的直方图(三个直方图之间的y范围是不一样的)。您可以注意到,大多数像素值位于原始图像的直方图的较低部分。在,更正后,由于饱和度以及右侧的偏移,我们可以在255观察到一个高峰。在伽马校正之后,直方图向右移动,但是暗区域中的像素比明亮区域中的像素更大偏移(参见伽马曲线图)。αβ
在本教程中,您已经看到了两种简单的方法来调整图像的对比度和亮度。它们是基本技术,不用于替代光栅图形编辑器!
Code
本教程的代码在这里。伽马校正代码:
Mat LookUpTable(1,256,CV_8U);
uchar * p = lookUpTable.ptr();
for(int i = 0; i <256; ++ i)
p [i] = saturate_cast <uchar>(pow(i / 255.0,gamma_)* 255.0);
Mat res = img.clone();
LUT(img,lookUpTable,res);
查询表用于提高计算性能,因为只需要计算256个值。