opencv-Mat-深度探究

Mat–深度解析

Mat作为图像处理操作的基础对象,不掀开它的神秘面纱,实在无法愉快的看源码,所以在此做一个学习总结~

先贴出opencv的官方文档地址,这里比较详细、概括性的介绍了opencv相关的结构和知识,其中包括了Mat结构,对于初学者,我感觉很值得一看~

想了解一个对象,就要拿出面试官八卦你的精神:

  1. 你叫什么?-----------------(Mat)
  2. 你是干什么的?-----------(Mat的功用)
  3. 你有什么优势?-----------(Mat比Iplimage改进的地方)
  4. 你能接手哪些业务面?–(Mat的作用对象)
  5. 你家里有几口人?--------(Mat的成员变量)
  6. 分别都有谁?--------------(Mat的成员变量的作用)
  7. 你会干什么?--------------(Mat的成员函数)
  8. 你是怎么干的?-----------(Mat的成员函数实现过程)

这一套逼问结束,估计该了解的也都了解了。。

那就开启这一次了解之旅吧~

1.Mat 是什么,干什么用的?

根据源码对Mat的解释:Mat是一个 n-dimensional dense numerical sigle-channel or multi-channel array,就是说Mat是一个n维多通道或单通道的数组。
具体是做什么用呢?看图说话:
在这里插入图片描述
在计算机的世界,图像经过取样、离散化处理后,最终只剩下描述图像的像素值的数字矩阵和其他描述信息,为了将这些信息存储下来便于后续处理,opencv中提供了Mat帮助我们充当数据的载体。

2.Mat作用的对象有哪些?

Mat只可以存图像吗? 不看不知道,一看吓一跳,Mat可以存的对象有很多:(complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fileds, point clouds, tensors,histograms) 其实就是(复数向量和矩阵、灰度或彩色图像、体素矢量场张量和直方图)
我想,Mat的产生,除了为搞图像的人提供载体,也为搞数学、物理的人提供了载体,不过,某有关系,我们了解行业相关就好了。。

3.Mat 有什么优势?

1.不需要手动分配和释放内存了,因为类的概念,提供了内存管理,相比IplImage,Mat可以更好的调节内存管理。
2.Mat包含了两部分数据:矩阵头 和 指向像素矩阵的值。提供了浅拷贝和深拷贝,引入了引用计数,方便数据管理并提高了管理效率。

4.Mat 存储方法

我们以灰度和彩色图像为例讲述其内存存储顺序,opencv官方文档中,给出了如下描述:
在这里插入图片描述
从图中,我们可以看到像素的排列顺序,但是在内存中可能Row1直接就接在了Row0后,也许整个图像存储完,是在一块连续内存上,因此Mat给出了一个cv::Mat::isContinuous()函数来询问矩阵是否在内存上顺序存储。

对于不同格式的图像存储,其主要依赖于我们选择的颜色空间和数据类型,在opencv中,常见的颜色系统包括:

  • RGB:彩色空间
  • HSV\HLS:色调、饱和度、强度
  • YUV\YCrCb:亮度、明度、饱和度
  • Lab:感知统一的颜色空间

所以具体的存储形式和颜色空间的排列会统一起来,具体opencv中其他色彩空间的排列方式,可自行探测哦~

5.Mat有多少成员变量

每个成员变量就像家庭成员,都有自己的职责和分工,了解它们的功用会帮我们在编写算法时提供便利。。

成员变量变量含义 及 作用
flags
dims矩阵维度(>=2)
rows/cols矩阵行数和列数,在图像中为图像的高和宽
data指向矩阵数据的起始的位置,不是指向矩阵头
size一个用来存放矩阵各维度数据的参数
step一个用来快速定位元素地址的元素
u和UMat相互影响的变量
datastart指向ROI区域起始点的指针
dataend指向ROI区域终点的指针
datalimit包含了ROI区域的全局区域的终点指针

