OpenCV Python 入门教程


OpenCV Python 入门教程:开启计算机视觉之旅

摘要: 本文是一篇面向初学者的 OpenCV Python 入门教程。我们将从 OpenCV 的基本概念讲起,涵盖环境搭建、图像与视频的基本操作、常见的图像处理技术(如颜色空间转换、阈值处理、滤波、形态学操作、边缘检测等),以及简单的绘图功能和人脸检测应用。本文旨在提供一个全面而详细的起点,帮助读者快速掌握 OpenCV Python 的基础知识,并为后续深入学习计算机视觉打下坚实的基础。

关键词: OpenCV, Python, 计算机视觉, 图像处理, 视频处理, 入门教程


1. 引言:什么是 OpenCV?为什么选择 Python?

1.1 计算机视觉(Computer Vision, CV)简介

计算机视觉是一门研究如何让计算机“看”懂世界的科学。它涉及到图像和视频的获取、处理、分析和理解,目标是模拟人类视觉系统的能力,甚至在某些方面超越人类。如今,计算机视觉技术已经广泛应用于人脸识别、自动驾驶、医学影像分析、安防监控、增强现实等众多领域。

1.2 OpenCV:开源计算机视觉库

OpenCV (Open Source Computer Vision Library) 是一个跨平台的开源计算机视觉和机器学习软件库。它诞生于 1999 年的 Intel 研究项目,如今已成为全球最流行、功能最强大的计算机视觉库之一。

OpenCV 包含数千种优化的算法,涵盖了经典的计算机视觉任务和最先进的机器学习方法。其核心优势在于:

  • 开源免费: 遵循 BSD 许可,可免费用于学术和商业用途。
  • 跨平台: 支持 Windows, Linux, macOS, Android, iOS 等主流操作系统。
  • 多语言接口: 提供 C++, Python, Java, MATLAB 等多种编程语言的接口。
  • 功能丰富: 包含了从基础图像处理到高级视觉分析的大量算法模块。
  • 性能优化: 许多底层算法使用 C/C++ 实现,并针对多核处理器进行了优化,运行效率高。
  • 庞大的社区: 拥有活跃的开发者社区和丰富的学习资源。

1.3 为什么选择 Python 接口 (cv2)?

虽然 OpenCV 的核心是 C++,但其 Python 接口(通常通过 import cv2 导入)因其简洁易用而广受欢迎,尤其是在快速原型开发和研究领域。选择 Python 接口的理由包括:

  • 简洁的语法: Python 语法简单直观,代码量通常比 C++ 少很多,易于学习和使用。
  • 强大的生态: Python 拥有 NumPy, SciPy, Matplotlib, Pandas, Scikit-learn, TensorFlow, PyTorch 等强大的科学计算和机器学习库,可以与 OpenCV 无缝集成,构建复杂的视觉应用。
  • 快速开发: Python 的解释性特性和丰富的库支持大大缩短了开发周期。
  • 胶水语言特性: Python 很容易将不同语言编写的模块(如 OpenCV 的 C++ 核心)粘合在一起。

对于初学者而言,OpenCV Python 是进入计算机视觉领域的理想选择。本教程将专注于使用 Python 接口来探索 OpenCV 的世界。

2. 环境搭建:安装 OpenCV Python

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

安装 OpenCV Python 包通常非常简单,可以通过 pip 在命令行(终端)中执行:

bash
pip install opencv-python

这个命令会安装核心的 OpenCV 模块。OpenCV 还有一个包含额外功能(如 SIFT、SURF 等专利算法,以及一些实验性模块)的 opencv-contrib-python 包。如果需要这些额外功能,可以安装它(它会自动包含 opencv-python):

bash
pip install opencv-contrib-python

对于大多数入门教程和基本应用,opencv-python 就足够了。

验证安装:

打开 Python 解释器或创建一个 Python 文件,输入以下代码:

```python
import cv2
import numpy as np # OpenCV 严重依赖 NumPy

打印 OpenCV 版本号

print(f"OpenCV Version: {cv2.version}")
print(f"NumPy Version: {np.version}")
```

如果能够成功导入 cv2numpy 并打印出版本号,说明安装成功。NumPy 是进行数值计算的基础库,OpenCV 中的图像在 Python 中表示为 NumPy 数组,因此 NumPy 是必不可少的依赖。通常 pip install opencv-python 会自动安装 NumPy。

3. 核心概念:图像的表示与操作

3.1 图像的本质:NumPy 数组

在 OpenCV Python 中,图像被表示为 NumPy 多维数组(ndarray)。理解这一点至关重要:

  • 灰度图像 (Grayscale Image): 是一个二维数组,每个元素代表一个像素的强度值(通常范围是 0 到 255,0 代表黑色,255 代表白色)。数组的形状是 (height, width)
  • 彩色图像 (Color Image): 是一个三维数组。最常见的颜色模型是 BGR(蓝、绿、红)。数组的形状是 (height, width, channels),其中 channels 通常为 3(对应 B, G, R 三个通道)。注意:OpenCV 默认使用 BGR 顺序,而不是常见的 RGB 顺序! 这点在与其他库(如 Matplotlib)交互时需要特别注意。

3.2 加载、显示和保存图像

这是使用 OpenCV 最基本的操作。

