提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
笔者本科时候有幸接触了OpenCV3.2.0版本的学习,后因考研压力不得不暂时停下学习的脚步,现在考研任务结束了,未来的导师也是从事的该方向,笔者又开始了新一轮的学习。回来发现OpenCV已经出到了4.5.5版本,遂重新下载新版本并决定记录这一学习历程。由于笔者水平有限,可能有错误之处还请诸位大佬多多包涵并烦请指出,让我们一起学习,共同进步。
首先需要说明的是:我是按着毛星云前辈编写的OpenCV3编程入门进行学习的,我会尽力把星云前辈的程序转成符合OpenCV4.5.5版本的。毛星云前辈于2021年12月11日不幸过世,他是我非常敬仰的一位业内大佬,我也是看他的书才开始接触OpenCV。
一、Mat是什么?
cv::Mat可以被视为是OpenCV所有和C++有关使用的核心,很多主要函数都是Mat类的成员(没错,它是一个类)。Mat类用于表示任意维度的稠密数组,它由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维度)的指针,它指示了数据真正的存储位置。(说实话第一遍看的时候我没太看懂)
简单说一下官网上给出的他存储数据的方式,它按下面的公式来排列数据:
如果是二维数组,公式就可以简化成下面这样:
这意味着 2 维矩阵是逐行存储的,3 维矩阵是逐平面存储的。
Mat有意思的是它的计数机制,调用时我们需添加一个指向存储所有像素值的矩阵指针。这是因为OpenCV 是一个图像处理库。它包含大量图像处理功能。为了解决计算难题,大多数时候您最终会使用库的多个功能。因此,将图像传递给函数是一种常见的做法。我们不应该忘记我们谈论的是图像处理算法,它往往计算量很大。我们要做的最后一件事是通过对可能较大的图像进行不必要的副本来进一步降低程序的速度。为了解决在程序运作时效率低下的问题。OpenCV 使用了引用计数系统。这个想法是每个Mat对象都有自己的标头,但是通过让它们的矩阵指针指向相同的地址,可以在两个Mat对象之间共享一个矩阵。此外,复制运算符只会复制标题和指向大矩阵的指针,而不是数据本身。当我们复制一个Mat对象的信息头,就会增加矩阵的引用次数;释放就减少,计数为0矩阵被清理。
二、创建Mat对象的方法
创建Mat对象的方法共有六种(官网给的),我们常用的是前三种,我会分别介绍一下(都采用了官网的例程,感兴趣的同学可以去看看):
- cv::Mat::Mat构造函数,如:
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
前面是定义它们的大小:行数和列数(二维矩阵)。
在这里要说明一个贯穿始终且非常重要的概念:OpenCV用于存储元素的数据类型和每个矩阵点的通道数的约定,即:
CV_[每项的位数][有符号或无符号][类型前缀]C[通道数]
例如,CV_8UC3意味着我们使用 8 位长的 unsigned char 类型,每个像素具有其中的三个以形成三个通道。最多为四个通道预定义了类型。
- 使用 C/C++ 数组并通过构造函数进行初始化
int sz[3] = {
2,2,2};
Mat L(3,sz, CV_8UC(1), Scalar::all(0));
- cv::Mat::create函数:
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;
- MATLAB 样式初始化程序
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
Mat Z = Mat::zeros(3,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
- 对于小型矩阵,您可以使用逗号分隔的初始化程序或初始化程序列表
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
这种情况下需要 C++11 支持
C = (Mat_<double>({
0, -1, 0, -1, 5, -1, 0, -1, 0})).reshape(3);
cout << "C = " << endl << " " << C << endl << endl;
- 为现有的Mat对象和cv::Mat::clone或cv::Mat::copyTo创建一个新标头
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;
总结
从这一章开始我们就算正式入门OpenCV的学习了,Mat这个类也将一直伴随我们的学习。