OpenCV(八):NumPy

OpenCV (Open Source Computer Vision Library) 和 NumPy (Numerical Python) 是 Python 中进行图像处理和计算机视觉任务时不可或缺 的两个库。OpenCV-Python 的核心实现依赖于 NumPy,所有 OpenCV 的图像数据结构都以 NumPy 数组(numpy.ndarray)的形式表示。

图像表示与数据结构:NumPy 数组

在 OpenCV 中,图像被视为多维的 NumPy 数组。这是理解一切操作的基础。

图像的本质:多维数组

  • 灰度图: 一个二维数组(矩阵),形状通常为 (H,W),其中 H 是高(行数),W 是宽(列数)。每个元素代表一个像素的强度值(通常在 0 到 255 之间)。
  • 彩色图 (BGR/RGB): 一个三维数组,形状通常为 (H,W,C),其中 H 是高,W 是宽,C 是通道数(通常 C=3,分别对应蓝、绿、红或红、绿、蓝)。
    • 注意: OpenCV 默认的颜色顺序是 BGR(蓝-绿-红),而不是常见的 RGB。

NumPy 数组的属性

使用 NumPy 数组的属性,可以方便地查看图像的基本信息:

  • .shape 返回图像的维度信息(高、宽、通道数)。
    • 例如:彩色图 img.shape 可能返回 (480, 640, 3)
    • 灰度图 img.shape 可能返回 (480, 640)
  • .dtype 返回数组中元素的类型。图像像素最常用的类型是 uint8(8 位无符号整数,范围 0−255)。
  • .size 返回数组中元素的总个数(高 × 宽 × 通道数)。

像素访问与操作

NumPy 的切片和索引机制使得像素级别的操作变得非常高效和直观。

  • 访问单个像素(彩色图): img[y, x] 返回一个包含 B,G,R 三个通道值的数组。
    • 例如:pixel = img[100, 150] 得到 [B, G, R]
  • 访问单个通道: img[:, :, 0] 提取蓝色通道。
  • 区域操作 (ROI, Region of Interest): 使用切片来选取图像的特定矩形区域。
    • 例如:roi = img[y1:y2, x1:x2]
  • 修改像素值: 可以直接赋值修改像素或区域。
    • 例如:将左上角 50×50 区域设为黑色(0):img[0:50, 0:50] = [0, 0, 0]

NumPy 在图像操作中的优势

将图像存储为 NumPy 数组带来了巨大的性能和功能优势。

向量化操作(Vectorization)

NumPy 的核心优势在于向量化操作,它允许对整个数组(或数组的子集)进行元素级别的运算,而无需编写显式的 Python 循环。这大大提高了运算速度,因为底层操作是通过高度优化的 C/C++/Fortran 代码执行的。

  • 亮度调整: 增加图像亮度 50(饱和操作,需要注意数据溢出)。
    • new_img = cv2.add(img, 50) 或使用 NumPy 的 Clip 确保值在 0−255 之间:new_img = np.clip(img.astype(np.int32) + 50, 0, 255).astype(np.uint8)
  • 通道分离与合并: OpenCV 的 cv2.split()cv2.merge() 可以完成,但 NumPy 也能高效实现。
    • b, g, r = cv2.split(img)
    • img_merged = cv2.merge((b, g, r))

图像创建与初始化

NumPy 提供了便捷的方式来创建各种初始化图像(画布):

  • 全黑图像: black_img = np.zeros((H, W, 3), dtype=np.uint8)
  • 全白图像: white_img = np.ones((H, W, 3), dtype=np.uint8) * 255
  • 随机噪点图: random_img = np.random.randint(0, 256, size=(H, W, 3), dtype=np.uint8)

