【opencv14】cv::Mat---Desne数组类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiu931110/article/details/85195307

0.给自己打个小广告

本人211硕士毕业,目前从事深度学习,机器学习计算机视觉算法行业,目前正在将我的各类学习笔记发布在我的公众号中,希望感兴趣一起学习的同学们可以关注下~~~
本人微信公众号:yuanCruise

1.cv::Mat类简介

cv::Mat用于大型矩阵类型,这可以说是Opencv库整个c++实现的核心类。OpenCV库中绝大多数函数都是cv::Mat类的成员函数,或者以cv::Mat作为参数,或者以cv::Mat作为返回值。

cv::Mat类用于表示任意维度的dense数组。大多数图像数据都是存储在dense数组中,当然与之对应的就是sparse数组。

  • dense数组:对于数组中的每个元素,内存中都存储了一个与当前元素对应的数据值,即使该元素值为零。
  • sparse数组:存储的是非零项。如果许多元素值实际上为零,那么这会节省大量存储空间。(Opencv中永cv::SpareseMat类表述sparse数组。)

Note:若你对opencv2.1之前的版本较熟悉,你可能记得 I p l I m a g e {IplImage} C v M a t {CvMat} 。但在之后的版本中,这些类都没了,都被 c v : : M a t {cv::Mat} 取代。

2.cv::Mat类中数据存储规则

cv::Mat类数据可以被用来表示各种维度的数组。数据存储在数组中,可以看作是一个n维的“光栅扫描顺序(raster scan order)”。

raster scan order解释:
在一维数组中,元素是顺序排列的。
在二维数组中,元素是被组织成行,每一行依次排列
在三维数组中,每一个平面可以认为二维数组,且每一个平面(二维数组)按行排列,且平面是一个接着一个。

每个数组都有一个标志元素,用来指示数组的内容。

  • dim:指定数组维度
  • rows/cols:指定数组的行数和列数(当dim>2时,这两个参数无效)
  • 指针:指向数组数据存储位置的数据指针
  • refcount:引用计数器,类似于利用cv::Ptr<>的引用计数器。该类成员的存在使得cv::Mat类能像数据智能指针一样被数据包含。

数据中的内存布局由数组step[]描述。cv::Mat类中,当下标为(i0,i1,’’’,iNd-1,iNd)时,数组的存储地址为:

对于二维数组而言:

cv::Mat中的每个数据元素本身可以是单个数字,也可以是多个数字。在多个数字的情况下,这个库称为多通道数组。事实上,n维数组和(n-1)维多通道数组实际上是非常相似的对象,但是由于在许多情况下,将数组看作向量值数组是有用的,比如图像而言通道为3表示彩色图像,通道为1表示灰度图像,所以库中对这种结构有特殊的规定。

在使用opencv2.1之前的版本中的 I p l I m a g e {IplImage} 时,有一个表征通道的成员变量: I p l I m a g e &MediumSpace; : &MediumSpace; : n C h a n n e l s {IplImage\::\::nChannels} 。但在之后的版本中用cv::Mat类后,没有这个成员变量了,要查看通道数则需要调用cv::channels()。

要设置这种通道的区别的主要原因是内存访问。根据定义,数组的元素可以是向量值的部分。例如,一个数组可以被称为32位浮点数的二维三通道数组;在这种情况下,数组的元素是三个大小为12字节的32位浮点数。
当然在实际情况下,数据元素在内存中布局时,数组的行不是绝对按照顺序连续存放的;可能在行与行之间会有小的间隔。

这种填充的目的是提高内存访问速度。

n维单通道阵列与(n-1)维多通道阵列的区别在于,这种填充总是发生在完整行的末尾(也就是说,同一个通道中的元素总是顺序连续的)。

3.创建数组
cv::Mat m;
// Create data area for 3 rows and 10 columns of 3-channel 32-bit floats
m.create( 3, 10, CV_32FC3 );
// Set the values in the 1st channel to 1.0, the 2nd to 0.0, and the 3rd to 1.0
m.setTo( cv::Scalar( 1.0f, 0.0f, 1.0f ) );

可以利用cv::Mat类实例化一个变量来创建数组。当然利用这种方式创建的数组没有大小,也没有数据类型。在用这种方式创建数组后,之后可使用诸如create()这样的成员函数来分配数组大小,数据类型等参数。数组类型这一个参数决定了它具有哪种元素。

  • 第一个位置参数:传入的是数组的行数,此例中表示数组3行
  • 第二个位置参数:传入的是数组的列数,此例中表示数组10列
  • 第三个位置参数:传入的是数组元素的类型,此例中32bit浮点数,3通道

第三个位置参数,即指定元素的基本类型,也指定通道数。所有可能的类型都在头文件中定义了,一共有6*3=18种可选方式(从如下组合中选取): C V _ { 8 U , 16 S , 16 U , 32 S , 32 F , 64 F } { 1 , 2 , 3 } CV\_{\{8U,16S,16U,32S,32F,64F\}\{1,2,3\}}
所以按照上述的例子CV_32FC3表示32bit的浮点数,3通道。

cv::Mat m( 3, 10, CV_32FC3, cv::Scalar( 1.0f, 0.0f, 1.0f ) );

如上述代码所示,前一段介绍的代码可以整合构造过程中,这是另一种构造函数(带参数的)。

