opencv 2.x学习笔记(五)关于Mat

来源:互联网 发布:会计代理记账软件 编辑:程序博客网 时间:2024/04/29 16:16

        Mat作为OpenCV中最基本数据结构。其重要性不言而喻。所以,了解Mat结构是我们通往OpenCV的重要一步。在讲解Mat结构之前,有必要先做一个简介。

简介

        在OpenCV 1.x版本中,OpenCV是基于C语言接口而建。为了在内存中存放图像,在OpenCV中使用了名为IplImage的结构体。在大名鼎鼎的《Learning OpenCV》一书中就对IplImage结构做了详细的讲解。但是,使用C语言结构的结构体,就意味着我们需要手动的去管理内存。当我们在写一个程序时,尤其是当代码的规模越来越大时,这种缺陷也就暴漏的更为严重。

        众所周知,在C++中,有着类的概念,也正因为C++的出现让我们自动的内存管理成为可能。为此,在OpenCV 2.0版本中,OpenCV引入了一个新的C++接口,利用自动内存管理给出了解决问题的新方法。使用这个方法,我们不在纠结与管理内存上面,而着力的解决自己的开发目标。

       了解到这些之后,我们可以很清楚的知道,我们使用Mat,将不在需要手动的分配空间,在不需要时,也不必去手动的释放空间。

      那么,Mat到底是什么呢?Mat实际上是一个类,他由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会根据图像的大小而不同。

     在OpenCV中海使用了引用计数机制。其思路是让每个Mat对象有自己的信息头,但是共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。在拷贝构造函数中则只拷贝信息头和矩阵指针,而不拷贝矩阵。这样我们将大大提高了程序的运行速度。当我们拷贝一个Mat对象的信息头时,矩阵的引用次数会增加,反之当一个信息头被释放之后,这个计数将被减一,当计数值变为零时,矩阵将被清理。当我们在某些情况下需要拷贝具体的矩阵内容时(而不仅仅是信息头和矩阵指针),这个时候我们可以使用函数clone()或者copyTo()。

       说了这么多,我们不妨看一下Mat源码中到底有哪些东西。

class CV_EXPORTS Mat{public:    // ... a lot of methods ...    ...    /*! includes several bit-fields:         - the magic signature         - continuity flag         - depth         - number of channels     */    int flags;    //! the array dimensionality, >= 2    int dims;    //! the number of rows and columns or (-1, -1) when the array has more than 2 dimensions    int rows, cols;    //! pointer to the data    uchar* data;    //! pointer to the reference counter;    // when array points to user-allocated data, the pointer is NULL    int* refcount;    // other members    ...};
        从上面我们可以很清楚的看到前面所讲到的指向存储所有像素值的矩阵的指针(data),以及引用计数指针refcount。现在大家也可以理解,第一第二篇文章中,每次用!image.data来判断是否正确读入图像的含义了吧!

        创建一个Mat对象也有着很多种方法,常用的如下:

    //! constructs 2D matrix of the specified size and type    // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.)    Mat(int rows, int cols, int type);    Mat(Size size, int type);    //! constucts 2D matrix and fills it with the specified value _s.    Mat(int rows, int cols, int type, const Scalar& s);    Mat(Size size, int type, const Scalar& s);    //! constructs n-dimensional matrix    Mat(int ndims, const int* sizes, int type);    Mat(int ndims, const int* sizes, int type, const Scalar& s);    //! allocates new matrix data unless the matrix already has specified size and type.    // previous data is unreferenced if needed.    void create(int rows, int cols, int type);    void create(Size size, int type);    void create(int ndims, const int* sizes, int type);

         需要注意的是,上面代码中的type的值定义如下:CV_<bit-depth>{U|S|F}C(<number_of_channels>)。所以定义如下:

// make a 7x7 complex matrix filled with 1+3j.Mat M(7,7,CV_32FC2,Scalar(1,3));// and now turn M to a 100x60 15-channel 8-bit matrix.// The old content will be deallocatedM.create(100,60,CV_8UC(15));// create a 100x100x100 8-bit arrayint sz[] = {100, 100, 100};Mat bigCube(3, sz, CV_8U, Scalar::all(0));

       我们可以通过at方法来简单的访问2维矩阵数据,也可以利用STL风格的迭代器,Mat中定义如下:

    //! special versions for 2D arrays (especially convenient for referencing image pixels)    template<typename _Tp> _Tp& at(Point pt);    template<typename _Tp> const _Tp& at(Point pt) const;    //! template methods for iteration over matrix elements.    // the iterators take care of skipping gaps in the end of rows (if any)    template<typename _Tp> MatIterator_<_Tp> begin();    template<typename _Tp> MatIterator_<_Tp> end();    template<typename _Tp> MatConstIterator_<_Tp> begin() const;    template<typename _Tp> MatConstIterator_<_Tp> end() const;

      关于如何访问矩阵中的数据,在下一讲将详细讲述,这里大家有个印象即可。
    Mat中为我们提供的操作,远远不止这些。简单的讲,我们可以很方便的利用Mat结构像在数学符号写的那么轻松的操作一个矩阵,并很方便的求矩阵的相乘,点积,矩阵的转置,矩阵的逆,矩阵的缩放,以及求矩阵的子矩阵等等。

一个Demo

#include <cv.h>#include <highgui.h>#include <iostream>using namespace std;using namespace cv;int main(){uchar a[][3] = {1, 2, 3,4, 5, 6,                7, 8, 9};Mat M(3, 3, CV_8U, Scalar::all(0));M.data = a[0];cout << "初始M =" << endl << M << endl;M.row(1) = M.row(1) + 2 * M.row(0);cout << "M的第二行加上二倍的第一行后M = " << endl << M << endl;M.col(0) = M.col(2) - M.col(1);cout << "M的第一列为第三列减去第二列M = " << endl << M << endl;Mat M1 = M.col(1);cout << "M1为M的第二列 M1 = " << endl << M1 << endl;Mat M2 = M.t();cout << "M2为M的转置 M2 = " << endl << M2 << endl;M2.at<uchar>(1, 1) = 3;cout << "M2的第二行第二列元素改为3 M2 = " << endl << M2 << endl;M = M + M2;cout << "M为M加上M2 M = " << endl << M << endl;return 0;}

运行结果:





0 0