OpenCV学习笔记之CXCORE篇

为使得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 函数来处理数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值