OpenCV计算机图像视觉基础学习笔记2——Mat对象与矩阵掩膜

最近在学车,好忙。。


Mat对象

保存图像使用的类型为自定义的Mat类型。计算机中把图像储存为一个二维数组。系统为Mat对象自动分配内存,不存在内存泄漏问题,它是一个面向对象的数据结构。Mat对象分为两部分:头部分和数据部分

在引进Mat对象之前,人们使用IplImage对象来管理图片。这是一个C风格的数据结构,在内存管理上有诸多不便,容易引起一些内存泄漏问题,它自2001年openCV发布后就一直存在,一直到openCV2.0后引进Mat对象,人们就开始把关注点更多的转向使用更方便的Mat。

常用的构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
Mat()

Mat(int rows, int cols, int type)

Mat(Size size, int type)

Mat(int rows, int cols, int type, const Scalar &s)

Mat(Size size, int type, const Scalar &s)

Mat(int ndims, const int *sizes, int type)

Mat(int ndims, const int *sizes, const Scalar &s)

简单举一个构造函数的例子:

Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255)),前两个参数分别表示行(row)和列(column),第三个参数常数CV_8UC3表示的含义可以这样解读:8表示每个通道占8位,U表示无符号,C表示char类型,3表示通道数目是3,第四个参数是一个向量,表示初始化每个像素值是多少,向量长度对应通道数目一致。

常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
void copyTo(Mat mat) // 复制图片

void convertTo(Mat dst, int type) // 负责转换数据类型不同的Mat,保证其他函数的兼容性

Mat clone() // 也是复制图片

int channels() //获取图像的通道数

int depth() // 获取图片的深度

bool empty() // 判断是否为空

uchar* ptr(i = 0) // 获取按行的图像指针

此外,还有一些其他的方法。像是Scalar()用来颜色赋值,cvtColor()转换颜色空间。

关于部分复制与完全复制。一般情况下只会复制Mat对象的头和指针部分,不会复制数据部分。像是下面这样:

1
2
Mat A = imread(imgFilePath);
Mat B(A); // 这就只复制头和指针

如果想把Mat对象的头和数据部分一起复制,可以这样写:

1
2
3
Mat F = A.clone();
Mat G;
A.copyTo(G);

使用Mat对象的四个要点:

  • 输出图像的内存是自动分配的
  • 使用openCV的C++接口,不需要考虑内存分配问题
  • 赋值操作和拷贝函数只会复制头部分
  • 使用clone与copyTo两个函数实现数据完全复制

矩阵掩膜操作

掩膜(mask,又称为kernel)一般用于提高图片的对比度。做出来的效果类似下图:

这个操作可以自己实现,但是openCV已经为我们封装好了。掩膜操作十分简单,用它可以重新计算每个像素的像素值。这个操作可以通过一个公式表示,下面给出,但这个公式其实并不需要记忆。

I(i,j) = 5*I(i,j)-[I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]

自己实现的话也就是用代码把这个公式跑一遍,用图像指针遍历整张图片做一遍操作就可以了。

在openCV里面这个操作被封装成了filter2D这个函数,只需要按要求传入几个参数就可以了。不过之前需要建立一个掩膜。这样写就好:Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

最后还有一个小知识点,使用getTickCount()计算运行时间。代码里写的比较明白。

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
61
62
63
64
65
66
67
68
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
int main() {
Mat src = imread("D:/QQ_files3/MobileFile/kksk.png");
Mat dst;
if (src.empty()) {
std::cout << "could not load image" << std::endl;
return -1;
}
namedWindow("src image", CV_WINDOW_AUTOSIZE);
imshow("src image", src);

int offsetx = src.channels();
std::cout << offsetx << std::endl;
// src.channels()获取原图片的通道数。
// 灰度图片的通道为1,RGB图片用三种颜色描述所以通道是3
// 还有4通道为RGBA,A为透明度,2通道(实通道,虚通道)不常见
//dst = Mat::zeros(src.size(), src.type());
//namedWindow("test init", CV_WINDOW_AUTOSIZE);
//imshow("test init", dst);
/*
// 掩膜操作实现图像对比度调整。
int cols = (src.cols - 1) * src.channels();
int offsetx = src.channels();
int rows = src.rows;
dst = Mat::zeros(src.size(), src.type());
// 创建一个黑色的图,每个像素的通道都为0
for (int row = 1; row < (rows - 1); row++) {
const uchar* previous = src.ptr<uchar>(row - 1);
// 获取图像像素矩阵的指针,括号内表示行数
const uchar* current = src.ptr<uchar>(row);
const uchar* next = src.ptr<uchar>(row + 1);
uchar* output = dst.ptr<uchar>(row);
for (int col = offsetx; col < cols; col++) {
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]));
}
// saturate_cast<uchar>()函数为像素范围处理函数
// 它确保RGB值的范围控制在0到255之间,小于0返回0,大于0返回255
}
*/
double t = getTickCount();
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
// 创建小数组
filter2D(src, dst, src.depth(), kernel);
/*
对图像应用可分离线性滤波器(矩阵掩膜操作)。
该函数对图像应用可分离线性滤波器,src的每一行都是用一维内核过滤。
然后,用1D对结果的每一列进行过滤内核核心。最终的结果移位后存储在dst中。
*/
double timeconsume = (getTickCount() - t) / getTickFrequency();
/*
通过查阅原型解释能知道,getTickCount()返回一个“Tick”值,用来表示时间点
getTickFrequency()是一个比较难理解的东西,原文是这样解释:

该函数返回某些架构(如x86、x64、PowerPC)。在其他平台上,该函数相当于getTickCount。
它也可以用来非常精确的时间测量,以及RNG初始化。
getTickCount通常是较好的测量解决方案执行时间。

总之记住它就好了。。用两个时间点之差可以计算运行时间。
*/
std::cout << "time:" << timeconsume << std::endl;

namedWindow("output image", CV_WINDOW_AUTOSIZE);
imshow("output image", dst);
waitKey(0);
return 0;
}

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


本文标题:OpenCV计算机图像视觉基础学习笔记2——Mat对象与矩阵掩膜

文章作者:Shawn Zhou

发布时间:2019年07月23日 - 17:07

最后更新:2019年07月23日 - 20:07

原始链接:http://shawnzhou.xyz/2019/07/23/19-07-23-01/

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

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