目标
- 访问像素值
- 用0来初始化矩阵
- cv::saturate_cast
- 像素转换
- 提高一张图像的亮度
原理
图像处理
图像变换可以被视作两个步骤:
- 点操纵(像素转换)
- 相邻区域转换(以面积为基础)
像素转换
- 在这种图像处理的转换过程中,每个输出的像素的值都取决于相对应的输入的像素的值。
- 此类操作的示例包括亮度和对比度调整以及颜色校正和转换。
亮度和对比度的调整
- 两种常用的点处理是带常数的乘法和加法:
- 参数α > 0 和 β 通常被叫做gain和bias参数,该参数将被用来控制对比度和亮度。
- 你可以简单地把f(x)当作原图像素,g(x)当作输出图像的像素,那么我们可以将表达式写成:
此处的i和j代表像素点的位置,i行j列
源码
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cin;
using std::cout;
using std::endl;
using namespace cv;
int main( int argc, char** argv )
{
// 加载图像
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
Mat image = imread( samples::findFile( parser.get<String>( "@input" ) ) );
if( image.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
// 构建一个新的与原图相同(大小和类型)的像素为零的图像
Mat new_image = Mat::zeros( image.size(), image.type() );
// 获取参数 α和β
double alpha = 1.0; /*< Simple contrast control */
int beta = 0; /*< Simple brightness control */
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;
// 遍历图像,应用线性变化
// 需注意一个像素点有三个通道(B,G,R or 0, 1, 2)
for( int y = 0; y < image.rows; y++ ) {
for( int x = 0; x < image.cols; x++ ) {
for( int c = 0; c < image.channels(); c++ ) {
new_image.at<Vec3b>(y,x)[c] =
saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );
}
}
}
imshow("Original Image", image);
imshow("New Image", new_image);
waitKey();
return 0;
}
此外,除了使用上述的for循环来遍历图片的每一个像素点,我们还可以使用cv::Mat::convertTo来实现,只是上述代码用来更加详细的展示其应用过程。
image.convertTo(new_image, -1, alpha, beta);
α和β参数
伽马矫正是另一个用来矫正图片亮度的技术。增加或减少β值将会为每一个像素增加或减少一个固定的常量值。像素值不在[0,255]范围内的将会被饱和,超过255的被压缩到255,小于0的被压缩到0。
浅灰色为原始图像的直方图,Gimp中亮度= 80时为深灰色。(GIMP(GNU Image Manipulation Program)是一款自由和开源的图像编辑软件,用于图像的润色、编辑和制作。)
直方图表示每个颜色级别具有该颜色级别的像素数。深色图像会有许多低颜色值的像素,因此直方图会在其左侧呈现一个峰值。当添加恒定偏差时,直方图向右移动,因为我们已经向所有像素添加了恒定偏差。
α参数将会修改色阶分布的方式。如果α小于1,色阶将被压缩,结果将是一个对比度较低的图像。
浅灰色为原始图像的直方图,Gimp中对比度< 0时为深灰色。
请注意,这些直方图是使用Gimp软件中的亮度-对比度工具获得的。亮度工具应该与β偏置参数相同,但对比度工具似乎与α增益不同,其中输出范围似乎以Gimp为中心(正如您可以在前面的直方图中注意到的那样)。
简单来说,β和亮度相关,但在提高亮度的同时,对比度也会降低,图像会出现轻微的模糊。α增益可以用来通过调整对比度减少这种影响,但由于过于饱和,我们也可能失去一些原来明亮区域的细节。具体两个参数的值设置成多少要看具体需求。
伽马矫正
伽马矫正可以通过使用一个从输入的值到映射的输出值的非线性转换来矫正一个图像的亮度。如下所示:
由于联系的非线性,对于所有的像素的影响并不相同,且受限于其的原本的值。
当γ<1时,原本的黑暗区域将会变得更亮,直方图会向右平移,代表亮度的提高。这种情况适用于增强图像中暗处的细节。
当γ>1时,原本的明亮区域将会变得更暗,直方图会向左平移,代表亮度的减少。这种情况适用于则增强图像中亮部的细节或整体实现较暗的色调。
通过调整γ值,可以有效地根据需求来突出不同的特征。
实际的例子 link
举一个实际的例子,来矫正一个曝光不足的图片,参数为α=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);
查询表被用来提高计算性能,伽马校正(LUT)的计算通常涉及到对每个像素进行幂运算,而查找表方法只需要预先计算出256个值,然后在实际处理图像时快速查找和应用这些预先计算的值,从而加快处理速度。
额外资源
- CRT显示器上的伽玛校正和图像
- 图形渲染中的伽马矫正
- 数码曝光技术