学习 OpenCV Python:基础知识与核心概念详解 – wiki基地


学习 OpenCV Python:基础知识与核心概念详解 (约3000字)

引言:开启计算机视觉之门

在人工智能和数据科学浪潮席卷全球的今天,计算机视觉(Computer Vision, CV)作为其关键分支,正以前所未有的速度渗透到我们生活的方方面面,从人脸识别解锁手机,到自动驾驶汽车感知环境,再到医疗影像分析辅助诊断,其应用场景日益广泛。而要在 Python 这个强大的编程语言生态中驾驭计算机视觉,OpenCV (Open Source Computer Vision Library) 无疑是最核心、最流行、功能最全面的库之一。

OpenCV 是一个开源的、跨平台的计算机视觉和机器学习软件库,它包含了超过 2500 种优化的算法,涵盖了经典的计算机视觉任务和最先进的机器学习技术。结合 Python 语言的简洁性、易用性和丰富的第三方库支持,OpenCV Python 绑定(cv2 模块)成为了研究人员、开发者和爱好者进入计算机视觉领域的首选工具。

本文旨在为初学者和希望系统学习 OpenCV Python 的读者提供一份详尽的指南,深入探讨其基础知识和核心概念,助您稳步踏上计算机视觉的探索之旅。

一、 为什么选择 OpenCV 和 Python?

  1. OpenCV 的优势:

    • 功能强大: 提供了涵盖图像处理、视频分析、特征检测、目标识别、3D 重建、机器学习等众多领域的算法。
    • 性能优越: 核心代码由 C/C++ 编写,并针对多种处理器架构(包括 Intel IPP 和 CUDA)进行了优化,运行效率高。
    • 跨平台性: 支持 Windows, Linux, macOS, Android, iOS 等多种操作系统。
    • 开源免费: 遵循 BSD 许可证,可免费用于学术和商业目的。
    • 社区活跃: 拥有庞大的用户和开发者社区,文档丰富,遇到问题容易找到解决方案。
  2. Python 的优势:

    • 简洁易学: 语法清晰,代码可读性强,上手门槛相对较低。
    • 生态丰富: 拥有 NumPy, SciPy, Matplotlib, Scikit-learn, TensorFlow, PyTorch 等强大的科学计算和机器学习库,可以与 OpenCV 无缝集成,实现复杂任务。
    • 开发效率高: “胶水语言”特性使其能够快速整合不同模块,适合快速原型设计和迭代开发。
    • 广泛应用: 在数据科学、Web 开发、自动化等领域均有广泛应用,学习 Python 具有普适性。

OpenCV 与 Python 的结合,可谓强强联手,既利用了 OpenCV 底层的高性能,又享受了 Python 的开发便利性,是进行计算机视觉项目开发的理想组合。

二、 环境搭建:安装 OpenCV Python 库

在开始之前,确保您的系统已经安装了 Python(推荐 3.6 或更高版本)和 pip 包管理器。

安装 OpenCV 的 Python 绑定通常非常简单,只需在终端或命令行中执行:

bash
pip install opencv-python

这将安装包含主要模块的核心包。然而,在实际开发中,我们往往还需要一些额外的算法,特别是涉及特征检测(如 SIFT, SURF)等专利算法(在新版本中可能受限或移至主库)以及其他贡献模块。为此,推荐安装 opencv-contrib-python 包,它包含了核心包以及所有的扩展模块:

bash
pip install opencv-contrib-python

注意: 请不要同时安装 opencv-pythonopencv-contrib-python,选择后者即可。安装 opencv-contrib-python 会自动包含 opencv-python 的内容。

为了验证安装是否成功,可以在 Python 解释器中尝试导入 cv2 模块:

python
import cv2
print(cv2.__version__)

如果成功打印出 OpenCV 的版本号,则表示安装完成。

三、 核心基石:图像在 OpenCV 中的表示 —— NumPy 数组

