图像亮度对比度调整

读写像素

读一个 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 给每个像素点每个通道赋值