之前列举了 OpenCV 中的12 个模块。现在对于每一个模块一一讲述,从 core 模块的 Mat 类开始 。
1、什么是 Mat 类?
Mat 类是用来保存图像以及其他矩阵数据结构(体素、向量场、直方图、张量、点云等)的数据结构,是从 OpenCV 2.0 以后才使用的,之前一直用 C 风格的 IplImage 。 使用 IplImage * 最大的问题就是容易造成内存泄露,管理内存相当麻烦 。而Mat 类的出现不需要我们手动为其开辟空间,也不需要在不需要它是立即释放。补充说明一下,很多 OpenCV 函数仍然手动地管理内存空间,这样不浪费内存,比如,传递一个已存在了的 Mat 对象时,开辟好了的那个空间会被再次使用。但,手动管理内存不再是必须的,对于初学者来说,完全不用考虑这些。
Mat 类由两部分组成:矩阵头和指向存储所有像素值的矩阵的指针。如何理解矩阵头呢?矩阵头相当于矩阵的说明书,它描述了矩阵的尺寸、存储方法、存储地址以及引用次数。何为引用次数?是这样的,矩阵头的尺寸是一个常数,不会随图像的变化而变化,但是存储图像的矩阵是随图像大小而变化的,一般来说,比矩阵头大好几个数量级。而在处理图像时,经常会遇到复制图像、传递图像的操作,此时如果复制整个矩阵,不仅耗费内存,还影响运行效率。所以,OpenCV中的“引用次数”,即“计数机制”,让每一个 Mat 对象都有一个矩阵头,但是它们共享一个矩阵。这是通过让矩阵指针指向同一个地址实现的。比如:
Mat A; //仅仅创建了矩阵头
A = imread("1.jpg",1); //为矩阵开辟一段内存,及创建了矩阵
Mat B(A); // 拷贝构造函数
Mat C = A; //赋值
当利用 Mat 类定义的时候,只是创建了矩阵头。在使用拷贝构造或者赋值的时候,其实是新创建了不同的信息头和矩阵指针,它们共享一个矩阵。
那有人说了,我就想复制整个矩阵,行吗?!当然行了,现在这世道,啥不行。此时可以使用 clone ()函数或者copyTo函数 。
Mat D = A.clone() //D等于A的复制品
Mat E;
A.copyTo(E);//把A复制到E
2、Mat 常见的构造方法
(1)Mat ::Mat() 无参数构造法,例如 Mat 就是无参数构造法
(2) Mat ::Mat(int rows, int cols, int type) //行列构造法,type表示什么下面讲
(3)Mat ::Mat(Size size, int type) //大小为size 的图像。比如 Mat A(cv::Size(7,7),CV_8UC3 ),创建 7× 7大小的图片
(4)Mat ::Mat(int rows, int cols, int type,const Scalar&s) //所有元素都初始化为 s
(5)Mat ::Mat(Size size, int type, const Scalar&s)
(6)Mat::Mat(const Mat& m) //将矩阵m 赋值给新创建的对象
下面解释 type 。一般来说,有CV_8UC1、CV_16SC3、CV_32FC3 ,8,16,32表示位数; U表示无符号整数、S 表示有符号整数、F表示 double类型。C 表示通道数,1表示单通道,3表示三通道。以下为编程测试(在linux 下,如果在windows 下面,效果是一样的)
int main()
{
Mat A;
cout <<"矩阵A: "<< A << endl;
Mat B(7,7,CV_8UC1);
cout << "B矩阵为: "<< endl << B << endl;
Mat C(7,7,CV_8UC3);
cout << "C矩阵为: "<< endl << C << endl;
Mat D(Size(7,7),CV_8UC1); //和 B 矩阵是等价的
cout << "D矩阵为: "<< endl << D << endl;
Mat E(7,7,CV_8UC1,Scalar(1) );
cout << "E矩阵为: "<< endl << E << endl;
Mat F(7,7,CV_8UC3,Scalar(1) );
cout << "F矩阵为: "<< endl << F << endl;
Mat G(7,7,CV_8UC3,Scalar(1,2,3) );
cout << "G矩阵为: "<< endl << G << endl;
waitKey();
return 0;
}
输出结果如下
最后,我想提一句,上面的矩阵由 Mat 定义出来了,你可以用 imshow()给输出出来,但是,尺寸要设置的大一些,不然你看不到输出结果,因为图片太小了。值得注意的是,利用无参数构造法会报错!