第二节 Mat数据类型详解

1、Mat数据类型描述

我们有多种从现实世界中获取数字图像的方法:数码相机,扫描仪,计算机断层扫描和磁共振成像等等。 在每种情况下,我们(人类)看到的都是图像。 但是,当将其转换为数字设备时,我们记录的是图像每个点的数值。如下图所示:

例如,在上图中,您可以看到汽车的镜子不过是一个包含所有像素点强度值的矩阵。OpenCV中的Mat是一个N维稠密数组,或多通道数组。它可用于存储实值或复值矢量和矩阵,灰度或彩色图像,体素体积,矢量场,点云,张量,直方图(尽管非常高维的直方图可能更好地存储在SparseMat中)。数组M的数据布局由数组M.step []定义,以便元素$(i_0,\cdots,i_{M.dims-1}) $的地址,其中 0 ≤ i k < M . s i z e [ k ] 0\leq i_k<M.size[k] 0≤ik​<M.size[k], 计算为:

a d d r ( M i 0 , ⋯ , i M . d i m s − 1 ) = M . d a t a + M . s t e p [ 0 ] ∗ i 0 + M . s t e p [ 1 ] ∗ i 1 + ⋯ + M . s t e p [ M . d i m s − 1 ] ∗ i M . d i m s − 1 addr(M_{i_0,\cdots,i_{M.dims-1}}) = M.data + M.step[0]*i_0 + M.step[1]*i_1 + \cdots + M.step[M.dims-1]*i_{M.dims-1} addr(Mi0​,⋯,iM.dims−1​​)=M.data+M.step[0]∗i0​+M.step[1]∗i1​+⋯+M.step[M.dims−1]∗iM.dims−1​

在二维数组的情况下,上述公式可简化为:

a d d r ( M i , j ) = M . d a t a + M . s t e p [ 0 ] ∗ i + M . s t e p [ 1 ] ∗ j addr(M_{i,j}) = M.data + M.step[0]*i + M.step[1]*j addr(Mi,j​)=M.data+M.step[0]∗i+M.step[1]∗j

注意, M . s t e p [ i ] ≥ M . s t e p [ i + 1 ] M.step [i]\ge M.step [i + 1] M.step[i]≥M.step[i+1](实际上, M . s t e p [ i ] ≥ M . s t e p [ i + 1 ] ∗ M . s i z e [ i + 1 ] M.step [i]\ge M.step [i + 1] * M.size [i + 1] M.step[i]≥M.step[i+1]∗M.size[i+1])。 这意味着二维矩阵逐行存储,三维矩阵逐平面存储,依此类推。 M.step [M.dims-1]是最小的,并且始终等于元素大小M.elemSize()。

因此,Mat中的数据布局与标准工具包和SDK中的大多数密集数组类型兼容,例如Numpy(ndarray),Win32(独立设备位图)以及其他,也就是说,与使用步骤( 或跨步)以计算像素的位置。 由于这种兼容性,可以为用户分配的数据制作Mat头,并使用OpenCV函数就地对其进行处理。

2、Mat的基本操作

2.1 、Mat创建


OpenCV提供很多创建Mat的方法,常用的几种方法如下:

 // 1、创建一个3x3矩阵,float类型,单通道矩阵cv::Mat m1(3,3,CV_32FC1);for(int y = 0;y < 3;y++){for (int x = 0;x < 3;x++){m1.at<float>(y,x) = (y+1) * (x+1);}}std::cout <<"m1 = \n"<< m1 << std::endl;// 2、重新创建矩阵,将m1的旧值删除,如果新矩阵比旧矩阵空间大,则重新分配空间m1.create(5,5,CV_32FC1);std::cout << "new m1 = \n" << m1 << std::endl;// 3、重新赋值m1.setTo(cv::Scalar(255.0));std::cout << "new m1 value = " << m1 << std::endl;// 4、创建一个3x3矩阵,float类型,3通道cv::Mat m2(3,3,CV_32FC3,cv::Scalar(255));std::cout << "m2 = \n" << format(m2, cv::Formatter::FMT_PYTHON) << std::endl;// 5、创建一个3x3矩阵,float类型,1通道cv::Mat m3(cv::Size(3,3),CV_32FC1);m3.setTo(cv::Scalar(255.0));std::cout << "m3 = \n" << m3 << std::endl;// 6、创建一个3x3矩阵,float类型,1通道,数组赋值float data[] = {1,2,3,4,5,6,7,8,9};cv::Mat m4(cv::Size(3,3),CV_32FC1,data);std::cout << "m4 = \n" << m4 << std::endl;// 7、创建一个3x3全零矩阵,1通道cv::Mat m5 = cv::Mat::zeros(cv::Size(3,3),CV_32FC1);std::cout << "m5 = \n" << m5 << std::endl;// 8、创建一个3x3的全1矩阵,1通道cv::Mat m6 = cv::Mat::ones(3,3,CV_32FC1);std::cout << "m6 = \n" << m6 << std::endl;// 9、创建一个3x3的单位矩阵,1通道cv::Mat m7 = cv::Mat::eye(3,3,CV_32FC1);std::cout << "m7 = \n" << m7 << std::endl;// 10、使用逗号分隔的初始化程序创建矩阵cv::Mat m8 = (cv::Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);std::cout << "m8 = \n" << m8 << std::endl;

