为使得OpenCV的整个库便于管理和扩充,将整个库分成若干子库,CxCore是最重要的一个子库,从“core"名字可以看出,该库提供了所有OpenCV运行时的一些最基本的数据结构,包括矩阵,数组的基本运算,包括出错处理的一些基本函数。具体分为下面若*分。
基础结构:
CvPoint
二维坐标系下的点,类型为整型
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 )
CvPoint2D32f
二维坐标下的点,类型为浮点
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 );
CvPoint3D32f
三维坐标下的点,类型为浮点
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 );
CvSize
矩形框大小,以像素为精度
typedef struct CvSize
{
int width; /* 矩形宽 */
int height; /* 矩形高 */
}
CvSize;
/* 构造函数 */
inline CvSize cvSize( int width, int height );
注意:构造函数的cv是小写!
CvSize2D32f
以亚像素精度标量矩形框大小
typedef struct CvSize2D32f
{
float width; /* 矩形宽 */
float height; /* 矩形高 */
}
CvSize2D32f;
/* 构造函数*/
inline CvSize2D32f cvSize2D32f( double width, double height );
{
CvSize2D32f s;
s.width = (float)width;
s.height = (float)height;
return s;
}
CvRect
矩形框的偏移和大小
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 );
{ CvRect os;
os.x = x;
os.y = y;
os.width = width;
os.height = heigth;
reture os;}
CvScalar
可存放在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,
double val2, double val3);
{ CvScalar arr;
arr.val[4] = {val0,val1,val2,val3};
reture arr;}
/* 构造函数:用val0123初始化所有val[0]...val[3] */
inline CvScalar cvScalarAll( double val0123 );
{ CvScalar arr;
arr.val[4] = {val0123,val0123,val0123,val0123,};
reture arr;}
/* 构造函数:用val0初始化val[0],用0初始化val[1],val[2],val[3] */
inline CvScalar cvRealScalar( double val0 );
{ CvScalar arr;
arr.val[4] = {val0};
reture arr;}
CvTermCriteria
迭代算法的终止准则
#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 );
/* 在满足max_iter和epsilon的条件下检查终止准则并将其转换使得type=CV_TERMCRIT_ITER+CV_TERMCRIT_EPS */
CvTermCriteria cvCheckTermCriteria( CvTermCriteria criteria,
double default_eps,
int default_max_iters );
CvMat
多通道矩阵
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;
CvMatND
多维、多通道密集数组
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;
CvSparseMat
多维、多通道稀疏数组
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;
IplImage
IPL 图像头
typedef struct _IplImage
{
int nSize; /* IplImage大小,=sizeof(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]; /* 被OpenCV忽略 */
int dataOrder; /* 0 - 交叉存取颜色通道,对三通道RGB图像,像素存储顺序为BGR BGR BGR ... BGR;
1 - 分开的颜色通道,对三通道RGB图像,像素存储顺序为RRR...R GGG...G BBB...B。
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 只支持其中的一个子集:
- alphaChannel 在OpenCV中被忽略。
- colorModel 和channelSeq 被OpenCV忽略。OpenCV颜色转换的唯一函数 cvCvtColor把原图像的颜色空间的目标图像的颜色空间作为一个参数。
- dataOrder 必须是IPL_DATA_ORDER_PIXEL (颜色通道是交叉存取),然而平面图像的被选择通道可以被处理,就像COI(感兴趣的通道)被设置过一样。
- align 是被OpenCV忽略的,而用 widthStep 去访问后继的图像行。
- 不支持maskROI 。处理MASK的函数把他当作一个分离的参数。MASK在 OpenCV 里是 8-bit,然而在 IPL他是 1-bit。
- tileInfo 不支持。
- BorderMode和BorderConst是不支持的。每个 OpenCV 函数处理像素的邻近的像素,通常使用单一的固定代码边际模式。
除了上述限制,OpenCV处理ROI有不同的要求。要求原图像和目标图像的尺寸或 ROI的尺寸必须(根据不同的操作,例如cvPyrDown 目标图像的宽(高)必须等于原图像的宽(高)除以2 ±1)精确匹配,而IPL处理交叉区域,如图像的大小或ROI大小可能是完全独立的。
CvArr
不确定数组
typedef void CvArr;
CvArr* 仅仅是被用于作函数的参数,用于指示函数接收的数组类型可以不止一个,如 IplImage*, CvMat* 甚至 CvSeq*. 最终的数组类型是在运行时通过分析数组头的前4 个字节判断。
数组操作
初始化
CreateImage
创建头并分配数据
IplImage* cvCreateImage( CvSize size, int depth, int channels );
size
图像宽、高.
depth
图像元素的位深度,可以是下面的其中之一:
IPL_DEPTH_8U - 无符号8位整型
IPL_DEPTH_8S - 有符号8位整型
IPL_DEPTH_16U - 无符号16位整型
IPL_DEPTH_16S - 有符号16位整型
IPL_DEPTH_32S - 有符号32位整型
IPL_DEPTH_32F - 单精度浮点数
IPL_DEPTH_64F - 双精度浮点数
channels
每个元素(像素)的颜色通道数量.可以是 1, 2, 3 或 4.通道是交叉存取的,例如通常的彩色图像数据排列是:
b0 g0 r0 b1 g1 r1 ...
虽然通常 IPL 图象格式可以存贮非交叉存取的图像,并且一些OpenCV 也能处理他, 但是这个函数只能创建交叉存取图像.
函数 cvCreateImage 创建头并分配数据,这个函数是下列的缩写型式
header = cvCreateImageHeader(size,depth,channels);
cvCreateData(header); //只是创建空间,并不会初始化空间内的数据
CreateImageHeader
分配,初始化,并且返回 IplImage结构
IplImage* cvCreateImageHeader( CvSize size, int depth, int channels );
size
图像宽、高.
depth
像深 (见 CreateImage).
channels
通道数 (见 CreateImage).
函数 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 宏)
ReleaseImageHeader
释放头
void cvReleaseImageHeader( IplImage** image );
image
双指针指向头内存分配单元.
函数 cvReleaseImageHeader 释放头. 相似于
if( image )
{
iplDeallocate( *image, IPL_IMAGE_HEADER | IPL_IMAGE_ROI );
*image = 0;
}
然而IPL函数不是作为默认的 (见 CV_TURN_ON_IPL_COMPATIBILITY 宏)
ReleaseImage
释放头和图像数据
void cvReleaseImage( IplImage** image );
image
双指针指向图像内存分配单元。
函数 cvReleaseImage 释放头和图像数据,相似于:
if( *image )
{
cvReleaseData( *image );
cvReleaseImageHeader( image );
}
InitImageHeader
初始化被用图分配的图像头
IplImage* cvInitImageHeader( IplImage* image, CvSize size, int depth,
int channels, int origin=0, int align=4 );
image
被初始化的图像头.
size
图像的宽高.
depth
像深(见 CreateImage).
channels
通道数(见 CreateImage).
origin
IPL_ORIGIN_TL 或 IPL_ORIGIN_BL.
align
图像行排列, 典型的 4 或 8 字节.
函数 cvInitImageHeader 初始化图像头结构, 指向用户指定的图像并且返回这个指针。
CloneImage
制作图像的完整拷贝
IplImage* cvCloneImage( const IplImage* image );
image
原图像.
函数 cvCloneImage 制作图像的完整拷贝包括头、ROI和数据
SetImageCOI
基于给定的值设置感兴趣通道
void cvSetImageCOI( IplImage* image, int coi );
image
图像头.
coi
感兴趣通道.
函数 cvSetImageCOI 基于给定的值设置感兴趣的通道。值 0 意味着所有的通道都被选定, 1 意味着第一个通道被选定等等。如果 ROI 是 NULL 并且COI!= 0, ROI 被分配. 然而大多数的 OpenCV 函数不支持 COI, 对于这种状况当处理分离图像/矩阵通道时,可以拷贝(通过 cvCopy 或cvSplit) 通道来分离图像/矩阵,处理后如果需要可再拷贝(通过cvCopy 或 cvCvtPlaneToPix)回来.
GetImageCOI
返回感兴趣通道号
int cvGetImageCOI( const IplImage* image );
image
图像头.
函数cvGetImageCOI 返回图像的感兴趣通道(当所有的通道都被选中返回值是0).
SetImageROI
基于给定的矩形设置'感兴趣'区域
void cvSetImageROI( IplImage* image, CvRect rect );
image
图像.
rect
ROI 矩形.
函数 cvSetImageROI 基于给定的矩形设置图像的 ROI(感兴趣区域) . 如果ROI是NULL 并且参数RECT的值不等于整个图像, ROI被分配. 不像 COI, 大多数的 OpenCV 函数支持 ROI 并且处理它就像它是一个分离的图像 (例如, 所有的像素坐标从ROI的左上角或左下角(基于图像的结构)计算。
ResetImageROI
释放图像的ROI
void cvResetImageROI( IplImage* image );
image
图像头.
函数 cvResetImageROI 释放图像 ROI. 释放之后整个图像被认为是全部被选中的。相似的结果可以通过下述办法
cvSetImageROI( image, cvRect( 0, 0, image->width, image->height ));
cvSetImageCOI( image, 0 );
但是后者的变量不分配 image->roi.
GetImageROI
返回图像的 ROI 坐标
CvRect cvGetImageROI( const IplImage* image );
image
图像头.
函数 cvGetImageROI 返回图像ROI 坐标. 如果没有ROI则返回矩形值为 cvRect(0,0,image->width,image->height)
CreateMat
创建矩阵
CvMat* cvCreateMat( int rows, int cols, int type );
rows
矩阵行数。
cols
矩阵列数。
type
矩阵元素类型。 通常以 CV_<比特数>(S|U|F)C<通道数>型式描述, 例如:
CV_8UC1 意思是一个8-bit 无符号单通道矩阵, CV_32SC2 意思是一个32-bit 有符号二个通道的矩阵。
函数 cvCreateMat 为新的矩阵分配头和下面的数据,并且返回一个指向新创建的矩阵的指针。是下列操作的缩写型式:
CvMat* mat = cvCreateMatHeader( rows, cols, type );
cvCreateData( mat );
矩阵按行存贮。所有的行以4个字节对齐。
CreateMatHeader
创建新的矩阵头
CvMat* cvCreateMatHeader( int rows, int cols, int type );
rows
矩阵行数.
cols
矩阵列数.
type
矩阵元素类型(见 cvCreateMat).
函数 cvCreateMatHeader 分配新的矩阵头并且返回指向它的指针. 矩阵数据可被进一步的分配,使用cvCreateData 或通过 cvSetData明确的分配数据.
ReleaseMat
删除矩阵
void cvReleaseMat( CvMat** mat );
mat
双指针指向矩阵.
函数cvReleaseMat 缩减矩阵数据参考计数并且释放矩阵头 :
if( *mat )
cvDecRefData( *mat );
cvFree( (void**)mat );
InitMatHeader
初始化矩阵头
CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type,
void* data=NULL, int step=CV_AUTOSTEP );
mat
指针指向要被初始化的矩阵头.
rows
矩阵的行数.
cols
矩阵的列数.
type
矩阵元素类型.
data
可选的,将指向数据指针分配给矩阵头.
step
排列后的数据的整个行宽,默认状态下,使用STEP的最小可能值。也就是说默认情况下假定矩阵的行与行之间无隙.
函数 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) 矩阵的积
Mat
初始化矩阵的头(轻磅变量)
CvMat cvMat( int rows, int cols, int type, void* data=NULL );
rows
矩阵行数
cols
列数.
type
元素类型(见CreateMat).
data
可选的分配给矩阵头的数据指针 .
函数 cvMat 是个一快速内连函数,替代函数 cvInitMatHeader. 也就是说它相当于:
CvMat mat;
cvInitMatHeader( &mat, rows, cols, type, data, CV_AUTOSTEP );
CloneMat
创建矩阵拷贝
CvMat* cvCloneMat( const CvMat* mat );
mat
输入矩阵.
函数 cvCloneMat 创建输入矩阵的一个拷贝并且返回 该矩阵的指针.
CreateMatND
创建多维密集数组
CvMatND* cvCreateMatND( int dims, const int* sizes, int type );
dims
数组维数. 但不许超过 CV_MAX_DIM (默认=32,但这个默认值可能在编译时被改变 )的定义
sizes
数组的维大小.
type
数组元素类型. 与 CvMat相同
函数cvCreateMatND 分配头给多维密集数组并且分配下面的数据,返回指向被创建数组的指针 . 是下列的缩减形式:
CvMatND* mat = cvCreateMatNDHeader( dims, sizes, type );
cvCreateData( mat );
矩阵按行存贮. 所有的行以4个字节排列。.
CreateMatNDHeader
创建新的数组头
CvMatND* cvCreateMatNDHeader( int dims, const int* sizes, int type );
dims
数组维数.
sizes
维大小.
type
数组元素类型. 与 CvMat相同
函数cvCreateMatND 分配头给多维密集数组。数组数据可以用 cvCreateData 进一步的被分配或利用cvSetData由用户明确指定.
ReleaseMatND
删除多维数组
void cvReleaseMatND( CvMatND** mat );
mat
指向数组的双指针.
函数 cvReleaseMatND 缩减数组参考计数并释放数组头:
if( *mat )
cvDecRefData( *mat );
cvFree( (void**)mat );
InitMatNDHeader
初始化多维数组头
CvMatND* cvInitMatNDHeader( CvMatND* mat, int dims, const int* sizes, int type, void* data=NULL );
mat
指向要被出初始化的数组头指针.
dims
数组维数.
sizes
维大小.
type
数组元素类型. 与 CvMat相同
data
可选的分配给矩阵头的数据指针.
函数 cvInitMatNDHeader 初始化 用户指派的CvMatND 结构.
CloneMatND
创建多维数组的完整拷贝
CvMatND* cvCloneMatND( const CvMatND* mat );
mat
输入数组
函数 cvCloneMatND 创建输入数组的拷贝并返回指针.
DecRefData
缩减数组数据的引用计数
void cvDecRefData( CvArr* arr );
arr
数组头.
如果引用计数指针非NULL,函数 cvDecRefData 缩减CvMat 或CvMatND 数据的引用计数,如果计数到0就删除数据。在当前的版本中只有当数据是用cvCreateData 分配的引用计数才会是非NULL。在其他的情况下比如:
· 使用cvSetData指派外部数据给矩阵头;
· 代表部分大的矩阵或图像的矩阵头;
· 是从图像头或N维矩阵头转换过来的矩阵头,
在这些情况下引用计数被设置成NULL因此不会被缩减。 无论数据是否被删除,数据指针和引用计数指针都将被这个函数清空。
IncRefData
增加数组数据的引用计数
int cvIncRefData( CvArr* arr );
arr
数组头.
函数 cvIncRefData 增加 CvMat 或 CvMatND 数据引用计数,如果引用计数非空返回新的计数值 否则返回0。
CreateData
分配数组数据
void cvCreateData( CvArr* arr );
arr
数组头.
函数 cvCreateData 分配图像,矩阵或多维数组数据. 对于矩阵类型使用OpenCV的分配函数,对于 IplImage类型如果CV_TURN_ON_IPL_COMPATIBILITY没有被调用也是可以使用这种方法的反之使用 IPL 函数分配数据
ReleaseData
释放数组数据
void cvReleaseData( CvArr* arr );
arr
数组头
函数cvReleaseData 释放数组数据. 对于 CvMat 或 CvMatND 结构只需调用 cvDecRefData(), 也就是说这个函数不能删除外部数据。见 cvCreateData.
SetData
指派用户数据给数组头
void cvSetData( CvArr* arr, void* data, int step );
arr
数组头.
data
用户数据.
step
整行字节长.
函数cvSetData 指派用户数据给数组头. 头应该已经使用 cvCreate*Header, cvInit*Header 或 cvMat (对于矩阵)初始化过.
GetRawData
返回数组的底层信息
void cvGetRawData( const CvArr* arr, uchar** data,
int* step=NULL, CvSize* roi_size=NULL );
arr
数组头.
data
输出指针,指针指向整个图像的结构或ROI
step
输出行字节长
roi_size
输出ROI尺寸
函数 cvGetRawData 添充给输出变量数组的底层信息。所有的输出参数是可选的, 因此这些指针可设为NULL。如果数组是设置了ROI的 IplImage 结构, ROI参数被返回。
注意:输出指针指向数组头的对应的内存,不能释放。建议用memcpy。
接下来的例子展示怎样利用这个函数去访问数组元素。
使用 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]);
GetMat
从不确定数组返回矩阵头
CvMat* cvGetMat( const CvArr* arr, CvMat* header, int* coi=NULL, int allowND=0 );
arr
输入数组.
header
指向 CvMat结构的指针,作为临时缓存 .
coi
可选的输出参数,用于输出COI.
allowND
如果非0,函数就接收多维密集数组 (CvMatND*)并且返回 2D (如果 CvMatND 是二维的)或 1D 矩阵(当 CvMatND 是一维或多于二维). 数组必须是连续的.
函数 cvGetMat从输入的数组生成矩阵头,输入的数组可以是 - CvMat结构, IplImage结构 或多维密集数组 CvMatND* (后者只有当 allowND != 0时才可以使用) . 如果是矩阵函数只是返回指向矩阵的指针.如果是 IplImage* 或 CvMatND* 函数用当前图像的ROI初始化头结构并且返回指向这个临时结构的指针。因为CvMat不支持COI,所以他们的返回结果是不同的.
这个函数提供了一个简单的方法,用同一代码处理 IplImage 和 CvMat二种数据类型。这个函数的反向转换可以用 cvGetImage将 CvMat 转换成 IplImage .
输入的数组必须有已分配好的底层数据或附加的数据,否则该函数将调用失败如果输入的数组是IplImage 格式,使用平面式数据编排并设置了COI,函数返回的指针指向被选定的平面并设置COI=0.利用OPENCV函数对于多通道平面编排图像可以处理每个平面。
GetImage
从不确定数组返回图像头
IplImage* cvGetImage( const CvArr* arr, IplImage* image_header );
arr
输入数组.
image_header
指向IplImage结构的指针,该结构存贮在一个临时缓存 .
函数 cvGetImage 从输出数组获得图头,该数组可以是矩阵- CvMat*, 或图像 - IplImage*。 如果是图像的话函数只是返回输入参数的指针,如果是 CvMat* 的话函数用输入参数矩阵初始化图像头。因此如果我们把 IplImage 转换成 CvMat 然后再转换 CvMat 回 IplImage,如果ROI被设置过了我们可能会获得不同的头,这样一些计算图像跨度的IPL函数就会失败。
CreateSparseMat
创建稀疏数组
CvSparseMat* cvCreateSparseMat( int dims, const int* sizes, int type );
dims
数组维数。相对于密集型矩阵,稀疏数组的维数是不受限制的(最多可达 216)。
sizes
数组的维大小。
type
数组元素类型,见 CvMat。
函数 cvCreateSparseMat 分配多维稀疏数组。刚初始化的数组不含元素,因此cvGet*D 或 cvGetReal*D函数对所有索引都返回0。
ReleaseSparseMat
删除稀疏数组
void cvReleaseSparseMat( CvSparseMat** mat );
mat
双指针指向数组。
函数 cvReleaseSparseMat释放稀疏数组并清空数组指针
CloneSparseMat
创建稀疏数组的拷贝
CvSparseMat* cvCloneSparseMat( const CvSparseMat* mat );
mat
输入数组。
函数 cvCloneSparseMat 创建输入数组的拷贝并返回指向这个拷贝的指针。
获取元素和数组子集
GetSubRect
返回输入的图像或矩阵的矩形数组子集的矩阵头
CvMat* cvGetSubRect( const CvArr* arr, CvMat* submat, CvRect rect );
arr
输入数组。
submat
指向矩形数组子集矩阵头的指针。
rect
以0坐标为基准的ROI。
函数 cvGetSubRect 根据指定的数组矩形返回矩阵头,换句话说,函数允许像处理一个独立数组一样处理输入数组的一个指定子矩形。函数在处理时要考虑进输入数组的ROI,因此数组的ROI是实际上被提取的。
GetRow, GetRows
返回数组的一行或在一定跨度内的行
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 );
arr
输入数组。
submat
指向返回的子数组头的指针。
row
被选定行的索引下标,索引下标从0开始。
start_row
跨度的开始行(包括此行)索引下标,索引下标从0开始。
end_row
跨度的结束行(不包括此行)索引下标,索引下标从0开始。
delta_row
在跨度内的索引下标跨步,从开始行到结束行每隔delta_row行提取一行。
函数GetRow 和 GetRows 返回输入数组中指定的一行或在一定跨度内的行对应的数组头。 注意GetRow 实际上是 以下cvGetRows调用的简写:
cvGetRow( arr, submat, row ) ~ cvGetRows( arr, submat, row, row + 1, 1 );
GetCol, GetCols
返回数组的一列或一定跨度内的列
CvMat* cvGetCol( const CvArr* arr, CvMat* submat, int col );
CvMat* cvGetCols( const CvArr* arr, CvMat* submat, int start_col, int end_col );
arr
输入数组。
submat
指向结果子数组头的指针。
col
被选定列的索引下标,索引下标从0开始。
start_col
跨度的开始列(包括该列)索引下标,索引下标从0开始。
end_col
跨度的结束列(不包括该列)索引下标,索引下标从0开始。
函数 GetCol 和 GetCols 根据指定的列/列跨度返回对应的数组头。注意GetCol 实际上是以下 cvGetCols调用的简写形式:
cvGetCol( arr, submat, col ); // ~ cvGetCols( arr, submat, col, col + 1 );
GetDiag
返回一个数组对角线
CvMat* cvGetDiag( const CvArr* arr, CvMat* submat, int diag=0 );
arr
输入数组.
submat
指向结果子集的头指针.
diag
数组对角线。0是主对角线,-1是主对角线上面对角线,1是主对角线下对角线,以此类推。
函数 cvGetDiag 根据指定的diag参数返回数组的对角线头。
GetSize
返回矩阵或图像ROI的大小
CvSize cvGetSize( const CvArr* arr );
arr
数组头。
函数 cvGetSize 返回图像或矩阵的行数和列数,如果是图像就返回ROI的大小。
InitSparseMatIterator
初始化稀疏数组元素迭代器
CvSparseNode* cvInitSparseMatIterator( const CvSparseMat* mat,
CvSparseMatIterator* mat_iterator );
mat
输入的数组.
mat_iterator
被初始化的迭代器.
函数 cvInitSparseMatIterator 初始化稀疏数组元素的迭代器并且返回指向第一个元素的指针,如果数组为空则返回NULL。
GetNextSparseNode
初始化稀疏数组元素迭代器
CvSparseNode* cvGetNextSparseNode( CvSparseMatIterator* mat_iterator );
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 );
GetElemType
返回数组元素类型
int cvGetElemType( const CvArr* arr );
arr
输入数组.
函数 GetElemType 返回数组元素类型就像在cvCreateMat 中讨论的一样:
CV_8UC1 ... CV_64FC4
GetDims, GetDimSize
返回数组维数和他们的大小或者特殊维的大小
int cvGetDims( const CvArr* arr, int* sizes=NULL );
int cvGetDimSize( const CvArr* arr, int index );
arr
输入数组.
sizes
可选的输出数组维尺寸向量,对于2D数组第一位是数组行数(高),第二位是数组列数(宽)
index
以0为基准的维索引下标(对于矩阵0意味着行数,1意味着列数,对于图象0意味着高,1意味着宽。
函数 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 );
Ptr*D
返回指向特殊数组元素的指针
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 );
arr
输入数组.
idx0
元素下标的第一个以0为基准的成员
idx1
元素下标的第二个以0为基准的成员
idx2
元素下标的第三个以0为基准的成员
idx
数组元素下标
type
可选的,矩阵元素类型输出参数
create_node
可选的,为稀疏矩阵输入的参数。如果这个参数非零就意味着被需要的元素如果不存在就会被创建。
precalc_hashval
可选的,为稀疏矩阵设置的输入参数。如果这个指针非NULL,函数不会重新计算节点的HASH值,而是从指定位置获取。这种方法有利于提高智能组合数据的操作(TODO: 提供了一个例子)
函数cvPtr*D 返回指向特殊数组元素的指针。数组维数应该与转递给函数物下标数相匹配,除了 cvPtr1D 函数,它可以被用于顺序存取的1D,2D或nD密集数组
函数也可以用于稀疏数组,并且如果被需要的节点不存在函数可以创建这个节点并设置为0
就像其它获取数组元素的函数 (cvGet[Real]*D, cvSet[Real]*D)如果元素的下标超出了范围就会产生错误
Get*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 );
arr
输入数组.
idx0
元素下标第一个以0为基准的成员
idx1
元素下标第二个以0为基准的成员
idx2
元素下标第三个以0为基准的成员
idx
元素下标数组
函数cvGet*D 返回指定的数组元素。对于稀疏数组如果需要的节点不存在函数返回0 (不会创建新的节点)
GetReal*D
返回单通道数组的指定元素
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 );
arr
输入数组,必须是单通道.
idx0
元素下标的第一个成员,以0为基准
idx1
元素下标的第二个成员,以0为基准
idx2
元素下标的第三个成员,以0为基准
idx
元素下标数组
函数cvGetReal*D 返回单通道数组的指定元素,如果数组是多通道的,就会产生运行时错误,而 cvGet*D 函数可以安全的被用于单通道和多通道数组,但他们运行时会有点慢
如果指定的点不存在对于稀疏数组点会返回0(不会创建新的节点)。
mGet
返回单通道浮点矩阵指定元素
double cvmGet( const CvMat* mat, int row, int col );
mat
输入矩阵.
row
行下标,以0为基点.
col
列下标,以0为基点
函数 cvmGet 是 cvGetReal2D对于单通道浮点矩阵的快速替代函数,函数运行比较快速因为它是内连函数 ,这个函数对于数组类型、数组元素类型的检查作的很少,并且仅在调式模式下检查数的行和列范围。
Set*D
修改指定的数组
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 );
arr
输入数组
idx0
元素下标的第一个成员,以0为基点
idx1
元素下标的第二个成员,以0为基点
idx2
元素下标的第三个成员,以0为基点
idx
元素下标数组
value
指派的值
函数 cvSet*D 指定新的值给指定的数组元素。对于稀疏矩阵如果指定节点不存在函数创建新的节点
SetReal*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 );
arr
输入数组.
idx0
元素下标的第一个成员,以0为基点
idx1
元素下标的第二个成员,以0为基点
idx2
元素下标的第三个成员,以0为基点
idx
元素下标数组
value
指派的值
函数 cvSetReal*D 分配新的值给单通道数组的指定元素,如果数组是多通道就会产生运行时错误。然而cvSet*D 可以安全的被用于多通道和单通道数组,只是稍微有点慢。
对于稀疏数组如果指定的节点不存在函数会创建该节点。
mSet
为单通道浮点矩阵的指定元素赋值。
void cvmSet( CvMat* mat, int row, int col, double value );
mat
矩阵.
row
行下标,以0为基点.
col
列下标,以0为基点.
value
矩阵元素的新值
函数cvmSet 是cvSetReal2D 快速替代,对于单通道浮点矩阵因为这个函数是内连的所以比较快,函数对于数组类型、数组元素类型的检查作的很少,并且仅在调式模式下检查数的行和列范围。
ClearND
清除指定数组元素
void cvClearND( CvArr* arr, int* idx );
arr
输入数组.
idx
数组元素下标
函数cvClearND 清除指定密集型数组的元素(置0)或删除稀疏数组的元素 ,如果元素不存在函数不作任何事
拷贝和添加
Copy
拷贝一个数组给另一个数组
void cvCopy( const CvArr* src, CvArr* dst, const CvArr* mask=NULL );
src
输入数组。
dst
输出数组。
mask
操作掩码是8比特单通道的数组,它指定了输出数组中被改变的元素。
函数cvCopy从输入数组中复制选定的成分到输出数组:
如果mask(I)!=0,则dst(I)=src(I)。
如果输入输出数组中的一个是IplImage类型的话,其ROI和COI将被使用。输入输出数组必须是同样的类型、维数和大小。函数也可以用来复制散列数组(这种情况下不支持mask)。
Set
设置数组所有元素为指定值
void cvSet( CvArr* arr, CvScalar value, const CvArr* mask=NULL );
arr
输出数组。
value
填充值。
mask
操作掩码是8比特单通道的数组,它指定了输出数组中被改变的元素。
函数 cvSet 拷贝数量值到输出数组的每一个被除数选定的元素:
arr(I)=value if mask(I)!=0
如果数组 arr 是 IplImage 类型, 那么就会使用ROI,但COI不能设置。
SetZero
清空数组
void cvSetZero( CvArr* arr );
#define cvZero cvSetZero
arr
要被清空数组.
函数 cvSetZero 清空数组. 对于密集型号数组(CvMat, CvMatND or IplImage) cvZero(array) 就相当于 cvSet(array,cvScalarAll(0),0), 对于稀疏数组所有的元素都将被删除.
SetIdentity
初始化带尺度的单位矩阵
void cvSetIdentity( CvArr* mat, CvScalar value=cvRealScalar(1) );
mat
待初始化的矩阵 (不一定是方阵)。
value
赋值给对角线元素的值。
函数 cvSetIdentity 初始化带尺度的单位矩阵:
arr(i,j)=value 如果 i=j,
否则为 0
Range
用指定范围的数来填充矩阵.
void cvRange( CvArr* mat, double start, double end );
mat
即将被初始化的矩阵,必须是指向单通道的32位(整型或浮点型)的矩阵的指针.
start
指定范围的最小边界
end
指定范围的最大边界
该函数按以下方式初始化矩阵:
arr(i,j)=(end-start)*(i*cols(arr)+j)/(cols(arr)*rows(arr))
例如:以下的代码将按相应的整型数初始化一维向量:
CvMat* A = cvCreateMat( 1, 10, CV_32S ); cvRange( A, 0, A->cols ); //A将被初始化为[0,1,2,3,4,5,6,7,8,9]
变换和置换
Reshape
不拷贝数据修改矩阵/图像形状
CvMat* cvReshape( const CvArr* arr, CvMat* header, int new_cn, int new_rows=0 );
arr
输入的数组.
header
被添充的矩阵头
new_cn
新的通道数.new_cn = 0 意味着不修改通道数
new_rows
新的行数. 如果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 );
ReshapeMatND
修改多维数组形状,拷贝/不拷贝数据
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))
arr
输入数组
sizeof_header
输出头的大小,对于IplImage, CvMat 和 CvMatND 各种结构输出的头均是不同的.
header
被添充的输出头.
new_cn
新的通道数,如果new_cn = 0 则通道数保持原样
new_dims
新的维数. 如果new_dims = 0 则维数保持原样。
new_sizes
新的维大小.只有当 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 );
Repeat
用原数组管道式添充输出数组
void cvRepeat( const CvArr* src, CvArr* dst );
src
输入数组, 图像或矩阵。
dst
输出数组,图像或矩阵
函数cvRepeat 使用被管道化的原数组添充输出数组:
dst(i,j)=src(i mod rows(src), j mod cols(src))
因此 ,输出数组可能小于也可能大于输入数组.
Flip
垂直,水平或即垂直又水平翻转二维数组
void cvFlip( const CvArr* src, CvArr* dst=NULL, int flip_mode=0);
#define cvMirror cvFlip
src
原数组.
dst
目标责任制数组. 如果 dst = NULL 翻转是在内部替换.
flip_mode
指定怎样去翻转数组。
flip_mode = 0 沿X-轴翻转, flip_mode > 0 (如 1) 沿Y-轴翻转, flip_mode < 0 (如 -1) 沿X-轴和Y-轴翻转.见下面的公式
函数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
函数主要使用在:
· 垂直翻转图像(flip_mode = 0)用于 顶-左和底-左图像结构的转换, 主要用于WIN32系统下的视频操作处理.
· 水平图像转换,使用连续的水平转换和绝对值差检查垂直轴对称(flip_mode > 0)
· 水平和垂直同时转换,用于连续的水平转换和绝对真理值差检查中心对称s(flip_mode < 0)
· 翻转1维指针数组的顺序(flip_mode > 0)
Split
分割多通道数组成几个单通道数组或者从数组中提取一个通道
void cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1,
CvArr* dst2, CvArr* dst3 );
#define cvCvtPixToPlane cvSplit
src
原数组.
dst0...dst3
目标通道
函数 cvSplit 分割多通道数组成分离的单通道数组d。可获得两种操作模式 . 如果原数组有N通道且前N输出数组非NULL, 所有的通道都会被从原数组中提取,如果前N个通道只有一个通道非NULL函数只提取该指定通道,否则会产生一个错误,馀下的通道(超过前N个通道的以上的)必须被设置成NULL,对于设置了COI的IplImage 结使用cvCopy 也可以从图像中提取单通道。
Merge
从几个单通道数组组合成多通道数组或插入一个单通道数组
void cvMerge( const CvArr* src0, const CvArr* src1,
const CvArr* src2, const CvArr* src3, CvArr* dst );
#define cvCvtPlaneToPix cvMerge
src0... src3
输入的通道.
dst
输出数组.
函数cvMerge 是前一个函数的反向操作。如果输出数组有N个通道并且前N个输入通道非NULL,就拷贝所有通道到输出数组,如果在前N个通道中只有一个单通道非NULL ,只拷贝这个通道到输出数组,否则 就会产生错误。除前N通道以外的馀下的通道必须置NULL。对于设置了COI的 IplImage结构使用 cvCopy也可以实现向图像中插入一个通道 。
MixChannels
拷贝输入数组的若干个通道到输出数组的某些通道上面.
void cvMixChannels( const CvArr** src, int src_count,
CvArr** dst, int dst_count,
const int* from_to, int pair_count );
src
输入数组
src_count
输入数组的个数
dst
输出数组
dst_count
输出数组的个数
from_to
对数的阵列
pair_count
from_to里面的对数的个数,或者说被拷贝的位面的个数.
RandShuffle
随机交换数组的元素
void cvRandShuffle( CvArr* mat, CvRNG* rng, double iter_factor=1.);
mat
输入的矩阵,用来被随机处理.
rng
随机数产生器用来随机交换数组元素.如果为NULL,一个当前的随机数发生器将被创建与使用.
iter_factor
相关的参数,用来刻划交换操作的密度.请看下面的说明.
这个函数在每个反复的操作中交换随机选择的矩阵里面的元素(在多通道的数组里面每个元素可能包括若干个部分),反复的次数(也就是交换的对数)等于round(iter_factor*rows(mat)*cols(mat)), 因此如果iter_factor=0,没有交换产生,如果等于1意味着随机交换了rows(mat)*cols(mat)对数.
算术,逻辑和比较
LUT
利用查找表转换数组
void cvLUT( const CvArr* src, CvArr* dst, const CvArr* lut );
src
元素为8位的原数组。
dst
与原数组有相同通道数的输出数组,深度不确定
lut
有256个元素的查找表;必须要与原输出数组有相同像深 。
函数cvLUT 使用查找表中的值添充输出数组. 坐标入口来自于原数组,也就是说函数处理每个元素按如下方式:
dst(I)=lut[src(I)+DELTA]
这里当src的深度是CV_8U时DELTA=0 ,src的深度是CV_8S时 DELTA=128
ConvertScale
使用线性变换转换数组
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 )
src
输入数组.
dst
输出数组
scale
比例因子.
shift
该加数被加到输入数组元素按比例缩放后得到的元素上
函数 cvConvertScale 有多个不同的目的因此就有多个同义函数(如上面的#define所示)。该函数首先对输入数组的元素进行比例缩放,然后将shift加到比例缩放后得到的各元素上,即: dst(I)=src(I)*scale + (shift,shift,...),最后可选的类型转换将结果拷贝到输出数组。
多通道的数组对各个通道是独立处理的。
类型转换主要用舍入和溢出截断来完成。也就是如果缩放+转换后的结果值不能用输出数组元素类型值精确表达,就设置成在输出数组数据轴上最接近该数的值。
如果 scale=1, shift=0 就不会进行比例缩放. 这是一个特殊的优化,相当于该函数的同义函数名:cvConvert 。如果原来数组和输出数组的类型相同,这是另一种特殊情形,可以被用于比例缩放和平移矩阵或图像,此时相当于该函数的同义函数名:cvScale。
ConvertScaleAbs
使用线性变换转换输入数组元素成8位无符号整型
void cvConvertScaleAbs( const CvArr* src, CvArr* dst, double scale=1, double shift=0 );
#define cvCvtScaleAbs cvConvertScaleAbs
src
原数组
dst
输出数组 (深度为 8u).
scale
比例因子.
shift
原数组元素按比例缩放后添加的值。
函数 cvConvertScaleAbs 与前一函数是相同的,但它是存贮变换结果的绝对值:
dst(I)=abs(src(I)*scale + (shift,shift,...))
函数只支持目标数数组的深度为 8u (8-bit 无符号) , 对于别的类型函数仿效于cvConvertScale 和 cvAbs 函数的联合
Add
计算两个数组中每个元素的和
void cvAdd( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
src1
第一个原数组
src2
第二个原数组
dst
输出数组
mask
操作的复盖面, 8-bit单通道数组; 只有复盖面指定的输出数组被修改。
函数 cvAdd 加一个数组到别一个数组中:
dst(I)=src1(I)+src2(I) if mask(I)!=0
除复盖面外所有的数组必须有相同的类型相同的大小(或ROI尺寸)。
AddS
计算数量和数组的和
void cvAddS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
src
原数组.
value
被加入数量
dst
输出数组
mask
操作的复盖面(8-bit单通道数组) ; 只有复盖面指定的输出数组被修改
函数 cvAddS 用数量值与原数组src1的每个元素想加并存贮结果到
dst(I)=src(I)+value if mask(I)!=0
除复盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)
AddWeighted
计算两数组的加权值的和
void cvAddWeighted( const CvArr* src1, double alpha,
const CvArr* src2, double beta,
double gamma, CvArr* dst );
src1
第一个原数组.
alpha
第一个数组元素的权值
src2
第二个原数组
beta
第二个数组元素的权值
dst
输出数组
gamma
添加的常数项。
函数 cvAddWeighted 计算两数组的加权值的和:
dst(I)=src1(I)*alpha+src2(I)*beta+gamma
所有的数组必须有相同的类型相同的大小(或ROI大小)
Sub
计算两个数组每个元素的差
void cvSub( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
src1
第一个原数组
src2
第二个原数组.
dst
输出数组.
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数cvSub 从一个数组减去别一个数组:
dst(I)=src1(I)-src2(I) if mask(I)!=0
除复盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)
SubS
计算数组和数量之间的差
void cvSubS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
src
原数组.
value
被减的数量.
dst
输出数组.
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数 cvSubS 从原数组的每个元素中减去一个数量:
dst(I)=src(I)-value if mask(I)!=0
除复盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)。
SubRS
计算数量和数组之间的差
void cvSubRS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
src
第一个原数组。
value
被减的数量
dst
输出数组
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数 cvSubRS 从一个数量减去原数组的每个元素:
dst(I)=value-src(I) if mask(I)!=0
除复盖面外所有数组都必须有相同的类型,相同的大小(或ROI大小)。
Mul
计算两个数组中每个元素的积
void cvMul( const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1 );
src1
第一个原数组.
src2
第二个原数组.
dst
输出数组.
scale
设置的比例因子
函数 cvMul 计算两个数组中每个元素的积:
dst(I)=scale•src1(I)•src2(I)
所有的数组必须有相同的类型和相同的大小(或ROI大小)
Div
两个数组每个元素相除
void cvDiv( const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1 );
src1
第一个原数组。如该指针为NULL,假高该数组的所有元素都为1
src2
第二个原数组。
dst
输出数组
scale
设置的比例因子
函数 cvDiv 用一个数组除以另一个数组:
dst(I)=scale•src1(I)/src2(I), if src1!=NULL
dst(I)=scale/src2(I),: if src1=NULL
所有的数组必须有相同的类型和相同的大小(或ROI大小)
And
计算两个数组的每个元素的按位与
void cvAnd( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
src1
第一个原数组
src2
第二个原数组.
dst
输出数组
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数 cvAnd 计算两个数组的每个元素的按位逻辑与:
dst(I)=src1(I)&src2(I) if mask(I)!=0
对浮点数组按位表示操作是很有利的。除复盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
AndS
计算数组每个元素与数量之间的按位与
void cvAndS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
src
原数组.
value
操作中用到的数量
dst
输出数组
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数 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
Or
计算两个数组每个元素的按位或
void cvOr( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
src1
第一个原数组
src2
第二个原数组
dst
输出数组.
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数 cvOr 计算两个数组每个元素的按位或:
dst(I)=src1(I)|src2(I)
对浮点数组按位表示操作是很有利的。除复盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
OrS
计算数组中每个元素与数量之间的按位或
void cvOrS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
src1
原数组
value
操作中用到的数量
dst
目数组.
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数 OrS 计算数组中每个元素和数量之间的按位或:
dst(I)=src(I)|value if mask(I)!=0
在实际操作之前首先把数量类型转换成与数组相同的类型。对浮点数组按位表示操作是很有利的。除复盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
Xor
计算两个数组中的每个元素的按位异或
void cvXor( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
src1
第一个原数组
src2
第二个原数组.
dst
输出数组
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改
函数 cvXor 计算两个数组元素的按位异或:
dst(I)=src1(I) src2(I) if mask(I)!=0
对浮点数组按位表示操作是很有利的。除复盖面,所有数组都必须有相同的类型,相同的大小(或ROI大小)。
XorS
计算数组元素与数量之间的按位异或
void cvXorS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
src
原数组
value
操作中用到的数量
dst
输出数组.
mask
操作复盖面( 8-bit 单通道数组); 只有复盖面指定的输出数组被修改。
函数 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)
Not
计算数组元素的按位取反
void cvNot( const CvArr* src, CvArr* dst );
src1
原数组
dst
输出数组
函数不取反每个数组元素的每一位
dst(I)=~src(I)
Cmp
比较两个数组元素P
void cvCmp( const CvArr* src1, const CvArr* src2, CvArr* dst, int cmp_op );
src1
第一个原数组
src2
第二个原数组,这两个数组必须都是单通道数据。
dst
输出数组必须是 8u 或 8s 类型.
cmp_op
该标识指定要检查的元素之间的关系:
CV_CMP_EQ - src1(I) "等于" src2(I)
CV_CMP_GT - src1(I) "大于" src2(I)
CV_CMP_GE - src1(I) "大于等于" src2(I)
CV_CMP_LT - src1(I) "小于" src2(I)
CV_CMP_LE - src1(I) "小于等于" src2(I)
CV_CMP_NE - src1(I) "不等于" src2(I)
函数 cvCmp 比较两个数组的对应元素并且添充输出数组:
dst(I)=src1(I) op src2(I),
这里 op 是 '=', '>', '>=', '<', '<=' or '!='.
如果元素之间的关系为真则设置dst(I)为 0xff (也就是所有的位都为 '1') 否则为0。除了输出数组所有数组必须是相同的类型相同的大小(或ROI大小)。
CmpS
比较数组的每个元素与数量的关系
void cvCmpS( const CvArr* src, double value, CvArr* dst, int cmp_op );
src
原数,输入数组必须是单通道数据。
value
用与数组元素比较的数量值
dst
输出数组必须是 8u 或 8s 类型.
cmp_op
该标识指定要检查的元素之间的关系:
CV_CMP_EQ - src1(I) "等于" value
CV_CMP_GT - src1(I) "大于" value
CV_CMP_GE - src1(I) "大于等于" value
CV_CMP_LT - src1(I) "小于" value
CV_CMP_LE - src1(I) "小于等于" value
CV_CMP_NE - src1(I) "不等于" value
函数 cvCmpS 比较数组元素与数量并且添充目标复盖面数组:
dst(I)=src(I) op scalar,
这里 op 是 '=', '>', '>=', '<', '<=' or '!='.
如果元素之间的关系为真则设置dst(I)为 0xff (也就是所有的位都为 '1') 否则为0。所有的数组必须有相同的大小(或ROI大小)
InRange
检查数组元素是否在两个数组之间
void cvInRange( const CvArr* src, const CvArr* lower, const CvArr* upper, CvArr* dst );
src
第一个原数组
lower
包括进的下边界数组
upper
不包括进的上边界线数组
dst
输出数组必须是 8u 或 8s 类型.
函数 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大小)。
InRangeS
检查数组元素是否在两个数量之间
void cvInRangeS( const CvArr* src, CvScalar lower, CvScalar upper, CvArr* dst );
src
第一个原数组
lower
包括进的下边界.
upper
不包括进的上边界
dst
输出数组必须是 8u 或 8s 类型.
函数 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大小)
Max
查找两个数组中每个元素的较大值
void cvMax( const CvArr* src1, const CvArr* src2, CvArr* dst );
src1
第一个原数组
src2
第二个原数组
dst
输出数组
函数 cvMax 计算两个数组中每个元素的较大值:
dst(I)=max(src1(I), src2(I))
所有的数组必须的一个单通道,相同的数据类型和相同的大小(或ROI大小)
MaxS
查找数组元素与数量之间的较大值
void cvMaxS( const CvArr* src, double value, CvArr* dst );
src
第一个原数组.
value
数量值.
dst
输出数组
函数 cvMaxS 计算数组元素和数量之间的较大值:
dst(I)=max(src(I), value)
所有数组必须有一个单一通道,相同的数据类型相同的大小(或ROI大小)
Min
查找两个数组元素之间 的较小值
void cvMin( const CvArr* src1, const CvArr* src2, CvArr* dst );
src1
第一个原数组
src2
第二个原数组.
dst
输出数组.
函数cvMin计算两个数组元素的较小值
dst(I)=min(src1(I),src2(I))
所有数组必须有一个单一通道,相同的数据类型相同的大小(或ROI大小)
MinS
查找数组元素和数量之间的较小值
void cvMinS( const CvArr* src, double value, CvArr* dst );
src
第一个原数组
value
数量值.
dst
输出数组..
函数 cvMinS 计算数组元素和数量之量的较小值:
dst(I)=min(src(I), value)
所有数组必须有一个单一通道,相同的数据类型相同的大小(或ROI大小)
AbsDiff
计算两个数组差的绝对值
void cvAbsDiff( const CvArr* src1, const CvArr* src2, CvArr* dst );
src1
第一个原数组
src2
第二个原数组
dst
输出数组
函数 cvAbsDiff 计算两个数组差的绝对值
dst(I)c = abs(src1(I)c - src2(I)c).
所有数组必须有相同的数据类型相同的大小(或ROI大小)
AbsDiffS
计算数组元素与数量之间差的绝对值
void cvAbsDiffS( const CvArr* src, CvArr* dst, CvScalar value );
#define cvAbs(src, dst) cvAbsDiffS(src, dst, cvScalarAll(0))
src
原数组.
dst
输出数组
value
数量.
函数 cvAbsDiffS 计算数组元素与数量之间差的绝对值
dst(I)c = abs(src(I)c - valuec).
所有数组必须有相同的数据类型相同的大小(或ROI大小)
统计
CountNonZero
计算非零数组元素个数
int cvCountNonZero( const CvArr* arr );
arr
数组, 必须是单通道数组或者设置COI(感兴趣通道)的多通道图像。
函数 cvCountNonZero 返回arr中非零元素的数目:
result = sumI arr(I)!=0
当IplImage 支持ROI和COI。
Sum
计算数组元素的和
CvScalar cvSum( const CvArr* arr );
arr
数组.
函数 cvSum 独立地为每一个通道计算数组元素的和 S :
Sc = sumI arr(I)c
如果数组是IplImage类型和设置了COI, 该函数只处理选定的通道并将和存储到第一个标量成员 (S0)。常见论坛讨论贴cvSum的结果分析
Avg
计算数组元素的平均值
CvScalar cvAvg( const CvArr* arr, const CvArr* mask=NULL );
arr
数组.
mask
可选操作掩模
函数 cvAvg 独立地为每一个通道计算数组元素的平均值 M :
如果数组是 IplImage 类型和设置 COI , 该函数只处理选定的通道并将和存储到第一个标量成员 (S0)。
AvgSdv
计算数组元素的平均值和标准差
void cvAvgSdv( const CvArr* arr, CvScalar* mean, CvScalar* std_dev, const CvArr* mask=NULL );
arr
数组
mean
指向平均值的指针, 如果不需要的话可以为空( NULL)。
std_dev
指向标准差的指针。
mask
可选操作掩模。
函数 cvAvgSdv 独立地为每一个通道计算数组元素的平均值和标准差:
如果数组是 IplImage 类型和 设置了COI ,该函数只处理选定的通道并将平均值和标准差存储到第一个输出标量成员 (mean0 和 std-dev0)。
MinMaxLoc
查找数组和子数组的全局最小值和最大值
void cvMinMaxLoc( const CvArr* arr, double* min_val, double* max_val,
CvPoint* min_loc=NULL, CvPoint* max_loc=NULL, const CvArr* mask=NULL );
arr
输入数组, 单通道或者设置了 COI 的多通道。
min_val
指向返回的最小值的指针。
max_val
指向返回的最大值的指针。
min_loc
指向返回的最小值的位置指针。
max_loc
指向返回的最大值的位置指针。
mask
选择一个子数组的操作掩模。
函数 MinMaxLoc 查找元素中的最小值和最大值以及他们的位置。函数在整个数组、或选定的ROI区域(对IplImage)或当MASK不为NULL时指定的数组区域中,搜索极值 。如果数组不止一个通道,它就必须是设置了 COI 的 IplImage 类型。 如果是多维数组 min_loc->x 和 max_loc->x 将包含极值的原始位置信息 (线性的)。
Norm
计算数组的绝对范数, 绝对差分范数或者相对差分范数
double cvNorm( const CvArr* arr1, const CvArr* arr2=NULL, int norm_type=CV_L2, const CvArr* mask=NULL );
arr1
第一输入图像
arr2
第二输入图像 ,如果为空(NULL), 计算 arr1 的绝对范数,否则计算 arr1-arr2 的绝对范数或者相对范数。
normType
范数类型,参见“讨论”。
mask
可选操作掩模。
如果 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 返回计算所得的范数。多通道数组被视为单通道处理,因此,所有通道的结果是结合在一起的。
Reduce
简化一个矩阵成为一个向量
cvReduce( const CvArr* src, CvArr* dst, int dim, int op=CV_REDUCE_SUM);
src
输入矩阵
dst
输出的通过处理输入矩阵的所有行/列而得到的单行/列向量
dim
矩阵被简化后的维数索引.0意味着矩阵被处理成一行,1意味着矩阵被处理成为一列,-1时维数将根据输出向量的大小自动选择.
op
简化操作的方式,可以有以下几种取值:
CV_REDUCE_SUM-输出是矩阵的所有行/列的和.
CV_REDUCE_AVG-输出是矩阵的所有行/列的平均向量.
CV_REDUCE_MAX-输出是矩阵的所有行/列的最大值.
CV_REDUCE_MIN-输出是矩阵的所有行/列的最小值.
这个函数通过把矩阵的每行/列当作一维向量并对其做某种特殊的操作将一个矩阵简化成为一个向量.例如,这个函数可以用于计算一个光栅图象的水平或者垂直投影.在取值为CV_REDUCE_AVG与CV_REDUCE_SUM的情况下输出可能有很大的位深度用于维持准确性,这两种方式也适合于处理多通道数组.
注意,对于CV_REDUCE_SUM和CV_REDUCE_AVG方式来说,输入和输出的位数定义有如下关系
输入:CV_8U 输出:CV_32S CV_32F
输入:CV_16U 输出:CV_32F CV_64F
输入:CV_16S 输出:CV_32F CV_64F
输入:CV_32F 输出: CV_32F CV_64F
输入:CV_64F 输出: CV_64F
而对于CV_REDUCE_MAX和CV_REDUCE_MIN方式来说,输入和输出的位数必须一致
线性代数
DotProduct
用欧几里得准则计算两个数组的点积
double cvDotProduct( const CvArr* src1, const CvArr* src2 );
src1
第一输入数组。
src2
第二输入数组。
函数 cvDotProduct 计算并返回两个数组的欧几里得点积。
src1•src2 = sumI(src1(I)*src2(I))
如果是多通道数组,所有通道的结果是累加在一起的。特别地, cvDotProduct(a,a),将返回 ||a||2 ,这里 a 是一个复向量。 该函数可以处理多通道数组,逐行或逐层等等。
Normalize
根据某种范数或者数值范围归一化数组.
void cvNormalize( const CvArr* src, CvArr* dst,
double a=1, double b=0, int norm_type=CV_L2,
const CvArr* mask=NULL );
src
输入数组
dst
输出数组,支持原地运算
a
输出数组的最小/最大值或者输出数组的范数
b
输出数组的最大/最小值
norm_type
归一化的类型,可以有以下的取值:
CV_C - 归一化数组的C-范数(绝对值的最大值)
CV_L1 - 归一化数组的L1-范数(绝对值的和)
CV_L2 - 归一化数组的(欧几里德)L2-范数
CV_MINMAX - 数组的数值被平移或缩放到一个指定的范围
mask
操作掩膜,用于指示函数是否仅仅对指定的元素进行操作
该函数归一化输入数组使它的范数或者数值范围在一定的范围内
当norm_type==CV_MINMAX:
dst(i,j)=(src(i,j)-min(src))*(b'-a')/(max(src)-min(src)) + a', if mask(i,j)!=0
dst(i,j)=src(i,j) otherwise
其中b'=MAX(a,b), a'=MIN(a,b);
当norm_type!=CV_MINMAX:
dst(i,j)=src(i,j)*a/cvNorm(src,0,norm_type,mask), if mask(i,j)!=0
dst(i,j)=src(i,j) otherwise
下面是一个简单的例子: float v[3] = { 1, 2, 3 };
CvMat V = cvMat( 1, 3, CV_32F, v );
// make vector v unit-length;
// equivalent to
// for(int i=0;i<3;i++) v[i]/=sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
cvNormalize( &V, &V );
CrossProduct
计算两个三维向量的叉积
void cvCrossProduct( const CvArr* src1, const CvArr* src2, CvArr* dst );
src1
第一输入向量。
src2
第二输入向量。
dst
输出向量
函数 cvCrossProduct 计算两个三维向量的差积:
dst = src1 × src2, (dst1 = src12src23 - src13src22 , dst2 = src13src21 - src11src23 , dst3 = src11src22 - src12src21).
ScaleAdd
计算一个数组缩放后与另一个数组的和
void cvScaleAdd( const CvArr* src1, CvScalar scale, const CvArr* src2, CvArr* dst );
#define cvMulAddS cvScaleAdd
src1
第一输入数组
scale
第一输入数组的缩放因子
src2
第二输入数组
dst
输出数组
函数 cvScaleAdd 计算一个数组缩放后与另一个数组的和:
dst(I)=src1(I)*scale + src2(I)
所有的数组参数必须有相同的类型和大小。
GEMM
通用矩阵乘法
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 )
src1
第一输入数组
src2
第二输入数组
src3
第三输入数组 (偏移量),如果没有偏移量,可以为空( NULL) 。
dst
输出数组
tABC
T操作标志,可以是 0 或者下面列举的值的组合:
CV_GEMM_A_T - 转置 src1
CV_GEMM_B_T - 转置 src2
CV_GEMM_C_T - 转置 src3
例如, CV_GEMM_A_T+CV_GEMM_C_T 对应
alpha*src1T*src2 + beta*src3T
函数 cvGEMM 执行通用矩阵乘法:
dst = alpha*op(src1)*op(src2) + beta*op(src3), 这里 op(X) 是 X 或者 XT
所有的矩阵应该有相同的数据类型和协调的矩阵大小。支持实数浮点矩阵或者复数浮点矩阵。
Transform
对数组每一个元素执行矩阵变换
void cvTransform( const CvArr* src, CvArr* dst, const CvMat* transmat, const CvMat* shiftvec=NULL );
src
输入数组
dst
输出数组
transmat
变换矩阵
shiftvec
可选偏移向量
函数 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 应该是实数浮点矩阵。
该函数可以用来进行 ND 点集的几何变换,任意的线性颜色空间变换,通道转换等。
PerspectiveTransform
向量数组的透视变换
void cvPerspectiveTransform( const CvArr* src, CvArr* dst, const CvMat* mat );
src
输入的三通道浮点数组
dst
输出三通道浮点数组
mat
4 × 4 变换矩阵
函数 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
MulTransposed
计算数组和数组的转置的乘积
void cvMulTransposed( const CvArr* src, CvArr* dst, int order, const CvArr* delta=NULL );
src
输入矩阵
dst
目标矩阵
order
乘法顺序
delta
一个可选数组, 在乘法之前从 src 中减去该数组。
函数 cvMulTransposed 计算 src 和它的转置的乘积。
函数求值公式:
如果 order=0
dst=(src-delta)*(src-delta)T
否则
dst=(src-delta)T*(src-delta)
Trace
返回矩阵的迹
CvScalar cvTrace( const CvArr* mat );
mat
输入矩阵
函数 cvTrace 返回矩阵mat的对角线元素的和。
Transpose
矩阵的转置
void cvTranspose( const CvArr* src, CvArr* dst );
#define cvT cvTranspose
src
输入矩阵
dst
目标矩阵
函数 cvTranspose 对矩阵 src 求转置:
dst(i,j)=src(j,i)
注意,假设是复数矩阵不会求得复数的共轭。共轭应该是独立的:查看的 cvXorS 例子代码。
Det
返回矩阵的行列式值
double cvDet( const CvArr* mat );
mat
输入矩阵
函数 cvDet 返回方阵 mat 的行列式值。对小矩阵直接计算,对大矩阵用 高斯(GAUSSIAN)消去法。对于对称正定(positive-determined)矩阵也可以用 SVD 函数来求,U=V=NULL ,然后用 w 的对角线元素的乘积来计算行列式。
Invert
查找矩阵的逆矩阵或伪逆矩阵
double cvInvert( const CvArr* src, CvArr* dst, int method=CV_LU );
#define cvInv cvInvert
src
输入矩阵
dst
目标矩阵
method
求逆方法:
CV_LU -最佳主元选取的高斯消除法
CV_SVD - 奇异值分解法 (SVD)
CV_SVD_SYM - 正定对称矩阵的 SVD 方法
函数 cvInvert 对矩阵 src 求逆并将结果存储到 dst。
如果是 LU 方法该函数返回 src 的行列式值 (src 必须是方阵)。 如果是 0, 矩阵不求逆, dst 用 0 填充。
如果 SVD 方法该函数返回 src 的条件数的倒数(最小奇异值和最大奇异值的比值) ,如果 src 全为 0 则返回0。 如果 src 是奇异的, SVD 方法计算一个伪逆矩阵。
Solve
求解线性系统或者最小二乘法问题
int cvSolve( const CvArr* src1, const CvArr* src2, CvArr* dst, int method=CV_LU );
src1
输入矩阵
src2
线性系统的右部
dst
输出解答
method
解决方法(矩阵求逆) :
CV_LU - 最佳主元选取的高斯消除法
CV_SVD - 奇异值分解法 (SVD)
CV_SVD_SYM - 对正定对称矩阵的 SVD 方法
函数 cvSolve 解决线性系统或者最小二乘法问题 (后者用 SVD 方法可以解决):
如果使用 CV_LU 方法。 如果 src1 是非奇异的,该函数则返回 1 ,否则返回 0 ,在后一种情况下 dst 是无效的。
SVD
对实数浮点矩阵进行奇异值分解
void cvSVD( CvArr* A, CvArr* W, CvArr* U=NULL, CvArr* V=NULL, int flags=0 );
A
M×N 的输入矩阵
W
结果奇异值矩阵 (M×N 或者 N×N) 或者 向量 (N×1).
U
可选的左部正交矩阵 (M×M or M×N). 如果 CV_SVD_U_T 被指定, 应该交换上面所说的行与列的数目。
V
可选右部正交矩阵(N×N)
flags
操作标志; 可以是 0 或者下面的值的组合:
· CV_SVD_MODIFY_A 通过操作可以修改矩阵 src1 。这样处理速度会比较快。
· CV_SVD_U_T 意味着会返回转置矩阵 U ,指定这个标志将加快处理速度。
· CV_SVD_V_T 意味着会返回转置矩阵 V ,指定这个标志将加快处理速度。
函数 cvSVD 将矩阵 A 分解成一个对角线矩阵和两个正交矩阵的乘积:
这里 W 是一个奇异值的对角线矩阵,它可以被编码成奇异值的一维向量,U 和 V 也是一样。 所有的奇异值都是非负的并按降序存储。(U 和 V 也相应的存储)。
SVD 算法在数值处理上已经很稳定,它的典型应用包括:
· 当 A 是一个方阵、对称阵和正矩阵时精确的求解特征值问题,例如, 当 A 时一个协方差矩阵时。在这种情况下 W 将是一个特征值的的向量,并且 U=V是矩阵的特征向量(因此,当需要计算特征向量时 U 和 V 只需要计算其中一个就可以了) 。
· 精确的求解病态线性系统。
· 超定线性系统的最小二乘求解。上一个问题和这个问题都可以用指定 CV_SVD 的 cvSolve 方法。
· 精确计算矩阵的不同特征,如秩(非零奇异值的数目), 条件数(最大奇异值和最小奇异值的比例), 行列式值(行列式的绝对值等于奇异值的乘积).上述的所有这些值都不要求计算矩阵 U 和 V 。
SVBkSb
奇异值回代算法(back substitution)
void cvSVBkSb( const CvArr* W, const CvArr* U, const CvArr* V,
const CvArr* B, CvArr* X, int flags );
W
奇异值矩阵或者向量
U
左正交矩阵 (可能是转置的)
V
右正交矩阵 (可能是转置的)
B
原始矩阵 A 的伪逆的乘法矩阵。这个是可选参数。如果它被省略则假定它是一个适当大小的单位矩阵(因此 x 将是 A 的伪逆的重建).。
X
目标矩阵: 奇异值回代算法的结果
flags
操作标志, 和刚刚讨论的 cvSVD 的标志一样。
函数 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) 计算中内部分配的临时矩阵。
EigenVV
计算对称矩阵的特征值和特征向量
void cvEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, double eps=0 );
mat
输入对称方阵。在处理过程中将被改变。
evects
特征向量输出矩阵, 连续按行存储
evals
特征值输出矩阵,按降序存储(当然特征值和特征向量的排序是同步的)。
eps
对角化的精确度 (典型地, DBL_EPSILON=≈10-15 就足够了)。
函数 cvEigenVV 计算矩阵 A 的特征值和特征向量:
mat*evects(i,:)' = evals(i)*evects(i,:)' (在 MATLAB 的记法)
矩阵 A 的数据将会被这个函数修改。
目前这个函数比函数 cvSVD 要慢,精确度要低, 如果已知 A 是正定的,(例如, 它是一个协方差矩阵), 它通常被交给函数 cvSVD 来计算其特征值和特征向量,尤其是在不需要计算特征向量的情况下
CalcCovarMatrix
计算向量集合的协方差矩阵
void cvCalcCovarMatrix( const CvArr** vects, int count, CvArr* cov_mat, CvArr* avg, int flags );
vects
输入向量。他们必须有同样的数据类型和大小。这个向量不一定非是一维的,他们也可以是二维(例如,图像)等等。
count
输入向量的数目
cov_mat
输出协方差矩阵,它是浮点型的方阵。
avg
输入或者输出数组 (依赖于标记“flags”) - 输入向量的平均向量。
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)比较向量中等等。
Mahalanobis
计算两个向量之间的马氏距离(Mahalanobis distance)
double cvMahalanobis( const CvArr* vec1, const CvArr* vec2, CvArr* mat );
vec1
第一个一维输入向量
vec2
第二个一维输入向量
mat
协方差矩阵的逆矩阵
函数 cvMahalanobis 计算两个向量之间的加权距离,其返回结果是:
协方差矩阵可以用函数cvCalcCovarMatrix 计算出来,逆矩阵可以用函数 cvInvert 计算出来 (CV_SVD 方法是一个比较好的选择, 因为矩阵可能是奇异的).
CalcPCA
对一个向量集做PCA变换
void cvCalcPCA( const CvArr* data, CvArr* avg,
CvArr* eigenvalues, CvArr* eigenvectors, int flags );
data
输入数据,每个向量是单行向量(CV_PCA_DATA_AS_ROW)或者单列向量(CV_PCA_DATA_AS_COL).
avg
平均向量,在函数内部计算或者由调用者提供
eigenvalues
输出的协方差矩阵的特征值
eigenvectors
输出的协方差矩阵的特征向量(也就是主分量),每个向量一行
flags
操作标志,可以是以下几种方式的组合:
CV_PCA_DATA_AS_ROW - 向量以行的方式存放(也就是说任何一个向量都是连续存放的)
CV_PCA_DATA_AS_COL - 向量以列的方式存放(也就是说某一个向量成分的数值是连续存放的)
(上面两种标志是互相排斥的)
CV_PCA_USE_AVG - 使用预先计算好的平均值
该函数对某个向量集做PCA变换.它首先利用cvCalcCovarMatrix计算协方差矩阵然后计算协方差矩阵的特征值与特征向量.输出的特征值/特征向量的个数小于或者等于MIN(rows(data),cols(data)).
ProjectPCA
把向量向某个子空间投影
void cvProjectPCA( const CvArr* data, const CvArr* avg,
const CvArr* eigenvectors, CvArr* result )
data
输入数据,每个向量可以是单行或者单列
avg
平均向量.要么它是单行向量那么意味着输入数据以行数据的形式存放,要么就是单列向量,那么就意味着那么输入向量就是以列的方式存放.
eigenvectors
特征向量(主分量),每个向量一行.
result
输出的分解系数矩阵,矩阵的行数必须与输入向量的个数相等,矩阵的列数必须小于特征向量的行数.
该函数将输入向量向一个正交系(eigenvectors)投影.在计算点乘之前,输入向量要减去平均向量:
result(i,:)=(data(i,:)-avg)*eigenvectors' // for CV_PCA_DATA_AS_ROW layout.
BackProjectPCA
根据投影系数重构原来的向量
void cvBackProjectPCA( const CvArr* proj, const CvArr* avg,
const CvArr* eigenvects, CvArr* result );
proj
输入数据,与cvProjectPCA里面的格式一致
avg
平均向量.如果它是单行向量,那么意味着输出向量是以行的方式存放.否则就是单列向量,那么输出向量就是以列的方式存放.
eigenvectors
特征向量(主分量),每个向量一行.
result
输出的重构出来的矩阵
该函数根据投影系数重构原来的向量:
result(i,:)=proj(i,:)*eigenvectors + avg // for CV_PCA_DATA_AS_ROW layout.
数学函数
Round, Floor, Ceil
转换浮点数为整数
int cvRound( double value );
int cvFloor( double value );
int cvCeil( double value );
value
输入浮点值
函数 cvRound, cvFloor, cvCeil 用一种舍入方法将输入浮点数转换成整数。 cvRound 返回和参数最接近的整数值。 cvFloor 返回不大于参数的最大整数值。cvCeil 返回不小于参数的最小整数值。在某些体系结构中该函数 工作起来比标准 C 操作起来还要快。如果参数的绝对值大于 231 ,结果是不可预料的。对特殊值 (±Inf, NaN) 未进行处理。
Sqrt
计算平方根
float cvSqrt( float value );
value
输入浮点值
函数 cvSqrt 计算输入值的平方根。如果输入的是复数, 结果将不可预料。
InvSqrt
计算平方根的倒数
float cvInvSqrt( float value );
value
输入浮点值
函数 cvInvSqrt 计算输入值的平方根的倒数,大多数情况下它比 1./sqrt(value) 要快。 如果输入的是 0 或者复数,结果将不可预料。特别值 (±Inf, NaN) 是不可控制的。
Cbrt
计算立方根
float cvCbrt( float value );
value
输入浮点值
函数 cvCbrt 计算输入值的立方根,大多数情况下它比 pow(value,1./3) 要快。 另外, 负数也是可操作的。特别值 (±Inf, NaN) 是不可控制的。
FastArctan
计算二维向量的角度
float cvFastArctan( float y, float x );
x
二维向量的 x 坐标
y
二维向量的 y 坐标
函数 cvFastArctan 计算二维向量的全范围角度角度, 变化范围是 0°到 360°。 精确度为 ~0.1° 。
IsNaN
判断输入是否是一个数字
int cvIsNaN( double value );
value
输入浮点值
函数 cvIsNaN 发现输入是一个数字则返回 1 ( IEEE754 标准 ),否则返回 0 。
IsInf
判断输入是否是无穷大
int cvIsInf( double value );
value
输入浮点值
函数 cvIsInf 如果输入是 ±Infinity ( IEEE754 标准 )则返回 1 ,否则返回 0 .
CartToPolar
计算二维向量的长度和/或者角度
void cvCartToPolar( const CvArr* x, const CvArr* y, CvArr* magnitude,
CvArr* angle=NULL, int angle_in_degrees=0 );
x
x 坐标数组
y
y 坐标数组
magnitude
存储向量长度输出数组, 如果不是必要的它可以为空(NULL)
angle
存储角度输出数组, 如果不是必要的它可以为空(NULL)。它可以被标准化为弧度 (0..2π) 或者度数(0..360°)
所有的数组只支持浮点类型的运算,也即x,y,magnitude,angle必须是浮点类型的数组。
angle_in_degrees
指示角度是用弧度或者度数表示的标志,缺省模式为弧度
函数 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.
(建议:英文文档虽然是写成atan( y(I)/x(I) ),但是建议和C中的表达方式统一。atan不能识别在那个象限,只能返回0-180°,atan2(x,y)才能返回0-360°的值)
PolarToCart
计算极坐标形式的二维向量对应的直角坐标
void cvPolarToCart( const CvArr* magnitude, const CvArr* angle,
CvArr* x, CvArr* y, int angle_in_degrees=0 );
magnitude
长度数组.如果为空(NULL),长度被假定为全是 1's.
angle
角度数组,弧度或者角度表示.
x
输出 x 坐标数组, 如果不需要,可以为空(NULL).
y
输出 y 坐标数组, 如果不需要,可以为空(NULL).
angle_in_degrees
指示角度是用弧度或者度数表示的标志,缺省模式为弧度
函数 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))
Pow
对数组内每个元素求幂
void cvPow( const CvArr* src, CvArr* dst, double power );
src
输入数组
dst
输出数组, 应该和输入数组有相同的类型
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, 优化算法被使用。
Exp
计算数组元素的指数幂
void cvExp( const CvArr* src, CvArr* dst );
src
输入数组
dst
输出数组, 它应该是 double 型的或者和输入数组有相同的类型
函数 cvExp 计算输入数组的每个元素的 e 次幂:
dst(I)=exp(src(I))
最大相对误差为 ≈7e-6. 通常, 该函数转换无法输出的值为 0 输出。
Log
计算每个数组元素的绝对值的自然对数
void cvLog( const CvArr* src, CvArr* dst );
src
输入数组。
dst
输出数组,它应该是 double 型的或者和输入数组有相同的类型。
函数 cvLog 计算输入数组每个元素的绝对值的自然对数:
dst(I)=log(abs(src(I))), src(I)!=0
dst(I)=C, src(I)=0
这里 C 是一个大负数 (≈-700 现在的实现中)。
SolveCubic
求解曲线函数的实根
void cvSolveCubic( const CvArr* coeffs, CvArr* roots );
coeffs
等式系数, 一个三到四个元素的数组.
roots
输出的矩阵等式的实根。它应该具有三个元素.
函数 cvSolveCubic 求解曲线函数的实根:
coeffs[0]*x^3 + coeffs[1]*x^2 + coeffs[2]*x + coeffs[3] = 0
(如果coeffs是四元素的矢量)
或者
x^3 + coeffs[0]*x^2 + coeffs[1]*x + coeffs[2] = 0
(如果coeffs是三元素的矢量)
函数返回求解得到的实根数目. 实根被存储在矩阵root中, 如果只有一个实根则用0来替代相关值.
随机数生成
RNG
初始化随机数生成器状态
CvRNG cvRNG( int64 seed=-1 );
seed
64-bit 的值用来初始化一个随机序列
函数 cvRNG 初始化随机数生成器并返回其状态。指向这个状态的指针可以传递给函数 cvRandInt, cvRandReal 和 cvRandArr . 在通常的实现中使用一个 multiply-with-carry generator 。
RandArr
用随机数填充数组并更新 RNG 状态
void cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, CvScalar param1, CvScalar param2 );
rng
被 cvRNG 初始化的 RNG 状态.
arr
输出数组
dist_type
分布类型:
CV_RAND_UNI - 均匀分布
CV_RAND_NORMAL - 正态分布 或者 高斯分布
param1
分布的第一个参数。如果是均匀分布它是随机数范围的闭下边界。如果是正态分布它是随机数的平均值。
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 */
RandInt
返回 32-bit 无符号整型并更新 RNG
unsigned cvRandInt( CvRNG* rng );
rng
被 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 */
RandReal
返回浮点型随机数并更新 RNG
double cvRandReal( CvRNG* rng );
rng
被 cvRNG 初始化的 RNG 状态
函数 cvRandReal 返回均匀分布的随机浮点数,范围在 0..1 之间 (不包括 1 )。
离散变换
DFT
执行一维或者二维浮点数组的离散傅立叶正变换或者离散傅立叶逆变换
#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, int nonzero_rows=0);
src
输入数组, 实数或者复数.
dst
输出数组,和输入数组有相同的类型和大小。
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 和高维的变换等等。
nonzero_rows
输入矩阵中非0行的个数(在2维的Forward变换中),或者是输出矩阵中感兴趣的行(在反向的2维变换中)。如果这个值是负数,0,或者大于总行数的一个值,它将会被忽略。这个参数可以用来加速2维DFT/IDFT的速度。见下面的例子。
函数 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 );
GetOptimalDFTSize
对于给定的矢量尺寸返回最优DFT尺寸
int cvGetOptimalDFTSize( int size0 );
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)
MulSpectrums
两个傅立叶频谱的每个元素的乘法(Performs per-element multiplication of two Fourier spectrums)
void cvMulSpectrums( const CvArr* src1, const CvArr* src2, CvArr* dst, int flags );
src1
第一输入数组
src2
第二输入数组
dst
输出数组,和输入数组有相同的类型和大小。
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 可以用来快速计算两个数组的卷积.
DCT
执行一维或者二维浮点数组的离散馀弦变换或者离散反馀弦变换
#define CV_DXT_FORWARD 0
#define CV_DXT_INVERSE 1
#define CV_DXT_ROWS: 4
void cvDCT( const CvArr* src, CvArr* dst, int flags );
src
输入数组, 1D 或者 2D 实数数组.
dst
输出数组,和输入数组有相同的类型和大小。
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)
内存存储(memory storage)
CvMemStorage
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 重置。
CvMemBlock
内存存储块结构
typedef struct CvMemBlock
{
struct CvMemBlock* prev;
struct CvMemBlock* next;
} CvMemBlock;
CvMemBlock 代表一个单独的内存存储块结构。 内存存储块中的实际数据存储在 header块 之后(即:存在一个头指针 head 指向的块 header ,该块不存储数据),于是,内存块的第 i 个字节可以通过表达式 ((char*)(mem_block_ptr+1))[i] 获得。然而,通常没必要直接去获得存储结构的域。
CvMemStoragePos
内存存储块地址
typedef struct CvMemStoragePos
{
CvMemBlock* top;
int free_space;
} CvMemStoragePos;
该结构(如以下所说)保存栈顶的地址,栈顶可以通过 cvSaveMemStoragePos 保存,也可以通过 cvRestoreMemStoragePos 恢复。
CreateMemStorage
创建内存块
CvMemStorage* cvCreateMemStorage( int block_size=0 );
block_size
存储块的大小以字节表示。如果大小是 0 byte, 则将该块设置成默认值 -- 当前默认大小为64k.
函数 cvCreateMemStorage 创建一内存块并返回指向块首的指针。起初,存储块是空的。头部(即:header)的所有域值都为 0,除了 block_size 外.
CreateChildMemStorage
创建子内存块
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.
ReleaseMemStorage
释放内存块
void cvReleaseMemStorage( CvMemStorage** storage );
storage
指向被释放了的存储块的指针
函数 cvReleaseMemStorage 释放所有的存储(内存)块 或者 将它们返回给各自的 parent(如果需要的话)。接下来再释放 header块(即:释放头指针 head 指向的块 = free(head))并清除指向该块的指针(即:head = NULL)。在释放作为 parent 的块之前,先清除各自的 child 块。
ClearMemStorage
清空内存存储块
void cvClearMemStorage( CvMemStorage* storage );
storage
存储存储块
函数 cvClearMemStorage 将存储块的 top 置到存储块的头部(注:清空存储块中的存储内容)。该函数并不释放内存(仅清空内存)。假使该内存块有一个父内存块(即:存在一内存块与其有父子关系),则函数就将所有的块返回给其 parent.
MemStorageAlloc
在存储块中分配一内存缓冲区
void* cvMemStorageAlloc( CvMemStorage* storage, size_t size );
storage
内存块.
size
缓冲区的大小.
函数 cvMemStorageAlloc 在存储块中分配一内存缓冲区。该缓冲区的大小不能超过内存块的大小,否则就会导致运行时错误。缓冲区的地址被调整为CV_STRUCT_ALIGN 字节 (当前为 sizeof(double)).
MemStorageAllocString
在存储块中分配一文本字符串
typedef struct CvString
{
int len;
char* ptr;
}
CvString;
CvString cvMemStorageAllocString( CvMemStorage* storage, const char* ptr, int len=-1 );
storage
存储块
ptr
字符串
len
字符串的长度(不计算‘\0’)。如果参数为负数,函数就计算该字符串的长度。
函数 cvMemStorageAlloString 在存储块中创建了一字符串的拷贝。它返回一结构,该结构包含字符串的长度(该长度或通过用户传递,或通过计算得到)和指向被拷贝了的字符串的指针。
SaveMemStoragePos
保存内存块的位置(地址)
void cvSaveMemStoragePos( const CvMemStorage* storage, CvMemStoragePos* pos );
storage
内存块.
pos
内存块顶部位置。
函数 cvSaveMemStoragePos 将存储块的当前位置保存到参数 pos 中。 函数 cvRestoreMemStoragePos 可进一步获取该位置(地址)。
RestoreMemStoragePos
恢复内存存储块的位置
void cvRestoreMemStoragePos( CvMemStorage* storage, CvMemStoragePos* pos );
storage
内存块.
pos
新的存储块的位置
函数 cvRestoreMemStoragePos 通过参数 pos 恢复内存块的位置。该函数和函数 cvClearMemStorage 是释放被占用内存块的唯一方法。注意:没有什么方法可去释放存储块中被占用的部分内存。
序列
CvSeq
可动态增长元素序列(OpenCV_1.0已发生改变,详见cxtypes.h) 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动态数据结构的基础。在1.0版本中,将前六个成员剥离出来定义成一个宏. 通过不同寻常的宏定义简化了带有附加参数的结构 CvSeq 的扩展。为了扩展 CvSeq, 用户可以定义一新的数据结构或在通过宏CV_SEQUENCE_FIELDS()所包括的 CvSeq 的域后在放入用户自定义的域。
有两种类型的序列 -- 稠密序列和稀疏序列。稠密序列都派生自 CvSeq, 它们用来代表可扩展的一维数组 -- 向量,栈,队列,双端队列。数据间不存在空隙(即:连续存放)-- 如果元素从序列中间被删除或插入新的元素到序列中(不是两端),那么此元素后边的相关元素会被移动。稀疏序列都派生自 CvSet,后面会有详细的讨论。它们都是由节点所组成的序列,每一个节点要么被占用空间要么是空,由 flag 标志指定。这些序列作为无序的数据结构而被使用,如点集,图,哈希表等。
域 header_size(结构的大小) 含有序列头部节点的实际大小,此大小大于或等于 sizeof(CvSeq).当这个宏用在序列中时,应该等于 sizeof(CvSeq),若这个宏用在其他结构中,如CvContour,结构的大小应该大于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)
CvSeqBlock
连续序列块
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,除非一些元素已被插入到序列中。
CvSlice
序列分割
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 获取子序列。
CreateSeq
创建一序列
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) 个字节的空间。
SetSeqBlockSize
设置序列块的大小
void cvSetSeqBlockSize( CvSeq* seq, int delta_elems );
seq
序列
delta_elems
满足元素所需的块的大小
函数 cvSetSeqBlockSize 会对内存分配的粒度产生影响。当序列缓冲区中空间消耗完时,函数为 delta_elems 个序列元素分配空间。如果新分配的空间与之前分配的空间相邻的话,这两个块就合并,否则,就创建了一个新的序列快。因此,参数值越大,序列中出现碎片的可能性就越小,不过内存中更多的空间将被浪费。当序列被创建后,参数 delta_elems 大小将被设置为 默认大小(1K).之后, 就可随时调用该函数,并影响内存分配。 函数可以修改被传递过来的参数值,以满足内存块的大小限制。
SeqPush
添加元素到序列的尾部
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 和相关函数)
SeqPop
删除序列尾部元素
void cvSeqPop( CvSeq* seq, void* element=NULL );
seq
序列
element
可选参数。如果该指针不为空,就拷贝被删元素到指针指向的位置
函数 cvSeqPop 从序列中删除一元素。如果序列已经为空,就报告一错误。函数时间复杂度为 O(1).
SeqPushFront
在序列头部添加元素
char* cvSeqPushFront( CvSeq* seq, void* element=NULL );
seq
序列
element
添加的元素
函数 cvSeqPushFront 类似于 cvSeqPush, 不过是在序列头部添加元素。时间复杂度为O(1).
SeqPopFront
删除序列的头部元素
void cvSeqPopFront( CvSeq* seq, void* element=NULL );
seq
序列
element
可选参数。如果该指针不为空,就拷贝被珊元素到指针指向的位置。
函数 cvSeqPopFront 删除序列的头部元素。如果序列已经为空,就报告一错误。函数时间复杂度为 O(1).
SeqPushMulti
添加多个元素到序列尾部或头部。
void cvSeqPushMulti( CvSeq* seq, void* elements, int count, int in_front=0 );
seq
序列
elements
待添加的元素
count
添加的元素个数
in_front
标示在头部还是尾部添加元素
CV_BACK ( = 0) -- 在序列尾部添加元素。
CV_FRONT( != 0) -- 在序列头部添加元素。
函数 cvSeqPushMulti 在序列头部或尾部添加多个元素。元素按输入数组中的顺序被添加到序列中,不过它们可以添加到不同的序列中
SeqPopMulti
删除多个序列头部或尾部的元素
void cvSeqPopMulti( CvSeq* seq, void* elements, int count, int in_front=0 );
seq
序列
elements
待删除的元素
count
删除的元素个数
in_front
标示在头部还是尾部删除元素
CV_BACK ( = 0) -- 删除序列尾部元素。
CV_FRONT( != 0) -- 删除序列头部元素。
函数 cvSeqPopMulti 删除多个序列头部或尾部的元素。如果待删除的元素个数超过了序列中的元素总数,则函数删除尽可能多的元素 。
SeqInsert
在序列中添加元素
char* cvSeqInsert( CvSeq* seq, int before_index, void* element=NULL );
seq
序列
before_index
元素插入的位置(索引)。如果插入的位置在 0(允许的参数最小值)前,则该函数等同于函数 cvSeqPushFront.如果是在 seq_total(允许的参数最大值)后,则该函数等同于 cvSeqPush.
element
待插入的元素
函数 cvSeqInsert 移动 从被插入的位置到序列尾部元素所在的位置的所有元素,如果指针 element 不位 null, 则拷贝 element 中的元素到指定位置。函数返回指向被插入元素的指针。
SeqRemove
从序列中删除指定的元素。
void cvSeqRemove( CvSeq* seq, int index );
seq
目标序列
index
被删除元素的索引或位置。
函数 cvSeqRemove 删除seq中指定索引(位置)的元素。如果这个索引超出序列的元素个数,会报告出错。企图从空序列中删除元素,函数也将报告错误。函数通过移动序列中的元素来删除被索引的元素。
ClearSeq
清空序列
void cvClearSeq( CvSeq* seq );
seq
Sequence.
seq
序列
函数 cvClearSeq 删除序列中的所有元素。函数不会将内存返回到存储器中,当新的元素添加到序列中时,可重新使用该内存。函数时间复杂度为 O(1).
GetSeqElem
返回索引所指定的元素指针
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), 假设块的大小要比元素的数量要小。
SeqElemIdx
返回序列中元素的索引
int cvSeqElemIdx( const CvSeq* seq, const void* element, CvSeqBlock** block=NULL );
seq
序列
element
指向序列中元素的指针
block
可选参数, 如果不为空(NULL),则存放包含该元素的块的地址
函数 cvSeqElemIdx 返回元素的索引,如果该元素不存在这个序列中,则返回一负数。
cvSeqToArray
拷贝序列中的元素到一个连续的内存块中
void* cvCvtSeqToArray( const CvSeq* seq, void* elements, CvSlice slice=CV_WHOLE_SEQ );
seq
序列
elemenets
指向目的(存放拷贝元素的)数组的指针,指针指向的空间必须足够大。
slice
拷贝到序列中的序列部分。
函数 cvCvtSeqToArray 拷贝整个序列或部分序列到指定的缓冲区中,并返回指向该缓冲区的指针.
MakeSeqHeaderForArray
构建序列
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指针,因此可以读取指针,但试图将元素添加到序列中则多数会引发错误。
SeqSlice
为各个序列碎片建立头
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 参数, 则使用该函数去获取该序列。.
CloneSeq
创建序列的一份拷贝
CvSeq* cvCloneSeq( const CvSeq* seq, CvMemStorage* storage=NULL );
seq
序列
storage
存放新序列的 header部分和拷贝数据(如果需要)的目的存储块。如果为 NULL, 则函数使用包含输入序列的存储块 。
函数 cvCloneSeq 创建输入序列的一份完全拷贝。调用函数 cvCloneSeq (seq, storage) 等同于调用 cvSeqSlice(seq, CV_WHOLE_SEQ, storage, 1).
SeqRemoveSlice
删除序列的 slice部分
void cvSeqRemoveSlice( CvSeq* seq, CvSlice slice );
seq
序列
slice
序列中被移动的那部分
函数 cvSeqRemoveSlice 删除序列中的 slice 部分
SeqInsertSlice
在序列中插入一数组
void cvSeqInsertSlice( CvSeq* seq, int before_index, const CvArr* from_arr );
seq
序列
slice
序列中被移动的那部分
from_arr
从中获取元素的数组
函数 cvSeqInsertSlice 在指定位置插入来自数组from_arr中 所有元素。数组 from_arr 可以是一个 矩阵也可以是另外一个序列。
SeqInvert
将序列中的元素进行逆序操作
void cvSeqInvert( CvSeq* seq );
seq
序列
函数 cvSeqInvert 对序列进行逆序操作 -- 即:使第一个元素成为最后一个,最后一个元素为第一个。
SeqSort
使用特定的比较函数对序列中的元素进行排序
/* 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 );
SeqSearch
查询序列中的元素
/* 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.
StartAppendToSeq
将数据写入序列中,并初始化该过程
void cvStartAppendToSeq( CvSeq* seq, CvSeqWriter* writer );
seq
指向序列的指针
writer
writer 的状态; 由该函数初始化
函数 cvStartAppendToSeq 初始化将数据写入序列这个过程。通过宏 CV_WRITE_SEQ_ELEM( written_elem, writer ),写入的元素被添加到序列尾部。注意:在写入期间,序列的其他操作可能会产生的错误的结果,甚至破怀该序列(见 cvFlushSeqWriter 的相关描述,有助于避免这些错误)
StartWriteSeq
创建新序列,并初始化写入部分(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 返回(因当在最后调用)
EndWriteSeq
完成写入操作
CvSeq* cvEndWriteSeq( CvSeqWriter* writer );
writer
写入部分 writer 的状态
函数 cvEndWriteSeq 完成写入操作并返回指向被写入元素的序列的地址。同时,函数会截取最后那个不完整的序列块,将块的剩馀部分返回到内存中之后,序列就可以被安全的读和写。
FlushSeqWriter
根据写入状态,刷新序列头部
void cvFlushSeqWriter( CvSeqWriter* writer );
writer
写入部分的状态
函数 cvFlushSeqWriter 用来使用户在写入过程中每当需要时读取序列元素,比如说,核查制定的条件。函数更新序列的头部,从而使读取序列中的数据成为可能。不过,写入并没有被关闭,为的是随时都可以将数据写入序列。在有些算法中,经常需要刷新,考虑使用 cvSeqPush 代替该函数。
StartReadSeq
初始化序列中的读取过程
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 );
GetSeqReaderPos
返回当前的读取器的位置
int cvGetSeqReaderPos( CvSeqReader* reader );
reader
读取器的状态.
函数 cvGetSeqReaderPos 返回当前的 reader 位置 (在 0 到 reader->seq->total - 1 中)
SetSeqReaderPos
移动读取器到指定的位置。
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 的位置移动到绝对位置,或相对于当前的位置(相对位置)
集合
CvSet
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)等
CreateSet
创建空的数据集
CvSet* cvCreateSet( int set_flags, int header_size,
int elem_size, CvMemStorage* storage );
set_flags
集合的类型
header_size
头节点的大小;应该等于 sizeof(CvSet)
elem_size
元素的大小;不能小8
storage
相关容器
函数 CvCreateSet 创建一具有特定头部节点大小和元素类型的空集。并返回指向该集合的指针。
SetAdd
占用集合中的一个节点
int cvSetAdd( CvSet* set_header, CvSetElem* elem=NULL, CvSetElem** inserted_elem=NULL );
set_header
集合
elem
可选的输入参数,被插入的元素。如果不为 NULL, 函数就将数据拷贝到新分配的节点。(拷贝后,清空第一个域的 MSB)
函数 cvSetAdd 分配一新的节点,将输入数据拷贝给它(可选),并且返回指向该节点的指针和节点的索引值。索引值可通过节点的flags域的低位中获得。函数的时间复杂度为 O(1), 不过,存在着一个函数可快速的分配内存。(见 cvSetNew)
SetRemove
从点集中删除元素
void cvSetRemove( CvSet* set_header, int index );
set_header
集合
index
被删元素的索引值
函数 cvSetRemove 从点集中删除一具有特定索引值的元素。如果指定位置的节点为空,函数将什么都不做。函数的时间复杂度为 O(1), 不过,存在一函数可更快速的完成该操作,该函数就是 cvSetRemoveByPtr
SetNew
添加元素到点集中
CvSetElem* cvSetNew( CvSet* set_header );
set_header
集合
函数 cvSetNew 是 cvSetAdd 的变体,内联函数。它占用一新节点,并返回指向该节点的指针而不是索引。
SetRemoveByPtr
删除指针指向的集合元素
void cvSetRemoveByPtr( CvSet* set_header, void* elem );
set_header
集合
elem
被删除的元素
函数 cvSetRemoveByPtr 是一内联函数,是函数 cvSetRemove 轻微变化而来的。该函数并不会检查节点是否为空 -- 用户负责这一检查。
GetSetElem
通过索引值查找相应的集合元素
CvSetElem* cvGetSetElem( const CvSet* set_header, int index );
set_header
集合
index
索引值
函数 cvGetSetElem 通过索引值查找相应的元素。函数返回指向该元素的指针,如果索引值无效或相应的节点为空,则返回 0。 若函数使用 cvGetSeqElem 去查找节点,则函数支持负的索引值。
ClearSet
清空点集
void cvClearSet( CvSet* set_header );
set_header
待清空的点集
函数 cvClearSet 删除集合中的所有元素。时间复杂度为 O(1).
图
CvGraph
有向权图和无向权图
#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的边是没什么区别的,在某一时刻,只可能存在一个,即:要么是<A, B>要么是<B, A>.
CreateGraph
创建一个空树
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 创建一空图并且返回指向该图的指针。
GraphAddVtx
插入一顶点到图中
int cvGraphAddVtx( CvGraph* graph, const CvGraphVtx* vtx=NULL,
CvGraphVtx** inserted_vtx=NULL );
graph
图
vtx
可选输入参数,用来初始化新加入的顶点(仅大小超过 sizeof(CvGraphVtx) 的用户自定义的域才会被拷贝)
inserted_vertex
可选的输出参数。如果不为 NULL, 则传回新加入顶点的地址
函数 cvGraphAddVtx 将一顶点加入到图中,并返回定点的索引
GraphRemoveVtx
通过索引从图中删除一顶点
int cvGraphRemoveVtx( CvGraph* graph, int index );
graph
图
vtx_idx
被珊顶点的索引
函数 cvGraphRemoveAddVtx 从图中删除一顶点,连同删除含有此顶点的边。如果输入的顶点不属于该图的话,将报告删除出错(不存在而无法删除)。返回值为被删除的边数,如果顶点不属于该图的话,返回 -1。
GraphRemoveVtxByPtr
通过指针从图中删除一顶点
int cvGraphRemoveVtxByPtr( CvGraph* graph, CvGraphVtx* vtx );
graph
图
vtx
指向被删除的边的指针
函数 cvGraphRemoveVtxByPtr 从图中删除一顶点,连同删除含有此顶点的边。如果输入的顶点不属于该图的话,将报告删除出错(不存在而无法删除)。返回值为被删除的边数,如果顶点不属于该图的话,返回 -1。
GetGraphVtx
通过索引值查找图的相应顶点
CvGraphVtx* cvGetGraphVtx( CvGraph* graph, int vtx_idx );
graph
图
vtx_idx
定点的索引值
函数 cvGetGraphVtx 通过索引值查找对应的顶点,并返回指向该顶点的指针,如果不存在则返回 NULL.
GraphVtxIdx
返回定点相应的索引值
int cvGraphVtxIdx( CvGraph* graph, CvGraphVtx* vtx );
graph
图
vtx
指向顶点的指针
函数 cvGraphVtxIdx 返回与顶点相应的索引值
GraphAddEdge
通过索引值在图中加入一条边
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。 如果是后者(即:返回值为负),函数默认的报告一个错误。
GraphAddEdgeByPtr
通过指针在图中加入一条边
int cvGraphAddEdgeByPtr( CvGraph* graph, CvGraphVtx* start_vtx, CvGraphVtx* end_vtx,
const CvGraphEdge* edge=NULL, CvGraphEdge** inserted_edge=NULL );
graph
图
start_vtx
指向起始顶点的指针
end_vtx
指向尾部顶点的指针。对于无向图来说,顶点参数的次序无关紧要。
edge
可选的输入参数,初始化边的数据
inserted_edge
可选的输出参数,包含被插入的边的地址。
函数 cvGraphAddEdge 连接两特定的顶点。如果该边成功地加入到图中,返回 1; 如果连接两顶点的边已经存在,返回 0; 如果顶点没被发现(不存在)或者起始顶点和尾部顶点是同一个定点,或其他特殊情况,返回 -1。 如果是后者(即:返回值为负),函数默认的报告一个错误
GraphRemoveEdge
通过索引值从图中删除顶点
void cvGraphRemoveEdge( CvGraph* graph, int start_idx, int end_idx );
graph
图
start_idx
起始顶点的索引值
end_idx
尾部顶点的索引值。对于无向图来说,顶点参数的次序无关紧要。
函数 cvGraphRemoveEdge 删除连接两特定顶点的边。若两顶点并没有相连接(即:不存在由这两个顶点连接的边),函数什么都不做。
GraphRemoveEdgeByPtr
通过指针从图中删除边
void cvGraphRemoveEdgeByPtr( CvGraph* graph, CvGraphVtx* start_vtx, CvGraphVtx* end_vtx );
graph
图
start_vtx
指向起始顶点的指针
end_vtx
指向尾部顶点的指针。对于无向图来说,顶点参数的次序无关紧要。
函数 cvGraphRemoveEdgeByPtr 删除连接两特定顶点的边。若两顶点并没有相连接(即:不存在由这两个顶点连接的边),函数什么都不做。
FindGraphEdge
通过索引值在图中查找相应的边
CvGraphEdge* cvFindGraphEdge( const CvGraph* graph, int start_idx, int end_idx );
#define cvGraphFindEdge cvFindGraphEdge
graph
图
start_idx
起始顶点的索引值
end_idx
尾部顶点的索引值。对于无向图来说,顶点参数的次序无关紧要
函数 cvFindGraphEdge 查找与两特定顶点相对应的边,并返回指向该边的指针。如果该边不存在,返回 NULL.
FindGraphEdgeByPtr
通过指针在图中查找相应的边
CvGraphEdge* cvFindGraphEdgeByPtr( const CvGraph* graph, const CvGraphVtx* start_vtx,
const CvGraphVtx* end_vtx );
#define cvGraphFindEdgeByPtr cvFindGraphEdgeByPtr
graph
图
start_vtx
指向起始顶点的指针
end_vtx
指向尾部顶点的指针。对于无向图来说,顶点参数的次序无关紧要。
函数 cvFindGraphEdgeByPtr 查找与两特定顶点相对应的边,并返回指向该边的指针。如果该边不存在,返回 NULL
GraphEdgeIdx
返回与该边相应的索引值
int cvGraphEdgeIdx( CvGraph* graph, CvGraphEdge* edge );
graph
图
edge
指向该边的指针
函数 cvGraphEdgeIdx 返回与边对应的索引值。
GraphVtxDegree
(通过索引值)统计与顶点相关联的边数
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) 返回依附于该顶点的下一条边。
GraphVtxDegreeByPtr
(通过指针)统计与顶点相关联的边数
int cvGraphVtxDegreeByPtr( const CvGraph* graph, const CvGraphVtx* vtx );
graph
图
vtx
顶点对应的指针
函数 cvGraphVtxDegreeByPtr 返回与特定顶点相关联的边数,包括以该顶点为起始顶点的和尾部顶点的
ClearGraph
删除图
void cvClearGraph( CvGraph* graph );
graph
图
函数 cvClearGraph 删除该图的所有顶点和边。时间复杂度为 O(1).
CloneGraph
克隆图
CvGraph* cvCloneGraph( const CvGraph* graph, CvMemStorage* storage );
graph
待拷贝的图
storage
容器,存放拷贝
函数 cvCloneGraph 创建图的完全拷贝。如果顶点和边含有指向外部变量的指针,那么图和它的拷贝共享这些指针。在新的图中,顶点和边可能存在不同,因为函数重新分割了顶点和边的点集。
CvGraphScanner
图的遍历
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)
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 要使用该初始化了的结构 -- 层层遍历的过程。
NextGraphItem
逐层遍历整个图
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 中。
ReleaseGraphScanner
完成图地遍历过程
void cvReleaseGraphScanner( CvGraphScanner** scanner );
scanner
指向遍历器的指针.
函数 cvGraphScanner 完成图的遍历过程,并释放遍历器的状态。
树
CV_TREE_NODE_FIELDS
用于树结点类型声明的(助手)宏
#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 -- 所有动态结构的基本类型。如果树的节点是由该宏所声明的,那么就可以使用(该部分的)以下函数对树进行相关操作。
CvTreeNodeIterator
打开现存的存储结构或者创建新的文件存储结构
typedef struct CvTreeNodeIterator
{
const void* node;
int level;
int max_level;
}
CvTreeNodeIterator;
结构 CvTreeNodeIterator 用来对树进行遍历。该树的节点是由宏 CV_TREE_NODE_FIELDS 声明。
InitTreeNodeIterator
用来初始化树结点的迭代器
void cvInitTreeNodeIterator( CvTreeNodeIterator* tree_iterator,
const void* first, int max_level );
tree_iterator
初始化了的迭代器
first
(开始)遍历的第一个节点
max_level
限制对树进行遍历的最高层(即:第 max_level 层)(假设第一个节点所在的层为第一层)。例如:1 指的是遍历第一个节点所在层,2 指的是遍历第一层和第二层
函数 cvInitTreeNodeIterator 用来初始化树的迭代器。
NextTreeNode
返回当前节点,并将迭代器 iterator 移向当前节点的下一个节点
void* cvNextTreeNode( CvTreeNodeIterator* tree_iterator );
tree_iterator
初始化了的迭代器
函数 cvNextTreeNode 返回当前节点并且更新迭代器(iterator) -- 并将 iterator 移向(当前节点)下一个节点。换句话说,函数的行为类似于表达式 *p++ (通常的 C 指针 或 C++ 集合迭代器)。 如果没有更多的节点(即:当前节点为最后的节点),则函数返回值为 NULL.
PrevTreeNode
返回当前节点,并将迭代器 iterator 移向当前节点的前一个节点
void* cvPrevTreeNode( CvTreeNodeIterator* tree_iterator );
tree_iterator
初始化了的迭代器
函数 cvPrevTreeNode 返回当前节点并且更新迭代器(iterator) -- 并将 iterator 移向(当前节点的)前一个节点。换句话说,函数的行为类似于表达式 *p-- (通常的 C 指针 或 C++ 集合迭代器)。 如果没有更多的节点(即:当前节点为头节点),则函数返回值为 NULL.
TreeToNodeSeq
将所有的节点指针(即:指向树结点的指针)收集到线性表 sequence 中
CvSeq* cvTreeToNodeSeq( const void* first, int header_size, CvMemStorage* storage );
first
初始树结点
header_size
线性表的表头大小,大小通常为 sizeof(CvSeq)
函数 cvTreeToNodeSeq 将树的节点指针挨个的存放到线性表中。存放的顺序以深度为先。
InsertNodeIntoTree
将新的节点插入到树中
void cvInsertNodeIntoTree( void* node, void* parent, void* frame );
node
待插入的节点
parent
树中的父节点(即:含有子节点的节点)
frame
顶部节点。如果节点parent 等同于 节点frame, 则将节点的域 v_prev 设为 NULL 而不是 parent.
函数 cvInsertNodeIntoTree 将另一个节点插入到树中。函数不分配任何内存,仅仅修改树节点的连接关系。
RemoveNodeFromTree
从树中删除节点
void cvRemoveNodeFromTree( void* node, void* frame );
node
待删除的节点。
frame
顶部节点。如果 node->v.prev = NULL 且 node->h.prev = NULL, 则将 frame->v.next 设为 node->h.next
函数 cvRemoveNodeFromTree 从树中删除节点。它不会释放任何内存,仅仅修改树中节点的连接关系
曲线与形状
CV_RGB
创建一个色彩值.
#define CV_RGB( r, g, b ) cvScalar( (b), (g), (r) )
Line
绘制连接两个点的线段
void cvLine( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color,
int thickness=1, int line_type=8, int shift=0 );
img
图像。
pt1
线段的第一个端点。
pt2
线段的第二个端点。
color
线段的颜色。
thickness
线段的粗细程度。
line_type
线段的类型。
8 (or 0) - 8-connected line(8邻接)连接 线。
4 - 4-connected line(4邻接)连接线。
CV_AA - antialiased 线条。
shift
坐标点的小数点位数。
函数cvLine 在图像中的点1和点2之间画一条线段。线段被图像或感兴趣的矩形(ROI rectangle)所裁剪。对于具有整数坐标的non-antialiasing 线条,使用8-连接或者4-连接Bresenham 算法。画粗线条时结尾是圆形的。画 antialiased 线条使用高斯滤波。要指定线段颜色,用户可以使用使用宏CV_RGB( r, g, b )。
Rectangle
绘制简单、指定粗细或者带填充的 矩形
void cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color,
int thickness=1, int line_type=8, int shift=0 );
img
图像.
pt1
矩形的一个顶点。
pt2
矩形对角线上的另一个顶点
color
线条颜色 (RGB) 或亮度(灰度图像 )(grayscale image)。
thickness
组成矩形的线条的粗细程度。取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形。
line_type
线条的类型。见cvLine的描述
shift
坐标点的小数点位数。
函数 cvRectangle 通过对角线上的两个顶点绘制矩形。
Circle
绘制圆形。
void cvCircle( CvArr* img, CvPoint center, int radius, CvScalar color,
int thickness=1, int line_type=8, int shift=0 );
img
图像。
center
圆心坐标。
radius
圆形的半径。
color
线条的颜色。
thickness
如果是正数,表示组成圆的线条的粗细程度。否则,表示圆是否被填充。
line_type
线条的类型。见 cvLine 的描述
shift
圆心坐标点和半径值的小数点位数。
函数cvCircle绘制或填充一个给定圆心和半径的圆。圆被感兴趣矩形所裁剪。若指定圆的颜色,可以使用宏 CV_RGB ( r, g, b )。
Ellipse
绘制椭圆圆弧和椭圆扇形。
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 );
img
图像。
center
椭圆圆心坐标。
axes
轴的长度。
angle
偏转的角度。
start_angle
圆弧起始角的角度。.
end_angle
圆弧终结角的角度。
color
线条的颜色。
thickness
线条的粗细程度。
line_type
线条的类型,见CVLINE的描述。
shift
圆心坐标点和数轴的精度。
函数cvEllipse用来绘制或者填充一个简单的椭圆弧或椭圆扇形。圆弧被ROI矩形所忽略。反走样弧线和粗弧线使用线性分段近似值。所有的角都是以角度的形式给定的。下面的图片将解释这些参数的含义。
Parameters of Elliptic Arc
EllipseBox
使用一种简单的方式来绘制椭圆圆弧和椭圆扇形。
void cvEllipseBox( CvArr* img, CvBox2D box, CvScalar color,
int thickness=1, int line_type=8, int shift=0 );
img
图像。
box
绘制椭圆圆弧所需要的外界矩形.
thickness
分界线线条的粗细程度。
line_type
分界线线条的类型,见CVLINE的描述。
shift
椭圆框顶点坐标的精度。
The function cvEllipseBox draws a simple or thick ellipse outline, or fills an ellipse. The functions provides a convenient way to draw an ellipse approximating some shape; that is what cvCamShift and cvFitEllipse do. The ellipse drawn is clipped by ROI rectangle. A piecewise-linear approximation is used for antialiased arcs and thick arcs.
FillPoly
填充多边形内部
void cvFillPoly( CvArr* img, CvPoint** pts, int* npts, int contours,
CvScalar color, int line_type=8, int shift=0 );
img
图像。
pts
指向多边形的数组指针。
npts
多边形的顶点个数的数组。
contours
组成填充区域的线段的数量。
color
多边形的颜色。
line_type
组成多边形的线条的类型。
shift
顶点坐标的小数点位数。
函数cvFillPoly用于一个单独被多边形轮廓所限定的区域内进行填充。函数可以填充复杂的区域,例如,有漏洞的区域和有交叉点的区域等等。
FillConvexPoly
填充凸多边形
void cvFillConvexPoly( CvArr* img, CvPoint* pts, int npts,
CvScalar color, int line_type=8, int shift=0 );
img
图像。
pts
指向单个多边形的指针数组。
npts
多边形的顶点个数。
color
多边形的颜色。
line_type
组成多边形的线条的类型。参见cvLine
shift
顶点坐标的小数点位数。
函数cvFillConvexPoly填充凸多边形内部。这个函数比函数cvFillPoly 更快。它除了可以填充凸多边形区域还可以填充任何的单调多边形。例如:一个被水平线(扫描线)至多两次截断的多边形。
PolyLine
绘制简单线段或折线。
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 );
img
图像。
pts
折线的顶点指针数组。
npts
折线的定点个数数组。也可以认为是pts指针数组的大小
contours
折线的线段数量。
is_closed
指出多边形是否封闭。如果封闭,函数将起始点和结束点连线。
color
折线的颜色。
thickness
线条的粗细程度。
line_type
线段的类型。参见cvLine。
shift
顶点的小数点位数。
函数cvPolyLine 绘制一个简单直线或折线。
文本
InitFont
初始化字体结构体。
void cvInitFont( CvFont* font, int font_face, double hscale,
double vscale, double shear=0,
int thickness=1, int line_type=8 );
font
被初始化的字体结构体。
font_face
字体名称标识符。只是Hershey 字体集( http://sources.isc.org/utils/misc/hershey-font.txt )的一个子集得到支持。
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字体标记合成,就是斜体字。
hscale
字体宽度。如果等于1.0f,字符的宽度是最初的字体宽度。如果等于0.5f,字符的宽度是最初的字体宽度的一半。
vscale
字体高度。如果等于1.0f,字符的高度是最初的字体高度。如果等于0.5f,字符的高度是最初的字体高度的一半。
shear
字体的斜度。当值为0时 ,字符不倾斜;当值为1.0f时,字体倾斜≈45度,等等。厚度让字母着重显示。函数cvLine用于绘制字母。
thickness
字体笔划的粗细程度。
line_type
字体笔划的类型,参见cvLine。
函数cvInitFont初始化字体结构体,字体结构体可以被传递到文字显示函数中。
PutText
在图像中显示文本字符串。
void cvPutText( CvArr* img, const char* text, CvPoint org, const CvFont* font, CvScalar color );
img
输入图像。
text
要显示的字符串。
org
第一个字符左下角的坐标。
font
字体结构体。
color
文本的字体颜色。
函数cvPutText将具有指定字体的和指定颜色的文本加载到图像中。加载到图像中的文本被感兴趣的矩形框(ROI rectangle)剪切。不属于指定字体库的字符用矩形字符替代显示。
GetTextSize
获得字符串的宽度和高度。
void cvGetTextSize( const char* text_string, const CvFont* font, CvSize* text_size, int* baseline );
font
字体结构体
text_string
输入字符串。
text_size
合成字符串的字符的大小。文本的高度不包括基线以下的部分。
baseline
相对于文字最底部点的基线的Y坐标。
函数cvGetTextSize是用于在指定字体时计算字符串的绑定区域(binding rectangle)。
点集和轮廓
DrawContours
在图像中绘制外部和内部的轮廓。
void cvDrawContours( CvArr *img, CvSeq* contour,
CvScalar external_color, CvScalar hole_color,
int max_level, int thickness=1,
int line_type=8, CvPoint offset=cvPoint(0,0) );
img
用以绘制轮廓的图像。和其他绘图函数一样,边界图像被感兴趣区域(ROI)所剪切。
contour
指针指向第一个轮廓。
external_color
外层轮廓的颜色。
hole_color
内层轮廓的颜色。
max_level
绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓。
thickness
绘制轮廓时所使用的线条的粗细度。如果值为负(e.g. =CV_FILLED),绘制内层轮廓。
line_type
线条的类型。参考cvLine.
offset
按照给出的偏移量移动每一个轮廓点坐标.当轮廓是从某些感兴趣区域(ROI)中提取的然后需要在运算中考虑ROI偏移量时,将会用到这个参数。
当thickness>=0,函数cvDrawContours在图像中绘制轮廓,或者当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 以指示的得到外形。
(注意:在cvFindContours中参数为CV_CHAIN_CODE时,cvDrawContours用CV_FILLED时不会画出任何图形)
InitLineIterator
初始化直线迭代器
int cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2,
CvLineIterator* line_iterator, int connectivity=8,
int left_to_right=0 );
img
用以获取直线的图像。
pt1
线段的第一个端点。
pt2
线段的第二个端点。
line_iterator
指向直线迭代状态结构体的指针。
connectivity
直线的邻接方式,4邻接或者8邻接。
left_to_right
标志值,指出扫描直线是从pt1和pt2外面最左边的点扫描到最右边的点(left_to_right≠0),还是按照指定的顺序,从pt1到pt2(left_to_right=0)。
函数cvInitLineIterator初始化直线迭代器并返回两个端点间点的数目。两个端点都必须在图像内部。在迭代器初始化以后,所有的在连接两个终点的栅栏线上的点,可以通过访问CV_NEXT_LINE_POINT点的方式获得。在线上的这些点使用4-邻接或者8-邻接的Bresenham算法计算得到。
例:使用直线迭代来计算沿着彩色线上的点的像素值。
CvScalar sum_line_pixels( IplImage* image, CvPoint pt1, CvPoint pt2 )
{
CvLineIterator iterator;
int blue_sum = 0, green_sum = 0, red_sum = 0;
int count = cvInitLineIterator( image, pt1, pt2, &iterator, 8, 0 );
for( int i = 0; i < count; i++ ){
blue_sum += iterator.ptr[0];
green_sum += iterator.ptr[1];
red_sum += iterator.ptr[2];
CV_NEXT_LINE_POINT(iterator);
/* print the pixel coordinates: demonstrates how to calculate the coordinates */
{
int offset, x, y;
/* assume that ROI is not set, otherwise need to take it into account. */
offset = iterator.ptr - (uchar*)(image->imageData);
y = offset/image->widthStep;
x = (offset - y*image->widthStep)/(3*sizeof(uchar) /* size of pixel */);
printf("(%d,%d)\n", x, y );
}
}
return cvScalar( blue_sum, green_sum, red_sum );
}
ClipLine
剪切图像矩形区域内部的直线。
int cvClipLine( CvSize img_size, CvPoint* pt1, CvPoint* pt2 );
img_size
图像的大小。
pt1
线段的第一个端点,会被函数修改。
pt2
线段的第二个端点,会被函数修改。
函数cvClipLine计算线段完全在图像中的一部分。如果线段完全在图像中,返回0,否则返回1。
Ellipse2Poly
用折线逼近椭圆弧
int cvEllipse2Poly( CvPoint center, CvSize axes,
int angle, int arc_start,
int arc_end, CvPoint* pts, int delta );
center
弧线的中心。
axes
弧线的Half-sizes。参见下图。
angle
椭圆的旋转角度(Rotation angle),参见下图。
start_angle
椭圆的Starting angle,参见下图。
end_angle
椭圆的Ending angle,参见下图。
pts
坐标点矩阵数组,由本函数填充。
delta
与下一条折线定点的夹角,近似精度。故,得到的点数最大为ceil((end_angle - start_angle)/delta) + 1。
函数cvEllipse2Poly计算给定的椭圆弧的逼近折线的顶点,被cvEllipse使用。
文件存储
CvFileStorage
文件存储结构
typedef struct CvFileStorage
{
... // hidden fields
} CvFileStorage;
构造函数 CvFileStorage 是将磁盘上存储的文件关联起来的“黑匣子” 。在下列函数描述中利用CvFileStorage 作为输入,允许存储或载入各种格式数据组成的层次集合,这些数据由标量值(scalar ),或者CXCore 对象(例如 矩阵,序列,图表 ) 和用户自定义对象。
CXCore 能将数据读入或写入 XML (http://www.w3c.org/XML) or YAML (http://www.yaml.org) 格式. 下面这个例子是利用CXCore函数将3×3单位浮点矩阵存入XML 和 YAML文档。
XML:
<?xml version="1.0">
<opencv_storage>
<A type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>f</dt>
<data>1. 0. 0. 0. 1. 0. 0. 0. 1.</data>
</A>
</opencv_storage>
YAML:
%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的扩展名。
CvFileNode
文件存储器节点
/* 文件节点类型 */
#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;
/*所有已读存储在文件元素的关键字被存储在hash表中,这样可以加速查找操作 */
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 是一个隐藏的构造函数)。图形中的数据可以用cvGetFileNodeByName函数得到,函数返回指向图表文件节点的指针。
一个用户对象是一个标准的类型实例,例如CvMat, CvSeq等,或者任何一个已注册的类型使用cvRegisterTypeInfo。这样的对象最初在文件中表现为一种层级关系,(像表现XML 和 YAM示例文件一样) 。在文件存储器打开并分析之后。当用户调用cvRead或cvReadByName函数时 那么对象将请求被解析 (按照原来的存储方式)。
CvAttrList
属性列表
typedef struct CvAttrList
{
const char** attr; /* NULL-指向数组对(attribute_name,attribute_value) 的空指针 */
struct CvAttrList* next; /* 指向下一个属性块的指针 */
}
CvAttrList;
/* 初始化构造函数CvAttrList */
inline CvAttrList cvAttrList( const char** attr=NULL, CvAttrList* next=NULL );
/* 返回值为属性值,找不到适合的属性则返回值为0(NULL)*/
const char* cvAttrValue( const CvAttrList* attr, const char* attr_name );
在当前版本的属性列表用来传递额外的参数,在使用cvWrite写入自定义数据对象时。除了对象类型说明(type_id 属性)以外,它不支持 XML 在标签内的属性(注:例如<A name="test"></A>不支持)。
OpenFileStorage
打开文件存储器读/写数据。
CvFileStorage* cvOpenFileStorage( const char* filename, CvMemStorage* memstorage, int flags );
filename
内存中的相关文件的文件名。
memstorage
内存中通常存储临时数据和动态结构,例如 CvSeq 和 CvGraph。如果memstorage 为空,将建立和使用一个暂存器。
flags
读/写选择器。
CV_STORAGE_READ - 内存处于读状态。
CV_STORAGE_WRITE - 内存处于写状态。
函数cvOpenFileStorage打开文件存储器读写数据,之后建立文件或继续使用现有的文件。文件扩展名决定读文件的类型 : .xml 是 XML的扩展名, .yml 或 .yaml 是 YAML的扩展名。该函数的返回指针指向CvFileStorage结构。
ReleaseFileStorage
释放文件存储单元
void cvReleaseFileStorage( CvFileStorage** fs );
fs
双指针指向被关闭的文件存储器。
函数cvReleaseFileStorage 关闭一个相关的文件存储器并释放所有的临时内存。只有在内存的I/O操作完成后才能关闭文件存储器。
写数据
StartWriteStruct
向文件存储器中写数据
void cvStartWriteStruct( CvFileStorage* fs, const char* name,
int struct_flags, const char* type_name=NULL,
CvAttrList attributes=cvAttrList());
fs
初始化文件存储器。
name
被写入的数据结构的名称。在存储器被读取时可以通过名称访问数据结构。
struct_flags
有下列两个值:
CV_NODE_SEQ - 被写入的数据结构为序列结构。这样的数据没有名称。
CV_NODE_MAP - 被写入的数据结构为图表结构。这样的数据含有名称。
这两个标志符必须被指定一个
CV_NODE_FLOW - 这个可选择标识符只能作用于YAML流。被写入的数据结构被看做一个数据流(不是数据块),它更加紧凑,当结构或数组里的数据是标量时,推荐用这个标志。
type_name
可选参数 - 对象类型名称。如果是XML用打开标识符type_id 属性写入。如果是YAML 用冒号后面的数据结构名写入,:: 基本上它是伴随用户对象出现的。当读存储器时,编码类型名通常决定对象类型(见Cvtypeinfo和cvfindtypeinfo) 。
attributes
这个参数当前版本没有使用。
函数 cvStartWriteStruct 开始写复合的数据结构(数据集合)包括序列或图表, 在结构体中所有的字段(可以是标量和新的结构)被写入后, 需要调用 cvEndWriteStruct . 该函数能够合并一些对象或写入一些用户对象(参考 CvTypeInfo )。
EndWriteStruct
结束数据结构的写操作
void cvEndWriteStruct( CvFileStorage* fs );
fs
初始化文件存储器。
函数cvEndWriteStruct 结束普通的写数据操作。
WriteInt
写入一个整型值
void cvWriteInt( CvFileStorage* fs, const char* name, int value );
fs
初始的文件存储器。
name
写入值的名称。如果母结构是一个序列,把name的值置为NULL。
value
写入的整型值。
函数 cvWriteInt 将一个单独的整型值(有符号的或无符号的)写入文件存储器。
WriteReal
写入一个浮点数
void cvWriteReal( CvFileStorage* fs, const char* name, double value );
fs
文件存储器。
name
写入值的名称。如果父结构是一个序列,则name的值应为NULL。
value
写入的浮点数。
函数 cvWriteReal 将一个单精度浮点数(有符号的或无符号的)写入文件存储器。一些特殊的值以特殊的编码表示:
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 );
}
WriteString
写入文本字符串
void cvWriteString( CvFileStorage* fs, const char* name,
const char* str, int quote=0 );
fs
文件存储器。
name
写入字符串的名称。如果父结构是一个序列,name的值应为NULL。
str
写入的文本字符串。
quote
如果不为0,不管是否需要引号,字符串都将被写入引号 。如果标识符为0。只有在需要的情况下写入引号(例如字符串的首位是数字或者空格时候就需要两边加上引号)。
函数 cvWriteString将文本字符串写入文件存储器。
WriteComment
写入注释
void cvWriteComment( CvFileStorage* fs, const char* comment, int eol_comment );
fs
文件存储器。
comment
写入的注释,注释可以是单行的或者多行的。
eol_comment
如果不为0,函数将注释加到当前行的后面。 如果为0,并且是多行注释或者当前行放不下,那么注释将从新的一行开始 。
函数 cvWriteComment将注释写入文件存储器。读内存时注释将被跳过,它只能被用于调试和查看描述。
StartNextStream
打开下一个数据流
void cvStartNextStream( CvFileStorage* fs );
fs
初始化文件存储器。
函数 cvStartNextStream 从文件存储器中打开下一个数据流。 YAML 和 XML 都支持多数据流。这对连接多个文件和恢复写入的程序很有用。
Write
写入用户对象
void cvWrite( CvFileStorage* fs, const char* name,
const void* ptr, CvAttrList attributes=cvAttrList() );
fs
文件存储器。
name
写入对象的名称。如果父结构是一个序列,name的值为NULL。
ptr
定义指针指向对象。
attributes
定义对象的属性,每种类型都有特别的指定(见讨论)。
函数 cvWrite将对象写入文件存储器。首先,使用cvTypeOf 查找恰当的类型信息。其次写入指定的方法类型信息。
属性被用于定制写入程序。下面的属性支持标准类型 (所有的*dt 属性在cvWriteRawData中都有相同的格式):
CvSeq
· 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;
}
WriteRawData
写入基本数据数组
void cvWriteRawData( CvFileStorage* fs, const void* src,
int len, const char* dt );
fs
文件存储器。
src
指针指向输入数组。
len
写入数组的长度。
dt
下面是每一个数组元素说明的格式: ([count]{'u'|'c'|'w'|'s'|'i'|'f'|'d'})..., 这些特性与C语言的类型相似 :
· 'u' - 8位无符号数。
· 'c' - 8位符号数。
· 'w' - 16位无符号数。
· 's' - 16位符号数。
· 'i' - 32位符号数。
· 'f' - 单精度浮点数。
· 'd' - 双精度浮点数。
· 'r' - 指针。输入的带符号的低32位整数。 这个类型常被用来存储结构体之间的链接。
count 是可选的,是当前类型的计数器。 例如, dt='2if' 是指任意的一个数组元素的结构是:2个字节整形数,后面跟一个单精度浮点数。上面的说明与‘iif', '2i1f' 等相同。另外一个例子:dt='u'是指 一个由类型组成的数组, dt='2d'是指由两个双精度浮点数构成的数组。
函数 cvWriteRawData 将数组写入文件存储器,数组由单独的数值构成。这个函数也可以用循环调用 cvWriteInt 和 cvWriteReal 替换,但是一个单独的函数更加有效。需要说明的是,那是因为元素没有名字,把它们写入序列(无名字)比写入图表(有名字关联)速度会快。
WriteFileNode
将文件节点写入另一个文件存储器
void cvWriteFileNode( CvFileStorage* fs, const char* new_node_name,
const CvFileNode* node, int embed );
fs
文件存储器
new_file_node
在目的文件存储器中设置新的文件节点名。保持现有的文件节点名,使用cvGetFileNodeName(节点).
node
被写入的节点。
embed
如果被写入的节点是个集合并且embed不为0,不建立额外的层次结构。 否则所有的节点元素被写入新建的文件节点上。不过需要确定一点的是,图表元素只被写入图表,序列元素只被写入序列
函数 cvWriteFileNode将一个文件节点的拷贝写入文件存储器可能应用范围是: 将几个文件存储器合而为一。在XML 和YAML 之间变换格式等。
读取数据
从文件存储器中得到数据有两种步骤:首先查找文件节点包括哪些被请求的数据;然后利用手动或者使用自定义read 方法取得数据。
GetRootFileNode
从文件存储器中得到一个高层节点
CvFileNode* cvGetRootFileNode( const CvFileStorage* fs, int stream_index=0 );
fs
初始化文件存储器。
stream_index
从零开始计数的基索引。参考 cvStartNextStream. 在通常情况下,文件中只有一个流,但是可以拥有多个。
函数 cvGetRootFileNode 返回一个高层文件节点。高层节点没有名称,它们和流相对应,接连存入文件存储器。如果超出索引范围, 函数返回NULL指针, 所以要得到所有高层节点需要反复调用函数stream_index=0,1,...,直到返回NULL指针。这个函数是在文件存储器中递归寻找的基础方法。
GetFileNodeByName
在图表或者文件存储器中查找节点
CvFileNode* cvGetFileNodeByName( const CvFileStorage* fs,
const CvFileNode* map,
const char* name );
fs
初始化文件存储器。
map
设置父图表。如果为NULL,函数 在所有的高层节点(流)中检索, 从第一个开始。
name
设置文件节点名。
函数 cvGetFileNodeByName 文件节点通过name 查找文件节点 该节点在图表中被查找,或者如果指针为NULL,那么在内存中的高层文件节点中查找。 在图表中或者在序列调用cvGetSeqElem中使用到这个函数,这样可能遍历整个文件存储器。 为了加速确定某个关键字的多重查询(例如 结构数组 ),可以在cvGetHashedKey 和cvGetFileNode之中用到一个。
GetHashedKey
返回一个指向已有名称的唯一指针
CvStringHashNode* cvGetHashedKey( CvFileStorage* fs, const char* name,
int len=-1, int create_missing=0 );
fs
初始化文件存储器。
name
设置文字节点名。
len
名称的长度(已知),如果值为-1 长度会被计算出来 。
create_missing
标识符说明 ,是否应该将一个缺省节点的值加入哈希表。
函数 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.yaml", 0, CV_STORAGE_READ );
CvStringHashNode* x_key = cvGetHashedKey( fs, "x", -1, 1 );
CvStringHashNode* y_key = cvGetHashedKey( 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;
}
请注意,无论使用那一种方法访问图表 ,都比使用序列慢,例如上面的例子,如果把数据作为整数对放在在单一数字序列中,效率会更高。
GetFileNode
在图表或者文件存储器中查找节点
CvFileNode* cvGetFileNode( CvFileStorage* fs, CvFileNode* map,
const CvStringHashNode* key, int create_missing=0 );
fs
初始化文件存储器。
map
设置母图表。如果为NULL,函数 在所有的高层节点(流)中检索,如果图表与值都为 NULLs,函数返回到根节点-图表包含高层节点
key
指向节点名的特殊节点 , 从cvGetHashedKey中得到 。
create_missing
标识符说明 ,是否应该将一个缺省节点加入图表。
函数 cvGetFileNode 查找一个文件节点。函数能够插入一个新的节点,当它不在图表中时
GetFileNodeName
返回文件节点名
const char* cvGetFileNodeName( const CvFileNode* node );
node
初始化文件节点。
函数 cvGetFileNodeName 返回文件节点名或返回NULL(如果文件节点没有名称或者node为NULL 。
ReadInt
从文件节点中得到整形值
int cvReadInt( const CvFileNode* node, int default_value=0 );
node
初始化文件节点
default_value
如果node为NULL,返回一个值。
函数 cvReadInt 从文件节点中返回整数。如果文件节点为NULL, default_value 被返回 。另外如果文件节点有类型 CV_NODE_INT, 则 node->data.i 被返回 。如果文件节点有类型 CV_NODE_REAL, 则 node->data.f 被修改成整数后返回。其他的情况是则结果不确定。
ReadIntByName
查找文件节点返回它的值
int cvReadIntByName( const CvFileStorage* fs, const CvFileNode* map,
const char* name, int default_value=0 );
fs
初始化文件存储器。
map
设置父图表。如果为NULL,函数 在所有的高层节点(流)中检索。
name
设置节点名。
default_value
如果文件节点为NULL,返回一个值。
函数 cvReadIntByName是 cvGetFileNodeByName 和 cvReadInt的简单重叠.
ReadReal
从文件节点中得到浮点形值
double cvReadReal( const CvFileNode* node, double default_value=0. );
node
初始化文件节点。
default_value
如果node为NULL,返回一个值。
函数cvReadReal 从文件节点中返回浮点形值。如果文件节点为NULL, default_value 被返回(这样就不用检查cvGetFileNode 返回的指针是否为空了) 。另外如果文件节点有类型 CV_NODE_REAL , 则node->data.f 被返回 。如果文件节点有类型 CV_NODE_INT , 则 node->data.i 被修改成浮点数后返回。另外一种情况是,结果不确定。 .
ReadRealByName
查找文件节点返回它的浮点形值
double cvReadRealByName( const CvFileStorage* fs, const CvFileNode* map,
const char* name, double default_value=0. );
fs
初始化文件存储器。
map
设置父图表。如果为NULL,函数 在所有的高层节点(流)中检索。
name
设置节点名。
default_value
如果node为NULL,返回一个值。
函数 cvReadRealByName 是 cvGetFileNodeByName 和cvReadReal 的简单重叠。
ReadString
从文件节点中得到字符串文本
const char* cvReadString( const CvFileNode* node, const char* default_value=NULL );
node
初始化文件节点。
default_value
如果node为NULL,返回一个值。
函数cvReadString 从文件节点中返回字符串文本。如果文件节点为NULL, default_value 被返回 。另外如果文件节点有类型CV_NODE_STR, 则data.str.ptr 被返回 。 另外一种情况是,结果不确定。
ReadStringByName
查找文件节点返回它的字符串文本
const char* cvReadStringByName( const CvFileStorage* fs, const CvFileNode* map,
const char* name, const char* default_value=NULL );
fs
初始化文件存储器。
map
设置母图表。如果为NULL,函数 在所有的高层节点(流)中检索。
name
设置节点名。
default_value
如果文件节点为NULL,返回一个值。
函数 cvReadStringByName是 cvGetFileNodeByName 和cvReadString 的简单重叠。
Read
解释对象并返回指向它的指针
void* cvRead( CvFileStorage* fs, CvFileNode* node,
CvAttrList* attributes=NULL );
fs
初始化文件存储器。
node
设置对象根节点。
attributes
不被使用的参数.
函数 cvRead 解释用户对象 (在文件存储器子树中建立新的对象)并返回。对象被解释 ,必须按原有的支持读方法的类型 (参考 CvTypeInfo).用类型名决定对象,并在文件中被解释。如果对象是动态结构,它将在内存中创建传递给cvOpenFileStorage或者使NULL指针被建立在临时性内存中。当cvReleaseFileStorage 被调用时释放内存。 如果对象不是动态结构 ,将在堆中被建立,释放它的内存需要用专用函数或通用函数cvRelease。
ReadByName
查找对象并解释
void* cvReadByName( CvFileStorage* fs, const CvFileNode* map,
const char* name, CvAttrList* attributes=NULL );
fs
初始化文件存储器。
map
设置父节点。如果它为NULL,函数从高层节点中查找。
name
设置节点名称。
attributes
不被使用的参数.
函数 cvReadByName 是由cvGetFileNodeByName 和 cvRead叠合的。 .
ReadRawData
读重数
void cvReadRawData( const CvFileStorage* fs, const CvFileNode* src,
void* dst, const char* dt );
fs
初始化文件存储器。
src
设置文件节点(有序的)来读数。
dst
设置指向目的数组的指针。
dt
数组元素的说明。格式参考 cvWriteRawData。
函数 cvReadRawData从有序的文件节点中读取标量元素。
StartReadRawData
初始化文件节点读取器
void cvStartReadRawData( const CvFileStorage* fs, const CvFileNode* src,
CvSeqReader* reader );
fs
初始化文件存储器。
src
设置文件节点(序列)来读数。
reader
设置顺序读取指针。
函数 cvStartReadRawData 初始化序列读取器从文件节点中读取数据。初始化的首部通过传给cvReadRawDataSlice使用 。
ReadRawDataSlice
初始化文件节点序列
void cvReadRawDataSlice( const CvFileStorage* fs, CvSeqReader* reader,
int count, void* dst, const char* dt );
fs
文件存储器。
reader
设置序列读取器 . 用 cvStartReadRawData.初始化。
count
被读取元素的数量。
dst
指向目的数组的指针。
dt
数组元素的说明。格式参考 cvWriteRawData。
函数 cvReadRawDataSlice 从文件节点读一个或多个元素,组成一个序列用于指定数组。读入元素的总数由其他数组的元素总和乘以每个数组元素数目。例如 如果 dt='2if', 函数将读是total*3数量的序列元素。对于任何数组,可以使用cvSetSeqReaderPos*定位,跳过某些位置或者重复读取。
运行时类型信息和通用函数
CvTypeInfo
类型信息
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)的大小 sizeof(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定义一个新的类型 ,并将类型信息结构加到文件列表的开始端, 它可以从标准类型中建立专门的类型,重载基本的方法。
RegisterType
定义新类型
void cvRegisterType( const CvTypeInfo* info );
info
类型信息结构。
函数 cvRegisterType 定义一个新类型,可以通过信息来描述它。这个函数在内存创建了一个copy,所以在用完以后,应该删除它。
UnregisterType
删除定义的类型
void cvUnregisterType( const char* type_name );
type_name
被删除的类型的名称。
函数 cvUnregisterType通过指定的名称删除已定义的类型。如果不知道类型名,可以用cvTypeOf或者连续扫描类型列表,从cvFirstType开始,然后调用 cvUnregisterType(info->type_name)。
FirstType
返回类型列表的首位。
CvTypeInfo* cvFirstType( void );
函数 cvFirstType 返回类型列表中的第一个类型。可以利用CvTypeInfo 的prev next来实现遍历。
FindType
通过类型名查找类型
CvTypeInfo* cvFindType( const char* type_name );
type_name
类型名
函数 cvFindType通过类型名查找指定的类型。如果找不到返回值为NULL。
TypeOf
返回对象的类型
CvTypeInfo* cvTypeOf( const void* struct_ptr );
struct_ptr
定义对象指针。
函数 cvTypeOf 查找指定对象的类型。它反复扫描类型列表,调用每一个类型信息结构中的函数和方法与对象做比较,直到它们中的一个的返回值不为0或者所有的类型都被访问。
Release
删除对象
void cvRelease( void** struct_ptr );
struct_ptr
定义指向对象的双指针。
函数 cvRelease 查找指定对象的类型,然后调用release。
Clone
克隆一个对象
void* cvClone( const void* struct_ptr );
struct_ptr
定义被克隆的对象
函数 cvClone 查找指定对象的类型,然后调用 clone。
Save
存储对象到文件中
void cvSave( const char* filename, const void* struct_ptr,
const char* name=NULL,
const char* comment=NULL,
CvAttrList attributes=cvAttrList());
filename
初始化文件名。
struct_ptr
指定要存储的对象。
name
可选择的对象名。如果为 NULL, 对象名将从filename中列出。
comment
可选注释。加在文件的开始处。
attributes
可选属性。 传递给cvWrite。
函数 cvSave存储对象到文件。它给cvWrite提供一个简单的接口。
Load
从文件中打开对象。
void* cvLoad( const char* filename, CvMemStorage* memstorage=NULL,
const char* name=NULL, const char** real_name=NULL );
filename
初始化文件名
memstorage
动态结构的内存,例如CvSeq或CvGraph。不能作用于矩阵或图像。:
name
可选对象名。如果为 NULL,内存中的第一个高层对象被打开。
real_name
可选输出参数。它包括已打开的对象的名称 (如果 name=NULL时有效)。
函数 cvLoad 从文件中打开对象。它给cvRead提供一个简单的接口.对象被打开之后,文件存储器被关闭,所有的临时缓冲区被删除。因而,为了能打开一个动态结构,如序列,轮廓或图像,你应该为该函数传递一个有效的目标存储器。
Cxcore其它混合函数
Wikipedia,*的百科全书
CheckArr
检查输入数组的每一个元素是否是合法值
int cvCheckArr( const CvArr* arr, int flags=0,
double min_val=0, double max_val=0);
#define cvCheckArray cvCheckArr
arr
待检查数组
flags
操作标志, 0 或者下面值的组合:
CV_CHECK_RANGE - 如果设置这个标志, 函数检查数组的每一个值是否在范围 [minVal,maxVal) 以内;否则,它只检查每一个元素是否是 NaN 或者 ±Inf。
CV_CHECK_QUIET - 设置这个标志后, 如果一个元素是非法值的或者越界时,函数不会产生错误。
min_val
有效值范围的闭下边界。只有当 CV_CHECK_RANGE 被设置的时候它才有作用。
max_val
有效值范围的开上边界。只有当 CV_CHECK_RANGE 被设置的时候它才有作用。
函数 cvCheckArr 检查每一个数组元素是否是 NaN 或者 ±Inf。如果 CV_CHECK_RANGE 被设定, 它将检查每一个元素是否大于等于 minVal 并且小于maxVal。如果检查成功函数返回非零值,例如,所有元素都是合法的并且在范围内,如果检查失败则返回 0 。 在后一种情况下如果 CV_CHECK_QUIET 标志没有被设定,函数将报出运行错误。
KMeans2
按照给定的类别数目对样本集合进行聚类
void cvKMeans2( const CvArr* samples, int cluster_count,
CvArr* labels, CvTermCriteria termcrit );
samples
输入样本的浮点矩阵,每个样本一行。
cluster_count
所给定的聚类数目
labels
输出整数向量:每个样本对应的类别标识
termcrit
指定聚类的最大迭代次数和/或精度(两次迭代引起的聚类中心的移动距离)
函数 cvKMeans2 执行 k-means 算法 搜索 cluster_count 个类别的中心并对样本进行分类,输出 labels(i) 为样本 i 的类别标识。
例子. 用 k-means 对高斯分布的随机样本进行聚类
#include "cxcore.h"
#include "highgui.h"
int 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;
}
}
SeqPartition
拆分序列为等效的类
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
存储序列的等效类的存储器,如果为空, 函数用 seq->storage 存储输出标签。
labels
输出参数。指向序列指针的指针,这个序列存储以0为开始的输入序列元素的标签。
is_equal
比较函数指针。如果两个特殊元素是来自同一个类,那这个比较函数返回非零值,否则返回 0 。划分算法用比较函数的传递闭包得到等价类。
userdata
直接传递给 is_equal 函数的指针。
函数 cvSeqPartition 执行二次方程算法为拆分集合为一个或者更多的等效类。函数返回等效类的数目。
例子:拆分二维点集。
#include "cxcore.h"
#include "highgui.h"
#include <stdio.h>
CvSeq* 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 );
CvScalar color = colors[*(int*)cvGetSeqElem( labels, i )];
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
错误处理器被调用以后程序不会被终止。栈被清空 (它用 C++ 异常处理机制完成写/输出--w/o)。当调用 CxCore 的函数 cvGetErrStatus 起作用以后用户可以检查错误代码。
Silent
和 Parent 模式相似, 但是没有错误处理器被调用。
事实上, 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"
#include <stdio.h>
void 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;
}
GetErrStatus
返回当前错误状态
int cvGetErrStatus( void );
函数 cvGetErrStatus 返回当前错误状态 - 这个状态是被最近调用的 cvSetErrStatus 设置的。 注意, 在 Leaf 模式下错误一旦发生程序立即被终止 ,因此对于总是需要调用函数仍然获得控制的应用,可以调用 cvSetErrMode 函数将错误模式设置为 Parent 或 Silent 。
SetErrStatus
设置错误状态
void cvSetErrStatus( int status );
status
错误状态
函数 cvSetErrStatus 设置错误状态为指定的值。大多数情况下, 该函数 被用来重设错误状态(设置为 CV_StsOk) 以从错误中恢复。在其他情况下调用 cvError 或 CV_ERROR 更自然一些。
GetErrMode
返回当前错误模式
int cvGetErrMode( void );
函数 cvGetErrMode 返回当前错误模式 - 这个值是被最近一次 cvSetErrMode 函数调用所设定的。
SetErrMode
设置当前错误模式
#define CV_ErrModeLeaf 0
#define CV_ErrModeParent 1
#define CV_ErrModeSilent 2
int cvSetErrMode( int mode );
mode
错误模式
函数 cvSetErrMode 设置指定的错误模式。关于不同的错误模式的讨论参考本节开始.
Error
产生一个错误
int cvError( int status, const char* func_name,
const char* err_msg, const char* file_name, int line );
status
错误状态
func_name
产生错误的函数名
err_msg
关于错误的额外诊断信息
file_name
产生错误的文件名
line
产生错误的行号
函数 cvError 设置错误状态为指定的值(通过 cvSetErrStatus) ,如果错误模式不是 Silent, 调用错误处理器。
ErrorStr
返回错误状态编码的原文描述
const char* cvErrorStr( int status );
status
错误状态
函数 cvErrorStr 返回指定错误状态编码的原文描述。如果是未知的错误状态该函数返回空(NULL)指针。
RedirectError
设置一个新的错误处理器
typedef int (CV_CDECL *CvErrorCallback)( int status, const char* func_name,
const char* err_msg, const char* file_name, int line, void* userdata );
CvErrorCallback cvRedirectError( CvErrorCallback error_handler,
void* userdata=NULL, void** prev_userdata=NULL );
error_handler
新的错误处理器
userdata
传给错误处理器的任意透明指针
prev_userdata
指向前面分配给用户数据的指针的指针
函数 cvRedirectError 在标准错误处理器或者有确定借口的自定义错误处理器中选择一个新的错误处理器。错误处理器和 cvError 函数有相同的参数。 如果错误处理器返回非零的值, 程序终止, 否则, 程序继续运行。错误处理器通过 cvGetErrMode 检查当前错误模式而作出决定。
cvNulDevReport cvStdErrReport cvGuiBoxReport
提供标准错误操作
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 );
status
错误状态
func_name
产生错误的函数名
err_msg
关于错误的额外诊断信息
file_name
产生错误的文件名
line
产生错误的行号
userdata
指向用户数据的指针,被标准错误操作忽略。
函数 cvNullDevReport, cvStdErrReport, cvGuiBoxReport 提供标准错误操作。cvGuiBoxReport 是 Win32 系统缺省的错误处理器, cvStdErrReport - 其他系统. cvGuiBoxReport 弹出错误描述的消息框并提供几个选择。下面是一个消息框的例子,如果和例子中的错误描述相同,它和上面的例子代码可能是兼容的。
错误消息对话框
如果错误处理器是 cvStdErrReport, 上面的消息将被打印到标准错误输出,程序将要终止和继续依赖于当前错误模式。
错误消息打印到标准错误输出 (在 Leaf 模式)
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...
系统函数
Alloc
分配内存缓冲区
void* cvAlloc( size_t size );
size
以字节为单位的缓冲区大小
函数 cvAlloc 分配字节缓冲区大小并返回分配的缓冲区的指针。如果错误处理函数产生了一个错误报告则返回一个空(NULL)指针。 缺省地 cvAlloc 调用 icvAlloc 而 icvAlloc 调用 malloc ,然而用 cvSetMemoryManager 调用用户自定义的内存分配和释放函数也是可能的。
Free
释放内存缓冲区
void cvFree( void** ptr );
buffer
指向被释放的缓冲区的双重指针
函数 cvFree 释放被 cvAlloc 分配的缓冲区。在退出的时候它清除缓冲区指针,这就是为什么要使用双重指针的原因 。 如果 *buffer 已经是空(NULL), 函数什么也不做。
GetTickCount
Returns number of tics
int64 cvGetTickCount( void );
函数 cvGetTickCount 返回从依赖于平台的事件(从启动开始 CPU 的ticks 数目, 从1970年开始的微秒数目等等)开始的 tics 的数目 。 该函数对于精确测量函数/用户代码的执行时间是很有用的。要转化 tics 的数目为时间单位,使用函数 cvGetTickFrequency 。
GetTickFrequency
返回每个微秒的 tics 的数目
double cvGetTickFrequency( void );
函数 cvGetTickFrequency 返回每个微秒的 tics 的数目。 因此, cvGetTickCount() 和 cvGetTickFrequency() 将给出从依赖于平台的事件开始的 tics 的数目 。
RegisterModule
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 );
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 是怎样连接到模块的。
GetModuleInfo
检索注册模块和插件的信息
void cvGetModuleInfo( const char* module_name,
const char** version,
const char** loaded_addon_plugins );
module_name
模块名, 或者 NULL ,则代表所有的模块
version
输出参数,模块的信息,包括版本信息
loaded_addon_plugins
优化插件的名字和版本列表,这里 CXCORE 可以被找到和载入
函数 cvGetModuleInfo 返回一个或者所有注册模块的信息。返回信息被存储到库当中,因此,用户不用释放或者修改返回的文本字符。
UseOptimized
在优化/不优化两个模式之间切换
int cvUseOptimized( int on_off );
on_off
优化(<>0) 或者 不优化 (0).
函数 cvUseOptimized 在两个模式之间切换,这里只有纯 C 才从 cxcore, OpenCV 等执行。如果可用 IPP 和 MKL 函数也可使用。 当 cvUseOptimized(0) 被调用, 所有的优化库都不被载入。该函数在调试模式下是很有用的, IPP&MKL 不工作, 在线跨速比较等。它返回载入的优化函数的数目。注意,缺省地优化插件是被载入的,因此在程序开始调用 cvUseOptimized(1) 是没有必要的(事实上, 它只会增加启动时间)
SetMemoryManager
分配自定义/缺省内存管理函数
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 );
alloc_func
分配函数; 除了 userdata 可能用来确定上下文关系外,接口和 malloc 相似
free_func
释放函数; 接口和 free 相似
userdata
透明的传给自定义函数的用户数据
函数 cvSetMemoryManager 设置将被 cvAlloc,cvFree 和高级函数 (例如. cvCreateImage) 调用的用户自定义内存管理函数(代替 malloc 和 free)。 注意, 当用 cvAlloc 分配数据的时候该函数被调用。 当然, 为了避免无限递归调用, 它不允许从自定义分配/释放函数调用 cvAlloc 和 cvFree 。
如果 alloc_func 和 free_func 指针是 NULL, 恢复缺省的内存管理函数。
SetIPLAllocators
切换图像 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 )
create_header
指向 iplCreateImageHeader 的指针
allocate_data
指向 iplAllocateImage 的指针
deallocate
指向 iplDeallocate 的指针
create_roi
指向 iplCreateROI 的指针
clone_image
指向 iplCloneImage 的指针
函数 cvSetIPLAllocators 使用 CXCORE 来进行图像 IPL 函数的 分配/释放 操作。 为了方便, 这里提供了环绕宏 CV_TURN_ON_IPL_COMPATIBILITY。 当 IPL 和 CXCORE/OpenCV 同时使用以及调用 iplCreateImageHeader 等情况该函数很有用。如果 IPL 仅仅是被调用来进行数据处理,该函数就必要了,因为所有的分配/释放都由 CXCORE 来完成, 或者所有的分配/释放都由 IPL 和一些 OpenCV 函数来处理数据。