```python
import cv2
import numpy as np
import os # 用于文件路径操作

--- 1. 加载图像 ---

cv2.imread(filepath, flags)

filepath: 图像文件的路径

flags: 加载模式

cv2.IMREAD_COLOR (1): 加载彩色图像(默认),忽略透明度。

cv2.IMREAD_GRAYSCALE (0): 以灰度模式加载图像。

cv2.IMREAD_UNCHANGED (-1): 加载完整图像,包括 alpha 通道(透明度)。

image_path = 'path/to/your/image.jpg' # 替换成你的图片路径

检查文件是否存在

if not os.path.exists(image_path):
print(f"错误:找不到图像文件 {image_path}")
# 可以创建一个假的黑色图像继续演示
img_color = np.zeros((300, 400, 3), dtype=np.uint8) # 高300, 宽400, 3通道, 8位无符号整数
img_gray = np.zeros((300, 400), dtype=np.uint8)
else:
img_color = cv2.imread(image_path, cv2.IMREAD_COLOR)
img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

检查图像是否成功加载

if img_color is None:
print(f"错误:无法加载彩色图像 {image_path}")
# 可能文件损坏或格式不支持
else:
print(f"彩色图像加载成功,形状: {img_color.shape}, 数据类型: {img_color.dtype}")

if img_gray is None:
print(f"错误:无法加载灰度图像 {image_path}")
else:
print(f"灰度图像加载成功,形状: {img_gray.shape}, 数据类型: {img_gray.dtype}")

--- 2. 显示图像 ---

cv2.imshow(windowname, image)

windowname: 显示窗口的标题

image: 要显示的图像 (NumPy 数组)

if img_color is not None:
cv2.imshow('Color Image', img_color)
if img_gray is not None:
cv2.imshow('Grayscale Image', img_gray)

cv2.waitKey(delay)

delay: 等待键盘事件的毫秒数。

如果 delay <= 0,表示无限等待,直到用户按下任意键。

如果 delay > 0,表示等待 delay 毫秒。如果期间有按键,返回按键的 ASCII 码;否则返回 -1。

这是让图像窗口保持显示的关键!否则窗口会一闪而过。

print("按任意键关闭所有窗口...")
key = cv2.waitKey(0) # 无限等待按键
print(f"你按下的键的 ASCII 码是: {key}") # 可以根据按键执行不同操作

cv2.destroyAllWindows()

关闭所有由 OpenCV 创建的窗口。

也可以用 cv2.destroyWindow(windowname) 关闭指定窗口。

cv2.destroyAllWindows()
print("窗口已关闭。")

--- 3. 保存图像 ---

cv2.imwrite(filename, image)

filename: 要保存的文件名(包含扩展名,如 .png, .jpg)

image: 要保存的图像 (NumPy 数组)

返回值: 成功返回 True,失败返回 False

if img_color is not None:
output_path_color = 'output_color.png' # 保存为 PNG 格式
success_color = cv2.imwrite(output_path_color, img_color)
if success_color:
print(f"彩色图像已保存到 {output_path_color}")
else:
print(f"保存彩色图像失败。")

if img_gray is not None:
output_path_gray = 'output_gray.jpg' # 保存为 JPG 格式
success_gray = cv2.imwrite(output_path_gray, img_gray)
if success_gray:
print(f"灰度图像已保存到 {output_path_gray}")
else:
print(f"保存灰度图像失败。")
```

要点总结:

  • cv2.imread() 加载图像。注意文件路径和加载模式。
  • 图像是 NumPy 数组。可以通过 .shape 获取维度 (高, 宽, 通道数),.dtype 获取数据类型(通常是 uint8)。
  • cv2.imshow() 显示图像,需要配合 cv2.waitKey() 才能让窗口停留。
  • cv2.waitKey(0) 等待用户按键,cv2.waitKey(1) 在视频处理循环中常用,表示等待 1ms。
  • cv2.destroyAllWindows() 关闭所有窗口,是个好习惯。
  • cv2.imwrite() 保存图像,扩展名决定了保存格式。

3.3 访问和修改像素值

由于图像是 NumPy 数组,可以直接通过索引访问和修改像素值。

```python

假设 img_color 是一个已加载的 BGR 彩色图像

(注意:确保图像加载成功且不为 None)

if img_color is not None:
height, width, channels = img_color.shape
print(f"图像尺寸: 高={height}, 宽={width}, 通道数={channels}")

# --- 访问像素 ---
# 访问坐标 (x=100, y=50) 的像素 (注意 NumPy 索引是 [row, col],即 [y, x])
px_y, px_x = 50, 100
if 0 <= px_y < height and 0 <= px_x < width:
    pixel_bgr = img_color[px_y, px_x]
    print(f"像素 ({px_x}, {px_y}) 的 BGR 值: {pixel_bgr}")

    # 单独访问 B, G, R 通道
    blue = img_color[px_y, px_x, 0]
    green = img_color[px_y, px_x, 1]
    red = img_color[px_y, px_x, 2]
    print(f"B={blue}, G={green}, R={red}")

    # 对于灰度图像 (img_gray)
    if img_gray is not None and 0 <= px_y < img_gray.shape[0] and 0 <= px_x < img_gray.shape[1]:
        intensity = img_gray[px_y, px_x]
        print(f"像素 ({px_x}, {px_y}) 的灰度值: {intensity}")
else:
    print(f"坐标 ({px_x}, {px_y}) 超出图像范围。")

# --- 修改像素 ---
# 将 (x=10, y=20) 的像素改为白色 (BGR: [255, 255, 255])
mod_y, mod_x = 20, 10
if 0 <= mod_y < height and 0 <= mod_x < width:
    img_color[mod_y, mod_x] = [255, 255, 255]
    print(f"已将像素 ({mod_x}, {mod_y}) 修改为白色。")

# 更高效的方式:使用 item() 和 itemset() (推荐用于访问单个像素)
if 0 <= px_y < height and 0 <= px_x < width:
    blue_val = img_color.item(px_y, px_x, 0) # 获取蓝色通道值
    print(f"使用 item() 获取的蓝色值: {blue_val}")
    # 设置 (x=15, y=25) 处的绿色通道值为 0
    set_y, set_x = 25, 15
    if 0 <= set_y < height and 0 <= set_x < width:
        img_color.itemset((set_y, set_x, 1), 0) # (y, x, channel_index), value
        print(f"已使用 itemset() 将像素 ({set_x}, {set_y}) 的绿色通道设为 0。")

# --- 获取图像属性 ---
print(f"图像形状 (height, width, channels): {img_color.shape}")
print(f"像素总数: {img_color.size}") # height * width * channels
print(f"图像数据类型: {img_color.dtype}")

# 显示修改后的图像 (如果进行了修改)
cv2.imshow('Modified Image', img_color)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print("彩色图像未成功加载,跳过像素操作。")

```

