OpenCV笔记(3) CV::Mat
1. 创建一个数组
1.1 使用构造函数
cv::Mat a; //默认构造函数
cv::Mat b = cv::Mat(); //默认构造函数
cv::Mat c = cv::Mat(3, 3, CV_8UC1); //指定类型的二维数组
cv::Mat d = cv::Mat(cv::Size(3, 3),CV_8UC1); //指定类型的二维数组
cv::Mat e = cv::Mat(cv::Size(3, 3), CV_32FC2, cv::Scalar(1, 2)); //指定初始化值
cv::Mat f = cv::Mat(cv::Size(3, 3), CV_8UC3, cv::Scalar(1, 2, 3)); //指定初始化值 cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << "d = " << d << endl;
cout << "e = " << e << endl;
cout << "f = " << f << endl;
下表列举了cv::Mat的基本构造函数。除了默认构造函数,它们主要分为如下几个类型:
- 要求输入行数和列数来构造二位数组的
- 使用cv::Size对象来构造二维数组
- 构造n维数组并且要求你通过一个整型的序列来确定每一位数据维度的
构造函数 | 说明 |
cv::Mat() | 默认构造函数 |
cv::Mat(int rows, int cols, int type); | 指定类型的二维数组 |
cv::Mat(cv::Size size, int type); | 指定类型的二维数组(大小由size指定) |
cv::Mat(int rows, int cols, int type, const Scalar& s); | 指定类型的二维数组,并指定初始化值 |
cv::Mat(cv::Size size, int type, const Scalar& s); | 指定类型的二维数组,并指定初始化值(大小由size指定) |
cv::Mat(int ndims, const int* sizes, int type); | 指定类型的多维数组 |
Mat(int ndims, const int* sizes, int type, const Scalar& s); | 指定类型的多维数组,并指定初始化值 |
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP) |
指定类型的多维数组,并指定预先存储的数据 |
1.2 从其他cv::Mat复制数据
方法主要有:
- 从一个数组创建另一个数组
- 从已存在的数组子区域创建数组
1)输入行列范围(只在二维矩阵的情况下有效)
2)使用cv::Rect来指定一个矩形的子区域(只在二维矩阵的情况下有效)
3)输入一个range数组 (range所指向的有效范围必须和mat的维度相等,如果你的mat时多维(>2)的数组,必须使用第三种)
- 从矩阵表达中生成新的矩阵
构造函数 | 说明 |
cv::Mat(const Mat& m) | 复制构造函数 |
cv::Mat(const Mat& m, const cv::Range& rows, const cv::Range& cols); |
只从指定的行列中复制数据的复制构造函数 |
cv::Mat(const Mat& m, const cv::Rect& roi); | 只从感兴趣区域中复制数据的复制构造函数 |
cv::Mat(const Mat& m, const cv::Range* ranges); | 服务于n维数组的,从泛化的ROI中复制数据的复制构造函数 |
cv::Mat(const cv::MatExpr& expr); | 从其他矩阵的线性代数表述中生成新矩阵的复制构造函数 |
示例:
cv::Mat R = (cv::Mat_<double>(3, 3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9); cv::Mat temp = R({ 0,0,2,2 });
1.3 通过create函数来创建数组
cv::Mat m;//数组没有大小和数据类型
m.create(4, 4, CV_8UC1);//再次使用成员函数create()来申请一个内存区域
cout << "m = " << m << endl;
1.4 通过opencv提供的类matlab的函数创建
cv::Mat Me = cv::Mat::eye(4, 4, CV_64F);
cout << "Me = " << Me << endl; cv::Mat Mo = cv::Mat::ones(4, 4, CV_64F);
cout << "Mo = " << Mo << endl; cv::Mat Mz = cv::Mat::zeros(4, 4, CV_64F);
cout << "Mz = " << Mz << endl;
1.5 数据自定义矩阵创建
cv::Mat m = (cv::Mat_<int>(3, 3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9);
2. 访问数组元素
2.1 访问一个元素
通过模板函数at<>()来实现。
这个函数有很多变体,对不同维度的数组有不同的参数要求。这个函数的工作方式是先将at<>()特化到矩阵所包含的数据类型,然后使用你想要的数据的行和列的位置访问该元素。
cv::Mat m1 = cv::Mat::eye(10, 10, CV_32FC1); //单通道数组
printf("Element(3,3) is %f\n", m1.at<float>(3, 3));
cv::Mat m2 = cv::Mat::eye(10, 10, CV_32FC2); //多通道数组
printf("Element(3,3) is (%f, %f)\n",m2.at<cv::Vec2f>(3, 3)[0], m2.at<cv::Vec2f>(3, 3)[1]);
输出结果为:
Element(3,3) is 1.000000
Element(3,3) is (1.000000, 0.000000)
通过模板函数ptr<>()来实现。
首先需要一个类型名来实例化。然后接受一个整型参数来指示希望指针指向的行,函数将返回一个和矩阵原始数据类型相同的数据指针(比如说,如果数组的类型是CV_32FC3,它将会返回一个float*)。因此给定一个类型为float三通道的矩阵mtx,结构体mtx.ptr<Vec3f>(3)将会返回mtx对应行指向第一个元素第一个(浮点)通道的指针,这通常是访问数组组块的一种方式。因为一旦你拥有指针,就可以向指定的位置写入数据。
cv::Mat m = cv::Mat::eye(4, 4, CV_32FC1);
cout << *m.ptr<float>(0) << endl; cv::Mat m1 = cv::Mat::eye(4, 4, CV_32FC2);
cout << *m1.ptr<cv::Vec2f>(0) << endl;
输出结果为
1
[1,0]
使用cv::Mat内嵌的迭代器机制
这种机制在某种程度上是基于STL容器所提供的机制。基础想法是OpenCV提供一对迭代器模板
- cv::MatConstIterator<> 用于只读(const)数组
- cv::MatIterator<> 用于非只读(ono-const)数组
cv::Mat的成员函数begin()和end()会返回这种类型的对象,因为迭代器具有足够的智能来处理连续的内存区域和非连续的内存区域,所以这种方法很方便。
所有迭代器都必须在数组建立的时候声明并且指定一个对象类型。下面有一个简单的使用迭代器来极端三通道三维数组中“最长”元素(一个三位向量域)的例子:
cv::Mat m;
m.create(cv::Size(3, 3), CV_32FC3);
cv::randu(m, -1.0f, 1.0f);
float max = 0.0f;
cv::MatConstIterator_<cv::Vec3f> it = m.begin<cv::Vec3f>();//书上的例子此处编译错误,使用了迭代器和begin模板类就ok了
//使用MatConstIterator类,编译没问题,输出会乱码
while (it != m.end<cv::Vec3f>()) {
float len2 = (*it)[0] * (*it)[0] + (*it)[1] * (*it)[1] + (*it)[2] * (*it)[2];
if (len2 > max) max = len2;
it++;
}
cout << max << endl;
2.2 通过面访问数组元素
还有另一种形式的迭代器,cv::NAryMatIterator。它只要求被迭代的数组有相同的几何结构(维度以及每个维度的范围)。该迭代器不会返回一个用于迭代的单独元素,而通过返回一堆数组进行N-ary迭代器操作,这些返回的数组也称为“面”(plane)。
2.3 通过块访问数组元素
你可能需要将一个数组的子集作为另一个数组访问。这个子集可能是一行或者一列,也可能是原始数组的一个子集。
最简单的方法是row()和col(),他将一个整型变量作为参数并返回这个变量所索引的行或者列。注意:表达式m2=m.row(3),这个表达式将创建一个新的数组头,并且分配它的data指针、step数组以及其他一写东西,这样它将可以访问m中的第三行数据。如果修改了m2中得到的数据,也会修改m中的数据,应使用真正的拷贝数据方法copyTo()。
还有rowRange()和colRange(),可以从多个连续的行(列)中提取数组。你可以1)指定开始和结束的行(列);2)传递指明想要的行(列)的cv::Range对象。在输入两个整数的情况下,数据范围会包括开始的索引但不包括结束的索引所指向的行(列)。在使用cv::Range的时候,也会受到类似的处理。
函数成员diag()返回的数组指向矩阵m的对角元素,调用m.diag()的时候,需要输入一个整型参数来说明哪一个对角需要被提取。如果参数为0,那么将会是主对角;如果是正数,相对于主对角向数组上部分偏移;如果是负的,反之。
最后一种提取子矩阵的方式是运算符()。通过这个运算符,你可以传递一对范围(指示行和列范围的cv::Range)或者一个cv::Rect来指定你想要的区域。
cv::Mat a = (cv::Mat_<int>(3, 3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9);
cout << a.diag() << endl; //输出[1;5;9]
cout << a.diag(1) << endl; //输出[2;6]
cv::Mat b = a.rowRange(0, 1);
cv::Mat c = a.colRange(0, 1);
cout << a(cv::Range(0, 1), cv::Range(1, 2)) << endl;//输出[2]
cout << a(cv::Rect(0,0,2,2)) << endl;//输出[1,2;4,5]
3. 矩阵运算
3.1 矩阵的加减乘除
操作m2=m1,m2只是对于m1的一个引用。相比而言,m2=m1+m0表达的意思则完全不一样。因为m1+m0是矩阵表达式,者会被计算并且将结果的指针指向m2。结果将处于一个新的内存区域中。
示例 | 说明 |
m0 + m1; m0 - m1; | |
m0 + s; m0 - s; s + m0; s - m0; | 矩阵和单个元素的加减 |
-m0; | |
s * m0; m0 * s; | |
m0.mul(m1); m0/m1; | 对应元素相乘/除 |
m0*m1 | 矩阵乘法 |
3.2 向量的点乘和叉乘
m0.dot(m1);
对于向量a和向量b:
a和b的点乘(内积)公式为:
m0.cross(m1);只适用于3x1矩阵
对于向量a和向量b:
a和b的点乘(外积)公式为:
其中:
3.3 其他
- 求逆 m0.inv(method)
- 求转置 m0.t()
- 取绝对值 cv::abs(m0)
- 按元素进行比较 m0>m1
- 逻辑操作 m0&m1
- min(m0,m1); max(m0,s);
4.成员函数
4.1 Clone()
在方法中创建的Mat,在作为返回值时要深拷贝一下再传出。
4.2 IsContinuous()
这里的continue的意思是在内存上continue,正常情况下,头一行的末尾在内存里和下一行的开头是相连的,但是有时候我们做了一些操作,选取了Mat 的一部分,例如选了一个ROI 这时候就不满足上面说的相连了。那么这时候continuous就被判定为假。
源自:《学习OpenCV3》第四章
OpenCV笔记(3) CV::Mat的更多相关文章
- OpenCV图片类cv::Mat和QImage之间进行转换(好多相关文章)
在使用Qt和OpenCV混合编程时,我们有时需要在两种图片类cv::Mat和QImage之间进行转换,下面的代码参考了网上这个帖子: //##### cv::Mat ---> QImage ## ...
- MatLab Load cv::Mat 导入数据
我们有时候在项目中需要将OpenCV中的cv::Mat导入MatLab进行分析与处理,那么如果把数据转过去呢,我们的做法是首先将cv::Mat导出为txt文件,或者是yml文件,请参见我之前的博客Wr ...
- OpenCV学习笔记(二) cv::Mat
部分内容转自:OpenCV Tuturial,ggicci 在OpenCV Tuturial中可查看Mat的初始化与打印方法. Mat本质上是由两个数据部分组成的类: 矩阵头(包含矩阵尺寸,存储方法, ...
- OpenCV 3.0 CvMat and cv::Mat Conversion
After OpenCV 3.0, CvMat cannot be directly converted to cv::Mat, we need to use function cvarrToMat( ...
- 图像储存容器Mat[OpenCV 笔记11]
IplImage 与 Mat IplImage是OpenCV1中的图像存储结构体,基于C接口创建.在退出之前必须release,否则就会造成内存泄露.在一些只能使用C语言的嵌入式系统中,不得不使用. ...
- OpenCV几种访问cv::Mat数据的方法
一般来说,如果是遍历数据的话用指针ptr比用at要快.特别是在debug版本下.因为debug中,OpenCV会对at中的坐标检查是否有溢出,这是非常耗时的. 代码如下 #include <op ...
- OpenCV 3.0中IplImage* 转cv::Mat
在OpenCV 2.0中使用: IplImage * ipl1, *ipl2; // ... const cv::Mat m = cv::Mat(ipl,false); cv::Mat m2 = ip ...
- 关于opencv中cv::Mat设置roi
opencv中设置roi实验: cv::Mat SrcImg; SrcImg = cv::imread("../resource/cpw3.png"); cv::imshow(&q ...
- Dlib笔记二:matrix或array2d与cv::Mat的互转
因为经常习惯的用OpenCV来做图像处理,所以难免希望将其他库的图像数据与OpenCV互转,所以今天就记录下这种互转的方法. 1.dlib::matrix/dlib::array2d转cv::Mat ...
- 【opencv】cv::Mat转std::vector<cv::Point2d> (注意两容器中数据类型的一致性)
获取cv::Mat大小: mymat.size() 获取cv::Mat指定位置的值:需指定数据类型,且注意数据类型应与存入时的数据类型一致,否则会导致不抛出异常的数据错误 mymat.at<,i ...
随机推荐
- DDD领域驱动设计总结和C#代码示例
DDD(领域驱动设计)是一种软件设计方法,它强调以业务领域为核心来驱动软件的设计和开发. DDD 的设计初衷是为了解决复杂业务领域的设计和开发问题,它提供了一套丰富的概念和模式,帮助开发者更好地理解和 ...
- Java 断言 Assert 使用教程与最佳实践
本文收录于 Github.com/niumoo/JavaNotes,Java 系列文档,数据结构与算法! 本文收录于网站:https://www.wdbyte.com/,我的公众号:程序猿阿朗 作为一 ...
- portainer安装,配置,使用
Portainer安装 Portainer是Docker容器管理可视化界面,主要是可以通过可视化界面创建,管理Dockert容器,并且支持多个节点管理(免费版支持五个节点). Portainer官网地 ...
- 日处理数据量超10亿:友信金服基于Flink构建实时用户画像系统的实践
导读:当今生活节奏日益加快,企业面对不断增加的海量信息,其信息筛选和处理效率低下的困扰与日俱增.由于用户营销不够细化,企业 App 中许多不合时宜或不合偏好的消息推送很大程度上影响了用户体验,甚至引发 ...
- 重磅发布 | Serverless 应用中心:Serverless 应用全生命周期管理平台
简介:Serverless 应用中心,是阿里云 Serverless 应用全生命周期管理平台.通过 Serverless 应用中心,用户在部署应用之前无需进行额外的克隆.构建.打包和发布操作,即可快 ...
- 利器解读!Linux 内核调测中最最让开发者头疼的 bug 有解了|龙蜥技术
简介:通过在Anolis 5.10 内核中增强 kfence 的功能,实现了一个线上的.精准的.可定制的内存调试解决方案. 编者按:一直持续存在内核内存调测领域两大行业难题: "内存被改& ...
- 淘宝推荐、视频搜索背后的检索技术竟是它!深度揭秘达摩院向量检索引擎Proxima
简介: 淘宝搜索推荐.视频搜索的背后使用了什么样的检索技术?非结构化数据检索,向量检索,以及多模态检索,它们到底解决了什么问题?今天由阿里巴巴达摩院的科学家从业务问题出发,抽丝剥茧,深度揭秘达摩院内部 ...
- 使用MQTT与函数计算做热力图的实践
简介: 在各类场景中,关于上报数据的处理无处不在,而以上提到的场景都可以通过本方案的MQTT+FC+API Gateway的方式参考优化来实现. 前言 最近几年,我们在一些商场.图书馆.机场或港口环境 ...
- 如何通过 Serverless 提高 Java 微服务治理效率?
简介: 在业务初期,因人手有限,想要快速开发并上线产品,很多团队使用单体的架构来开发.但是随着公司的发展,会不断往系统里面添加新的业务功能,系统越来越庞大,需求不断增加,越来越多的人也会加入到开发团队 ...
- 使用 Arthas 排查开源 Excel 组件问题
简介: 有了实际的使用之后,不免会想到,Arthas 是如何做到在程序运行时,动态监测我们的代码的呢?带着这样的问题,我们一起来看下 Java Agent 技术实现原理. 背景介绍 项目中有使 ...