f(row, col)
:原始图像的像素。g(row, col)
:调整后图像的像素。a(a>0
:称为增益(gain),常常被用来控制图像的对比度,其取值范围一般为0.0-3.0b
:称为偏置(bias),常常被用来控制图像的亮度。g(row, col) = a*f(row, col) + b
:随原始图像进行对比度亮度调节的公式。new_img.at (row, col)[c]
:opencv访问图片每个像素的语法。saturate_cast()
:防止溢出。当运算完之后,结果为负,则转为0,结果超出255,则为255。遍历原始图片的每一个像素,对R,G,B分别使用公式进行变换。
void contrast_bright (int, void*)
{
contrastValue_f = 0.1 * contrastValue; // 为了使滑动条调节的幅度变大,把 contrastValue值进行缩小。
for (int row = 0; row < img.rows; row++)
{
for (int col = 0; col < img.cols; col++)
{
for (int c = 0; c < 3; c++) {
new_img.at<Vec3b> (row, col)[c] = saturate_cast<uchar>(contrastValue_f * (img.at<Vec3b> (row, col)[c]) + brightValue); // g(x,y) = af(x,y) + b; a 是对比度调节,b是亮度调节
}
}
}
imshow ("Effect Image", new_img);
}
Mat.at (row, col)[c]
:遍历图片像素的方法。https://docs.opencv.org/3.4/d5/d98/tutorial_mat_operations.htmlsaturate_cast
:防止溢出。网上关于图片饱和度调整的算法很多,不知道选择哪一个。所以就都试一遍看看哪个效果好。这里,参考了:https://blog.csdn.net/u012198575/article/details/82985482 这篇文章提及的算法,算法思路写的比较清楚。
void saturability (int, void*)
{
float increment = (saturation - 80) * 1.0 / max_increment;
for (int col = 0; col < img.cols; col++)
{
for (int row = 0; row < img.rows; row++)
{
// R,G,B 分别对应数组中下标的 2,1,0
uchar r = img.at<Vec3b> (row, col)[2];
uchar g = img.at<Vec3b> (row, col)[1];
uchar b = img.at<Vec3b> (row, col)[0];
float maxn = max (r, max (g, b));
float minn = min (r, min (g, b));
float delta, value;
delta = (maxn - minn) / 255;
value = (maxn + minn) / 255;
float new_r, new_g, new_b;
if (delta == 0) // 差为 0 不做操作,保存原像素点
{
new_img.at<Vec3b> (row, col)[0] = new_b;
new_img.at<Vec3b> (row, col)[1] = new_g;
new_img.at<Vec3b> (row, col)[2] = new_r;
continue;
}
float light, sat, alpha;
light = value / 2;
if (light < 0.5)
sat = delta / value;
else
sat = delta / (2 - value);
if (increment >= 0)
{
if ((increment + sat) >= 1)
alpha = sat;
else
{
alpha = 1 - increment;
}
alpha = 1 / alpha - 1;
new_r = r + (r - light * 255) * alpha;
new_g = g + (g - light * 255) * alpha;
new_b = b + (b - light * 255) * alpha;
}
else
{
alpha = increment;
new_r = light * 255 + (r - light * 255) * (1 + alpha);
new_g = light * 255 + (g - light * 255) * (1 + alpha);
new_b = light * 255 + (b - light * 255) * (1 + alpha);
}
new_img.at<Vec3b> (row, col)[0] = new_b;
new_img.at<Vec3b> (row, col)[1] = new_g;
new_img.at<Vec3b> (row, col)[2] = new_r;
}
}
imshow ("Effect Image", new_img);
}
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int contrastValue; // 对比度值
float contrastValue_f; // 实际使用的对比度值,对contrastValue进行的缩小处理
int brightValue; // 亮度值
int saturation; // 饱和度
const int max_increment = 200;
Mat img, new_img; //img:原始图像; new_img:最终要展示图像;
void contrast_bright (int, void*);
void saturability (int, void*);
// 调整对比度和亮度
void contrast_bright (int, void*)
{
contrastValue_f = 0.1 * contrastValue;
for (int row = 0; row < img.rows; row++)
{
for (int col = 0; col < img.cols; col++)
{
for (int c = 0; c < 3; c++) {
new_img.at<Vec3b> (row, col)[c] = saturate_cast<uchar>(contrastValue_f * (img.at<Vec3b> (row, col)[c]) + brightValue); // g(x,y) = af(x,y) + b; a 是对比度调节,b是亮度调节
}
}
}
imshow ("Effect Image", new_img);
}
//调整饱和度
void saturability (int, void*)
{
float increment = (saturation - 80) * 1.0 / max_increment;
for (int col = 0; col < img.cols; col++)
{
for (int row = 0; row < img.rows; row++)
{
// R,G,B 分别对应数组中下标的 2,1,0
uchar r = img.at<Vec3b> (row, col)[2];
uchar g = img.at<Vec3b> (row, col)[1];
uchar b = img.at<Vec3b> (row, col)[0];
float maxn = max (r, max (g, b));
float minn = min (r, min (g, b));
float delta, value;
delta = (maxn - minn) / 255;
value = (maxn + minn) / 255;
float new_r, new_g, new_b;
if (delta == 0) // 差为 0 不做操作,保存原像素点
{
new_img.at<Vec3b> (row, col)[0] = new_b;
new_img.at<Vec3b> (row, col)[1] = new_g;
new_img.at<Vec3b> (row, col)[2] = new_r;
continue;
}
float light, sat, alpha;
light = value / 2;
if (light < 0.5)
sat = delta / value;
else
sat = delta / (2 - value);
if (increment >= 0)
{
if ((increment + sat) >= 1)
alpha = sat;
else
{
alpha = 1 - increment;
}
alpha = 1 / alpha - 1;
new_r = r + (r - light * 255) * alpha;
new_g = g + (g - light * 255) * alpha;
new_b = b + (b - light * 255) * alpha;
}
else
{
alpha = increment;
new_r = light * 255 + (r - light * 255) * (1 + alpha);
new_g = light * 255 + (g - light * 255) * (1 + alpha);
new_b = light * 255 + (b - light * 255) * (1 + alpha);
}
new_img.at<Vec3b> (row, col)[0] = new_b;
new_img.at<Vec3b> (row, col)[1] = new_g;
new_img.at<Vec3b> (row, col)[2] = new_r;
}
}
imshow ("Effect Image", new_img);
}
int main ()
{
img = imread ("test.jpg"); // 加载图片,保存在 Mat 对象 img 中
new_img = Mat::zeros (img.size (), img.type ()); // 最终要展示结果的对象
contrastValue = 1; //对比度初始值
brightValue = 1; //亮度初始值
saturation = 10; //饱和度初始值
namedWindow ("Effect Image", WINDOW_NORMAL); // 创建效果图窗口
createTrackbar ("Contrast:", "Effect Image", &contrastValue, 100, contrast_bright); // 创建对比度滑动条
createTrackbar ("Brightness:", "Effect Image", &brightValue, 200, contrast_bright); // 创建亮度滑动条
createTrackbar ("Saturability:", "Effect Image", &saturation, 200, saturability); // 创建饱和度滑动条
// 函数回调,因为是全局变量,所以 userdata 为0
contrast_bright (contrastValue, 0);
contrast_bright (brightValue, 0);
saturability (saturation, 0);
cv::waitKey (0);
return 0;
}