OpenCV 学习笔记 [1] 基本数据结构, CvMat

    今后开始通过《 Learning OpenCV 》 英文版和查阅 OpenCV2.1 源代码来学习OpenCV。

    OpenCV是Intel®开源计算机视觉库。它由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法。

    OpenCV包含以下几部分:

  • CV                                        图像处理和视觉算法
  • CXCORE                               基本数据结构和算法 绘图函数
  • HIGHGUI                               GUI,图像视频输入输出相关
  • Machine Learning(ML)         机器学习相关             //ML!=Make Love
  • CVAUX                                 一些即将被淘汰的算法和函数

    目前查阅的相关源代码主要是以下文件:

  • cxtypes.h     定义了C语言数据结构
  • cxcore.h       声明了与数据结构相关的基本算法和绘图函数
  • cxcore.hpp   定义了C++数据结构的类
  • cxmat.hpp    与Mat类相关的函数
  • cxoperations.hpp    定义C++其他数据结构的操作

    计划先通过学习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 );  

 

  • 访问CvMat数据

 

我们可以通过宏定义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 and indices order should match the array dimension order. */ CVAPI(uchar*) cvPtrND( const CvArr* arr, const int* idx, int* type CV_DEFAULT(NULL), int create_node CV_DEFAULT(1), unsigned* precalc_hashval CV_DEFAULT(NULL)); /* value = arr(idx0,idx1,...) */ CVAPI(CvScalar) cvGet1D( const CvArr* arr, int idx0 ); CVAPI(CvScalar) cvGet2D( const CvArr* arr, int idx0, int idx1 ); CVAPI(CvScalar) cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 ); CVAPI(CvScalar) cvGetND( const CvArr* arr, const int* idx ); /* for 1-channel arrays */ CVAPI(double) cvGetReal1D( const CvArr* arr, int idx0 ); CVAPI(double) cvGetReal2D( const CvArr* arr, int idx0, int idx1 ); CVAPI(double) cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 ); CVAPI(double) cvGetRealND( const CvArr* arr, const int* idx ); /* arr(idx0,idx1,...) = value */ CVAPI(void) cvSet1D( CvArr* arr, int idx0, CvScalar value ); CVAPI(void) cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value ); CVAPI(void) cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value ); CVAPI(void) cvSetND( CvArr* arr, const int* idx, CvScalar value ); /* for 1-channel arrays */ CVAPI(void) cvSetReal1D( CvArr* arr, int idx0, double value ); CVAPI(void) cvSetReal2D( CvArr* arr, int idx0, int idx1, double value ); CVAPI(void) cvSetReal3D( CvArr* arr, int idx0, int idx1, int idx2, double value ); CVAPI(void) cvSetRealND( CvArr* arr, const int* idx, double value ); /* clears element of ND dense array, in case of sparse arrays it deletes the specified node */ CVAPI(void) cvClearND( CvArr* arr, const int* idx ); 

 

      用cvPtr*D来访问数据的一个重要的原因是,我们得到了一个指针,可以通过算术运算来移动指针去访问相邻的地址或指定的位置。但需要注意的是各个通道的物理存储是相邻的,例如一个三通道(RGB)二维矩阵,它每行的存储为:rgbrgbrgb..。这样的话,如果想访问相邻通道,那么指针+1,如果想访问相邻像素点,那么指针需要+3。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(OpenCV)