注意: 直接使用 Python 循环逐个访问像素效率较低。对于需要处理整张图像的操作,应优先使用 OpenCV 提供的函数或 NumPy 的向量化操作,它们底层通常由 C++ 实现,速度快得多。

3.4 图像区域 (Region of Interest, ROI)

ROI 是指图像中你感兴趣的一块矩形区域。在 NumPy 中,可以通过数组切片轻松提取 ROI。

```python

假设 img_color 是一个已加载的 BGR 彩色图像

if img_color is not None:
# 定义 ROI 的左上角和右下角坐标 (x1, y1) 和 (x2, y2)
x1, y1 = 100, 80
x2, y2 = 250, 200

# 检查坐标是否有效
height, width = img_color.shape[:2]
if 0 <= x1 < x2 <= width and 0 <= y1 < y2 <= height:
    # 使用 NumPy 切片提取 ROI (注意顺序是 [y1:y2, x1:x2])
    roi = img_color[y1:y2, x1:x2]

    # 显示 ROI
    cv2.imshow('ROI', roi)

    # --- 对 ROI 进行操作 ---
    # 例如,将 ROI 区域变为其反色 (简单的颜色反转)
    # 注意:直接修改 roi 会影响原始图像 img_color,因为切片是视图 (view) 而非拷贝 (copy)
    # roi = 255 - roi # 这会修改 img_color 的对应区域
    # cv2.imshow('Modified Original Image', img_color)

    # 如果想在不改变原图的情况下操作 ROI,需要先拷贝
    roi_copy = roi.copy()
    roi_copy = 255 - roi_copy
    cv2.imshow('Inverted ROI Copy', roi_copy)

    # --- 将 ROI 粘贴到图像的其他位置 ---
    paste_x, paste_y = 0, 0 # 目标位置的左上角
    roi_height, roi_width = roi.shape[:2]
    paste_x2, paste_y2 = paste_x + roi_width, paste_y + roi_height

    # 确保粘贴区域不越界
    if 0 <= paste_y2 <= height and 0 <= paste_x2 <= width:
        img_color[paste_y:paste_y2, paste_x:paste_x2] = roi # 将原始 ROI 粘贴过去
        cv2.imshow('Pasted ROI', img_color)
    else:
        print("粘贴区域超出图像边界。")

    cv2.waitKey(0)
    cv2.destroyAllWindows()
else:
    print("ROI 坐标无效。")

else:
print("彩色图像未成功加载,跳过 ROI 操作。")
```

3.5 颜色通道的拆分与合并

有时需要单独处理 B, G, R 各个通道。

```python

假设 img_color 是一个已加载的 BGR 彩色图像

if img_color is not None:
# 拆分通道
b, g, r = cv2.split(img_color)

# 显示各个通道 (它们是灰度图像)
cv2.imshow('Blue Channel', b)
cv2.imshow('Green Channel', g)
cv2.imshow('Red Channel', r)
cv2.waitKey(1) # 短暂等待,确保窗口显示

# --- 合并通道 ---
# 注意合并顺序 B, G, R
merged_img = cv2.merge((b, g, r))
cv2.imshow('Merged Image', merged_img)
cv2.waitKey(1)

# --- 示例:将蓝色通道置零 ---
zeros = np.zeros(img_color.shape[:2], dtype=img_color.dtype) # 创建与图像同尺寸的零矩阵
img_no_blue = cv2.merge((zeros, g, r)) # B 通道用 0 替换
cv2.imshow('Image without Blue Channel', img_no_blue)

cv2.waitKey(0)
cv2.destroyAllWindows()

# --- 更高效的方式 (NumPy 索引) ---
img_copy = img_color.copy()
img_copy[:, :, 0] = 0 # 将所有像素的蓝色通道 (索引 0) 设置为 0
cv2.imshow('Image without Blue (NumPy)', img_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print("彩色图像未成功加载,跳过通道操作。")
```

cv2.split() 相对耗时,如果只是想访问或修改某个通道,使用 NumPy 索引(如 img_color[:,:,0])通常更高效。

4. 基本图像处理技术

4.1 颜色空间转换

OpenCV 支持多种颜色空间之间的转换,最常用的是 BGR 与灰度 (Grayscale) 和 HSV (Hue, Saturation, Value) 之间的转换。

  • 灰度: 简化图像信息,常用于后续处理,如边缘检测、特征提取。
  • HSV: 对光照变化相对不敏感。Hue (色调) 代表颜色本身,Saturation (饱和度) 代表颜色的纯度,Value (明度) 代表颜色的亮度。HSV 空间在基于颜色的对象分割中非常有用。

```python
import cv2
import numpy as np

假设 img_color 是一个已加载的 BGR 彩色图像

(如果 img_color is None,需要先加载或创建图像)

if img_color is not None:
# --- BGR 转 灰度 ---
gray_image = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)
cv2.imshow('Grayscale', gray_image)

# --- BGR 转 HSV ---
hsv_image = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)
cv2.imshow('HSV', hsv_image)

# 可以查看 HSV 各通道
h, s, v = cv2.split(hsv_image)
cv2.imshow('Hue', h)
cv2.imshow('Saturation', s)
cv2.imshow('Value', v)

# --- 示例:基于 HSV 颜色分割 (例如,提取蓝色物体) ---
# 定义蓝色的 HSV 范围 (注意:Hue 的范围通常是 0-179 在 OpenCV 中)
lower_blue = np.array([100, 50, 50])  # 蓝色下界 (低 H, 低 S, 低 V)
upper_blue = np.array([130, 255, 255]) # 蓝色上界 (高 H, 高 S, 高 V)

# 根据阈值创建掩码 (mask),在范围内的像素为白色 (255),否则为黑色 (0)
mask = cv2.inRange(hsv_image, lower_blue, upper_blue)
cv2.imshow('Blue Mask', mask)

# 使用掩码提取原图中的蓝色区域
# cv2.bitwise_and(src1, src2, mask=mask) 对掩码为白色的区域进行按位与操作
blue_objects = cv2.bitwise_and(img_color, img_color, mask=mask)
cv2.imshow('Detected Blue Objects', blue_objects)

cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print("彩色图像未成功加载,跳过颜色空间转换。")
```

