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 数组,简单赋值 = 只是创建了一个新的引用,它甚至都不是一个新对象(它共享头部和数据)。这极易导致意外的副作用,通常只有当你确定两个变量必须共享所有状态时才使用。
相关推荐
txwtech4 小时前
第9篇 opencv提取矩形角度不是很准确的处理方法
人工智能·opencv·计算机视觉
TreeExplore4 小时前
OpenCV工程中直接包含调用vs2022
人工智能·opencv·计算机视觉
Juchecar4 小时前
从瑟瑟发抖到电流“高速公路”:揭秘能量与神奇的微观世界
人工智能
珊珊而川4 小时前
vllm论文中figure3每个块的区别
人工智能
北海道浪子4 小时前
[免费送$1000]ClaudeCode、Codex等AI模型在开发中的使用
前端·人工智能·后端
福客AI4 小时前
电商智能客服机器人:客服的服务革新之路
人工智能·机器人
CoderIsArt5 小时前
CORDIC三角计算技术
人工智能·算法·机器学习
taxunjishu5 小时前
Ethernet/ip 转 Modbus RTU 驱动,罗克韦尔 PLC 与华为逆变器打造光伏电站智能监控典范
人工智能·物联网·自动化·区块链
Alex艾力的IT数字空间5 小时前
基于PyTorch和CuPy的GPU并行化遗传算法实现
数据结构·人工智能·pytorch·python·深度学习·算法·机器学习