CvMat矩阵数据结构


      当我们学习CvMat之前,OpenCV中矩阵的概念比线性代数中矩阵的概念更抽象和复杂一些一些。例如,创建矩阵的函数:CvMat*cvCreateMat(int rows,int cols,inttype),其中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;


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,inttype),该方法既设置了矩阵的头部,又为数据部分分配了内存空间,该函数是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, inttype,
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(constCvArr* arr,index)


### CvMat 的定义与作用 CvMat 是 OpenCV 早期版本中用于表示矩阵数据的核心结构体,主要用于图像处理和数值计算。它在设计上侧重于图像操作的优化,例如缩放、单通道提取以及图像阈值处理等[^1]。尽管随着 OpenCV 2.x 及更高版本的发展,`cv::Mat` 成为了更推荐使用的数据类型,但 CvMat 仍然在一些遗留代码或特定模块中被使用。 CvMat 结构体包含多个字段,如行数(rows)、列数(cols)、元素类型(type)和数据指针(data),这些信息使得它能够灵活地存储和操作二维数组数据。由于其直接暴露底层内存结构的特点,开发者可以对其进行高效的内存访问和修改。 ### CvMat 在计算机视觉中的使用方式 在图像处理任务中,CvMat 常用于实现图像滤波、边缘检测、形态学变换等操作。例如,在膨胀操作中,可以通过创建一个内核矩阵并将其封装为 CvMat 类型来参与图像卷积运算。以下是一个使用 `cvCreateStructuringElementEx` 创建结构化元素并进行膨胀处理的示例: ```c #include <opencv/cv.h> #include <opencv/highgui.h> int main() { IplImage* src = cvLoadImage("input.jpg", CV_LOAD_IMAGE_GRAYSCALE); IplImage* dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); // 创建 3x3 十字形结构元素 CvMat* element = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_CROSS, NULL); // 执行膨胀操作 cvDilate(src, dst, element, 1); cvSaveImage("output.jpg", dst); cvReleaseImage(&src); cvReleaseImage(&dst); cvReleaseMat(&element); return 0; } ``` 上述代码展示了如何利用 CvMat 实现图像的膨胀操作。其中 `cvCreateStructuringElementEx` 函数生成了一个自定义形状的结构化元素,并通过 `cvDilate` 对输入图像执行了膨胀处理。 ### CvMat 与其他图像类型的集成 在 OpenCV 中,CvMat 可以与 `IplImage` 和 `cv::Mat` 进行相互转换,从而实现不同数据结构之间的兼容性。例如,将 IplImage 转换为 CvMat 的过程可以通过 `cvGetMat` 函数完成,而从 CvMat 到 `cv::Mat` 的转换则可以直接赋值指针或者复制数据内容。 以下是将 CvMat 转换为 `cv::Mat` 的一种方法: ```cpp CvMat* cMat = ...; // 已有的 CvMat 指针 cv::Mat cppMat(cMat->rows, cMat->cols, CV_32FC1, cvMatData(cMat)); ``` 该段代码将 CvMat 数据映射到 `cv::Mat` 上,无需额外的内存拷贝,适用于需要跨类型共享数据的应用场景。 ### 开发中常见问题及解决方案 在实际开发过程中,CvMat 使用时可能会遇到内存泄漏、数据格式不匹配等问题。为了避免这些问题,建议遵循以下最佳实践: - **确保正确释放资源**:每次调用 `cvCreate...` 系列函数创建对象后,必须在适当的时候调用对应的 `cvRelease...` 函数释放内存。 - **注意数据类型一致性**:在矩阵运算或图像操作前,应确认输入输出的数据类型是否一致,否则可能导致不可预知的行为。 - **避免直接访问内部数据结构**:虽然 CvMat 提供了 `data.ptr`、`data.fl` 等字段用于访问具体数据,但在非必要情况下尽量使用 OpenCV 提供的 API 来操作数据,以提高代码可维护性和安全性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值