2.2、Mat元素访问


1)通过at<>访问,单通道矩阵

// 1、元素访问,单通道
std::cout << "m4.at<float>(1,1) = " << m4.at<float>(1,1) << std::endl;

2)通过at<>访问,多通道矩阵

// 2、元素访问,多通道
int counts = 1;
cv::Mat m9(3,3,CV_32FC3);
for(int i = 0;i < 3;i++){for(int j = 0;j < 3;j++){for(int k = 0;k < 3;k++){m9.at<cv::Vec3f>(i,j)[k] = counts;counts++;}}
}
std::cout << "m9 = \n" << format(m9, cv::Formatter::FMT_PYTHON) << std::endl;
std::cout << "m9.at<cv::Vec3f>(1,1) = " << m9.at<cv::Vec3f>(1,1) << std::endl;

3)通过指针方式访问

// 3、通过指针方式访问
for(int y = 0;y < 3;y++){float * dataptr = m9.ptr<float>(y);for(int x = 0;x < 3;x++){float * curdata = dataptr + x * 3;curdata[0] = curdata[0] * curdata[0];curdata[1] = curdata[1] * curdata[1];curdata[2] = curdata[2] * curdata[2];}}
std::cout << "m9 = " << format(m9, cv::Formatter::FMT_PYTHON) << std::endl;

4)通过迭代器访问

// 4、通过迭代器访问
// 常量迭代器
cv::MatConstIterator_<float> it = m9.begin<float>();
cv::MatConstIterator_<float> itend = m9.end<float>();
float sum = 0.0;
for(;it != itend;++it){sum += *it;
}
std::cout << "sum of m9 = " << sum << std::endl;// 改变矩阵元素的值
cv::Mat_<cv::Vec3f>::iterator it1 = m9.begin<cv::Vec3f>();
cv::Mat_<cv::Vec3f>::iterator it1end = m9.end<cv::Vec3f>();
for(;it1 != it1end;++it1){(*it1)[0] = (float) sqrt((*it1)[0]);(*it1)[1] = (float) sqrt((*it1)[1]);(*it1)[2] = (float) sqrt((*it1)[2]);
}
std::cout << "\nm9 = " << format(m9, cv::Formatter::FMT_PYTHON) << std::endl;

5)块方式访问

// 5、通过块访问
// 提取整行数据
cv::Mat rowdata = m9.rowRange(0,1);
std::cout << "row = \n" << format(rowdata, cv::Formatter::FMT_PYTHON) << std::endl;
rowdata = m9.row(0);
std::cout << "row = \n" << format(rowdata, cv::Formatter::FMT_PYTHON) << std::endl;//提取整列数据
cv::Mat coldata = m9.colRange(0,1);
std::cout << "col = \n" << format(coldata, cv::Formatter::FMT_PYTHON) << std::endl;
coldata = m9.col(2);
std::cout << "col = \n" << format(coldata, cv::Formatter::FMT_PYTHON) << std::endl;

2.3、Mat运算

1)加法运算

// 1、加法运算
cv::Mat m10 = m3 + m4;
std::cout << "m3 + m4 = \n" << m10 << std::endl;
m10 = m3 + 10;
std::cout << "m3 + 10 = \n" << m10 << std::endl;

2)减法运算

// 2、减法运算
cv::Mat m11 = m3 - m4;
std::cout << "m3 - m4 = \n" << m11 << std::endl;
m11 = m3 - 100;
std::cout << "m3 - 100  = \n" << m11 << std::endl;

3)乘法运算

// 3、乘法运算
// 与常量相乘
cv::Mat m12 = m3 * 0.5;
std::cout << "m3 * 0.5 = " << m12 << std::endl;
// 按矩阵乘法运算
cv::Mat m13 = m3 * m4;
std::cout << "m3 * m4 = " << m13 << std::endl;
// 按逐个元素相乘
cv::Mat m14 = m3.mul(m4);
std::cout << "m3.mul(m4) = " << m14 << std::endl;

4)除法运算

// 4、除法运算
// 与常量相除
cv::Mat m15 = m3 / 0.5;
std::cout << "m15 = " << m15 << std::endl;
// 按元素相除
cv::Mat m16 = m3 / m4;
std::cout << "m16 = " << m16 << std::endl;