cv2.cvtColor() 是进行颜色空间转换的核心函数。cv2.inRange()cv2.bitwise_and() 组合是实现基于颜色的对象分割的常用方法。

4.2 图像阈值处理

阈值处理是将灰度图像转换为二值图像(只有两种像素值,通常是黑和白)的过程。常用于物体分割。

```python
import cv2
import numpy as np

需要先加载或创建灰度图像 img_gray

如果只有彩色图 img_color,先转换: img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)

if img_gray is not None:
# --- 1. 简单阈值 (Simple Thresholding) ---
# cv2.threshold(src, thresh, maxval, type)
# src: 输入灰度图像
# thresh: 阈值
# maxval: 当像素值超过阈值(或满足特定类型条件)时赋予的值
# type: 阈值类型
# cv2.THRESH_BINARY: 像素 > thresh ? maxval : 0
# cv2.THRESH_BINARY_INV: 像素 > thresh ? 0 : maxval
# cv2.THRESH_TRUNC: 像素 > thresh ? thresh : 像素值 (截断)
# cv2.THRESH_TOZERO: 像素 > thresh ? 像素值 : 0
# cv2.THRESH_TOZERO_INV: 像素 > thresh ? 0 : 像素值
# 返回值: ret (实际使用的阈值), thresholded_image (处理后的二值图像)

threshold_value = 127
max_value = 255
ret, thresh_binary = cv2.threshold(img_gray, threshold_value, max_value, cv2.THRESH_BINARY)
ret, thresh_binary_inv = cv2.threshold(img_gray, threshold_value, max_value, cv2.THRESH_BINARY_INV)
ret, thresh_trunc = cv2.threshold(img_gray, threshold_value, max_value, cv2.THRESH_TRUNC)
ret, thresh_tozero = cv2.threshold(img_gray, threshold_value, max_value, cv2.THRESH_TOZERO)
ret, thresh_tozero_inv = cv2.threshold(img_gray, threshold_value, max_value, cv2.THRESH_TOZERO_INV)

print(f"简单阈值处理使用的阈值: {ret}") # 对于简单阈值,ret 等于输入的 thresh

cv2.imshow('Original Gray', img_gray)
cv2.imshow('THRESH_BINARY', thresh_binary)
cv2.imshow('THRESH_BINARY_INV', thresh_binary_inv)
# cv2.imshow('THRESH_TRUNC', thresh_trunc) # 其他类型可以按需显示
# cv2.imshow('THRESH_TOZERO', thresh_tozero)
# cv2.imshow('THRESH_TOZERO_INV', thresh_tozero_inv)
cv2.waitKey(1)

# --- 2. 自适应阈值 (Adaptive Thresholding) ---
# 对于光照不均匀的图像效果更好,阈值是根据像素邻域计算的。
# cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
# src: 输入灰度图像
# maxValue: 赋予的最高像素值
# adaptiveMethod: 计算阈值的方法
#   cv2.ADAPTIVE_THRESH_MEAN_C: 邻域均值减去 C
#   cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 邻域高斯加权和减去 C
# thresholdType: 必须是 cv2.THRESH_BINARY 或 cv2.THRESH_BINARY_INV
# blockSize: 计算阈值的邻域大小(必须是奇数)
# C: 从均值或加权和中减去的常数

block_size = 11 # 必须是奇数
C_value = 2
adaptive_thresh_mean = cv2.adaptiveThreshold(img_gray, max_value, cv2.ADAPTIVE_THRESH_MEAN_C,
                                           cv2.THRESH_BINARY, block_size, C_value)
adaptive_thresh_gaussian = cv2.adaptiveThreshold(img_gray, max_value, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                               cv2.THRESH_BINARY, block_size, C_value)

cv2.imshow('Adaptive Mean Thresholding', adaptive_thresh_mean)
cv2.imshow('Adaptive Gaussian Thresholding', adaptive_thresh_gaussian)
cv2.waitKey(1)

# --- 3. Otsu's 二值化 (自动寻找最优阈值) ---
# 对于双峰图像(直方图有两个明显峰值)效果好。
# 在 cv2.threshold() 中将阈值设为 0,并添加 cv2.THRESH_OTSU 标志。
# 它会自动计算一个最优阈值,并将其作为 ret 返回。
ret_otsu, thresh_otsu = cv2.threshold(img_gray, 0, max_value, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(f"Otsu's 方法计算得到的阈值: {ret_otsu}")
cv2.imshow("Otsu's Thresholding", thresh_otsu)

cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print("灰度图像未成功加载,跳过阈值处理。")

```

4.3 图像平滑/滤波 (Blurring/Filtering)

滤波是图像处理中常用的技术,主要用于:

  • 降噪 (Noise Reduction): 去除图像中的随机噪声。
  • 平滑 (Smoothing): 模糊图像,减弱细节。

常见的滤波方法包括:

  • 均值滤波 (Averaging): 用邻域像素的平均值代替中心像素。简单快速,但可能模糊边缘。
  • 高斯滤波 (Gaussian Blurring): 使用高斯核进行加权平均,离中心像素越近的像素权重越大。效果比均值滤波更自然,对边缘保留稍好。
  • 中值滤波 (Median Blurring): 用邻域像素的中值代替中心像素。对椒盐噪声 (Salt-and-pepper noise) 特别有效,且能较好地保留边缘。
  • 双边滤波 (Bilateral Filtering): 在高斯滤波的基础上加入了像素值相似度的考量,能在降噪的同时更好地保留边缘信息,但计算量较大。