因为受到网友darkragon-001的启发,画图来表示一下各个变量的含义,化抽象为具象哈~
在这里插入图片描述
看图的时候,有1个个注意点:
1.将整幅画面看为三维的矩阵,是用来对比二维矩阵下step的不同。其他时候只需要看最上层的二维矩阵即可并认为Mat是一个二维矩阵即可。
知识点:

  • 1.step,如果Mat为三维矩阵,那么step[0]为MNelemSize,即MN这一面所占内存大小,如果Mat为二维图像(以浅灰色那一面为例),step[0]为MelemSize,,即一行数据所占内存大小,相应的step[1]为一个数据所占内存大小,这块可以去了解我的另外一篇博文,有详细讲~

  • 2.size,size作为一个opencv自定义的结构体,也是用来帮助定位矩阵的尺寸分布,可以用size[i]调用的方法查找矩阵第i维的维度,如果Mat是二维的,也可以用size()->width,size()->height的方法去查找矩阵的长和宽。

  • 3.绿色区域为ROI区域,即存在某个图像M1为绿色部分且为M2的一部分,当然当M1==M2时,就不是一个ROI图像了。这种ROI的设置,对于实现图像算法,比如滤波,卷积时边缘处理,非常的方便。

6.Mat有多少成员变量,都有什么作用?

为了方便阅读,同样以表格的形式表现出来~

