Mat - The Basic Image Container

Mat

使用Mat对象无需再自己手动分配和释放内存

Mat对象由两个数据部分组成:

  1. 矩阵头

    包含着一些信息,例如矩阵尺寸,用于记录矩阵存储位置的方法等等

  2. 矩阵指针

    包含像素值(根据选择的存储方式决定维度)

其中,矩阵头大小恒定,但矩阵实际尺寸视情况而定。

为了加快程序的运算速度,每一个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::clonecv::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;
    
posted @ 2022-06-03 22:55  帝皇の惊  阅读(70)  评论(0)    收藏  举报