```python
import cv2
import numpy as np

假设 img_color 是一个已加载的 BGR 彩色图像

if img_color is not None:
# --- 1. 均值滤波 ---
# cv2.blur(src, ksize)
# ksize: 核大小 (width, height),例如 (5, 5) 表示 5x5 的邻域
kernel_size = (5, 5)
averaged = cv2.blur(img_color, kernel_size)
cv2.imshow('Averaged Blur', averaged)

# --- 2. 高斯滤波 ---
# cv2.GaussianBlur(src, ksize, sigmaX[, sigmaY[, borderType]])
# ksize: 高斯核大小 (width, height),必须是正奇数。
# sigmaX: X 方向的高斯核标准差。如果为 0,则根据核大小自动计算。
# sigmaY: Y 方向的标准差。如果为 0,则取 sigmaX 的值。如果 sigmaX 和 sigmaY 都为 0,则根据 ksize 计算。
gaussian_kernel_size = (5, 5) # 必须是正奇数
sigma_x = 0 # 自动计算
gaussian_blurred = cv2.GaussianBlur(img_color, gaussian_kernel_size, sigma_x)
cv2.imshow('Gaussian Blur', gaussian_blurred)

# --- 3. 中值滤波 ---
# cv2.medianBlur(src, ksize)
# ksize: 核大小,必须是大于 1 的奇数整数 (例如 3, 5, 7)。
median_kernel_size = 5 # 必须是奇数
median_blurred = cv2.medianBlur(img_color, median_kernel_size)
cv2.imshow('Median Blur', median_blurred)

# --- 4. 双边滤波 ---
# cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, borderType])
# d: 滤波期间使用的每个像素邻域的直径。如果非正,则从 sigmaSpace 计算。
# sigmaColor: 颜色空间滤波器的 sigma 值。值越大,邻域中越多颜色相近的像素会被混合。
# sigmaSpace: 坐标空间滤波器的 sigma 值。值越大,距离越远的像素会相互影响(只要颜色足够接近)。
# 对边缘保留效果好,但速度慢。
d = 9         # 邻域直径
sigma_color = 75
sigma_space = 75
bilateral_filtered = cv2.bilateralFilter(img_color, d, sigma_color, sigma_space)
cv2.imshow('Bilateral Filter', bilateral_filtered)

cv2.imshow('Original', img_color)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print("彩色图像未成功加载,跳过滤波操作。")
```

选择哪种滤波器取决于具体的噪声类型和应用需求。

4.4 形态学操作 (Morphological Transformations)

形态学操作是基于图像形状的一系列操作,通常作用于二值图像或灰度图像。它们依赖于一个结构元素 (Structuring Element) 或 核 (Kernel),该元素定义了操作的邻域范围和形状。

基本操作:

  • 腐蚀 (Erosion): 使白色区域(前景)收缩,细小的白色物体可能会消失。可以去除小的噪声点。
  • 膨胀 (Dilation): 使白色区域(前景)扩张,可以连接断开的区域,填充小的孔洞。

基于腐蚀和膨胀的组合操作:

  • 开运算 (Opening): 先腐蚀后膨胀。主要用于去除小的白色噪声点(类似“散落的沙子”),同时保持较大物体的大小基本不变。
  • 闭运算 (Closing): 先膨胀后腐蚀。主要用于填充前景物体内部的小孔洞或连接邻近的物体。
  • 形态学梯度 (Morphological Gradient): 膨胀图与腐蚀图之差,可以得到物体的轮廓。
  • 顶帽 (Top Hat): 原图与开运算结果之差,可以分离出比邻域亮的斑点。
  • 黑帽 (Black Hat): 闭运算结果与原图之差,可以分离出比邻域暗的斑点。

```python
import cv2
import numpy as np

通常在二值图像上效果更明显,先创建一个简单的二值图

binary_img = np.zeros((300, 500), dtype=np.uint8)
cv2.rectangle(binary_img, (50, 50), (200, 200), 255, -1) # 画一个实心白色矩形
cv2.rectangle(binary_img, (250, 100), (450, 250), 255, -1)

添加一些噪声

白色噪声点 (椒)

cv2.circle(binary_img, (100, 100), 5, 0, -1) # 在矩形内挖个小黑洞
cv2.circle(binary_img, (300, 150), 5, 0, -1)

黑色噪声点 (盐)

cv2.circle(binary_img, (30, 30), 3, 255, -1) # 在背景上加个小白点
cv2.circle(binary_img, (400, 50), 3, 255, -1)

cv2.imshow('Original Binary Image', binary_img)
cv2.waitKey(1)

--- 定义结构元素 (Kernel) ---

常见的有矩形、椭圆、十字形

cv2.getStructuringElement(shape, ksize[, anchor])

shape: cv2.MORPH_RECT, cv2.MORPH_ELLIPSE, cv2.MORPH_CROSS

ksize: 核的大小 (width, height)

kernel_size = (5, 5)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, kernel_size)

kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, kernel_size)

print("使用的结构元素:\n", kernel)

--- 1. 腐蚀 ---

cv2.erode(src, kernel[, iterations=1])

iterations: 腐蚀操作的次数

erosion = cv2.erode(binary_img, kernel, iterations=1)
cv2.imshow('Erosion', erosion)
cv2.waitKey(1)

--- 2. 膨胀 ---

cv2.dilate(src, kernel[, iterations=1])

dilation = cv2.dilate(binary_img, kernel, iterations=1)
cv2.imshow('Dilation', dilation)
cv2.waitKey(1)

--- 3. 开运算 (先腐蚀后膨胀) ---

cv2.morphologyEx(src, op, kernel)

op: 操作类型,如 cv2.MORPH_OPEN

opening = cv2.morphologyEx(binary_img, cv2.MORPH_OPEN, kernel)
cv2.imshow('Opening (Remove small white noise)', opening)
cv2.waitKey(1)

--- 4. 闭运算 (先膨胀后腐蚀) ---

closing = cv2.morphologyEx(binary_img, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Closing (Fill small black holes)', closing)
cv2.waitKey(1)

--- 5. 形态学梯度 ---

gradient = cv2.morphologyEx(binary_img, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('Morphological Gradient (Outline)', gradient)
cv2.waitKey(1)

--- 6. 顶帽 ---

tophat = cv2.morphologyEx(binary_img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('Top Hat', tophat)
cv2.waitKey(1)

--- 7. 黑帽 ---

blackhat = cv2.morphologyEx(binary_img, cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('Black Hat', blackhat)

cv2.waitKey(0)
cv2.destroyAllWindows()
```

