Opencv中文网

创建图像Mat类

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日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1