《学习OpenCV》随笔——第3章 初探OpenCV
CvPoint这个类型和MFC以及Qt中的相应结构很类似,都是integer类型的x,y。但是它的变体类型是CvPoint2D32f和CvPoint3D32f。前者也是有两个成员,但是是浮点类型的。后者是浮点类型的,而且多了一个z。
CvSize这个类型和CvPoint很相似。在这一章的最后,给出了上述两个类型的C语言定义。CvSize数据成员是integer类型的width和height,如果希望使用浮点类型,可以使用CvSize2D32f。CvRect类型派生于CvPoint和CvSize,包含4个成员,x,y,width,height。
CvScalar包含4个整型成员,CvScalar有一个单独的成员val,它是一个只想4个双精度浮点数数组的指针即double val[4]。这个类型很重要,之后的很多矩阵操作函数中都有他的身影。
CvArr-》CvMat-》IplImage。这里属于依次派生,即CvArr作为剩下两个的父类。在这里和C++很相似,体现了极其浓烈的面向对象编程的思想。因此,我们看到形参表中需要CvArr*类型参数时,就可以考虑将这两者带入,这样就可以进行一系列操作。
OpenCv矩阵的概念相对于线性代数更为抽象,尤其是矩阵的元素,并非只能取数值类型。
矩阵由宽度,高度,类型,行数据长度(step,行的长度用字节表示而不是整形或者浮点型长度)和一个指向数据的指针构成。
typedef struct CvMat { int type; int step; int *refcount; union { uchar*ptr; short* s; int * i; float* fl; double *db; }data; union { int rows; int height; }; union { int cols; int width; }; }CvMat;
上述代码就是CvMat的类型结构,我们可以看到,这其中有三个联合体,在32位系统中,sizeof(CvMat)=24。
矩阵有多种创建方法,最常见的方法是用cvCreateMat()这个函数进行创建。它由多个原函数组成,例如,cvCreateMatHeader()和cvCreateData()。cvCreateMatHeader()函数创建CvMat结构,不为数据分配内存,二cvCreateData()函数只负责数据的内存分配。有时,只需要函数cvCreateMatHeader(),因为已因其他理由分配了存储空间,或因为还不准备分配存储空间。第三种方法使用函数cvCloneMat(CvMat*),他依据一个现有矩阵创建一个新的矩阵。当这个矩阵不再需要时,可以调用函数cvReleaseMat(CvMat*)释放它。上述的几个函数,在手册中基本上就是这个意思。
查询矩阵,我们可以使用函数cvGetElemType(const CvArr* arr),cvGetDims(const CvArr*arr,int *size=NULL)和cvGetDimSize(const CvArr*,int index),第一个返回一个整型常数,表示存储在数组里的元素类型。第二个取出数组以及一个可选择的整形指针,他返回维数,如果整型指针不为空,它将存储对应数组的高度和宽度。最后的函数通过一个指示维数的整型数简单地返回矩阵在那个位数上矩阵的大小。
在这里,还要提一句,在OpenCv提供的参考手册中,有这么一段话需要注意
A primitive OpenCV data type is one of unsigned char, bool, signed char, unsigned short, signed short, int, float, double, or a tuple of values of one of these types, where all the values in the tuple have the same type. Any primitive type from the list can be defined by an identifier in the form CV_<bit-depth>{U|S|F}C(<number_of_channels>)
这段话的意思是:
OpenCv的基本类型,是unsigned char, bool, signed char, unsigned short, signed short, int, float或者是一组具有相同类型的数据。任何在上述情形下的基础类型可以被定义为CV_<位数>{U|S|F}C<通道数>这样的一个标识符形式。
这里通道的概念我有我的现在的理解,就是用n个通道来描述CV_mUCn的这么一种数据类型,这里m是用来表述矩阵中存储数据的每个元素的位数。这里的通道的位数概念和有(无)符号位数的用法统统类似于11111111之于有无符号数对于二进制数的解释(2015年5月7日注解)。
矩阵数据的存取
宏CV_MAT_ELEM(),传入矩阵、待提取的元素的类型、行和列数4个参数,返回提取出的元素的值。
宏CV_MAT_ELEM_PTR(),传入矩阵、待返回元素的行和列号这三个参数,返回只想这个元素的指针。该宏和CV_MAT_ELEM()宏的最重要的区别是后者在指针解引用之前将其转化成制定的类型。如果需要同时读取数据和设置数据,可以直接调用CV_MAT_ELEM_PTR()。这些在访问距震中所有元素时,不好用,效率较低。
cvGet*D、cvPtr*D这种函数族也可以进行矩阵数据的存取。
step是距震中行的长度,单位为字节。处于效率考虑,矩阵或图像的内存分配都是4字节的整数倍。因此,如果我们得到一个字节指针,该指针指向数据元素,那么我们可以用step和这个指针相加以使指针指向正好在我们的点的下一行元素。
cvmSet(),cvmGet() 用于处理浮点单通道。
恰当的方法:
在自定义指针进行操作。数据是按光栅扫描顺序存储的。计算只想矩阵的指针时,矩阵的元素data是一个联合体。所以,对这个指针解引用的时候,必须指明结构体中的正确的元素以便得到正确的指针类型。然后,为了使指针产生正确的偏移,必须用矩阵的行数据长度(step)元素。
IplImage这个数据结构在书中列举如下:
typedef struct_IplIMage { int nSize; int ID; int nChannels; int depth; char colorModel[4]; char channelSeq[4]; int dataOrder; int origin; int align; int width; int height; struct _IplROI* roi; struct _IplImage * maskROI; void *imageId; struct _IplTileInfo *tileInfo; int imageSize; char *imageData; int widthStep; int BorderMode[4]; int BorderConst[4]; char *imageDataOrigin; } IplImage;
width和height这两个变量很重要,其次是depth和nchannals