【从零学习OpenCV 4】Mat类介绍
重磅干货,第一时间送达
经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《从零学习OpenCV 4》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。
其实在最早的OpenCV 1.0版本中,图像使用名为IplImage的C语言结构体进行存储的,所以在很多比较老的OpenCV版本教程中常会看到其身影。但是使用IplImage类型存在需要用户手动释放内存的缺点,如果程序结束后存在没有释放内存的IplImage变量,就会造成内存泄漏的问题。值得庆幸的是,随着OpenCV版本的更新,OpenCV引入C++接口,提供Mat类用于存储数据,利用自动内存管理技术很好的解决了内存自动释放的问题,当变量不再需要时立即释放内存。
Mat类用来保存矩阵类型的数据信息,包括向量、矩阵、灰度或彩色图像等数据。Mat类分为矩阵头和指向存储数据的矩阵指针两部分。矩阵头中包含矩阵的尺寸、存储方法、地址和引用次数等。矩阵头的大小是一个常数,不会随着矩阵尺寸大小而改变。在绝大多数情况下矩阵头大小远小于矩阵中数据量的大小,因此图像复制和传递过程中主要的开销是存放矩阵数据。为了解决这个问题,在OpenCV中复制和传递图像时,只是复制了矩阵头和指向存储数据的指针,因此在创建Mat类时可以先创建矩阵头后赋值数据,其方法如代码清单2-1所示。
代码清单2-1 创建Mat类
cv::Mat a; //创建一个名为a的矩阵头
a = cv::imread(“test.jpg”); //向a中赋值图像数据,矩阵指针指向像素数据
cv::Mat b=a; //复制矩阵头,并命名为b
上面这段代码首先创建了一个名为a的矩阵头,之后读入一张图像并将a中的矩阵指针指向该图像的像素数据,最后将a矩阵头中的内容复制到b矩阵头中。虽然a、b有各自的矩阵头,但是其矩阵指针指向的是同一个矩阵数据,通过任意一个矩阵头修改矩阵中的数据,另一个矩阵头指向的数据也会跟着发生改变。但是当删除a变量时,b变量并不会指向一个空数据,只有当两个变量都删除后,才会释放矩阵数据。因为矩阵头中引用次数标记了引用某个矩阵数据的次数,只有当矩阵数据引用次数为0的时候才会释放矩阵数据。
提示
采用引用次数来释放存储内容是C++中常见的方式,用这种方式可以避免仍有某个变量引用数据时将这个数据删除造成程序崩溃的问题,同时极大的缩减了程序运行时所占用的内存。
接下来我们来了解Mat类里可以存储的数据类型,根据官方给出的Mat类继承图,如图2-2所示,我们发现Mat类可以存储的数据类型包含double、float、uchar、unsigned char以及自定义的模板等。
图2-2 Mat类继承关系图
我们可以通过代码清单2-2的方式声明一个存放指定类型的Mat类变量:
代码清单2-2 声明一个指定类型的Mat类
cv::Mat A = Mat_<double>(3,3);//创建一个3*3的矩阵用于存放double类型数据
由于OpenCV提出Mat类主要用于存储图像,而像素值的最大值又决定了图像的质量,如果用8位无符号整数去存储16位图像,会造成严重的图像颜色失真或造成数据错误。而由于不同位数的编译器对数据长度定义不同,为了避免在不同环境下因变量位数长度不同而造成程序执行问题,OpenCV根据数值变量存储位数长度定义了数据类型,表2-1中列出了OpenCV中的数据类型与取值范围。
表2-1 OpenCV中的数据类型与取值范围
数据类型 |
具体类型 |
取值范围 |
CV_8U |
8位无符号整数 |
0—255 |
CV_8S |
8位符号整数 |
-128—127 |
CV_16U |
16位无符号整数 |
0-65535 |
CV_16S |
16位符号整数 |
-32768—32767 |
CV_32S |
32位符号整数 |
-2147483648—2147483647 |
CV_32F |
32位浮点整数 |
-FLT_MAX—FLT_MAX, INF, NAN |
CV_64F |
64位浮点整数 |
-DBL_MAX—DBL_MAX, INF, NAN |
仅有数据类型是不够的,还需要定义图像数据的通道(Channel)数,例如灰度图像数据是单通道数据,彩色图像数据是3通道或者4通道数据。因此针对这个情况,OpenCV还定义了通道数标识,C1、C2、C3、C4分别表示单通道、双通道、3通道和4通道。每一种数据类型都存在多个通道的情况,所以将数据类型与通道数表示结合便得到了OpenCV中对图像数据类型的完整定义,例如CV_8UC1表示的就是8位单通道数据,用于表示8位灰度图,而CV_8UC3表示的是8位3通道数据,用于表示8位彩色图。我们可以通过代码清单2-3的方式创建一个声明通道数和数据类型的Mat类:
代码清单2-3 通过OpenCV数据类型创建Mat类
cv::Mat a(640,480,CV_8UC3) //创建一个640*480的3通道矩阵用于存放彩色图像
cv::Mat a(3,3,CV_8UC1) //创建一个3*3的8位无符号整数的单通道矩阵
cv::Mat a(3,3,CV_8U) //创建单通道矩阵C1标识可以省略
注意
虽然在64位编辑器里,uchar和CV_8U都表示8位无符号整数,但是两者有严格的定义,CV_8U只能用在Mat类内部的方法。例如用Mat_<CV_8U>(3,3)和Mat a(3,3,uchar)会提示创建错误。