OpenCV进阶

cv::Mat 数据结构和相关深拷贝与浅拷贝

cv::Mat 是 OpenCV 中用于存储图像、矩阵等多维数据的核心数据结构。它的设计核心目标是实现高效的内存管理和便捷的数据操作。

📚 cv::Mat 的数据结构

cv::Mat 的设计巧妙地将其分为两个部分,这也是理解其拷贝行为的关键:

  1. 矩阵头 (Matrix Header)

    这是一个轻量级的结构体,包含了描述数据的元信息,例如:

    • 尺寸信息 :行数 (rows)、列数 (cols)。
    • 类型信息 :数据类型 (type),如 CV_8UC3 表示8位无符号整数、3通道。
    • 数据指针 :一个指向实际像素数据的指针 (data)。
    • 引用计数器 :一个指向引用计数器 (refcount) 的指针,用于追踪有多少个 Mat 对象共享同一块数据。
  2. 数据区 (Data Region)

    这是存储实际像素值的连续内存块。对于一幅图像来说,这部分占用的内存通常远大于矩阵头。

这种"头"与"数据"分离的设计,使得 cv::Mat 可以通过引用计数 机制来高效地管理内存。当多个 Mat 对象共享同一块数据时,只有最后一个对象被销毁时,数据内存才会被真正释放。

🧐 深拷贝与浅拷贝的区别

cv::Mat 的拷贝操作分为浅拷贝和深拷贝,它们的根本区别在于是否复制了数据区。

浅拷贝 (Shallow Copy)
  • 行为 :仅复制矩阵头 ,新的 Mat 对象和原始对象共享同一块数据区内存。
  • 影响 :修改其中任何一个 Mat 对象的像素数据,都会影响到所有共享该数据区的对象。
  • 性能:非常高效,因为它只复制了一个小的结构体,避免了大量数据的复制。
  • 实现方式
    • 使用赋值运算符 =
    • 使用拷贝构造函数 Mat B(A)
    • 创建感兴趣区域 (ROI),如 Mat roi = img(Rect(...))
深拷贝 (Deep Copy)
  • 行为 :不仅复制矩阵头 ,还会为新的 Mat 对象重新分配一块独立的内存来存储数据副本。
  • 影响:新对象和原始对象完全独立,修改其中一个不会影响另一个。
  • 性能:相对较慢,因为需要分配新内存并复制所有像素数据。
  • 实现方式
    • 使用 clone() 方法
    • 使用 copyTo() 方法

📊 核心区别对比

特性 浅拷贝 (Shallow Copy) 深拷贝 (Deep Copy)
内存共享 是,共享数据区 否,拥有独立数据区
修改影响 相互影响 互不影响
性能开销 低(仅复制矩阵头) 高(复制全部数据)
实现方法 =, 拷贝构造函数, ROI clone(), copyTo()

💻 代码示例

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    // 创建一个 2x2 的单通道矩阵
    cv::Mat mat1 = (cv::Mat_<int>(2, 2) << 1, 2, 3, 4);

    // --- 浅拷贝 ---
    cv::Mat mat2 = mat1; // 赋值操作,浅拷贝
    mat2.at<int>(0, 0) = 99; // 修改 mat2
    // 此时 mat1 也被修改了,输出: [99, 2; 3, 4]
    std::cout << "mat1 after shallow copy: " << mat1 << std::endl;

    // --- 深拷贝 ---
    cv::Mat mat3 = mat1.clone(); // 克隆操作,深拷贝
    mat3.at<int>(0, 0) = 100; // 修改 mat3
    // 此时 mat1 不受影响,输出: [99, 2; 3, 4]
    std::cout << "mat1 after deep copy: " << mat1 << std::endl;

    return 0;
}

⚠️ 特别注意:ROI 的浅拷贝

从原图中提取一个感兴趣区域(ROI)也是一种浅拷贝操作。这意味着对 ROI 的修改会直接反映到原始图像上。

cpp 复制代码
cv::Mat image = cv::imread("image.jpg");
// 提取左上角 100x100 的区域,这是浅拷贝
cv::Mat roi = image(cv::Rect(0, 0, 100, 100));
// 将 ROI 区域设置为黑色,原图对应区域也会变黑!
roi.setTo(cv::Scalar(0, 0, 0));

如果需要得到一个独立的 ROI 副本,必须在提取后调用 clone()

cpp 复制代码
// 创建一个独立的 ROI 副本
cv::Mat roi_clone = image(cv::Rect(0, 0, 100, 100)).clone();

🤔 clone() 与 copyTo() 的细微差别

虽然两者都用于深拷贝,但在使用上略有不同:

  • clone() : 总是创建一个新的 Mat 对象并分配内存,返回这个新对象。它更直接,适用于需要绝对独立副本的场景。
  • copyTo() : 将一个 Mat 的数据复制到另一个已存在Mat 对象中。如果目标 Mat 的大小或类型不匹配,copyTo() 会先为其重新分配内存。它更适合在目标内存已预分配或需要复用的场景,并且支持使用掩码(mask)进行选择性复制。

###未完待续

相关推荐
huisheng_qaq2 小时前
【01-AI入门篇】深入理解AI感知智能和认知智能
人工智能·ai·chatgpt·认知智能·感知智能
数智化精益手记局2 小时前
8d报告案例分析:拆解8d报告案例分析的8个步骤,解决生产现场重复发生的质量难题
大数据·数据结构·数据库·人工智能·精益工程
Eva_Hua2 小时前
NTIRE2025 高效4x超分辨率赛道
人工智能
乔江seven2 小时前
【李沐 | 动手学深度学习】13 含并行连结的网络(GoogLeNet)
人工智能·深度学习·卷积神经网络·googlenet
马士兵教育2 小时前
AI方向的就业工作岗位?
人工智能
2601_950760792 小时前
TNF-α信号通路与自身免疫性疾病研究进展
人工智能·机器学习·蛋白
高洁012 小时前
AI模型部署进阶:Docker容器化部署AI项目
人工智能·python·深度学习·数据挖掘·知识图谱
NOCSAH2 小时前
统好AI:数智化转型的核心支撑路径
大数据·人工智能·产品运营
FlyIer5562 小时前
软件“日抛”需加限定词:给人用的可抛,给流程与Agent用的不可抛
大数据·人工智能