理解 OpenCV 如何处理图像至关重要。在 OpenCV Python 中,图像被表示为 NumPy 数组(numpy.ndarray。这是学习 OpenCV 的第一个,也是最重要的核心概念。

  • 灰度图像: 一个二维 NumPy 数组,形状为 (height, width)。数组中的每个元素代表一个像素的强度值,通常是 uint8 类型(0-255),0 代表黑色,255 代表白色。
  • 彩色图像: 一个三维 NumPy 数组,形状为 (height, width, channels)
    • height:图像的高度(像素行数)。
    • width:图像的宽度(像素列数)。
    • channels:颜色通道数。OpenCV 默认使用 BGR(蓝-绿-红)颜色顺序,而不是常见的 RGB。因此,一个彩色像素通常由 3 个 uint8 值组成,分别代表蓝色、绿色和红色的强度。例如,img[y, x] 会返回一个包含 [Blue, Green, Red] 值的列表或数组。

示例:

“`python
import numpy as np
import cv2

创建一个 100×200 的黑色灰度图像

gray_img = np.zeros((100, 200), dtype=np.uint8)

创建一个 100×200 的蓝色 BGR 彩色图像

B=255, G=0, R=0

blue_img = np.zeros((100, 200, 3), dtype=np.uint8)
blue_img[:, :] = [255, 0, 0] # 将所有像素设置为蓝色

访问图像属性

print(“灰度图像形状:”, gray_img.shape) # 输出: (100, 200)
print(“彩色图像形状:”, blue_img.shape) # 输出: (100, 200, 3)
print(“图像数据类型:”, blue_img.dtype) # 输出: uint8
print(“图像总像素数:”, blue_img.size) # 输出: 100 * 200 * 3 = 60000
“`

由于图像就是 NumPy 数组,这意味着你可以直接使用 NumPy 提供的所有强大的数组操作功能来处理图像,如切片、索引、算术运算、逻辑运算等,这极大地简化了图像处理任务。

四、 基础操作:图像的读写与显示

  1. 读取图像 (cv2.imread)

    • cv2.imread(filepath, flags):从指定路径加载图像。
    • filepath:图像文件的路径。
    • flags:指定加载图像的颜色类型,常用:
      • cv2.IMREAD_COLOR (或 1):加载彩色图像(默认),忽略 Alpha 透明通道。
      • cv2.IMREAD_GRAYSCALE (或 0):以灰度模式加载图像。
      • cv2.IMREAD_UNCHANGED (或 -1):加载完整图像,包括 Alpha 通道(如果存在)。

    “`python
    img_color = cv2.imread(‘path/to/your/image.jpg’, cv2.IMREAD_COLOR)
    img_gray = cv2.imread(‘path/to/your/image.jpg’, cv2.IMREAD_GRAYSCALE)

    if img_color is None:
    print(“无法加载彩色图像”)
    if img_gray is None:
    print(“无法加载灰度图像”)
    ``
    **注意:** 如果图像路径错误或文件损坏,
    imread会返回None`,务必进行检查。

  2. 显示图像 (cv2.imshow, cv2.waitKey, cv2.destroyAllWindows)

    • cv2.imshow(window_name, image):在一个窗口中显示图像。
      • window_name:显示窗口的标题(字符串)。
      • image:要显示的 NumPy 数组图像。
    • cv2.waitKey(delay):等待键盘事件。这是 必须 的步骤,否则图像窗口可能一闪而过或无响应。
      • delay:等待时间(毫秒)。如果为 0,则无限期等待,直到用户按键。如果大于 0,则等待指定毫秒数。它返回按键的 ASCII 码,如果超时则返回 -1。常用于视频播放的帧率控制或等待用户交互。
    • cv2.destroyAllWindows():关闭所有由 OpenCV 创建的窗口。
    • cv2.destroyWindow(window_name):关闭指定的窗口。

    “`python
    cv2.imshow(‘Color Image’, img_color)
    cv2.imshow(‘Grayscale Image’, img_gray)

    print(“按任意键退出…”)
    key = cv2.waitKey(0) # 等待用户按键
    print(f”你按下的键的 ASCII 码是: {key}”)

    cv2.destroyAllWindows()
    “`

  3. 保存图像 (cv2.imwrite)

    • cv2.imwrite(filename, image):将图像保存到文件。
    • filename:保存的文件名(包含扩展名,如 .jpg, .png)。OpenCV 根据扩展名确定文件格式。
    • image:要保存的 NumPy 数组图像。

    python
    success = cv2.imwrite('output/gray_image.png', img_gray)
    if success:
    print("灰度图像保存成功!")
    else:
    print("灰度图像保存失败。")

五、 颜色空间转换 (cv2.cvtColor)

虽然 OpenCV 默认使用 BGR,但很多其他库(如 Matplotlib)或应用场景(如颜色检测)可能需要 RGB、HSV、HLS 或灰度等不同的颜色空间。

  • cv2.cvtColor(src, code):转换图像的颜色空间。
    • src:源图像。
    • code:转换代码,例如:
      • cv2.COLOR_BGR2GRAY:BGR 到灰度。
      • cv2.COLOR_BGR2RGB:BGR 到 RGB。
      • cv2.COLOR_BGR2HSV:BGR 到 HSV。
      • cv2.COLOR_RGB2BGR:RGB 到 BGR。
      • cv2.COLOR_GRAY2BGR:灰度到 BGR(会复制灰度值到三个通道)。

“`python
img_rgb = cv2.cvtColor(img_color, cv2.COLOR_BGR2RGB)
img_hsv = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)

注意:如果使用 Matplotlib 显示 OpenCV 加载的彩色图像,需要先转为 RGB

import matplotlib.pyplot as plt

plt.imshow(img_rgb)

plt.show()

“`

HSV (Hue, Saturation, Value) 颜色空间 对于基于颜色的对象分割特别有用,因为它将颜色信息(色调 H)与光照强度(饱和度 S,明度 V)分离开来。

六、 基本图像操作

  1. 访问和修改像素值: 直接使用 NumPy 索引。
    “`python
    # 获取 (y, x) 处的像素值 (假设是彩色图像)
    (b, g, r) = img_color[100, 50]
    print(f”Pixel at (50, 100) – B:{b}, G:{g}, R:{r}”)

    修改 (y, x) 处的像素值为白色

    img_color[100, 50] = [255, 255, 255]

    获取灰度图像 (y, x) 处的像素值

    intensity = img_gray[100, 50]
    print(f”Intensity at (50, 100): {intensity}”)

    修改灰度值

    img_gray[100, 50] = 0 # 设置为黑色
    ``
    **注意:** 索引顺序是
    [行(y), 列(x)]`。直接访问像素效率较低,应尽量使用 OpenCV 的内置函数或 NumPy 的向量化操作。

  2. 获取图像属性: 已在第三节介绍 (shape, size, dtype)。

  3. 图像 ROI (Region of Interest): 感兴趣区域,即图像的一个子区域。利用 NumPy 切片可以轻松获取 ROI。
    “`python
    # 选择从 (x1, y1) 到 (x2, y2) 的矩形区域
    # 注意 NumPy 切片是 [y1:y2, x1:x2]
    roi = img_color[100:200, 200:300] # 行从100到199,列从200到299
    cv2.imshow(‘ROI’, roi)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    对 ROI 的修改会直接反映在原图上,因为 ROI 是原数组的一个视图 (View)

    roi[:, :] = [0, 255, 0] # 将 ROI 区域变为绿色
    cv2.imshow(‘Modified Original’, img_color)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    如果需要独立副本,使用 .copy()

    roi_copy = img_color[100:200, 200:300].copy()
    “`

  4. 拆分和合并颜色通道 (cv2.split, cv2.merge)
    “`python
    b, g, r = cv2.split(img_color) # 拆分为三个单通道灰度图像
    cv2.imshow(‘Blue Channel’, b)
    cv2.waitKey(0)

    merged_img = cv2.merge((b, g, r)) # 重新合并通道
    cv2.imshow(‘Merged Image’, merged_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    可以将某个通道置零

    img_no_blue = img_color.copy()
    img_no_blue[:, :, 0] = 0 # 将蓝色通道设置为 0
    cv2.imshow(‘No Blue’, img_no_blue)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    “`

  5. 图像缩放 (cv2.resize)

    • cv2.resize(src, dsize, fx=0, fy=0, interpolation=cv2.INTER_LINEAR)
      • src:输入图像。
      • dsize:目标尺寸 (width, height)。如果设置为 None,则尺寸由 fxfy 决定。
      • fx, fy:沿 x 轴和 y 轴的缩放比例。如果 dsize 不是 None,则忽略这两个参数。
      • interpolation:插值方法,常用的有:
        • cv2.INTER_NEAREST:最近邻插值(速度快,效果差)。
        • cv2.INTER_LINEAR:双线性插值(默认,常用)。
        • cv2.INTER_CUBIC:双三次插值(效果更好,速度较慢)。
        • cv2.INTER_AREA:区域插值(用于缩小图像,效果好)。

    “`python
    height, width = img_color.shape[:2]

    按绝对尺寸缩放

    resized_abs = cv2.resize(img_color, (int(width0.5), int(height0.5)), interpolation=cv2.INTER_AREA)

    按比例缩放

    resized_rel = cv2.resize(img_color, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)

    cv2.imshow(‘Original’, img_color)
    cv2.imshow(‘Resized Absolute’, resized_abs)
    cv2.imshow(‘Resized Relative’, resized_rel)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    “`

  6. 图像旋转与平移(仿射变换 cv2.warpAffine
    平移、旋转、缩放等几何变换可以通过仿射变换实现。这需要定义一个 2×3 的变换矩阵 M

    • 平移:
      python
      rows, cols = img_color.shape[:2]
      tx, ty = 100, 50 # 向右平移 100 像素,向下平移 50 像素
      M_trans = np.float32([[1, 0, tx], [0, 1, ty]])
      img_translated = cv2.warpAffine(img_color, M_trans, (cols, rows))
      cv2.imshow('Translated', img_translated)
      cv2.waitKey(0)
      cv2.destroyAllWindows()
    • 旋转: OpenCV 提供了更方便的函数 cv2.getRotationMatrix2D 来生成旋转矩阵。
      python
      center = (cols // 2, rows // 2) # 旋转中心
      angle = 45 # 旋转角度(逆时针为正)
      scale = 1.0 # 缩放因子
      M_rot = cv2.getRotationMatrix2D(center, angle, scale)
      img_rotated = cv2.warpAffine(img_color, M_rot, (cols, rows))
      cv2.imshow('Rotated', img_rotated)
      cv2.waitKey(0)
      cv2.destroyAllWindows()
  7. 在图像上绘制图形和文字
    OpenCV 提供了绘制线段、矩形、圆形、椭圆、多边形和文字的功能。这些函数会直接修改输入的图像。

    • cv2.line(img, pt1, pt2, color, thickness)
    • cv2.rectangle(img, pt1, pt2, color, thickness) (pt1: 左上角, pt2: 右下角)
    • cv2.circle(img, center, radius, color, thickness) (thickness=-1 表示填充)
    • cv2.putText(img, text, org, fontFace, fontScale, color, thickness, lineType)

    “`python
    draw_img = img_color.copy() # 创建副本进行绘制

    绘制绿色直线

    cv2.line(draw_img, (0, 0), (cols//2, rows//2), (0, 255, 0), 5) # BGR

    绘制蓝色矩形

    cv2.rectangle(draw_img, (100, 100), (300, 250), (255, 0, 0), 3)

    绘制红色填充圆形

    cv2.circle(draw_img, (400, 150), 50, (0, 0, 255), -1)

    添加文字

    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(draw_img, ‘OpenCV Basics’, (50, 350), font, 1.5, (255, 255, 255), 2, cv2.LINE_AA)

    cv2.imshow(‘Drawing Demo’, draw_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    “`

七、 核心图像处理技术

  1. 阈值处理 (cv2.threshold, cv2.adaptiveThreshold)
    阈值处理是图像分割的一种简单而有效的方法,用于将图像转换为二值图像(只有两种像素值,通常是黑和白)。

    • 简单阈值 (cv2.threshold): 全局应用一个阈值。
      “`python
      # ret: 实际使用的阈值(对于 Otsu 方法有用)
      # thresholded_img: 输出的二值图像
      ret, thresh_binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
      # >127 的像素变为 255 (maxval), <=127 的变为 0
      ret, thresh_binary_inv = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
      # >127 的像素变为 0, <=127 的变为 255

      Otsu’s Binarization: 自动寻找最优阈值(假设图像直方图有双峰)

      ret_otsu, thresh_otsu = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

      cv2.imshow(‘Binary Threshold’, thresh_binary)
      cv2.imshow(‘Otsu Threshold’, thresh_otsu)
      cv2.waitKey(0)
      cv2.destroyAllWindows()
      * **自适应阈值 (`cv2.adaptiveThreshold`):** 对图像不同区域使用不同的阈值,适用于光照不均的图像。python

      cv2.ADAPTIVE_THRESH_MEAN_C: 邻域均值

      cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 邻域高斯加权和

      blockSize: 计算阈值的邻域大小 (必须是奇数)

      C: 从均值或加权和中减去的常数

      thresh_adaptive_mean = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
      cv2.THRESH_BINARY, 11, 2)
      thresh_adaptive_gaussian = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
      cv2.THRESH_BINARY, 11, 2)
      cv2.imshow(‘Adaptive Mean’, thresh_adaptive_mean)
      cv2.imshow(‘Adaptive Gaussian’, thresh_adaptive_gaussian)
      cv2.waitKey(0)
      cv2.destroyAllWindows()
      “`

  2. 图像平滑/模糊 (cv2.blur, cv2.GaussianBlur, cv2.medianBlur, cv2.bilateralFilter)
    用于降噪和模糊图像。

    • 均值滤波 (cv2.blur): 用邻域像素的平均值代替中心像素。
    • 高斯滤波 (cv2.GaussianBlur): 用高斯核进行加权平均,离中心越近的像素权重越大。对高斯噪声效果好。
    • 中值滤波 (cv2.medianBlur): 用邻域像素的中值代替中心像素。对椒盐噪声效果极佳。
    • 双边滤波 (cv2.bilateralFilter): 在平滑的同时保持边缘清晰。考虑了空间距离和像素值差异。

    “`python

    核大小,必须是正奇数

    kernel_size = (5, 5)

    blurred_avg = cv2.blur(img_color, kernel_size)
    blurred_gaussian = cv2.GaussianBlur(img_color, kernel_size, 0) # sigmaX=0, OpenCV 会自动计算

    中值滤波的核大小是一个整数 k, 表示 kxk 的核

    blurred_median = cv2.medianBlur(img_color, 5)

    双边滤波参数: d(邻域直径), sigmaColor(颜色空间标准差), sigmaSpace(坐标空间标准差)

    blurred_bilateral = cv2.bilateralFilter(img_color, 9, 75, 75)

    cv2.imshow(‘Gaussian Blur’, blurred_gaussian)
    cv2.imshow(‘Median Blur’, blurred_median)
    cv2.imshow(‘Bilateral Filter’, blurred_bilateral)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    “`

  3. 形态学操作 (cv2.erode, cv2.dilate, cv2.morphologyEx)
    基于形状的图像处理技术,通常作用于二值图像。需要定义一个结构元素 (Structuring Element)

    • 腐蚀 (cv2.erode): “收缩” 物体边界,可以去除小的噪点。
    • 膨胀 (cv2.dilate): “扩张” 物体边界,可以填充物体内部的小孔洞,连接断开的区域。
    • 开运算 (cv2.MORPH_OPEN): 先腐蚀后膨胀。去除小的噪点(类似腐蚀),同时基本保持物体大小(类似膨胀)。
    • 闭运算 (cv2.MORPH_CLOSE): 先膨胀后腐蚀。填充物体内部的小孔洞,连接邻近物体(类似膨胀),同时基本保持物体大小(类似腐蚀)。
    • 形态学梯度 (cv2.MORPH_GRADIENT): 膨胀图减腐蚀图,得到物体轮廓。
    • 顶帽 (cv2.MORPH_TOPHAT): 原图减开运算图,得到噪声或小亮点。
    • 黑帽 (cv2.MORPH_BLACKHAT): 闭运算图减原图,得到物体内部的小孔洞或暗点。

    “`python

    创建结构元素 (例如 5×5 的矩形)

    kernel = np.ones((5, 5), np.uint8)

    或者使用 cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))

    cv2.MORPH_ELLIPSE, cv2.MORPH_CROSS

    img_binary = thresh_otsu # 使用前面得到的二值图

    eroded = cv2.erode(img_binary, kernel, iterations=1)
    dilated = cv2.dilate(img_binary, kernel, iterations=1)
    opened = cv2.morphologyEx(img_binary, cv2.MORPH_OPEN, kernel)
    closed = cv2.morphologyEx(img_binary, cv2.MORPH_CLOSE, kernel)
    gradient = cv2.morphologyEx(img_binary, cv2.MORPH_GRADIENT, kernel)

    cv2.imshow(‘Original Binary’, img_binary)
    cv2.imshow(‘Eroded’, eroded)
    cv2.imshow(‘Dilated’, dilated)
    cv2.imshow(‘Opened’, opened)
    cv2.imshow(‘Closed’, closed)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    “`

  4. 边缘检测 (Canny 算法 cv2.Canny)
    Canny 是最常用、效果也较好的边缘检测算法之一。

    • cv2.Canny(image, threshold1, threshold2)
      • image:通常是灰度图。
      • threshold1, threshold2:低阈值和高阈值(用于滞后阈值处理)。梯度大于 threshold2 的像素被视为强边缘,梯度在两者之间的像素如果连接到强边缘则被视为边缘。推荐 threshold2threshold1 的 2 到 3 倍。

    python
    edges = cv2.Canny(img_gray, 100, 200) # 调整阈值观察效果
    cv2.imshow('Canny Edges', edges)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

  5. 轮廓检测与绘制 (cv2.findContours, cv2.drawContours)
    轮廓可以看作是连接了所有具有相同颜色或强度的连续点的曲线。常用于物体检测和识别。

    • cv2.findContours(image, mode, method)

      • image:通常是二值图像(Canny 边缘图或阈值图)。注意:此函数会修改源图像,如果需要保留原图,请传入副本 (image.copy())。
      • mode:轮廓检索模式。常用:
        • cv2.RETR_EXTERNAL:只检索最外层轮廓。
        • cv2.RETR_LIST:检索所有轮廓,不建立父子关系。
        • cv2.RETR_TREE:检索所有轮廓,并建立完整的层级结构。
      • method:轮廓逼近方法。常用:
        • cv2.CHAIN_APPROX_NONE:存储所有轮廓点。
        • cv2.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角线段,只存储端点。节省内存。
      • 返回值: contours (轮廓列表,每个轮廓是点的 NumPy 数组), hierarchy (轮廓间的层级关系)。在新版 OpenCV (4.x+) 中,返回值可能只有 contourshierarchy 两项。
    • cv2.drawContours(image, contours, contourIdx, color, thickness)

      • image:在其上绘制轮廓的图像(通常是原彩色图)。
      • contoursfindContours 返回的轮廓列表。
      • contourIdx:要绘制的轮廓索引(-1 表示绘制所有轮廓)。
      • color, thickness:绘制的颜色和线条粗细。

    “`python

    使用 Canny 边缘图查找轮廓

    contours, hierarchy = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    print(f”找到了 {len(contours)} 个外部轮廓。”)

    在原彩色图上绘制轮廓

    contour_img = img_color.copy()
    cv2.drawContours(contour_img, contours, -1, (0, 255, 0), 2) # 绘制所有轮廓,绿色,粗细为 2

    cv2.imshow(‘Contours’, contour_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    还可以计算轮廓的面积、周长、边界框、最小外接圆/矩形等

    for cnt in contours:

    area = cv2.contourArea(cnt)

    perimeter = cv2.arcLength(cnt, True) # True表示闭合轮廓

    x, y, w, h = cv2.boundingRect(cnt) # 边界框

    cv2.rectangle(contour_img, (x, y), (x+w, y+h), (0, 0, 255), 1)

    “`

  6. 直方图 (cv2.calcHist, Matplotlib 可视化)
    图像直方图是像素强度分布的图形表示。

    • cv2.calcHist([images], [channels], mask, [histSize], ranges)
      • [images]: 源图像列表 (用方括号括起来)。
      • [channels]: 要计算直方图的通道索引列表。灰度图是 [0],彩色图 BGR 分别是 [0], [1], [2]
      • mask: 掩码图像 (可选,只统计掩码区域)。
      • [histSize]: BINS 的数量 (每个维度)。例如 [256]
      • ranges: 像素值范围。例如 [0, 256] (不包括 256)。

    “`python
    import matplotlib.pyplot as plt

    计算灰度直方图

    hist_gray = cv2.calcHist([img_gray], [0], None, [256], [0, 256])

    计算彩色图像各通道直方图

    color = (‘b’, ‘g’, ‘r’)
    plt.figure()
    plt.title(‘Color Histogram’)
    plt.xlabel(‘Bins’)
    plt.ylabel(‘# of Pixels’)
    for i, col in enumerate(color):
    hist_color = cv2.calcHist([img_color], [i], None, [256], [0, 256])
    plt.plot(hist_color, color=col)
    plt.xlim([0, 256])

    plt.figure()
    plt.title(‘Grayscale Histogram’)
    plt.xlabel(‘Bins’)
    plt.ylabel(‘# of Pixels’)
    plt.plot(hist_gray)
    plt.xlim([0, 256])

    plt.show()

    直方图均衡化 (改善对比度)

    equ_gray = cv2.equalizeHist(img_gray)
    cv2.imshow(‘Original Gray’, img_gray)
    cv2.imshow(‘Equalized Gray’, equ_gray)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    “`

八、 处理视频

OpenCV 可以轻松处理视频文件或来自摄像头的实时视频流。视频本质上是连续的图像帧序列。

  1. 从摄像头捕获 (cv2.VideoCapture(0))
    0 通常代表默认的内置摄像头。如果有多个摄像头,可以尝试 1, 2 等。
  2. 从视频文件读取 (cv2.VideoCapture('video.mp4'))
    指定视频文件路径。

“`python

从摄像头捕获

cap = cv2.VideoCapture(0)

或者从文件读取

cap = cv2.VideoCapture(‘my_video.avi’)

if not cap.isOpened():
print(“错误:无法打开视频源”)
exit()

while True:
# 逐帧读取
# ret: 读取是否成功 (True/False)
# frame: 读取到的当前帧 (NumPy 数组)
ret, frame = cap.read()

# 如果正确读取帧 ret 为 True
if not ret:
    print("无法接收帧 (视频流结束?)。正在退出...")
    break

# 在这里对 frame 进行处理 (例如转灰度, Canny边缘检测等)
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
edges_frame = cv2.Canny(gray_frame, 50, 150)

# 显示处理后的帧
cv2.imshow('Original Video', frame)
cv2.imshow('Edges Video', edges_frame)

# 按 'q' 键退出循环
# waitKey(1) 等待 1ms, 使得视频能正常播放,同时也检测按键
if cv2.waitKey(1) == ord('q'):
    break

释放资源

cap.release()
cv2.destroyAllWindows()
“`

  1. 保存视频 (cv2.VideoWriter)
    需要指定输出文件名、编解码器 (FourCC)、帧率 (fps) 和帧大小。

    “`python

    …(在 cap = cv2.VideoCapture(0) 之后)…

    frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = int(cap.get(cv2.CAP_PROP_FPS)) # 获取原始帧率可能不准确,有时需手动设置

    定义编解码器并创建 VideoWriter 对象

    FourCC 是一个4字节码,用于指定视频编解码器

    常见: ‘XVID’, ‘MJPG’, ‘MP4V’, ‘DIVX’ (可能需安装对应解码器)

    在 Windows 上 ‘XVID’ 通常可用,在 Linux/macOS 上 ‘MJPG’ 或 ‘MP4V’ 可能更好

    fourcc = cv2.VideoWriter_fourcc(*’XVID’)
    out = cv2.VideoWriter(‘output/output_video.avi’, fourcc, 20.0, (frame_width, frame_height)) # 使用 20fps 保存

    …(在 while 循环内部,处理完 frame 之后)…

    if ret:
    # 写入处理后的帧 (确保写入的帧尺寸与 VideoWriter 初始化时一致)
    # 如果处理改变了尺寸或通道数,需要调整
    # 例如,如果想保存灰度视频,需要先将 gray_frame 转回 BGR
    # gray_bgr_frame = cv2.cvtColor(gray_frame, cv2.COLOR_GRAY2BGR)
    # out.write(gray_bgr_frame)

    # 保存原始彩色帧
    out.write(frame)
    
    cv2.imshow('Frame', frame)
    if cv2.waitKey(1) == ord('q'):
        break
    

    else:
    break

    …(在循环结束后)…

    cap.release()
    out.release() # 释放 VideoWriter 对象
    cv2.destroyAllWindows()
    “`

九、 进阶之路:下一步学什么?

掌握了以上基础知识和核心概念后,您已经具备了使用 OpenCV Python 进行基本计算机视觉任务的能力。后续可以深入探索以下方向:

  1. 特征检测与匹配: SIFT, SURF, ORB, AKAZE 等算法,用于图像匹配、目标跟踪、全景拼接。
  2. 目标检测:
    • 传统方法: Haar 级联分类器, HOG (Histogram of Oriented Gradients)。
    • 深度学习方法: SSD (Single Shot MultiBox Detector), YOLO (You Only Look Once), Faster R-CNN 等,通常需要结合 TensorFlow, PyTorch 等框架。
  3. 图像分割: GrabCut, 分水岭算法, 以及基于深度学习的语义分割、实例分割。
  4. 光流估计: 跟踪视频中像素点的运动。
  5. 机器学习集成: 使用 OpenCV 内置的 SVM, K-Means 等,或结合 Scikit-learn 进行更复杂的分类、聚类任务。
  6. 3D 视觉: 相机标定, 立体视觉, Structure from Motion (SfM)。
  7. 性能优化: 利用 T-API (Transparent API) 加速,或直接在 C++ 中实现性能关键部分。

结语

OpenCV Python 是一个强大而迷人的工具,它为我们打开了理解和交互视觉世界的大门。本文从环境搭建、核心概念(图像即 NumPy 数组)、基础操作(读写、显示、颜色、几何变换、绘图)到核心图像处理技术(阈值、滤波、形态学、边缘、轮廓、直方图)以及视频处理,为您构建了一个相对完整的知识框架。

学习计算机视觉是一个持续实践和探索的过程。最重要的不是记住所有函数的参数,而是理解每个操作背后的原理、适用场景以及它们如何组合起来解决实际问题。动手编写代码,处理不同的图像和视频,尝试各种参数组合,是巩固知识、提升技能的最佳途径。希望本文能成为您 OpenCV Python 学习道路上的坚实起点,祝您在计算机视觉的奇妙世界中探索愉快!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部