6)矩阵求逆

// 矩阵求逆
cv::Mat m17 = m4.inv();
std::cout << "m17 = " << m17 << std::endl;

7)矩阵转置

// 矩阵转置
cv::Mat m22 = m3.t();
std::cout << "m22 = " << m22 << std::endl;

8)矩阵逻辑运算

// 矩阵逻辑运算
// 按元素与运算
cv::Mat m18 = m3 & m4;
std::cout << "m18 = " << m18 << std::endl;
// 与常量进行与运算
m18 = m3 & 0xF0;
std::cout << "m18 = " << m18 << std::endl;// 按元素或运算
cv::Mat m19 = m3 | m4;
std::cout << "m19 = " << m19 << std::endl;
// 与常量进行或运算
m19 = m3 | 0xFA;
std::cout << "m19 = " << m19 << std::endl;// 按元素异或运算
cv::Mat m20 = m3 ^ m4;
std::cout << "m20 = " << m20 << std::endl;
// 与常量进行异或运算
m20 = m3 ^ 0xFF;
std::cout << "m20 = " << m20 << std::endl;// 非运算
cv::Mat m21 = ~m3;
std::cout << "m21 = " << m21 << std::endl;

9)矩阵比较

// 矩阵比较,按元素比较
cv::Mat m23 = m3 >= m4;
std::cout << "m3 >= m4?\n" << m23 << std::endl;

10)矩阵最大、最小值提取

// 矩阵最大值、最小值提取
// 矩阵间最大、最小值提取
cv::Mat m24 = cv::max(m3,m4);
std::cout << "m24 = " << m24 << std::endl;
cv::Mat m25 = cv::min(m3,m4);
std::cout << "m25 = " << m25 << std::endl;
// 矩阵与常量最大、最小值提取
cv::Mat m26 = cv::min(m3,0.5);
std::cout << "m26 = " << m26 << std::endl;
cv::Mat m27 = cv::max(m3,1.2);
std::cout << "m27 = " << m27 << std::endl;

2.4 其他函数


Opencv的Mat类型还提供了其他很多函数,方便使用。

m1 = m0.clone():完全复制所有数据元素
m0.copyTo(m1):将m0复制给m1;如果需要给m1分配空间,则与clone方法效果一样
m0.converTo(m1,type,scale,offset):转换矩阵数据类型并进行尺度变换和增加偏置之后赋值给m1
m0.assignTo(m1,type):只在内部使用,被convertTo函数调用
m0.setTo(s,mask):将矩阵所有元素设置为s,如果mask存在,则只对mask区域进行操作
m0.reshape(chan,rows):改变矩阵形状
m0.push_back(s):在末尾增加一个mx1大小数组
m0.push_back(m1):向mxn大小矩阵增加k行并复制到m1中,m1大小必须为kxn
m0.pop_back(n):向mxm大小矩阵移除n行(默认是1)
m0.locateROI(size,offset):将m0矩阵全尺寸写入cv::Size变量size,如果m0矩阵只是一个大矩阵的一小块区域,还会写入一个Point类型的offset
m0.adjustROI(t,b,l,r),通过上、下、左、右调整ROI范围
m0.total():计算数组序列的元素的数目(不包括通道)
m0.isContinous():如果m0没有空隙,则返回true
m0.elemSize():返回矩阵元素的位长度(比如三通道浮点矩阵将返回12)
m0.elemSize1():返回矩阵基本元素的位长度(比如三通道浮点矩阵将返回4)
m0.type():返回矩阵元素的类型(如CV_32FC3)
m0.depth():返回矩阵中的元素类型(比如CV_32F)
m0.size():返回矩阵m0的大小
m0.empty():如果矩阵没有元素,则返回true,否则,返回false。

3、SparseMat数据类型描述及操作


在OpenCV中,SparseMat类表示多维稀疏数值数组。稀疏数组可以存储Mat可以存储的任何类型的元素。 稀疏意味着仅存储非零元素(尽管由于对稀疏矩阵进行操作,其存储的元素中的某些元素实际上可以变为0。您可以检测这些元素并使用SparseMat :: erase删除它们 )。 非零元素存储在哈希表中,该表在填充时会增长,因此搜索时间平均为O(1)(无论元素是否存在)。

1)创建SparseMat

// 创建SparseMat
const int dims = 5;
int size[5] = {10, 10, 10, 10, 10};
cv::SparseMat sparse_mat(dims, size, CV_32F);

2)SparseMat数据访问

SparseMat的数据访问有四种方式:cv::SparseMat::ptr()、cv::SparseMat::ref()、cv::SparseMat::value()、cv::SparseMat::find()

