OpenCV中的Mat类

Posted ppxiehhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV中的Mat类相关的知识,希望对你有一定的参考价值。

什么是Mat类

在OpenCV中,Mat类是用来存放图像的基本类,它是用来保存图像矩阵类型的数据信息,包括向量、矩阵、灰度或彩色图像等数据。

Mat类主要包含有两部分(并不是两个)数据:一部分是矩阵头(matrix header),这部分的大小是固定的,包含矩阵的大小,存储的方式,矩阵存储的地址和引用次数等;另一个部分是一个指向像素矩阵的指针(指针变量)(data)。

在绝大多数情况下矩阵头的大小远小于像素矩阵中数据量的大小,小了好多个数量级,因此图像复制和传递过程中主要的开销是存放矩阵数据。为了解决这个问题,在OpenCV中复制和传递图像时,只是复制了矩阵头和指向存储数据的指针,因此在创建Mat类时可以先创建矩阵头后赋值数据。

总之,是把Mat类中的成员数据分成了两部分:矩阵头和指向像素矩阵的指针变量。很多时候为便于理解,干脆直接把Mat类的对象理解成矩阵头,也即指向像素矩阵的指针在矩阵头中(一句话,Mat类的实例化对象就是矩阵头,矩阵头中包含:矩阵的大小、存储的方式、矩阵存储的地址和引用次数等、指向像素矩阵的指针),看了很多博客,感觉这样是最好理解的,很多时候不必死磕概念,或许概念本身界定都不太清晰!这样理解,矩阵头就是Mat类的实例化对象,看了Mat类定义的源码发现,指向像素矩阵的指针就存在于Mat类中,也即存在于矩阵头中。如图:

Mat类的关键部分定义(以opencv 4.1.0为例,opencv文件夹 -> bulid文件夹 -> include文件夹 -> opencv2文件夹 -> core文件夹 -> mat.hpp中788行),指向像素矩阵的指针(uchar* data)大概在2085行

class CV_EXPORTS Mat

    public:
    // 一系列函数
    ...
    
    /* flag 参数中包含许多关于矩阵的信息,如:
    -Mat 的标识
    -数据是否连续
    -深度
    -通道数目
    */
    int flags;
    
    // 矩阵的维数,取值应该大于或等于 2
    int dims;
    
    // 矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1
    int rows, cols;
    
    // 指向数据的指针:大概在2085行
    uchar* data;
    
    // 指向引用计数的指针
    // 如果数据是由用户分配的,则为 NULL
    int* refcount;
    
    // 其他成员变量和成员函数
    ...

下面我们通过读取一张本地图片返回一个Mat结构:

cv::Mat a; //仅创建了矩阵头a
a = cv::imread("test.jpg");
cv::Mat b = a;

上面这段代码首先创建了一个名为a的矩阵头,之后读入一张图像并将a中的矩阵指针指向该图像的像素数据,最后将a矩阵头中的内容复制到b矩阵头中。虽然a、b有各自的矩阵头,但是其矩阵指针指向的是同一个矩阵数据,通过任意一个矩阵头修改矩阵中的数据,另一个矩阵头指向的数据也会跟着发生改变。但是当删除a变量时,b变量并不会指向一个空数据,只有当两个变量都删除后,才会释放矩阵数据。因为在OpenCV的设计中,矩阵头中引用次数标记了引用某个矩阵数据的次数,只有当矩阵数据引用次数为0的时候才会真正释放矩阵数据。

采用引用次数来释放存储内容是C++中常见的方式,用这种方式可以避免仍有某个变量引用数据时将这个数据删除造成程序崩溃的问题,同时极大的缩减了程序运行时所占用的内存。

图解分析

cv::Mat a; //仅创建了矩阵头a
a = cv::imread("test.jpg");
cv::Mat b = a;

第一句

cv::Mat a;//仅创建了矩阵头,此时矩阵头a中的指针data为NULL
  • 图解
  • 单步调试

第二句

a = cv::imread("test.jpg");//为像素矩阵开辟内存,矩阵头a中的指针变量data存放的是像素矩阵的起始地址,指针data指向像素矩阵
  • 图解
  • 单步调试

第三句

cv::Mat b = a;//浅拷贝,仅仅把矩阵头a中的内容拷贝给矩阵头b,故矩阵头a和矩阵头b中的指向像素矩阵的指针变量所存放的地址相同,故指针都指向同一块内存
  • 图解
  • 单步调试

    a中的指向像素矩阵的指针变量中存放的地址,与b中的指向像素矩阵的指针变量中存放的地址相同,并且像素矩阵的起始地址和结束地址都相同,如下:

最后,单步调试的程序

#include<opencv2\\opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;

int main() 
	cv::Mat a;//第一句
	int c = 1;//
	c = c + 1;//
	a = cv::imread("C:/Users/Administrator/Desktop/test.jpg");//第二句
	c = c + 1;//
	cv::Mat b = a;//第三句
	c = c + 1;//
	return 0;

参考资料

以上是关于OpenCV中的Mat类的主要内容,如果未能解决你的问题,请参考以下文章

opencv 中mat类型对应的头文件是啥?

在android上的opencv中创建yuv mat

opencv 中mat类型对应的头文件是啥?

OpenCV中的Mat类

OpenCV实战——像素操作

Mat 类简析