opencv语法Mat类型总结

Mat_类型

方法描述示例
Mat数据类型Mat mat(3000, 4000, CV_8UC3);//行、列、元素大小字节数
mat.data图像数据QImage img(mat.data, src.cols, src.rows, QImage::Format_RGB888);
rows数据行mat.rows //数据行,可代表高度
cols数据列mat.cols //数据列,可代表宽度
elemSize元素大小字节数mat.elemSize();
step表示行数据长度(单位是字节数)mat.step; //确定了mat如何去换行。(mat每一行内是连续的,只是行结束地址不定)
create()先定义了mat,后面确定行、列等参数后使用creatmat.create(3000, 4000, CV_8UC3); //如果mat原来有空间则会被释放

在这里插入图片描述

在这里插入图片描述

确保像素值在0-255之间:
在这里插入图片描述

QT使用Mat类型

QT默认是不支持Mat类型作为信号槽的,需要注册,如下是方法。

// 注册
qRegisterMetaType<cv::Mat>("cv::Mat"); //信号槽传递的参数,QT默认不支持cv::Mat。采用此种方式注册。<cv::Mat>是类型,"cv::Mat"是名字

//使用例程
    QObject::connect(XVideoThread::Get(),  //哪个对象发出
        SIGNAL(ViewImage1(cv::Mat)),  //发出的信号。
        ui.cameraShow,
        SLOT(SetImage(cv::Mat))
    );

mat之深拷贝、浅拷贝理解

cv::Mat a; //创建一个名为a的矩阵头
a = cv::imread(“test.jpg”); //向a中赋值图像数据,矩阵指针指向像素数据。默认读的格式为BGR
cv::Mat b=a; //复制矩阵头,并命名为b(浅复制)。当删除a变量时,b变量并不会指向一个空数据,只有当两个变量都删除后,才会释放矩阵数据。因为矩阵头中引用次数标记了引用某个矩阵数据的次数,只有当矩阵数据引用次数为0的时候才会释放矩阵数据。采用引用次数来释放存储内容是C++中常见的方式,用这种方式可以避免仍有某个变量引用数据时将这个数据删除造成程序崩溃的问题,同时极大的缩减了程序运行时所占用的内存。

1.Mat类型遍历与赋值
1)拷贝赋值
浅层拷贝:Mat B=A;B就是浅层拷贝A,B只拷贝了A的的头部和地址,当B被操作后A也随之改变。
深层拷贝:Mat A=imread("x.jpg"); Mat B=A.clone();B是开辟了新的内存完全的复制了A的内容,操作B不会对A造成影响。
src1.copyTo(des);   //不能用des = src1 (浅复制),这样做会导致des改变的时候src1(原图)也改变了。

//注意:浅拷贝 - 不复制数据只创建矩阵头,数据共享(更改a,b,c的任意一个都会对另外2个产生同样的作用)
Mat aa;
Mat ba = aa; //aa “copy” to ba
Mat ca(aa); //aa “copy” to ca
//注意:深拷贝
Mat aa;
Mat ba = aa.clone( ); //aa copy to ba
Mat ca;
aa.copyTo( ca ); //aa copy to ca

2)将数据类型为U16的dataU16赋值给数据类型为u8的dataU8(数据类型转换).
    Mat dataU16 = Mat(Size(w, h), CV_16UC1);
    Mat dataU8 = Mat(Size(w, h), CV_8UC1);
    U16* pxvecU16 = dataU16.ptr<U16>(0);
    U8* pxvecU8 = dataU8.ptr<U8>(0);
    for (int i = 0; i < dataU16.rows; i++)
    {
        pxvecU16 = dataU16.ptr < U16>(i);
        pxvecU8 = dataU8.ptr<U8>(i);
        for (int j = 0; j < dataU16.cols; j++)
        {
            pxvecU8[j] = (U8)pxvecU16[j];
        }
    }

mat之构造函数

非常好的参考教程0-Mat类介绍
非常好的参考教程1-Mat类构造与赋值

1.常规1
cv::Mat::Mat( int  rows, int  cols, int  type)
rows:构造矩阵的行数  (高度)
cols:矩阵的列数  (宽度)
cv::Mat M(480,640,CV_8UC3); 表示定义了一个480行,640列的矩阵,矩阵的每个单元的由三个(C3:3 Channel)8位无符号整形(unsigned char 8, U8)构成。表示的是三通道,是彩色图像。

2.常规2
通过输入矩阵的行、列以及存储数据类型实现构造。这种定义方式清晰、直观、易于阅读,常用在明确需要存储数据尺寸和数据类型的情况下,例如相机的内参矩阵、物体的旋转矩阵等。利用输入矩阵尺寸和数据类型构造Mat类的方法存在一种变形,通过将行和列组成一个Size()结构进行赋值。
cv::Mat::Mat(Size size(), int  type)
size:2D数组变量尺寸,通过Size(cols, rows)(宽,高)进行赋值。
type:与代码清单2-5中的参数一致
cv::Mat a(Size(480, 640), CV_8UC1); //构造一个行为640,列为480的单通道矩阵

