当前位置:网站首页>opencv语法Mat类型总结
opencv语法Mat类型总结
2022-08-01 17:33:00 【小小工程员】
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,后面确定行、列等参数后使用creat | mat.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的九个整数,先将矩阵中的第一行存满,之后再存入第二行、第三行,即1、2、3存放在矩阵a的第一行,4、5、6存放在矩阵a的第二行,7,8,9存放在矩阵a的第三行。
第二行代码创建了一个2×3的矩阵,其存放方式与矩阵a相同。采用枚举法时,输入的数据个数一定要与矩阵元素个数相同,例如代码第一行代码(a=)只输入从1到8八个数,赋值过程会出现报错,因此本方法常用在矩阵数据比较少的情况。
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)等主对角位置处为1。
diag():构建对角矩阵,其参数必须是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-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;
}
边栏推荐
- How can become a good architect necessary skills: painting for all the people praise the system architecture diagram?What is the secret?Quick to open this article and have a look!.
- [ACNOI2022]物品
- 素域和扩域
- 参观首钢园
- MySQL locking case analysis
- sql添加索引
- ROS2系列知识(6):Action服务概念
- Live tonight!
- 金仓数据库KingbaseES安全指南--6.5. LDAP身份验证
- DevExpress的GridControl帮助类
猜你喜欢
下载 | 谷歌科学家Kevin P. Murphy发布新书《概率机器学习:高级主题》
数字化采购管理系统开发:精细化采购业务流程管理,赋能企业实现“阳光采购”
The site is not found after the website is filed. You have not bound this domain name or IP to the corresponding site! The configuration file does not take effect!
Complete knapsack problem to find the number of combinations and permutations
Description of common operations and help projects about DevExpress in C#
频域分析实践介绍
浅谈游戏音效测试点
广汽埃安“弹匣电池”,四大核心技术,出行安全保障
成为优秀架构师必备技能:怎样才能画出让所有人赞不绝口的系统架构图?秘诀是什么?快来打开这篇文章看看吧!...
Daily Yuxian Big Defeat
随机推荐
GridControl helper class for DevExpress
Winform message prompt box helper class
Flask框架实战
Topology零部件拆解3D可视化解决方案
MySQL 慢查询
个人日记
hcip第九天
Ali's official Redis development specification
DataTable Helper Class for C#
gtk显示4通道rgba图像
星途一直缺颠覆性产品?青岛工厂这款M38T,会是个突破点?
成为优秀架构师必备技能:怎样才能画出让所有人赞不绝口的系统架构图?秘诀是什么?快来打开这篇文章看看吧!...
TCP million concurrent server optimization parameters
面经汇总-社招-6年
使用设备树时对应的驱动编程
golang json 返回空值
史上最全的Redis基础+进阶项目实战总结笔记
2022年SQL大厂高频实战面试题(详细解析)
GRUB2的零日漏洞补丁现已推出
LeaRun.net快速开发动态表单