OpenCV(十二):Mat

数据结构概述

在 C++ 中,cv::Mat 是 OpenCV 的核心数据结构,用于存储图像和矩阵。它由两部分组成:

  1. 矩阵头(Matrix Header):包含矩阵的尺寸(行、列)、数据类型、存储地址(指向数据的指针)等元信息。
  2. 数据块(Data Block):实际存储像素值或矩阵元素的内存块。

在 Python 中,cv2.imread() 等函数返回的图像对象是一个 numpy.ndarray 。NumPy 数组同样包含元数据(Metadata) (如形状 shape、数据类型 dtype 等)和实际的数据缓冲区(Data Buffer)

无论是 cv::Mat 还是 numpy.ndarray,它们的深拷贝浅拷贝 主要区别就在于是否创建新的数据块

浅拷贝(Shallow Copy)

浅拷贝创建了一个新的对象,但它只复制了原始对象的顶层结构 ,即元数据/头部信息 。对于复杂对象(如包含其他对象的对象),浅拷贝只会复制其中包含的对象的引用(指针) ,而不会复制实际的数据内容

Python 中 NumPy 的浅拷贝机制:

在 NumPy/OpenCV 中,主要的浅拷贝方式包括:

  1. 简单的变量赋值(Assignment)

    Python 复制代码
    import cv2
    import numpy as np
    
    img_original = cv2.imread("image.jpg")
    img_shallow_assign = img_original
    • 行为 :这并不是真正的拷贝,而是引用传递img_shallow_assignimg_original 指向内存中的同一个对象 (同一个 ndarray),共享头部和数据块
    • 后果 :修改 img_shallow_assign 直接影响 img_original,反之亦然。
  2. 视图/切片(View/Slice)

    Python 复制代码
    # 浅拷贝示例:切片操作
    img_shallow_slice = img_original[100:200, 100:200]
    • 行为 :切片操作通常会创建一个新的 ndarray 对象(新的头部) ,但其数据指针仍指向原始数组的数据块 。这个新对象被称为原始数组的视图(View)
    • 后果 :虽然是不同的变量名和头部,但它们共享底层数据 。修改 img_shallow_slice 的像素值 影响 img_original 中对应区域的像素值。
  3. ndarray.view() 方法

    Python 复制代码
    # 浅拷贝示例:view() 方法
    img_shallow_view = img_original.view()
    • 行为 :明确地创建一个新的 ndarray 头部,但与原始数组共享数据缓冲区。
    • 后果 :修改 img_shallow_view 影响 img_original

浅拷贝总结

特点 描述
头部 新对象有自己的头部信息(形状、数据类型等)。 (赋值除外,赋值连头部都共享)
数据 共享原始对象的底层数据块。
独立性 不独立。修改其中任何一个对象的数据,另一个对象的数据也会随之改变。
速度/内存 快,内存占用少,因为没有复制数据。
适用场景 当你希望在不改变内存中实际数据的前提下,以不同的方式(如不同的数据类型、形状)来查看或操作同一块数据时。

深拷贝(Deep Copy)

深拷贝创建了一个完全独立 的新对象。它不仅复制了原始对象的顶层结构(头部),还会递归地复制 原始对象中的所有数据块。这意味着,深拷贝的结果对象拥有全新的、独立的内存数据

Python 中 NumPy 的深拷贝机制

在 OpenCV/NumPy 中,实现深拷贝的主要方法是:

  1. ndarray.copy() 方法

    Python 复制代码
    # 深拷贝示例:.copy() 方法
    img_deep_copy = img_original.copy()
    • 行为 :创建了一个新的 ndarray 头部 ,并为图像数据分配了全新的内存空间,然后将原始数据内容复制到新内存中。
    • 后果img_deep_copy 是一个完全独立的副本。修改 img_deep_copy 的像素值不会 影响 img_original,反之亦然。
  2. cv2.clone() (C++ 中常用,Python 对应 copy()):

    虽然在 C++ 中有 Mat::clone() 方法,但在 Python 的 NumPy 环境下,ndarray.copy() 是最常用的深拷贝方法,效果等同于 C++ 中的 clone()。

  3. cv2.copyTo() (功能等价于 copy()):

    在 C++ 中 Mat::copyTo() 也是常用的深拷贝方法,在 Python 中也可以用于深拷贝,但不如 img.copy() 直接和常用。

  4. copy.deepcopy() 函数

    Python 复制代码
    import copy
    # 深拷贝示例:copy 模块
    img_deep_copy_module = copy.deepcopy(img_original)
    • 行为copy 模块是 Python 内置的,deepcopy() 适用于任何复杂的 Python 对象,它会进行递归复制。对于 NumPy 数组,其效果与 ndarray.copy() 相同,但通常**ndarray.copy() 效率更高**,因为它是 NumPy 库内部优化的 C 语言实现。

深拷贝总结

特点 描述
头部 新对象有自己的头部信息(形状、数据类型等)。
数据 拥有独立分配的新数据块。
独立性 完全独立 。修改一个对象不会影响另一个对象。
速度/内存 慢,内存占用大,因为它需要分配新内存并复制所有数据。
适用场景 当你需要在保留原始数据的同时,对副本进行修改或处理,且不希望相互影响时(例如图像滤波、目标检测后的标注绘制等)。