3.定义特殊矩阵
cv::Mat mz = cv::Mat::zeros(cv::Size(w,h),CV_8UC1); // 全零矩阵
【或者:Mat tmpdata = Mat::zeros(h, w, CV_8UC1);//h⾏w列的全0矩阵】
cv::Mat mo = cv::Mat::ones(cv::Size(w,h),CV_8UC1);  // 全1矩阵
【或者:Mat tmpdata = Mat::ones(h, w, CV_8UC1);//h⾏w列的全1矩阵】
cv::Mat me = cv::Mat::eye(cv::Size(w,h),CV_32FC1);  // 对⾓线为1的对⾓矩阵
【或者:Mat tmpdata = Mat::eye(h, w, CV_32FC1);//h⾏w列的对⾓矩阵】

4.利用已有矩阵构造Mat类(浅拷贝方式)
cv::Mat::Mat( const Mat & m);
m:已经构建完成的Mat类矩阵数据。(浅拷贝方式)这种构造方式非常简单,可以构造出与已有的Mat类变量存储内容一样的变量。注意这种构造方式只是复制了Mat类的矩阵头,矩阵指针指向的是同一个地址,因此如果通过某一个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变量,例如从第2行到第5行可以表示为Range(2,5)。
colRange:在已有矩阵中需要截取的列数范围,是一个Range变量,例如从第2列到第5列可以表示为Range(2,5),当不输入任何值时表示所有列都会被截取。这种方式主要用于在原图中截图使用,不过需要注意的是,通过这种方式构造的Mat类与已有Mat类享有共同的数据,即如果两个Mat类中有一个数据发生更改,另一个也会随之更改。

>赋值:
1)在构造时赋值的方法
cv::Mat::Mat(int  rows,int  cols,int  type, const Scalar & s )
rows:矩阵的行数
cols:矩阵的列数
type:存储数据的类型
s:给矩阵中每个像素赋值的参数变量,例如Scalar(0, 0, 255)。Scalar结构中变量的个数一定要与定义中的通道数相对应,如果Scalar结构中变量个数大于通道数,则位置大于通道数之后的数值将不会被读取,例如执行a(2, 2, CV_8UC2, Scalar(0,0,255))后,每个像素值都将是(0,0),而255不会被读取。如果Scalar结构中变量数小于通道数,则会以0补充。
cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0,0,255));//创建一个3通道矩阵,每个像素都是0,0,255
cv::Mat b(2, 2, CV_8UC2, cv::Scalar(0,255));//创建一个2通道矩阵,每个像素都是0,255
cv::Mat c(2, 2, CV_8UC1, cv::Scalar(255)); //创建一个单通道矩阵,每个像素都是255

2)枚举赋值法
这种赋值方式是将矩阵中所有的元素都一一枚举出,并用数据流的形式赋值给Mat类。
cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
cv::Mat b = (cv::Mat_<double>(2, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2);
上面第一行代码创建了一个3×3的矩阵,矩阵中存放的是从1-9的九个整数,先将矩阵中的第一行存满,之后再存入第二行、第三行,即123存放在矩阵a的第一行,456存放在矩阵a的第二行,789存放在矩阵a的第三行。
第二行代码创建了一个2×3的矩阵,其存放方式与矩阵a相同。采用枚举法时,输入的数据个数一定要与矩阵元素个数相同,例如代码第一行代码(a=)只输入从18八个数,赋值过程会出现报错,因此本方法常用在矩阵数据比较少的情况。

3)循环赋值
与通过枚举法赋值方法相类似,循环法赋值也是对矩阵中的每一位元素进行赋值,但是可以不在声明变量的时候进行赋值,而且可以对矩阵中的任意部分进行赋值。
cv::Mat c = cv::Mat_<int>(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;
	}
}
上面代码同样创建了一个3×3的矩阵,通过for循环的方式,对矩阵中的每一位元素进行赋值。需要注意的是,在给矩阵每个元素进行赋值的时候,赋值函数中声明的变量类型要与矩阵定义时的变量类型相同,即上面代码中第1行和第6行中变量类型要相同,如果第6行代码改成c.at<double>(i, j) ,程序就会报错,无法赋值。

4)类方法赋值
在Mat类里提供了可以快速赋值的方法,可以初始化指定的矩阵。例如生成单位矩阵、对角矩阵、所有元素都为0或者1的矩阵等。
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)(3,3)等主对角位置处为1diag():构建对角矩阵,其参数必须是Mat类型的1维变量,用来存放对角元素的数值。
ones():构建一个全为1的矩阵,参数含义与eye()相同。
zeros():构建一个全为0的矩阵,参数含义与eye()相同。

