在讲解Mat之前,先来介绍一些基础知识。

阵列的数据类型

阵列的数据类型定义了为阵列的每个元素(图片中的像素)分配的比特数以及如何使用这些比特数表示元素的值。任何阵列的元素都应该有下面数据类型的一种:

单通道阵列

  • CV_8U (8 bit 无符号整数)
  • CV_8S (8 bit 有符号整数)
  • CV_16U(16 bit 无符号整数)
  • CV_16S (16 bit 有符号整数)
  • CV_32S (32 bit 有符号整数)
  • CV_32F (32 bit 浮点数)
  • CV_64F (64 bit 浮点数)

举例来说:下图展示了一个使用8 bit无符号整数的单通道阵列。因为数据类型是8 bit无符号整数,因此这个阵列的每个元素为0-255的值。

OpenCV数据结构之Mat-程序旅途

多通道阵列

我们可以为多通道阵列定义上面的所有的数据类型(最多支持512个通道)。这里只演示为多通道定义CV_8U数据类型:

  • CV_8UC1 (单通道阵列,8 bit 无符号整数)
  • CV_8UC2 (2通道阵列,8 bit 无符号整数)
  • CV_8UC3 (3通道阵列,8 bit 无符号整数)
  • CV_8UC4 (4通道阵列,8 bit 无符号整数)
  • CV_8UC(n) (n通道阵列,8 bit 无符号整数 (n 可以从 1 到 512) )

例1:下图展示了一个使用8 bit 无符号整数的3通道阵列。因为数据类型是8 bit无符号整数,因此这个阵列的每个元素为0-255的值。由于是3通道阵列,所以阵列由带有3个元素的元组组成,第一个元组是{54, 0, 34},第二个元组是 {58, 78, 185} ,以此类推。 OpenCV数据结构之Mat-程序旅途 例2:下图展示了一个使用8 bit有符号整数的2通道阵列。因为数据类型是8 bit有符号整数,故阵列的每个元素值从-128到127。因为这是一个2通道阵列,所以阵列由有2个元素的元组组成。第一个元组是{-85,-127},第二个是{25,23},以此类推。 OpenCV数据结构之Mat-程序旅途 注意: CV_8U = CV_8UC1 = CV_8UC(1)

使用示例

  • Mat img1(3, 5, CV_32F ); //宽3x高5 使用32 bit浮点数的单通道阵列
  • Mat img2(23, 53, CV_64FC(5) ); //23 x 53 使用64 bit浮点数的5通道阵列
  • Mat img3(Size(100, 200), CV_16UC2 ); //100 x 200 使用16 bit无符号整数的2通道阵列

记住:一些OpenCV函数不支持上面全部的数据类型,所以在使用时注意一些。

IplImage的位深(C style)

  • IPL_DEPTH_<bit_depth>(S|U|F)
    • <bit_depth> 可能的值为 1,8,16,32 and 64
    • S = Signed
    • U = Unsigned
    • F = Float
    • 位深为1的图片应该为unsigned
    • 位深为8的图片应该为signed或unsigned
    • 位深为16的图片应该为signed或unsigned
    • 位深为32位的图片应该为signed或float
    • 位深为64的图片应该为float
  • 例子:
    • IPL_DEPTH_1U (位深为1,unsigned)
    • IPL_DEPTH_8U (位深为8,unsigned)
    • IPL_DEPTH_16U
    • IPL_DEPTH_32F (位深为32,float)
    • IPL_DEPTH_8S
    • IPL_DEPTH_16S (位深为16,signed)
    • IPL_DEPTH_32S
    • IPL_DEPTH_64F

位深表示为每个像素分配的比特数。比如,使用IPL_DEPTH_8U的IplImage每个像素使用8 bit无符号整数,这表示每个像素的值是从0-255的整数。 当前IplImage数据结构支持IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,  IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F。

Mat类

Mat是基本的图像容器,它由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。Mat使用了引用技术机制,这样在拷贝Mat对象的时候,只拷贝信息头,所有的对象都共享一个矩阵。

Mat A, C;                                 // 只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存

Mat B(A);                                 // 使用拷贝构造函数

C = A;                                    // 赋值运算符

以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。实际上,不同的对象只是访问相同数据的不同途径而已。这里还要提及一个比较棒的功能:你可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域( ROI ),你只需要创建包含边界信息的信息头:

Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries

如果真的需要拷贝矩阵(不只是信息头和矩阵指针),可以使用函数 clone() 或者 copyTo()

Mat F = A.clone();
Mat G;
A.copyTo(G);

现在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。 Mat 不但是一个很赞的图像容器类,它同时也是一个通用的矩阵类,所以可以用来创建和操作多维矩阵。创建一个Mat对象有多种方法:

Mat() 构造函数

	Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
	cout << "M = " << endl << " " << M << endl << endl;

OpenCV数据结构之Mat-程序旅途 对于二维多通道图像,首先要定义其尺寸,即行数和列数。CV_8UC3在前面已经详细解释了,这里不再赘述。Scalar是个short型vector。指定这个能够使用指定的定制化值来初始化矩阵。

在 C\C++ 中通过构造函数进行初始化

	int sz[3] = { 2, 2, 2 };
	Mat L(3, sz, CV_8UC(1), Scalar::all(0));

上面的例子演示了如何创建一个超过两维的矩阵:指定维数,然后传递一个指向一个数组的指针,这个数组包含每个维度的尺寸;其余的相同

为已存在IplImage指针创建信息头

	IplImage* img = cvLoadImage("greatwave.png", 1);
	Mat mtx(img); // convert IplImage* -> Mat

Create() 函数

	Mat M;
	M.create(4, 4, CV_8UC(2));
	cout << "M = " << endl << " " << M << endl << endl;
OpenCV数据结构之Mat-程序旅途

这个创建方法不能为矩阵设初值,它只是在改变尺寸时重新为矩阵数据开辟内存。

MATLAB形式的初始化方式: zeros(), ones(), :eyes()

使用以下方式指定尺寸和数据类型:

	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;
OpenCV数据结构之Mat-程序旅途

对于小矩阵你可以用逗号分隔的初始化函数

	Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	cout << "C = " << endl << " " << C << endl << endl;
OpenCV数据结构之Mat-程序旅途

使用 clone() 或者 copyTo() 为一个存在的 Mat 对象创建一个新的信息头

	Mat C(3, 1, CV_8UC1, Scalar(1));
	Mat RowClone = C.row(1).clone();
	cout << "RowClone = " << endl << " " << RowClone << endl << endl;

总结

本篇文章介绍了阵列的数据类型和位深的内容,讲解了Mat类的一些基础知识以及创建Mat对象的一些方法。