4.5 边缘检测 (Edge Detection)

边缘是图像中亮度发生急剧变化的地方,通常对应于物体的边界。Canny 边缘检测是一种常用且效果较好的多阶段算法。

Canny 算法步骤:

  1. 高斯滤波: 降噪。
  2. 计算梯度: 计算图像的梯度强度和方向。
  3. 非极大值抑制 (Non-maximum Suppression): 细化边缘,只保留梯度方向上的局部最大值点。
  4. 双阈值处理: 使用高低两个阈值。梯度强度高于高阈值的确定为强边缘;低于低阈值的确定为非边缘;介于两者之间的,只有当其连接到强边缘时才被认为是边缘(滞后阈值处理)。

```python
import cv2
import numpy as np

假设 img_gray 是一个已加载的灰度图像

if img_gray is not None:
# Canny 边缘检测
# cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
# image: 输入单通道 8 位图像 (通常是灰度图)
# threshold1: 低阈值 (用于边缘连接)
# threshold2: 高阈值 (用于强边缘识别)
# apertureSize: Sobel 算子的大小,默认为 3。
# L2gradient: 计算梯度幅值的标志,True 使用更精确的 L2 范数,False 使用 L1 范数。

low_threshold = 50
high_threshold = 150 # 通常高阈值是低阈值的 2-3 倍

edges = cv2.Canny(img_gray, low_threshold, high_threshold)

cv2.imshow('Original Gray', img_gray)
cv2.imshow('Canny Edges', edges)

# 可以尝试不同的阈值
edges_low = cv2.Canny(img_gray, 20, 60)
cv2.imshow('Canny Edges (Low Thresholds)', edges_low)

edges_high = cv2.Canny(img_gray, 100, 200)
cv2.imshow('Canny Edges (High Thresholds)', edges_high)


cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print("灰度图像未成功加载,跳过边缘检测。")
```

调整 threshold1threshold2 对 Canny 检测结果影响很大,需要根据具体图像进行尝试。

5. 在图像上绘图

OpenCV 提供了在图像上绘制基本形状和文本的功能,这对于可视化结果(如标记检测到的对象)非常有用。

```python
import cv2
import numpy as np

创建一个黑色背景画布

canvas = np.zeros((400, 600, 3), dtype=np.uint8) # 高400, 宽600, BGR

定义颜色 (BGR 格式)

blue = (255, 0, 0)
green = (0, 255, 0)
red = (0, 0, 255)
white = (255, 255, 255)
yellow = (0, 255, 255)

--- 1. 绘制直线 ---

cv2.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

pt1, pt2: 线段的起点和终点 (x, y)

thickness: 线宽。如果为负数 (如 -1 或 cv2.FILLED),则绘制填充形状。

start_point = (50, 50)
end_point = (300, 100)
cv2.line(canvas, start_point, end_point, green, thickness=3)

--- 2. 绘制矩形 ---

cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])

pt1: 矩形的一个顶点 (通常是左上角) (x, y)

pt2: 矩形的对角顶点 (通常是右下角) (x, y)

top_left = (100, 150)
bottom_right = (250, 250)
cv2.rectangle(canvas, top_left, bottom_right, blue, thickness=2) # 空心矩形

绘制实心矩形

cv2.rectangle(canvas, (350, 50), (450, 150), red, thickness=-1) # 或 cv2.FILLED

--- 3. 绘制圆形 ---

cv2.circle(img, center, radius, color[, thickness[, lineType[, shift]]])

center: 圆心坐标 (x, y)

radius: 半径 (像素)

center_coordinates = (400, 300)
radius = 50
cv2.circle(canvas, center_coordinates, radius, yellow, thickness=5) # 空心圆
cv2.circle(canvas, (100, 300), 30, white, thickness=-1) # 实心圆

--- 4. 绘制椭圆 ---

cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]])

center: 椭圆中心 (x, y)

axes: 轴长 (主轴长/2, 短轴长/2)

angle: 旋转角度 (顺时针)

startAngle, endAngle: 椭圆弧的起始和结束角度 (0-360)

center_ellipse = (300, 200)
axes_length = (80, 40) # 长轴160, 短轴80
angle = 45 # 旋转45度
start_angle = 0
end_angle = 360 # 绘制完整椭圆
cv2.ellipse(canvas, center_ellipse, axes_length, angle, start_angle, end_angle, white, thickness=2)

--- 5. 绘制多边形 ---

需要先定义顶点坐标

pts = np.array([[10, 200], [50, 250], [100, 220], [80, 180]], np.int32)
pts = pts.reshape((-1, 1, 2)) # 转换成 OpenCV 需要的格式

cv2.polylines(img, [pts], isClosed, color[, thickness[, lineType[, shift]]])

[pts]: 包含顶点数组的列表

isClosed: True 表示闭合多边形,False 表示折线

cv2.polylines(canvas, [pts], isClosed=True, color=red, thickness=3)

--- 6. 添加文本 ---

cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])

text: 要绘制的字符串

org: 文本框的左下角坐标 (x, y)

fontFace: 字体类型 (如 cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_PLAIN 等)

fontScale: 字体大小(缩放因子)

color: 文本颜色 (BGR)

thickness: 字体粗细

text = "OpenCV Python Tutorial"
org = (50, 380) # 左下角位置
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1
color = green
thickness = 2
cv2.putText(canvas, text, org, font, font_scale, color, thickness, cv2.LINE_AA) # LINE_AA 提供抗锯齿效果

显示绘制结果

cv2.imshow('Drawing Canvas', canvas)
cv2.waitKey(0)
cv2.destroyAllWindows()
```

6. 视频处理基础

OpenCV 不仅能处理静态图像,还能轻松处理视频文件或来自摄像头的实时视频流。视频本质上是一系列连续的图像帧。

6.1 读取视频文件或摄像头

使用 cv2.VideoCapture 对象。

