最近开始打算把OpenCV和Caffe的源码研究下,以提高自己对图像处理的理解。在代码的研究过程中使用source insight 4.0来对源码进行阅读,主要参考官方文档。对C++的类而言,每次阅读源码都首先理清类中的属性和方法及继承关系。
首先每一个Mat类的属性如下:(opencv\sources\modules\core\include\opencv2\core\mat.hpp)
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;
//! the matrix dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data;
//! helper fields used in locateROI and adjustROI
const uchar* datastart;
const uchar* dataend;
const uchar* datalimit;
//! custom allocator
MatAllocator* allocator;
//! and the standard allocator
static MatAllocator* getStdAllocator();
static MatAllocator* getDefaultAllocator();
static void setDefaultAllocator(MatAllocator* allocator);
//! interaction with UMat
UMatData* u;
MatSize size;
MatStep step;
每一个Mat对象的创建都是使用的create方法,
inline
Mat::Mat(int _rows, int _cols, int _type, const Scalar& _s)
: flags(MAGIC_VAL), dims(0), rows(0), cols(0), data(0), datastart(0), dataend(0),
datalimit(0), allocator(0), u(0), size(&rows), step(0)
{
create(_rows, _cols, _type);
*this = _s;
}
inline
void Mat::create(int _rows, int _cols, int _type)
{
_type &= TYPE_MASK;
if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data )
return;
int sz[] = {_rows, _cols};
create(2, sz, _type);
}
在此函数中需要注意的是MatAllocator和finalizeHdr函数内部的细节,可以看到Mat对象的最终内存分配是由MatAllocator的allocate创建的UMatData对象,使用finalizeHdr对对象进行初始化。
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, 0, USAGE_DEFAULT);
CV_Assert(u != 0);
}
catch(...)
{
if(a != a0)
u = a0->allocate(dims, size, _type, 0, step.p, 0, USAGE_DEFAULT);
CV_Assert(u != 0);
}
CV_Assert( step[dims-1] == (size_t)CV_ELEM_SIZE(flags) );
}
addref();
finalizeHdr(*this);
}
MatAllocator* UMat::getStdAllocator()
{
#ifdef HAVE_OPENCL
if( ocl::haveOpenCL() && ocl::useOpenCL() )
return ocl::getOpenCLAllocator();
#endif
return Mat::getDefaultAllocator();
}
MatAllocator* Mat::getStdAllocator()
{
CV_SINGLETON_LAZY_INIT(MatAllocator, new StdMatAllocator())
}
#define CV_SINGLETON_LAZY_INIT(TYPE, INITIALIZER) CV_SINGLETON_LAZY_INIT_(TYPE, INITIALIZER, instance)
// TODO Memory barriers?
#define CV_SINGLETON_LAZY_INIT_(TYPE, INITIALIZER, RET_VALUE) \
static TYPE* volatile instance = NULL; \
if (instance == NULL) \
{ \
cv::AutoLock lock(cv::getInitializationMutex()); \
if (instance == NULL) \
instance = INITIALIZER; \
} \
return RET_VALUE;
UMatData* allocate(int dims, const int* sizes, int type,
void* data0, size_t* step, int /*flags*/, UMatUsageFlags /*usageFlags*/) const
{
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;
}
struct CV_EXPORTS UMatData
{
enum { COPY_ON_MAP=1, HOST_COPY_OBSOLETE=2,
DEVICE_COPY_OBSOLETE=4, TEMP_UMAT=8, TEMP_COPIED_UMAT=24,
USER_ALLOCATED=32, DEVICE_MEM_MAPPED=64,
ASYNC_CLEANUP=128
};
UMatData(const MatAllocator* allocator);
~UMatData();
// provide atomic access to the structure
void lock();
void unlock();
bool hostCopyObsolete() const;
bool deviceCopyObsolete() const;
bool deviceMemMapped() const;
bool copyOnMap() const;
bool tempUMat() const;
bool tempCopiedUMat() const;
void markHostCopyObsolete(bool flag);
void markDeviceCopyObsolete(bool flag);
void markDeviceMemMapped(bool flag);
const MatAllocator* prevAllocator;
const MatAllocator* currAllocator;
int urefcount;
int refcount;
uchar* data;
uchar* origdata;
size_t size;
int flags;
void* handle;
void* userdata;
int allocatorFlags_;
int mapcount;
UMatData* originalUMatData;
};
static void finalizeHdr(Mat& m)
{
updateContinuityFlag(m);
int d = m.dims;
if( d > 2 )
m.rows = m.cols = -1;
if(m.u)
m.datastart = m.data = m.u->data;
if( m.data )
{
m.datalimit = m.datastart + m.size[0]*m.step[0];
if( m.size[0] > 0 )
{
m.dataend = m.ptr() + m.size[d-1]*m.step[d-1];
for( int i = 0; i < d-1; i++ )
m.dataend += (m.size[i] - 1)*m.step[i];
}
else
m.dataend = m.datalimit;
}
else
m.dataend = m.datalimit = 0;
}
至此Mat对象的分配和初始化基本就完成了,还需要强调的是UMat对象的方法中有并行运算的操作,其他的操作基本和Mat相同。
class CV_EXPORTS MatAllocator
{
public:
MatAllocator() {}
virtual ~MatAllocator() {}
// let's comment it off for now to detect and fix all the uses of allocator
//virtual void allocate(int dims, const int* sizes, int type, int*& refcount,
// uchar*& datastart, uchar*& data, size_t* step) = 0;
//virtual void deallocate(int* refcount, uchar* datastart, uchar* data) = 0;
virtual UMatData* allocate(int dims, const int* sizes, int type,
void* data, size_t* step, int flags, UMatUsageFlags usageFlags) const = 0;
virtual bool allocate(UMatData* data, int accessflags, UMatUsageFlags usageFlags) const = 0;
virtual void deallocate(UMatData* data) const = 0;
virtual void map(UMatData* data, int accessflags) const;
virtual void unmap(UMatData* data) const;
virtual void download(UMatData* data, void* dst, int dims, const size_t sz[],
const size_t srcofs[], const size_t srcstep[],
const size_t dststep[]) const;
virtual void upload(UMatData* data, const void* src, int dims, const size_t sz[],
const size_t dstofs[], const size_t dststep[],
const size_t srcstep[]) const;
virtual void copy(UMatData* srcdata, UMatData* dstdata, int dims, const size_t sz[],
const size_t srcofs[], const size_t srcstep[],
const size_t dstofs[], const size_t dststep[], bool sync) const;
// default implementation returns DummyBufferPoolController
virtual BufferPoolController* getBufferPoolController(const char* id = NULL) const;
};
class StdMatAllocator : public MatAllocator
{
public:
UMatData* allocate(int dims, const int* sizes, int type,
void* data0, size_t* step, int /*flags*/, UMatUsageFlags /*usageFlags*/) const
{
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;
}
bool allocate(UMatData* u, int /*accessFlags*/, UMatUsageFlags /*usageFlags*/) const
{
if(!u) return false;
return true;
}
void deallocate(UMatData* u) const
{
if(!u)
return;
CV_Assert(u->urefcount == 0);
CV_Assert(u->refcount == 0);
if( !(u->flags & UMatData::USER_ALLOCATED) )
{
fastFree(u->origdata);
u->origdata = 0;
}
delete u;
}
};
struct CV_EXPORTS MatSize
{
explicit MatSize(int* _p);
Size operator()() const;
const int& operator[](int i) const;
int& operator[](int i);
operator const int*() const;
bool operator == (const MatSize& sz) const;
bool operator != (const MatSize& sz) const;
int* p;
};
struct CV_EXPORTS MatStep
{
MatStep();
explicit MatStep(size_t s);
const size_t& operator[](int i) const;
size_t& operator[](int i);
operator size_t() const;
MatStep& operator = (size_t s);
size_t* p;
size_t buf[2];
protected:
MatStep& operator = (const MatStep&);
};
///////////////////////////// MatSize ////////////////////////////
inline
MatSize::MatSize(int* _p)
: p(_p) {}
inline
Size MatSize::operator()() const
{
CV_DbgAssert(p[-1] <= 2);
return Size(p[1], p[0]);
}
inline
const int& MatSize::operator[](int i) const
{
return p[i];
}
inline
int& MatSize::operator[](int i)
{
return p[i];
}
inline
MatSize::operator const int*() const
{
return p;
}
inline
bool MatSize::operator == (const MatSize& sz) const
{
int d = p[-1];
int dsz = sz.p[-1];
if( d != dsz )
return false;
if( d == 2 )
return p[0] == sz.p[0] && p[1] == sz.p[1];
for( int i = 0; i < d; i++ )
if( p[i] != sz.p[i] )
return false;
return true;
}
inline
bool MatSize::operator != (const MatSize& sz) const
{
return !(*this == sz);
}
///////////////////////////// MatStep ////////////////////////////
inline
MatStep::MatStep()
{
p = buf; p[0] = p[1] = 0;
}
inline
MatStep::MatStep(size_t s)
{
p = buf; p[0] = s; p[1] = 0;
}
inline
const size_t& MatStep::operator[](int i) const
{
return p[i];
}
inline
size_t& MatStep::operator[](int i)
{
return p[i];
}
inline MatStep::operator size_t() const
{
CV_DbgAssert( p == buf );
return buf[0];
}
inline MatStep& MatStep::operator = (size_t s)
{
CV_DbgAssert( p == buf );
buf[0] = s;
return *this;
}