示例

python 复制代码
import cv2
import numpy as np

# 1. 准备原始图像 (假设我们有一张 100x100 的三通道 BGR 图像)
# 实际操作中,请替换为 cv2.imread("your_image.jpg")
img_original = np.zeros((100, 100, 3), dtype=np.uint8) 
# 将左上角像素设为白色 (255, 255, 255)
img_original[0, 0] = [255, 255, 255] 
print(f"原始图像 [0, 0] 像素: {img_original[0, 0]}")

# ----------------- 浅拷贝 (视图/切片) -----------------
# 切片操作创建一个视图,共享数据
img_shallow_slice = img_original[50:80, 50:80] 

# 修改浅拷贝(切片)的左上角像素 (即原始图像的 [50, 50] 像素)
img_shallow_slice[0, 0] = [0, 0, 255] # 改为蓝色

print("\n--- 浅拷贝操作 ---")
print(f"浅拷贝 [0, 0] 像素 (修改后): {img_shallow_slice[0, 0]}")
# 检查原始图像中对应的像素
print(f"原始图像 [50, 50] 像素 (被影响): {img_original[50, 50]}") # 结果是 [0, 0, 255]

# ----------------- 深拷贝 (copy()) -----------------
# 使用 .copy() 方法创建深拷贝
img_deep_copy = img_original.copy()

# 将深拷贝的 [0, 0] 像素修改为绿色 (0, 255, 0)
img_deep_copy[0, 0] = [0, 255, 0] 

print("\n--- 深拷贝操作 ---")
print(f"深拷贝 [0, 0] 像素 (修改后): {img_deep_copy[0, 0]}")
# 检查原始图像中对应的像素
print(f"原始图像 [0, 0] 像素 (未被影响): {img_original[0, 0]}") # 结果是 [255, 255, 255]

# ----------------- 赋值 (最浅的拷贝/引用) -----------------
img_assign = img_original 
img_assign[1, 1] = [255, 0, 0] # 修改为红色

print("\n--- 赋值操作 ---")
print(f"赋值 [1, 1] 像素: {img_assign[1, 1]}")
print(f"原始图像 [1, 1] 像素 (被影响): {img_original[1, 1]}") # 结果是 [255, 0, 0]

# --- 结论 ---
# 1. 浅拷贝/切片:修改 img_shallow_slice[0, 0] 影响了 img_original[50, 50]。
# 2. 深拷贝:修改 img_deep_copy[0, 0] 没有影响 img_original[0, 0]。
# 3. 赋值:修改 img_assign[1, 1] 影响了 img_original[1, 1]。

总结

在 OpenCV 的 Python 实践中,选择正确的拷贝方式是高效和安全编程的关键:

  1. 何时使用深拷贝 (img.copy())?
    • 当你需要对图像进行修改 (如绘图、阈值处理、颜色转换、滤波等),但又希望保留原始图像不变时。
    • 这是最安全的选择,确保操作的隔离性。
  2. 何时使用浅拷贝(切片 img[y:y+h, x:x+w])?
    • 当你需要提取图像的某个区域进行操作 ,并且希望修改能够反映回原图时。
    • 当你需要仅查看图像的某个区域,而不想复制数据以节省内存和时间时。
    • 当你需要创建一个临时变量,仅用于引用原图,并且知道不会进行修改操作时。
  3. 避免使用简单赋值
    • 对于 NumPy/OpenCV 数组,简单赋值 = 只是创建了一个新的引用,它甚至都不是一个新对象(它共享头部和数据)。这极易导致意外的副作用,通常只有当你确定两个变量必须共享所有状态时才使用。
相关推荐
cici158741 分钟前
MATLAB全景拼接完整实现方案
图像处理·计算机视觉·matlab
deardao9 分钟前
【时序异常检测综述】十年回顾:深入研究时间序列异常检测
人工智能·机器学习·时间序列·事件检测
Axis tech12 分钟前
SenseGlove R1外骨骼手套专为机器人遥操作设计
人工智能
沫儿笙12 分钟前
弧焊机器人节气设备
人工智能·机器人
百锦再19 分钟前
Python实现开源AI模型引入及测试全过程
人工智能·python·ai·开源·aigc·模型·自然语言
咋吃都不胖lyh25 分钟前
详解 UCB 算法的置信区间与核心逻辑(通俗 + 公式 + 实例)
人工智能·算法·机器学习
造火箭26 分钟前
普通手机使用Open-AutoGLM 感受豆包AI 手机的体验
人工智能·智能手机
Mr.Lee jack26 分钟前
【torch.compile】PyTorch Dynamo 和 Inductor 编译流程
人工智能·pytorch·深度学习
浮不上来29 分钟前
人工兔优化算法(ARO)详解:原理、实现与应用
大数据·人工智能·机器学习·优化算法
Elastic 中国社区官方博客33 分钟前
Elastic 在 AWS re:Invent:总结一年在 agentic AI 创新中的合作
大数据·人工智能·elasticsearch·搜索引擎·云计算·全文检索·aws