```python
import cv2

--- 读取视频文件 ---

video_path = 'path/to/your/video.mp4' # 替换成你的视频文件路径
cap_file = cv2.VideoCapture(video_path)

--- 或者,读取摄像头 ---

参数 0 通常代表默认的内置摄像头。如果有多个摄像头,可以尝试 1, 2, ...

cap_cam = cv2.VideoCapture(0)

选择要使用的 VideoCapture 对象

cap = cap_file # 或者 cap = cap_cam

检查视频是否成功打开

if not cap.isOpened():
print(f"错误: 无法打开视频源")
else:
print("视频源成功打开。按 'q' 键退出。")
while True:
# --- 逐帧读取 ---
# cap.read() 返回两个值:
# ret: 布尔值,表示是否成功读取到帧 (如果到达视频末尾或出错,则为 False)
# frame: 读取到的图像帧 (NumPy 数组)
ret, frame = cap.read()

    # 如果成功读取到帧 (ret is True)
    if ret:
        # --- 在这里可以对 frame 进行处理 ---
        # 例如,转换为灰度图
        gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 例如,应用 Canny 边缘检测
        edges = cv2.Canny(gray_frame, 50, 150)

        # 显示原始帧和处理后的帧
        cv2.imshow('Original Video', frame)
        cv2.imshow('Processed Frame (Edges)', edges)

        # --- 等待按键,实现播放控制 ---
        # cv2.waitKey(delay) delay > 0 表示等待多少毫秒
        # 按 'q' 键退出循环
        if cv2.waitKey(25) & 0xFF == ord('q'): # 等待 25ms,大约对应 40 FPS
            print("检测到按键 'q',退出循环。")
            break
    else:
        # 如果读取失败 (可能已到视频末尾)
        print("无法读取帧,可能视频已结束或发生错误。")
        break

--- 释放资源 ---

释放 VideoCapture 对象

cap.release()

关闭所有 OpenCV 窗口

cv2.destroyAllWindows()
print("视频处理结束,资源已释放。")
```

6.2 获取视频属性

可以获取视频的宽度、高度、帧率等信息。

```python
if cap_file.isOpened(): # 假设 cap_file 仍然是打开的 VideoCapture 对象
width = int(cap_file.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap_file.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap_file.get(cv2.CAP_PROP_FPS)
frame_count = int(cap_file.get(cv2.CAP_PROP_FRAME_COUNT)) # 对文件有效
fourcc_code = int(cap_file.get(cv2.CAP_PROP_FOURCC))
# 将 FourCC 整数代码转换为字符
fourcc_str = "".join([chr((fourcc_code >> 8 * i) & 0xFF) for i in range(4)])

print(f"视频宽度: {width} 像素")
print(f"视频高度: {height} 像素")
print(f"帧率 (FPS): {fps:.2f}")
print(f"总帧数: {frame_count}")
print(f"编解码器 FourCC: {fourcc_str} ({fourcc_code})")

cap_file.release() # 记得释放

else:
print("无法打开视频文件以获取属性。")
```

6.3 写入视频文件

可以将处理后的帧保存为新的视频文件。需要使用 cv2.VideoWriter 对象。

```python
import cv2

假设我们从摄像头读取并想保存处理后的视频 (灰度)

cap = cv2.VideoCapture(0) # 打开摄像头

if not cap.isOpened():
print("错误:无法打开摄像头")
else:
# 获取摄像头的宽度和高度
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS) # 尝试获取帧率,可能不准确
if fps == 0: # 如果获取不到,设一个默认值
fps = 20.0
print(f"摄像头分辨率: {frame_width}x{frame_height}, 帧率: {fps}")

# --- 定义视频编码器 (Codec) 和创建 VideoWriter 对象 ---
# FourCC 是一个 4 字节的代码,用于指定视频编解码器。
# 常见的有: 'XVID', 'MJPG', 'MP4V', 'DIVX', 'H264' 等。
# 可用性取决于你的系统安装的编解码器。'XVID' 通常比较通用。
# 在 Windows 上可用: 'DIVX'
# 在 macOS 或 Linux 上可用: 'XVID', 'MJPG', 'MP4V'
# 注意:FourCC 是区分大小写的!
fourcc = cv2.VideoWriter_fourcc(*'XVID') # 对于 .avi 文件
# fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 对于 .mp4 文件 (可能需要额外库支持)

output_filename = 'output_video.avi' # 输出文件名和格式

# cv2.VideoWriter(filename, fourcc, fps, frameSize[, isColor])
# filename: 输出文件名
# fourcc: 编解码器
# fps: 输出视频的帧率
# frameSize: (width, height)
# isColor: True 表示彩色视频,False 表示灰度视频。默认为 True。
# 注意:写入的帧必须与 frameSize 和 isColor 设置匹配!
out = cv2.VideoWriter(output_filename, fourcc, fps, (frame_width, frame_height), isColor=False) # 保存灰度视频

if not out.isOpened():
    print(f"错误:无法创建 VideoWriter 对象,请检查编解码器 ({fourcc}) 和路径。")
else:
    print(f"开始录制视频到 {output_filename}... 按 'q' 停止。")
    while True:
        ret, frame = cap.read()
        if ret:
            # 处理帧 (例如,转为灰度)
            gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

            # --- 写入处理后的帧 ---
            out.write(gray_frame) # 写入灰度帧

            # 显示原始帧 (可选)
            cv2.imshow('Recording... Press Q to Stop', frame)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                print("停止录制。")
                break
        else:
            print("无法从摄像头读取帧。")
            break

    # --- 释放资源 ---
    out.release() # 必须释放 VideoWriter 以完成文件写入

cap.release()
cv2.destroyAllWindows()
print("录制完成,资源已释放。")

```

7. 简单应用:人脸检测 (Haar Cascades)

OpenCV 提供了一种基于 Haar 特征的级联分类器(Haar Cascades)进行对象检测的方法,尤其常用于人脸检测。这是一种基于机器学习的经典方法,虽然现在有更先进的深度学习模型,但 Haar Cascade 速度快,易于使用,是入门的好例子。

你需要预先训练好的 XML 分类器文件。OpenCV 安装时通常会自带这些文件,或者你可以从 OpenCV 的 GitHub 仓库下载 (data/haarcascades 目录下)。

