OpenCV计算机图像视觉基础学习笔记3——对图像的操作

咕了一个多星期我又来更新了(

由于学车耽误了一些学习计划,这半个月里可能要对原定的计划稍微修改一下。


图像的逐像素操作

使用逐像素操作可以整体修改图像的颜色。以取反色为例,核心语句是gray_src.at<uchar>(row, col) = 255 - gray;,这是灰度图的操作,对BGR彩色图的操作后面会提到。首先定义一个gray变量为gray_src.at<uchar>(row, col),它代表gray_src这张图的(row,col)坐标的像素。然后用255减去这个值得到反色。整个过程通过一个二重循环控制rowcol完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Mat src, gray_src;
int height = src.rows; // 获取图片的高度
int width = src.cols; // 获取图片的宽度
// ps: 外国人所理解的行和列与中国人正好相反,对于他们来说,纵列为行,横列为列。

cvtColor(src, gray_src, CV_BGR2GRAY); // 使用cvtColor函数把原图转化成灰度图

for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int gray = gray_src.at<uchar>(row, col);
gray_src.at<uchar>(row, col) = 255 - gray;
// 用255减去原来的灰度值得到灰度反色
}
}

下面说说彩色图片的反色操作,尽管你可以直接用bitwise_not(src, notsrc);直接完成反色操作,不过这里还是说一下如果要自己写的话应该怎么写。

我们知道,BGR图像的通道是3。在二重循环取像素的时候,每一个像素点里是包含了三个值的颜色,需要对这三个值都进行255 - x的操作才算是可以。其实这里用的类型就从<uchar>变成了<Vec3b>,通过下标[0] [1] [2]进行访问。下面是核心代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Mat src, dst_src;
int height = src.rows;
int width = src.cols;
dst_src = src.clone();

for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
// 三通道做法,设定bgr,对三个通道各自进行取反色。
int b = dst_src.at<Vec3b>(row, col)[0];
int g = dst_src.at<Vec3b>(row, col)[1];
int r = dst_src.at<Vec3b>(row, col)[2];
dst_src.at<Vec3b>(row, col)[0] = 255 - b;
dst_src.at<Vec3b>(row, col)[1] = 255 - g;
dst_src.at<Vec3b>(row, col)[2] = 255 - 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
Mat src, gray_src, dst_src;

src = imread("C:/Users/ssdrt/Pictures/pic.jpg");
if (src.empty()) {
cout << "could not load this image" << endl;
return -1;
}

int height = src.rows;
int width = src.cols;
dst_src = src.clone();

cvtColor(src, gray_src, CV_BGR2GRAY);

for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int gray = gray_src.at<uchar>(row, col);
gray_src.at<uchar>(row, col) = 255 - gray;
// 用255减去原来的灰度值得到灰度反色
}
}

for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
// 三通道做法,设定bgr,对三个通道各自进行取反色。
int b = dst_src.at<Vec3b>(row, col)[0];
int g = dst_src.at<Vec3b>(row, col)[1];
int r = dst_src.at<Vec3b>(row, col)[2];
dst_src.at<Vec3b>(row, col)[0] = 255 - b;
dst_src.at<Vec3b>(row, col)[1] = 255 - g;
dst_src.at<Vec3b>(row, col)[2] = 255 - r;
}
}

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

namedWindow("test gray src", CV_WINDOW_AUTOSIZE);
imshow("test gray src", gray_src);

namedWindow("test reverse src", CV_WINDOW_AUTOSIZE);
imshow("test reverse src", dst_src);

Mat notsrc;

bitwise_not(src, notsrc);

namedWindow("test not src", CV_WINDOW_AUTOSIZE);
imshow("test not src", notsrc);

waitKey(0);
return 0;
}

图像的线性混合操作

简单来讲,是把两张相同尺寸的图片(这个一定注意,必须是尺寸相同)按照一定的透明比例混合在一起达到一个融合的效果。它可以通过一个公式来表示。

其中,g(x)代表混合之后的图像,f~0~(x)和f~1~(x)代表两张图片。这里的图片是用函数形式表示的,道理倒是也讲得通:图片可以看成一个二维的矩阵,对于一个给定的x值,总能通过一个对应关系找到对应的y值,就能确定图中一个点的位置。至于那个α,它代表的是混合的时候两张图片的透明度,RGBA格式应该不陌生吧。这里α的取值为(0,1)

在openCV中,给出了混合操作的APIaddWeighted,它接收六个参数,分别为图像1,图像1的α值,图像2,图像2的α值,γ值,输出位置。需要知道的是γ值在这里充当一个校验值,当混合之后图像整体较暗,通过调整γ值就可以把图片的亮度调整回正常。同样的,当亮度较高时将γ值取负就能降低亮度。如果有什么不明白的看代码即可。

我写好之后使用闪4的结局插图测试了一下,效果意外的还不错。

用这两张图进行合成:


合成之后的效果(α=0.4, γ=-10.0):

合成之后的效果(α=0.5, γ=0.0):

源代码:

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
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
Mat src1, src2, dst;
src1 = imread("C:/Users/ssdrt/Pictures/新建文件夹/sen4ed1.png");
src2 = imread("C:/Users/ssdrt/Pictures/新建文件夹/sen4ed2.png");
if (!src1.data) {
cout << "could not load image1!" << endl;
return -1;
}

if (!src2.data) {
cout << "could not load image2!" << endl;
return -1;
}

double alpha = 0.4;
if (src1.rows == src2.rows && src1.cols == src2.cols && src1.type() == src2.type()) {
addWeighted(src1, alpha, src2, (1.0 - alpha), -10.0, dst);
namedWindow("demo", CV_WINDOW_FULLSCREEN);
imshow("demo", dst);

char c;
cout << "save or not?(y/n): ";
cin >> c;
if (c == 'y') {
imwrite("C:/Users/ssdrt/Pictures/新建文件夹/sen4ed4.png", dst);
cout << "image saved!" << endl;
}
else {
cout << "image not saved!" << endl;
}

}
else {
cout << "size error!" << endl;
return -1;
}

waitKey(0);
return 0;
}

-------------本文结束,感谢您的阅读转载请注明原作者及出处-------------


本文标题:OpenCV计算机图像视觉基础学习笔记3——对图像的操作

文章作者:Shawn Zhou

发布时间:2019年08月02日 - 11:08

最后更新:2019年08月02日 - 15:08

原始链接:http://shawnzhou.xyz/2019/08/02/19-08-02-01/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

知识无价,码字不易。对您有用,敬请打赏。金额随意,感谢关心。
0%