< NCNN-Lession-7 > Mat类的实现
开始
这一节我们终于要学习Mat类.大家可以看到,这个类的名字”Mat”其实和Opencv中常用的Mat类是一样的名字,但二者不是同一个东西,一个是ncnn的Mat类,一个是Opencv的Mat类,大家要注意.好,废话不多说,我们再插上一个小红旗(压压惊):
作用
Mat类的作用其实存放神经网络需要处理的数据,这些数据一般包含一下几种(不限于):
- 输入图片
- 模型的weight和bias
- 模型处理的top_blob和bottom_blob
Mat好处在于如下(不限于):
- 方便的构建数据,如根据长宽高构建不同维度的数据.
- 方便的索引数据,如按照channel索引数据.
- 可以很方便的获取数据信息,如维度,长,宽等等.
- 可以很方便的拷贝数据,如clone方法
实现
首先不废话,先上个Mat的概况图:
上图中,右半部分是Mat类所包含的必要的成员变量,左半部分是Mat所包含的必要的成员函数.
成员函数很简单,名字和类型都在上图中有写到,这里就不再做过多的说明.下面我们着重说一下Mat类的成员函数.
我们从开辟内存的函数Mat::create来说起.我看先来看一下它的实现:
1 | inline void Mat::create(int _w, int _h, int _c, size_t _elemsize, int _elempack, |
上述函数针对一个三维的数组开辟空间,三个维度分别为长h,宽w和通道数c(channel).
其中cstep使用了AlignSize()函数,它的含义是计算出一个大于步长数字且该数可以刚好被对齐数(16)整除,这个数字其实就是每一个channel所包含的字节空间数.
计算这个数字的目的是为了进行另外一个维度的对齐,也就是空间尺寸上的对齐.我们在上一节中讲到,alllocator中的alignPtr函数做到了首地址上的对齐.
由于每一个元素的字节数是elemsize,所以一共需要申请的空间如下:
1 | size_t tmp_size = c* cstep * elemsize |
关于具体的空间分配情况,我做了一个如下的表格来说明,假如说我们申请了一个(2x2x4)的Mat,具体的空间分配方式如下所示
head | pad_x | (head_use)elemsize | elemsize | elemsize | elemsize | pad_y | pad_y… | (x) |
---|---|---|---|---|---|---|---|---|
(x) | (x) | (channel2)elemsize | elemsize | elemsize | elemsize | pad_y | pad_y… | (x) |
(x) | (x) | (channel3)elemsize | elemsize | elemsize | elemsize | pad_y | pad_y… | (x) |
(x) | (x) | (channel4)elemsize | elemsize | elemsize | elemsize | pad_y | pad_y… | pad_z… |
下面对上面的表格做一个说明:
其中head代表总的申请空间的地址.
pad_x是为了让使用的空间的首地址能够对齐来进行的把head指针向前推移的操作,
elemsize是每一个元素占用的空间.
pad_y是为了使得申请的cstep得到空间上的对齐而额外pad的空间,pay_z是为了使的申请的total_size对对齐而额外pad的空间.
(x)没有任何意义,请跳过
上个分为4行表示主要是因为我们的channel共有4个,每一行代表一个channel,在实际的内存中,这四行其实是连续的.
下面我们说一下channel这个函数,这个函数其实是构造了一个Mat对象,只不过把Mat对象的数据指针给移动到了对应的位置上:
1 | inline Mat Mat::channel(int _c) { |
其他的函数都是大同小异,大家可以看今天的示例代码.
最后还要说一下,Mat类中定义了一个重载类型转化符,当Mat类被某个指针强制转化的时候,其实是返回了它的成员函数data的指针被强制转化的结果:
1 | template <typename T> |
有的同学可能注意到Mat这个类还有两个的参数没有说,那就是elempack和refcount,我们这一节暂时不讲.
代码示例
测试程序在这里.
代码结构如下:
< NCNN-Lession-7 > Mat类的实现