成员函数函数作用 及 功能
1.构造函数构造一个Mat对象
Mat()
Mat(int rows, int cols, int type)
Mat(int _rows, int _cols, int _type, const Scalar& _s)
Mat(Size size, int type)
Mat(int rows, int cols, int type, const Scalar& s)
Mat(Size size, int type, const Scalar& s以上构造2维矩阵
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
Mat(Size size, int type, void* data, size_t step=AUTO_STEP)根据data构造2维矩阵
Mat(int ndims, const int* sizes, int type)
Mat(const std::vector& sizes, int type)
Mat(int ndims, const int* sizes, int type, const Scalar& s)
Mat(const std::vector& sizes, int type, const Scalar& s以上构造n维矩阵
Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)
Mat(const std::vector& sizes, int type, void* data, const size_t* steps=0)根据data构造n维矩阵
Mat(const Mat& m)根据m构造
Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all())根据m和行列的范围构造
Mat(const Mat& m, const Rect& roi)根据m和roi进行构造
Mat(const Mat& m, const std::vector& ranges)
Mat(const Mat& m, const std::vector& ranges)以上根据m构造出来的图像被修改时也会改变m中的值,这种关联关系需谨慎对待
2.运算符操作Mat之间+=-这类符号操作
Mat& operator = (const Mat& m)与m共用一套data
Mat& operator = (const MatExpr& expr)自己可以是已存在的,内存也会被管理
UMat getUMat(int accessFlags, UMatUsageFlags usageFlags = USAGE_DEFAULT) const从Mat中追溯UMat
3.行、列操作得到Mat中成员变量或信息的函数
Mat::row(int y) const返回第y行的矩阵,data共用,创建新的函数头
Mat::col(int x) const返回第x列的矩阵,data共用,创建新的函数头
Mat::rowRange(int startrow, int endrow) const返回startrow-endrow全部行矩阵
Mat::rowRange(const Range& r) const返回r范围内全部行的矩阵
Mat::colRange(int startcol, int endcol) const返回startcol-endcol全部列的矩阵
Mat::colRange(const Range& r) const返回r范围内的全部列的矩阵
因为取行、列都属于内存不变,所以进行列之间拷贝时,需要copyto操作
4.提取对角线矩阵对矩阵取反z型对角线
Mat diag(int d=0) const.diag(0)取对角线上的矩阵
static Mat diag(const Mat& d)根据主对角线矩阵创建正方形矩阵
5.克隆函数
Mat clone() const CV_NODISCARD从矩阵头到数据全部进行拷贝
void copyTo( OutputArray m ) const拷贝给已有矩阵m,若m尺寸类型不合适,会重新分配内存
void copyTo( OutputArray m, InputArray mask ) constmask与this尺寸相同,mask中非0元素可进行拷贝
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const将this转换类型输出给m,m为单独的Mat
void assignTo( Mat& m, int type=-1 ) const内部封装了convertTo
Mat& operator = (const Scalar& s)将数组中元素转换为s的值,类型不变
Mat& setTo(InputArray value, InputArray mask=noArray())更高级的变量,能根据mask转换数组中的value
6.改变2维矩阵shape或通道数,在不复制data的前提下不增不减元素个数,不拷贝数据
Mat reshape(int cn, int rows=0) const重新排列新的通道数,新的行数
Mat reshape(int cn, int newndims, const int* newsz) const
Mat reshape(int cn, const std::vector& newshape) const以上是对矩阵的reshape
7.矩阵转置
MatExpr t() const返回了一个临时转置变量可进一步参与复杂矩阵表达
8.矩阵反转
MatExpr inv(int method=DECOMP_LU) const返回一个临时反转矩阵
9.矩阵间元素相乘或相除,等于*
MatExpr mul(InputArray m, double scale=1) const矩阵乘法
10.矩阵叉乘
Mat cross(InputArray m) const必须是两个3元素浮点型vectors相乘
11.矩阵点乘
double dot(InputArray m) const对点乘的两个矩阵视为1维矩阵进行点乘
12.根据需求返回特定类型矩阵
static MatExpr zeros(int rows, int cols, int type)返回全0矩阵
static MatExpr zeros(Size size, int type)
static MatExpr zeros(int ndims, const int* sz, int type)
static MatExpr ones(int rows, int cols, int type)返回全1矩阵
static MatExpr ones(Size size, int type)
static MatExpr ones(int ndims, const int* sz, int type)
static MatExpr eye(int rows, int cols, int type)返回对角矩阵
static MatExpr eye(Size size, int type)
13.构造新的数组data
void create(int rows, int cols, int type)
void create(Size size, int type)
void create(int ndims, const int* sizes, int type)
void create(const std::vector& sizes, int type)
14.增加引用计数
void addref()增加当前矩阵的引用计数,是一个隐式调用的原子操作,是安全的,所以我们是不需要关心这个的使用的
15.预留空间
void reserve(size_t sz)一个类似STL vector的预留行操作
void reserveBuffer(size_t sz)预留bytes操作,如果重新申请空间了,之前存在的内容会丢失
void resize(size_t sz)改变矩阵行的数量,如果reallocated了,min(sz,rows)
void resize(size_t sz, const Scalar& s)改变矩阵行数,新增元素用s初始化
16.增加、移除元素
void push_back(const _Tp& elem)
void push_back(const Mat_<_Tp>& elem)
void push_back(const std::vector<_Tp>& elem)给matrix最底层增加元素,元素类型和列数要与matrix保持一致
void push_back(const Mat& m)给matrix增加line
void pop_back(size_t nelems=1)移除matrix元素,从底层开始
17.ROI区域操作
void locateROI( Size& wholeSize, Point& ofs ) const定位当前矩阵的头在父矩阵中的位置,和父矩阵的大小
Mat& adjustROI( int dtop, int dbottom, int dleft, int dright )修改当前矩阵,在父矩阵上下左右四个方向上的变化
18.矩阵提取,只产生新的矩阵头
Mat operator()( Range rowRange, Range colRange ) const()操作,行范围列范围
Mat operator()( const Rect& roi ) const根据矩阵范围提取
Mat operator()( const Range* ranges ) const
Mat operator()(const std::vector& ranges) const
19.判断性函数
bool isContinuous() const判断数组是否在内存上没有间断,连续一体的
bool isSubmatrix() const判断矩阵是否为子矩阵
bool empty() constdata是否为NULL
20.取值操作
size_t elemSize() const当type为16UC3时,elemSize=16bit/8*3=6bytes
size_t elemSize1() constelemSize/channels
int type() const元素类型
int depth() constCV_16S,这种
int channels() const通道数
size_t step1(int i=0) conststep/eleSize1()
size_t total() const数组元素个数
size_t total(int startDim, int endDim=INT_MAX) const某个子数组startDim-endDim维度中的元素个数
int checkVector(int elemChannels, int depth=-1, bool requireContinuous=true) const检测是否为一个2d或3d矩阵

除了以上的函数,Mat中还包含了at,ptr,begin,end,操作符,更多的是对图像中元素的取或写操作~
总结:
1.通过对Mat成员函数的调查,发现其成员函数数量众多,功能比较全面,但万变不离其宗,是"增删查改“的扩充与完善。
2.通过阅读Mat的成员函数,我想大家都会对Mat所处理的内存也会有一定的了解了,而我们写一个算法,其实最终就是对这块内存的一顿操作,更好的了解内存结构可以帮助我们更好的实现算法~
3.我想通过对Mat的发问及了解,Mat中一些变量和函数也不在变得神秘,对于读源码也有很大的帮助,今天就先记录到这里吧~

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值