精华内容
下载资源
问答
  • OpenCV基础---图像存储器(Mat类
    千次阅读
    2021-09-12 08:44:43

    一 图像存储容器

            学习图像处理首先要学会如何操作矩阵信息,在OpenCV中提供了一个Mat类用于存储矩阵数据。

           Mat类用来保存矩阵类型的数据信息,包括向量,矩阵,灰度或彩色图像等数据。Mat类分为矩阵头和指向存储数据的矩阵指针两部分。其中矩阵头中包含矩阵的尺寸、存储方法、地址和引用次数。矩阵头的大小是一个常数,不会随着矩阵尺寸的大小二改变。在绝大多数情况下,矩阵头大小远小于矩阵中数据量的大小,因此图像复制和传递过程中的主要开销是存放矩阵数据。

    1.创建Mat类

    cv::Mat a;          //创建一个名为a的矩阵头
    a = cv::imread("test.jpg")   //向a中赋值图像数据,矩阵指针指向像素数据
    cv::Mat b = a   //复制矩阵头

            上述代码,首先创建了名为a的矩阵头,之后读入一张图像并将中的矩阵指针指向该图的像素数据,最后将a矩阵头中的内容复制到b矩阵头中。虽然a、b有各自的矩阵头,但是矩阵指针所指向的是同一矩阵数据,所以通过任意矩阵头修改矩阵中的数据时,另一个矩阵头指向的数据也会发生改变。但是当删除变量a时,b变量并不会指向一个空数据,只用当两个变量同时删除时,才会释放矩阵数据。因为矩阵头中引用次数标记了引用某个矩阵的次数,只有当矩阵的引用次数为0的时候才会释放矩阵数据。

    1.1声明一个指定类型的Mat类

    cv::Mat A = Mat_<double>(3,3);   //创建一个3*3的矩阵用于存放double数据类型

            Mat可以存储的数据类型包括double,float,uchar,unsigned char,以及自定义模板。

    1.2通过OpenCV数据类型创建Mat类

    OpenCV中的数据类型
    数据类型具体类型取值范围
    CV_8U8位无符号整数0~255
    CV_8S8位符号整数-128~127
    CV_16U16位无符号整数0~65535
    CV_16S16位符号整数-32768~32767
    CV_32S32位符号整数-2147483~2147483647
    CV_32F32位浮点小数

    CV_64F

    64位浮点小数
    cv::Mat a(640,480,CV_8UC3)    //创建一个640*480 的三通道矩阵用于存放彩色图像
    cv::Mat a(3,3,CV_8UC1)    //创建一个3*3的8位无符号整数的单通道矩阵
    cv::Mat a(3,3,CV_8U)   //创建单通道矩阵,c1标识可以省略

            注意:虽然在64位编译器里,uchar和CV_8U都表示8位无符号整数,但是两者有着严格的定义,CV_8U只能用于Mat类内部构建方法,如果用Mat_<CV_8U>(3,3)和Mat a(3,3,CV_8U),就会提示创建错误。

    2.Mat类构造与赋值

    2.1 Mat类的构造

    (1)利用默认构造函数

    cv::Mat::Mat();

            这种构造方式不需要输入任何的参数,在后续给变量赋值的时候会自动判断矩阵的类型大小,实现灵活的存储,常用于存储读取的图像数据和某个函数的运算输出结果。

    (2)根据输入矩阵尺寸和类型构造

    cv::Mat::Mat( int rows,
                int cols,
                int type,
                )
    //rows:构造矩阵的行数
    //cols:构造矩阵的列数
    //type:矩阵中存储的数据类型

    (3)用Size()结构构造Mat类

    cv::Mat::Mat(Size size(),
                int type
                )
    //size: 二维数组变量尺寸,通过Size(cols,rows)来进行赋值,注意列在前,行在后
    //type:略

    (4)利用已有矩阵构造Mat实例

    cv::Mat()::Mat(const Mat &m)
    //m:以构建完成的Mat类矩阵变量

            希望复制两个一模一样的Mat类而批次之间不会影响,那么可以使用m=a.clone()

    (5)创建已有Mat类的子类

    cv::Mat::Mat(const Mat &m,
                const Range &rowRange,
                const Range &colRange = Range::all()
                )
    //m: 已经构建完成的Mat类矩阵数据
    //rowRange:在以有矩阵中需要截取的行数范围,是一个Range变量,例如Range(2,5)表示从第二行,    //到第五行
    //colRange:截取列的范围
    
    //这种方式主要用于在原图中截图使用。不过需要注意的是,通过这种方式构造的Mat类与已有Mat类享有共同的数据。

    2.Mat类的赋值

    (1)在构造时赋值

    cv::Mat::Mat(int rows,
                int cols,
                int type,
                const Scalar &s
                )
    //rows:略
    //cols:略
    //type:略
    //s: 给矩阵中没个像素赋值的参数变量
    
    //实例: 
    cv::Mat::Mat(2, 2, CV_8UC3, CV::Scalar(0,0,255))

    (2)枚举法赋值

    cv::Mat::Mat(cv::Mat_<int>(3,3)<<1, 2, 3, 4, 5, 6, 7, 8, 9);

    (3)利用循环赋值

    cv::Mat c = cv::Mat_(3, 3); //定义一个3*3的矩阵
    for (int i = 0; i < c.rows; i++)
    {
        for (int j = 0; j < c.cols; j++)
        {
            c.at<int>(i, j) = i + j;
        {
    }

    (4)利用类方法赋值

    cv::Mat a = cv::Mat::eye(3, 3, CV_8UC1);
    cv::Mat b = (cv::Mat_<int>(1, 3) <<1, 2, 3);
    cv::Mat c = cv::Mat::diag(b);
    cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);
    cv::Mat e = cv::Mat::zeros(4, 2, CV_8UC3);
    
    
    //eye:创建一个单位矩阵,如果行和列不相等,在(1,1),(2,2),等主对角线出为1
    //diag:创建对角矩阵,其参数必须是Mat类型的一维变量,用来存放对角元素的数值
    //ones:创建一个全为1的矩阵
    //zeros:创建一个全为0的矩阵

    (5)利用数组进行赋值

    float a[8] = {5, 6, 7, 8, 1, 2, 3, 4, };
    cv::Mat b = cv::Mat(2, 2, CV_32FC2, a);
    CV::Mat c = cv::Mat(2, 4, CV_32FC1, a);
    
    //当矩阵中的元素数目大于数组中的数据时,将用-1.0737418e+08填充给矩阵;当矩阵中元素的数目小于数组中的数据时,将矩阵赋值完成后,数组中剩余数据将不再赋值。

    3.Mat类支持的运算

            Mat支持加减乘除的运算,在对图像进行卷积运算时,需要两个矩阵进行乘法运算,OpenCV提供了两个Mat类矩阵的乘法运算,而且定义了两个矩阵的内积和对应位的乘法运算。

    cv::Mat() j,m;
    double k;
    j = c*d;
    k = a.dot(b);
    m = a.mul(b);

     4.Mat类元素的读取

    Mat类矩阵常用的属性
    属性作用
    cols矩阵的列数
    rows矩阵的行数
    step以字节为单位的矩阵有效宽度
    elemSize()每个元素的字节数
    total()矩阵中元素的个数
    channels()矩阵的通道数

    (1)通过at方法读取Mat类矩阵中的元素

    
    //读取单通道矩阵元素
    cv::Mat a = (cv::Mat_<uchar>(3, 3) <<1, 2, 3, 4, 5, 6, 7, 8, 9);
    int value = (int)a.at<uchar>(0, 0);
    
    //读取多通道矩阵元素
    cv::Mat b(3, 4, CV_8UC3, CV::Scalar(0, 0, 1));
    cv::Vec3b vc3 = b.at<cv::Vec3b>(0, 0);
    int first = (int)vc3.val[0];
    int second = (int)vc3.val[1];
    int third = (int)vc3.val[2];
    
    //在使用多通道变量类型是,同样需要注意at方法中数据变量类型与矩阵的变量类型相对应,
    //并且cv::Vec3b类型在输入每个通道数据是需要将其变量类型强制转化成int类型。

            针对三通道矩阵,定义了cv::Vec3b,cv::Vec3s,cv::Vec3w,cv::Vec3d,cv::Vec3f,cv::Vec3i。b是uchar类型的缩写,s是short类型的缩写,w是ushort类型的缩写,d是double类型的缩写,f是float类型的缩写, i是int类型的缩写。

    (2)通过指针ptr读取Mat类矩阵中的元素

            Mat类矩阵中每一行中的每个元素都是挨着存放的,所以给出了通道指针ptr读取Mat类矩阵元素。

    cv::Mat b(3, 4, CV_8UC3, cv::Scalar(0, 0, 1));
    for (int i = 0; i < b.rows; i++)
    {
        uchar* ptr = b.ptr<uchar>(i);
        for(int j = 0; j < b.cols*b.channels(); j++)
        {
            cout << (int)ptr[j] <<endl;
        }
    }

    (3)通过迭代器访问Mat类矩阵中的元素

    cv::MatIterator_<uchar> it = a.begin<uchar>();
    cv::MatIterator_<uchar> it_end = a.end<uchar>();
    for(int i = 0; it ! = it_end; it++)
    {
        cout << (int)(*it) <<"";
        if((++i%a.cols) == 0)
        {
            cout << endl;
        }
    
    } 

            Mat类的迭代器变量类型是cv::MatIterator_<>,在定义时同样需要在尖括号中声明数据的变量类型。Mat类迭代器的起始是Mat.begin<>(),借宿是Mat.end<>(),与其他迭代器用法相同,通过“++”运算实现指针位置向下迭代,数据的读取方式是先读取第一个元素的每一个通道,之后再读取第二个元素的每一个通道,直到最后一个元素的最后一个通道。

    (4)通过矩阵元素的地址定位方式访问元素

    (int)(*(b.data + b.step[0] * row + b.step[1] * col + channel));

    更多相关内容
  • OPENCV_Mat类存取方法

    2016-05-13 17:27:23
    OPENCV_Mat类存取方法
  • 1、Mat类简介 2、Mat类的构造与赋值 2.1、Mat类的构造 2.2、Mat类的赋值 3、Mat类支持的运算 3.1Mat类的加减乘除运算: 3.2两个Mat类矩阵的乘法运算 4、Mat类元素的读取 4.1多通道数据的存储 4.2Mat类常用...

    基于windows10、vs2015、OpenCv4.1.0。

    目录

    1、Mat类简介

    2、Mat类的构造与赋值

    2.1、Mat类的构造

    2.2、Mat类的赋值

    3、Mat类支持的运算

    3.1Mat类的加减乘除运算:

    3.2两个Mat类矩阵的乘法运算

    4、Mat类元素的读取

    4.1多通道数据的存储

     4.2Mat类常用属性和成员方法

    4.3通过at读取Mat类单通道矩阵的元素

    4.4通过at读取Mat类多通道矩阵的元素

    4.5通过指针ptr读取Mat矩阵种的元素

    4.6通过迭代器访问Mat类矩阵中的元素


    1、Mat类简介

    OpenCv提供了Mat类用来存储矩阵类型数据,包括向量、矩阵、图像等数据。Mat类分为分为矩阵头和指向存储数据的矩阵指针两部分。矩阵头包含矩阵的尺寸、存储方法、地址和引用次数。矩阵头是一个常数,绝大数情况下,矩阵头的大小远小于矩阵数据量的大小。

    #include <opencv2\opencv.hpp>
    #include <iostream>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
    	Mat a;
    	a = imread("D:\\lena.jpg"); //向a中赋值图像数据
    	Mat b = a;//只是复制了矩阵头和指向存储数据的指针(指向的是同一个矩阵数据)
    	waitKey();
    	return 0;
    }

    声明一个指定类型的Mat类:

    Mat b = Mat_<double>(3, 3);//创建一个3*3的矩阵存放double类型数据

    通过OpenCv数据类型创建Mat类

    Mat a(640, 480, CV_8UC3)//创建一个640*480的3通道矩阵用于存放彩色图像
    Mat a(3, 3, CV_8UC1);//创建一个3*3的8位无符号整数的单通道矩阵
    Mat a(3, 3, CV_8U);//创建单通道矩阵,C1标识可以省略

    2、Mat类的构造与赋值

    2.1、Mat类的构造

    默认构造函数

    cv::Mat::Mat();

     这种构造方式,不需要传递任何参数,在后续给变量赋值的时候会自动判断矩阵的类型与大小,实现灵活的存储,常用于存储读取的图像数据和某个函数运算的输出结果。

    根据输入矩阵尺寸和类型构造

    cv::Mat::Mat(int rows,int cols,int type)

    其中rows:构造矩阵的行数;cols:矩阵的列数;type:矩阵存储的数据类型,例如CV_8UC1之类。

    用Size()结构构造Mat类。

    cv::Mat::Mat(Size size(), int type);

    其中size:为二位数组变量尺寸,通过Size(cols,rows)进行赋值(列在前,行在后);type:矩阵存储的数据类型,例如CV_8UC1之类。

    示例:

    cv::Mat a(Size(480,580), CV_8UC1);//构造一个行580,列480的单通道矩阵

    利用已有矩阵构造Mat类

    cv::Mat::Mat(const Mat & a);

    这种构造方式只是复制了Mat类的矩阵头,矩阵指针指向是同一个地址。(浅复制)

    若要深复制,可用

    b = a.clone();

    构造已有Mat的子类

    cv::Mat::Mat(const Mat & a,const Range & rowRange, const Range & colRange=Range::all);

    其中,m:已经构建完成的Mat;rowRange:在已有的矩阵中需要获取的行数范围,是一个Range变量,例如从第二行到第五行表示为Range(2,5);colRange也是一样,在已有的矩阵中需要获取的列数范围,是一个Range变量,例如从第二列到第五列表示为Range(2,5)省略时,所有的列都会被截取。(注意:这种方式构造的类与已有Mat类共享数据。)

     例如:

    cv::Mat b(a, Range(2, 5), Range(1, 4));//从a中截取第二行到第五行,第一列到第四列的内容
    cv::Mat b(a, Range(2, 5));//从a中截取第二行到第五行的内容

    2.2、Mat类的赋值

    在构造时赋值

    cv::Mat::Mat(int rows, int cols, int type, const Scalar & s)

    其中rows:矩阵的行数; cols:矩阵的列数;type:存储数据的类型;s:给矩阵中每个橡树赋值的参数变量,例如Scalar(0,0,255)

    例如:

    cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0, 0, 255));//创建一个3通道矩阵,每个像素都是0,0,255
    cv::Mat a(2, 2, CV_8UC2, cv::Scalar(0, 255));//创建一个2通道矩阵,每个像素都是0,255
    cv::Mat a(2, 2, CV_8UC1, cv::Scalar(255));//创建一个单通道矩阵,每个像素都是255

     枚举法赋值

    cv::Mat a= (cv::Mat_<int>(3,3)<<1,2,3,4,5,6,7,8,9);
    cv::Mat a = (cv::Mat_<double>(2, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0);

    第一行创建一个3*3的  矩阵,数据类型为int型,一行一行的存入。

    循环法赋值:

    cv::Mat a = (cv::Mat_<int>(3, 3));//定义一个3*3的矩阵
    for(int i = 0; i < a.cols;i++) //矩阵行数循环
    {
    	for (int j; j < a.cols; j++) //矩阵列数循环
    	{
    		a.at<int>(i, j) = i + j;
    	}
    }
    

    利用类方法赋值示例:

    	cv::Mat a = cv::Mat::eye(3, 3, CV_8UC1);
    	cv::Mat b = (cv::Mat_<int>(1, 3) << 1, 2, 3);
    	cv::Mat c = cv::Mat::diag(b);
    	cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);
    	cv::Mat e = cv::Mat::zeros(3, 2, CV_8UC3);

    利用数组进行赋值:

    3、Mat类支持的运算

    3.1Mat类的加减乘除运算:

    cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
    cv::Mat b = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
    cv::Mat c = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0,5.0, 6.0, 7.0, 8.0, 9.0);
    cv::Mat d = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
    cv::Mat e, f, g, h,i;
    e = a + b;
    f = c - d;
    g = 2 * a;
    h = d / 2.0;
    i = a - 1;

    当两个类进行加减运算时,需保证数据要一致,比如int和double类型数据的两个类不能进行加减运算。常数与Mat类变量运算,结果的数据类型保留Mat类变量的数据类型。

    3.2两个Mat类矩阵的乘法运算

    cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
    cv::Mat b = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
    cv::Mat c = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
    cv::Mat d = (cv::Mat_<double>(3, 3) << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
    cv::Mat e, f;
    double k;
    e = c*d;//数学乘积
    k = a.dot(b);//内积
    f=a.mul(b)//对应位乘积

    4、Mat类元素的读取

    4.1多通道数据的存储

    3通道3*3矩阵存储方式 

    先存储第一个元素的三个通道数据,再存储第二个元素三个通道数据。

     4.2Mat类常用属性和成员方法

    属性作用
    cols矩阵的列数
    rows矩阵的行数
    steps以字节为单位的有效宽度
    dims矩阵的维度
    成员方法
    elemSize()每个元素的字节数
    total()矩阵中元素的个数
    channels()矩阵的通道数

    4.3通过at读取Mat类通道矩阵的元素

    	cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
    	int b = a.at<int>(0, 0);

    通过at读取元素需要跟上“<数据类型>”,类型不匹配则会报错。该方法以坐标的形式给出需要读取的元素坐标(行数,列数)。

    4.4通过at读取Mat类通道矩阵的元素

    因为单通道图像是一个二维矩阵,所以 a t方法的最后给出二维坐标即可访问对应位置元素。在OpenCv中,针对三通道,定义了,cv::Vec3b、cv::Vec3s、cv::Vec3w、cv::Vec3d、cv::Vec3f、cv::Vec3i共6种类型,其中数字表示通道数,最后一位是类型的缩写,b是uchar的缩写,s是short类型的缩写,w是ushort类型的缩写,d是double类型的缩写,f是float类型的缩写,i是int类型的缩写。

    举例说明:

    	cv::Mat a(3,4,CV_8UC3,Scalar(2,0,255));
    	cv::Vec3b b = a.at<Vec3b>(0, 0);
    	int first = b[0];  //值为2
    	int second = b.val[1];//值为0
    	int third = b.val[2];//值为255

    4.5通过指针ptr读取Mat矩阵种的元素

    矩阵中每一行的每个元素都是挨着放的,如果能找到每一行元素的起始地址位置,那么读取矩阵中每一行不同位置的元素时将指针在起始位置向后移动若干位即可。

    	cv::Mat a(3,4,CV_8UC3,Scalar(2,0,255));
    	for (int i = 0; i < a.rows;i++)
    	{
    		uchar *ptr = a.ptr<uchar>(i);
    		for (int j = 0; j < a.cols*a.channels(); j++)
    		{
    			cout << (int)ptr[j] << endl;
    		}
    	}

    当读取第2行数据中第3个数据时,可直接用a.ptr<uchar>(1)[2]直接访问

    4.6通过迭代器访问Mat类矩阵中的元素

     Mat类变量也是一个容器变量,故Mat类变量拥有迭代器,用于访问Mat变量中的数据,通过迭代器可以实现对矩阵中每一个元素的遍历。

    	cv::Mat a = (cv::Mat_<uchar>(3, 3) << 3, 4, 5, 1, 6, 7, 8, 0, 1);
    	cv::MatIterator_<uchar> it = a.begin < uchar > ();
    	cv::MatIterator_<uchar> it_end = a.end < uchar >();
    	for (int i = 0;it!=it_end;it++)
    	{
    		cout << (int)(*it) << " ";
    		if (++i%a.channels()==0)
    		{
    			cout << endl;
    		}
    
    	}

    Mat类的迭代器变量类型是cv::MatIterator_< >,在定义时同样需要在括号中声明数据的变量类型。Mat类迭代器的起始是Mat.begin< >(),结束是Mat.end< >(),与其他迭代器用法相同,通过“++”运算实现指针位置向下迭代,数据的读取方式是先读取第一个元素的每一个通道,之后再读取第二个元素的每一个通道,直到最后一个元素的最后一个通道。

    展开全文
  • 4、OpenCV中的Mat类

    千次阅读 2021-01-06 15:36:28
    OpenCV 中的Mat类一、学习目标二、Mat 介绍(建议细读)三、Mat的存储方法四、显示地创建 Mat 对象五、格式化输出 Mat 对象六、其他常见元素的输出七、致谢 一、学习目标 我们有多种方法从现实世界中获取数字图像:...

    一、学习目标

    我们有多种方法从现实世界中获取数字图像:如数码相机、扫描仪、计算机断层摄影术(CT)和磁共振成像等。在任何情况下,我们(人类)看到的都是图像。然而,当将其转换到我们的数字设备时,我们所使用的是图像中每个点的数值(像素值)。
    在这里插入图片描述
    例如,在上面的图像中,你可以看到汽车的后视镜只是一个包含所有像素点的强度值(像素值)的矩阵。我们获取和存储像素值的方式可能会根据我们的需要而有所不同,但最终计算机世界中的所有图像都可能被简化为数字矩阵和描述矩阵本身的其他信息。OpenCV是一个计算机视觉库,它的主要关注处理和操作这些信息。因此,我们首先需要熟悉的是OpenCV如何存储和处理图像。

    二、Mat 介绍(建议细读)

    OpenCV从2001年就出现了。当时,OpenCV的库是围绕C接口构建的,为了将图像存储在内存中,Opencv使用了一个称为IplImage的C语言结构体。在大多数较老的教程和教学资料中看到的就是这种结构体。这样做有很多的弊端,它把C语言的所有缺点都展现了出来,其中最大的问题是手动内存管理,用户必须负责内存的分配和回收。虽然这对较小的程序来说不是问题,但一旦你的代码库增长得比较大,那么进行内存管理就会相当复杂,其难度不亚于解决真正得图像处理问题,这样就有点舍本逐末了。

    幸运的是c++引入了类的概念,通过自动内存管理机制(或多或少)使用户更容易使用类。幸运的是c++与C是完全兼容的,所以修改后不会出现兼容性问题。因此,OpenCV 2.0引入了一个新的c++接口,它提供了一种新的处理方式,这意味着你不需要手动处理内存管理,这会使你的代码更简洁。c++接口的主要缺点是,目前许多嵌入式开发系统只支持C。因此,除非你的目标是嵌入式平台,否则使用老接口是没有意义的(除非你是一个受虐狂程序员,你在自找麻烦)。

    关于Mat,我们需要知道的第一件事是,我们不再需要手动分配它的内存并在不需要时立即释放它。虽然我们也可以自行分配和管理内存,但大多数OpenCV函数将自动分配其数据所需的内存空间。如果您传递一个已经存在的Mat对象,它已经为矩阵分配了所需的空间,那么它将被重用。换句话说,我们在任何时候都只使用执行任务所需的内存。

    Mat是一个类,其包含两个数据部分:Mat头部(包含矩阵的大小,存储方法,和存储地址等信息)和一个指向包含像素值矩阵的指针(根据选择的存储方法选择任意维度)。Mat头部的大小是恒定的,然而存储像素值的矩阵本身的大小可能因图像而异,通常要大几个数量级。

    OpenCV是一个图像处理库。它包含了大量的图像处理功能。为了解决一个计算机视觉问题,大多数时候您将使用库的多个函数。正因为如此,将图像传递给函数是一种常见的做法。我们不要忘了,我们正在讨论的是图像处理算法,它往往计算量很大。我们最不希望做的就是对大尺寸的图像进行不必要的复制,这会进一步降低程序的速度。

    为了解决这个问题,OpenCV使用了一个引用计数系统。每个Mat对象都有自己的头部,但是两个Mat对象之间可以共享一个矩阵(像素矩阵),即让它们的矩阵指针指向相同的地址。此外,复制操作只复制Mat头部信息和指向数据矩阵的指针,而不复制数据本身(浅拷贝)。

        Mat A, C;                          // 只创建Mat的矩阵头信息
        A = imread(argv[1], IMREAD_COLOR); // 这里我们将知道所使用的方法(三通道彩色图,分配矩阵)
        Mat B(A);                          // 使用拷贝构造函数
        C = A; 
    

    所有上述对象最终都指向同一个数据矩阵,使用其中任何一个对象进行修改也会影响到其他所有对象。实际上,不同的对象只是对相同的底层数据提供了不同的访问方法。然而,它们的Mat头部是不同的。真正有趣的是,您可以创建只引用完整数据一部分的Mat对象。例如,要在一个图像中创建一个感兴趣的区域(ROI),你只需创建一个带有新的边界的Mat头部:

        Mat D (A, Rect(10, 10, 100, 100) );  // 使用矩阵定义感兴趣区域,后面会讲Rect对象
        Mat E = A(Range::all(), Range(1,3)); // 使用行和列的边界定义感兴趣区域
    

    现在你可能会问:如果矩阵本身属于多个Mat对象,那么当这个Mat对象不再使用的时候谁负责清理它?答案很简单:最后一个使用它的对象。这可以通过使用引用计数机制来处理。每当有人复制一个Mat对象的头部时,矩阵的计数器就会增加。每当清理Mat头部时,此计数器将减少。当计数器达到0时,释放矩阵。当然,有时你也想真正地复制矩阵本身,所以OpenCV提供了cv::Mat::clone()cv::Mat::copyTo() 函数。

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

    现在修改FG将不会影响由A的 Mat头部指向的矩阵。现在,你需要记住的是:

    • OpenCV函数的输出图像内存分配是自动的(除非另有说明)。
    • 您不需要考虑使用OpenCV的c++接口进行内存管理。
    • 赋值操作和拷贝构造函数只复制Mat头部
    • 图像的底层矩阵可以使用cv::Mat::clone()cv::Mat::copyTo() 函数进行复制。

    三、Mat的存储方法

    这是关于如何存储像素值。您可以选择使用的颜色空间和数据类型。颜色空间指的是我们如何组合颜色部分以编码给定的颜色。最简单的是灰度图,我们使用的颜色是黑色和白色。这些组合可以让我们创造出许多灰色的阴影。

    对于彩色的方式,我们有更多的方法可供选择。每一个像素值都将其分解为三到四个基本部分,我们可以使用这些部分的组合来创建其他部分。最流行的一种是RGB,主要是因为这也是我们的眼睛构建颜色的方式。它的基色是红、绿、蓝。为了编码颜色的透明度,有时会添加第四个元素:alpha (a)

    然而,还有许多其他的颜色系统,它们都有自己的优势:

    • RGB是最常见的,因为我们的眼睛使用的是类似的东西,但是请记住,OpenCV标准显示系统使用BGR颜色空间组成颜色(红色和蓝色通道交换位置)。
    • HSVHLS将颜色分解为色相、饱和度和值/亮度分量,这是我们描述颜色的一种更自然的方式。例如,你可以去掉最后一个组件,这样你的算法对输入图像的光照条件就不那么敏感了。
    • YCrCb是常用的JPEG图像格式。
    • CIE Lab*是一个感知上一致的颜色空间,如果你需要测量一种给定颜色到另一种颜色的距离,它就派上用场了。

    每个颜色空间的组成部分都有自己的有效域。这就引出了所使用的数据类型。我们如何存储一个组件定义了我们在它的域上拥有的控件。最小的数据类型可能是char,这意味着一个字节或8位。这可能是无符号的(因此可以存储0到255的值)或有符号的(值从-127到+127)。尽管在三个组件的情况下,这已经给出了1600万种可能的颜色来表示(就像RGB的情况),我们可以通过使用float(4字节= 32位)或double(8字节= 64位)的数据类型来获得更精细的控制。然而,请记住,增加组件的大小(存储字节大小)也会增加图像所占内存的大小。

    四、显示地创建 Mat 对象

    加载修改保存图像教程中,您已经学习了如何使用cv::imwrite() 函数将Mat对象写入图像文件。但是,出于调试目的,查看实际值要方便得多。你可以使用Mat<< 操作符来实现这一点。请注意,这只对二维矩阵有效。

    尽管Mat作为图像容器工作得非常好,但它也是一个通用的矩阵类。因此,可以创建和操作多维矩阵。你可以用多种方式创建Mat对象:

    • cv::Mat::Mat构造函数
        Mat M(2,2, CV_8UC3, Scalar(0,0,255));
        cout << "M = " << endl << " " << M << endl << endl;
    

    在这里插入图片描述
    对于二维地单通道或多通道图像,我们首先定义它们的大小:行和列。

    然后,我们需要指定用于存储元素的数据类型以及每个矩阵点的通道数量。为了做到这一点,我们可以使用如下方法定义:

        CV_[每个数值的位数][有符号或者无符号][类型前缀]C[通道数目]
    

    例如,CV_8UC3意味着我们使用长度为8位的unsigned char类型,每个像素有三个unsigned char类型来形成三个通道。有最多四个通道预定义的类型。cv::Scalar为四元短向量。指定它,您就可以用自定义值初始化所有矩阵的像素值。如果需要更多,可以使用上面的宏创建类型,在括号中设置通道号,下面会有示例。

    • 使用C/ c++数组并通过构造函数进行初始化
        int sz[3] = {2,2,2};
        Mat L(3,sz, CV_8UC(1), Scalar::all(0));
    

    上面的例子展示了如何创建一个超过二维的矩阵。指定它的维度,然后传递一个包含每个维度大小的指针,其余部分保持不变。

    • cv::Mat::create函数
        M.create(4,4, CV_8UC(2));
        cout << "M = "<< endl << " "  << M << endl << endl;
    

    在这里插入图片描述
    不能使用此构造初始化矩阵值。如果新的矩阵大小不适合旧的,它只会重新分配它的矩阵数据内存。

    • 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 << " " << Z << endl << endl;
    

    在这里插入图片描述

    • 对于较小的矩阵,你可以使用逗号分隔的初始化式或初始化列表(最后一种情况需要c++ 11支持):
        Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
        cout << "C = " << endl << " " << C << endl << endl;
    
        C = (Mat_<double>({0, -1, 0, -1, 5, -1, 0, -1, 0})).reshape(3);
        cout << "C = " << endl << " " << C << endl << endl;
    

    在这里插入图片描述

    • 为现有的Mat对象使用cv::Mat::clonecv::Mat::copyTo创建一个新的头部。
        Mat RowClone = C.row(1).clone();
        cout << "RowClone = " << endl << " " << RowClone << endl << endl;
    

    在这里插入图片描述
    请注意
    你可以用**cv::randu()**函数用随机值填充一个矩阵。你需要为随机值给出一个下限和上限:

        Mat R = Mat(3, 2, CV_8UC3);
        randu(R, Scalar::all(0), Scalar::all(255));
    

    五、格式化输出 Mat 对象

    在上面的例子中,您可以看到默认的格式选项。然而,OpenCV允许你格式化你的矩阵输出:

    • 默认方式
     cout << "R (default) = " << endl <<        R           << endl << endl;
    

    在这里插入图片描述

    • 以python风格
        cout << "R (python)  = " << endl << format(R, Formatter::FMT_PYTHON) << endl << endl;
    

    在这里插入图片描述

    • 逗号分隔值(CSV)
        cout << "R (csv)     = " << endl << format(R, Formatter::FMT_CSV   ) << endl << endl;
    

    在这里插入图片描述

    • numpy风格
        cout << "R (numpy)   = " << endl << format(R, Formatter::FMT_NUMPY ) << endl << endl;
    

    在这里插入图片描述

    • C 风格
        cout << "R (c)       = " << endl << format(R, Formatter::FMT_C     ) << endl << endl;
    

    在这里插入图片描述

    六、其他常见元素的输出

    OpenCV也通过 << 操作符提供了对其他常见OpenCV数据结构输出的支持:

    • 2D 点
        Point2f P(5, 1);
        cout << "Point (2D) = " << P << endl << endl;
    

    在这里插入图片描述

    • 3D 点
        Point3f P3f(2, 6, 7);
        cout << "Point (3D) = " << P3f << endl << endl;
    

    在这里插入图片描述

    • std::vector 对比 cv::Mat
        vector<float> v;
        v.push_back( (float)CV_PI);   v.push_back(2);    v.push_back(3.01f);
        cout << "Vector of floats via Mat = " << Mat(v) << endl << endl;
    

    在这里插入图片描述

    • std::vector 的点
        vector<Point2f> vPoints(20);
        for (size_t i = 0; i < vPoints.size(); ++i)
            vPoints[i] = Point2f((float)(i * 5), (float)(i % 7));
        cout << "A vector of 2D Points = " << vPoints << endl << endl;
    

    在这里插入图片描述

    七、致谢

    1、常规谢天谢地谢父母,感谢各位好友
    2、特别感谢OpenCV的官方文档
    3、感兴趣的小伙伴欢迎入群一起学习和探讨。飞机票: 入群链接

    展开全文
  • openCV学习(一)——Mat类详解

    千次阅读 2022-04-27 16:28:33
    学习内容: Mat类的概述 Mat类的创建 Mat类的成员函数说明 Mat常见的矩阵操作 一、Mat类的概述 1.Mat类有两个数据部分:矩阵头(包含矩阵的大小,存储方法和存储地址等)和一个指针,指向包含像素值的矩阵地址。...

    学习目标:

    熟知Mat类,能够创建、赋值,了解其成员函数和常见的运算操作。


    学习内容:

    1. Mat类的概述
    2. Mat类的创建
    3. Mat类的成员函数说明
    4. Mat常见的矩阵操作

    一、Mat类的概述

    1.Mat类有两个数据部分:矩阵头(包含矩阵的大小,存储方法和存储地址等)和一个指针,指向包含像素值的矩阵地址。矩阵头的存储空间是常数,矩阵本身的尺寸随着图像大小改变。

    2.矩阵的拷贝、赋值运算符通常只赋值矩阵头和矩阵指针,但不是矩阵本身,即采用浅拷贝来提高效率


    二、Mat类的创建

    1.构造方法

     

    Mat img(3,2, CV_8UC3, Scalar(0,0,255))

     创建一个Mat类型的变量img,它是一个3行2列的矩阵,元素是8位无符号整数且有三个通道,初始化为Scalar(0,0,255)即红色图像。

    • Mat类中type类型,CV_8UC1,CV_16SC3,CV_64FC4,其中8U表示8位无符号整数,16S表示16位有符号整数,64F表示64位浮点数,C表示通道数,C1表示一个通道的图像,即灰度图,C4表示4个通道的图像。
    • openCV中用Scalar(0,255,0)表示颜色,默认颜色顺序为BGR。

    2.create()

    使用create创建Mat对象时不能初始化。

    Mat img;
    img.create(3,2,CV_8UC1)
    

    3.直接赋值

    m = (Mat_<int>(2,3) << 1,2,3,4,5,6); // 直接赋值 
    

    4.浅拷贝

    矩阵相互赋值,只创建矩阵头,共享数据部分。

    Mat img1( img );    //img和myMat共享图像数据
    Mat myMat= img1;   //仅创建头部分,共享数据部分
    

    5.深拷贝

    使用clone()和copyTo()实现深拷贝

    Mat img = myMat.clone()
    Mat G;
    img.copyTo(G);
    

    6.使用eye()、ones()、zeros()初始化Mat

    Mat A = Mat:: eye(4,4,CV_64F) //初始化为对角矩阵
    Mat B = Mat:: ones( 2, 2, CV_8U)  //所有元素初始化为1
    Mat C = Mat:: zeros( 3, 3, CV_8UC1)  //所有元素初始化为0
    

    三、Mat类的成员函数

    • col(int i)  //获取第i列数据,返回也是Mat类型
    • row(int j)  //获取第j行数据,返回也是Mat类型
    • size()  //矩阵尺寸[rows, cols]
    • total()  //rows* cols,而非矩阵元素数
    • channels()  //图像通道数
    • rowRange()和colRange() // 读取连续几行或者几列
    • m.rowRange(2,4)
    • 遍历像素函数at()
    • 格式为:myMat.at<float>(i, j) 获取矩阵第i行第j列的元素
    • 获取矩阵行首元素指针ptr(),格式为mymat.ptr<int>(i)  指向第i行首元素的指针
    • m.isContiuous()每一行是否连续存储的。

    四、Mat常见的运算操作

    设为A,B为Mat类型,s是Scalar类型,a是一个实数。下面列出关于Mat的常用运算:

    • 矩阵加减: A+B,A-B,A+s,A-s,s+A,s-A,-A.
    • 矩阵乘以实数: A*a,a*A
    • 逐元素乘除: A.mul(B),A/B,a/A
    • 矩阵乘法: A*BmaxVal; Point minPos,m
    • 矩阵倒置: A.t()
    • 矩阵的逆: A.inv()
    • 矩阵比较: A comp B,A comp a,a comp A。这里comp包括 >, >=,==,!=,<=,<。得出的结果是一个单通道8位的矩阵,元素的值为255或0。
    • 矩阵位操作: A logic B, A logic s,s logic A。这里logic包括:&,|,^
    • 向量的差乘和内积: A.cross(B),A.dot(B);

    以上方法均为Mat的内部方法,可以直接调用。

    展开全文
  • OpenCV中Mat类的使用

    千次阅读 2021-02-26 04:17:57
    OpenCV的Mat类可以表示一个N维的数组,数组的每个位置可以是单通道或多通道。可以用来存储图片(灰度或彩色)、向量、直方图等很多东西。
  • Mat类详解

    2019-07-17 11:10:42
    Mat类可以被看做是opencv中C++版本的矩阵类,替代原来C版本的矩阵结构体CvMat和图像结构体IplImage; Mat最大的优势跟STL的兼容性很好,有很多类似于STL的操作。但是Mat远远强于后者; Mat是一种高效的数据类型,它...
  • OpenCV中的Mat类

    千次阅读 2020-05-03 17:04:02
    OpenCV Mat类 OpenCV 自 2001 年出现以来。在那些日子里库是围绕C接口构建的。在那些日子里,他们使用名为IplImage C 的结构在内存中存储图像。 使用这个结构的问题是将 C 语言的所有负面效果都摆到了桌面上。最大的...
  • 【opencv4.3.0教程】03之Mat类详解

    千次阅读 多人点赞 2020-05-10 19:06:01
    二、Mat定义 1、Mat是个啥 2、Mat代码定义 三、常用成员及方法介绍 1、常用成员 2、构造函数 3、其他方法 四、常用方法 1、构造函数 2、其他方法 五、Mat容器使用 1、Mat创建对象 ...
  • Mat类定义

    千次阅读 2018-07-02 11:01:44
    本文转自:http://blog.csdn.net/holybin/article/details/17751063综述Mat类可以被看做是opencv中C++版本的矩阵类,替代原来C版本的矩阵结构体CvMat和图像结构体IplImage;Mat最大的优势跟STL的兼容性很好,有...
  • Mat类概述、Mat对象的构造方法、Mat对象常用方法、int depth()、void copyTo(Mat mat)、void convertTo(Mat dst,int type)、Mat clone()、void create(xxx)、int channels()、int rows()、int cols()、头部信息、...
  • Mat类 基本介绍 Mat类是OpenCV中使用最频繁的类之一,用于储存矩阵数据及相关操作,也是程序中图像的主要形式。 Mat类主要由两部分组成:一个描述头(matrix header)及一个指向矩阵数据的指针。其中,描述头包含了...
  • OpenCV | Mat类的copyT、clone、=赋值的区别1、clone2、copyTo3、等号‘=’赋值4、验证 先说一下Mat类的结构。 Mat类我们可以分成两部分:头部分、矩阵数据部分。 头部分:用于记录矩阵数据的大小、类型、数据指针...
  • C++ OpenCV Mat类简介

    2020-06-20 09:29:37
    数字图像在计算机内存中以矩阵形式存储,而Mat类是操作矩阵的类。 一、图像像素值的访问 cv::Mat image = cv::Mat(400,600,cv_8UC1); //weight=400,height=600,8unit单通道 uchar * data00 = image.ptr<...
  • OpenCV学习(2): Mat 源码

    千次阅读 2018-04-21 16:42:39
    Mat类源码 Mat类源码 引入 存储物理结构 Array Type定义 Mat类的构成 构造函数 矩阵操作 简单使用 实例化 元素访问 opencv源码 类定义源码 部分函数源码 引入 现时世界中我们所看到的图像...
  • 【从零学习OpenCV 4】Mat类构造与赋值

    千次阅读 2019-12-23 09:05:49
    本文首发于“小白学视觉”微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社所有,禁止转载,侵权必究!...前一小节已经介绍了三种构造Mat类变量的方法,但是后两种没有给变量...
  • 这个是关于OpenCV中Mat类的一些数学运算,包括加减运算,点乘,点除,矩阵乘法,还有幂函数指数函数等计算以及一些区域的选取。
  • Mat类与颜色空间 EmguCV常用数据结构 Point类–System.Drawing.Point Point类数据结构表示二维坐标系的点,由坐标x, y指定的2D点,如: Point pt; pt.x = 20; pt.y = 10; 其他类型 Rectangle类–System.Drawing....
  • 为了方便运算,Mat类变量支持矩阵的加减乘除运算,即我们在使用Mat类变量时,将其看做普通的矩阵即可,例如Mat类变量与常数相乘遵循矩阵与常数相乘的运算法则。Mat类与常数运算时,可以直接通过加减乘除符号实现。 ...
  • 本文首发于“小白学视觉”微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社所有,禁止转载,侵权必究!...对于Mat类矩阵的读取与更改,我们已经在矩阵的循环赋值中见过如何用...
  • 1、初识Mat ... 列数、通道数以及数据类型,所以Mat类的构造函数如下: Mat(int rows, int cols, int type) Mat(Size(int cols, int rows), int type)// Size 第一个元素是矩阵的列数(宽) 2、...
  • 参考材料 参考材料 类Mat导言 Ope nCV c + + n 维稠密数组 CV_EXPORTS Mat { public : / / 很多的方法. /!包括几位字段 -神奇的签名 -连续性标志 -深度Note :应该是位深 -通道数 */ int flags; Note :目前还不...
  • OpenCV3之Mat类的创建

    2020-01-04 21:45:58
    文章目录一、Mat类的类型CV_8UC11.CV_8UC1(1)格式(2)意思①depth深度:②符号类型:只有三个值:`S`,`U`,`F`③`C`:表示通道,写出来只是为了分割前后两个数字,以免混淆。④cn通道数:取值可以是`1`,`2`,`3...
  • opencv之Mat类初始化

    2020-04-20 10:48:17
    早期的 OpenCV 中,使用 IplImage 和 CvMat 数据结构来表示图像。IplImage 和 CvMat 都...这个开发者带来了一定的负担,开发者应该将更多精力用于算法设计,因此在新版本的 OpenCV 中 引入了 Mat 。 新加入的 Ma...
  • Mat 早期的 OpenCV 中,使用 IplImage 和 CvMat 数据结构来表示图像。 IplImage和 CvMat 都是 C 语言的结构。 使用这两个结构的问题是内存需要手动管理,开发者必须清楚的知道何时需要申请内存,何时需要释放内存...
  • Mat_ Mat_是对 Mat 的一个包装,其定义如下: template<typename _Tp> class Mat_ : public Mat { public: //只定义了几个方法 //没有定义新的属性 };这是一个非常轻量级的包装,既然已经有 Mat ,为何...
  • Mat类的介绍与图像的读写 1. Mat类的介绍 opencv2.x采用全行的图像数据结构Mat。Mat结构不需要我们为其手动开辟空间,也不需要立即释放存储空间,Mat类能够自动管理内存。Mat类由矩阵头和指向存储所有像素值的矩阵...
  • opencv Mat类的介绍

    2019-02-19 15:20:54
    Mat类是我们使用opencv处理图像必定会用到的类。在使用时候我们需要知道: 1、不必手动为其开辟空间 2、不必在不需要时立即释放空间 mat类由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方式、存储地址) 和 一...
  • C++ OpenCV的Mat类

    千次阅读 2018-09-05 16:16:50
    在C++的编程体系下,Mat类在OpenCV中是最重要的一种图像表示形式(即为Matrix)。这里的Mat 可以指定存储的数据类型、行列数,以及每个元素中的数值个数(通道数)等等。 Mat 类的数据分为两个部分: 1.属性部分 2....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 77,920
精华内容 31,168
关键字:

mat 类

友情链接: 归档.zip