现在的位置: 首页 > 综合 > 正文

OpenCV学习笔记系列(二)

2018年04月25日 ⁄ 综合 ⁄ 共 3309字 ⁄ 字号 评论关闭

矩阵和图象类型

三种图象类型的类或结构继承图:

clip_image001

在使用OpenCV时,将会反复的遇到IplImage这个数据类型。IplImage是用来编码我们通常所讲的“图象”的基本的数据结构。这些图象可能是灰度的、彩色的、四通道(RGB+alpha)的,每个通道可能包含多种类型的整数的浮点型数据。因此这个类型比我们立刻想到的三通道8位RGB图象一般(general)的多。

在我们具体讨论图象之前,我们需要首先看一下另一种数据类型:CvMat,OpenCV的矩阵结构。虽然OpenCV完全是用C实现的,CvMat和IplImage之间的关系和C++中继承类似。无论出于何种意图和目的,IplImage都可以看做是从CvMat中衍生出来的。所以在理解增加了复杂度之前衍生类之前,最好先了解它的基类。类CvArr用以衍以CvMat的抽象基类。你可能会在函数原型(function prototypes)中经常看到CvArr(更准确的说是CvArr )。当它出现时,函数接受传入CvMat 或IplImage*。

注:我看到在cxtypes.h中,有这样的经典的注释: CvArr* is used to pass arbitrary array-like data structures into functions, where the particular array type is recognized at runtime. CvArr*用于向函数传入任意array-like的数据结构,具体的数组类型会在运行时确定。

CvMat矩阵结构

在深入了解矩阵之前,我们必须知道两件事情。第一,在OpenCV中没有向量(Vector)的构造方法。当我们需要向量时,我们使用1行的矩阵(或1列的矩阵,如果需要转置或共轭向量)。第二,OpenCV中矩阵的概念比在线性代数课上所学的矩阵更抽象。例如创建二维矩阵的例程原型为:

cvMat* cvCreateMat (int rows, int cols, int type);

这里type可以是预定义的长列表中的任何一种形式:CV_<bit_depth>(S|U|F)C<number_of_channel>。因此,矩阵可以包含32位的浮点型数(CV_32FC1),或者三通道8位整形数(CV_8UC3),或者不计其数的其他元素。CMat的元素没有要求一定是一个数(An element of a CvMat is not necessarily a single number)。矩阵中的一个元素可以用多重值来表示,这允许我们可以表示RGB图像的多重颜色通道(Being able to represent multiple values for a single entry in the matrix allows us to do things like represent multiple color channels in an RGB image)。对于一副简单的包含红、绿、蓝通道的图像,多数图像的操作可以独立的施加到每个通道。

从内部来讲,CvMat的结构相对简单:
typedef struct CvMat
{
        int type;
        int step; 

/* for internal use only */
int* refcount;
int hdr_refcount;

 

union
{
        uchar* ptr;
        short* s;
        int* i;
        float* fl;
        double* db;
} data; 

#ifdef __cplusplus
union
{
        int rows;
        int height;
};

union
{
        int cols;
        int width;
};

#else
        int rows;
        int cols;
#endif

}CvMat;

它包含一个宽度、一个高度、一个元素类型、一个步长(一行中的字节数,而不是元素的个数)以及一个指向数据数组的指针(其他的元素我们暂时不讨论)。你可以使用CvMat指针直接访问这些成员,或者,对于比较常用的元素,使用已提供的访问函数。例如为了获取矩阵的尺寸信息,你可以调用cvGetSize(CvMat*)返回一个CvSize结构,也可以用matrix->width和matrix->height独立的访问width和height成员。

这个信息一般被称为矩阵头(matrix header)。许多例程会对矩阵头和数据予以区分。

矩阵有多种创建方法。最常用的方法是使用cvCreateMat( ),它非常容易记忆,组合了两个原子函数cvCreateMatHeader( )好cvCreateData( )。cvCreateMatHeader( )创建一个CvMat结构,但没有给数据分配内存,cvCreateData( )处理给数据分配内存。

在OpenCV的源文件cxarray.cpp中,我们可以看到cvCreateMat( )的实现:

 

// Creates CvMat and underlying data
CV_IMPL CvMat* cvCreateMat( int height, int width, int type )
{
CvMat* arr = cvCreateMatHeader( height, width, type );
cvCreateData( arr );
return arr;
}

有时候,仅仅是cvCreateMatHeader( )也是需要的,因为有时候出于某种原因你已经为数据分配了内存,或者因为你还没有准备好为数据分配内存。第三种方法是使用cvCloneMat(CvMat*),它根据一个已有的矩阵创建一个新的矩阵。当矩阵不再需要时,调用cvReleaseMat(CvMat**)可以把它释放掉。

和OpenCV众多的结构相似,有一个名为cvMat的构造函数用以创建一个CvMat结构。这个例程并不实际分配内存,它只是创建矩阵头(这和cvInitMatHeader( )相似)。可以通过这个例程把矩阵头的指针指向已有的数据,这是一种很好的构造矩阵的方法。列如,下面用固定的数据创建一个OpenCV矩阵:

float vals[] = { 0.866025, -0.500000, 0.500000, 0.866025 };
CvMat rotmat;
cvInitMatHeader(&rotmat, 2, 2, CV_32FC1, vals);

一旦我们有了一个矩阵,我们可以对它做很多事情。最简单的操作是查询数组定义的方方面面和数据的存取。查询矩阵,我们可以使用cvGetElemType(const CvMat * arr)、cvGetDims(const CvMat* arr, int* size=NULL)和cvGetDimSize(const CvMat* arr, int index)。第一个函数返回一个整型常数,用以代表存储在数组中元素的类型(它可能是CV_8UC1、CV_64FC4等)。第二个输入一个矩阵和一个可选的整型指针,返回数据的维数(对于上面例子,将返回2,不过很快我们将会遇到N维类矩阵的对象)。如果整型指针不为NULL,它将会存储数组的宽和高。最后一个函数输入1个整数指示关心的维,然后简单的返回矩阵在那个维上的长度(The last function takes an integer indicating the dimension of interest and simply returns the extent of the matrix in that dimension)。对于这里讨论的通常的二维矩阵,维度0表示宽,维度1表示高(For the regular two-dimensional matrices discussed here, dimension zero (0) is always the “width” and dimension one (1) is always the height.)。

其实,通过看cxarray.cpp的源代码,我们可以知道,上面三个函数用if语句具体对常规矩阵、图象、N维矩阵、稀疏矩阵做了区分。如果输入的矩阵类型不同,返回值的意义也不相同。如果是图象琮考虑了ROI是否存在的问题。上面对函数的讲解只是讨论了输入矩阵为常规矩阵的情况

抱歉!评论已关闭.