目标
在本教程中,将学习如何:
访问像素值
用零初始化矩阵
了解cv :: saturate_cast是什么
获取关于像素变换的一些很酷的信息
在实际的例子中提高图像的亮度
理论
下面的解释属于Richard Szeliski 的“ 《计算机视觉:算法与应用》 ”一书
图像处理
一般的图像处理算子是采用一个或多个输入图像并产生输出图像的函数。
图像变换可以看作:
点运算(像素变换)
邻域(区域)运算
像素变换
在这种图像处理变换中,每个输出像素的值只取决于相应的输入像素值(可能是一些全局采集的信息或参数)。
这些操作的示例包括亮度和对比度调整以及颜色校正和变换。
亮度和对比度调整
- 两个常用的点处理过程是乘法和加法:
- 参数α > 0和β通常被称为增益和偏置参数; 有时这些参数据分别控制对比度和亮度。
- 你可以想到f(x )作为源图像像素和g(x )作为输出图像像素。然后,我们可以更方便地将表达式写成:
i 和 j 表示像素位于第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("lena.bmp"); // 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;
}
解释
- 要访问图像中的每个像素,使用这种语法:image.at <Vec3b>(y,x)[c]其中y是行,x是列,c是R,G或B(0,1或2)。
- 由于操作α·p(i,j)+β可以给出值超出范围或不是整数(如果α是float),我们使用cv :: saturate_cast来确保这些值是有效的。
- 如果不使用for循环访问每个像素,我们可以简单地使用这个命令:
image.convertTo(new_image,-1,alpha,beta);
其中cv :: Mat :: convertTo将有效地执行* new_image = a * image + beta *。
但是,我想告诉你如何访问每个像素。在任何情况下,两种方法都给出相同的结果,但是convertTo是更优化的,并且工作得更快。
结果
联系实际
在本段中,将通过调整图像的亮度和对比度来实践我们所学到的来校正曝光不足的图像。我们还将看到另一种技术来校正称为伽玛校正图像亮度。
亮度和对比度调整
增加(/减少)β值将为每个像素添加(/减少)常量值。像素值超出[0; 255]范围将饱和(即像素值高于0-255)将被钳位到0-255中。
直方图表示每个颜色级别具有该颜色级别的像素数。黑暗的图像将具有许多具有低颜色值的像素,因此直方图将在其左部分呈现峰值。当添加恒定偏差时,直方图向右移动,因为我们向所有像素添加了一个恒定的偏置。
该α参数将修改水平如何传播。如果α < 1,颜色级别将被压缩,结果将是对比度较小的图像。
请注意,这些直方图是使用Gimp软件中的Brightness-Contrast工具获得的。亮度工具应与β相同偏差参数,但对比度工具似乎不同于α 输出范围似乎以Gimp为中心的增益(可以注意到先前的直方图)。
可以发生的是修改β偏差将提高亮度,但是同时,随着对比度的降低,图像会出现轻微的面纱。该α 增益可以用来减少这种影响,但由于饱和度,我们将在原来的明亮区域中丢失一些细节。
伽玛校正
伽马校正可用于通过使用输入值和映射的输出值之间的非线性变换来校正图像的亮度:
由于该关系是非线性的,因此对于所有像素的效果将不同,并且将取决于它们的原始值。
当γ< 1,原始的黑暗区域会更亮,直方图将向右移动,而与γ相反> 1。
纠正曝光不足的图像
以下图像已更正:α = 1.3,β= 40
总体亮度得到改善,但您可以注意到,由于所使用的实现的数值饱和度(在摄影中
突出显示剪切),云层现在已经饱和了。
以下图像已被更正:γ= 0.4。
伽马校正应倾向于增加较少的饱和效应,因为映射是非线性的,并且没有像以前的方法那样的数值饱和。
上图比较了三个图像的直方图(三个直方图之间的y范围不一样)。可以注意到,大多数像素值都在原始图像的直方图的较低部分。经过α,β校正,由于饱和度以及右侧偏移,我们可以在255观察到一个高峰。在伽马校正之后,直方图向右移动,但暗区域中的像素比明亮区域中的像素更大偏移(参见伽马曲线图)。
伽马校正代码:
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个值。