5)利用数组进行赋值
这种方法与枚举法相类似,但是该方法可以根据需求改变Mat类矩阵的通道数,可以看作枚举法的拓展。
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);
这种赋值方式首先将需要存入到Mat类中的变量存入到一个数组中,之后通过设置Mat类矩阵的尺寸和通道数将数组变量拆分成矩阵,这种拆分方式可以自由定义矩阵的通道数,当矩阵中的元素数目大于数组中的数据时,将用-1.0737418e+08填充赋值给矩阵,如果矩阵中元素的数目小于数组中的数据时,将矩阵赋值完成后,数组中剩余数据将不再赋值。由数组赋值给矩阵的过程是首先将矩阵中第一个元素的所有通道依次赋值,之后再赋值下一个元素,为了更好的体会这个过程,我们将定义的b和c矩阵在图2-4(下面已放出图片)中给出。

  • 图2-4(图片来自网络)
    图2-4
  • 图2-5 Mat类继承关系
    cv::Mat A = Mat_<double>(3,3);//创建一个3*3的矩阵用于存放double类型数据(下图来自网络)
    在这里插入图片描述

参考代码

qRegisterMetaType<cv::Mat>("cv::Mat"); //信号槽传递的参数,QT默认不支持cv::Mat。采用此种方式注册。<cv::Mat>是类型,"cv::Mat"是名字

	//类型创建
	Mat mat(3000, 4000, CV_8UC3);
	//mat.create(3000, 4000, CV_8UC3);
	//元素大小字节数
	int es = mat.elemSize();
	int size = mat.rows*mat.cols*es;
	
	//地址遍历连续的Mat   默认存储方式: BGR
	for (int i = 0; i < size; i += es)
	{
		mat.data[i] = 255;	//B
		mat.data[i + 1] = 100;//G
		mat.data[i + 2] = 100;//R
	}
	//地址遍历不一定连续的Mat
	for (int row = 0; row < mat.rows; row++)
	{
		for (int col = 0; col < mat.cols; col++)
		{
			(&mat.data[row*mat.step])[col*es] = 0;//B   ,row:行 mat.step:行之间的间隔  &mat.data[row*mat.step]得到对应行的第一个元素的地址。
			(&mat.data[row*mat.step])[col*es+1] = 0;//G
			(&mat.data[row*mat.step])[col*es+2] = 255;//R
		}
	}
	
	//使用ptr遍历Mat  用指针方式
	for (int row = 0; row < mat.rows; row++)
	{
		for (int col = 0; col < mat.cols; col++)
		{
			Vec3b *c = mat.ptr<Vec3b>(row, col);  //返回的指针,不需要对元素进行拷贝。
			c->val[0] = 0;		//B
			c->val[1] = 255;	//G
			c->val[2] = 0;		//R
			
		}
	}
	PrintMs("mat.ptr ");
	try
	{
		//使用at来遍历  用引用方式
		for (int row = 0; row < mat.rows; row++)
		{
			for (int col = 0; col < mat.cols; col++)
			{
				Vec3b &m = mat.at<Vec3b>(row, col);  //函数返回是一个引用。要定义一个引用变量(引用开销小)!如果定义一个普通变量,则会做拷贝。
				m[0] = 100;
				m[1] = 100;
				m[2] = 100;
				//也可以这样,性能更高:
				//mat.at<Vec3b>(row, col)[0] = 100;
				//mat.at<Vec3b>(row, col)[1] = 100;
				//mat.at<Vec3b>(row, col)[1] = 100;
			}
		}
		PrintMs("mat.at");
	}
	catch (Exception &ex)   //捕获异常
	{
		cout << ex.what() << endl;
	}
	
	//通过迭代器来遍历。可以不用管mat的行、列
	PrintMs("");
	auto it = mat.begin<Vec3b>();
	auto it_end = mat.end<Vec3b>();
	for (; it != it_end; it++)
	{
		(*it).val[0] = 0;	//B  (*it)取值
		(*it).val[1] = 0;	//G
		(*it).val[2] = 255;	//R
	}
	PrintMs("mat.itr");



>测试2 - CVMAT的像素数据读取方法
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
 
using namespace std;
using namespace cv;
 
 
int main(int argc, const char * argv[]) {
    // insert code here...
    float vals[] = {0.866025,-0.500000,0.500000,0.866025};
    CvMat rotmat;
    cvInitMatHeader(&rotmat, 2, 2, CV_32FC1,vals);  //直接赋值操作
    cout<<"rotmat->rows = "<<rotmat.rows<<endl;
    cout<<"rotmat->clos = "<<rotmat.cols<<endl;
    cout<<"rotmat->step = "<<rotmat.step<<endl;  //表示行数据长度(单位是字节数)
    
    for(unsigned int i = 0;i<rotmat.rows;i++)
    {
        for(unsigned int j = 0;j<rotmat.cols;j++)
        {
            cout<<i<<","<<j<<"->"<<*(rotmat.data.fl + i*rotmat.rows + j)<<endl;  //普通提取
            cout<<i<<","<<j<<"->"<<*((float *)(rotmat.data.ptr + i * rotmat.step) + j)<<endl;  //取uchar进行强转 偏移
        }
    }
    return 1;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值