理解数组中的数据没有严格地附加到数组对象上是至关重要的。Mat对象实际上是一个数据区域的标头,原则上是一个完全独立的东西。例如,可以将一个矩阵n赋值给另一个矩阵m(即m = n)。在这种情况下,m内部的数据指针将被改变为指向与n相同的数据,之前m的数据指针(如果有的话)所指向的数据将被释放。同时,它们现在共享的数据区域的引用计数器将增加。最后,将更新m的成员(例如行,列和标志),以准确描述m中数据现在指向的数据。 这一切使得这些行为变得很方便,其中数组可以彼此分配,并且在幕后自动执行此操作以提供正确的结果。

4.cv::Mat对象的构造函数

下表是cv::Mat类可用的构造函数的完整列表。当然大部分情况下,我们只会用到下表中的某几个。

操作 例子
默认构造函数 cv::Mat;
按类型的2维矩阵构造函数 cv::Mat( int rows, int cols, int type );
带初始化值的按类型的2维矩阵构造函数 cv::Mat(int rows, int cols, int type,const Scalar& s);
具有预先存在的数据的2维矩阵构造函数 cv::Mat(int rows, int cols, int type,void* data, size_t step=AUTO_STEP);
按类型的2维数组构造函数(大小以sz为单位) cv::Mat( cv::Size sz, int type );
带初始化值的按类型的2维矩阵构造函数(大小以sz为单位) cv::Mat(cv::Size sz,int type, const Scalar& s);
具有预先存在的数据的2维矩阵构造函数(大小以sz为单位) cv::Mat( cv::Size sz, int type,void* data, size_t step=AUTO_STEP)
按类型的多维矩阵构造函数 cv::Mat(int ndims, const int* sizes,int type);
带初始化值的按类型的多维矩阵构造函数 cv::Mat(int ndims, const int* sizes,int type, const Scalar& s);
具有预先存在的数据的多维矩阵构造函数 cv::Mat(int ndims, const int* sizes,int type, void* data,size_t step=AUTO_STEP);

上表列出了cv::Mat类对象的基本构造函数,下表将列出拷贝构成函数,即如何从一个已知数组创建数组。除了基本的复制构造函数之外,还有三种方法用于从现有数组的子区域构造数组,以及一种使用某种矩阵表达式的结果初始化新矩阵的构造函数。

操作 例子
拷贝构造函数 cv::Mat( const Mat& mat );
仅拷贝指定行列的构造函数 cv::Mat(const Mat& mat,const cv::Range&rows,const cv::Range&cols);
仅拷贝指定区域的构造函数 cv::Mat(const Mat& mat,const cv::Rect& roi);
广义感兴趣区域复制构造函数,它使用范围数组从n维数组中进行选择 cv::Mat(const Mat& mat,const cv::Range*ranges);
复制构造函数,用其他矩阵的代数表达式的结果初始化m cv::Mat( const cv::MatExpr&expr );

上述拷贝构造函数中,利用子区域(也称为“感兴趣区域”)构造函数有三种形式:一种采用一系列行和一系列列(这仅适用于二维矩阵),一种使用cv :: Rect指定矩形子区域(也仅适用于二维矩阵),最后一个采用范围数组。 在后一种情况下,指针参数范围指向的有效范围的数量必须等于数组mat的维数。 如果mat是ndim大于2的多维数组,则必须使用第三个选项。

5.与旧版本之间的兼容问题

如果您正在维护包含C样式数据结构的opencv2.1版本之前的代码,您可能希望从现有的CvMat或IplImage结构创建新的C ++样式的cv :: Mat结构。 在这种情况下,我们有两个选择:

  • 在现有的数据上构建一个标记头m(通过将copyData设置为false)
  • 重新分配内存,并将老数据复制到m中(通过将copyData设置为true)
cv::Mat(
const CvMat* old,
bool copyData=false
);
cv::Mat(
const IplImage*
old,
bool copyData=false
);
6.模板构造函数

模板构造函数之所以被称之为模板构造函数,不是因为它们从cv::Mat构造出了一个模板类,而是因为它们从本身就是模板的东西(cv::Vec<>或cv::Matx<>),来创建cv::Mat类实例。所以模板构造函数能够用任意cv::Vec<>,cv::Matx<>类的实例来创建cv::Mat数组。或者将任意类型的STL库中的vector<>实例来构建相同类型的cv::Mat数组。

操作 例子
从相同类型的cv :: Vec构造类型为T和大小为n的一维数组 cv::Mat(const cv::Vec<T,n>&vec,bool copyData=true);
从相同类型的cv :: Matx构造类型为T且大小为m×n的二维数组 cv::Mat(const cv::Matx<T,m,n>&vec,bool copyData=true);
从包含相同类型元素的STL向量构造类型为T的一维数组 cv::Mat(const std::vector&vec,bool copyData=true);
7.Mat类的静态成员函数

cv::Mat类提供一些能够创建常用类的静态成员函数。

//创建一个大小为rows×cols的cv :: Mat,其中所有的值为类型为type(CV_32F等)的零
cv::Mat::zeros( rows, cols, type );

//创建一个大小为rows×cols的cv :: Mat,其中所有的值为类型为type(CV_32F等)的一
cv::Mat::ones( rows, cols, type);

//单位矩阵
cv::Mat::eye( rows, cols, type);

猜你喜欢

转载自blog.csdn.net/qiu931110/article/details/85195307