转自:http://fsa.ia.ac.cn/opencv-doc-cn/opencv-doc-cn-0.9.7/ref/opencvref_cxcore.cn.htm
HUNNISH 注:
本翻译是直接根据 OpenCV Beta 4.0 版本的用户手册翻译的,原文件是:
翻译中肯定有不少错误,另外也有些术语和原文语义理解不透导致翻译不准确或者错误,也请有心人赐教。
翻译由Y.Q.Zhang, J.H.Tan, X.C.Qin, M.Liu 和 Z.T.Fan 鼎力完成,全文由Hunnish做统一修改校正。
翻译由张兆翔,于示琪进一步校正完成。
基于二维整形坐标轴的点
typedef struct CvPoint
{
int x; /* X坐标, 通常以0为基点 */
int y; /* y坐标,通常以0为基点 */
}
CvPoint;
/* 构造函数 */
inline CvPoint cvPoint( int x, int y );
/* 从 CvPoint2D32f类型转换得来 */
inline CvPoint cvPointFrom32f( CvPoint2D32f point );
二维浮点坐标上的点
typedef struct CvPoint2D32f
{
float x; /* X坐标, 通常以0为基点*/
float y; /* Y坐标, 通常以0为基点*/
}
CvPoint2D32f;
/* 构造函数 */
inline CvPoint2D32f cvPoint2D32f( double x, double y );
/* 从CvPoint转换来 */
inline CvPoint2D32f cvPointTo32f( CvPoint point );
三维浮点坐标上的点
typedef struct CvPoint3D32f { float x; /* x-坐标,通常基于0 */ float y; /* y-坐标, 通常基于0*/ float z; /* z-坐标,通常基于0 */ } CvPoint3D32f; /* 构造函数 */ inline CvPoint3D32f cvPoint3D32f( double x, double y, double z );
2D point with double precision floating-point coordinates
typedef struct CvPoint2D64f { double x; /* x-坐标,通常基于0 */ double y; /* y-坐标,通常基于0 */ } CvPoint2D64f; /* 构造函数 */ inline CvPoint2D64f cvPoint2D64f( double x, double y ); /* 从 CvPoint转换得来 */ inline CvPoint2D64f cvPointTo64f( CvPoint point );
3D point with double precision floating-point coordinates
typedef struct CvPoint3D64f { double x; /* x-坐标,通常基于0 */ double y; /* y-坐标,通常基于0 */ double z; /* z-坐标,通常基于0 */ } CvPoint3D64f; /* 构造函数 */ inline CvPoint3D64f cvPoint3D64f( double x, double y, double z );
矩形框大小,以像素为精度
typedef struct CvSize { int width; /* 矩形宽 */ int height; /* 矩形高 */ } CvSize; /* 构造函数 */ inline CvSize cvSize( int width, int height );
以低像素精度标量矩形框大小
typedef struct CvSize2D32f { float width; /* 矩形宽 */ float height; /* 矩形高 */ } CvSize2D32f; /* 构造函数*/ inline CvSize2D32f cvSize2D32f( double width, double height );
矩形框的偏移和大小
typedef struct CvRect { int x; /* 方形的最左角的x-坐标 */
int y; /* 方形的最上或者最下角的y-坐标 */
int width; /* 宽 */ int height; /* 高 */ } CvRect; /* 构造函数*/ inline CvRect cvRect( int x, int y, int width, int height );
可存放在1-,2-,3-,4-TUPLE类型的捆绑数据的容器
typedef struct CvScalar { double val[4] } CvScalar; /* 构造函数:用val0初始化val[0]用val1初始化val[1]等等*/ inline CvScalar cvScalar( double val0, double val1=0, double val2=0, double val3=0 ); /* 构造函数:用val0123初始化val0123 */ inline CvScalar cvScalarAll( double val0123 ); /* 构造函数:用val0初始化val[0],val[1]…val[3]用0初始化 */ inline CvScalar cvRealScalar( double val0 );
迭代算法的终止标准
#define CV_TERMCRIT_ITER 1 #define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER #define CV_TERMCRIT_EPS 2 typedef struct CvTermCriteria { int type; /* CV_TERMCRIT_ITER 和 CV_TERMCRIT_EPS的联合 */ int max_iter; /* 迭代的最大数 */ double epsilon; /* 结果的精确性 */ } CvTermCriteria; /* 构造函数 */ inline CvTermCriteria cvTermCriteria( int type, int max_iter, double epsilon ); /* 检查终止标准并且转换使type=CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,并且满足max_iter 和 epsilon 限制条件 */ CvTermCriteria cvCheckTermCriteria( CvTermCriteria criteria, double default_eps, int default_max_iters );
多通道矩阵
typedef struct CvMat { int type; /* CvMat 标识 (CV_MAT_MAGIC_VAL), 元素类型和标记 */ int step; /* 以字节为单位的行数据长度*/ int* refcount; /* 数据参考计数 */ union { uchar* ptr; short* s; int* i; float* fl; double* db; } data; /* data 指针 */ #ifdef __cplusplus union { int rows; int height; }; union { int cols; int width; }; #else int rows; /* 行数 */ int cols; /* 列数*/ #endif } CvMat;
多维、多通道密集数组
typedef struct CvMatND { int type; /* CvMatND 标识(CV_MATND_MAGIC_VAL), 元素类型和标号*/ int dims; /* 数组维数 */ int* refcount; /* 数据参考计数 */ union { uchar* ptr; short* s; int* i; float* fl; double* db; } data; /* data 指针*/ /* 每维的数据结构 (元素号,以字节为单位的元素之间的距离)是配套定义的 */ struct { int size; int step; } dim[CV_MAX_DIM]; } CvMatND;
多维、多通道稀疏数组
typedef struct CvSparseMat { int type; /* CvSparseMat 标识 (CV_SPARSE_MAT_MAGIC_VAL), 元素类型和标号 */ int dims; /* 维数 */ int* refcount; /* 参考数量 - 未用 */ struct CvSet* heap; /* HASH表节点池 */ void** hashtable; /* HASH表:每个入口有一个节点列表,有相同的 "以HASH大小为模板的HASH值" */ int hashsize; /* HASH表大小 */ int total; /* 稀疏数组的节点数 */ int valoffset; /* 数组节点值在字节中的偏移 */ int idxoffset; /* 数组节点索引在字节中的偏移 */ int size[CV_MAX_DIM]; /*维大小 */ } CvSparseMat;
IPL 图像头
typedef struct _IplImage { int nSize; /* IplImage大小 */ int ID; /* 版本 (=0)*/ int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */ int alphaChannel; /* 被OpenCV忽略 */ int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U, IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */ char colorModel[4]; /* 被OpenCV忽略 */ char channelSeq[4]; /* 同上 */ int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道. 只有cvCreateImage可以创建交叉存取图像 */ int origin; /* 0 - 顶—左结构, 1 - 底—左结构 (Windows bitmaps 风格) */ int align; /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */ int width; /* 图像宽像素数 */ int height; /* 图像高像素数*/ struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */ struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */ void *imageId; /* 同上*/ struct _IplTileInfo *tileInfo; /*同上*/ int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/ char *imageData; /* 指向排列的图像数据 */ int widthStep; /* 排列的图像行大小,以字节为单位 */ int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */ int BorderConst[4]; /* 同上 */ char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */ } IplImage;
IplImage结构来自于
Intel Image Processing Library(是其本身所具有的). OpenCV 只支持其中的一个子集:
alpha通道在
OpenCV中被忽略. colorModel
和channelSeq
被OpenCV忽略. OpenCV颜色转换的 唯一个函数 cvCvtColor把原图像的颜色空间的目标图像的颜色空间作为一个参数. 数据顺序
必须是IPL_DATA_ORDER_PIXEL (颜色通道是交叉存取), 然面平面图像的被选择通道可以被处理,就像COI(感兴趣的通道)被设置过一样. widthStep
被用于去接近图像行序列,排列是被OpenCV忽略的. 不支持maskROI
. 处理MASK的函数把他当作一个分离的参数. MASK在 OpenCV 里是 8-bit, 然而在 IPL他是 1-bit. 边际模式和边际常量是不支持的
. 每个 OpenCV 函数处理像素的邻近的像素,通常使用单一的固定代码边际模式. 除了上述限制, OpenCV处理ROI有不同的要求.要求原图像和目标图像的尺寸或 ROI的尺寸必须(根据不同的作操,例如cvPyrDown 目标图像的宽(高)必须等于原图像的宽(高)除2 ±1)精确匹配,而IPL处理交叉区域,如图像的大小或ROI大小可能是完全独立的。
不确定数组
typedef void CvArr;
CvArr* 仅仅是被用于作函数的参数,用于指示函数接收的数组类型可以不止一个,如 IplImage*, CvMat* 甚至 CvSeq*. 最终的数组类型是在运行时通过分析数组头的前4 个字节判断。
创建头并分配数据
IplImage* cvCreateImage( CvSize size, int depth, int channels );
函数 cvCreateImage 创建头并分配数据,这个函数是下列的缩写型式
header = cvCreateImageHeader(size,depth,channels); cvCreateData(header);
分配,初始化,并且返回 IplImage结构
IplImage* cvCreateImageHeader( CvSize size, int depth, int channels );
函数 cvCreateImageHeader 分配, 初始化, 并且返回 IplImage结构
. 这个函数相似于:
iplCreateImageHeader( channels, 0, depth, channels == 1 ? "GRAY" : "RGB", channels == 1 ? "GRAY" : channels == 3 ? "BGR" : channels == 4 ? "BGRA" : "", IPL_DATA_ORDER_PIXEL, IPL_ORIGIN_TL, 4, size.width, size.height, 0,0,0,0);
然而IPL函数不是作为默认的 (见 CV_TURN_ON_IPL_COMPATIBILITY
宏)
释放头
void cvReleaseImageHeader( IplImage** image );
函数 cvReleaseImageHeader 释放头. 相似于
if( image ) { iplDeallocate( *image, IPL_IMAGE_HEADER | IPL_IMAGE_ROI ); *image = 0; }
然而IPL函数不是作为默认的 (见 CV_TURN_ON_IPL_COMPATIBILITY
宏)
释放头和图像数据
void cvReleaseImage( IplImage** image );
函数 cvReleaseImage 释放头和图像数据,相似于:
if( *image ) { cvReleaseData( *image ); cvReleaseImageHeader( image ); }
初始他被用图分配的图像头
IplImage* cvInitImageHeader( IplImage* image, CvSize size, int depth, int channels, int origin=0, int align=4 );
IPL_ORIGIN_TL
或 IPL_ORIGIN_BL
.
函数 cvInitImageHeader 初始他图像头结构, 指向用户指定的图像并且返回这个指针。
制作图像的完整拷贝
IplImage* cvCloneImage( const IplImage* image );
函数 cvCloneImage 制作图像的完整拷贝包括头、ROI和数据
基于给定的值设置感兴趣通道
void cvSetImageCOI( IplImage* image, int coi );
函数 cvSetImageCOI 基于给定的值设置感兴趣的通道。值 0 意味着所有的通道都被选定, 1 意味着第一个通道被选定等等。如果 ROI 是 NULL
并且COI!= 0
, ROI 被分配. 然而大多数的 OpenCV 函数不支持 COI, 对于这种状况当处理分离图像/矩阵通道时,可以拷贝(通过 cvCopy 号 cvSplit) 通道来分离图像/矩阵,处理后如果需要可再拷贝(通过cvCopy 或 cvCvtPlaneToPix)回来.
返回感兴趣通道号
int cvGetImageCOI( const IplImage* image );
函数cvGetImageCOI 返回图像的感兴趣通道(当所有的通道都被选中返回值是0).
基于给定的矩形设置感兴趣区域
void cvSetImageROI( IplImage* image, CvRect rect );
函数 cvSetImageROI 基于给定的矩形设置图像的 ROI(感兴趣区域) . 如果ROI是NULL
并且参数RECT的值不等于整个图像, ROI被分配. 不像 COI, 大多数的 OpenCV 函数支持 ROI 并且处理它就行它是一个分离的图像 (例如, 所有的像素坐标从ROI的顶-左或底-左角(基于图像的结构)计算。
释放图偈的ROI
void cvResetImageROI( IplImage* image );
函数 cvResetImageROI 释放图像 ROI. 释放之后整个图像被认为是全部被选中的。相似的结果可以通过下述办法
cvSetImageROI( image, cvRect( 0, 0, image->width, image->height )); cvSetImageCOI( image, 0 );
但是后者的变量不分配 image->roi
.
返回图像的 ROI 坐标
CvRect cvGetImageROI( const IplImage* image );
函数 cvGetImageROI 返回图像ROI 坐标. 如果没有ROI则返回矩形值为 cvRect(0,0,image->width,image->height)
创建矩阵
CvMat* cvCreateMat( int rows, int cols, int type );
CV_(S|U|F)C型式描述
, 例如:
CV_8UC1
意思是一个8-bit 无符号单通道矩阵, CV_32SC2
意思是一个32-bit 有符号二个通道的矩阵.
函数 cvCreateMat 为新的矩阵分配头和下面的数据, 并且返回一个指向新创建的矩阵的指针. 是下列操作的缩写型式:
CvMat* mat = cvCreateMatHeader( rows, cols, type ); cvCreateData( mat );
矩阵按行存贮. 所有的行以4个字节排列。
创建新的矩阵头
CvMat* cvCreateMatHeader( int rows, int cols, int type );
函数 cvCreateMatHeader 分配新的矩阵头并且返回指向它的指针. 矩阵数据可被进一步的分配,使用cvCreateData 或通过 cvSetData明确的分配数据.
删除矩阵
void cvReleaseMat( CvMat** mat );
函数cvReleaseMat 缩减矩阵数据参考计数并且释放矩阵头 :
if( *mat ) cvDecRefData( *mat ); cvFree( (void**)mat );
初始化矩阵头
CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type, void* data=NULL, int step=CV_AUTOSTEP );
函数 cvInitMatHeader 初始化已经分配了的 CvMat 结构. 它可以被OpenCV矩阵函数用于处理原始数据。
例如, 下面的代码计算通用数组格式存贮的数据的矩阵乘积.
计算两个矩阵的积
double a[] = { 1, 2, 3, 4 5, 6, 7, 8, 9, 10, 11, 12 }; double b[] = { 1, 5, 9, 2, 6, 10, 3, 7, 11, 4, 8, 12 }; double c[9]; CvMat Ma, Mb, Mc ; cvInitMatHeader( &Ma, 3, 4, CV_64FC1, a ); cvInitMatHeader( &Mb, 4, 3, CV_64FC1, b ); cvInitMatHeader( &Mc, 3, 3, CV_64FC1, c ); cvMatMulAdd( &Ma, &Mb, 0, &Mc ); // c 数组存贮 a(3x4) 和 b(4x3) 矩阵的积
初始化矩阵的头(轻磅变量)
CvMat cvMat( int rows, int cols, int type, void* data=NULL );
函数 cvMat 是个一快速内连函数,替代函数 cvInitMatHeader. 也就是说他相当于:
CvMat mat; cvInitMatHeader( &mat, rows, cols, type, data, CV_AUTOSTEP );
创建矩阵拷贝
CvMat* cvCloneMat( const CvMat* mat );
函数 cvCloneMat 创建输入矩阵的一个拷贝并且返回 该矩阵的指针.
创建多维密集数组
CvMatND* cvCreateMatND( int dims, const int* sizes, int type );
函数cvCreateMatND 分配头给多维密集数组并且分配下面的数据,返回指向被创建数组的指针 . 是下列的缩减形式:
CvMatND* mat = cvCreateMatNDHeader( dims, sizes, type ); cvCreateData( mat );
矩阵按行存贮. 所有的行以4个字节排列。.
创建新的数组头
CvMatND* cvCreateMatNDHeader( int dims, const int* sizes, int type );
函数cvCreateMatND 分配头给多维密集数组。数组数据可以用 cvCreateData 进一步的被分配或利用cvSetData由用户明确指定.
删除多维数组
void cvReleaseMatND( CvMatND** mat );
函数 cvReleaseMatND 缩减数组参考计数并释放数组头:
if( *mat ) cvDecRefData( *mat ); cvFree( (void**)mat );
初始化多维数组头
CvMatND* cvInitMatNDHeader( CvMatND* mat, int dims, const int* sizes, int type, void* data=NULL );
函数 cvInitMatNDHeader 初始化 用户指派的CvMatND 结构.
创建多维数组的完整拷贝
CvMatND* cvCloneMatND( const CvMatND* mat );
函数 cvCloneMatND 创建输入数组的拷贝并返回指针.
缩减数组数据的参考计数
void cvDecRefData( CvArr* arr );
函数 cvDecRefData 缩减CvMat 或CvMatND 数据的参考计数,如参考计数指针非NULL并且计数到0就删除数据,在当前的执行中只有当数据是用cvCreateData 分配的参考计算才会是非NULL,换句话说 :
使用cvSetData指派外部数据给头
矩阵头代表部分大的矩阵或图像
矩阵头是从图像头或N维矩阵头转换过来的。
参考计数如果被设置成NULL就不会被缩减. 无论数据是否被删除,数据指针和参考计数指针都将被这个函数清空。
增加数组数据的参考计数
int cvIncRefData( CvArr* arr );
函数 cvIncRefData 增加 CvMat 或 CvMatND 数据参考计数,如果参考计数非空返回新的计数值 否则返回0。
分配数组数据
void cvCreateData( CvArr* arr );
函数 cvCreateData 分配图像,矩阵或多维数组数据. 对于矩阵类型使用OpenCV的分配函数,对于 IplImage类型如果CV_TURN_ON_IPL_COMPATIBILITY没有被调用也是可以使用这种方法的反之使用
IPL 函数分配数据
释放数组数据
void cvReleaseData( CvArr* arr );
函数cvReleaseData 释放数组数据. 对于 CvMat 或 CvMatND 结构只需调用 cvDecRefData(), 也就是说这个函数不能删除外部数据。见 cvCreateData.
指派用户数据给数组头
void cvSetData( CvArr* arr, void* data, int step );
函数cvSetData 指派用记数据给数组头. 头应该已经使用 cvCreate*Header, cvInit*Header 或 cvMat (对于矩阵)初始化过.
返回组数的底层信息
void cvGetRawData( const CvArr* arr, uchar** data, int* step=NULL, CvSize* roi_size=NULL );
函数 cvGetRawData 添充给输出变量数组的底层信息。所有的输出参数是可选的 , 因此这些指针可设为NULL. 如果数组是设置了ROI的 IplImage
结构, ROI参数被返回。
接下来的例子展示怎样去接近数组元素。
使用 GetRawData 计算单通道浮点数组的元素绝对值.
float* data; int step; CvSize size; int x, y; cvGetRawData( array, (uchar**)&data, &step, &size ); step /= sizeof(data[0]); for( y = 0; y < size.height; y++, data += step ) for( x = 0; x < size.width; x++ ) data[x] = (float)fabs(data[x]);
从不确定数组返回矩阵头
CvMat* cvGetMat( const CvArr* arr, CvMat* header, int* coi=NULL, int allowND=0 );
函数 cvGetMat从输入的数组生成矩阵头,输入的数组可以是 - CvMat结构, IplImage结构
或多维密集数组 CvMatND* (后者只有当 allowND != 0时才可以使用
) . 如果是矩阵函数只是返回指向矩阵的指针.如果是 IplImage*
或 CvMatND* 函数用当前图像的ROI初始化头结构并且返回指向这个临时结构的指针。因为CvMat不支持COI,所以他们的返回结果是不同的.
这个函数提供了一个简单的方法,用同一代码处理 IplImage
和 CvMat二种数据类型。这个函数的反向转换可以用 cvGetImage将 CvMat 转换成 IplImage
.
输入的数组必须有已分配好的底层数据或附加的数据,否则该函数将调用失败
如果输入的数组是IplImage
格式,使用平面式数据编排并设置了COI,函数返回的指针指向被选定的平面并设置COI=0.利用OPENCV函数对于多通道平面编排图像可以处理每个平面。
从不确定数组返回图像头
IplImage* cvGetImage( const CvArr* arr, IplImage* image_header );
IplImage结构的指针,该结构存贮在一个临时缓存
.
函数 cvGetImage 从输出数组获得图头,该数组可是以矩阵- CvMat*, 或图像 - IplImage*
. 如果是图像函数只是返回输入参数的指针,如是查 CvMat* 函数用输入参数矩阵初始化图像头。因此如果我们把 IplImage
转换成 CvMat 然后再转换 CvMat 回 IplImage,如果ROI被设置过了我们可能会获得不同的头,这样一些计算图像跨度的IPL函数就会失败。
创建稀疏数组
CvSparseMat* cvCreateSparseMat( int dims, const int* sizes, int type );
函数 cvCreateSparseMat 分配多维稀疏数组. Initially the array contain no elements, that is cvGet*D or cvGetReal*D return zero for every index
删除稀疏数组
void cvReleaseSparseMat( CvSparseMat** mat );
函数 cvReleaseSparseMat释放稀疏数组并清空数组指针
创建稀疏数组的拷贝
CvSparseMat* cvCloneSparseMat( const CvSparseMat* mat );
函数 cvCloneSparseMat 创建输入数组的拷贝并返回指向这个拷贝的指针.
根据输入的图像或矩阵的矩形数组子集返回矩阵头
CvMat* cvGetSubRect( const CvArr* arr, CvMat* submat, CvRect rect );
函数 cvGetSubRect 根据指定的数组矩形返回矩阵头,换句话说,函数允许处理输入数组的指定的一个子矩形,就像一个独立的数组一样进行处理。函数在处理时要考虑进输入数组的ROI,因此数组的ROI是实际上被提取的。
返回数组的行或在一定跨度内的行
CvMat* cvGetRow( const CvArr* arr, CvMat* submat, int row ); CvMat* cvGetRows( const CvArr* arr, CvMat* submat, int start_row, int end_row, int delta_row=1 );
delta_row行提取一行。
函数GetRow
和 GetRows
根据指定的行或跨度从输入数组中返回对应的头。 GetRow
是 cvGetRows的缩写:
cvGetRow( arr, submat, row ) ~ cvGetRows( arr, submat, row, row + 1, 1 );
返回数组的列或一定跨度内的列
CvMat* cvGetCol( const CvArr* arr, CvMat* submat, int col ); CvMat* cvGetCols( const CvArr* arr, CvMat* submat, int start_col, int end_col );
函数 GetCol
和 GetCols
根据指定的列/列跨度返回头。GetCol
是 cvGetCols的缩写形式:
cvGetCol( arr, submat, col ); // ~ cvGetCols( arr, submat, col, col + 1 );
返回一个数组对角线
CvMat* cvGetDiag( const CvArr* arr, CvMat* submat, int diag=0 );
函数 cvGetDiag 根据指定的diag参数返回数组的对角线头。
返回图像或矩阵ROI大小
CvSize cvGetSize( const CvArr* arr );
函数 cvGetSize 返回矩阵或图像的行数和列数,如果是图像就返回ROI的大小
初始化稀疏数线元素迭代器
CvSparseNode* cvInitSparseMatIterator( const CvSparseMat* mat, CvSparseMatIterator* mat_iterator );
函数 cvInitSparseMatIterator 初始化稀疏数组元素的迭代器并且返回指向第一个元素的指针,如果数组为空则返回NULL。
初始化稀疏数线元素迭代器
CvSparseNode* cvGetNextSparseNode( CvSparseMatIterator* mat_iterator );
函数cvGetNextSparseNode 移动迭代器到下一个稀疏矩阵元素并返回指向他的指针。在当前的版本不存在任何元素的特殊顺序,因为元素是按HASH表存贮的下面的列子描述怎样在稀疏矩阵上迭代 :
利用cvInitSparseMatIterator 和cvGetNextSparseNode 计算浮点稀疏数组的和。
double sum; int i, dims = cvGetDims( array ); CvSparseMatIterator mat_iterator; CvSparseNode* node = cvInitSparseMatIterator( array, &mat_iterator ); for( ; node != 0; node = cvGetNextSparseNode( &mat_iterator )) { int* idx = CV_NODE_IDX( array, node ); /* get pointer to the element indices */ float val = *(float*)CV_NODE_VAL( array, node ); /* get value of the element (assume that the type is CV_32FC1) */ printf( "(" ); for( i = 0; i < dims; i++ ) printf( "%4d%s", idx[i], i < dims - 1 "," : "): " ); printf( "%g/n", val ); sum += val; } printf( "/nTotal sum = %g/n", sum );
返回数组元素类型
int cvGetElemType( const CvArr* arr );
函数 GetElemType
返回数组元素类型就像在cvCreateMat 中讨论的一样:
CV_8UC1 ... CV_64FC4
返回数组维数和他们的大小或者殊维的大小
int cvGetDims( const CvArr* arr, int* sizes=NULL ); int cvGetDimSize( const CvArr* arr, int index );
函数 cvGetDims 返回维数和他们的大小。如果是 IplImage
或 CvMat 总是返回2,不管图像/矩阵行数。函数 cvGetDimSize 返回特定的维大小(每维的元素数)。例如,接下来的代码使用二种方法计算数组元素总数。
// via cvGetDims() int sizes[CV_MAX_DIM]; int i, total = 1; int dims = cvGetDims( arr, size ); for( i = 0; i < dims; i++ ) total *= sizes[i]; // via cvGetDims() and cvGetDimSize() int i, total = 1; int dims = cvGetDims( arr ); for( i = 0; i < dims; i++ ) total *= cvGetDimsSize( arr, i );
返回指向特殊数组元素的指针
uchar* cvPtr1D( const CvArr* arr, int idx0, int* type=NULL ); uchar* cvPtr2D( const CvArr* arr, int idx0, int idx1, int* type=NULL ); uchar* cvPtr3D( const CvArr* arr, int idx0, int idx1, int idx2, int* type=NULL ); uchar* cvPtrND( const CvArr* arr, int* idx, int* type=NULL, int create_node=1, unsigned* precalc_hashval=NULL );
函数cvPtr*D 返回指向特殊数组元素的指针。数组维数应该与转递给函数物下标数相匹配,除了 cvPtr1D 函数,它可以被用于顺序存取的1D,2D或nD密集数组
函数也可以用于稀疏数组,并且如果被需要的节点不存在函数可以创建这个节点并设置为0
就像其它获取数组元素的函数 (cvGet[Real]*D, cvSet[Real]*D)如果元素的下标超出了范围就会产生错误
返回特殊的数组元素
CvScalar cvGet1D( const CvArr* arr, int idx0 ); CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 ); CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 ); CvScalar cvGetND( const CvArr* arr, int* idx );
函数cvGet*D 返回指定的数组元素。对于稀疏数组如果需要的节点不存在函数返回0 (不会创建新的节点)
返回单通道数组的指定元素
double cvGetReal1D( const CvArr* arr, int idx0 ); double cvGetReal2D( const CvArr* arr, int idx0, int idx1 ); double cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 ); double cvGetRealND( const CvArr* arr, int* idx );
函数cvGetReal*D 返回单通道数组的指定元素,如果数组是多通道的,就会产生运行时错误,而 cvGet*D 函数可以安全的被用于单通道和多通道数组,但他们运行时会有点慢
如果指定的点不存在对于稀疏数组点会返回0(不会创建新的节点)。
返回单通道浮点矩阵指定元素
double cvmGet( const CvMat* mat, int row, int col );
函数 cvmGet 是 cvGetReal2D对于单通道浮点矩阵的快速替代函数,函数运行比较快速因为它是内连函数 ,这个函数对于数组类型、数组元素类型的检查作的很少,并且仅在调式模式下检查数的行和列范围。
修改指定的数组
void cvSet1D( CvArr* arr, int idx0, CvScalar value ); void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value ); void cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value ); void cvSetND( CvArr* arr, int* idx, CvScalar value );
函数 cvSet*D 指定新的值给指定的数组元素。对于稀疏矩阵如果指定节点不存在函数创建新的节点
修改指定数组元素值
void cvSetReal1D( CvArr* arr, int idx0, double value ); void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value ); void cvSetReal3D( CvArr* arr, int idx0, int idx1, int idx2, double value ); void cvSetRealND( CvArr* arr, int* idx, double value );
函数 cvSetReal*D 分配新的值给单通道数组的指定元素,如果数组是多通道就会产生运行时错误。然而cvSet*D 可以安全的被用于多通道和单通道数组,只是稍微有点慢。
对于稀疏数组如果指定的节点不存在函数会创建该节点。
返回单通道浮点矩阵的指定元素
void cvmSet( CvMat* mat, int row, int col, double value );
函数cvmSet 是cvSetReal2D 快速替代,对于单通道浮点矩阵因为这个函数是内连的所以比较快,函数对于数组类型、数组元素类型的检查作的很少,并且仅在调式模式下检查数的行和列范围。
清除指定数组元素
void cvClearND( CvArr* arr, int* idx );
函数cvClearND 清除指定密集型数组的元素(置0)或删除稀疏数组的元素 ,如果元素不存在函数不作任何事
拷贝一个数组给另一个数组
void cvCopy( const CvArr* src, CvArr* dst, const CvArr* mask=NULL );
函数 cvCopy copies selected elements from input array to output array:
dst(I)=src(I) if mask(I)!=0.
If any of the passed arrays is of IplImage
type, then its ROI and COI fields are used. Both arrays must have the same type, the same number of dimensions and the same size. 函数 can also copy sparse arrays (mask is not supported in this case).
Sets every element of array to given value
void cvSet( CvArr* arr, CvScalar value, const CvArr* mask=NULL );
函数 cvSet 拷贝数量值到输出数组的每一个被除数选定的元素:
arr(I)=value if mask(I)!=0
如果数组 arr
是 IplImage
类型, 那么就会使用ROI,但 COI不能设置 .
清空数组
void cvSetZero( CvArr* arr ); #define cvZero cvSetZero
函数 cvSetZero 清空数组. 对于密集型号数组(CvMat, CvMatND or IplImage) cvZero(array) 就相当于 cvSet(array,cvScalarAll(0),0), 对于稀疏数组所有的元素都将被删除.
不拷贝数据修改矩阵/图像形状
CvMat* cvReshape( const CvArr* arr, CvMat* header, int new_cn, int new_rows=0 );
new_cn = 0
意味着不修改通道数
new_rows = 0保持原行数不修改否则根据
new_cn
值修改输出数组
函数 cvReshape 初始化 CvMat 头header 以便于让头指向修改后的形状(但数据保持原样)-也就是说修改通道数,修改行数或者两者者改变.
例如, 接下来的代码创建一个图像缓存、两个图像头,第一个是 320x240x3 图像第二个是 960x240x1 图像:
IplImage* color_img = cvCreateImage( cvSize(320,240), IPL_DEPTH_8U, 3 ); CvMat gray_mat_hdr; IplImage gray_img_hdr, *gray_img; cvReshape( color_img, &gray_mat_hdr, 1 ); gray_img = cvGetImage( &gray_mat_hdr, &gray_img_hdr );
下一个例子转换3x3 矩阵成单向量 1x9
CvMat* mat = cvCreateMat( 3, 3, CV_32F ); CvMat row_header, *row; row = cvReshape( mat, &row_header, 0, 1 );
修改多维数组形状,拷贝/不拷贝数据
CvArr* cvReshapeMatND( const CvArr* arr, int sizeof_header, CvArr* header, int new_cn, int new_dims, int* new_sizes ); #define cvReshapeND( arr, header, new_cn, new_dims, new_sizes ) / cvReshapeMatND( (arr), sizeof(*(header)), (header), / (new_cn), (new_dims), (new_sizes))
new_cn = 0
则通道数保持原样
new_dims = 0
则维数保持原样。
new_dims-1值被使用,因为要保持数组的总数一致,因此如果
new_dims = 1
, new_sizes
是不被使用的
函数cvReshapeMatND 是 cvReshape 的高级版本,它可以处理多维数组(能够呛处理通用的图像和矩阵)并且修改维数,下面的是使用cvReshapeMatND重写 cvReshape的二个例子 :
IplImage* color_img = cvCreateImage( cvSize(320,240), IPL_DEPTH_8U, 3 ); IplImage gray_img_hdr, *gray_img; gray_img = (IplImage*)cvReshapeND( color_img, &gray_img_hdr, 1, 0, 0 ); ... /*second example is modified to convert 2x2x2 array to 8x1 vector */ int size[] = { 2, 2, 2 }; CvMatND* mat = cvCreateMatND( 3, size, CV_32F ); CvMat row_header, *row; row = cvReshapeND( mat, &row_header, 0, 1, 0 );
用原数组管道式添充输出数组
void cvRepeat( const CvArr* src, CvArr* dst );
函数cvRepeat 使用被管道化的原数组添充输出数组:
dst(i,j)=src(i mod rows(src), j mod cols(src))
因此 ,输出数组可能小于输入数组
垂直,水平或即垂直又水平翻转二维数组
void cvFlip( const CvArr* src, CvArr* dst=NULL, int flip_mode=0); #define cvMirror cvFlip
dst = NULL
翻转是在内部替换.
函数cvFlip 以三种方式之一翻转数组 (行和列下标是以0为基点的):
dst(i,j)=src(rows(src)-i-1,j) if flip_mode = 0
dst(i,j)=src(i,cols(src1)-j-1) if flip_mode > 0
dst(i,j)=src(rows(src)-i-1,cols(src)-j-1) if flip_mode < 0
函数主要使用在:
分割多通道数组成几个单通道数组或者从数组中提取一个通道
void cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1, CvArr* dst2, CvArr* dst3 ); #define cvCvtPixToPlane cvSplit
函数 cvSplit 分割多通道数组成分离的单通道数组d。可获得两种操作模式 . 如果原数组有N通道且前N输出数组非NULL, 所有的通道都会被从原数组中提取,如果前N个通道只有一个通道非NULL函数只提取该指定通道,否则会产生一个错误,余下的通道(超过前N个通道的以上的)必须被设置成NULL,对于设置了COI的IplImage 结使用cvCopy 也可以从图像中提取单通道。
从几个单通道数组组合多通道数组或插入一个单通到数组
void cvMerge( const CvArr* src0, const CvArr* src1, const CvArr* src2, const CvArr* src3, CvArr* dst ); #define cvCvtPlaneToPix cvMerge
函数cvMerge 是前一个函数的反向操作。如果输出数组有N个通道并且前N个输入通道非NULL,就拷贝所有通道到输出数组,如果在前N个通道中只有一个单通道非NULL ,只拷贝这个通道到输出数组,否则 就会产生错误。除前N通道以外的余下的通道必须置NULL。对于设置了COI的 IplImage结构使用 cvCopy也可以实现向图像中插入一个通道 。
利用搜索表转换数组
void cvLUT( const CvArr* src, CvArr* dst, const CvArr* lut );
函数cvLUT 使用搜索中的值添充输出数组. 坐标入口来自于原数组,也就是说函数处理每个元素按如下方式:
dst(I)=lut[src(I)+DELTA]
这里当src的深度是CV_8U时DELTA=0
,src的深度是CV_8S
时 DELTA=128
使用线性变换转换数组
void cvConvertScale( const CvArr* src, CvArr* dst, double scale=1, double shift=0 ); #define cvCvtScale cvConvertScale #define cvScale cvConvertScale #define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 )
函数 cvConvertScale 有多个不同的目的因此就有多个意义,函数按比例从一个数组中拷贝元素到另一个元素这种操作是最先执行的,或者任意的类型转换,正如下面的操作:
dst(I)=src(I)*scale + (shift,shift,...)
多通道的数组对各个地区通道是独立处理的。
类型转换主要用于舍入和饱合度,也就是如果缩放 后的结果+转换值不能用输出数组元素类型值精确表达,就设置成最接近该数的值到真正的坐标轴上。
如果 scale=1, shift=0
就不会进行比例缩放. 这是一个特殊的优化,相当cvConvert 。如果原和输出数组的类型相同这同样也是一另一种情形,函数可以被用于比例化并且移动矩阵或图像这就变成 cvScale的同义词了。
使用线性变换转换输入数组元素成8位无符号整型
void cvConvertScaleAbs( const CvArr* src, CvArr* dst, double scale=1, double shift=0 ); #define cvCvtScaleAbs cvConvertScaleAbs
函数 cvConvertScaleAbs 与前一函数是相同的,但它是存贮变换结果的绝对值:
dst(I)=abs(src(I)*scale + (shift,shift,...))
函数只支持目标数数组的深度为 8u (8-bit 无符号) , 对于别的类型函数仿效于cvConvertScale 和 cvAbs 函数的联合
计算两个数中每个元素的和
void cvAdd( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
函数 cvAdd 加一个数组到别一个数组中:
dst(I)=src1(I)+src2(I) if mask(I)!=0
除覆盖面外所有的数组必须有相同的类型相同的大小(或ROI尺寸)。
计算数量和数组的和
void cvAddS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
函数 cvAddS 用数量值与原数组src1
的每个元素想加并存贮结果到
dst(I)=src(I)+value if mask(I)!=0
除覆盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)
计算两数组的加磅值的和
void cvAddWeighted( const CvArr* src1, double alpha, const CvArr* src2, double beta, double gamma, CvArr* dst );
函数 cvAddWeighted 计算两数组的加磅值的和:
dst(I)=src1(I)*alpha+src2(I)*beta+gamma
所有的数组必须的相同的类型相同的大小(或ROI大小)
计算两个数组每个元素的差
void cvSub( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
函数cvSub 从一个数组减去别一个数组:
dst(I)=src1(I)-src2(I) if mask(I)!=0
除覆盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)
计算数组和数量之间的差
void cvSubS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
函数 cvSubS 从原数组的每个元素中减去一个数量:
dst(I)=src(I)-value if mask(I)!=0
除覆盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)。
计算数量和数组之间的差
void cvSubRS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
函数 cvSubRS 从一个数量减去原数组的每个元素:
dst(I)=value-src(I) if mask(I)!=0
除覆盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)。
计算两个数组中每个元素的积
void cvMul( const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1 );
函数 cvMul 计算两个数组中每个元素的积:
dst(I)=scale•src1(I)•src2(I)
所有的数组必须有相同的类型和相同的大小(或ROI大小)
两个数组每个元素相除
void cvDiv( const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1 );
函数 cvDiv 用一个数组除以另一个数组:
dst(I)=scale•src1(I)/src2(I), if src1!=NULL dst(I)=scale/src2(I), if src1=NULL
所有的数组必须有相同的类型和相同的大小(或ROI大小)
计算两个数组的每个元素的按位与
void cvAnd( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
函数 cvAnd 计算两个数组的每个元素的按位逻辑与:
dst(I)=src1(I)&src2(I) if mask(I)!=0
对浮点数组按位表示操作是很有利的。除覆盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
计算数组每个元素与数量之间的按位与
void cvAndS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
函数 AndS 计算数组中每个元素与数量之量的按位与:
dst(I)=src(I)&value if mask(I)!=0
在实际操作之前首先把数量类型转换成与数组相同的类型。对浮点数组按位表示操作是很有利的。除覆盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
接下来的例子描述怎样计算浮点数组元素的绝对值,通过清除最前面的符号位:
float a[] = { -1, 2, -3, 4, -5, 6, -7, 8, -9 }; CvMat A = cvMat( 3, 3, CV_32F, &a ); int i, abs_mask = 0x7fffffff; cvAndS( &A, cvRealScalar(*(float*)&abs_mask), &A, 0 ); for( i = 0; i < 9; i++ ) printf("%.1f ", a[i] );
代码结果是:
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
计算两个数组每个元素的按位或
void cvOr( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
函数 cvOr 计算两个数组每个元素的按位或:
dst(I)=src1(I)|src2(I)
对浮点数组按位表示操作是很有利的。除覆盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
计算数组中每个元素与数量之间的按位或
void cvOrS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
函数 OrS 计算数组中每个元素和数量之间的按位或:
dst(I)=src(I)|value if mask(I)!=0
在实际操作之前首先把数量类型转换成与数组相同的类型。对浮点数组按位表示操作是很有利的。除覆盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
计算两个数组中的每个元素的按位异或
void cvXor( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
函数 cvXor 计算两个数组元素的按位异或:
dst(I)=src1(I)^src2(I) if mask(I)!=0
对浮点数组按位表示操作是很有利的。除覆盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
计算数组元素与数量之间的按位异或
void cvXorS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
函数 XorS 计算数组元素与数量之间的按位异或:
dst(I)=src(I)^value if mask(I)!=0
在实际操作之前首先把数量类型转换成与数组相同的类型。对浮点数组按位表示操作是很有利的。除覆盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
下面例子描述怎样对共轭复数向量转换,通过转换前面的符号位:
float a[] = { 1, 0, 0, 1, -1, 0, 0, -1 }; /* 1, j, -1, -j */ CvMat A = cvMat( 4, 1, CV_32FC2, &a ); int i, neg_mask = 0x80000000; cvXorS( &A, cvScalar( 0, *(float*)&neg_mask, 0, 0 ), &A, 0 ); for( i = 0; i < 4; i++ ) printf("(%.1f, %.1f) ", a[i*2], a[i*2+1] );
The code should print:
(1.0,0.0) (0.0,-1.0) (-1.0,0.0) (0.0,1.0)
计算数组元素的按位取反
void cvNot( const CvArr* src, CvArr* dst );
函数不取反每个数组元素的每一位
dst(I)=~src(I)
比较两个数组元素P
void cvCmp( const CvArr* src1, const CvArr* src2, CvArr* dst, int cmp_op );
函数 cvCmp 比较两个数组的对应元素并且添充输出数组:
dst(I)=src1(I) op src2(I),
这里 op
是 '=', '>', '>=', '<', '<=' or '!='.
如果元素之间的关系为真则设置dst(I)为
0xff (也就是所有的位都为 '1') 否则为0。除了输出数组所有数组必须是相同的类型相同的大小(或ROI大小)。
比较数组的每个元素与数量的关系
void cvCmpS( const CvArr* src, double value, CvArr* dst, int cmp_op );
函数 cvCmpS 比较数组元素与数量并且添充目标覆盖面数组:
dst(I)=src(I) op scalar,
这里 op
是 '=', '>', '>=', '<', '<=' or '!='.
如果元素之间的关系为真则设置dst(I)为
0xff (也就是所有的位都为 '1') 否则为0。所有的数组必须有相同的大小(或ROI大小)
检查数组元素是否在两个数组之间
void cvInRange( const CvArr* src, const CvArr* lower, const CvArr* upper, CvArr* dst );
函数 cvInRange 对输入的数组作范围检查:
dst(I)=lower(I)0 <= src(I)0 < upper(I)0
对于单通道数组:
dst(I)=lower(I)0 <= src(I)0 < upper(I)0 && lower(I)1 <= src(I)1 < upper(I)1
对二通道数组,以此类推
如果
src(I)
在范围内dst(I)被设置为
0xff (每一位都是 '1')否则置0 。 除了输出数组所有数组必须是相同的类型相同的大小(或ROI大小)。
检查数组元素是否在两个数量之间
void cvInRangeS( const CvArr* src, CvScalar lower, CvScalar upper, CvArr* dst );
函数 cvInRangeS 检查输入数组元素范围:
dst(I)=lower0 <= src(I)0 < upper0
对于单通道数组:
dst(I)=lower0 <= src(I)0 < upper0 && lower1 <= src(I)1 < upper1
对于双通道数组以此类推
如果
src(I)
在范围内dst(I)被设置为
0xff (每一位都是 '1')否则置0 。所有的数组必须有相同的大小(或ROI大小)
查找两个数组中每个元素的较大值
void cvMax( const CvArr* src1, const CvArr* src2, CvArr* dst );
函数 cvMax 计算两个数组中每个元素的较大值:
dst(I)=max(src1(I), src2(I))
所有的数组必须的一个单通道,相同的数据类型和相同的大小(或ROI大小)
查找数组元素与数量之间的较大值
void cvMaxS( const CvArr* src, double value, CvArr* dst );
函数 cvMaxS 计算数组元素和数量之间的较大值:
dst(I)=max(src(I), value)
所有数组必须有一个单一通道,相同的数据类型相同的大小(或ROI大小)
查找两个数组元素之间 的较小值
void cvMin( const CvArr* src1, const CvArr* src2, CvArr* dst );
函数cvMin计算两个数组元素的较大值
dst(I)=min(src1(I),src2(I))
所有数组必须有一个单一通道,相同的数据类型相同的大小(或ROI大小)
查找数组元素和数量之间的较大值
void cvMinS( const CvArr* src, double value, CvArr* dst );
函数 cvMinS 计算数组元素和数量之量的较小值:
dst(I)=min(src(I), value)
所有数组必须有一个单一通道,相同的数据类型相同的大小(或ROI大小)
计算两个数组差的绝对值
void cvAbsDiff( const CvArr* src1, const CvArr* src2, CvArr* dst );
函数 cvAbsDiff 计算两个数组差的绝对值
dst(I)c = abs(src1(I)c - src2(I)c).
所有数组必须有相同的数据类型相同的大小(或ROI大小)
计算数组元素与数量之间差的绝对值
void cvAbsDiffS( const CvArr* src, CvArr* dst, CvScalar value ); #define cvAbs(src, dst) cvAbsDiffS(src, dst, cvScalarAll(0))
函数 cvAbsDiffS 计算数组元素与数量之间差的绝对值
dst(I)c = abs(src(I)c - valuec).
所有数组必须有相同的数据类型相同的大小(或ROI大小)
计算非零数组元素
int cvCountNonZero( const CvArr* arr );
函数cvCountNonZero 返回arr中非零元素的数目:
result = sumI arr(I)!=0
当IplImage 支持ROI和COI。
计算数组元素的和
CvScalar cvSum( const CvArr* arr );
函数cvSum 独立地为每一个通道计算数组元素的和 S:
Sc = sumI arr(I)c
如果数组是IplImage类型和设置了COI, 该函数只处理选定的通道并将和存储到第一个标量成员 (S0)。
计算数组元素的平均值
CvScalar cvAvg( const CvArr* arr, const CvArr* mask=NULL );
函数 cvAvg 独立地为每一个通道计算数组元素的平均值 M
:
N = sumI mask(I)!=0 Mc = 1/N • sumI,mask(I)!=0 arr(I)c
如果数组是 IplImage
类型和设置 COI , 该函数只处理选定的通道并将和存储到第一个标量成员 (S0)。
计算数组元素的平均值
void cvAvgSdv( const CvArr* arr, CvScalar* mean, CvScalar* std_dev, const CvArr* mask=NULL );
函数 cvAvgSdv 独立地为每一个通道计算数组元素的平均值和标准差:
N = sumI mask(I)!=0 meanc = 1/N • sumI,mask(I)!=0 arr(I)c std_devc = sqrt(1/N • sumI,mask(I)!=0 (arr(I)c - Mc)2)
如果数组是 IplImage
类型和 设置了COI ,该函数只处理选定的通道并将平均值和标准差存储到第一个输出标量成员 (M0 and S0)。
查找数组和子数组的全局最小值和最大值
void cvMinMaxLoc( const CvArr* arr, double* min_val, double* max_val, CvPoint* min_loc=NULL, CvPoint* max_loc=NULL, const CvArr* mask=NULL );
函数 MinMaxLoc
查找元素中的最小值和最大值以及他们的位置。函数在整个数组、或选定的ROI区域(对IplImage)或当MASK不为NULL时指定的数组区域中,搜索极值 。如果数组不止一个通道,它就必须是设置了 COI
的 IplImage
类型。 如果是多维数组 min_loc->x
和 max_loc->x
将包含极值的原始位置信息 (线性的)。
计算数组的绝对范数, 绝对差分范数或者相对差分范数
double cvNorm( const CvArr* arr1, const CvArr* arr2=NULL, int norm_type=CV_L2, const CvArr* mask=NULL );
arr1
的绝对范数,否则计算 arr1
- arr2
的绝对范数或者相对范数。
如果 arr2
为空(NULL),函数 cvNorm 计算 arr1
的绝对范数:
norm = ||arr1||C = maxI abs(arr1(I)), 如果normType
= CV_C norm = ||arr1||L1 = sumI abs(arr1(I)), 如果normType
= CV_L1 norm = ||arr1||L2 = sqrt( sumI arr1(I)2), 如果normType
= CV_L2
如果 arr2
不为空(NULL), 该函数计算绝对差分范数或者相对差分范数:
norm = ||arr1-arr2||C = maxI abs(arr1(I)-arr2(I)), 如果normType
= CV_C norm = ||arr1-arr2||L1 = sumI abs(arr1(I)-arr2(I)), 如果normType
= CV_L1 norm = ||arr1-arr2||L2 = sqrt( sumI (arr1(I)-arr2(I))2 ), 如果normType
= CV_L2 或者 norm = ||arr1-arr2||C/||arr2||C, 如果normType
= CV_RELATIVE_C norm = ||arr1-arr2||L1/||arr2||L1, 如果normType
= CV_RELATIVE_L1 norm = ||arr1-arr2||L2/||arr2||L2, 如果normType
= CV_RELATIVE_L2
函数 Norm
返回计算所得的范数。多通道数组被视为单通道处理,因此,所有通道的结果是结合在一起的。
初始化带尺度的单位矩阵
void cvSetIdentity( CvArr* mat, CvScalar value=cvRealScalar(1) );
函数 cvSetIdentity 初始化带尺度的单位矩阵:
arr(i,j)=value 如果 i=j, 否则为 0
用欧几里得准则计算两个数组的点积
double cvDotProduct( const CvArr* src1, const CvArr* src2 );
函数 cvDotProduct 计算并返回两个数组的欧几里得点积。
src1•src2 = sumI(src1(I)*src2(I))
如果是多通道数组,所有通道的结果是累加在一起的。特别地, cvDotProduct(a,a)
,将返回 ||a||2
,这里 a
是一个复向量。 该函数可以处理多通道数组,逐行或逐层等等。
计算两个三维向量的叉积
void cvCrossProduct( const CvArr* src1, const CvArr* src2, CvArr* dst );
函数 cvCrossProduct 计算两个三维向量的差积:
dst = src1 × src2, (dst1 = src12src23 - src13src22 , dst2 = src13src21 - src11src23 , dst3 = src11src22 - src12src21).
计算一个数组缩放后与另一个数组的和
void cvScaleAdd( const CvArr* src1, CvScalar scale, const CvArr* src2, CvArr* dst ); #define cvMulAddS cvScaleAdd
函数 cvScaleAdd 计算一个数组缩放后与另一个数组的和:
dst(I)=src1(I)*scale + src2(I)
所有的数组参数必须有相同的类型和大小。
通用矩阵乘法
void cvGEMM( const CvArr* src1, const CvArr* src2, double alpha, const CvArr* src3, double beta, CvArr* dst, int tABC=0 ); #define cvMatMulAdd( src1, src2, src3, dst ) cvGEMM( src1, src2, 1, src3, 1, dst, 0 ) #define cvMatMul( src1, src2, dst ) cvMatMulAdd( src1, src2, 0, dst )
alpha*src1T*src2 + beta*src3T
函数 cvGEMM 执行通用矩阵乘法:
dst = alpha*op(src1)*op(src2) + beta*op(src3), 这里 op(X) 是 X 或者 XT
所有的矩阵应该有相同的数据类型和协调的矩阵大小。支持实数浮点矩阵或者复数浮点矩阵。
对数组每一个元素执行矩阵变换
void cvTransform( const CvArr* src, CvArr* dst, const CvMat* transmat, const CvMat* shiftvec=NULL );
函数 cvTransform 对数组 src 每一个元素执行矩阵变换并将结果存储到 dst
:
dst(I)=transmat*src(I) + shiftvec 或者 dst(I)k=sumj(transmat(k,j)*src(I)j) + shiftvec(k)
N
-通道数组 src
的每一个元素都被视为一个N元向量,使用一个 M
×N
的变换矩阵 transmat
和偏移向量 shiftvec
把它变换到一个 M
-通道的数组 dst
的一个元素中。 这里可以选择将偏移向量 shiftvec
嵌入到 transmat
中。这样的话 transmat
应该是 M
×N+1
的矩阵,并且最右边的一列被看作是偏移向量 。
输入数组和输出数组应该有相同的位深(depth)和同样的大小或者 ROI 大小。 transmat
和 shiftvec
应该是实数浮点矩阵。
该函数可以用来进行 N
D 点集的几何变换,任意的线性颜色空间变换,通道转换等。
向量数组的透视变换
void cvPerspectiveTransform( const CvArr* src, CvArr* dst, const CvMat* mat );
函数 cvPerspectiveTransform 用下面的方式变换 src 的每一个元素 (通过将其视为二维或者三维的向量):
(x, y, z) -> (x'/w, y'/w, z'/w) 或者 (x, y) -> (x'/w, y'/w), 这里 (x', y', z', w') = mat*(x, y, z, 1) 或者 (x', y', w') = mat*(x, y, 1) 并且 w = w' 如果 w'!=0, 否则 w = inf
计算数组和数组的转置的乘积
void cvMulTransposed( const CvArr* src, CvArr* dst, int order, const CvArr* delta=NULL );
函数 cvMulTransposed 计算 src 和它的转置的乘积。
函数求值公式:
如果 order=0
dst=(src-delta)*(src-delta)T
否则
dst=(src-delta)T*(src-delta)
返回矩阵的迹
CvScalar cvTrace( const CvArr* mat );
函数 cvTrace 返回矩阵mat的对角线元素的和。
tr(src1)=sumimat(i,i)
矩阵的转置
void cvTranspose( const CvArr* src, CvArr* dst ); #define cvT cvTranspose
函数 cvTranspose 对矩阵 src 求转置
:
dst(i,j)=src(j,i)
注意,假设是复数矩阵不会求得复数的共轭。共轭应该是独立的:查看的 cvXorS 例子代码。
返回矩阵的行列式值
double cvDet( const CvArr* mat );
函数 cvDet 返回方阵 mat
的行列式值。对小矩阵直接计算,对大矩阵用 高斯(GAUSSIAN)消去法。对于对称正定(positive-determined)矩阵也可以用 SVD 函数来求,U=V=NULL ,
然后用 w 的对角线元素的乘积来计算行列式。
查找矩阵的逆矩阵或伪逆矩阵
double cvInvert( const CvArr* src, CvArr* dst, int method=CV_LU ); #define cvInv cvInvert
函数 cvInvert 对矩阵 src1
求逆并将结果存储到 src2
如果是 LU
方法该函数返回 src1
的行列式值 (src1 必须是方阵)。 如果是 0, 矩阵不求逆, src2
用 0 填充。
如果 SVD
方法该函数返回 src1
的条件数的倒数(最小奇异值和最大奇异值的比值) ,如果 src1
全为 0 则返回0。 如果 src1 是奇异的, SVD 方法计算一个伪逆矩阵。
求解线性系统或者最小二乘法问题
int cvSolve( const CvArr* src1, const CvArr* src2, CvArr* dst, int method=CV_LU );
函数 cvSolve 解决线性系统或者最小二乘法问题 (后者用 SVD 方法可以解决):
dst = arg minX||src1*X-src2||
如果使用 CV_LU
方法。 如果 src1 是非奇异的,该函数则返回 1 ,否则返回 0 ,在后一种情况下 dst 是无效的。
对实数浮点矩阵进行奇异值分解
void cvSVD( CvArr* A, CvArr* W, CvArr* U=NULL, CvArr* V=NULL, int flags=0 );
M
× N
的输入矩阵
M
× N
或者 N
× N
) 或者 向量 ( N
× 1
).
M
× M
or M
× N
). 如果 CV_SVD_U_T 被指定, 应该交换上面所说的行与列的数目。
N
× N
)
CV_SVD_MODIFY_A
通过操作可以修改矩阵 src1
。这样处理速度会比较快。 CV_SVD_U_T
意味着会返回转置矩阵 U
,指定这个标志将加快处理速度。 CV_SVD_V_T
意味着会返回转置矩阵 V
,指定这个标志将加快处理速度。函数 cvSVD 将矩阵 A 分解成一个对角线矩阵和两个正交矩阵的乘积:
A=U*W*VT
这里 w 是一个奇异值的对角线矩阵,它可以被编码成奇异值的一维向量,u 和 v 也是一样。 所有的奇异值都是非负的并按降序存储。(u 和 v 也相应的存储)。
SVD 算法在数值处理上已经很稳定,它的典型应用包括:
并且
U
=V
是矩阵的特征向量(因此,当需要计算特征向量时 u 和 v 只需要计算其中一个就可以了) 。 CV_SVD
的 cvSolve 方法。 奇异值回代算法(back substitution)
void cvSVBkSb( const CvArr* W, const CvArr* U, const CvArr* V, const CvArr* B, CvArr* X, int flags );
函数 cvSVBkSb 为被分解的矩阵 A 和矩阵 B 计算回代逆(back substitution) (参见 cvSVD 说明) :
X=V*W-1*UT*B
这里
W-1(i,i)=1/W(i,i) 如果 W(i,i) > epsilon•sumiW(i,i), 否则 0
epsilon
是一个依赖于矩阵数据类型的的很小的数。
该函数和 cvSVD 函数被用来执行 cvInvert 和 cvSolve, 用这些函数 (svd & bksb)的原因是初级函数(low-level) 函数可以避免高级函数 (inv & solve) 计算中内部分配的临时矩阵。
计算对称矩阵的特征值和特征向量
void cvEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, double eps=0 );
函数 cvEigenVV 计算矩阵 A
的特征值和特征向量:
mat*evects(i,:)' = evals(i)*evects(i,:)' (在 MATLAB 的记法)
矩阵 A 的数据将会被这个函数修改。
目前这个函数比函数 cvSVD 要慢,精确度要低, 如果已知 A 是正定的,(例如, 它是一个协方差矩阵), 它通常被交给函数 cvSVD 来计算其特征值和特征向量,尤其是在不需要计算特征向量的情况下
计算向量集合的协方差矩阵
void cvCalcCovarMatrix( const CvArr** vects, int count, CvArr* cov_mat, CvArr* avg, int flags );
CV_COVAR_SCRAMBLED
- 输出协方差矩阵按下面计算:
scale*[vects[0]-avg,vects[1]-avg,...]T*[vects[0]-avg,vects[1]-avg,...]
,
count
× count
. 这样一个不寻常的矩阵用于一组大型向量的快速PCA方法(例如, 人脸识别的 EigenFaces 技术)。这个混杂("scrambled")矩阵的特征值将和真正的协方差矩阵的特征值匹配,真正的特征向量可以很容易的从混杂("scrambled")协方差矩阵的特征向量中计算出来。
CV_COVAR_NORMAL
- 输出协方差矩阵被计算成:
scale*[vects[0]-avg,vects[1]-avg,...]*[vects[0]-avg,vects[1]-avg,...]T
,
cov_mat
将是一个和每一个输入向量的元素数目具有同样线性大小的通常协方差矩阵。 CV_COVAR_SCRAMBLED
和 CV_COVAR_NORMAL
只能同时指定其中一个。
CV_COVAR_USE_AVG
- 如果这个标志被指定, 该函数将不会从输入向量中计算 avg
,而是用过去的 avg 向量,如果 avg 已经以某种方式计算出来了这样做是很有用的。或者如果协方差矩阵是部分计算出来的 - 倘若这样, avg
不是输入向量的子集的平均值,而是整个集合的平均向量。
CV_COVAR_SCALE
- 如果这个标志被指定,协方差矩阵被缩放了。 the covariation matrix is scaled.在 "normal" 模式下缩放比例是 1./count
, 在 "scrambled" 模式下缩放比例是每一个输入向量的元素总和的倒数。 缺省地(如果没有指定标志) 协方差矩阵不被缩放 ( scale=1
)。
函数 cvCalcCovarMatrix 计算输入向量的协方差矩阵和平均向量。该函数 可以被运用到主成分分析中(PCA),
以及马氏距离(Mahalanobis distance)比较向量中等等。
计算两个向量之间的马氏距离(Mahalonobis distance)
double cvMahalanobis( const CvArr* vec1, const CvArr* vec2, CvArr* mat );
函数 cvMahalonobis 计算两个向量之间的加权距离,其返回结果是:
d(vec1,vec2)=sqrt( sumi,j {mat(i,j)*(vec1(i)-vec2(i))*(vec1(j)-vec2(j))} )
协方差矩阵可以用函数cvCalcCovarMatrix 计算出来,逆矩阵可以用函数 cvInvert 计算出来 (CV_SVD 方法是一个比较好的选择, 因为矩阵可能是奇异的).
转换浮点数为整数
int cvRound( double value ); int cvFloor( double value ); int cvCeil( double value );
函数 cvRound, cvFloor, cvCeil 用一种舍入方法将输入浮点数转换成整数。 cvRound 返回和参数最接近的整数值。 cvFloor 返回不大于参数的最大整数值。cvCeil 返回不小于参数的最小整数值。在某些体系结构中该函数 工作起来比标准 C 操作起来还要快。如果参数的绝对值大于 231 , 结果是不可预料的。特别值 (±Inf, NaN) 是不可控制的。
计算平方根
float cvSqrt( float value );
函数 cvSqrt 计算输入值的平方根。如果输入的是复数, 结果将不可预料。
计算平方根的倒数
float cvInvSqrt( float value );
函数 cvInvSqrt 计算输入值的平方根的倒数,大多数情况下它比 1./sqrt(value)
要快。 如果输入的是 0 或者复数,结果将不可预料。特别值 (±Inf, NaN) 是不可控制的。
计算立方根
float cvCbrt( float value );
函数 cvCbrt 计算输入值的立方根,大多数情况下它比 pow(value,1./3)
要快。 另外, 负数也是可操作的。特别值 (±Inf, NaN) 是不可控制的。
计算二维向量的角度
float cvFastArctan( float y, float x );
函数 cvFastArctan 计算二维向量的全范围角度角度, 变化范围是 0°到 360°。 精确度为 ~0.1° 。
判断输入是否是一个数字
int cvIsNaN( double value );
函数 cvIsNaN 发给输入是一个数字则返回 1 ( IEEE754 标准 ),否则返回 0 。
判断输入是否是无穷大
int cvIsInf( double value );
函数 cvIsInf 如果输入是 ±Infinity ( IEEE754 标准 )则返回 1 ,否则返回 0 .
计算二维向量的长度和/或者角度
void cvCartToPolar( const CvArr* x, const CvArr* y, CvArr* magnitude, CvArr* angle=NULL, int angle_in_degrees=0 );
函数 cvCartToPolar 计算二维向量(x(I),y(I))
的长度,角度,或者两者同时计算:
magnitude(I)=sqrt( x(I)2+y(I)2 ), angle(I)=atan( y(I)/x(I) )
角度的精确度 ≈0.1°. (0,0) 点的角度被设置为 0.
计算极坐标形式的二维向量对应的直角坐标
void cvPolarToCart( const CvArr* magnitude, const CvArr* angle, CvArr* x, CvArr* y, int angle_in_degrees=0 );
函数 cvPolarToCart 计算每个向量magnitude(I)*exp(angle(I)*j), j=sqrt(-1)
的 x 坐标,y 坐标或者两者都计算:
x(I)=magnitude(I)*cos(angle(I)), y(I)=magnitude(I)*sin(angle(I))
对数组内每个元素求幂
void cvPow( const CvArr* src, CvArr* dst, double power );
函数 cvPow 计算输入数组的每个元素的 p 次幂:
dst(I)=src(I)p, 如果p是整数 否则dst(I)=abs(src(I))p
也就是说,对于非整型的幂指数使用输入数组元素的绝对值进行计算。然而,使用一些额外的操作,负值也可以得到正确的结果,象下面的例子,计算数组元素的立方根:
CvSize size = cvGetSize(src); CvMat* mask = cvCreateMat( size.height, size.width, CV_8UC1 ); cvCmpS( src, 0, mask, CV_CMP_LT ); /* 查找负数 */ cvPow( src, dst, 1./3 ); cvSubRS( dst, cvScalarAll(0), dst, mask ); /* 输入的负值的结果求反 */ cvReleaseMat( &mask );
对于一些幂值, 例如整数值, 0.5 和 -0.5, 优化算法被使用。
计算数组元素的指数幂
void cvExp( const CvArr* src, CvArr* dst );
double
型的或者和输入数组有相同的类型
函数 cvExp 计算输入数组的每个元素的 e 次幂:
dst(I)=exp(src(I))
最大相对误差为 ≈7e-6. 通常, 该函数转换无法输出的值为 0 输出。
Calculates natural logarithm of every array element absolute value计算每个数组元素的绝对值的自然对数
void cvLog( const CvArr* src, CvArr* dst );
double
型的或者和输入数组有相同的类型
函数 cvLog 计算输入数组每个元素的绝对值的自然对数:
dst(I)=log(abs(src(I))), src(I)!=0 dst(I)=C, src(I)=0
这里 C
是一个大负数 (≈-700 通常的运算中)
求解曲线函数的实根
void cvSolveCubic( const CvArr* coeffs, CvArr* roots );
函数 cvSolveCubic
求解曲线函数的实根:
coeffs[0]*x3 + coeffs[1]*x2 + coeffs[2]*x + coeffs[3] = 0 (如果coeffs是四元素的矢量) or x3 + coeffs[0]*x2 + coeffs[1]*x + coeffs[2] = 0 (如果coeffs是三元素的矢量)
函数返回求解得到的实根数目. 实根被存储在矩阵root
中, 如果只有一个实根则用0来替代相关值.
初始化随机数生成器状态
CvRNG cvRNG( int64 seed=-1 );
函数 cvRNG 初始化随机数生成器并返回其状态。指向这个状态的指针可以传递给函数 cvRandInt, cvRandReal 和 cvRandArr . 在通常的实现中使用一个 multiply-with-carry generator 。
用随机数填充数组并更新 RNG 状态
void cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, CvScalar param1, CvScalar param2 );
函数 cvRandArr 用均匀分布的或者正态分布的随机数填充输出数组。在下面的例子中该函数被用来添加一些正常分布的浮点数到二维数组的随机位置。
/* let's noisy_screen be the floating-point 2d array that is to be "crapped" */ CvRNG rng_state = cvRNG(0xffffffff); int i, pointCount = 1000; /* allocate the array of coordinates of points */ CvMat* locations = cvCreateMat( pointCount, 1, CV_32SC2 ); /* arr of random point values */ CvMat* values = cvCreateMat( pointCount, 1, CV_32FC1 ); CvSize size = cvGetSize( noisy_screen ); cvRandInit( &rng_state, 0, 1, /* 现在使用虚参数以后再调整 */ 0xffffffff /*这里使用一个确定的种子 */, CV_RAND_UNI /* 指定为均匀分布类型 */ ); /* 初始化 locations */ cvRandArr( &rng_state, locations, CV_RAND_UNI, cvScalar(0,0,0,0), cvScalar(size.width,size.height,0,0) ); /* modify RNG to make it produce normally distributed values */ rng_state.disttype = CV_RAND_NORMAL; cvRandSetRange( &rng_state, 30 /* deviation */, 100 /* average point brightness */, -1 /* initialize all the dimensions */ ); /* generate values */ cvRandArr( &rng_state, values, CV_RAND_NORMAL, cvRealScalar(100), // average intensity cvRealScalar(30) // deviation of the intensity ); /* set the points */ for( i = 0; i < pointCount; i++ ) { CvPoint pt = *(CvPoint*)cvPtr1D( locations, i, 0 ); float value = *(float*)cvPtr1D( values, i, 0 ); *((float*)cvPtr2D( noisy_screen, pt.y, pt.x, 0 )) += value; } /* not to forget to release the temporary arrays */ cvReleaseMat( &locations ); cvReleaseMat( &values ); /* RNG state does not need to be deallocated */
返回 32-bit 无符号整型并更新 RNG
unsigned cvRandInt( CvRNG* rng );
RandSetRange
(虽然, 后面这个函数对我们正讨论的函数的结果没有什么影响)随意地设置。
函数 cvRandInt 返回均匀分布的随机 32-bit 无符号整型值并更新 RNG 状态。它和 C 运行库里面的 rand() 函数十分相似,但是它产生的总是一个 32-bit 数而 rand() 返回一个 0 到 RAND_MAX
(它是 2**16 或者 2**32, 依赖于操作平台)之间的数。
该函数用来产生一个标量随机数,例如点, patch sizes, table indices 等,用模操作可以产生一个确定边界的整数,人和其他特定的边界缩放到 0.. 1可以产生一个浮点数。下面是用 cvRandInt 重写的前一个函数讨论的例子:
/* the input and the task is the same as in the previous sample. */ CvRNG rng_state = cvRNG(0xffffffff); int i, pointCount = 1000; /* ... - no arrays are allocated here */ CvSize size = cvGetSize( noisy_screen ); /* make a buffer for normally distributed numbers to reduce call overhead */ #define bufferSize 16 float normalValueBuffer[bufferSize]; CvMat normalValueMat = cvMat( bufferSize, 1, CV_32F, normalValueBuffer ); int valuesLeft = 0; for( i = 0; i < pointCount; i++ ) { CvPoint pt; /* generate random point */ pt.x = cvRandInt( &rng_state ) % size.width; pt.y = cvRandInt( &rng_state ) % size.height; if( valuesLeft <= 0 ) { /* fulfill the buffer with normally distributed numbers if the buffer is empty */ cvRandArr( &rng_state, &normalValueMat, CV_RAND_NORMAL, cvRealScalar(100), cvRealScalar(30) ); valuesLeft = bufferSize; } *((float*)cvPtr2D( noisy_screen, pt.y, pt.x, 0 ) = normalValueBuffer[--valuesLeft]; } /* there is no need to deallocate normalValueMat because we have both the matrix header and the data on stack. It is a common and efficient practice of working with small, fixed-size matrices */
返回浮点型随机数并更新 RNG
double cvRandReal( CvRNG* rng );
函数 cvRandReal 返回均匀分布的随机浮点数,范围在 0..1 之间 (不包括 1 )。
执行一维或者二维浮点数组的离散傅立叶正变换或者离散傅立叶逆变换
#define CV_DXT_FORWARD 0 #define CV_DXT_INVERSE 1 #define CV_DXT_SCALE 2 #define CV_DXT_ROWS 4 #define CV_DXT_INV_SCALE (CV_DXT_SCALE|CV_DXT_INVERSE) #define CV_DXT_INVERSE_SCALE CV_DXT_INV_SCALE void cvDFT( const CvArr* src, CvArr* dst, int flags );
CV_DXT_FORWARD
- 正向 1D 或者 2D 变换. 结果不被缩放.
CV_DXT_INVERSE
- 逆向 1D 或者 2D 变换. 结果不被缩放.当然 CV_DXT_FORWARD
和 CV_DXT_INVERSE
是互斥的.
CV_DXT_SCALE
- 对结果进行缩放: 用数组元素除以它. 通常, 它和 CV_DXT_INVERSE
组合在一起,可以使用缩写 CV_DXT_INV_SCALE
.
CV_DXT_ROWS
- 输入矩阵的每个独立的行进行整型或者逆向变换。这个标志允许用户同时变换多个向量,减少开销(它往往比处理它自己要快好几倍), 进行 3D 和高维的变换等等。
函数 cvDFT 执行一维或者二维浮点数组的离散傅立叶正变换或者离散傅立叶逆变换:
N 元一维向量的正向傅立叶变换: y = F(N)•x, 这里 F(N)jk=exp(-i•2Pi•j•k/N), i=sqrt(-1) N 元一维向量的逆向傅立叶变换: x'= (F(N))-1•y = conj(F(N))•y x = (1/N)•x M×N 元二维向量的正向傅立叶变换: Y = F(M)•X•F(N) M×N 元二维向量的逆向傅立叶变换: X'= conj(F(M))•Y•conj(F(N)) X = (1/(M•N))•X'
假设时实数数据 (单通道) ,从 IPL 借鉴过来的压缩格式被用来表现一个正向傅立叶变换的结果或者逆向傅立叶变换的输入:
Re Y0,0 Re Y0,1 Im Y0,1 Re Y0,2 Im Y0,2 ... Re Y0,N/2-1 Im Y0,N/2-1 Re Y0,N/2 Re Y1,0 Re Y1,1 Im Y1,1 Re Y1,2 Im Y1,2 ... Re Y1,N/2-1 Im Y1,N/2-1 Re Y1,N/2 Im Y1,0 Re Y2,1 Im Y2,1 Re Y2,2 Im Y2,2 ... Re Y2,N/2-1 Im Y2,N/2-1 Im Y2,N/2 ............................................................................................ Re YM/2-1,0 Re YM-3,1 Im YM-3,1 Re YM-3,2 Im YM-3,2 ... Re YM-3,N/2-1 Im YM-3,N/2-1 Re YM-3,N/2 Im YM/2-1,0 Re YM-2,1 Im YM-2,1 Re YM-2,2 Im YM-2,2 ... Re YM-2,N/2-1 Im YM-2,N/2-1 Im YM-2,N/2 Re YM/2,0 Re YM-1,1 Im YM-1,1 Re YM-1,2 Im YM-1,2 ... Re YM-1,N/2-1 Im YM-1,N/2-1 Im YM-1,N/2
注意:如果 N 时偶数最后一列存在(is present), 如果 M 时偶数最后一行(is present).
如果是一维实数的变换结果就像上面矩阵的第一行的形式。
利用DFT求解二维卷积
CvMat* A = cvCreateMat( M1, N1, CV_32F ); CvMat* B = cvCreateMat( M2, N2, A->type ); // it is also possible to have only abs(M2-M1)+1×abs(N2-N1)+1 // part of the full convolution result CvMat* conv = cvCreateMat( A->rows + B->rows - 1, A->cols + B->cols - 1, A->type ); // initialize A and B ... int dft_M = cvGetOptimalDFTSize( A->rows + B->rows - 1 ); int dft_N = cvGetOptimalDFTSize( A->cols + B->cols - 1 ); CvMat* dft_A = cvCreateMat( dft_M, dft_N, A->type ); CvMat* dft_B = cvCreateMat( dft_M, dft_N, B->type ); CvMat tmp; // copy A to dft_A and pad dft_A with zeros cvGetSubRect( dft_A, &tmp, cvRect(0,0,A->cols,A->rows)); cvCopy( A, &tmp ); cvGetSubRect( dft_A, &tmp, cvRect(A->cols,0,dft_A->cols - A->cols,A->rows)); cvZero( &tmp ); // no need to pad bottom part of dft_A with zeros because of // use nonzero_rows parameter in cvDFT() call below cvDFT( dft_A, dft_A, CV_DXT_FORWARD, A->rows ); // repeat the same with the second array cvGetSubRect( dft_B, &tmp, cvRect(0,0,B->cols,B->rows)); cvCopy( B, &tmp ); cvGetSubRect( dft_B, &tmp, cvRect(B->cols,0,dft_B->cols - B->cols,B->rows)); cvZero( &tmp ); // no need to pad bottom part of dft_B with zeros because of // use nonzero_rows parameter in cvDFT() call below cvDFT( dft_B, dft_B, CV_DXT_FORWBRD, B->rows ); cvMulSpectrums( dft_A, dft_B, dft_A, 0 /* or CV_DXT_MUL_CONJ to get correlation rather than convolution */ ); cvDFT( dft_A, dft_A, CV_DXT_INV_SCALE, conv->rows ); // calculate only the top part cvGetSubRect( dft_A, &tmp, cvRect(0,0,conv->cols,conv->rows) ); cvCopy( &tmp, conv );
对于给定的矢量尺寸返回最优DFT尺寸
int cvGetOptimalDFTSize( int size0 );
函数 cvGetOptimalDFTSize
返回最小值 N
that is greater to equal to size0
, such that DFT of a vector of size N
can be computed fast. In the current implementation N=2p×3q×5r
for some p, q, r
.
The function returns a negative number if size0
is too large (very close to INT_MAX
)
两个傅立叶频谱的每个元素的乘法(Performs per-element multiplication of two Fourier spectrums)
void cvMulSpectrums( const CvArr* src1, const CvArr* src2, CvArr* dst, int flags );
CV_DXT_ROWS
- 把数组的每一行视为一个单独的频谱 (参见 cvDFT 的参数讨论).
CV_DXT_MUL_CONJ
- 在做乘法之前取第二个输入数组的共轭.
函数 cvMulSpectrums 执行两个 CCS-packed 或者实数或复数傅立叶变换的结果复数矩阵的每个元素的乘法。(performs per-element multiplication of the two CCS-packed or complex matrices that are results of real or complex Fourier transform.)
该函数和 cvDFT 可以用来快速计算两个数组的卷积.
执行一维或者二维浮点数组的离散余弦变换或者离散反余弦变换
#define CV_DXT_FORWARD 0 #define CV_DXT_INVERSE 1 #define CV_DXT_ROWS 4 void cvDCT( const CvArr* src, CvArr* dst, int flags );
CV_DXT_FORWARD
- 1D 或者 2D 余弦变换.
CV_DXT_INVERSE
- 1D or 2D 反余弦变换.
CV_DXT_ROWS
- 对输入矩阵的每个独立的行进行余弦或者反余弦变换. 这个标志允许用户同时进行多个向量的变换,可以用来减少开销(它往往比处理它自己要快好几倍),以及 3D 和高维变换等等。
函数 cvDCT 执行一维或者二维浮点数组的离散余弦变换或者离散反余弦变换:
N 元一维向量的余弦变换: y = C(N)•x, 这里 C(N)jk=sqrt((j==0?1:2)/N)•cos(Pi•(2k+1)•j/N) N 元一维向量的反余弦变换: x = (C(N))-1•y = (C(N))T•y M×N 元二维向量的余弦变换: Y = (C(M))•X•(C(N))T M×N 元二维向量的反余弦变换: X = (C(M))T•Y•C(N)
Growing memory storage
typedef struct CvMemStorage
{
struct CvMemBlock* bottom;/* first allocated block */
struct CvMemBlock* top; /* the current memory block - top of the stack */
struct CvMemStorage* parent; /* borrows new blocks from */
int block_size; /* block size */
int free_space; /* free space in the top
block (in bytes) */
} CvMemStorage;
内存存储器是一个可用来存储诸如序列,轮廓,图形,子划分等动态增长数据结构的底层结构。它是由一系列以同等大小的内存块构成,呈列表型 ---bottom 域指的是列首,top 域指的是当前指向的块但未必是列尾.在bottom和top之间所有的块(包括bottom, 不包括top)被完全占据了空间;在 top和列尾之间所有的块(包括块尾,不包括top)则是空的;而top块本身则被占据了部分空间 -- free_space 指的是top块剩余的空字节数。
新分配的内存缓冲区(或显示的通过 cvMemStorageAlloc 函数分配,或隐示的通过 cvSeqPush, cvGraphAddEdge等高级函数分配)总是起始于当前块(即top块)的剩余那部分,如果剩余那部分能满足要求(够分配的大小)。分配后,free_space 就减少了新分配的那部分内存大小,外加一些用来保存适当列型的附加大小。当top块的剩余空间无法满足被分配的块(缓冲区)大小时,top块的下一个存储块被置为当前块(新的top块) -- free_space 被置为先前分配的整个块的大小。
如果已经不存在空的存储块(即:top块已是列尾),则必须再分配一个新的块(或从parent那继承,见 cvCreateChildMemStorage)并将该块加到列尾上去。于是,存储器(memory storage)就如同栈(Stack)那样, bottom指向栈底,(top, free_space)对指向栈顶。栈顶可通过 cvSaveMemStoragePos保存,通过 cvRestoreMemStoragePos 恢复指向, 通过 cvClearStorage 重置。
内存存储块结构
typedef struct CvMemBlock { struct CvMemBlock* prev; struct CvMemBlock* next; } CvMemBlock;
CvMemBlock 代表一个单独的内存存储块结构。 内存存储块中的实际数据存储在 header块 之后(即:存在一个头指针 head 指向的块 header ,该块不存储数据),于是,内存块的第 i 个字节可以通过表达式 ((char*)(mem_block_ptr+1))[i] 获得。然而,通常没必要直接去获得存储结构的域。
内存存储块地址
typedef struct CvMemStoragePos { CvMemBlock* top; int free_space; } CvMemStoragePos;
该结构(如以下所说)保存栈顶的地址,栈顶可以通过 cvSaveMemStoragePos 保存,也可以通过 cvRestoreMemStoragePos 恢复。
CvMemStorage* cvCreateMemStorage( int block_size=0 );
block_size:存储块的大小以字节表示。如果大小是 0 byte, 则将该块设置成默认值 -- 当前默认大小为64k.
函数 cvCreateMemStorage 创建一内存块并返回指向块首的指针。起初,存储块是空的。头部(即:header)的所有域值都为 0,除了 block_size 外.
创建子内存块
CvMemStorage* cvCreateChildMemStorage( CvMemStorage* parent );
parent 父内存块
函数 cvCreateChildMemStorage 创建一类似于普通内存块的子内存块,除了内存分配/释放机制不同外。当一个子存储块需要一个新的块加入时,它就试图从parent 那得到这样一个块。如果 parent 中 还未被占据空间的那些块中的第一个块是可获得的,就获取第一个块(依此类推),再将该块从 parent 那里去除。如果不存在这样的块,则 parent 要么分配一个,要么从它自己 parent (即:parent 的 parent) 那借个过来。换句话说,完全有可能形成一个链或更为复杂的结构,其中的内存存储块互为 child/ parent 关系(父子关系)。当子存储结构被释放或清除,它就把所有的块还给各自的 parent. 在其他方面,子存储结构同普通存储结构一样。
子存储结构在下列情况中是非常有用的。想象一下,如果用户需要处理存储在某个块中的动态数据,再将处理的结果存放在该块中。在使用了最简单的方法处理后,临时数据作为输入和输出数据被存放在了同一个存储块中,于是该存储块看上去就类似下面处理后的样子: Dynamic data processing without using child storage.
结果,在存储块中,出现了垃圾(临时数据)。然而,如果在开始处理数据前就先建立一个子存储块,将临时数据写入子存储块中并在最后释放子存储块,那么最终在 源/目的存储块 (source / destination storage) 中就不会出现垃圾, 于是该存储块看上去应该是如下形式:Dynamic data processing using a child storage.
释放内存块
void cvReleaseMemStorage( CvMemStorage** storage );
storage: 指向被释放了的存储块的指针
函数 cvReleaseMemStorage 释放所有的存储(内存)块 或者 将它们返回给各自的 parent(如果需要的话)。 接下来再释放 header块(即:释放头指针 head 指向的块 = free(head))并清除指向该块的指针(即:head = NULL)。在释放作为 parent 的块之前,先清除各自的 child 块。
清空内存存储块
void cvClearMemStorage( CvMemStorage* storage );
storage:存储存储块
函数 cvClearMemStorage 将存储块的 top 置到存储块的头部(注:清空存储块中的存储内容)。该函数并不释放内存(仅清空内存)。假使该内存块有一个父内存块(即:存在一内存块与其有父子关系),则函数就将所有的块返回给其 parent.
在存储块中分配以内存缓冲区
void* cvMemStorageAlloc( CvMemStorage* storage, size_t size );
storage:内存块.
size:缓冲区的大小.
函数 cvMemStorageAlloc 在存储块中分配一内存缓冲区。该缓冲区的大小不能超过内存块的大小,否则就会导致运行时错误。缓冲区的地址被调整为CV_STRUCT_ALIGN 字节 (当前为 sizeof(double)).
在存储块中分配一文本字符串
typedef struct CvString { int len; char* ptr; } CvString; CvString cvMemStorageAllocString( CvMemStorage* storage, const char* ptr, int len=-1 );
storage:存储块
ptr:字符串
len:字符串的长度(不计算‘/0’)。如果参数为负数,函数就计算该字符串的长度。
函数 cvMemStorageAlloString 在存储块中创建了一字符串的拷贝。它返回一结构,该结构包含字符串的长度(该长度或通过用户传递,或通过计算得到)和指向被拷贝了的字符串的指针。
保存内存块的位置(地址)
void cvSaveMemStoragePos( const CvMemStorage* storage, CvMemStoragePos* pos );
storage:内存块.
pos:内存块顶部位置。
函数 cvSaveMemStoragePos 将存储块的当前位置保存到参数 pos 中。 函数 cvRestoreMemStoragePos 可进一步获取该位置(地址)。
恢复内存存储块的位置
void cvRestoreMemStoragePos( CvMemStorage* storage, CvMemStoragePos* pos );
storage:内存块.
pos:新的存储块的位置
函数 cvRestoreMemStoragePos 通过参数 pos 恢复内存块的位置。该函数和函数 cvClearMemStorage 是释放被占用内存块的唯一方法。注意:没有什么方法可去释放存储块中被占用的部分内存。
Growable sequence of elements
#define CV_SEQUENCE_FIELDS() / int flags; /* micsellaneous flags */ / int header_size; /* size of sequence header */ / struct CvSeq* h_prev; /* previous sequence */ / struct CvSeq* h_next; /* next sequence */ / struct CvSeq* v_prev; /* 2nd previous sequence */ / struct CvSeq* v_next; /* 2nd next sequence */ / int total; /* total number of elements */ / int elem_size;/* size of sequence element in bytes */ / char* block_max;/* maximal bound of the last block */ / char* ptr; /* current write pointer */ / int delta_elems; /* how many elements allocated when the sequence grows (sequence granularity) */ / CvMemStorage* storage; /* where the seq is stored */ / CvSeqBlock* free_blocks; /* free blocks list */ / CvSeqBlock* first; /* pointer to the first sequence block */ typedef struct CvSeq { CV_SEQUENCE_FIELDS() } CvSeq;
结构CvSeq是所有OpenCV动态数据结构的基础。
通过不同寻常的宏定义简化了带有附加参数的结构 CvSeq 的扩展。为了扩展 CvSeq, 用户可以定义一新的数据结构或在通过宏CV_SEQUENCE_FIELDS()所包括的 CvSeq 的域后在放入用户自定义的域。
有两种类型的序列 -- 稠密序列和稀疏序列。稠密序列都派生自 CvSeq, 它们用来代表可扩展的一维数组 -- 向量,栈,队列,双端队列。数据间不存在空隙(即:连续存放)-- 如果元素从序列中间被删除或插入新的元素到序列中(不是两端),那么此元素后边的相关元素会被移动。稀疏序列都派生自 CvSet,后面会有详细的讨论。它们都是由节点所组成的序列,每一个节点要么被占用空间要么是空,由 flag 标志指定。这些序列作为无序的数据结构而被使用,如点集,图,哈希表等。
The field header_size
contains the actual size of the sequence header and should be greater or equal to sizeof(CvSeq)
.
域 header_size 含有序列头部节点的实际大小,此大小大于或等于 sizeof(CvSeq).
域 h_prev, h_next, v_prev, v_next 可用来创建不同序列的层次结构。域 h_prev, h_next 指向同一层次结构前一个和后一个序列,而域 v_prev, v_next指向在垂直方向上的前一个和后一个序列,即:父亲和子孙。
域 first 指向第一个序列快,块结构在后面描述。
域 total 包含稠密序列的总元素数和稀疏序列被分配的节点数。
域 flags 的高16位描述(包含)特定的动态结构类型(CV_SEQ_MAGIC_VAL 表示稠密序列,CV_SET_MAGIC_VAL 表示稀疏序列),同时包含形形色色的信息。
低 CV_SEQ_ELTYPE_BITS 位包含元素类型的 ID(标示符)。大多数处理函数并不会用到元素类型,而会用到存放在 elem_size 中的元素大小 。如果序列中包含 CvMat 中的数据,那么元素的类型就与 CvMat 中的类型相匹配, 如:CV_32SC2 可以被使用为由二维空间中的点序列, CV_32FC1用为由浮点数组成的序列等。通过宏 CV_SEQ_ELTYPE(seq_header_ptr) 来获取序列中元素的类型。处理数字序列的函数判断: elem.size 等同于序列元素的大小。除了与 CvMat 相兼容的类型外,还有几个在头 cvtypes.h 中定义的额外的类型。
Standard Types of Sequence Elements
#define CV_SEQ_ELTYPE_POINT CV_32SC2 /* (x,y) */ #define CV_SEQ_ELTYPE_CODE CV_8UC1 /* freeman code: 0..7 */ #define CV_SEQ_ELTYPE_GENERIC 0 /* unspecified type of sequence elements */ #define CV_SEQ_ELTYPE_PTR CV_USRTYPE1 /* =6 */ #define CV_SEQ_ELTYPE_PPOINT CV_SEQ_ELTYPE_PTR /* &elem: pointer to element of other sequence */ #define CV_SEQ_ELTYPE_INDEX CV_32SC1 /* #elem: index of element of some other sequence */ #define CV_SEQ_ELTYPE_GRAPH_EDGE CV_SEQ_ELTYPE_GENERIC /* &next_o, &next_d, &vtx_o, &vtx_d */ #define CV_SEQ_ELTYPE_GRAPH_VERTEX CV_SEQ_ELTYPE_GENERIC /* first_edge, &(x,y) */ #define CV_SEQ_ELTYPE_TRIAN_ATR CV_SEQ_ELTYPE_GENERIC /* vertex of the binary tree */ #define CV_SEQ_ELTYPE_CONNECTED_COMP CV_SEQ_ELTYPE_GENERIC /* connected component */ #define CV_SEQ_ELTYPE_POINT3D CV_32FC3 /* (x,y,z) */
后面的 CV_SEQ_KIND_BITS
字节表示序列的类型:
Standard Kinds of Sequences
/* generic (unspecified) kind of sequence */ #define CV_SEQ_KIND_GENERIC (0 << CV_SEQ_ELTYPE_BITS) /* dense sequence suntypes */ #define CV_SEQ_KIND_CURVE (1 << CV_SEQ_ELTYPE_BITS) #define CV_SEQ_KIND_BIN_TREE (2 << CV_SEQ_ELTYPE_BITS) /* sparse sequence (or set) subtypes */ #define CV_SEQ_KIND_GRAPH (3 << CV_SEQ_ELTYPE_BITS) #define CV_SEQ_KIND_SUBDIV2D (4 << CV_SEQ_ELTYPE_BITS)
连续序列块
typedef struct CvSeqBlock { struct CvSeqBlock* prev; /* previous sequence block */ struct CvSeqBlock* next; /* next sequence block */ int start_index; /* index of the first element in the block + sequence->first->start_index */ int count; /* number of elements in the block */ char* data; /* pointer to the first element of the block */ } CvSeqBlock;
序列块构成一个双向的循环列表,因此指针 prev 和 next 永远不会为 null, 而总是指向序列中的前一个和后一个序列块。也就是说:最后一个序列块的 next 指向的就是序列中的第一个块,而第一个块的 prev 指向最后一个块。域 start_index 和 count 有助于跟踪序列中块的位置。 例如,一个含10个元素的序列被分成了3块,每一块的大小分别为3, 5, 2,第一块的参数 start_index 为 2, 那么该序列的 (start_index, count) 相应为 (2,3),(5,5),(10,2)。第一个块的参数 start_index 通常为 0,除非一些元素已被插入到序列中。
序列分割
typedef struct CvSlice { int start_index; int end_index; } CvSlice; inline CvSlice cvSlice( int start, int end ); #define CV_WHOLE_SEQ_END_INDEX 0x3fffffff #define CV_WHOLE_SEQ cvSlice(0, CV_WHOLE_SEQ_END_INDEX) /* calculates the sequence slice length */ int cvSliceLength( CvSlice slice, const CvSeq* seq );
有关序列的一些操作函数将 CvSlice 作为输入参数,默认情况下该参数通常被设置成整个序列(CV_WHOLE_SEQ)。start_index 和 end_index 任何一个都可以是负数或 超过序列长度,start_index 是闭界,end_index 是开界。如果两者相等,那么分割被认为是空分割(即:不包含任何元素)。由于序列被看作是循环结构, 所以分割可以选择序列中靠后的几个元素,靠前的参数反而跟着它们,如 cvSlice(-2,3)。函数用下列发法来规范分割参数:首先, 调用 cvSliceLength 来决定分割的长度,然后, start_index 被使用类似于 cvGetSeqElem 的参数来规范(例如:负数也被允许)。实际的分割操作起始于规范化了的 start_index ,中止于 start_index + cvSliceLength()。(再次假设序列是循环结构)
如果函数并不接受分割参数,但你还是想要处理序列的一部分,那么可以使用函数 cvSeqSlice 获取子序列。
创建一序列
CvSeq* cvCreateSeq( int seq_flags, int header_size, int elem_size, CvMemStorage* storage );
seq_flags: 序列的符号标志。如果序列不会被传递给任何使用特定序列的函数,那么将它设为 0, 否则从预定义的序列类型中选择一合适的类型。
header_size: 序列头部的大小;必须大于或等于 sizeof(CvSeq). 如果制定了类型或它的扩展名,则此类型必须适合基类的头部大小。
elem_size: 元素的大小,以字节计。这个大小必须与序列类型相一致。例如,对于一个点的序列,元素类型 CV_SEQ_ELTYPE_POINT 应当被指定, 参数elem_size 必须等同于 sizeof(CvPoint).
函数 cvCreateSeq 创建一序列并且返回指向该序列的指针。函数在存储块中分配序列的头部作为一个连续躯体,并且设置结构的 flags域
, elem_size域
, header_size域
和 storage域 的值为被传递过来的值,设置 delta_elems 为默认值(可通过函数
cvSetSeqBlockSize 重新对其赋值),清空其他的头 部域,包括前sizeof(CvSeq) 个字节的空间。
设置序列块的大小
void cvSetSeqBlockSize( CvSeq* seq, int delta_elems );
seq: 序列
delta_elems: 满足元素所需的块的大小
函数 cvSetSeqBlockSize 会对内存分配的粒度产生影响。 当序列缓冲区中空间消耗完时,函数为 delta_elems 个序列元素分配空间。如果新分配的空间与 之前分配的空间相邻的话,这两个块就合并,否则,就创建了一个新的序列快。因此,参数值越大,序列中出现碎片的可能性就越小,不过内存中更多的空间将被浪费。当序列被创建后,参数 delta_elems 大小将被设置为 默认大小(1K).之后, 就可随时调用该函数,并影响内存分配。 函数可以修改被传递过来的参数值,以满足内存块的大小限制 。
Adds element to sequence end
char* cvSeqPush( CvSeq* seq, void* element=NULL );
seq: 块
element: 添加的元素
函数 cvSeqPush 在序列块的尾部添加一元素并返回指向该元素得指针。如果输入参数为 null, 函数就仅仅分配一空间,留给下一个元素使用。下列代码说明如何使用该函数去创建一空间。
The following code demonstrates how to create a new sequence using this function:
CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* seq = cvCreateSeq( CV_32SC1, /* sequence of integer elements */ sizeof(CvSeq), /* header size - no extra fields */ sizeof(int), /* element size */ storage /* the container storage */ ); int i; for( i = 0; i < 100; i++ ) { int* added = (int*)cvSeqPush( seq, &i ); printf( "%d is added/n", *added ); } ... /* release memory storage in the end */ cvReleaseMemStorage( &storage );
函数 cvSeqPush 的时间复杂度为 O(1). 如果需要分配并使用的空间比较大,则存在一分配较快的函数(见:cvStartWriteSeq 和相关函数)
删除序列尾部元素
void cvSeqPop( CvSeq* seq, void* element=NULL );
seq: 序列
element: 可选参数。如果该指针不为空,就拷贝被珊元素到指针指向的位置
函数 cvSeqPop 从序列中删除一元素。如果序列已经为空,就报告一错误。函数时间复杂度为 O(1).
在序列头部添加元素
char* cvSeqPushFront( CvSeq* seq, void* element=NULL );
seq: 序列
element: 添加的元素
函数 cvSeqPushFront 类似于 cvSeqPush, 不过是在序列头部添加元素。时间复杂度为O(1).
删除序列的头部元素
void cvSeqPopFront( CvSeq* seq, void* element=NULL );
seq: 序列
element: 可选参数。如果该指针不为空,就拷贝被珊元素到指针指向的位置。
函数 cvSeqPopFront 删除序列的头部元素。如果序列已经为空,就报告一错误。函数时间复杂度为 O(1).
添加多个元素到序列尾部或头部。
void cvSeqPushMulti( CvSeq* seq, void* elements, int count, int in_front=0 );
seq: 序列
elements: 待添加的元素
count: 添加的元素个数
in_front: 标示在头部还是尾部添加元素
CV_BACK ( = 0) -- 在序列尾部添加元素。
CV_FRONT( != 0) -- 在序列头部添加元素。
函数 cvSeqPushMulti 在序列头部或尾部添加多个元素。 元素按输入数组中的顺序被添加到序列中,不过它们可以添加到不同的序列中
删除多个序列头部或尾部的元素
void cvSeqPopMulti( CvSeq* seq, void* elements, int count, int in_front=0 );
seq: 序列
elements: 待删除的元素
count: 删除的元素个数
in_front: 标示在头部还是尾部删除元素
CV_BACK ( = 0) -- 删除序列尾部元素。
CV_FRONT( != 0) -- 删除序列头部元素。
函数 cvSeqPopMulti 删除多个序列头部或尾部的元素。 如果待删除的元素个数超过了序列中的元素总数,则函数删除尽可能多的元素 。
在序列中添加元素
char* cvSeqInsert( CvSeq* seq, int before_index, void* element=NULL );
seq: 序列
before_index: 元素插入的位置(索引)。如果插入的位置在 0(允许的参数最小值)前,则该函数等同于函数 cvSeqPushFront.如果是在 seq_total(允许的参数最大值)后,则该函数等同于 cvSeqPush.
element: 待插入的元素
函数 cvSeqInsert 移动 从被插入的位置到序列尾部元素所在的位置的所有元素,如果 指针 element 不位 null, 则拷贝 element 中的元素到指定位置。函数返回指向被插入元素的指针。
删除序列中的元素。
void cvSeqRemove( CvSeq* seq, int index );
seq: 序列
index: 被珊元素的索引。
函数 cvSeqRemove 删除指定的索引元素。如果索引出了序列的范围,就报告发现错误。企图从空序列中删除元素,函数报告错误。函数通过移动序列中的元素来删除索引元素。
清空序列
void cvClearSeq( CvSeq* seq );
seq: 序列
函数 cvClearSeq 删除序列中的所有元素。函数不会将内存返回到存储器中,当新的元素添加到序列中时,可重新使用该内存。函数时间复杂度为 O(1).
返回索引所指定的元素指针
char* cvGetSeqElem( const CvSeq* seq, int index ); #define CV_GET_SEQ_ELEM( TYPE, seq, index ) (TYPE*)cvGetSeqElem( (CvSeq*)(seq), (index) )
seq: 序列
index: 索引
函数 cvGetSeqElem 查找序列中索引所指定的元素,并返回指向该元素的指针。如果元素不存在,则返回 0。 函数支持负数,即: -1 代表 序列的最后一个元素, -2 代表最后第二个元素,等。如果序列只包含一个块,或者所需的元素在第一个块中,那么应当使用宏,
CV_GET_SEQ_ELEM( elemType, seq, index )宏中的参数 elemType 是序列中元素的类型(如:CvPoint), 参数 seq 表示序列, 参数 index 代表所需元素的索引。 该宏首先核查所需的元素是否属于第一个块,如果是,则返回该元素,否则,该宏就调用主函数 GetSeqElem. 如果索引为负数的话,则总是调用函数
cvGetSeqElem。函数的时间复杂度为 O(1), 假设块的大小要比元素的数量要小。
返回序列中元素的索引
int cvSeqElemIdx( const CvSeq* seq, const void* element, CvSeqBlock** block=NULL );
seq: 序列
element: 指向序列中元素的指针
block: 可选参数, 如果不为空(NULL),则存放包含该元素的块的地址
函数 cvSeqElemIdx 返回元素的索引,如果该元素不存在这个序列中,则返回一负数。
拷贝序列中的元素到一个连续的内存块中
void* cvCvtSeqToArray( const CvSeq* seq, void* elements, CvSlice slice=CV_WHOLE_SEQ );
seq: 序列
elemenets: 指向目的(存放拷贝元素的)数组的指针,指针指向的空间必须足够大。
slice: 拷贝到序列中的序列部分。
函数 cvCvtSeqToArray 拷贝整个序列或部分序列到指定的缓冲区中,并返回指向该缓冲区的指针.
构建序列
CvSeq* cvMakeSeqHeaderForArray( int seq_type, int header_size, int elem_size, void* elements, int total, CvSeq* seq, CvSeqBlock* block );
seq_type:序列的类型
header_size:序列的头部大小。大小必须大于等于数组的大小。
elem_size:元素的大小
elements:形成该序列的元素
total:序列中元素的总数。参数值必须等于数据的大小
seq:指向被使用作为序列头部的局部变量
block:指向局部变量的指针
函数 cvMakeSeqHeaderForArray 初始化序列的头部。序列块由用户分配(例如:在栈上)。该函数不拷贝数据。创建的序列只包含一个块,和一个 NULL指针,因此可以读取指针,但试图将元素添加到序列中则多数会引发错误。
为各个序列碎片建立头
CvSeq* cvSeqSlice( const CvSeq* seq, CvSlice slice, CvMemStorage* storage=NULL, int copy_data=0 );
seq: 序列
slice: 部分序列块
storage: 存放新的序列和拷贝数据(如果需要)的目的存储空间。如果为NULL, 则函数使用包含该输入数据的存储空间。
copy_data: 标示是否要拷贝元素, 如果 copy_data != 0, 则需要拷贝;如果 copy_data == 0, 则不需拷贝。
函数 cvSeqSlice 创建一序列,该序列表示输入序列中特定的一部分 (slice),。新序列要么与原序列共享元素要么拥有自己的一份拷贝。因此,如果 有人需要去 处理 该部分序列,但函数却没有 slice 参数, 则使用该函数去获取该序列。.
创建序列的一份拷贝
CvSeq* cvCloneSeq( const CvSeq* seq, CvMemStorage* storage=NULL );
seq: 序列
storage:存放新序列的 header部分和拷贝数据(如果需要)的目的存储块。如果为 NULL, 则函数使用包含输入序列的存储块 。
函数 cvCloneSeq 创建输入序列的一份完全拷贝。调用函数 cvCloneSeq (seq, storage) 等同于调用 cvSeqSlice(seq, CV_WHOLE_SEQ, storage, 1).
删除序列的 slice部分
void cvSeqRemoveSlice( CvSeq* seq, CvSlice slice );
seq: 序列
slice: 序列中被移动的那部分
函数 cvSeqRemoveSlice 删除序列中的 slice 部分
在序列中插入一数组
void cvSeqInsertSlice( CvSeq* seq, int before_index, const CvArr* from_arr );
seq: 序列
slice: 序列中被移动的那部分
from_arr: 从中获取元素的数组
函数 cvSeqInsertSlice 在指定位置插入 来自数组from_arr中 所有元素。数组 from_arr 可以是一个 矩阵也可以是另外一个 序列。
将序列中的元素进行逆序操作
void cvSeqInvert( CvSeq* seq );
seq: 序列
函数 cvSeqInvert 对序列进行逆序操作 -- 即:使第一个元素成为最后一个,最后一个元素为第一个。
使用特定的比较函数对序列中的元素进行排序
/* a < b ? -1 : a > b ? 1 : 0 */
typedef int (CV_CDECL* CvCmpFunc)(const void* a, const void* b, void* userdata);
void cvSeqSort( CvSeq* seq, CvCmpFunc func, void* userdata=NULL );
seq: 待排序的序列
func: 比较函数,按照元素间的大小关系返回负数,零,正数(见:上面的声明和下面的例子) --相关函数为 C 运行时库中的 qsort, 后者(qsort)不使用参数userdata.
userdata: 传递给比较函数的用户参数;有些情况下,可避免全局变量的使用
函数 cvSeqSort 使用特定的标准对序列进行排序。下面是一个 使用该函数的实例
/* Sort 2d points in top-to-bottom left-to-right order */ static int cmp_func( const void* _a, const void* _b, void* userdata ) { CvPoint* a = (CvPoint*)_a; CvPoint* b = (CvPoint*)_b; int y_diff = a->y - b->y; int x_diff = a->x - b->x; return y_diff ? y_diff : x_diff; } ... CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* seq = cvCreateSeq( CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), storage ); int i; for( i = 0; i < 10; i++ ) { CvPoint pt; pt.x = rand() % 1000; pt.y = rand() % 1000; cvSeqPush( seq, &pt ); } cvSeqSort( seq, cmp_func, 0 /* userdata is not used here */ ); /* print out the sorted sequence */ for( i = 0; i < seq->total; i++ ) { CvPoint* pt = (CvPoint*)cvSeqElem( seq, i ); printf( "(%d,%d)/n", pt->x, pt->y ); } cvReleaseMemStorage( &storage );
/* a < b ? -1 : a > b ? 1 : 0 */ typedef int (CV_CDECL* CvCmpFunc)(const void* a, const void* b, void* userdata); char* cvSeqSearch( CvSeq* seq, const void* elem, CvCmpFunc func, int is_sorted, int* elem_idx, void* userdata=NULL );
seq: 序列
elem: 待查询的元素
func: 比较函数,按照元素间的大小关系返回负数,零,正数(见:cvSeqSort)
is_sorted: 标示序列是否已经排序
elem_idx: 输出参数;(已查找到)元素的索引值
user_data: 传递到比较函数的用户参数;在某些情况下,有助于避免使用全局变量。
函数 cvSeqSearch 查找序列中的元素。如果序列已被排序,则使用二分查找(时间复杂度为 O(log(N))否则使用简单线性查找。若查找的元素不存在,函数返回 NULL 指针,而索引值设置为序列中的元素数(如果使用的是线性查找)或 满足表达式 seq(i) > elem 的最小的 i.
将数据写入序列中,并初始化该过程
void cvStartAppendToSeq( CvSeq* seq, CvSeqWriter* writer );
seq: 指向序列的指针
writer: writer 的状态; 由该函数初始化
函数 cvStartAppendToSeq 初始化将数据写入序列这个过程。通过宏 CV_WRITE_SEQ_ELEM( written_elem, writer ),
写入的元素被添加到序列尾部。注意:在写入期间,序列的其他操作可能会产生的错误的结果,甚至破怀该序列(见 cvFlushSeqWriter 的相关描述,有助于避免这些错误)
创建新序列,并初始化写入部分(writer)
void cvStartWriteSeq( int seq_flags, int header_size, int elem_size,
CvMemStorage* storage, CvSeqWriter* writer );
seq_flags: 标示被创建的序列。如果序列还未传递给任何处理特定序列类型的函数,则序列值等于0, 否则,必须从之前定义的序列类型中选择一个合适的类型。
header_size: 头部的大小。 参数值不小于 sizeof(CvSeq). 如果定义了某一类型,则该类型不许符合基类的条件。
elem_size: 元素的大小(以字节计);必须与序列类型相一致。例如:如果创建了包含指针的序列(元素类型为 CV_SEQ_ELTYPE_POINT), 那么elem_size 必须等同于 sizeof(CvPoint).
storage: 序列的(在内存)位置
writer: 写入部分 writer 的状态; 由该函数初始化
函数 cvStartWriteSeq 是 函数 cvCreateSeq 和函数 cvStartAppendToSeq 的组合。 指向被创建的序列的指针存放在 writer->seq 中, 通过函数cvEndWriteSeq 返回(因当在最后调用)
CvSeq* cvEndWriteSeq( CvSeqWriter* writer );
函数 cvEndWriteSeq 完成写入操作并返回指向被写入元素的序列的地址。同时,函数会截取最后那个不完整的序列块,将块的剩余部分返回到内存中之后,序列就可以被安全的读和写。
FlushSeqWriter
根据写入状态,刷新序列头部
void cvFlushSeqWriter( CvSeqWriter* writer );
writer: 写入部分的状态
函数 cvFlushSeqWriter 用来使用户在写入过程中每当需要时读取序列元素,比如说,核查制定的条件。函数更新序列的头部,从而使读取序列中的数据成为可能。不过,写入并没有被关闭,为的是随时都可以将数据写入序列。在有些算法中,经常需要刷新,考虑使用 cvSeqPush 代替该函数。
初始化序列中的读取过程
void cvStartReadSeq( const CvSeq* seq, CvSeqReader* reader, int reverse=0 );
seq: 序列
reader: 读取部分的状态; 由该函数初始化
reverse: 决定遍历序列的方向。如果 reverse 为0,则读取顺序被定位从序列头部元素开始,否则从尾部开始读取
函数 cvStartReadSeq 初始化读取部分的状态。毕竟,顺序读取可通过调用宏 CV_READ_SEQ_ELEM( read_elem, reader ),逆序读取可通过调用宏CV_REV_READ_SEQ_ELEM( read_elem, reader )。这两个宏都将序列元素读进read_elem中, 并将指针移到下一个元素。下面代码显示了如何去使用reader 和 writer.
CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* seq = cvCreateSeq( CV_32SC1, sizeof(CvSeq), sizeof(int), storage ); CvSeqWriter writer; CvSeqReader reader; int i; cvStartAppendToSeq( seq, &writer ); for( i = 0; i < 10; i++ ) { int val = rand()%100; CV_WRITE_SEQ_ELEM( val, writer ); printf("%d is written/n", val ); } cvEndWriteSeq( &writer ); cvStartReadSeq( seq, &reader, 0 ); for( i = 0; i < seq->total; i++ ) { int val; #if 1 CV_READ_SEQ_ELEM( val, reader ); printf("%d is read/n", val ); #else /* alternative way, that is prefferable if sequence elements are large, or their size/type is unknown at compile time */ printf("%d is read/n", *(int*)reader.ptr ); CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); #endif } ... cvReleaseStorage( &storage );
返回当前的读取器的位置
int cvGetSeqReaderPos( CvSeqReader* reader );
函数 cvGetSeqReaderPos 返回当前的 reader 位置 (在 0 到 reader->seq->total - 1 中)
移动读取器到指定的位置。
void cvSetSeqReaderPos( CvSeqReader* reader, int index, int is_relative=0 );
reader: reader 的状态
index: 目的位置。如果使用了 positioning mode, 则实际位置为 index % reader->seq->total.
is_relative: 如果不位 0, 那么索引(index) 就相对于当前的位置
函数 cvSetSeqReaderPos 将 read 的位置移动到绝对位置,或相对于当前的位置(相对位置)
Collection of nodes
typedef struct CvSetElem
{
int flags; /* it is negative if the node is free and zero or positive otherwise */
struct CvSetElem* next_free; /* if the node is free, the field is a
pointer to next free node */
}
CvSetElem;
#define CV_SET_FIELDS() /
CV_SEQUENCE_FIELDS() /* inherits from CvSeq */ /
struct CvSetElem* free_elems; /* list of free nodes */
typedef struct CvSet
{
CV_SET_FIELDS()
} CvSet;
在 OpenCV 的稀疏数据结构中, CvSet 是一基本结构。
从上面的声明中可知:CvSet 继承自 CvSeq, 并在此基础上增加了个 free_elems 域,该域是空节点组成的列表。集合中的每一个节点,无论空否,都是线性表中的一个元素。尽管对于稠密的表中的元素没有限制,集合(派生的结构)元素必须起始于整数域,并与结构 CvSetElem 相吻合,因为这两个域对于(由空节点组成)集合的组织是必要的。如果节点为空,flags 为负,next_free 指向下一个空节点。如果节点已被占据空间,flags 为正, flags 包含节点索引值(使用表达式 set_elem->flags & CV_SET_ELEM_IDX_MASKH 获取), flags 的剩余内容由用户决定。宏 CV_IS_SET_ELEM(set_elem.ptr)用来识别特定的节点是否为空。
起初,集合 set 同表 list 都为空。当需要一个来自集合中的新节点时,就从表 list 中去获取,然后表进行了更新。如果表 list 碰巧为空,于是就分配一内存块,块中的所有节点与表 list 相连。结果,集合的 total 域被设置为空节点和非空节点的和。当非空节点别释放后,就将它加到空节点列表中。最先被释放的节点也就是最先被占用空间的节点
在 OpenCV 中, CvSet 用来代表图形(CvGraph), 稀疏多维数组(CvSparseMat), 平面子划分(planner subdivisions)等
CvSet* cvCreateSet( int set_flags, int header_size, int elem_size, CvMemStorage* storage );
set_flags: 集合的类型
header_size: 头节点的大小;可能小于 sizeof(CvSet)
elem_size: 元素的大小; 可能小于 CvSetElem
storage: 相关容器
函数 CvCreateSet 创建一具有特定头部节点大小和元素类型的空集。并返回指向该集合的指针。
占用集合中的一个节点
int cvSetAdd( CvSet* set_header, CvSetElem* elem=NULL, CvSetElem** inserted_elem=NULL );
set_header: 集合
elem: 可选的输入参数,被插入的元素。如果不为 NULL, 函数就将数据拷贝到新分配的节点。(拷贝后,清空第一个域的 MSB)
函数 cvSetAdd 分配一新的节点,将输入数据拷贝给它(可选),并且返回指向该节点的指针和节点的索引值。索引值可通过节点的flags域的低位中获得。函数的时间复杂度为 O(1), 不过,存在着一个函数可快速的分配内存。(见 cvSetNew)
从点集中删除元素
void cvSetRemove( CvSet* set_header, int index );
set_header: 集合
index:被删元素的索引值
函数 cvSetRemove 从点集中删除一具有特定索引值的元素。如果指定位置的节点为空,函数将什么都不做。函数的时间复杂度为 O(1), 不过,存在一函数可更快速的完成该操作,该函数就是 cvSetRemoveByPtr
添加元素到点集中
CvSetElem* cvSetNew( CvSet* set_header );
函数 cvSetNew 是 cvSetAdd 的变体,内联函数。它占用一新节点,并返回指向该节点的指针而不是索引。
删除指针指向的集合元素
void cvSetRemoveByPtr( CvSet* set_header, void* elem );
set_header: 集合
elem: 被删除的元素
函数 cvSetRemoveByPtr 是一内联函数,是函数 cvSetRemove 轻微变化而来的。该函数并不会检查节点是否为空 -- 用户负责这一检查。
通过索引值查找相应的集合元素
CvSetElem* cvGetSetElem( const CvSet* set_header, int index );
set_header: 集合
index: 索引值
函数 cvGetSetElem 通过索引值查找相应的元素。函数返回指向该元素的指针,如果索引值无效或相应的节点为空,则返回 0。 若函数使用 cvGetSeqElem 去查找节点,则函数支持负的索引值。
清空点集
void cvClearSet( CvSet* set_header );
set_header: 待清空的点集
函数 cvClearSet 删除集合中的所有元素。时间复杂度为 O(1).
有向权图和无向权图
#define CV_GRAPH_VERTEX_FIELDS() /
int flags; /* vertex flags */ /
struct CvGraphEdge* first; /* the first incident edge */
typedef struct CvGraphVtx
{
CV_GRAPH_VERTEX_FIELDS()
}
CvGraphVtx;
#define CV_GRAPH_EDGE_FIELDS() /
int flags; /* edge flags */ /
float weight; /* edge weight */ /
struct CvGraphEdge* next[2]; /* the next edges in the incidence lists for staring (0) */ /
/* and ending (1) vertices */ /
struct CvGraphVtx* vtx[2]; /* the starting (0) and ending (1) vertices */
typedef struct CvGraphEdge
{
CV_GRAPH_EDGE_FIELDS()
}
CvGraphEdge;
#define CV_GRAPH_FIELDS() /
CV_SET_FIELDS() /* set of vertices */ /
CvSet* edges; /* set of edges */
typedef struct CvGraph
{
CV_GRAPH_FIELDS()
}
CvGraph;
在 OpenCV 图形结构中,CvGraph 是一基本结构。
图形结构继承自 CvSet -- 该部分描绘了普通图的属性和图的顶点,也包含了一个点集作为其成员 -- 该点集描述了图的边缘。利用宏(可以简化结构扩展和定制)使用与其它OpenCV可扩展结构一样的方法和技巧,同样的方法和技巧,我们声明了定点,边和头部结构。虽然顶点结构和边结构无法从CvSetElem 显式地继承时,但它们满足点集元素的两个条件(在开始是有一个整数域和满足 CvSetElem 结构)。 flags 域用来标记顶点和边是否已被占用或者处于其他目的,如:遍历图时(见:cvStartScanGraph 等),因此最好不要去直接使用它们。图代表的就是边的集合。存在有向和无向的区别。对于后者(无向图),在连接顶点 A 到 顶点 B 的边同连接顶点 B 到 顶点 A的边是没什么区别的,在某一时刻,只可能存在一个,即:要么是要么是.
创建一个空树
CvGraph* cvCreateGraph( int graph_flags, int header_size, int vtx_size, int edge_size, CvMemStorage* storage );
graph_flags: 被创建的图的类型。通常,无向图为 CV_SEQ_KIND_GRAPH,无向图为 CV_SEQ_KIND_GRAPH | CV_GRAPH_FLAG_ORIENTED.
header_size: 头部大小;可能小于 sizeof(CvGraph)
vtx_size: 顶点大小;常规的定点结构必须来自 CvGraphVtx (使用宏 CV_GRAPH_VERTEX_FIELDS())
edge_size: 边的大小;常规的边结构必须来自 CvGraphEdge (使用宏 CV_GRAPH_EDGE_FIELDS())
storage: 图的容器
函数 cvCreateGraph 创建一空图并且返回指向该图的指针。
插入一顶点到图中
int cvGraphAddVtx( CvGraph* graph, const CvGraphVtx* vtx=NULL,
CvGraphVtx** inserted_vtx=NULL );
graph: 图
vtx: 可选输入参数,用来初始化新加入的顶点(仅大小超过 sizeof(CvGraphVtx) 的用户自定义的域才会被拷贝)
inserted_vertex: 可选的输出参数。如果不为 NULL, 则传回新加入顶点的地址
函数 cvGraphAddVtx 将一顶点加入到图中,并返回定点的索引
通过索引从图中删除一顶点
int cvGraphRemoveVtx( CvGraph* graph, int index );
graph: 图
vtx_idx: 被珊顶点的索引
函数 cvGraphRemoveAddVtx 从图中删除一顶点,连同删除含有此顶点的边。如果输入的顶点不属于该图的话,将报告删除出错(不存在而无法删除)。返回值为被删除的边数,如果顶点不属于该图的话,返回 -1。
通过指针从图中删除一顶点
int cvGraphRemoveVtxByPtr( CvGraph* graph, CvGraphVtx* vtx );
graph: 图
vtx; 指向被删除的边的指针
函数 cvGraphRemoveVtxByPtr 从图中删除一顶点,连同删除含有此顶点的边。如果输入的顶点不属于该图的话,将报告删除出错(不存在而无法删除)。返回值为被删除的边数,如果顶点不属于该图的话,返回 -1。
通过索引值查找图的相应顶点
CvGraphVtx* cvGetGraphVtx( CvGraph* graph, int vtx_idx );
graph: 图
vtx_idx: 定点的索引值
函数 cvGetGraphVtx 通过索引值查找对应的顶点,并返回指向该顶点的指针,如果不存在则返回 NULL.
返回定点相应的索引值
int cvGraphVtxIdx( CvGraph* graph, CvGraphVtx* vtx );
graph: 图
vtx: 指向顶点的指针
函数 cvGraphVtxIdx 返回与顶点相应的索引值
通过索引值在图中加入一条边
int cvGraphAddEdge( CvGraph* graph, int start_idx, int end_idx, const CvGraphEdge* edge=NULL, CvGraphEdge** inserted_edge=NULL );
graph: 图
start_idx; 边的起始顶点的索引值
end_idx: 边的尾部顶点的索引值(对于无向图,参数的次序无关紧要,即:start_idx 和 end_idx 可互为起始顶点和尾部顶点)
edge: 可选的输入参数,初始化边的数据
inserted_edge: 可选的输出参数,包含被插入的边的地址。
函数 cvGraphAddEdge 连接两特定的顶点。如果该边成功地加入到图中,返回 1; 如果连接两顶点的边已经存在,返回 0; 如果顶点没被发现(不存在)或者起始顶点和尾部顶点是同一个定点,或其他特殊情况,返回 -1。 如果是后者(即:返回值为负),函数默认的报告一个错误。
通过指针在图中加入一条边
int cvGraphAddEdgeByPtr( CvGraph* graph, CvGraphVtx* start_vtx, CvGraphVtx* end_vtx, const CvGraphEdge* edge=NULL, CvGraphEdge** inserted_edge=NULL );
const CvGraphEdge* edge=NULL, CvGraphEdge** inserted_edge=NULL ) graph:图
start_vtx: 指向起始顶点的指针
end_vtx: 指向尾部顶点的指针。对于无向图来说,顶点参数的次序无关紧要。
edge: 可选的输入参数,初始化边的数据
inserted_edge: 可选的输出参数,包含被插入的边的地址。
函数 cvGraphAddEdge 连接两特定的顶点。如果该边成功地加入到图中,返回 1; 如果连接两顶点的边已经存在,返回 0; 如果顶点没被发现(不存在)或者起始顶点和尾部顶点是同一个定点,或其他特殊情况,返回 -1。 如果是后者(即:返回值为负),函数默认的报告一个错误
通过索引值从图中删除顶点
void cvGraphRemoveEdge( CvGraph* graph, int start_idx, int end_idx );
graph: 图
start_idx: 起始顶点的索引值
end_idx; 尾部顶点的索引值。对于无向图来说,顶点参数的次序无关紧要。
函数 cvGraphRemoveEdge 删除连接两特定顶点的边。若两顶点并没有相连接(即:不存在由这两个顶点连接的边),函数什么都不做。
通过指针从图中删除边
void cvGraphRemoveEdgeByPtr( CvGraph* graph, CvGraphVtx* start_vtx, CvGraphVtx* end_vtx );
graph: 图
start_vtx; 指向起始顶点的指针
end_vtx; 指向尾部顶点的指针。对于无向图来说,顶点参数的次序无关紧要。
函数 cvGraphRemoveEdgeByPtr 删除连接两特定顶点的边。若两顶点并没有相连接(即:不存在由这两个顶点连接的边),函数什么都不做。
通过索引值在图中查找相应的边
CvGraphEdge* cvFindGraphEdge( const CvGraph* graph, int start_idx, int end_idx );
#define cvGraphFindEdge cvFindGraphEdge
graph: 图
start_idx: 其实顶点的索引值
end_idx; 尾部顶点的索引值。对于无向图来说,顶点参数的次序无关紧要
函数 cvFindGraphEdge 查找与两特定顶点相对应的边,并返回指向该边的指针。如果该边不存在,返回 NULL.
通过指针在图中查找相应的边
CvGraphEdge* cvFindGraphEdgeByPtr( const CvGraph* graph, const CvGraphVtx* start_vtx,
const CvGraphVtx* end_vtx );
#define cvGraphFindEdgeByPtr cvFindGraphEdgeByPtr
graph: 图
start_vtx; 指向起始顶点的指针
end_vtx; 指向尾部顶点的指针。对于无向图来说,顶点参数的次序无关紧要。
函数 cvFindGraphEdgeByPtr 查找与两特定顶点相对应的边,并返回指向该边的指针。如果该边不存在,返回 NULL
int cvGraphEdgeIdx( CvGraph* graph, CvGraphEdge* edge );
graph: 图
edge: 指向该边的指针
函数 cvGraphEdgeIdx 返回与边对应的索引值。
(通过索引值)统计与顶点相关联的边数
int cvGraphVtxDegree( const CvGraph* graph, int vtx_idx );
graph: 图
vtx_idx: 顶点对应的索引值
函数 cvGraphVtxDegree 返回与特定顶点相关联的边数,包括以该顶点为起始顶点的和尾部顶点的。统计边数,可以适用下列代码:
CvGraphEdge* edge = vertex->first; int count = 0; while( edge ) { edge = CV_NEXT_GRAPH_EDGE( edge, vertex ); count++; }
宏 CV_NEXT_GRAPH_EDGE(edge, vertex) 返回依附于该顶点的下一条边。
(通过指针)统计与顶点相关联的边数
int cvGraphVtxDegreeByPtr( const CvGraph* graph, const CvGraphVtx* vtx );
graph: 图
vtx: 顶点对应的指针
函数 cvGraphVtxDegreeByPtr 返回与特定顶点相关联的边数,包括以该顶点为起始顶点的和尾部顶点的
void cvClearGraph( CvGraph* graph );
graph:图
函数 cvClearGraph 删除该图的所有顶点和边。时间复杂度为 O(1).
克隆图
CvGraph* cvCloneGraph( const CvGraph* graph, CvMemStorage* storage );
graph: 待拷贝的图
storage: 容器,存放拷贝
函数 cvCloneGraph 创建图的完全拷贝。如果顶点和边含有指向外部变量的指针,那么图和它的拷贝共享这些指针。在新的图中,顶点和边可能存在不同,因为函数重新分割了顶点和边的点集。
typedef struct CvGraphScanner { CvGraphVtx* vtx; /* current graph vertex (or current edge origin) */ CvGraphVtx* dst; /* current graph edge destination vertex */ CvGraphEdge* edge; /* current edge */ CvGraph* graph; /* the graph */ CvSeq* stack; /* the graph vertex stack */ int index; /* the lower bound of certainly visited vertices */ int mask; /* event mask */ } CvGraphScanner;
结构 cvGraphScanner 深度遍历整个图。 函数的相关讨论如下(看:StartScanGraph)
创建一结构,用来对图进行深度遍历
CvGraphScanner* cvCreateGraphScanner( CvGraph* graph, CvGraphVtx* vtx=NULL, int mask=CV_GRAPH_ALL_ITEMS );
graph: 图
vtx: 开始遍历的(起始)顶点。如果为 NULL, 便利就从第一个顶点开始(指:顶点序列中,具有最小索引值的顶点)
mask: 事件掩码(event mask)代表用户感兴趣的事件(此时 函数 cvNextGraphItem 将控制返回给用户)。这个只可能是 CV_GRAPH_ALL_ITEMS (如果用户对所有的事件都感兴趣的话)或者是下列标志的组合:
CV_GRAPH_VERTEXT -- 在第一次被访问的顶点处停下
CV_GRAPH_TREE_EDGE -- 在 tree edge 处停下(tree edge 指连接最后被访问的顶点与接下来被访问的顶点的边)
CV_GRAPH_BACK_EDGE -- 在 back edge 处停下(back edge 指连接最后被访问的顶点与其在搜索树中祖先的边)
CV_GRAPH_FORWARD_EDGE -- 在 forward edge 处停下 (forward edge 指连接最后被访问的顶点与其在搜索树中后裔的边)
CV_GRAPH_CROSS_EDGE -- 在 cross edge 处停下(cross edge 指连接不同搜索树中或同一搜索树中不同分支的边.只有在有向图中, 才存在着一 概念)
CV_GRAPH_ANY_EDGE -- 在 any edge 处停下(any edge 指 任何边,包括 tree edge, back edge, forward edge, cross edge)
CV_GRAPH_NEW_TREE -- 在每一个新的搜索树开始处停下。首先遍历从起始顶点开始可以访问到的顶点和边,然后查找搜索图中访问不到的顶点或边并恢复遍历。在开始遍历一颗新的树时(包括第一次调用 cvNextGraphItem 时的树),产生 CV_GRAPH_NEW_TREE 事件。
函数 cvCreateGraphScanner 创建一结构用来深度遍历搜索树。函数 cvNextGraphItem 要使用该初始化了的结构 -- 层层遍历的过程。
逐层遍历整个图
int cvNextGraphItem( CvGraphScanner* scanner );
scanner: 图的遍历状态。被此函数更新。
函数 cvNextGraphItem 遍历整个图,直到用户感兴趣的事件发生(即:调用 cvCreateGraphScanner 时, mask 对应的事件)或遍历结束。在前面一种情况下,函数返回 参数mask 相应的事件,当再次调用函数时,恢复遍历)。在后一种情况下,返回 CV_GRAPH_OVER(-1)。当 mask 相应的事件为 CV_GRAPH_BACKTRACKING 或 CV_GRAPH_NEW_TEEE 时, 当前正在被访问的顶点被存放在 scanner->vtx 中。如果事件与 边edge 相关,那么 edge 本身被存放在 scanner->edge, 该边的起始顶点存放在 scanner->vtx 中, 尾部节点存放在 scanner->dst 中。
完成图地遍历过程
void cvReleaseGraphScanner( CvGraphScanner** scanner );
scanner: 指向遍历器的指针.
函数 cvGraphScanner 完成图的遍历过程,并释放遍历器的状态。
用于树结点类型声明的(助手)宏
#define CV_TREE_NODE_FIELDS(node_type) / int flags; /* micsellaneous flags */ / int header_size; /* size of sequence header */ / struct node_type* h_prev; /* previous sequence */ / struct node_type* h_next; /* next sequence */ / struct node_type* v_prev; /* 2nd previous sequence */ / struct node_type* v_next; /* 2nd next sequence */
宏 CV_TREE_NODE_FIELDS() 用来声明一层次性结构,例如 CvSeq -- 所有动态结构的基本类型。如果树的节点是由该宏所声明的,那么就可以使用(该部分的)以下函数对树进行相关操作。
打开现存的存储结构或者创建新的文件存储结构
typedef struct CvTreeNodeIterator { const void* node; int level; int max_level; } CvTreeNodeIterator;
结构 CvTreeNodeIterator 用来对树进行遍历。该树的节点是由宏 CV_TREE_NODE_FIELDS 声明。
用来初始化树结点的迭代器
void cvInitTreeNodeIterator( CvTreeNodeIterator* tree_iterator, const void* first, int max_level );
tree_iterator: 初始化了的迭代器
first: (开始)遍历的第一个节点
max_level: 限制对树进行遍历的最高层(即:第 max_level 层)(假设第一个节点所在的层为第一层)。例如:1 指的是遍历第一个节点所在层,2 指的是遍历第一层和第二层
函数 cvInitTreeNodeIterator 用来初始化树的迭代器。
返回当前节点,并将迭代器 iterator 移向当前节点的下一个节点
void* cvNextTreeNode( CvTreeNodeIterator* tree_iterator );
tree_iterator: 初始化了的迭代器
函数 cvNextTreeNode 返回当前节点并且更新迭代器(iterator) -- 并将 iterator 移向(当前节点)下一个节点。换句话说,函数的行为类似于表达式 *p++ (通常的 C 指针 或 C++ 集合迭代器)。 如果没有更多的节点(即:当前节点为最后的节点),则函数返回值为 NULL.
返回当前节点,并将迭代器 iterator 移向当前节点的前一个节点
void* cvPrevTreeNode( CvTreeNodeIterator* tree_iterator );
tree_iterator: 初始化了的迭代器
函数 cvPrevTreeNode 返回当前节点并且更新迭代器(iterator) -- 并将 iterator 移向(当前节点的)前一个节点。换句话说,函数的行为类似于表达式 *p-- (通常的 C 指针 或 C++ 集合迭代器)。 如果没有更多的节点(即:当前节点为头节点),则函数返回值为 NULL.
CvSeq* cvTreeToNodeSeq( const void* first, int header_size, CvMemStorage* storage );
first: 初始树结点
header_size: 线性表的表头大小,大小通常为 sizeof(CvSeq)
函数 cvTreeToNodeSeq 将树的节点指针挨个的存放到线性表中。存放的顺序以深度为先。
将新的节点插入到树中
void cvInsertNodeIntoTree( void* node, void* parent, void* frame );
node: 待插入的节点
parent: 树中的父节点(即:含有子节点的节点)
frame: 顶部节点。如果 节点parent 等同于 节点frame, 则将节点的域 v_prev 设为 NULL 而不是 parent.
函数 cvInsertNodeIntoTree 将另一个节点插入到树中。函数不分配任何内存,仅仅修改树节点的连接关系。
从树中删除节点
void cvRemoveNodeFromTree( void* node, void* frame );
node:待删除的节点。
frame: 顶部节点。如果 node->v.prev = NULL 且 node->h.prev = NULL, 则将 frame->v.next 设为 node->h.next
函数 cvRemoveNodeFromTree 从树中删除节点。它不会释放任何内存,仅仅修改树中节点的连接关系
绘图函数作用于任何象素深度的矩阵/图像. Antialiasing技术只能在8位图像上实现.所有的函数包括彩色图像的色彩参数(色彩参数是指rgb它是由宏CV_RGB或cvScalar函数构成。)和灰度图像的亮度。
.如果一幅绘制图形部分或全部位于图像之外,那么对它先做裁剪。 对于彩色图像正常的色彩通道是B(蓝),G(绿),R(红)..。如果需要其它的色彩,可以通过cvScalar中的特殊色彩通道构造色彩,或者在绘制图像之前或之后 使用 cvCvtColor或者cvTransform来转换。
创建 一个色彩值.
#define CV_RGB( r, g, b ) cvScalar( (b), (g), (r) )
绘制连接两个点的线段
void cvLine( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8, int shift=0 );
8
(or 0
) - 8-(connected line)连接 线。
4
- 4-(connected line)连接线。
CV_AA
- antialiased 线条。
函数cvLine 在图像中的点1和点2之间画一条线段。线段被图像或感兴趣的矩形所裁剪。对于具有整数坐标的 non-antialiasing 线条,使用8-连接或者4-连接Bresenham 算法。画粗线条时结尾是圆形的。画 antialiased 线条使用高斯滤波。对于特殊颜色的线条使用宏 CV_RGB( r, g, b )
。
绘制简单、指定粗细或者带填充的 矩形
void cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8, int shift=0 );
函数 cvRectangle 通过对角线上的两个顶点绘制矩形。
绘制圆形。
void cvCircle( CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int line_type=8, int shift=0 );
函数cvCircle绘制或填充一个给定圆心和半径的圆。圆被感兴趣矩形所裁剪。 若指定圆的颜色,可以使用宏 CV_RGB ( r, g, b )
。
绘制椭圆圆弧和椭圆扇形。
void cvEllipse( CvArr* img, CvPoint center, CvSize axes, double angle, double start_angle, double end_angle, CvScalar color, int thickness=1, int line_type=8, int shift=0 );
函数cvEllipse用来绘制或者填充一个简单的椭圆弧或椭圆扇形。圆弧被ROI矩形所忽略。反走样弧线和粗弧线使用线性分段近似值。所有的角都是以角度的形式给定的。图片下面要解释参数的含义。
Parameters of Elliptic Arc
填充多边形内部
void cvFillPoly( CvArr* img, CvPoint** pts, int* npts, int contours, CvScalar color, int line_type=8, int shift=0 );
函数cvFillPoly用于一个单独被多变形轮廓所限定的区域内进行填充。函数可以填充复杂的区域,例如,有漏洞的区域和有交叉点的区域等等。
填充多边形外部
void cvFillConvexPoly( CvArr* img, CvPoint* pts, int npts, CvScalar color, int line_type=8, int shift=0 );
函数cvFillConvexPoly填充多边形限定区域的。这个函数比函数cvFillPoly 在响应速度上更快。它除了可以填充多边形区域的外部还可以填充任何的单调多边形。例如:一个被水平线(扫描线)至多两次截断的多边形。
绘制多边形。
void cvPolyLine( CvArr* img, CvPoint** pts, int* npts, int contours, int is_closed, CvScalar color, int thickness=1, int line_type=8, int shift=0 );
函数cvPolyLine 绘制一个简单的或多样的多角曲线。
字体结构初始化。
void cvInitFont( CvFont* font, int font_face, double hscale, double vscale, double shear=0, int thickness=1, int line_type=8 );
CV_FONT_HERSHEY_SIMPLEX
- 正常大小无衬线字体。
CV_FONT_HERSHEY_PLAIN
- 小号无衬线字体。
CV_FONT_HERSHEY_DUPLEX
- 正常大小无衬线字体。( 比CV_FONT_HERSHEY_SIMPLEX更复杂
)
CV_FONT_HERSHEY_COMPLEX
- 正常大小有衬线字体。
CV_FONT_HERSHEY_TRIPLEX
- 正常大小有衬线字体 ( 比CV_FONT_HERSHEY_COMPLEX更复杂
)
CV_FONT_HERSHEY_COMPLEX_SMALL
- CV_FONT_HERSHEY_COMPLEX 的小译本。
CV_FONT_HERSHEY_SCRIPT_SIMPLEX
- 手写风格字体。
CV_FONT_HERSHEY_SCRIPT_COMPLEX
- 比CV_FONT_HERSHEY_SCRIPT_SIMPLEX更复杂。
CV_FONT_ITALIC 字体标记合成
。就是斜体字。
函数cvInitFont完成对文本的描述。
在图像中加入文本。
void cvPutText( CvArr* img, const char* text, CvPoint org, const CvFont* font, CvScalar color );
函数cvPutText将具有指定字体的和指定颜色的文本加载到图像中。加载到图像中的文本被感兴趣的矩形框圈定。
设置字符串文本的宽度和高度。
void cvGetTextSize( const char* text_string, const CvFont* font, CvSize* text_size, int* baseline );
函数cvGetTextSize是用于当特殊的字体在指定的字符串中被使用的时候计算binding rectangle的。
在图像中绘制简单的和复杂的轮廓。
void cvDrawContours( CvArr *img, CvSeq* contour, CvScalar external_color, CvScalar hole_color, int max_level, int thickness=1, int line_type=8 );
contour
but draws child contours of contour
up to abs(max_level
)-1 level.
函数cvDrawContours cvDrawContours 如果 thickness
>=0 在图像中绘制轮廓。如果 thickness
<0 在限定的区域内绘制轮廓。
#include "cv.h" #include "highgui.h" int main( int argc, char** argv ) { IplImage* src; // 第一条命令行参数确定了图像的文件名。 if( argc == 2 && (src=cvLoadImage(argv[1], 0))!= 0) { IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3 ); CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* contour = 0; cvThreshold( src, src, 1, 255, CV_THRESH_BINARY ); cvNamedWindow( "Source", 1 ); cvShowImage( "Source", src ); cvFindContours( src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); cvZero( dst ); for( ; contour != 0; contour = contour->h_next ) { CvScalar color = CV_RGB( rand()&255, rand()&255, rand()&255 ); /* 用1替代 CV_FILLED 所指示的轮廓外形 */ cvDrawContours( dst, contour, color, color, -1, CV_FILLED, 8 ); } cvNamedWindow( "Components", 1 ); cvShowImage( "Components", dst ); cvWaitKey(0); } }
在样本中用1替代 CV_FILLED 所指示的轮廓外形。
文件存储器的初始化
typedef struct CvFileStorage { ... // hidden fields } CvFileStorage;
构造函数 CvFileStorage 是将磁盘上存储的文件关联起来的“黑匣子” 。在下列函数描述中利用CvFileStorage
输入并允许存储或打开由标量值组成的层次集合,根据 CXCore 对象(例如 矩阵,序列,图表 ) 和用户自定义对象。
CXCore 能将数据读入或写入 XML (http://www.w3c.org/XML) or YAML (http://www.yaml.org) 格式. 下面这个例子是利用CXCore函数将3×3单位浮点矩阵存入XML 和 YAML文档。
3 3 - f
1. 0. 0. 0. 1. 0. 0. 0. 1.
%YAML:1.0 A: !!opencv-matrix rows: 3 cols: 3 dt: f data: [ 1., 0., 0., 0., 1., 0., 0., 0., 1.]
从例子中可以看到, XML试用嵌套标签来表现层次,而 YAML用缩排来表现(类似于Python语言) 。
相同的 CXCore 函数也能够在这两种格式下读写数据,特殊的格式决定了文件的扩展名, .xml 是 XML 的扩展名, .yml 或 .yaml 是 YAML的扩展名。
文件存储器节点
/* 文件节点类型 */ #define CV_NODE_NONE 0 #define CV_NODE_INT 1 #define CV_NODE_INTEGER CV_NODE_INT #define CV_NODE_REAL 2 #define CV_NODE_FLOAT CV_NODE_REAL #define CV_NODE_STR 3 #define CV_NODE_STRING CV_NODE_STR #define CV_NODE_REF 4 /* not used */ #define CV_NODE_SEQ 5 #define CV_NODE_MAP 6 #define CV_NODE_TYPE_MASK 7 /* 可选标记 */ #define CV_NODE_USER 16 #define CV_NODE_EMPTY 32 #define CV_NODE_NAMED 64 #define CV_NODE_TYPE(tag) ((tag) & CV_NODE_TYPE_MASK) #define CV_NODE_IS_INT(tag) (CV_NODE_TYPE(tag) == CV_NODE_INT) #define CV_NODE_IS_REAL(tag) (CV_NODE_TYPE(tag) == CV_NODE_REAL) #define CV_NODE_IS_STRING(tag) (CV_NODE_TYPE(tag) == CV_NODE_STRING) #define CV_NODE_IS_SEQ(tag) (CV_NODE_TYPE(tag) == CV_NODE_SEQ) #define CV_NODE_IS_MAP(tag) (CV_NODE_TYPE(tag) == CV_NODE_MAP) #define CV_NODE_IS_COLLECTION(tag) (CV_NODE_TYPE(tag) >= CV_NODE_SEQ) #define CV_NODE_IS_FLOW(tag) (((tag) & CV_NODE_FLOW) != 0) #define CV_NODE_IS_EMPTY(tag) (((tag) & CV_NODE_EMPTY) != 0) #define CV_NODE_IS_USER(tag) (((tag) & CV_NODE_USER) != 0) #define CV_NODE_HAS_NAME(tag) (((tag) & CV_NODE_NAMED) != 0) #define CV_NODE_SEQ_SIMPLE 256 #define CV_NODE_SEQ_IS_SIMPLE(seq) (((seq)->flags & CV_NODE_SEQ_SIMPLE) != 0) typedef struct CvString { int len; char* ptr; } CvString; /*关键字readed的原理是 文件存储器对存储的无用信息进行加速查找操作 */ typedef struct CvStringHashNode { unsigned hashval; CvString str; struct CvStringHashNode* next; } CvStringHashNode; /* 文件存储器的基本元素是-标量或集合*/ typedef struct CvFileNode { int tag; struct CvTypeInfo* info; /* 类型信息(只能用于用户自定义对象,对于其它对象它为0) */ union { double f; /* 浮点数*/ int i; /* 整形数 */ CvString str; /* 字符文本 */ CvSeq* seq; /* 序列 (文件节点的有序集合) */ struct CvMap* map; /*图表 (指定的文件节点的集合 ) */ } data; } CvFileNode;
这个构造函数只是用于重新找到文件存储器上的数据(例如 ,从文件中下载数据)。 当数据已经写入文件时利用最小的缓冲继续完成,此时没有数据存放在文件存储器。
相反,当从文件中读数据时,所有文件在内存中像树一样被解析和描绘。树的每一个节点被CvFileNode表现出来。文件节点N的类型能够通过CV_NODE_TYPE(N->tag)
被重新找到。一些节点(叶结点)作为变量:字符串文本,整数,浮点数。其它的文件节点是集合文件节点,有两个类型集合:序列和图表 (我们这里使用 YAML 符号,无论用哪种方法,对于XML是同样有效)。序列(不要与CvSeq混淆) 是由有序的非指定文件节点构成的,图表是由无序的指定文件节点构成的。因而 ,序列的原理是通过索引(cvGetSepElem)来存取,图形的原理是通过姓名(cvGetFileNodeByName)来存取 下表描述不同类型的节点:
Type | CV_NODE_TYPE(node->tag) | Value |
Integer | CV_NODE_INT | node->data.i |
Floating-point | CV_NODE_REAL | node->data.f |
Text string | CV_NODE_STR | node->data.str.ptr |
Sequence | CV_NODE_SEQ | node->data.seq |
Map | CV_NODE_MAP | node->data.map* |
CvMap
There 是一个隐藏的构造函数)。图形原理可以被cvGetFileNodeByName函数重新得到通过指针指向图表文件节点。
一个用户对象是一个标准的类型实例,例如CvMat, CvSeq等,或者任何一个已注册的类型使用cvRegisterTypeInfo。这样的对象,例如图表(像表现XML 和 YAM示例文件一样)是最初在文件中表现出来的 。在文件存储器打开并分析之后。当用户调用cvRead或cvReadByName函数时 那么对象将请求被解析 (内存中原来的表述)。
显示属性
typedef struct CvAttrList { const char** attr; /* NULL-将中止一对数组 (attribute_name,attribute_value) */ struct CvAttrList* next; /* 指针指向下一个属性块 */ } CvAttrList; /* initializes初始化构造函数CvAttrList */ inline CvAttrList cvAttrList( const char** attr=NULL, CvAttrList* next=NULL ); /* 返回值为属性值,找不到适合的属性则返回值为0(NULL)*/ const char* cvAttrValue( const CvAttrList* attr, const char* attr_name );
在执行当前的属性时通常需通过特定的参数来完成,除了对象类型说明(type_id
属性)以外,标示符不支持 XML 属性。
打开文件存储器读/写数据。
CvFileStorage* cvOpenFileStorage( const char* filename, CvMemStorage* memstorage, int flags );
CV_STORAGE_READ
- 内存处于读状态。
CV_STORAGE_WRITE
- 内存处于写状态。
函数cvOpenFileStorage打开文件存储器读写数据,之后建立文件或继续使用现有的文件 。文件扩展名决定读文件的类型 : .xml
是 XML的扩展名, .yml
或 .yaml
是 YAML的扩展名。该函数的返回指针指向CvFileStorage构造函数。
释放文件存储单元
void cvReleaseFileStorage( CvFileStorage** fs );
函数cvReleaseFileStorage 关闭一个相关的文件存储器并释放所有的临时内存。只有在内存的I/O操作完成后才能关闭文件存储器。
向文件存储器中写数据
void cvStartWriteStruct( CvFileStorage* fs, const char* name, int struct_flags, const char* type_name=NULL, CvAttrList attributes=cvAttrList());
CV_NODE_SEQ
- 被写入的数据结构为序列结构。这样的数据没有名称。
CV_NODE_MAP
- 被写入的数据结构为图表结构。这样的数据含有名称。
CV_NODE_FLOW
- 这个可选择标识符只能作用于YAML流。被写入的数据结构被看做一个数据流(不是数据块),它更加紧凑是所有标量的基础。
type_id
属性写入。如果是YAML 用冒号后面的数据结构名写入, 基本上它是伴随用户对象出现的。当存储器读时,编码类型名通常决定对象类型 (见Cvtypeinfo和cvfindtypeinfo) 。
函数 cvStartWriteStruct 开始写复合的数据结构(数据集合)包括序列或图表, 标量和结构被写入, cvEndWriteStruct 被指定. 该函数能够合并一些对象或写入一些用户对象函数。
中止写数据结构
void cvEndWriteStruct( CvFileStorage* fs );
函数cvEndWriteStruct 中止普通的写数据操作。
写入一个整形值
void cvWriteInt( CvFileStorage* fs, const char* name, int value );
函数 cvWriteInt 将一个单独的整形值(有名称的或无名称的)写入文件存储器。
写入一个浮点形值
void cvWriteReal( CvFileStorage* fs, const char* name, double value );
函数 cvWriteReal 将一个单独的整形值(有名称的或无名称的)写入文件存储器。 特殊的值被编码:The special values are encoded: NaN (一个编码量的非) 例如 .NaN, ±无穷大 例如 +.Inf (-.Inf)。
下面的实例表明 怎样使用低级写函数存储自定义数据结构。
void write_termcriteria( CvFileStorage* fs, const char* struct_name, CvTermCriteria* termcrit ) { cvStartWriteStruct( fs, struct_name, CV_NODE_MAP, NULL, cvAttrList(0,0)); cvWriteComment( fs, "termination criteria", 1 ); // 正确的描述 if( termcrit->type & CV_TERMCRIT_ITER ) cvWriteInteger( fs, "max_iterations", termcrit->max_iter ); if( termcrit->type & CV_TERMCRIT_EPS ) cvWriteReal( fs, "accuracy", termcrit->epsilon ); cvEndWriteStruct( fs ); }
写入字符串文本
void cvWriteString( CvFileStorage* fs, const char* name, const char* str, int quote=0 );
函数 cvWriteString将字符串文本写入文件存储器。
写入注释
void cvWriteComment( CvFileStorage* fs, const char* comment, int eol_comment );
函数 cvWriteComment将注释写入文件存储器。读内存时注释将被跳过,它只能被用于调试和描述。
打开下一个数据流
void cvStartNextStream( CvFileStorage* fs );
函数 cvStartNextStream 从文件存储器中打开下一个数据流。 YAML 和 XML 都支持多数据流。这对连接多个文件和恢复写入的程序很有用。
写入用户对象
void cvWrite( CvFileStorage* fs, const char* name, const void* ptr, CvAttrList attributes=cvAttrList() );
函数 cvWrite将对象写入文件存储器 。首先,使用cvTypeOf 查找恰当的类型信息。其次写入指定的方法类型信息。
属性被用于定制输入程序。下面的属性支持标准类型 The standard types support the following attributes (所有的*dt
属性在cvWriteRawData中都有相同的格式):
header_dt
-序列首位用户区的描述,它紧跟在CvSeq或CvChain(如果是自由序列)或CvContour(如果是轮廓或点序列)之后。 dt
- 序列原理的描述 。 recursive
- 如果属性没有被引用并且不等于“0”或“false",则所有的序列树(轮廓)都被存储。 CvGraph
header_dt
- 图表头用户区的描述,它紧跟在 CvGraph之后。 vertex_dt
- 图表顶点用户区的描述。 edge_dt
- 图表边用户区的描述( 注意磅值经常被写入,所以不需要详细的说明)。 下面的代码的含义是建立YAML文件用来描述CvFileStorage
:
#include "cxcore.h" int main( int argc, char** argv ) { CvMat* mat = cvCreateMat( 3, 3, CV_32F ); CvFileStorage* fs = cvOpenFileStorage( "example.yml", 0, CV_STORAGE_WRITE ); cvSetIdentity( mat ); cvWrite( fs, "A", mat, cvAttrList(0,0) ); cvReleaseFileStorage( &fs ); cvReleaseMat( &mat ); return 0; }
写入重数
void cvWriteRawData( CvFileStorage* fs, const void* src, int len, const char* dt );
([count]{'u'|'c'|'w'|'s'|'i'|'f'|'d'})...
, 这些特性与C语言的类型相似 :
count
是用来选择计数值的确定类型。 例如, dt='2if'
是指任意的一个数组元素的结构是:2个字节整形数,后面跟一个单精度浮点数。上面的说明与 ‘iif'
, '2i1f'
等相同。另外一个例子: dt='u'
是指 一个由类型组成的数组, dt='2d'
是指由两个双精度浮点数构成的数组。
函数 cvWriteRawData 将重数写入文件存储器。那些元素由单独的重数构成。一些函数命令能够被循环调用替换包括 cvWriteInt 和 cvWriteReal命令,但是一个单独的命令更加有效。 注意,那是因为元素没有名字, 把它们写入序列比写入图表要好。
将文件节点写入另一个文件存储器
void cvWriteFileNode( CvFileStorage* fs, const char* new_node_name, const CvFileNode* node, int embed );
cvGetFileNodeName(节点)
.
函数 cvWriteFileNode将一个文件节点的拷贝写入文件存储器 可能请求的函数是: 将几个文件存储器合而为一。在XML 和YAML 之间变换格式等。
从文件存储器中得到数据有两种方法:第一, 查找文件节点包括那些被请求的数据;其次 ,利用手动或者使用自定义read
方法取得数据。
从文件存储器中得到一个高层节点
CvFileNode* cvGetRootFileNode( const CvFileStorage* fs, int stream_index=0 );
函数 cvGetRootFileNode 返回一个高层文件节点。 高层节点没有名称,它们和流相对应,接连存入文件存储器。如果超出索引, 函数返回NULL指针, 所以高层节点反复调用函数stream_index=0,1,...
,直到返回NULL指针。这个函数可以在文件存储器中反复调用。
在图表或者文件存储器中查找节点
CvFileNode* cvGetFileNodeByName( const CvFileStorage* fs, const CvFileNode* map, const char* name );
函数 cvGetFileNodeByName 文件节点通过name
查找文件节点 该节点在图表中被查找或者 ,如果指针为NULL,在内存中的高层文件节点中查找。 在图表中或者cvGetSeqElem序列中使用这个函数 。可能反复调用文件存储器i 加速确定某个多重表示值(例如 结构数组 )可能在cvGetHashedKey 和cvGetFileNode之中用到一个。
返回一个指向已有名称的唯一指针
CvStringHashNode* cvGetHashedKey( CvFileStorage* fs, const char* name, int len=-1, int create_missing=0 );
函数 cvGetHashedKey返回指向每一个特殊文件节点名的特殊指针。这个指针通过cvGetFileNode 函数。它比cvGetFileNodeByName快。
观察下面例子: 用二维图来表示一个点集,例:
%YAML:1.0 points: - { x: 10, y: 10 } - { x: 20, y: 20 } - { x: 30, y: 30 } # ...
因而,它使用哈希指针“x”和“y"加速对点的编译。
例:从一个文件存储器中读取顺序的结构
#include "cxcore.h" int main( int argc, char** argv ) { CvFileStorage* fs = cvOpenFileStorage( "points.yml", 0, CV_STORAGE_READ ); CvStringHashNode* x_key = cvGetHashedNode( fs, "x", -1, 1 ); CvStringHashNode* y_key = cvGetHashedNode( fs, "y", -1, 1 ); CvFileNode* points = cvGetFileNodeByName( fs, 0, "points" ); if( CV_NODE_IS_SEQ(points->tag) ) { CvSeq* seq = points->data.seq; int i, total = seq->total; CvSeqReader reader; cvStartReadSeq( seq, &reader, 0 ); for( i = 0; i < total; i++ ) { CvFileNode* pt = (CvFileNode*)reader.ptr; #if 1 /* 快变量 */ CvFileNode* xnode = cvGetFileNode( fs, pt, x_key, 0 ); CvFileNode* ynode = cvGetFileNode( fs, pt, y_key, 0 ); assert( xnode && CV_NODE_IS_INT(xnode->tag) && ynode && CV_NODE_IS_INT(ynode->tag)); int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); #elif 1 /* 慢变量:不使用x值与y值 */ CvFileNode* xnode = cvGetFileNodeByName( fs, pt, "x" ); CvFileNode* ynode = cvGetFileNodeByName( fs, pt, "y" ); assert( xnode && CV_NODE_IS_INT(xnode->tag) && ynode && CV_NODE_IS_INT(ynode->tag)); int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); #else /* 最慢的可以轻松使用的变量 */ int x = cvReadIntByName( fs, pt, "x", 0 /* default value */ ); int y = cvReadIntByName( fs, pt, "y", 0 /* default value */ ); #endif CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); printf("%d: (%d, %d)/n", i, x, y ); } } cvReleaseFileStorage( &fs ); return 0; }
请注意,无论使用那一种方法访问图表 ,都比使用序列慢,例如上面的例子,在单一数字序列中点像数字一样被编译。
在图表或者文件存储器中查找节点
CvFileNode* cvGetFileNode( CvFileStorage* fs, CvFileNode* map, const CvStringHashNode* key, int create_missing=0 );
函数 cvGetFileNode 查找一个文件节点。函数能够插入一个新的节点,当它不在图表中时
返回文件节点名
const char* cvGetFileNodeName( const CvFileNode* node );
函数 cvGetFileNodeName 返回文件节点名或返回NULL(如果文件节点没有名称或者node为NULL 。
从文件节点中得到整形值
int cvReadInt( const CvFileNode* node, int default_value=0 );
函数 cvReadInt 从文件节点中返回整数。如果文件节点为NULL, default_value
被返回 。另外如果文件节点有类型 CV_NODE_INT
, 则 node->data.i
被返回 。如果文件节点有类型 CV_NODE_REAL
, 则 node->data.f
被修改成整数后返回。 另外一种情况是,结果不确定。
查找文件节点返回它的值
int cvReadIntByName( const CvFileStorage* fs, const CvFileNode* map, const char* name, int default_value=0 );
函数 cvReadIntByName是 cvGetFileNodeByName 和 cvReadInt的简单重叠.
从文件节点中得到浮点形值
double cvReadReal( const CvFileNode* node, double default_value=0. );
函数cvReadReal 从文件节点中返回浮点形值。如果文件节点为NULL, default_value
被返回 。另外如果文件节点有类型 CV_NODE_REAL
, 则
node->data.f
被返回 。如果文件节点有类型 CV_NODE_INT
, 则 node->data.i
被修改成浮点数后返回。 另外一种情况是,结果不确定。 .
查找文件节点返回它的浮点形值
double cvReadRealByName( const CvFileStorage* fs, const CvFileNode* map, const char* name, double default_value=0. );
函数 cvReadRealByName 是 cvGetFileNodeByName 和cvReadReal 的简单重叠。
从文件节点中得到字符串文本
const char* cvReadString( const CvFileNode* node, const char* default_value=NULL );
函数cvReadString 从文件节点中返回字符串文本。如果文件节点为NULL, default_value
被返回 。另外如果文件节点有类型CV_NODE_STR
, 则data.str.ptr
被返回 。 另外一种情况是,结果不确定。
查找文件节点返回它的字符串文本
const char* cvReadStringByName( const CvFileStorage* fs, const CvFileNode* map, const char* name, const char* default_value=NULL );
函数 cvReadStringByName是 cvGetFileNodeByName 和cvReadString 的简单重叠。
解释对象并返回指向它的指针
void* cvRead( CvFileStorage* fs, CvFileNode* node, CvAttrList* attributes=NULL );
函数 cvRead 解释用户对象 (在文件存储器子树中建立新的对象)并返回。对象被解释 ,必须按原有的支持读方法的类型 (参考 CvTypeInfo).用类型名决定对象,并在文件中被解释 。如果对象是动态结构,它将在内存中通过cvOpenFileStorage或者使NULL指针被建立。在临时性内存中。当cvReleaseFileStorage 被调用时释放内存。 如果对象不是动态结构 ,将在堆中被建立用专用函数或通用函数cvRelease. 释放内存。
查找对象并解释
void* cvReadByName( CvFileStorage* fs, const CvFileNode* map, const char* name, CvAttrList* attributes=NULL );
函数 cvReadByName 是由cvGetFileNodeByName 和 cvRead叠合的。 .
读重数
void cvReadRawData( const CvFileStorage* fs, const CvFileNode* src, void* dst, const char* dt );
函数 cvReadRawData从有序的文件节点中读取元素。
初始化文件节点读取器
void cvStartReadRawData( const CvFileStorage* fs, const CvFileNode* src, CvSeqReader* reader );
函数 cvStartReadRawData 初始化顺序读取器从文件节点中读取数据。能够通过cvReadRawDataSlice.初始化文件读取器 。
初始化文件节点按序读取器
void cvReadRawDataSlice( const CvFileStorage* fs, CvSeqReader* reader, int count, void* dst, const char* dt );
函数 cvReadRawDataSlice 从文件节点读一个或多个元素,组成一个序列用于指定数组。读入元素的总数由其他数组的元素总和构成的。例如 如果 dt='2if'
, 函数将读是总数三倍的序列元素。
类型信息
typedef int (CV_CDECL *CvIsInstanceFunc)( const void* struct_ptr ); typedef void (CV_CDECL *CvReleaseFunc)( void** struct_dblptr ); typedef void* (CV_CDECL *CvReadFunc)( CvFileStorage* storage, CvFileNode* node ); typedef void (CV_CDECL *CvWriteFunc)( CvFileStorage* storage, const char* name, const void* struct_ptr, CvAttrList attributes ); typedef void* (CV_CDECL *CvCloneFunc)( const void* struct_ptr ); typedef struct CvTypeInfo { int flags; /* 不常用 */ int header_size; /* (CvTypeInfo)的大小 */ struct CvTypeInfo* prev; /* 在列表中已定义过的类型 */ struct CvTypeInfo* next; /* 在列表中下一个已定义过的类型 */ const char* type_name; /*定义类型名,并写入文件存储器 */ /* methods */ CvIsInstanceFunc is_instance; /* 选择被传递的对象属于的类型 */ CvReleaseFunc release; /* 释放对象的内存空间 */ CvReadFunc read; /* 从文件存储器中读对象 */ CvWriteFunc write; /* 将对象写入文件存储器 */ CvCloneFunc clone; /* 复制一个对象 */ } CvTypeInfo;
结构 CvTypeInfo包含的信息包括标准的或用户自定义的类型。类型有没有包含指向相应的CvTypeInfo结构的指针。在已有的对象中查找类型的方法是使用cvTypeOf函数。已有的信息可以通过类型名使用cvFindType来查找,这个方法在从文件存储器中读对象的时候被使用。 用户可以通过cvRegisterType定义一个新的类型 ,并将类型信息结构加到文件列表的开始端, 它可以从标准类型中建立专门的类型,不必考虑基本的方法。
定义新类型
void cvRegisterType( const CvTypeInfo* info );
函数 cvRegisterType 定义一个新类型,可以通过信息来描述它。
删除定义的类型
void cvUnregisterType( const char* type_name );
函数 cvUnregisterType通过指定的名称删除已定义的类型。 如果不知道类型名,可以用cvTypeOf或者连续扫描类型列表,从cvFirstType开始,然后调用 cvUnregisterType(info->type_name)
。
返回类型列表的首位。
CvTypeInfo* cvFirstType( void );
函数 cvFirstType 返回类型列表中的第一个类型。
通过类型名查找类型
CvTypeInfo* cvFindType( const char* type_name );
函数 cvFindType通过类型名查找指定的类型。如果找不到返回值为NULL。
返回对象的类型
CvTypeInfo* cvTypeOf( const void* struct_ptr );
函数 cvTypeOf 查找指定对象的类型。它反复扫描类型列表,调用每一个类型信息结构中的函数和方法与对象做比较,直到它们中的一个的返回值不为0或者所有的类型都被访问。
删除对象
void cvRelease( void** struct_ptr );
函数 cvRelease 查找指定对象的类型,然后调用release
。
克隆一个对象
void* cvClone( const void* struct_ptr );
函数 cvClone 查找指定对象的类型,然后调用 clone
。
存储对象到文件中
void cvSave( const char* filename, const void* struct_ptr, const char* name=NULL, const char* comment=NULL, CvAttrList attributes=cvAttrList());
filename
中列出。
函数 cvSave存储对象到文件。它给cvWrite提供一个简单的界面。
从文件中打开对象。
void* cvLoad( const char* filename, CvMemStorage* memstorage=NULL, const char* name=NULL, const char** real_name=NULL );
name=NULL
时有效)。
函数 cvLoad 从文件中打开对象。它给cvRead提供一个简单的界面.对象被打开之后,文件存储器被关闭,所有的临时缓冲区被删除。因而,打开一个动态结构,如序列,轮廓或图像。 它应该通过一个有效的目的文件内存发挥作用。
检查输入数组的每一个元素是否是非法值
int cvCheckArr( const CvArr* arr, int flags=0, double min_val=0, double max_val=0); #define cvCheckArray cvCheckArr
CV_CHECK_RANGE
- 如果设置这个标志, 函数检查数组的每一个值是否在范围 [minVal,maxVal) 以内,如果不在范围内它不检查每一个元素是否是 NaN 或者 ±Infinity。
CV_CHECK_QUIET
- 如果设置这个标志, 如果一个元素是非法的或者越界的,函数 不会产生一个错误。
CV_CHECK_RANGE
被设置的时候它才有作用。
CV_CHECK_RANGE
被设置的时候它才有作用。
函数 cvCheckArr 检查每一个数组元素不是 NaN 也不是 ±Infinity 。如果 CV_CHECK_RANGE
被设定, 它也检查每一个元素是大于等于 minVal 并且小于maxVal。如果检查成功函数返回非零值,例如,所有元素都是合法的并且在范围内,如果检查失败则返回 0 。 在后一种情况下如果 CV_CHECK_QUIET
标志没有被设定, 函数报出运行错误。
按照给定的群的数目拆分向量的集合(Splits set of vectors by given number of clusters)
void cvKMeans2( const CvArr* samples, int cluster_count, CvArr* labels, CvTermCriteria termcrit );
函数 cvKMeans2 执行 k-means 算法 查找 cluster_count
群的中心并沿着群分组输入样例,输出 labels(i)
包含样例的群索引,存储在样例矩阵的第 i 行。
#include "cxcore.h" #include "highgui.h" void main( int argc, char** argv ) { #define MAX_CLUSTERS 5 CvScalar color_tab[MAX_CLUSTERS]; IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 ); CvRNG rng = cvRNG(0xffffffff); color_tab[0] = CV_RGB(255,0,0); color_tab[1] = CV_RGB(0,255,0); color_tab[2] = CV_RGB(100,100,255); color_tab[3] = CV_RGB(255,0,255); color_tab[4] = CV_RGB(255,255,0); cvNamedWindow( "clusters", 1 ); for(;;) { int k, cluster_count = cvRandInt(&rng)%MAX_CLUSTERS + 1; int i, sample_count = cvRandInt(&rng)%1000 + 1; CvMat* points = cvCreateMat( sample_count, 1, CV_32FC2 ); CvMat* clusters = cvCreateMat( sample_count, 1, CV_32SC1 ); /* generate random sample from multigaussian distribution */ for( k = 0; k < cluster_count; k++ ) { CvPoint center; CvMat point_chunk; center.x = cvRandInt(&rng)%img->width; center.y = cvRandInt(&rng)%img->height; cvGetRows( points, &point_chunk, k*sample_count/cluster_count, k == cluster_count - 1 ? sample_count : (k+1)*sample_count/cluster_count ); cvRandArr( &rng, &point_chunk, CV_RAND_NORMAL, cvScalar(center.x,center.y,0,0), cvScalar(img->width/6, img->height/6,0,0) ); } /* shuffle samples */ for( i = 0; i < sample_count/2; i++ ) { CvPoint2D32f* pt1 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count; CvPoint2D32f* pt2 = (CvPoint2D32f*)points->data.fl + cvRandInt(&rng)%sample_count; CvPoint2D32f temp; CV_SWAP( *pt1, *pt2, temp ); } cvKMeans2( points, cluster_count, clusters, cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0 )); cvZero( img ); for( i = 0; i < sample_count; i++ ) { CvPoint2D32f pt = ((CvPoint2D32f*)points->data.fl)[i]; int cluster_idx = clusters->data.i[i]; cvCircle( img, cvPointFrom32f(pt), 2, color_tab[cluster_idx], CV_FILLED ); } cvReleaseMat( &points ); cvReleaseMat( &clusters ); cvShowImage( "clusters", img ); int key = cvWaitKey(0); if( key == 27 ) // 'ESC' break; } }
Splits sequence into equivalency classes拆分序列为等效的类
typedef int (CV_CDECL* CvCmpFunc)(const void* a, const void* b, void* userdata); int cvSeqPartition( const CvSeq* seq, CvMemStorage* storage, CvSeq** labels, CvCmpFunc is_equal, void* userdata );
seq->storage
存储输出标签 labels
is_equal
函数的透明指针。
函数 cvSeqPartition 执行二次方程算法为拆分集合为一个或者更多的等效类。 函数返回等效等效类的数目。
#include "cxcore.h" #include "highgui.h" #includeCvSeq* point_seq = 0; IplImage* canvas = 0; CvScalar* colors = 0; int pos = 10; int is_equal( const void* _a, const void* _b, void* userdata ) { CvPoint a = *(const CvPoint*)_a; CvPoint b = *(const CvPoint*)_b; double threshold = *(double*)userdata; return (double)(a.x - b.x)*(a.x - b.x) + (double)(a.y - b.y)*(a.y - b.y) <= threshold; } void on_track( int pos ) { CvSeq* labels = 0; double threshold = pos*pos; int i, class_count = cvSeqPartition( point_seq, 0, &labels, is_equal, &threshold ); printf("%4d classes/n", class_count ); cvZero( canvas ); for( i = 0; i < labels->total; i++ ) { CvPoint pt = *(CvPoint*)cvGetSeqElem( point_seq, i, 0 ); CvScalar color = colors[*(int*)cvGetSeqElem( labels, i, 0 )]; cvCircle( canvas, pt, 1, color, -1 ); } cvShowImage( "points", canvas ); } int main( int argc, char** argv ) { CvMemStorage* storage = cvCreateMemStorage(0); point_seq = cvCreateSeq( CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), storage ); CvRNG rng = cvRNG(0xffffffff); int width = 500, height = 500; int i, count = 1000; canvas = cvCreateImage( cvSize(width,height), 8, 3 ); colors = (CvScalar*)cvAlloc( count*sizeof(colors[0]) ); for( i = 0; i < count; i++ ) { CvPoint pt; int icolor; pt.x = cvRandInt( &rng ) % width; pt.y = cvRandInt( &rng ) % height; cvSeqPush( point_seq, &pt ); icolor = cvRandInt( &rng ) | 0x00404040; colors[i] = CV_RGB(icolor & 255, (icolor >> 8)&255, (icolor >> 16)&255); } cvNamedWindow( "points", 1 ); cvCreateTrackbar( "threshold", "points", &pos, 50, on_track ); on_track(pos); cvWaitKey(0); return 0; }
在 OpenCV 中错误处理和 IPL (Image Processing Library)很相似。假如错误处理函数不返回错误代码,而是用CV_ERROR 宏调用 cvError 函数报错,按次序地,用 cvSetErrStatus 函数设置错误状态,然后调用标准的或者用户自定义的错误处理器(它可以显示一个消息对话框,写出错误日志等等, 参考函数 cvRedirectError, cvNulDevReport, cvStdErrReport, cvGuiBoxReport)。每个程序的线程都有一个全局变量,它包含了错误状态(一个整数值)。这个状态可以被 cvGetErrStatus 函数检索到。
有三个错误处理模式(参考 cvSetErrMode 和 cvGetErrMode):
事实上, Leaf 和 Parent 模式的语义被错误处理器执行,上面的描述对 cvNulDevReport, cvStdErrReport. cvGuiBoxReport 的行为有一些细微的差别,一些自定义的错误处理器可能语义上会有很大的不同。
报错,检查错误等的宏
/* special macros for enclosing processing statements within a function and separating them from prologue (resource initialization) and epilogue (guaranteed resource release) */ #define __BEGIN__ { #define __END__ goto exit; exit: ; } /* proceeds to "resource release" stage */ #define EXIT goto exit /* Declares locally 函数 name for CV_ERROR() use */ #define CV_FUNCNAME( Name ) / static char cvFuncName[] = Name /* Raises an error within the current context */ #define CV_ERROR( Code, Msg ) / { / cvError( (Code), cvFuncName, Msg, __FILE__, __LINE__ ); / EXIT; / } /* Checks status after calling CXCORE function */ #define CV_CHECK() / { / if( cvGetErrStatus() < 0 ) / CV_ERROR( CV_StsBackTrace, "Inner function failed." ); / } /* Provies shorthand for CXCORE function call and CV_CHECK() */ #define CV_CALL( Statement ) / { / Statement; / CV_CHECK(); / } /* Checks some condition in both debug and release configurations */ #define CV_ASSERT( Condition ) / { / if( !(Condition) ) / CV_ERROR( CV_StsInternal, "Assertion: " #Condition " failed" ); / } /* these macros are similar to their CV_... counterparts, but they do not need exit label nor cvFuncName to be defined */ #define OPENCV_ERROR(status,func_name,err_msg) ... #define OPENCV_ERRCHK(func_name,err_msg) ... #define OPENCV_ASSERT(condition,func_name,err_msg) ... #define OPENCV_CALL(statement) ...取代上面的讨论, 这里有典型的 CXCORE 函数和这些函数使用的样例。
#include "cxcore.h" #includevoid cvResizeDCT( CvMat* input_array, CvMat* output_array ) { CvMat* temp_array = 0; // declare pointer that should be released anyway. CV_FUNCNAME( "cvResizeDCT" ); // declare cvFuncName __BEGIN__; // start processing. There may be some declarations just after this macro, // but they couldn't be accessed from the epilogue. if( !CV_IS_MAT(input_array) || !CV_IS_MAT(output_array) ) // use CV_ERROR() to raise an error CV_ERROR( CV_StsBadArg, "input_array or output_array are not valid matrices" ); // some restrictions that are going to be removed later, may be checked with CV_ASSERT() CV_ASSERT( input_array->rows == 1 && output_array->rows == 1 ); // use CV_CALL for safe function call CV_CALL( temp_array = cvCreateMat( input_array->rows, MAX(input_array->cols,output_array->cols), input_array->type )); if( output_array->cols > input_array->cols ) CV_CALL( cvZero( temp_array )); temp_array->cols = input_array->cols; CV_CALL( cvDCT( input_array, temp_array, CV_DXT_FORWARD )); temp_array->cols = output_array->cols; CV_CALL( cvDCT( temp_array, output_array, CV_DXT_INVERSE )); CV_CALL( cvScale( output_array, output_array, 1./sqrt((double)input_array->cols*output_array->cols), 0 )); __END__; // finish processing. Epilogue follows after the macro. // release temp_array. If temp_array has not been allocated before an error occured, cvReleaseMat // takes care of it and does nothing in this case. cvReleaseMat( &temp_array ); } int main( int argc, char** argv ) { CvMat* src = cvCreateMat( 1, 512, CV_32F ); #if 1 /* no errors */ CvMat* dst = cvCreateMat( 1, 256, CV_32F ); #else CvMat* dst = 0; /* test error processing mechanism */ #endif cvSet( src, cvRealScalar(1.), 0 ); #if 0 /* change 0 to 1 to suppress error handler invocation */ cvSetErrMode( CV_ErrModeSilent ); #endif cvResizeDCT( src, dst ); // if some error occurs, the message box will popup, or a message will be // written to log, or some user-defined processing will be done if( cvGetErrStatus() < 0 ) printf("Some error occured" ); else printf("Everything is OK" ); return 0; }
返回当前错误状态
int cvGetErrStatus( void );
函数 cvGetErrStatus 返回当前错误状态 - 这个状态是被上一步调用的 cvSetErrStatus 设置的。 注意, 在 Leaf 模式下错误一旦发生程序立即被终止 ,因此对于总是需要调用函数后蔡获得控制的应用,可以调用 cvSetErrMode 函数将错误模式设置为 Parent 或 Silent 。
设置错误状态
void cvSetErrStatus( int status );
函数 cvSetErrStatus 设置错误状态为指定的值。 大多数情况下, 该函数 被用来重设错误状态(设置为 CV_StsOk
) 以从错误中恢复。在其他情况下调用 cvError 或 CV_ERROR 更自然一些。
返回当前错误模式
int cvGetErrMode( void );
函数 cvGetErrMode 返回当前错误模式 - 这个值是在之前被 cvSetErrMode 函数设定的。
设置当前错误模式
#define CV_ErrModeLeaf 0 #define CV_ErrModeParent 1 #define CV_ErrModeSilent 2 int cvSetErrMode( int mode );
函数 cvSetErrMode 设置指定的错误模式。关于不同的错误模式的讨论参考本节开始.
产生一个错误
int cvError( int status, const char* func_name, const char* err_msg, const char* file_name, int line );
函数 cvError 设置错误状态为指定的值(通过 cvSetErrStatus) ,如果错误模式不是 Silent, 调用错误处理器。
返回错误状态编码的原文描述
const char* cvErrorStr( int status );
函数 cvErrorStr 返回指定错误状态编码的原文描述。如果是步知道的状态该函数返回空 (NULL)指针。
设置一个新的错误处理器
typedef int (CV_CDECL *CvErrorCallback)( int status, const char* func_name, const char* err_msg, const char* file_name, int line ); CvErrorCallback cvRedirectError( CvErrorCallback error_handler, void* userdata=NULL, void** prev_userdata=NULL );
函数 cvRedirectError 在标准错误处理器或者有确定借口的自定义错误处理器中选择一个新的错误处理器 。错误处理器和 cvError 函数有相同的参数。 如果错误处理器返回非零的值, 程序终止, 否则, 程序继续运行。错误处理器通过 cvGetErrMode 检查当前错误模式而作出决定。
提供标准错误操作
int cvNulDevReport( int status, const char* func_name, const char* err_msg, const char* file_name, int line, void* userdata ); int cvStdErrReport( int status, const char* func_name, const char* err_msg, const char* file_name, int line, void* userdata ); int cvGuiBoxReport( int status, const char* func_name, const char* err_msg, const char* file_name, int line, void* userdata );
函数 cvNullDevReport, cvStdErrReport, cvGuiBoxReport 提供标准错误操作。cvGuiBoxReport 是 Win32 系统缺省的错误处理器, cvStdErrReport - 其他系统. cvGuiBoxReport 弹出错误描述的消息框并提供几个选择。 下面是一个消息框的例子,如果和例子中的错误描述相同,它和上面的例子代码可能是兼容的。
OpenCV ERROR: Bad argument (input_array or output_array are not valid matrices) in function cvResizeDCT, D:/User/VP/Projects/avl_proba/a.cpp(75) Terminating the application...
分配内存缓冲区
void* cvAlloc( size_t size );
函数 cvAlloc 分配字节缓冲区大小并返回分配的缓冲区的指针。如果错误处理函数产生了一个错误报告 则返回一个空(NULL)指针。 缺省地 cvAlloc 调用 icvAlloc 而 icvAlloc 调用 malloc ,然而用 cvSetMemoryManager 调用用户自定义的内存分配和释放函数也是可能的。
释放内存缓冲区
void cvFree( void** ptr );
函数 cvFree 释放被 cvAlloc 分配的缓冲区。在退出的时候它清除缓冲区指针,这就是为什么要使用双重指针的原因 。 如果 *buffer 已经是空(NULL), 函数什么也不做。
Returns number of tics
int64 cvGetTickCount( void );
函数 cvGetTickCount 返回从依赖于平台的事件(从启动开始 CPU 的ticks 数目, 从1970年开始的微秒数目等等)开始的 tics 的数目 。 该函数对于精确测量函数/用户代码的执行时间是很有用的。要转化 tics 的数目为时间单位,使用函数 cvGetTickFrequency 。
返回每个微秒的 tics 的数目
double cvGetTickFrequency( void );
函数 cvGetTickFrequency 返回每个微秒的 tics 的数目。 因此, cvGetTickCount() 和 cvGetTickFrequency() 将给出从依赖于平台的事件开始的 tics 的数目 。
Registers another module注册另外的模块
typedef struct CvPluginFuncInfo { void** func_addr; void* default_func_addr; const char* func_names; int search_modules; int loaded_from; } CvPluginFuncInfo; typedef struct CvModuleInfo { struct CvModuleInfo* next; const char* name; const char* version; CvPluginFuncInfo* func_tab; } CvModuleInfo; int cvRegisterModule( const CvModuleInfo* module_info );
函数 cvRegisterModule 添加模块到已注册模块列表中。模块被注册后,用 cvGetModuleInfo 函数可以检索到它的信息。注册模块可以通过 CXCORE的 支持利用优化插件 (IPP, MKL, ...)。 CXCORE , CV (computer vision), CVAUX (auxilary computer vision) 和 HIGHGUI (visualization & image/video acquisition) 自身就是模块的例子。 通常注册后共享库就被载入。参考 cxcore/src/cxswitcher.cpp and cv/src/cvswitcher.cpp 获取细节信息, 怎样注册的参考 cxcore/src/cxswitcher.cpp , cxcore/src/_cxipp.h 显示了 IPP 和 MKL 是怎样连接到模块的。
检索注册模块和插件的信息
void cvGetModuleInfo( const char* module_name, const char** version, const char** loaded_addon_plugins );
函数 cvGetModuleInfo 返回一个或者所有注册模块的信息。返回信息被存储到库当中,因此,用户不用释放或者修改返回的文本字符。
在优化/不优化两个模式之间切换
int cvUseOptimized( int on_off );
函数 cvUseOptimized 在两个模式之间切换,这里只有纯 C 才从 cxcore, OpenCV 等执行。如果可用 IPP 和 MKL 函数也可使用。 当 cvUseOptimized(0)
被调用, 所有的优化库都不被载入。该函数在调试模式下是很有用的, IPP&MKL 不工作, 在线跨速比较等。它返回载入的优化函数的数目。注意,缺省地优化插件是被载入的,因此在程序开始调用 cvUseOptimized(1)
是没有必要的(事实上, 它只会增加启动时间)
分配自定义/缺省内存管理函数
typedef void* (CV_CDECL *CvAllocFunc)(size_t size, void* userdata); typedef int (CV_CDECL *CvFreeFunc)(void* pptr, void* userdata); void cvSetMemoryManager( CvAllocFunc alloc_func=NULL, CvFreeFunc free_func=NULL, void* userdata=NULL );
userdata
可能用来确定上下文关系外,接口和 malloc
相似
free
相似
函数 cvSetMemoryManager 设置将被 cvAlloc,cvFree 和高级函数 (例如. cvCreateImage) 调用的用户自定义内存管理函数(代替 malloc 和 free)。 注意, 当用 cvAlloc 分配数据的时候该函数被调用。 当然, 为了避免无限循环调用, 它不允许从自定义分配/释放函数调用 cvAlloc 和 cvFree 。
如果 alloc_func
和 free_func
指针是 NULL
, 恢复缺省的内存管理函数。
切换图像 IPL 函数的分配/释放
typedef IplImage* (CV_STDCALL* Cv_iplCreateImageHeader) (int,int,int,char*,char*,int,int,int,int,int, IplROI*,IplImage*,void*,IplTileInfo*); typedef void (CV_STDCALL* Cv_iplAllocateImageData)(IplImage*,int,int); typedef void (CV_STDCALL* Cv_iplDeallocate)(IplImage*,int); typedef IplROI* (CV_STDCALL* Cv_iplCreateROI)(int,int,int,int,int); typedef IplImage* (CV_STDCALL* Cv_iplCloneImage)(const IplImage*); void cvSetIPLAllocators( Cv_iplCreateImageHeader create_header, Cv_iplAllocateImageData allocate_data, Cv_iplDeallocate deallocate, Cv_iplCreateROI create_roi, Cv_iplCloneImage clone_image ); #define CV_TURN_ON_IPL_COMPATIBILITY() / cvSetIPLAllocators( iplCreateImageHeader, iplAllocateImage, / iplDeallocate, iplCreateROI, iplCloneImage )
函数 cvSetIPLAllocators 使用 CXCORE 来进行图像 IPL 函数的 分配/释放 操作。 为了方便, 这里提供了环绕宏 CV_TURN_ON_IPL_COMPATIBILITY
。 当 IPL 和 CXCORE/OpenCV 同时使用以及调用 iplCreateImageHeader
等情况该函数很有用。如果 IPL 仅仅是被调用来进行数据处理,该函数就必要了,因为所有的分配/释放都由 CXCORE 来完成, 或者所有的分配/释放都由 IPL 和一些 OpenCV 函数来处理数据。
AbsDiff | AddWeighted | Avg |
AbsDiffS | Alloc | AvgSdv |
Add | And | |
AddS | AndS |
CalcCovarMatrix | CloneMat | CreateImage |
CartToPolar | CloneMatND | CreateImageHeader |
Cbrt | CloneSeq | CreateMat |
CheckArr | CloneSparseMat | CreateMatHeader |
Circle | Cmp | CreateMatND |
Clear*D | CmpS | CreateMatNDHeader |
ClearGraph | ConvertScale | CreateMemStorage |
ClearMemStorage | ConvertScaleAbs | CreateSeq |
ClearSeq | Copy | CreateSet |
ClearSet | CountNonZero | CreateSparseMat |
Clone | CreateChildMemStorage | CrossProduct |
CloneGraph | CreateData | CvtSeqToArray |
CloneImage | CreateGraph |
DCT | Det | DrawContours |
DFT | Div | |
DecRefData | DotProduct |
EigenVV | EndWriteStruct | Exp |
Ellipse | Error | |
EndWriteSeq | ErrorStr |
FastArctan | FindGraphEdgeByPtr | FlushSeqWriter |
FillConvexPoly | FindType | Free |
FillPoly | FirstType | |
FindGraphEdge | Flip |
GEMM | GetImageROI | GetTickFrequency |
Get*D | GetMat | GraphAddEdge |
GetCol | GetModuleInfo | GraphAddEdgeByPtr |
GetDiag | GetNextSparseNode | GraphAddVtx |
GetDims | GetRawData | GraphEdgeIdx |
GetElemType | GetReal*D | GraphRemoveEdge |
GetErrMode | GetRootFileNode | GraphRemoveEdgeByPtr |
GetErrStatus | GetRow | GraphRemoveVtx |
GetFileNode | GetSeqElem | GraphRemoveVtxByPtr |
GetFileNodeByName | GetSeqReaderPos | GraphVtxDegree |
GetFileNodeName | GetSetElem | GraphVtxDegreeByPtr |
GetGraphVtx | GetSize | GraphVtxIdx |
GetHashedKey | GetSubRect | GuiBoxReport |
GetImage | GetTextSize | Get |
GetImageCOI | GetTickCount |
InRange | InitMatHeader | InvSqrt |
InRangeS | InitMatNDHeader | Invert |
IncRefData | InitSparseMatIterator | IsInf |
InitFont | InitTreeNodeIterator | IsNaN |
InitImageHeader | InsertNodeIntoTree |
KMeans2 |
LUT | Load | |
Line | Log |
Mahalonobis | MemStorageAlloc | MinS |
MakeSeqHeaderForArray | MemStorageAllocString | Mul |
Mat | Merge | MulSpectrums |
Max | Min | MulTransposed |
MaxS | MinMaxLoc |
NextGraphItem | Norm | NulDevReport |
NextTreeNode | Not |
OpenFileStorage | Or | OrS |
PerspectiveTransform | Pow | PutText |
PolarToCart | PrevTreeNode | |
PolyLine | Ptr*D |
RNG | ReadString | ReleaseMat |
RandArr | ReadStringByName | ReleaseMatND |
RandInt | Rectangle | ReleaseMemStorage |
RandReal | RedirectError | ReleaseSparseMat |
Read | RegisterModule | RemoveNodeFromTree |
ReadByName | RegisterType | Repeat |
ReadInt | Release | ResetImageROI |
ReadIntByName | ReleaseData | Reshape |
ReadRawData | ReleaseFileStorage | ReshapeMatND |
ReadRawDataSlice | ReleaseGraphScanner | RestoreMemStoragePos |
ReadReal | ReleaseImage | Round |
ReadRealByName | ReleaseImageHeader |
SVBkSb | SeqSlice | SetZero |
SVD | SeqSort | Solve |
Save | Set | Split |
SaveMemStoragePos | Set*D | Sqrt |
ScaleAdd | SetAdd | StartAppendToSeq |
SeqElemIdx | SetData | StartNextStream |
SeqInsert | SetErrMode | StartReadRawData |
SeqInsertSlice | SetErrStatus | StartReadSeq |
SeqInvert | SetIPLAllocators | StartScanGraph |
SeqPartition | SetIdentity | StartWriteSeq |
SeqPop | SetImageCOI | StartWriteStruct |
SeqPopFront | SetImageROI | StdErrReport |
SeqPopMulti | SetMemoryManager | Sub |
SeqPush | SetNew | SubRS |
SeqPushFront | SetReal*D | SubS |
SeqPushMulti | SetRemove | Sum |
SeqRemove | SetRemoveByPtr | Set |
SeqRemoveSlice | SetSeqBlockSize | |
SeqSearch | SetSeqReaderPos |
Trace | Transpose | TypeOf |
Transform | TreeToNodeSeq |
UnregisterType | UseOptimized |
Write | WriteInt | WriteString |
WriteComment | WriteRawData | |
WriteFileNode | WriteReal |
Xor | XorS |