
OpenCV核心库的数据结构
一、基本数据结构
CvPoint、CvPoint2D32f、CvPoint3D32f、
CvSize、CvSize2D32f、CvSize3D32f、CvRect
CvScalar(N元组)
CvMat、CvMatND、CvSpareseMat、CvTermCriteria、CvArr、IplImage
例题4-1:基本数据类型使用
说明:
cvFlip:垂直,水平或即垂直又水平翻转二维数组
void cvFlip( const CvArr* src, CvArr* dst=NULL, int flip_mode=0);
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
二、数组有关的结构
1、数组定义
IplImage和cvMat结构的基本使用方法
IplImage:图像结构
typedef struct _IplImage
{
int nSize; /* IplImage大小 */
int ID; /* 版本 (=0)*/
int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */
int alphaChannel; /* 被OpenCV忽略 */
int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S,
IPL_DEPTH_16U, IPL_DEPTH_16S, IPL_DEPTH_32S,
IPL_DEPTH_32F and IPL_DEPTH_F 可支持 */
char colorModel[4]; /* 被OpenCV忽略 */
char channelSeq[4]; /* 同上 */
int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道.
cvCreateImage只能创建交叉存取图像 */
int origin; /* 0 - 顶—左结构,
1 - 底—左结构 (Windows bitmaps 风格) */
int align; /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
int width; /* 图像宽像素数 */
int height; /* 图像高像素数*/
struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */
struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */
void *imageId; /* 同上*/
struct _IplTileInfo *tileInfo; /*同上*/
int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/
char *imageData; /* 指向排列的图像数据 */
int widthStep; /* 排列的图像行大小,以字节为单位 */
int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */
int BorderConst[4]; /* 同上 */
char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
}
IplImage;
IplImage结构来自于 Intel Image Processing Library(是其本身所具有的)。OpenCV 只支持其中的一个子集:
例题4-2:显示图像信息。
图像处理函数:
IplImage * cvCreateImage(CvSize size,int depth, int channels);
Void cvReleaseImage(IplImage ** image);
IplImage * cvCloneImage(const IplImage * image);
Void cvConvertImage(const CvArr * src,CvArr * dst, int flags=0);
Flages:0:没变化,1:垂直翻转,2交换红蓝信道)
Void cvSetImageCOI(IplImage * image, int coi);根据给定值coi设置感兴趣信道
Int cvGetImageCOI(const IplImage * image);返回感兴趣信道号
Void cvSetImageROI(IplImage * image,CvRect rect);基于给定的矩形设置ROI(Region Of Interest)感兴趣区域
Void cvResetImageROI(IplImage * image);释放图像的ROI
CvRect cvGetImageROI(const IplImage * image);返回图像ROI坐标
IplImage * cvGetImage(const CvArr * arr,IplImage * image_header);从不确定数组返回图像头。输入数组可以是CvMat* 或 IplImage*。
例4-3:IplImage相关函数及ROI设定与获取
2、CvMat矩阵
(1)CvMat相关函数
CvMat * cvCreateMat(int rows,int cols,int type);创建矩阵(行数、列数、元素类型)
元素类型type:通常以CV_<比特数>(S|U|F)C<通道数>的形式描述,例如:
CV_8UC1:表示一个8bit无符号单信道矩阵;
CV_8SC1:表示一个8bit有符号单信道矩阵;
CV_32SC:表示一个32bit有符号双信道矩阵;
CvMat * cvCreateMatHeader(int rows, int cols, int type);创建新的矩阵头,但并没有分配数据
CvMat * cvInitMatHeader(CvMat * mat, int rows, int cols, int type, void * data=NULL, int step=CV_AUTOSTEP);初始化矩阵头,data:指向分配给矩阵头的数据指针。
CvMat cvMat(int rows ,int cols, int type, void * data=NULL);用于替代函数cvInitMatHeader。
CvMat * cvCloneMat(const CvMat * mat);矩阵复制
Void cvReleaseMat(CvMat ** mat);删除矩阵
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
所有的矩阵应该有相同的数据类型和协调的矩阵大小。支持实数浮点矩阵或者复数浮点矩阵
例4:cvMat处理函数(创建矩阵、矩阵相乘)
(2)复制和添加相关函数
void cvCopy( const CvArr* src, CvArr* dst, const CvArr* mask=NULL );
如果输入输出数组中的一个是IplImage类型的话,其ROI和COI将被使用。
void cvSet( CvArr* arr, CvScalar value, const CvArr* mask=NULL );
如果数组 arr 是 IplImage 类型, 那么就会使用ROI,但COI不能设置。
void cvSetZero( CvArr* arr );
#define cvZero cvSetZero 清空数组
void cvSetIdentity( CvArr* mat, CvScalar value=cvRealScalar(1) ); 初始化带尺度的单位矩阵
void cvRange( CvArr* mat, double start, double end ); 用指定范围的数来填充矩阵
例题4-5 矩阵元素添加
(3)数组变换
CvMat* cvReshape( const CvArr* arr, CvMat* header, int new_cn, int new_rows=0 );
不拷贝数据修改矩阵/图像形状(通道数、行数)
void cvRepeat( const CvArr* src, CvArr* dst ); 用原数组管道式添充输出数组
dst(i,j)=src(i mod rows(src), j mod cols(src))
void cvFlip( const CvArr* src, CvArr* dst=NULL, int flip_mode=0);
垂直,水平或即垂直又水平翻转二维数组
void cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1,CvArr* dst2, CvArr* dst3 );
分割多通道数组成几个单通道数组或者从数组中提取一个通道
void cvMerge( const CvArr* src0, const CvArr* src1, const CvArr* src2, const CvArr* src3, CvArr* dst ); 从几个单通道数组组合成多通道数组或插入一个单通道数组
void cvRandShuffle( CvArr* mat, CvRNG* rng, double iter_factor=1.); 随机交换数组的元素
例题4-6 数组变换
(4)获取数组元素和数组子集
CvMat* cvGetSubRect( const CvArr* arr, CvMat* submat, CvRect rect );
返回输入的图像或矩阵的矩形数组子集的矩阵头
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 );
返回数组的一行或在一定跨度内的行
CvMat* cvGetCol( const CvArr* arr, CvMat* submat, int col );
CvMat* cvGetCols( const CvArr* arr, CvMat* submat, int start_col, int end_col );
返回数组的一列或一定跨度内的列
CvMat* cvGetDiag( const CvArr* arr, CvMat* submat, int diag=0 );
返回一个数组对角线(diag=0:主对角线,diag=-1主对角线上面的对角线,diag=1主对角线下面的对角线,以此类推)
CvSize cvGetSize( const CvArr* arr );
返回图像或矩阵的行数和列数,如果是图像就返回ROI的大小
int cvGetElemType( const CvArr* arr ); 返回数组元素类型
int cvGetDims( const CvArr* arr, int* sizes=NULL ); 返回数组维数大小
int cvGetDimSize( const CvArr* arr, int index ); 返回特殊维的大小
void cvClearND( CvArr* arr, int* idx ); 清除指定数组元素
宏 CV_MAT_ELEM(mat,elemtype,row,col) 获取cvMat元素
宏CV_IMAGE_ELEM(image,elemtype,row,col) 获取IplImage元素灰度值。例如获取RGB彩色图像image的[i,j]点的r,g,b三个分量,可采用:
Uchar * ptr=&CV_IMAGE_ELEM(image,uchar,i,j,3);
那么prt[0]、prt[1]、prt[2]就分别对应该点b,g,r三个分量。
例题4-7 获取数组子集函数使用
(5)数学函数
int cvRound( double value ); 取整
int cvFloor( double value ); 向下取整
int cvCeil( double value ); 向上取整
float cvSqrt( float value ); 计算平方根
float cvInvSqrt( float value ); 计算平方根的倒数
float cvCbrt( float value ); 计算立方根
float cvFastArctan( float y, float x ); 计算二维向量的角度
int cvIsNaN( double value ); 判断输入是否是一个数字
int cvIsInf( double value ); 判断输入是否是无穷大
void cvCartToPolar( const CvArr* x, const CvArr* y, CvArr* magnitude, CvArr* angle=NULL,
int angle_in_degrees=0 ); 计算二维向量的长度和/或者角度
void cvPolarToCart( const CvArr* magnitude, const CvArr* angle, CvArr* x, CvArr* y,
int angle_in_degrees=0 ); 计算极坐标形式的二维向量对应的直角坐标
void cvPow( const CvArr* src, CvArr* dst, double power ); 对数组内每个元素求幂
void cvExp( const CvArr* src, CvArr* dst ); 计算数组元素的指数幂
void cvLog( const CvArr* src, CvArr* dst ); 计算每个数组元素的绝对值的自然对数
(6)数组算术逻辑比较
void cvConvertScale( const CvArr* src, CvArr* dst, double scale=1, double shift=0 );
使用线性变换转换数组,即: dst(I)=src(I)*scale + (shift,shift,...),
void cvAdd( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
计算两个数组中每个元素的和,即:dst(I)=src1(I)+src2(I) if mask(I)!=0
void cvAddS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
计算数量和数组中之间的和,即:dst(I)=src1(I)+value if mask(I)!=0
void cvAddWeighted( const CvArr* src1, double alpha,const CvArr* src2, double beta,
double gamma, CvArr* dst );
计算两数组的加权值的和,即:dst(I)=src1(I)*alpha+src2(I)*beta+gamma
void cvSub( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
计算两个数组每个元素的差
void cvSubS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
计算数组和数量之间的差dst(I)=src(I) – value if mask(I)!=0
void cvSubRS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
计算数量和数组之间的差dst(I)=value-src(I) if mask(I)!=0
void cvMul( const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1 );
计算两个数组中每个元素的积,即:dst(I)=scale*src1(I)*src2(I)
void cvDiv( const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1 );
两个数组每个元素相除,即:dst(I)=scale*src1(I)/src2(I), if src1!=NULL
dst(I)=scale/src2(I),: if src1=NULL
void cvAnd( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
计算两个数组的每个元素的按位与
void cvAndS( const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL );
计算数组每个元素与数量之间的按位与
类似的还有cvOr、cvOrS、cvXor、cvXorS、cvNot
void cvCmp( const CvArr* src1, const CvArr* src2, CvArr* dst, int cmp_op );
比较两个数组元素P,即:dst(I)=src1(I) op src2(I), 这里 op 是 '=', '>', '>=', '<', '<=' or '!='.
CV_CMP_EQ、CV_CMP_GT、CV_CMP_GE、CV_CMP_LT、CV_CMP_LE、CV_CMP_NE
关系为真则设置dst(I)为 0xff (也就是所有的位都为 '1') 否则为0。
void cvCmpS( const CvArr* src, double value, CvArr* dst, int cmp_op );
比较数组的每个元素与数量的关系,即:dst(I)=src(I) op scalar
void cvInRange( const CvArr* src, const CvArr* lower, const CvArr* upper, CvArr* dst );
检查数组元素是否在两个数组之间,对于单通道:dst(I)=lower(I)0 <= src(I)0 < upper(I)0
对于双通道:dst(I)=lower(I)0 <= src(I)0 < upper(I)0 && dst(I)=lower(I)1 <= src(I)1 < upper(I)1
void cvInRangeS( const CvArr* src, CvScalar lower, CvScalar upper, CvArr* dst );
检查数组元素是否在两个数量之间
void cvMax( const CvArr* src1, const CvArr* src2, CvArr* dst );
查找两个数组中每个元素的较大值,即:dst(I)=max(src1(I), src2(I))
类似的还有cvMaxS、cvMin、cvMinS、cvAbsDiff(绝对值的差)、cvAbsDiffS
int cvCountNonZero( const CvArr* arr ); 计算非零数组元素的数目
CvScalar cvSum( const CvArr* arr ); 地为每一个通道计算数组元素的和
CvScalar cvAvg( const CvArr* arr, const CvArr* mask=NULL );为每一个通道计算元素平均值
void cvAvgSdv( const CvArr* arr, CvScalar* mean, CvScalar* std_dev,
const CvArr* mask=NULL );
为每一个通道计算数组元素的平均值和标准差
void cvMinMaxLoc( const CvArr* arr, double* min_val, double* max_val,
CvPoint* min_loc=NULL, CvPoint* max_loc=NULL, const CvArr* mask=NULL );
查找数组和子数组的全局最小值和最大值
double cvNorm( const CvArr* arr1, const CvArr* arr2=NULL, int norm_type=CV_L2,
const CvArr* mask=NULL );
计算数组的绝对范数, 绝对差分范数或者相对差分范数
(7)随机数
CvRNG cvRNG( int seed=-1 );
初始化随机数生成器并返回其状态。指向这个状态的指针可以传递给函数 cvRandInt,
cvRandReal 和 cvRandArr .
void cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, CvScalar param1, CvScalar param2 );
均匀分布或正态分布随机数填充数组
unsigned cvRandInt( CvRNG* rng ); 返回 32-bit 无符号整型
double cvRandReal( CvRNG* rng ); 返回浮点型随机数
例题4-8 数组统计及随机函数的使用
(8)线性代数
OpenCv还提供了线性代数相关函数求矩阵的行列式、矩阵的逆、矩阵转置以及矩阵乘法等。
(9)离散变换
OpenCV提供了cvDFT和cvDCT两个函数实现离散傅里叶变换、傅里叶逆变换和离散余弦变换、反余弦变换。
void cvDFT( const CvArr* src, CvArr* dst, int flags );
执行一维或者二维浮点数组的离散傅立叶正变换或者离散傅立叶逆变换,
flags变换标志, 下面的值的组合:
CV_DXT_FORWARD(正向不缩放)、CV_DXT_INVERSE(逆向不缩放)、
CV_DXT_SCALE(对结果进行缩放,常与逆向结合为CV_DXT_INV_SCALE)、
CV_DXT_ROWS(输入矩阵的每个的行进行整型或者逆向变换)
void cvDCT( const CvArr* src, CvArr* dst, int flags );
执行一维或者二维浮点数组的离散馀弦变换或者离散反馀弦变换
三、动态结构
在OpenCV中主要以内存存储器(CvMemStorage)为底层结构来实现序列(CvSeq)、集合(CvSet)、图(CvGraph)和树这些动态结构。
1、内存存储
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块剩馀的空字节数。
相关函数:cvCreateMemStorage、cvReleaseMemStorage、cvSaveMemStoragePos、cvRestoreMemStoragePos、cvClearStorage。
typedef struct CvMemBlock
{
struct CvMemBlock* prev;
struct CvMemBlock* next;
} CvMemBlock;
CvMemBlock 代表一个单独的内存存储块结构。
typedef struct CvMemStoragePos
{
CvMemBlock* top;
int free_space;
} CvMemStoragePos;
该结构保存栈顶的地址
CvMemStorage* cvCreateMemStorage( int block_size=0 );
创建内存块返回指向块首的指针
block_size存储块的大小以字节表示当前默认大小为k
void cvSaveMemStoragePos( const CvMemStorage* storage, CvMemStoragePos* pos );
将存储块的当前位置保存到参数 pos 中
void cvRestoreMemStoragePos( CvMemStorage* storage, CvMemStoragePos* pos );
通过参数 pos 恢复内存块的位置
void cvClearMemStorage( CvMemStorage* storage );
清空内存存储块
void cvReleaseMemStorage( CvMemStorage** storage );
指向被释放了的存储块的指针
2、序列
typedef struct CvSeq
{
CV_SEQUENCE_FIELDS()
} CvSeq;
结构CvSeq是所有OpenCV动态数据结构的基础。有两种类型的序列 -- 稠密序列和稀疏序列。稠密序列都派生自 CvSeq, 它们用来代表可扩展的一维数组 -- 向量,栈,队列,双端队列。数据间不存在空隙(即:连续存放)-- 如果元素从序列中间被删除或插入新的元素到序列中(不是两端),那么此元素后边的相关元素会被移动。稀疏序列都派生自 CvSet。
相关函数:
CvSeq* cvCreateSeq( int seq_flags, int header_size, int elem_size, CvMemStorage* storage );
创建一个序列
CvSeq* cvCloneSeq( const CvSeq* seq, CvMemStorage* storage=NULL );
克隆一个序列
void cvSeqInvert( CvSeq* seq ); 将序列中的元素进行逆序
void cvSeqSort( CvSeq* seq, CvCmpFunc func, void* userdata=NULL );
使用特定的比较函数对序列中的元素进行排序
char* cvSeqSearch( CvSeq* seq, const void* elem, CvCmpFunc func, int is_sorted,
int* elem_idx, void* userdata=NULL ); 查询序列中的元素
char* cvSeqPush( CvSeq* seq, void* element=NULL ); 添加元素到序列的尾部
void cvSeqPop( CvSeq* seq, void* element=NULL ); 删除序列尾部元素
char* cvSeqPushFront( CvSeq* seq, void* element=NULL ); 在序列头部添加元素
void cvSeqPopFront( CvSeq* seq, void* element=NULL ); 删除序列的头部元素
void cvSeqPushMulti( CvSeq* seq, void* elements, int count, int in_front=0 );
添加多个元素到序列尾部或头部
void cvSeqPopMulti( CvSeq* seq, void* elements, int count, int in_front=0 );
删除多个序列尾部或头部元素
char* cvSeqInsert( CvSeq* seq, int before_index, void* element=NULL ); 在序列中添加元素
void cvSeqRemove( CvSeq* seq, int index ); 删除序列中的元素
void cvStartAppendToSeq( CvSeq* seq, CvSeqWriter* writer ); 将数据写入序列中
void cvStartWriteSeq( int seq_flags, int header_size, int elem_size, CvMemStorage* storage,
CvSeqWriter* writer );
创建新序列,并初始化写入部分
CvSeq* cvEndWriteSeq( CvSeqWriter* writer );
完成写入操作并返回指向被写入元素的序列的地址
void cvStartReadSeq( const CvSeq* seq, CvSeqReader* reader, int reverse=0 );
初始化序列中的读取过程
例题4-9
