Mat - The Basic Image Container
Mat
使用Mat对象无需再自己手动分配和释放内存
Mat对象由两个数据部分组成:
-
矩阵头
包含着一些信息,例如矩阵尺寸,用于记录矩阵存储位置的方法等等
-
矩阵指针
包含像素值(根据选择的存储方式决定维度)
其中,矩阵头大小恒定,但矩阵实际尺寸视情况而定。
为了加快程序的运算速度,每一个Mat对象都有自己的头,而一个矩阵有可能通过共享矩阵指针的方式来被两个Mat对象使用。并且,拷贝运算符只会拷贝矩阵头与矩阵指针,而不会拷贝矩阵本身。
Mat A, C; // creates just the header parts
A = imread(argv[1], IMREAD_COLOR); // here we'll know the method used (allocate matrix)
Mat B(A); // Use the copy constructor
C = A; // Assignment operator
上面例子中的所有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
Mat对象使用引用计数来负责内存的释放。
赋值矩阵本身时,可以使用cv::Mat::clone()或cv::Mat::copyTo()
Mat F = A.clone();
Mat G;
A.copyTo(G);
存储方法
色彩空间是指我们如何组合颜色分量以编码给定的颜色。最简单的一个是灰度,我们可以支配的颜色是黑色和白色。这些的组合使我们能够创建许多灰色阴影。
最受欢迎的色彩空间是RGB,因为这也是我们眼睛建立颜色感受的方式,其基色为红,绿,蓝,有时候为了对颜色透明度进行区分,会加入第四个元素alpha。
还有许多其他色彩空间,例如:
- RGB是最常见的,因为我们的眼睛使用类似的东西,但请记住,OpenCV标准显示系统使用BGR色彩空间组成颜色(而非RGB)
- HSV和HLS将颜色分解为它们的色调,饱和度和明度/亮度分量,这是我们描述颜色的更自然的方式。例如,您可能会忽略最后一个分量,从而使算法对输入图像的光照条件不那么敏感。
- YCrCb被流行的JPEG图像格式使用。
- CIE Lab* 是一个感知上均匀的色彩空间,如果您需要测量给定颜色到另一种颜色的距离,它会派上用场。
每个色彩空间都有自己的有效域。这将决定我们使用的数据类型。我们如何存储颜色决定了我们对其有效域的控制。可能的最小数据类型是char,这意味着 1 个字节或 8 位。这可能是无符号(因此可以存储从 0 到 255 的值)或有符号(从 -127 到 +127 的值)。尽管在三个分量(如RGB)的情况下,这种宽度已经给出了1600万种可能的颜色来表示,但我们可以通过为每个分量使用浮点数(4字节= 32位)或双精度(8字节= 64位)数据类型来获得更精细的控制。但是,请记住,增加组件的大小也会增加内存中整个画面的大小。
显示创建Mat对象
可以使用"<<"来查看矩阵具体的值,但该方式仅适用于二维矩阵。
-
使用Mat构造函数进行创建
Mat m(2, 2, CV_8UC3, Scalar(0, 0, 255)); cout << "M = " << endl << " " << m << endl<< endl;对于二维和多通道图像,我们首先定义它们的大小:行数和列数。
然后,我们需要指定用于存储元素的数据类型以及每个矩阵点的通道数。为此,我们根据以下约定构造了多个定义:
CV_[每个项目的位数][有符号或无符号][类型前缀]C[通道号]例如,CV_8UC3意味着我们使用长度为 8 位的无符号 char 类型,每个像素有三个uchar来形成三个通道。
-
使用 C/C++数组并通过构造函数进行初始化
int sz[3] = {2, 2, 2}; Mat L(3, sz, CV_8UC1, Scalar::all(0)); // 三维,sz数组决定了每个维度的而大小 -
使用cv::Mat::create函数
Mat M; M.create(4, 4, CV_8UC(2)); cout << M;注意:这个创建方法不能为矩阵设初值,它只是在改变尺寸时重新为矩阵数据开辟内存。
-
使用Matlab风格的初始化函数:cv::Mat::zeros(), cv::Mat::ones(), cv::Mat::eye()
需要提供大小和数据类型来创建矩阵:
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 << " " << E << endl << endl; -
对于小型矩阵,可以使用逗号分隔的初始值项或初始值列表:
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); cout << "C = " << endl << " " << C << endl; C = (Mat_<double>({0, -1, 0, -1, 5, -1, 0, -1, 0})).reshape(3); cout << "C = " << endl << " " << C << endl; // reshape(cn, rows):在不改变数据的情况下,将矩阵更改为 // 具有cn个通道,rows行的矩阵 -
为现有Mat对象创建新的头,并对齐使用cv::Mat::clone或cv::Mat::copyTo
Mat RowClone = C.row(1).clone(); cout << "RowClone = " << endl << " " << RowClone << endl;
注意:可以使用cv::randu()来随机填充一个矩阵,在使用时需要为随机值提供一个上下限
Mat R = Mat(3, 2, CV_8UC3);
randu(R, Scalar::all(0), Scalar::all(255));
格式化输出
有不同的格式,这里不再展示,可以使用的有
// M为需要输出的矩阵
cout << R; // 默认
cout << format(R, Formatter::FMT_PYTHON); // python
cout << format(R, Formatter::FMT_CSV); // Comma separated values (CSV)
cout << format(R, Formatter::FMT_NUMPY); // numpy
cout << format(R, Formatter::FMT_C); // C
其他常用输出
OpenCV也通过<<运算符支持其他常见OpenCV数据结构的输出:
-
2D Point
Point2f P(5, 1); cout << "Point(2d) = " << P << endl; -
3D Point
Point3f P3F(2, 6, 7); cout << "Point(3d) = " << P3F << endl; -
通过cv::Mat输出std::vector
vector<float> v; v.push_back((float)CV_PI); v.push_back(2); cout << "Vector of floats via Mat = " << Mat(v) << endl; -
由points组成的std::vector
vector<Point2f> vPoints(20); int j = 0; for (auto i = vPoints.begin(); i != vPoints.end(); ++i) { *i = Point2f((float)(j * 5), float(j % 7)); j++; } cout << "A vector of 2D Points = " << vPoints << endl;

浙公网安备 33010602011771号