OpenCV中的所有类型或函数都定义在命名空间cv之中,其中有一个类专门用来表示图像数据,那就是Mat类。它的前身就是OpenCV1.0中的IplImage结构体,IplImage是一个C语言结构体,感兴趣的小伙伴可以参考下面的简化版。
typedef struct _IplImage {
int nSize; // 结构体大小(sizeof(IplImage))
int width; // 图像宽度(像素)
int height; // 图像高度(像素)
char *imageData; // 指向像素数据的指针
int imageSize; // 图像数据总大小(字节)
int depth; // 像素深度(如 IPL_DEPTH_8U, IPL_DEPTH_32F)
int nChannels; // 通道数(1=灰度,3=BGR)
int origin; // 图像原点(0=左上角,1=左下角)
int widthStep; // 每行的字节数(考虑内存对齐)
// ... 其他字段(ROI、掩码等)
} IplImage;
Mat类是OpenCV中最核心的图像和矩阵数据结构,它使用引用计数机制,自动释放不再使用的内存,程序员不必担心发生内存泄露;拥有灵活的矩阵表示,可以表示N维的数据;支持从8位无符号整数到64位浮点数的多种数据类型,所以可以支持多种数据格式的图像处理;且内置多种矩阵操作和数学运算。下面来看一下它的重要定义成员。
class CV_EXPORTS Mat {
public:
// 数据指针
uchar* data;
// 维度信息
int dims;
int rows, cols; // 2维矩阵的行列数
// 步长(step) - 每一维的字节数
size_t step[DIM];
// 引用计数
int* refcount;
// 其他成员...
};
Mat主要由头部和数据块组成。头部包含了图像的相关信息(如大小,通道数,数据类型),数据块包含了图像的所有像素值。其中,data表示图像数据的指针,dims表示维数,rows表示图像的高度,cols表示图像的宽度,refcount表示引用计数。
关于引用计数,这是Mat最重要的内存管理机制——写时复制(Copy-on-Write,COW)。比如将一个Mat对象复制给另一个Mat对象时,两个对象的头部不同,但会共享数据块。当多个Mat对象共享同一数据块时,它们通过引用计数跟踪共享状态。仅当某个对象尝试修改共享数据时,才会触发实际的深拷贝(内存复制)。
写时复制的优势是减少内存占用,提升性能。如果需要强制深拷贝,可以使用Mat提供的clone()方法。
cv::Mat B = A.clone(); // 显式复制数据(无视COW)
创建图像
关于使用Mat类在内存中创建图像,有多种方式。这是因为Mat类本身有多个构造函数。下面我将分享一些常用的创建图像的方法。
1.默认构造函数
Mat img;
表示创建一个空矩阵,后续会用create()或 imread()等方法填充数据。
2.指定尺寸和类型的构造函数
cv::Mat img1(480, 640, CV_8UC3); // 480行×640列,3通道8位图像
cv::Mat img2(cv::Size(640, 480), CV_32FC1); // 单通道32位浮点矩阵
其中,img1表和img2的宽640像素,高480像素。
3.带初始化的构造函数
Mat red_img(480, 640, CV_8UC3, cv::Scalar(0, 0, 255)); // 全红色图像
4.从现有数据构造(不复制数据)
float data[] = {1, 2, 3, 4, 5, 6};
cv::Mat mat(2, 3, CV_32FC1, data); // 2×3矩阵,共用data内存
5.从其他Mat构造(共享/复制数据)
Mat A = cv::imread("image.jpg");
Mat B(A); // B与A共享数据(COW)
Mat roi(A, cv::Rect(10, 10, 100, 100)); // 共享A的ROI数据
其中,A表示从本地磁盘读取的图像,B和A的数据共同指向同一个内存,roi表示图像A的部分数据,3个Mat对象都共享同一个内存。
6.多维矩阵构造函数
int dims[] = {3, 4, 5}; // 3×4×5的张量
cv::Mat tensor(3, dims, CV_32FC1);
7.特殊矩阵构造函数
Mat I = cv::Mat::eye(3, 3, CV_32FC1); // 3×3单位矩阵
8.从OpenCV 1.x的IplImage或CvMat转换
IplImage* ipl_img = cvLoadImage("image.jpg");
Mat mat(ipl_img, true); // 深拷贝数据
cvReleaseImage(&ipl_img);
9.从STL容器构造
std::vector<float> vec = {1, 2, 3, 4};
cv::Mat mat(vec, false); // 共享vec内存(危险!需确保vec存活)
总结:Mat 的构造函数共有 约15个主要重载(含模板),覆盖以下场景:
- 创建空矩阵
- 指定尺寸和类型
- 从外部数据包装
- 从其他
Mat
共享/复制 - 多维矩阵
- 特殊矩阵(零矩阵/单位矩阵等)
- 兼容旧版结构
- STL容器集成
在后续,我将继续深入挖掘Mat类的各项基本技能,在实战中熟悉它的使用。
——重庆教主 2025年5月7日
若文章对您有帮助,可以激励一下我哦,祝您平安幸福!
微信 | 支付宝 |
---|---|
![]() |
![]() |