ptr方式访问示例如下:

// 创建SparseMat
const int dims = 5;
int size[5] = {10, 10, 10, 10, 10};
cv::SparseMat sparse_mat(dims, size, CV_32F);
for(int i = 0; i < 1000; i++)
{int idx[dims];for(int k = 0; k < dims; k++)idx[k] = rand() % size[k];// 赋值给SparseMatsparse_mat.ref<float>(idx) += 1.f;
}
cout << "nnz = " << sparse_mat.nzcount() << endl;

SparseMat还有很多函数,在这里就不做一一说明,感兴趣的同学可以参考文档。

OpenCV 4.x API 详解与C++实例-Mat数据类型详解相关推荐

  1. OpenCV Mat类详解和用法

    OpenCV Mat类详解和用法 我们有多种方法可以获得从现实世界的数字图像:数码相机.扫描仪.计算机体层摄影或磁共振成像就是其中的几种.在每种情况下我们(人类)看到了什么是图像.但是,转换图像到我们 ...

  2. OpenCV参考手册之Mat类详解(三)

    Mat::eye 返回一个恒等指定大小和类型矩阵. C++: static MatExpr Mat::eye(int rows, int cols, inttype) C++: static MatE ...

  3. OpenCV学习三:Mat类详解

    目标 我们有多种方法可以获得从现实世界的数字图像:数码相机.扫描仪.计算机体层摄影或磁共振成像就是其中的几种.在每种情况下我们(人类)看到了什么是图像.但是,转换图像到我们的数字设备时我们的记录是图像 ...

  4. python六大数据类型详解

    python 六大数据类型详解 文章目录 python 六大数据类型详解 数据类型简介 Number(数值) String(字符串) Python字符串的45个方法详解 一.大小写转换 01.capi ...

  5. Python数据类型详解03

    原文博客地址: Python数据类型详解03 第一篇Python数据类型详解01中主要介绍了Python中的一些常用的数据类型的基础知识 第二篇Python数据类型详解02文章中, 详细介绍了数字(N ...

  6. php为什么需要配置路由器,laravel 配置路由 api和web定义的路由的区别详解

    1.路由经过中间件方面不同 打开kerenl.php就可以看到区别 protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware ...

  7. OpenCV Mat类详解和用法(官网原文)

    参考文章:OpenCV Mat类详解和用法 我马克一下,日后更 官网原文链接:https://docs.opencv.org/3.2.0/d6/d6d/tutorial_mat_the_basic_i ...

  8. [python opencv 计算机视觉零基础到实战] 四、了解色彩空间及其详解

    一.学习目标 了解什么是色彩空间 了解opencv中色彩空间的转换 目录 [python opencv 计算机视觉零基础到实战] 一.opencv的helloworld [[python opencv ...

  9. OpenCV参考手册之Mat类详解

    OpenCV参考手册之Mat类详解(一) OpenCV参考手册之Mat类详解(二) OpenCV参考手册之Mat类详解(三)

最新文章

  1. CF498C Array and Operations(数论 + 最大流)
  2. 不要再代码里频繁的new和delete
  3. C++ Primer 5th笔记(chap 19 特殊工具与技术)typeid运算符
  4. mastercam2019安装教程
  5. spring cloud常用组件介绍
  6. 阿里云交通数据中台解决方案,打造“数字化生产力”
  7. 解决‘C:\Program‘ 不是内部或外部命令,也不是可运行的程序或批处理文件
  8. composer常用命令
  9. 数据挖掘:探索性数据分析(EDA)(补充)
  10. 如何为MindManager时间表思维导图添加春节假期?
  11. 如何在C#中生成与PHP一样的MD5 Hash Code
  12. java 字面值_Java基础之字面值
  13. ESXi社区版NVMe驱动更新v1.1
  14. C# WinForm调用Shell_NotifyIcon
  15. ld链接动态库静态库问题
  16. polkitd进程解释
  17. 软件安全实验——栈溢出漏洞利用
  18. .Net Standard 2.1对您意味着什么
  19. [转][darkbaby]任天堂传——失落的泰坦王朝(中)
  20. 山东大学项目实训——解决微信小程序无法获得用户信息问题

热门文章

  1. 全维状态观测器和降维状态观测器
  2. html中aside整体,html中aside标签的使用方法
  3. .net core Ocelot Consul 实现API网关 服务注册 服务发现 负载均衡
  4. 智利近海发生6.7级地震:暂无伤亡 不会引发海啸
  5. aire 计算机术语,法语计算机及网络词汇(5)
  6. [Mysql] 插入数据
  7. React Native TextInput 收起键盘
  8. 使用证书在WEB服务器上设置SSL(下)
  9. 2023 闲鱼二手数码项目
  10. EventBus 源码解析