在我们讨论IplImage之前,我们需要看另一个数据类型:CvMat,即OpenCV的矩阵类型。尽管OpenCV是用C语言实现的,但是CvMat和IplImage的关系其实就类似于C++中的类的继承关系。IplImage类继承自CvMat类。所以,我们最好先了解一下IplImage的基类CvMat类的情况,然后再看更复杂的IplImage类。而CvArr类,是CvMat类的抽象基类。正因为CvArr类是基类,所以当我们看到OpenCV的函数参数为CvArr*类型的参数时,我们可以代入CvMat*或者IplImage*类型的实参。
CvMat矩阵数据结构
当我们学习CvMat之前,我们必须知道两个事情,首先OpenCV中是没有"vector(向量)"数据类型的,当我们需要一个"vector"时,我们就使用一个三行一列的矩阵。其次,OpenCV中矩阵的概念比线性代数中矩阵的概念更抽象和复杂一些一些。例如,创建矩阵的函数:CvMat* cvCreateMat(int rows,int cols,int type),其中type代表预定义的数据类型,即矩阵中每一个元素的数据类型,该类型的形式是:CV_<bit数>(S|U|F)C<通道数>,例如,数据类型可能是CV32FC1,即32bit的浮点数,或CV_8UC3,8bit的无符号整数,或CV_8UC3,无符号8bit整数,3通道,等等。我们会发现,cvMat里,矩阵中行和列上的每一个元素,不必是一个单独的数字,可能是一系列数字(有几个通道就有几个数字)。每一个元素可以代表多个值,就允许了我们在矩阵中包含一个RGB的图像。
从内部的结构上看,CvMat相当的简单,我们可以通过代码看一下该数据结构的原型(代码在.../opencv/cxcore/include/cxtypes.h):
其中包含了width,height,type,step(是一行元素的长度,与width类似,但以字节计算),以及指向数据的指针.你可以通过CvMat数据类型的变量直接接触该类型内部的成员,例如,CvMat* matrix ,就可以用matrix->height,matrix->width来获得矩阵的尺寸。又或者通过OpenCV的函数来获得。例如,可以用cvGetSize(CvMat*)来获得CvSize对象,这代表该矩阵的长和宽。
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类型的数据的“头部”,即矩阵的定义部分。许多OpenCV的函数,将矩阵的头部和数据部分分开处理。
矩阵的创建可以用多种方法,最简单的一种是CvMat* cvCreateMat(int rows,int cols,int type),该方法既设置了矩阵的头部,又为数据部分分配了内存空间,该函数是cvCreateMatHeader()和cvCreateData()的合并缩写。cvCreateMatHeader()只创建CvMat头部,但不为数据部分分配空间。而cvCreateData()则是为矩阵的数据部分分配了内存空间。有时候,我们只需要cvCreateMatHeader()就可以了,因为基于一些理由,我们可能已经为数据部分分配了空间,或者此时分配空间还不是时候。另一个创建矩阵的方法cvCloneMat(CvMat*)是从一个已经有的矩阵,来“克隆”出一个新的矩阵来。当我们不再需要某矩阵时,我们需要调用长cvReleaseMat(CvMat*)来释放它。
就象其他的OpenCV数据类型一样,矩阵数据类型有一个构造函数,CvMat cvMat( int rows, int cols, int type, void* data=NULL );这个函数没有为矩阵的数据部分分配空间,只是初始化了矩阵的头部,类似cvInitMatheader()。
以下是这些函数的原型:
1,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个字节对齐。
2,CreateMatHeader
创建新的矩阵头
CvMat* cvCreateMatHeader( int rows, int cols, int type );
rows
矩阵行数.
cols
矩阵列数.
type
矩阵元素类型(见 cvCreateMat).
函数 cvCreateMatHeader 分配新的矩阵头并且返回指向它的指针. 矩阵数据可被进一步的分配,使用cvCreateData 或通过 cvSetData明确的
分配数据.
3,ReleaseMat
删除矩阵
void cvReleaseMat( CvMat** mat );
mat
双指针指向矩阵.
函数cvReleaseMat 缩减矩阵数据参考计数并且释放矩阵头 :
if( *mat )
cvDecRefData( *mat );
cvFree( (void**)mat );
4,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) 矩阵的积
5,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 );
6,CloneMat
创建矩阵拷贝
CvMat* cvCloneMat( const CvMat* mat );
mat
输入矩阵.
函数 cvCloneMat 创建输入矩阵的一个拷贝并且返回该矩阵的指针.
以下的例子中,我们让矩阵的数据部分指向了已经分配好了的数据:
float vals[] = { 0.866025, -0.500000, 0.500000, 0.866025};
CvMat rotmat;
cvInitMatHeader(
&rotmat,
2,
2,
CV_32FC1,
vals
);
当我们定义好了矩阵,我们可以通过一些函数查看矩阵的属性,例如:cvGetElemType(const cvArr* arr),cvGetDims(const CvArr* arr,int*
sizes=NULL),cvGetDimSize(const CvArr* arr,index)
7.GetElemType
返回数组元素类型
int cvGetElemType( const CvArr* arr );
arr
输入数组.
函数 GetElemType 返回数组元素类型就像在cvCreateMat 中讨论的一样:
CV_8UC1 ... CV_64FC4
8.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 返回特定的维大小(每维的元素数)。
以下是一个例子:
#pragma comment( lib, "cxcore.lib" )
#include "cv.h"
#include <stdio.h>
void main()
{
CvMat rotmat;
int sizes[2];
float array[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14};
//注意,此句更改为 cvInitMatHeader(&rotmat,3,5,CV_32FC1,NULL);时,cvGetDimSize不能起作用
cvInitMatHeader(&rotmat,3,5,CV_32FC1,array);
int type = cvGetElemType(&rotmat);
cvGetDims(&rotmat,sizes);
printf("type=%d/n",type);
printf("height=%d,width=%d/n",sizes[0],sizes[1]);
printf("height=%d,width=%d/n",cvGetDimSize(&rotmat, 0),cvGetDimSize(&rotmat, 1));
}