数据类型转换(dtype

图像处理中经常需要进行数据类型转换,例如,为了进行精确的浮点数运算(如傅里叶变换或归一化),或为了处理溢出问题。

  • 归一化到 0.0−1.0: float_img = img.astype(np.float32) / 255.0
  • 反归一化回 0−255: uint8_img = (float_img * 255).astype(np.uint8)

OpenCV 函数对 NumPy 的无缝支持

OpenCV-Python 的设计哲学是所有接受图像作为输入的函数,都期望接收一个 NumPy 数组;所有返回图像的函数,都会返回一个 NumPy 数组。

图像 I/O

  • 读取图像: cv2.imread() 返回一个 NumPy 数组。
  • 显示图像: cv2.imshow() 接受一个 NumPy 数组作为参数。
  • 写入图像: cv2.imwrite() 接受一个 NumPy 数组作为参数。

几何变换

像平移、旋转、缩放等操作,其变换矩阵本身就是 NumPy 数组,而 OpenCV 函数内部会对输入的图像(NumPy 数组)高效地进行矩阵运算。

  • 平移: 需要定义 2×3 的平移矩阵 M,该矩阵就是一个 NumPy 数组。

    python 复制代码
    M = np.float32([[1, 0, tx], [0, 1, ty]])
    shifted = cv2.warpAffine(img, M, (W, H))
  • 旋转: cv2.getRotationMatrix2D() 返回的也是一个 NumPy 数组。

卷积与滤波

像高斯模糊、Sobel 梯度等操作,其核心是卷积运算。OpenCV 的 cv2.filter2D() 函数可以接受一个 NumPy 数组作为自定义卷积核(Kernel)

python 复制代码
# 定义一个 3x3 的均值模糊核
kernel = np.ones((3, 3), np.float32) / 9
dst = cv2.filter2D(img, -1, kernel)

与其他库的集成

NumPy 的通用性使得 OpenCV 能够轻松地与其他流行的 Python 科学计算和数据可视化库集成。

  • Matplotlib: 最常用的可视化工具。要正确显示 OpenCV 读取的彩色图,通常需要进行颜色通道转换,因为 Matplotlib 期望的是 RGB 顺序。

    python 复制代码
    import matplotlib.pyplot as plt
    # OpenCV to RGB
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    plt.imshow(img_rgb)
    plt.show()
  • Scikit-image, SciPy: 这些库中的许多图像处理算法可以直接接收和返回 NumPy 数组,实现功能上的扩展。

使用示例

python 复制代码
import numpy as np
import time

# --- 一、 数组的创建 (Creating Arrays) ---

print("--- 1. 数组的创建 ---")

# 1.1 从 Python 列表创建 (最常用)
a = np.array([1, 2, 3])
b = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64) # 指定数据类型
print("a (一维数组):\n", a)
print("b (二维数组):\n", b)
print("b 的形状 (shape):", b.shape)
print("b 的数据类型 (dtype):", b.dtype)
print("-" * 20)

# 1.2 使用内置函数创建全零/全一/空数组
c = np.zeros((2, 3), dtype=np.int32) # 2行3列的全零矩阵
d = np.ones((1, 4))                  # 1行4列的全一矩阵 (默认 float)
e = np.empty((2, 2))                 # 创建一个元素值随机的数组 (通常用于占位)
print("c (全零):\n", c)
print("d (全一):\n", d)
print("-" * 20)

# 1.3 创建序列数组
f = np.arange(10)      # 0 到 9 的整数序列
g = np.arange(2, 10, 2) # 2 到 10 (不包含) 步长为 2
h = np.linspace(0, 1, 5) # 在 0 和 1 之间平均创建 5 个点
print("f (arange 0-9):", f)
print("h (linspace 0-1, 5点):", h)
print("-" * 20)

# 1.4 随机数组
i = np.random.rand(2, 3) # 2x3 均匀分布的随机数 (0-1)
j = np.random.randint(0, 10, size=(2, 2)) # 2x2 随机整数 (0 到 9)
print("i (随机浮点数):\n", i)
print("-" * 20)

# --- 二、 数组的索引与切片 (Indexing and Slicing) ---

print("--- 2. 数组的索引与切片 ---")

# 2.1 一维数组
arr1d = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print("arr1d:", arr1d)
print("arr1d[2]:", arr1d[2])        # 元素索引
print("arr1d[2:5]:", arr1d[2:5])    # 切片 [2, 3, 4]
print("arr1d[:5]:", arr1d[:5])      # 从开始到索引 5 之前
print("arr1d[5:]:", arr1d[5:])      # 从索引 5 到末尾
print("arr1d[::-1]:", arr1d[::-1])  # 逆序
print("-" * 20)

# 2.2 二维数组 (图像/矩阵操作的核心)
arr2d = np.array([[1, 2, 3],
                  [4, 5, 6],
                  [7, 8, 9]])
print("arr2d:\n", arr2d)
print("单个元素 arr2d[1, 2]:", arr2d[1, 2]) # 索引:第1行,第2列 (从0开始) -> 6
print("切片 arr2d[0:2, 1:3]:\n", arr2d[0:2, 1:3]) # 第0, 1行 和 第1, 2列
# 结果: [[2, 3], [5, 6]]
print("仅获取第1行 arr2d[1, :]:", arr2d[1, :]) # 获取整行
print("仅获取第2列 arr2d[:, 2]:", arr2d[:, 2]) # 获取整列 -> [3, 6, 9]
print("-" * 20)

# 2.3 布尔索引 (非常强大)
mask = arr2d > 5 # 创建布尔数组 (True/False)
print("布尔掩码:\n", mask)
print("arr2d[mask]:", arr2d[mask]) # 只保留 True 对应的元素 -> [6 7 8 9]
arr2d[arr2d < 3] = 0 # 将所有小于 3 的元素设置为 0
print("修改后的 arr2d:\n", arr2d)
print("-" * 20)

# --- 三、 数组的基本操作 (Basic Operations) ---

print("--- 3. 数组的基本操作 ---")

m1 = np.array([1, 2, 3])
m2 = np.array([4, 5, 6])

# 3.1 算术运算 (逐元素操作/Element-wise)
print("加法 m1 + m2:", m1 + m2)     # [5, 7, 9]
print("乘法 m1 * m2:", m1 * m2)     # [4, 10, 18]
print("标量运算 m1 * 2:", m1 * 2)   # [2, 4, 6]
print("-" * 20)

