在本教程中,您将学习如何:
注意,以下解释属于Richard Szeliski 所着的Computer Vision:Algorithms and Applications一书
两个常用的点过程是乘法和加法与常量:
假设f(x)作为源图像像素,g(x)作为输出图像像素,上述表达式可以改写为:
其中,i和j表示像素位于第i行和第j列。
/**
* @file BasicLinearTransforms.cpp
* @brief Simple program to change contrast and brightness
* @author OpenCV team
*/
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include
// 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;
/**
* @function main
* @brief Main function
*/
int main( int argc, char** argv )
{
/// Read image given by user
//! [basic-linear-transform-load]
CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
Mat image = imread( parser.get( "@input" ) );
if( image.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " " << endl;
return -1;
}
//! [basic-linear-transform-load]
//! [basic-linear-transform-output]
Mat new_image = Mat::zeros( image.size(), image.type() );
//! [basic-linear-transform-output]
//! [basic-linear-transform-parameters]
double alpha = 1.0; /*< Simple contrast control */
int beta = 0; /*< Simple brightness control */
/// Initialize values
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;
//! [basic-linear-transform-parameters]
/// Do the operation new_image(i,j) = alpha*image(i,j) + beta
/// Instead of these 'for' loops we could have used simply:
/// image.convertTo(new_image, -1, alpha, beta);
/// but we wanted to show you how to access the pixels :)
//! [basic-linear-transform-operation]
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(y,x)[c] =
saturate_cast( alpha*image.at(y,x)[c] + beta );
}
}
}
//! [basic-linear-transform-operation]
//! [basic-linear-transform-display]
/// Show stuff
imshow("Original Image", image);
imshow("New Image", new_image);
/// Wait until user press some key
waitKey();
//! [basic-linear-transform-display]
return 0;
}
CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
Mat image = imread( parser.get( "@input" ) );
if( image.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " " << endl;
return -1;
}
Mat new_image = Mat::zeros( image.size(), image.type() );
// the same size and type of Mat img
我们观察到cv :: Mat :: zeros返回一个基于image.size()和image.type()的Matlab样式的零初始值设定项
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;
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(y,x)[c] =
saturate_cast( alpha*image.at(y,x)[c] + beta );
}
}
}
请注意以下内容(仅限C ++代码):
imshow("Original Image", image);
imshow("New Image", new_image);
waitKey();
注意
我们可以简单地使用此命令,而不是使用for循环来访问每个像素:
image.convertTo(new_image,-1,alpha,beta);
其中cv :: Mat :: convertTo将有效地执行* new_image = a * image + beta *。但是,我们想向您展示如何访问每个像素。在任何情况下,两种方法都给出相同的结果,但convertTo更优化,工作速度更快。
$ ./BasicLinearTransforms lena.jpg
基本线性变换
-------------------------
*输入alpha值[1.0-3.0]:2.2
*输入beta值[0-100]:50
我们得到这个:
在本段中,我们将通过调整图像的亮度和对比度来实践我们所学到的校正曝光不足图像的方法。我们还将看到另一种技术来校正称为伽马校正图像亮度。
增加(/减少)β将为每个像素添加(/减去)一个常量值。像素值在[0; 255]范围将饱和(即,高于(/小于)255(/ 0)的像素值将被截止到255(/ 0))。
原始图像的直方图,当Gimp中亮度= 80时为深灰色
直方图针对每个颜色级别表示具有该颜色级别的像素的数量。暗图像将具有许多具有低颜色值的像素,因此直方图将在其左侧部分呈现峰值。当添加恒定偏置时,直方图向右移动,因为我们已经为所有像素添加了恒定偏置。
在一个参数将修改级别的传播方式。如果α<1,颜色等级将被压缩,结果将是对比度较低的图像。
原始图像的直方图,当在Gimp中对比度<0时为深灰色
请注意,这些直方图是使用Gimp软件中的亮度 - 对比度工具获得的。亮度工具应与β相同偏差参数但对比工具似乎与α不同 输出范围似乎以Gimp为中心的增益(正如您在前面的直方图中所注意到的那样)。
β可能会发生偏差会改善亮度,但同时图像会出现轻微的面纱,因为对比度会降低。该α 增益可以用来减少这种效果,但由于饱和,我们将失去原始明亮区域的一些细节。
通过在输入值和映射的输出值之间使用非线性变换,Gamma校正可用于校正图像的亮度:
由于此关系是非线性的,因此对于所有像素,效果将不相同,并且将取决于它们的原始值。
绘制不同的伽玛值,当γ< 1中,原始的暗区将是明亮,直方图将被向右移位,而这将是与相对的γ> 1。
以下图像已更正为:α=1.3和β= 40。
整体亮度已得到改善,但您可以注意到,经过对比度调整之后,暗部细节得到了明显改善,与此同时,高亮度部分的云已经过饱和,使得天空颜色失真。
以下图像已用:γ校正= 0.4。
伽马校正应该倾向于增加图像的动态对比度,因为映射是非线性的并且不存在如先前方法中那样的数值饱和。
左:alpha,beta校正后的直方图; 中:原始图像的直方图; 右:伽马校正后的直方图
上图比较了三幅图像的直方图(三个直方图之间的y范围不同)。您可以注意到,大多数像素值位于原始图像直方图的下半部分。在α之后,b校正,由于饱和以及右移,我们可以观察到255处的大峰值。在伽马校正之后,直方图向右移动但是暗区域中的像素比亮区域中的像素更加偏移(参见伽马曲线图)。
伽马变换代码段:
C++
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
p[i] = saturate_cast(pow(i / 255.0, gamma_) * 255.0);
Mat res = img.clone();
LUT(img, lookUpTable, res);
完整代码:
#include
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
using std::cout;
using std::endl;
using namespace cv;
namespace
{
/** Global Variables */
int alpha = 100;
int beta = 100;
int gamma_cor = 100;
Mat img_original, img_corrected, img_gamma_corrected;
void basicLinearTransform(const Mat &img, const double alpha_, const int beta_)
{
Mat res;
img.convertTo(res, -1, alpha_, beta_);
hconcat(img, res, img_corrected);
imshow("Brightness and contrast adjustments", img_corrected);
}
void gammaCorrection(const Mat &img, const double gamma_)
{
CV_Assert(gamma_ >= 0);
//! [changing-contrast-brightness-gamma-correction]
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
p[i] = saturate_cast(pow(i / 255.0, gamma_) * 255.0);
Mat res = img.clone();
LUT(img, lookUpTable, res);
//! [changing-contrast-brightness-gamma-correction]
hconcat(img, res, img_gamma_corrected);
imshow("Gamma correction", img_gamma_corrected);
}
void on_linear_transform_alpha_trackbar(int, void *)
{
double alpha_value = alpha / 100.0;
int beta_value = beta - 100;
basicLinearTransform(img_original, alpha_value, beta_value);
}
void on_linear_transform_beta_trackbar(int, void *)
{
double alpha_value = alpha / 100.0;
int beta_value = beta - 100;
basicLinearTransform(img_original, alpha_value, beta_value);
}
void on_gamma_correction_trackbar(int, void *)
{
double gamma_value = gamma_cor / 100.0;
gammaCorrection(img_original, gamma_value);
}
}
int main( int argc, char** argv )
{
CommandLineParser parser( argc, argv, "{@input | ../data/lena.jpg | input image}" );
img_original = imread( parser.get( "@input" ) );
if( img_original.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " " << endl;
return -1;
}
img_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());
img_gamma_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());
hconcat(img_original, img_original, img_corrected);
hconcat(img_original, img_original, img_gamma_corrected);
namedWindow("Brightness and contrast adjustments");
namedWindow("Gamma correction");
createTrackbar("Alpha gain (contrast)", "Brightness and contrast adjustments", &alpha, 500, on_linear_transform_alpha_trackbar);
createTrackbar("Beta bias (brightness)", "Brightness and contrast adjustments", &beta, 200, on_linear_transform_beta_trackbar);
createTrackbar("Gamma correction", "Gamma correction", &gamma_cor, 200, on_gamma_correction_trackbar);
on_linear_transform_alpha_trackbar(0, 0);
on_gamma_correction_trackbar(0, 0);
waitKey();
imwrite("linear_transform_correction.png", img_corrected);
imwrite("gamma_correction.png", img_gamma_corrected);
return 0;
}
Python
lookUpTable = np.empty((1,256), np.uint8)
for i in range(256):
lookUpTable[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
res = cv.LUT(img_original, lookUpTable)
完整代码:
from __future__ import print_function
from __future__ import division
import cv2 as cv
import numpy as np
import argparse
alpha = 1.0
alpha_max = 500
beta = 0
beta_max = 200
gamma = 1.0
gamma_max = 200
def basicLinearTransform():
res = cv.convertScaleAbs(img_original, alpha=alpha, beta=beta)
img_corrected = cv.hconcat([img_original, res])
cv.imshow("Brightness and contrast adjustments", img_corrected)
def gammaCorrection():
## [changing-contrast-brightness-gamma-correction]
lookUpTable = np.empty((1,256), np.uint8)
for i in range(256):
lookUpTable[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
res = cv.LUT(img_original, lookUpTable)
## [changing-contrast-brightness-gamma-correction]
img_gamma_corrected = cv.hconcat([img_original, res]);
cv.imshow("Gamma correction", img_gamma_corrected);
def on_linear_transform_alpha_trackbar(val):
global alpha
alpha = val / 100
basicLinearTransform()
def on_linear_transform_beta_trackbar(val):
global beta
beta = val - 100
basicLinearTransform()
def on_gamma_correction_trackbar(val):
global gamma
gamma = val / 100
gammaCorrection()
parser = argparse.ArgumentParser(description='Code for Changing the contrast and brightness of an image! tutorial.')
parser.add_argument('--input', help='Path to input image.', default='lena.jpg')
args = parser.parse_args()
img_original = cv.imread(cv.samples.findFile(args.input))
if img_original is None:
print('Could not open or find the image: ', args.input)
exit(0)
img_corrected = np.empty((img_original.shape[0], img_original.shape[1]*2, img_original.shape[2]), img_original.dtype)
img_gamma_corrected = np.empty((img_original.shape[0], img_original.shape[1]*2, img_original.shape[2]), img_original.dtype)
img_corrected = cv.hconcat([img_original, img_original])
img_gamma_corrected = cv.hconcat([img_original, img_original])
cv.namedWindow('Brightness and contrast adjustments')
cv.namedWindow('Gamma correction')
alpha_init = int(alpha *100)
cv.createTrackbar('Alpha gain (contrast)', 'Brightness and contrast adjustments', alpha_init, alpha_max, on_linear_transform_alpha_trackbar)
beta_init = beta + 100
cv.createTrackbar('Beta bias (brightness)', 'Brightness and contrast adjustments', beta_init, beta_max, on_linear_transform_beta_trackbar)
gamma_init = int(gamma * 100)
cv.createTrackbar('Gamma correction', 'Gamma correction', gamma_init, gamma_max, on_gamma_correction_trackbar)
on_linear_transform_alpha_trackbar(alpha_init)
on_gamma_correction_trackbar(gamma_init)
cv.waitKey()
查找线性表用于提高计算性能,因为只需要计算一次256个值。