0%

图像亮度对比度调整

读写像素

读一个GRAY像素点的像素值(CV_8UC1)

1
2
3
4
//方式一
Scalar intensity = img.at<uchar>(y, x);
//方式二
Scalar intensity = img.at<uchar>(Point(x, y));

读一个BGR像素点的像素值

1
2
3
4
5
6
7
8
9
10
//读取整形
Vec3b intensity = src.at<Vec3b>(x, y);
int b = intensity [0];
int g = intensity [1];
int r = intensity [2];
//读取浮点型
Vec3f intensity = img.at<Vec3f>(y, x);
float blue = intensity.val[0];
float green = intensity.val[1];
float red = intensity.val[2];

Vec3b与Vec3F

  • Vec3b对应三通道的顺序是blue、green、red的uchar类型数据。
  • Vec3f对应三通道的float类型数据
  • 把CV_8UC1转换到CV32F1实现如下:src.convertTo(dst, CV_32F);

修改像素值

灰度图像
img.at(y, x) = 128;

RGB三通道图像
img.at(y,x)[0]=128; // blue
img.at(y,x)[1]=128; // green
img.at(y,x)[2]=128; // red

空白图像赋值
img = Scalar(0);

ROI选择
Rect r(10, 10, 100, 100);
Mat smallImg = img(r);

获取灰度图像素点

接下来用灰度图的反色作为示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{

Mat src = imread("C:\\Users\\Tim\\Desktop\\Image\\a.jpg");
if (src.empty())
{
cout << "load image filed..." << endl;
return -1;
}

namedWindow("src_window", CV_WINDOW_AUTOSIZE);
imshow("src_window", src);

Mat src_gray;
cvtColor(src, src_gray, CV_BGR2GRAY);

namedWindow("src_gray_window", CV_WINDOW_AUTOSIZE);
imshow("src_gray_window", src_gray);

int height = src_gray.rows;
int width = src_gray.cols;

//单通道
for (int row = 0; row < height;row++){
for (int col = 0; col < width; col++) {
//给每一个像素点取反
int gray = src_gray.at<uchar>(row, col);
src_gray.at<uchar>(row, col) = 255 - gray;
}
}
//展示灰度图的反色图
namedWindow("gray_window",CV_WINDOW_AUTOSIZE);
imshow("gray_window", src_gray);
waitKey(0);
return 0;
}

mark

获取三通道图像素点

对于三通道的像素点获取,以获取原图的反色图为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{

Mat src = imread("C:\\Users\\Tim\\Desktop\\Image\\a.jpg");
if (src.empty())
{
cout << "load image filed..." << endl;
return -1;
}

namedWindow("src_window", CV_WINDOW_AUTOSIZE);
imshow("src_window", src);

Mat src_gray;
cvtColor(src, src_gray, CV_BGR2GRAY);

int height = src_gray.rows;
int width = src_gray.cols;

//三通道图像初始化
Mat dst;
dst.create(src.size(), src.type());
height = src.rows;
width = src.cols;

//获取通道数
int cn = src.channels();

for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (cn == 1) {
//单通道图的处理,这块也就是上面的代码
int gray = src_gray.at<uchar>(row, col);
src_gray.at<uchar>(row, col) = 255 - gray;
}
else if(cn == 3){
//从原图中读取
int b = src.at<Vec3b>(row, col)[0];
int g = src.at<Vec3b>(row, col)[1];
int r = src.at<Vec3b>(row, col)[2];
//写入目标图像
dst.at<Vec3b>(row, col)[0] = 255 - b;
dst.at<Vec3b>(row, col)[1] = 255 - g;
dst.at<Vec3b>(row, col)[2] = 255 - r;
}
}
}
//使用API进行操作
//bitwise_not(src, dst)
namedWindow("gray_window",CV_WINDOW_AUTOSIZE);
imshow("gray_window", dst);
waitKey(0);
return 0;
}

mark

上面的反色,使用API也可以做到:bitwise_not(src, dst)

