由于最近项目需要用到NCNN,发现用起来还是非常舒适的,纯c++代码,忍不住看了看源码,顺便做做笔记,实际就是在源码上做了些注释。由于代码较多,这里只记录比较重要的部分。
构造函数
/* 创建一个Mat
elemsize: float32/ int32: 4; float16: 2; int8/uint8: 1; empty: 0
elempack: 表示一个数据包的元素个数,就是对Mat的元素分组打包
dims: Mat维度
w、h、c: Mat的宽、高、通道数
cstep: 一个通道的大小,可以看作是w*h的乘积
*/
inline Mat::Mat()
: data(0), refcount(0), elemsize(0), elempack(0), allocator(0), dims(0), w(0), h(0), c(0), cstep(0)
{
}
inline Mat::Mat(int _w, int _h, int _c, size_t _elemsize, Allocator* _allocator)
: data(0), refcount(0), elemsize(0), elempack(0), allocator(0), dims(0), w(0), h(0), c(0), cstep(0)
{
create(_w, _h, _c, _elemsize, _allocator);
}
inline Mat::Mat(int _w, int _h, int _c, size_t _elemsize, int _elempack, Allocator* _allocator)
: data(0), refcount(0), elemsize(0), elempack(0), allocator(0), dims(0), w(0), h(0), c(0), cstep(0)
{
create(_w, _h, _c, _elemsize, _elempack, _allocator);
}
/*
创建Mat的函数
*/
inline void Mat::create(int _w, int _h, int _c, size_t _elemsize, int _elempack, Allocator* _allocator)
{
if (dims == 3 && w == _w && h == _h && c == _c && elemsize == _elemsize && elempack == _elempack && allocator == _allocator) // 如果当前的Mat初始化结果满足create的结果,无需再次创建新的Mat,则直接返回即可
return;
release();
elemsize = _elemsize;
elempack = _elempack;
allocator = _allocator;
dims = 3;
w = _w;
h = _h;
c = _c;
cstep = alignSize((size_t)w * h * elemsize, 16) / elemsize; //每个通道的元素个数
if (total() > 0) // total()表示所有的元素个数,cstep*c
{
size_t totalsize = alignSize(total() * elemsize, 4); // 除了数据需要分配的空间大小,还需要一个引用计数的空间
if (allocator)
data = allocator->fastMalloc(totalsize + (int)sizeof(*refcount));
else
data = fastMalloc(totalsize + (int)sizeof(*refcount));
refcount = (int*)(((unsigned char*)data) + totalsize);
*refcount = 1;
}
}
/*
创建一个shape和m相同的Mat
*/
inline void Mat::create_like(const Mat& m, Allocator* _allocator)
{
int _dims = m.dims;
if (_dims == 1)
create(m.w, m.elemsize, m.elempack, _allocator);
if (_dims == 2)
create(m.w, m.h, m.elemsize, m.elempack, _allocator);
if (_dims == 3)
create(m.w, m.h, m.c, m.elemsize, m.elempack, _allocator);
}
shape相关
/*
shape函数返回的是一个shape大小的Mat, 而不是pytorch那样直接用字符串表示的shape
*/
inline Mat Mat::shape() const
{
if (dims == 1)
return Mat(w * elempack, (void*)0);
if (dims == 2)
return Mat(w, h * elempack, (void*)0);
if (dims == 3)
return Mat(w, h, c * elempack, (void*)0);
return Mat();
}
/*
reshape到指定的维度大小
*/
inline Mat Mat::reshape(int _w, int _h, int _c, Allocator* _allocator) const
{
if (w * h * c != _w * _h * _c) // 如果元素总数不对应,则返回空
return Mat();
if (dims < 3) // 当前的Mat是一维向量或者二维矩阵,要转换成三维的情况
{
if ((size_t)_w * _h != alignSize((size_t)_w * _h * elemsize, 16) / elemsize)
{
Mat m;
m.create(_w, _h, _c, elemsize, elempack, _allocator);
// align channel
for (int i = 0; i < _c; i++) // 每个通道放入_w*_h个元素
{
const void* ptr = (unsigned char*)data + (size_t)i * _w * _h * elemsize; // 从data的头指针开始的第i * _w * _h * elemsize位置为起始位置 写
void* mptr = (unsigned char*)m.data + i * m.cstep * m.elemsize; // 从新Mat m的第i * m.cstep * m.elemsize位置接收数据
memcpy(mptr, ptr, (size_t)_w * _h * elemsize); // 拷贝到mptr _w*_h个数据
}
return m;
}
}
else if (c != _c) // 如果当前Mat是三维的,并且通道数不同,则先将当前的Mat展平成一维向量,然后reshape
{
// flatten and then align
Mat tmp = reshape(_w * _h * _c, _allocator);
return tmp.reshape(_w, _h, _c, _allocator);
}
//如果是三维的,并且通道数也相同,按照设定的尺寸重新分配空间,可能的情况是3x4x5->3x5x4, 宽和高转换维度
Mat m = *this;
m.dims = 3;
m.w = _w;
m.h = _h;
m.c = _c;
m.cstep = alignSize((size_t)_w * _h * elemsize, 16) / elemsize;
return m;
}
索引函数
inline Mat Mat::channel(int _c) // 返回第_c个通道的Mat, _c作为起始位置
{
return Mat(w, h, (unsigned char*)data + cstep * _c * elemsize, elemsize, elempack, allocator);
}
inline Mat Mat::channel_range(int _c, int channels) // 返回从第_c个通道开始的channels个通道的Mat
{
return Mat(w, h, channels, (unsigned char*)data + cstep * _c * elemsize, elemsize, elempack, allocator);
}
inline Mat Mat::row_range(int y, int rows) // 返回从第y行开始,取rows行的Mat
{
return Mat(w, rows, (unsigned char*)data + (size_t)w * y * elemsize, elemsize, elempack, allocator);
}
inline Mat Mat::range(int x, int n) // 返回从第x个元素开始,n个元素的Mat
{
return Mat(n, (unsigned char*)data + x * elemsize, elemsize, elempack, allocator);
}
需要注意的是,NCNN Mat可以使用[]进行索引,但是存在补齐的机制,所以最好不要直接去直接对整个Mat索引,最好是先取到一个channel,再用[]索引。
比如这个,一个通道的元素数是2x3是6个,会在元素5的后面补两个空位,导致m[8]为6
经过实验,不会对16补齐,只会对4和8补齐
文章转载于:知乎
作者:闪电侠的右手
推荐阅读