# 3.2 矩阵乘法 (使用 @ 或 np.dot())
matA = np.array([[1, 2], [3, 4]])
matB = np.array([[5, 6], [7, 8]])
matC = matA @ matB # 矩阵乘法
print("矩阵 A:\n", matA)
print("矩阵 B:\n", matB)
print("矩阵乘法 A @ B:\n", matC)
print("-" * 20)

# 3.3 聚合函数 (Aggregation)
arr_agg = np.array([[10, 20], [30, 40]])
print("arr_agg:\n", arr_agg)
print("总和:", arr_agg.sum())       # 100
print("最小值:", arr_agg.min())       # 10
print("按列求和 (axis=0):", arr_agg.sum(axis=0)) # [40, 60]
print("按行求和 (axis=1):", arr_agg.sum(axis=1)) # [30, 70]
print("-" * 20)

# --- 四、 数组形状操作 (Shape Manipulation) ---

print("--- 4. 数组形状操作 ---")

# 4.1 reshape (改变数组形状,不改变数据)
arr_r = np.arange(12)
arr_2x6 = arr_r.reshape(2, 6)
arr_3x4 = arr_r.reshape(3, 4)
arr_4x_1 = arr_r.reshape(4, -1) # -1 自动计算维度
print("原始数组:\n", arr_r)
print("重塑为 3x4:\n", arr_3x4)
print("-" * 20)

# 4.2 flatten (将数组展平为一维)
arr_flat = arr_3x4.flatten()
print("展平后:", arr_flat)
print("-" * 20)

# 4.3 堆叠 (Stacking)
s1 = np.array([1, 2, 3])
s2 = np.array([4, 5, 6])
v_stack = np.vstack((s1, s2)) # 垂直堆叠 (行增加)
h_stack = np.hstack((s1, s2)) # 水平堆叠 (列增加)
print("垂直堆叠 (vstack):\n", v_stack)
print("水平堆叠 (hstack):", h_stack)
print("-" * 20)

# --- 五、 NumPy 与 Python 列表的性能对比 (简要) ---

print("--- 5. 性能对比 (NumPy 优势) ---")
array_size = 1000000

# 5.1 Python 列表加法
list_a = list(range(array_size))
list_b = list(range(array_size))

t0 = time.time()
list_result = [list_a[i] + list_b[i] for i in range(array_size)]
t1 = time.time()
print(f"Python 列表加法耗时: {(t1 - t0):.4f} 秒")

# 5.2 NumPy 数组加法 (向量化操作)
np_a = np.array(list_a)
np_b = np.array(list_b)

t2 = time.time()
np_result = np_a + np_b
t3 = time.time()
print(f"NumPy 数组加法耗时: {(t3 - t2):.4f} 秒")
# NumPy 通常快数十到数百倍

总结

NumPy 不仅仅是 OpenCV 的一个依赖库,它是 OpenCV-Python 图像处理功能的基石

特性 NumPy 作用 核心意义
数据表示 将图像表示为多维数组 (ndarray) 统一、高效的数据结构
性能 实现底层 C 优化的向量化操作 显著提升图像处理速度
操作便捷性 提供强大的索引、切片和广播机制 简化复杂的像素级和区域操作
互操作性 作为科学计算领域的通用数组格式 便于与 Matplotlib、SciPy 等库集成
相关推荐
算家计算4 小时前
英伟达谷歌打响“太空算力争夺战”,下一战场竟是星辰大海?
人工智能·芯片·资讯
HyperAI超神经5 小时前
在线教程丨端侧TTS新SOTA!NeuTTS-Air基于0.5B模型实现3秒音频克隆
人工智能·深度学习·机器学习·音视频·tts·音频克隆·neutts-air
wwwzhouhui5 小时前
2025年11月1日-AI 驱动教学革命:3 分钟生成专业级动画课件,还能导出视频 GIF!
人工智能·音视频·ai动画教学
国科安芯5 小时前
抗辐照MCU芯片在无人叉车领域的性能评估与选型建议
网络·人工智能·单片机·嵌入式硬件·安全
用户5191495848455 小时前
原型污染攻击工具揭秘:Prototype Pollution Gadgets Finder
人工智能·aigc
VXHAruanjian8885 小时前
以智促效,释放创新力量,RPA助力企业全面自动化变革
大数据·人工智能
Godspeed Zhao5 小时前
自动驾驶中的传感器技术76——Navigation(13)
人工智能·机器学习·自动驾驶
CoovallyAIHub5 小时前
首届AI交易大赛对决!中国模型包揽冠亚军,GPT-5亏损62%垫底
人工智能·google·数据分析
王中阳Go5 小时前
5 - 工具调用 - AI 超级智能体项目教程
人工智能
智塑未来5 小时前
广州全运会即将开幕,获得文远知行自动驾驶技术支持
人工智能·机器学习·自动驾驶