常见的有:haarcascade_frontalface_default.xml, haarcascade_eye.xml 等。

```python
import cv2
import os

--- 加载 Haar Cascade 分类器 ---

指定 XML 文件的路径。你需要根据你的 OpenCV 安装位置或下载位置修改这个路径。

示例路径 (可能需要调整):

1. 如果使用 pip 安装,可能在 Python site-packages/cv2/data/ 目录下

2. 从 OpenCV GitHub 下载后指定路径

cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
eye_cascade_path = cv2.data.haarcascades + 'haarcascade_eye.xml'

if not os.path.exists(cascade_path):
print(f"错误:找不到人脸检测器文件 {cascade_path}")
# 退出或采取其他措施
exit()
if not os.path.exists(eye_cascade_path):
print(f"错误:找不到眼睛检测器文件 {eye_cascade_path}")
exit()

创建分类器对象

face_cascade = cv2.CascadeClassifier(cascade_path)
eye_cascade = cv2.CascadeClassifier(eye_cascade_path)

if face_cascade.empty():
print("错误:加载人脸检测器失败。")
exit()
if eye_cascade.empty():
print("错误:加载眼睛检测器失败。")
exit()

--- 加载要检测的图像 ---

image_path = 'path/to/image/with/faces.jpg' # 替换成包含人脸的图片路径
img = cv2.imread(image_path)

if img is None:
print(f"错误:无法加载图像 {image_path}")
else:
# --- 转换为灰度图 (Haar Cascade 在灰度图上效果更好) ---
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# --- 进行人脸检测 ---
# cascade.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]])
# image: 输入灰度图像
# scaleFactor: 每次图像缩小的比例 (例如 1.1 或 1.3)。值越小,检测越慢但可能更准。
# minNeighbors: 每个候选矩形需要多少个邻居才能被保留。值越大,检测到的假阳性越少,但可能漏检。
# minSize: 检测对象的最小尺寸 (width, height)。
# maxSize: 检测对象的最大尺寸。
# 返回值: 检测到的对象的矩形列表 (x, y, w, h)

scale_factor = 1.1
min_neighbors = 5
min_face_size = (30, 30) # 忽略小于 30x30 像素的人脸

faces = face_cascade.detectMultiScale(gray,
                                    scaleFactor=scale_factor,
                                    minNeighbors=min_neighbors,
                                    minSize=min_face_size)

print(f"检测到 {len(faces)} 张人脸。")

# --- 在检测到的人脸上绘制矩形 ---
for (x, y, w, h) in faces:
    # 在原彩色图上绘制蓝色矩形
    cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2) # BGR, thickness=2

    # --- 在人脸区域内检测眼睛 ---
    # 提取人脸的 ROI (灰度和彩色)
    roi_gray = gray[y:y+h, x:x+w]
    roi_color = img[y:y+h, x:x+w]

    # 在人脸 ROI 中检测眼睛
    eyes = eye_cascade.detectMultiScale(roi_gray,
                                        scaleFactor=1.1, # 通常眼睛检测的参数需要更细致
                                        minNeighbors=8,
                                        minSize=(15, 15)) # 眼睛的最小尺寸

    print(f"  在人脸 (x={x}, y={y}) 内检测到 {len(eyes)} 只眼睛。")

    # 在检测到的眼睛上绘制绿色矩形 (注意坐标是相对于 ROI 的)
    for (ex, ey, ew, eh) in eyes:
        cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 1) # Green, thickness=1

# 显示带有人脸和眼睛标记的图像
cv2.imshow('Face and Eye Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 可以选择保存结果
# cv2.imwrite('detected_faces_eyes.jpg', img)

```

这个例子展示了如何加载预训练模型,进行对象检测,并在结果上进行绘制。你也可以将这个逻辑应用到视频流的每一帧,实现实时人脸检测。

8. 总结与后续学习

恭喜你!通过本教程,你已经掌握了 OpenCV Python 的基础知识,包括:

  • 安装和设置 OpenCV 环境。
  • 理解图像在 OpenCV Python 中如何表示 (NumPy 数组)。
  • 加载、显示、保存图像和视频。
  • 访问像素、操作 ROI、处理颜色通道。
  • 应用核心图像处理技术:颜色空间转换、阈值处理、滤波、形态学操作、边缘检测。
  • 在图像上绘制图形和文本。
  • 使用 Haar Cascades 进行简单的人脸检测。

这仅仅是计算机视觉和 OpenCV 的冰山一角。接下来,你可以探索更多有趣且强大的领域:

  • 特征检测与描述: 如 SIFT, SURF, ORB, AKAZE 等,用于图像匹配、目标识别、三维重建。
  • 对象跟踪 (Object Tracking): 如 MeanShift, CamShift, 以及基于深度学习的跟踪器 (GOTURN, KCF)。
  • 光流 (Optical Flow): 估计图像序列中像素的运动。
  • 相机标定与三维重建: 获取相机参数,从 2D 图像恢复 3D 场景信息。
  • 机器学习与深度学习集成:
    • OpenCV 自带的机器学习模块 (SVM, K-Nearest Neighbors)。
    • OpenCV 的 DNN (Deep Neural Networks) 模块,可以加载和运行预训练的深度学习模型 (如 TensorFlow, Caffe, PyTorch, ONNX 格式),进行图像分类、对象检测 (如 YOLO, SSD)、语义分割等高级任务。
  • 图像拼接、背景消除、文档扫描 等具体应用。

学习资源推荐:

最重要的是动手实践。尝试修改教程中的代码,用自己的图像和视频进行实验,挑战一些小的项目,例如:

  • 创建一个简单的文档扫描器。
  • 检测视频中的特定颜色物体并跟踪它。
  • 实现一个基本的车牌识别系统 (可能需要 OCR 库)。
  • 用 DNN 模块运行一个预训练的物体检测模型。

计算机视觉是一个充满挑战和乐趣的领域,OpenCV Python 为你打开了探索这个世界的大门。祝你学习愉快,在计算机视觉的旅程中不断进步!


THE END