Matrix的类型
enum { MAGIC_VAL = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG };
enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 };
/*! includes several bit-fields:
- the magic signature
- continuity flag
- depth
- number of channels
*/
int flags;
从定义可以看出flags是int类型,共占32位,包含信息the magic signature,continuity flag,depth,number of channels。
/****************************************************************************************\
* Matrix type (Mat) *
\****************************************************************************************/
#define CV_MAT_CN_MASK ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_MAT_CN(flags) ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_TYPE_MASK (CV_DEPTH_MAX*CV_CN_MAX - 1)
#define CV_MAT_TYPE(flags) ((flags) & CV_MAT_TYPE_MASK)
#define CV_MAT_CONT_FLAG_SHIFT 14
#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG)
#define CV_IS_CONT_MAT CV_IS_MAT_CONT
#define CV_SUBMAT_FLAG_SHIFT 15
#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT)
#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG)
/** Size of each channel item,
这个数值对应图像深度
16F 64F 32F 32S 16S 16U 8S 8U
0x28442211 = 0010 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */
#define CV_ELEM_SIZE1(type) ((0x28442211 >> CV_MAT_DEPTH(type)*4) & 15)
#define CV_ELEM_SIZE(type) (CV_MAT_CN(type)*CV_ELEM_SIZE1(type))
depth信息
DEPTH_MASK = 7 ,最后3位是深度信息,Depth总共只有8个类型,定义如下,定义了单个通道单个像素的值的类型。
#define CV_CN_MAX 512
#define CV_CN_SHIFT 3
#define CV_DEPTH_MAX (1 << CV_CN_SHIFT)
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_16F 7
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE
#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))
#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))
#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))
#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))
#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))
#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))
#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))
#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
#define CV_16FC4 CV_MAKETYPE(CV_16F,4)
#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n))
Chanels信息
TYPE_MASK = 0x00000FFF,类型总共12位,除了最后三位深度信息,其余都是chanels,总共9位,也就是最大支持512
continuity flag
#define CV_MAT_CONT_FLAG_SHIFT 14
#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG)
第15位代表Mat的内存是否连续,一般由creat创建的mat均是连续的,如果是连续,将加快对数据的访问。
Submatrix
#define CV_SUBMAT_FLAG_SHIFT 15
#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT)
#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG)
第16位代表该Mat是否为某一个Mat的submatrix,一般通过ROI以及row()、col()、rowRange()、colRange()等得到的mat均为submatrix。
magic
MAGIC_MASK = 0xFFFF0000,16-31代表magic signature,暂理解为用来区分Mat的类型
inline
bool Mat::isContinuous() const
{
return (flags & CONTINUOUS_FLAG) != 0;
}
inline
bool Mat::isSubmatrix() const
{
return (flags & SUBMATRIX_FLAG) != 0;
}
总结
从低位到高位:
0-2位代表depth即数据类型(如CV_8U),OpenCV的数据类型共7类,故只需3位即可全部表示。
3-11位代表通道数channels,因为OpenCV默认最大通道数为512,故只需要9位即可全部表示,可参照下面求通道数的部分。
0-11位共同代表type即通道数和数据类型(如CV_8UC3)
12-13位暂没发现用处,也许是留着后用,待发现了再补上。
14位代表Mat的内存是否连续,一般由creat创建的mat均是连续的,如果是连续,将加快对数据的访问。
15位代表该Mat是否为某一个Mat的submatrix,一般通过ROI以及row()、col()、rowRange()、colRange()等得到的mat均为submatrix。
16-31代表magic signature,暂理解为用来区分Mat的类型,如果Mat和SparseMat
mat创建
mat的初始化方法有很多,最后都是通过create创建的内存块
void Mat::create(int d, const int* _sizes, int _type)
{
int i;
CV_Assert(0 <= d && d <= CV_MAX_DIM && _sizes);
_type = CV_MAT_TYPE(_type);
if( data && (d == dims || (d == 1 && dims <= 2)) && _type == type() )
{
if( d == 2 && rows == _sizes[0] && cols == _sizes[1] )//需要的二维矩阵和当前一样
return;
for( i = 0; i < d; i++ )
if( size[i] != _sizes[i] )
break;
if( i == d && (d > 1 || size[1] == 1))//维度相同,每个维度大小相同
return;
}
int _sizes_backup[CV_MAX_DIM]; // #5991
if (_sizes == (this->size.p))
{
for(i = 0; i < d; i++ )
_sizes_backup[i] = _sizes[i];
_sizes = _sizes_backup;
}
release();//释放之前的内存
if( d == 0 )
return;
flags = (_type & CV_MAT_TYPE_MASK) | MAGIC_VAL;
setSize(*this, d, _sizes, 0, true);//设置数据大小
if( total() > 0 )
{
MatAllocator *a = allocator, *a0 = getDefaultAllocator();//如果不设置分配器,就使用默认的分配器
#ifdef HAVE_TGPU
if( !a || a == tegra::getAllocator() )
a = tegra::getAllocator(d, _sizes, _type);
#endif
if(!a)
a = a0;
try
{
u = a->allocate(dims, size, _type, 0, step.p, ACCESS_RW /* ignored */, USAGE_DEFAULT);//分配内存
CV_Assert(u != 0);
}
catch (...)
{
if (a == a0)
throw;
u = a0->allocate(dims, size, _type, 0, step.p, ACCESS_RW /* ignored */, USAGE_DEFAULT);//如果设置的分配器分配失败会使用默认的分配器尝试一次分配。如果再次异常,不会处理异常
CV_Assert(u != 0);
}
CV_Assert( step[dims-1] == (size_t)CV_ELEM_SIZE(flags) );//判断分配的内存大小是不是对的。
}
addref();//给分配的内存添加一个引用计数
finalizeHdr(*this);
}
mat初始化的时候都会在初始化列表将所有成员初始化为0,默认构造函数因为没有参数,所有没有使用create生成mat,其余的一半都会传入一个可以确定mat的维度和每个维度大小也就是矩阵大小的参数,
从CV_MAX_DIM 可以看出来这个矩阵最多有32维。还有一个每个元素大小的type值,也就是通道和颜色深度。
创建新的内存之前会先尝试看是不是需要重新分配,减少操作,一般新建的对象都是需要分配的,这里主要讲分配,不具体分析什么情况不会重新分配。
分配之前会使用release, CV_XADD(&u->refcount, -1),会通过_InterlockedExchangeAdd函数进行支持多线程的数据原子操作,修改指针引用计数,一旦引用为0就会自动释放内存,然后把指针置为空,正常来说,多次调用release是没有问题的,但是有时候,new的mat对象delete之后会有异常,不知道是不是release还有其他问题,所以尽量不要是要new delete操作处理mat对象,我这里是使用shared ptr让系统维护内存,然后就没有问题了。
inline
void Mat::release()
{
if( u && CV_XADD(&u->refcount, -1) == 1 )
deallocate();
u = NULL;
datastart = dataend = datalimit = data = 0;
for(int i = 0; i < dims; i++)
size.p[i] = 0;
#ifdef _DEBUG
flags = MAGIC_VAL;
dims = rows = cols = 0;
if(step.p != step.buf)
{
fastFree(step.p);
step.p = step.buf;
size.p = &rows;
}
#endif
}
释放掉之前的内存之后,会设置一下和图片大小相关的一些参数,因为后面需要使用这些参数分配内存。系统有一个默认的内存分配器,mat的构造函数允许你指定分配器,如果你不指定就使用默认的,同样的会对你指定的分配器分配失败之后,抓取异常,重新使用默认的分配器分配一次。
默认的构造是StdMatAllocator ,并且不能再被派生。
class StdMatAllocator CV_FINAL : public MatAllocator,下面是分配的函数
UMatData* allocate(int dims, const int* sizes, int type,
void* data0, size_t* step, AccessFlag /*flags*/, UMatUsageFlags /*usageFlags*/) const CV_OVERRIDE
{
size_t total = CV_ELEM_SIZE(type);
for( int i = dims-1; i >= 0; i-- )
{
if( step )
{
if( data0 && step[i] != CV_AUTOSTEP )
{
CV_Assert(total <= step[i]);
total = step[i];
}
else
step[i] = total;
}
total *= sizes[i];
}
uchar* data = data0 ? (uchar*)data0 : (uchar*)fastMalloc(total);
UMatData* u = new UMatData(this);
u->data = u->origdata = data;
u->size = total;
if(data0)
u->flags |= UMatData::USER_ALLOCATED;
return u;
}
可以看到这个分配函数实际上只是算了需要分配的内存的大小,然后将这个内存放到UMatData的date段。这里fastMalloc是直接使用的malloc,然后就是针对不同平台做了些微调。
以上就是mat矩阵的一些分析,后面有时间再补。