Mat

作者: tiyife | 来源:发表于2018-07-17 23:26 被阅读0次

    Mat的基本介绍

    基本上讲 Mat 是一个类,由两个数据部分组成:

    • 矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)
    • 一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。
      矩阵头的尺寸是常数值,包含几个固定的图像属性,但矩阵本身的尺寸会依图像的大小和存储类型的不同而不同。

    Mat 的传递

    Mat中的矩阵头相比于存储像素值的矩阵而言非常小,所以在Mat的传递过程中通常传递的是Mat的矩阵头。每一个Mat拥有自己的矩阵头,但是不同的Mat共享一个像素值矩阵,矩阵维护一个引用次数,每拷贝一次Mat对象,矩阵的引用次数就会加一,反之矩阵头被释放时计数减一,最后一个引用的矩阵头负责释放掉矩阵。

    Mat A, C;                                 // 只创建矩阵头
    A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
    Mat B(A);                                 // 拷贝A的矩阵头
    C = A;                                    // 拷贝A的矩阵头
    Mat D (A, Rect(10, 10, 100, 100) ); // 创建一个新的矩阵头,并指向A的矩阵
    Mat E = A(Range:all(), Range(1,3)); // 创建一个新的矩阵头,并指向A的矩阵s
    

    上述方法都是通过拷贝矩阵头的方法来实现Mat的传递,当用户想要拷贝整个矩阵的时候可以通过copyto()和clone()来实现。

        Mat image = imread("2.jpg");
        resize(image, image, Size(), 0.5, 0.5);
    
        Mat image1(image) ;//仅是创建了Mat的头部分,image1与image共享数据区
        Mat image2 = image ;//仅是创建了Mat的头部分,image1与image共享数据区
        Mat image3 = image.clone() ;//完全拷贝,把image中的所有信息拷贝到image3中
        Mat image4;
        image.copyTo(image4);//根据image信息创建新的矩阵头,并拷贝image的数据区到image1中
        
        for (int i = 0; i < image.rows; ++i)
        {
            uchar* ptr = image.ptr(i);
            for (int j = 0; j < image.cols; ++j)
            {
                ptr[j] = 0;
            }
        }
        imshow("image", image);
        imshow("image1", image1);
        imshow("image2", image2);
        imshow("image3", image3);
        imshow("image4", image4);
        waitKey();
        return 0;
    

    上述代码运行后结果如下图,可见修改了image的数据区之后iamge1和image2都随着发生变化了,而image3和image4仍保持着原图的样子。


    图片.png

    Mat 的常用属性

    Mat有很多属性,有一些我经常用的如rows、cols、dims和channels等,也有一些调试的时候经常遇到的,这里放在一起统一整理一下。

    • data 指向矩阵数据部分的指针

    • rows 矩阵的行数

    • cols 矩阵的列数

    • dims 矩阵的维度,二维矩阵dims=2,三维矩阵dims=3.

    • size 矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)

    • channels 矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则channels = 3

    • type 用于表示矩阵中元素存储类型的预定义的常量,其本质是特殊命名规则下的int类型常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。
      数据类型包括这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。
      例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2。
      C1,C2,C3,C4则表示通道是1,2,3,4

    • depth
      矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,
      将type的预定义值去掉通道信息就是depth值:
      CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F

    • elemSize
      矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes

    • elemSize1
      矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels

    • step
      step[0]是矩阵中一行元素的字节数。
      step[1]是矩阵中一个元素的字节数,也就是和上面所说的elemSize相等。

      上面说到,Mat中一个uchar* data指向矩阵数据的首地址,而现在又知道了每一行和每一个元素的数据大小,就可以快速的访问Mat中的任意元素了。下面公式:


      TIM截图20180717231815.png

    • step1
      规整化的step,值为step / elemSize1。 定义如下:
    inline size_t Mat::step1(int i) const { return step.p[i]/elemSize1(); }
    

    以CV16UC4的img为例,来看下step,step1具体的值:

    image
    img的type是CV_16UC4, step[0]是其一行所占的数据字节数4 *4 * 16 / 8 = 32
    step[1] 是一个元素所占的字节数,img的一个元素具有4个通道,故:4 * 16 / 8 = 8
    step1 = step / elemSize1,elemSize1是元素的每个通道所占的字节数。

    未完待续

    相关文章

      网友评论

          本文标题:Mat

          本文链接:https://www.haomeiwen.com/subject/zvzgpftx.html