今后开始通过《 Learning OpenCV 》 英文版和查阅 OpenCV2.1 源代码来学习OpenCV。
OpenCV是Intel®开源计算机视觉库。它由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法。
OpenCV包含以下几部分:
目前查阅的相关源代码主要是以下文件:
计划先通过学习C版的库,再学习C++版本。以下记录的是C语言版。
————————————————————————基本数据结构———————————————————————————
点 CvPoint (CvPoint2D32f,CvPoint3D32f)
typedef struct CvPoint { int x; int y; } CvPoint; CV_INLINE CvPoint cvPoint( int x, int y ) { CvPoint p; p.x = x; p.y = y; return p; } typedef struct CvPoint2D32f { float x; float y; } CvPoint2D32f; CV_INLINE CvPoint2D32f cvPoint2D32f( double x, double y ) { CvPoint2D32f p; p.x = (float)x; p.y = (float)y; return p; } CV_INLINE CvPoint2D32f cvPointTo32f( CvPoint point ) { return cvPoint2D32f( (float)point.x, (float)point.y ); } CV_INLINE CvPoint cvPointFrom32f( CvPoint2D32f point ) { CvPoint ipt; ipt.x = cvRound(point.x); ipt.y = cvRound(point.y); return ipt; } typedef struct CvPoint3D32f { float x; float y; float z; } CvPoint3D32f; CV_INLINE CvPoint3D32f cvPoint3D32f( double x, double y, double z ) { CvPoint3D32f p; p.x = (float)x; p.y = (float)y; p.z = (float)z; return p; } typedef struct CvPoint2D64f { double x; double y; } CvPoint2D64f; CV_INLINE CvPoint2D64f cvPoint2D64f( double x, double y ) { CvPoint2D64f p; p.x = x; p.y = y; return p; } typedef struct CvPoint3D64f { double x; double y; double z; } CvPoint3D64f; CV_INLINE CvPoint3D64f cvPoint3D64f( double x, double y, double z ) { CvPoint3D64f p; p.x = x; p.y = y; p.z = z; return p; }
数据类型 内容 介绍
CvPoint int x, y 图像上的点
CvPoint2D32f float x, y 2维的点
CvPoint3D32f float x, y, z 3维的点
CvSize 与CvPoint相似,只不过数据成员为width和height。
typedef struct { int width; int height; } CvSize; CV_INLINE CvSize cvSize( int width, int height ) { CvSize s; s.width = width; s.height = height; return s; } typedef struct CvSize2D32f { float width; float height; } CvSize2D32f; CV_INLINE CvSize2D32f cvSize2D32f( double width, double height ) { CvSize2D32f s; s.width = (float)width; s.height = (float)height; return s; }
接下来是CvRect,它是CvPoint与CvSize的结合体,包含x,y,width,height。
typedef struct CvRect { int x; int y; int width; int height; } CvRect; CV_INLINE CvRect cvRect( int x, int y, int width, int height ) { CvRect r; r.x = x; r.y = y; r.width = width; r.height = height; return r; }
下面是CvScalar:通常可以用CvScalar来作为存储小于等于4个数值的结构体。
它有三个构造函数. 第一个是cvScalar(), 通过一个,两个,三个或四个参数来初始化val[];第二个是cvRealScalar(),它只有一个参数,把val[0]初始化为指定的值,其余三个数初始化为0;第三个是cvScalarAll(),它有一个参数,并把4个数全部初始化为这个参数。 代码如下:
typedef struct CvScalar { double val[4]; } CvScalar; CV_INLINE CvScalar cvScalar( double val0, double val1 CV_DEFAULT(0), double val2 CV_DEFAULT(0), double val3 CV_DEFAULT(0)) { CvScalar scalar; scalar.val[0] = val0; scalar.val[1] = val1; scalar.val[2] = val2; scalar.val[3] = val3; return scalar; } CV_INLINE CvScalar cvRealScalar( double val0 ) { CvScalar scalar; scalar.val[0] = val0; scalar.val[1] = scalar.val[2] = scalar.val[3] = 0; return scalar; } CV_INLINE CvScalar cvScalarAll( double val0123 ) { CvScalar scalar; scalar.val[0] = val0123; scalar.val[1] = val0123; scalar.val[2] = val0123; scalar.val[3] = val0123; return scalar; }
——————————————————————————CvMat————————————————————————————
上面是一些基本的数据结构,以下记录一个非常重要的数据结构:CvMat
CvMat 派生于CvArr,而它有个子类叫IplImage。
CvArr
|
— — CvMat
|
——IplImage
在一些函数中经常会见到参数为(CvArr *)此时可以将CvMat* 或ImlImage*作为参数带入函数,将会发生动态绑定,确定进行正在操作的是哪个类。
CvMat的源代码如下:
typedef struct CvMat { int type; int step; /* for internal use only */ int* refcount; int hdr_refcount; union { uchar* ptr; short* s; int* i; float* fl; double* db; } data; #ifdef __cplusplus union { int rows; int height; }; union { int cols; int width; }; #else int rows; int cols; #endif } CvMat;
CvMat可以通过一些方法来创建。
(cxcore/cxarray.cpp)
// Creates CvMat and underlying data CV_IMPL CvMat* cvCreateMat( int height, int width, int type ) { CvMat* arr = cvCreateMatHeader( height, width, type ); cvCreateData( arr ); return arr; } // Creates CvMat header only CV_IMPL CvMat* cvCreateMatHeader( int rows, int cols, int type ) { type = CV_MAT_TYPE(type); if( rows <= 0 || cols <= 0 ) CV_Error( CV_StsBadSize, "Non-positive width or height" ); int min_step = CV_ELEM_SIZE(type)*cols; if( min_step <= 0 ) CV_Error( CV_StsUnsupportedFormat, "Invalid matrix type" ); CvMat* arr = (CvMat*)cvAlloc( sizeof(*arr)); arr->step = min_step; arr->type = CV_MAT_MAGIC_VAL | type | CV_MAT_CONT_FLAG; arr->rows = rows; arr->cols = cols; arr->data.ptr = 0; arr->refcount = 0; arr->hdr_refcount = 1; icvCheckHuge( arr ); return arr; } // Initializes CvMat header, allocated by the user CV_IMPL CvMat* cvInitMatHeader( CvMat* arr, int rows, int cols, int type, void* data, int step ) { if( !arr ) CV_Error( CV_StsNullPtr, "" ); if( (unsigned)CV_MAT_DEPTH(type) > CV_DEPTH_MAX ) CV_Error( CV_BadNumChannels, "" ); if( rows <= 0 || cols <= 0 ) CV_Error( CV_StsBadSize, "Non-positive cols or rows" ); type = CV_MAT_TYPE( type ); arr->type = type | CV_MAT_MAGIC_VAL; arr->rows = rows; arr->cols = cols; arr->data.ptr = (uchar*)data; arr->refcount = 0; arr->hdr_refcount = 0; int pix_size = CV_ELEM_SIZE(type); int min_step = arr->cols*pix_size; if( step != CV_AUTOSTEP && step != 0 ) { if( step < min_step ) CV_Error( CV_BadStep, "" ); arr->step = step; } else { arr->step = min_step; } arr->type = CV_MAT_MAGIC_VAL | type | (arr->rows == 1 || arr->step == min_step ? CV_MAT_CONT_FLAG : 0); icvCheckHuge( arr ); return arr; } // Deallocates the CvMat structure and underlying data CV_IMPL void cvReleaseMat( CvMat** array ) { if( !array ) CV_Error( CV_HeaderIsNull, "" ); if( *array ) { CvMat* arr = *array; if( !CV_IS_MAT_HDR_Z(arr) && !CV_IS_MATND_HDR(arr) ) CV_Error( CV_StsBadFlag, "" ); *array = 0; cvDecRefData( arr ); cvFree( &arr ); } } // Creates a copy of matrix CV_IMPL CvMat* cvCloneMat( const CvMat* src ) { if( !CV_IS_MAT_HDR( src )) CV_Error( CV_StsBadArg, "Bad CvMat header" ); CvMat* dst = cvCreateMatHeader( src->rows, src->cols, src->type ); if( src->data.ptr ) { cvCreateData( dst ); cvCopy( src, dst ); } return dst; }
需要注意的是 cvMat()函数并没有创建数据,所以cvMat()函数要和cvCreateData()函数一起使用,或者用cvCreateMat()来代替以上两个函数。见以下代码:
/* Inline constructor. No data is allocated internally!!! * (Use together with cvCreateData, or use cvCreateMat instead to * get a matrix with allocated data): */ CV_INLINE CvMat cvMat( int rows, int cols, int type, void* data CV_DEFAULT(NULL)) { CvMat m; assert( (unsigned)CV_MAT_DEPTH(type) <= CV_64F ); type = CV_MAT_TYPE(type); m.type = CV_MAT_MAGIC_VAL | CV_MAT_CONT_FLAG | type; m.cols = cols; m.rows = rows; m.step = m.cols*CV_ELEM_SIZE(type); m.data.ptr = (uchar*)data; m.refcount = NULL; m.hdr_refcount = 0; return m; }
下面举例说明如何用cvInitMatHeader()来为已经存在的矩阵初始化矩阵头:
float vals[] = { 0.866025, -0.500000, 0.500000, 0.866025 }; CvMat rotmat; cvInitMatHeader( &rotmat, 2, 2, CV_32FC1, vals );
好了,矩阵建立好了,那么我们怎么提取矩阵的信息呢?
有如下函数:
cvGetElemType( const CvArr* arr ) 获得矩阵的type
cvGetDims( const CvArr* arr, int* sizes=NULL ) 获得矩阵的维数
cvGetDimSize( const CvArr* arr, int index ). 获得矩阵相应坐标的Size
源代码中是这么注释的:
/* Returns type of array elements: CV_8UC1 ... CV_64FC4 ... */ CVAPI(int) cvGetElemType( const CvArr* arr ); /* Retrieves number of an array dimensions and optionally sizes of the dimensions */ CVAPI(int) cvGetDims( const CvArr* arr, int* sizes CV_DEFAULT(NULL) ); /* Retrieves size of a particular array dimension. For 2d arrays cvGetDimSize(arr,0) returns number of rows (image height) and cvGetDimSize(arr,1) returns number of columns (image width) */ CVAPI(int) cvGetDimSize( const CvArr* arr, int index );
我们可以通过宏定义CV_MAT_ELEM_PTR()和CV_MAT_ELEM().来得到矩阵相应位置的指针和存储的数据。
下面是宏定义的源代码:
#define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size ) / (assert( (unsigned)(row) < (unsigned)(mat).rows && / (unsigned)(col) < (unsigned)(mat).cols ), / (mat).data.ptr + (size_t)(mat).step*(row) + (pix_size)*(col)) #define CV_MAT_ELEM_PTR( mat, row, col ) / CV_MAT_ELEM_PTR_FAST( mat, row, col, CV_ELEM_SIZE((mat).type) ) #define CV_MAT_ELEM( mat, elemtype, row, col ) / (*(elemtype*)CV_MAT_ELEM_PTR_FAST( mat, row, col, sizeof(elemtype)))
可以看到,通过输入相应的行,列以及数据类型,就可以得到相应的指针和数据,非常的方便,非常Easy。
例如:
CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 ); float element_3_2 = 7.7; *( (float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2;
我们可以通过相应的函数来对高维矩阵进行操作。
/* ptr = &arr(idx0,idx1,...). All indexes are zero-based, the major dimensions go first (e.g. (y,x) for 2D, (z,y,x) for 3D */ CVAPI(uchar*) cvPtr1D( const CvArr* arr, int idx0, int* type CV_DEFAULT(NULL)); CVAPI(uchar*) cvPtr2D( const CvArr* arr, int idx0, int idx1, int* type CV_DEFAULT(NULL) ); CVAPI(uchar*) cvPtr3D( const CvArr* arr, int idx0, int idx1, int idx2, int* type CV_DEFAULT(NULL)); /* For CvMat or IplImage number of indices should be 2 (row index (y) goes first, column index (x) goes next). For CvMatND or CvSparseMat number of infices should match number of
用cvPtr*D来访问数据的一个重要的原因是,我们得到了一个指针,可以通过算术运算来移动指针去访问相邻的地址或指定的位置。但需要注意的是各个通道的物理存储是相邻的,例如一个三通道(RGB)二维矩阵,它每行的存储为:rgbrgbrgb..。这样的话,如果想访问相邻通道,那么指针+1,如果想访问相邻像素点,那么指针需要+3。