三通道图转为灰度图的其他方式

mark

通过把原图中的像素点设定为像素点中的最大或者最小值也是可以达到转换为灰度图的效果!

图像混合

理论-线性混合操作

mark

相关API (addWeighted)

参数1:输入图像Mat – src1
参数2:输入图像src1的alpha值
参数3:输入图像Mat – src2
参数4:输入图像src2的alpha值
参数5:gamma值
参数6:输出混合图像
注意点:两张图像的大小和类型必须一致才可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{

Mat src1 = imread("C:\\Users\\Tim\\Desktop\\Image\\a.jpg");
Mat src2 = imread("C:\\Users\\Tim\\Desktop\\Image\\b.jpg");
Mat ret;//输出图像
if (src1.empty() || src2.empty())
{
cout << "load image filed..." << endl;
return -1;
}

namedWindow("src1", CV_WINDOW_AUTOSIZE);
imshow("src1", src1);
namedWindow("src2", CV_WINDOW_AUTOSIZE);
imshow("src2", src2);

double alpha = 0.5;
//两张图片大小一致、类型一致
if (src1.rows == src2.rows && src1.cols == src2.cols
{
addWeighted(src1, alpha, src2, (1.0 - alpha), 0, ret);
//add(src1, src2, dst, Mat());
//multiply(src1, src2, dst, 1.0);
namedWindow("ret", CV_WINDOW_AUTOSIZE);
imshow("ret", ret);
}
else
{
cout << "Images vary in size or type!" << endl;
return -1;
}

waitKey(0);
return 0;
}

这是两张图片各占50%的权重合成的图像:

mark

对比度和亮度调整

图像亮度本质上图像中每个像素的亮度,每个像素的亮度本质上RGB值的大小,RGB值为0,则像素点为黑色,RGB都为255时像素点最亮,为白色。对比度则是不同像素点之间的差值,差值越大,对比度越明显。
图像变换可以看作如下:

  • 像素变换 – 点操作
  • 邻域操作 – 区域

mark

调整图像亮度和对比度属于像素变换-点操作,搞清楚原理下面开始撸代码:

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <opencv2\opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{

Mat src = imread("C:\\Users\\Tim\\Desktop\\Image\\a.jpg");
Mat dst;
if (src.empty())
{
cout << "load image filed..." << endl;
return -1;
}
char *input_win = "input_win";
namedWindow(input_win, CV_WINDOW_AUTOSIZE);
imshow("input_win", src);

int height = src.rows;
int wight = src.cols;

//构建与src同等大小的空白图
dst = Mat::zeros(src.size(), src.type());

//差异倍数
float alpha = 0.4f;
//增益变量
float beta = 0.0f;

for (int row = 0; row < height; row++) {
for (int col = 0; col < wight; col++) {
//根据通道数目来判断处理方式
if (src.channels() == 3){
float b = src.at<Vec3b>(row, col)[0];
float g = src.at<Vec3b>(row, col)[1];
float r = src.at<Vec3b>(row, col)[2];

dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
}
else if (src.channels() == 1) {
float v = src.at<uchar>(row, col);
dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);
}
}
}

//输出图像
char *output_win = "output_win";
namedWindow(output_win, CV_WINDOW_AUTOSIZE);
imshow(output_win, dst);

waitKey(0);
return 0;
}

从代码中可以看出:如果将值乘上差异倍数,那么像素点之间的差异倍数(alpha)越大,像素点之间的差异也就越大,这样导致的直接结果就是图像的对比度增强增益变量越大,那么像素本身的值在增大,导致的结果就是亮度增强

演示效果

mark

重要的API

  • Mat new_image = Mat::zeros(image.size(), image.type()); 创建一张跟原图像大小和类型一致的空白图像、像素值初始化为0
  • saturate_cast(value)确保值大小范围为0~255之间
  • Mat.at(y,x)[index]=value给每个像素点每个通道赋值
  • 本文作者: Tim
  • 本文链接: https://zouchanglin.cn/2477429062.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!