OpenCV学习笔记:Mat类详解(二)

1. 前言:Mat类的深入解读

我在学《数据结构》的时候,每接触一种新的ADT(abstract data type, 抽象数据类型 ),一般的套路都是这样的——
1.先了解ADT的结构,如何定义?包括哪些数据对象?如,线性表是n个具有相同特性的数据元素的有限序列。
2.再学习基于该ADT结构的一些操作和算法。如,线性表的增删查改,基于顺序表的排序算法;
3.最后再学习该ADT的存储方式和实现过程。如,链表在计算机内存中的存储。

那么,在了解了基本结构之后,作为OpenCV最重要的ADT——Mat类,在计算中如何存储,以及有哪些基本操作的呢?

2.元素数据寻址

Mat类是一个n维单通道或多通道的稠密型数值阵列,可以用于存储实数或者负数向量和矩阵(real or complex-valued vectors or matrices)、灰度图和彩色图(grayscale or color images )、矢量场(vector fields)等,阵列M的数据分布取决于数组M.step[],因此M阵列中元素(i0,...,iM.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
  • 1

其中,
step[i]是Mat类中十分重要的一个属性,表示第i维的总大小,单位字节
M.data指向存储这列的首地址(类似于数组名)
M.dims是总维度

例如,二维矩阵的寻址可以表示为:

addr(Mi,j)=M.data+M.step[0]∗i+M.step[1]∗j

注意, M.step[i] >= M.step[i+1] ,实际上, M.step[i] >= M.step[i+1]* M.size[i+1],其中,M.size[i]表示第i维包含的个数,这也就意味着,二维矩阵是按行存储(stored row-by-row),而三维矩阵是按面存储(stored plane-by-plane),高维以此类推。M.size[M.dims-1]表示矩阵的最低维,大小与M.elemSize() 相等

举个例子,来说明矩阵的存储方式,以及矩阵各个属性的意义——

int sizeMat[] = { 3, 4, 6 };
Mat src(3, sizeMat, CV_32FC3, Scalar::all(0));
cout << "src.dims = " << src.dims << endl;
cout << "src.step[0] = " << src.step[0] << endl;
cout << "src.step[1] = " << src.step[1] << endl;
cout << "src.step[2] = " << src.step[2] << endl << endl;

cout << "src.size[0] = " << src.size[0] << endl;
cout << "src.size[1] = " << src.size[1] << endl;
cout << "src.size[2] = " << src.size[2] << endl << endl;

cout << "src.step1[0] = " << src.step1(0) << endl;
cout << "src.step1[1] = " << src.step1(1) << endl;
cout << "src.step1[2] = " << src.step1(2) << endl << endl;

cout << "src.elemSize() = " << src.elemSize() << endl;
cout << "src.elemSize1() = " << src.elemSize1() << endl;

 

 

创建一个3维的矩阵,尺寸为 3 * 4 * 6,那么输出为

Mat类型维度是从高维度到低维度排列的,比如3维矩阵,按照面、行、点的顺序对应其维度大小,我们上面所定义的矩阵包含3个面、每个面包含4行,每行包含6个点。

因此,step[0]表示第一维,面( plane),所包含总元素的字节大小

我们定义的矩阵每个面有4*6=24个点,每个点定义为CV_32FC3类型,即3通道的32位float型,一个元素包含3个float,即 3 *4=12字节,那么整个面的大小就为 24 *12 =288 字节

同理,step[1]表示第二维,行( row) ,所包含总元素的大小

step[1] = 6 *12 =72 字节

step[2]表示第三维,点( point) ,所包含总元素大小

step[2] = 12字节

step1(i)表示第i维的包含的通道数
第一维,包含4 * 6个点, 每个点都是3通道的,因此,第一维包含4 * 6 * 3 =72个通道
同理,第二维包含 18个通道,第三维包含3个通道

属性size表示每个维度的大小,即——
第一维大小为3,size(0) = 3
第二维为4,size(1) = 4
第三维为6,size(2) = 6

属性elemSize表示的是每一个矩阵元素的大小,这个元素可能包含多个通道,而elemSize1表示的是每个通道下所包含基本类型的大小,即elemSize=channels * elemSize1

最后再通过一张图来表示Mat类的寻址方式

3.Mat类元素访问方法

(1)Mat成员函数at<>()访问元素

Mat的成员函数at()是一个模板函数,针对不同的情况,有多个重载函数可供选择,在这里,我们使用最常见的二维矩阵的at函数

for (int r = 0; r < src.rows; r++)  
{  
     for (int c = 0; c < src.cols; c++)  
     {     
      cout<< src.at<Vec3f>(r,c)<<endl;  
     }     
}  

注意,使用at函数时,应该知道矩阵元素的类型和通道数,根据矩阵元素类型和通道数来确定at函数传递的类型,在上例当中,我们定义的src矩阵是CV_32FC3,即3通道float型,对应Vec3f可以兼容该类型。

 

(2)使用Mat的成员函数ptr<>()访问元素

Vec3f*temp(NULL);
for (int r = 0; r < src.rows; r++)  
{  
  temp = src.ptr<Vec3f>(r);  
  for (int c = 0; c < src.cols; c++)  
  {  
    cout<<  temp[c];  
  }  
}  

 

注意,ptr 是指向矩阵的行,同样,使用ptr函数也应该知道矩阵元素的类型和通道数

(3)使用OpenCV迭代器

MatIterator_<Vec3b> it_src;  
MatIterator_<Vec3b> itEnd_src;
it_src = src.begin<Vec3f>();  
itEnd_src = src.end<Vec3f>(); 
for (; it_src != itEnd_src; it_src++)  
{  
  cout<< *it_src;  
}  

 

 

 

4.参考文献

[1] https://www.douban.com/note/265479171/

[2] http://blog.csdn.net/qianqing13579/article/details/45318279

[3] http://blog.csdn.net/bendanban/article/details/30527785

posted @ 2018-04-16 16:50  月夜_1  阅读(448)  评论(0编辑  收藏  举报