Python OpenCV 初学者指南:踏入计算机视觉世界
目录
- 引言:什么是 OpenCV?为什么选择 Python?
- 环境搭建:Python、pip 和 OpenCV 的安装
- OpenCV 基础概念:图像是什么?NumPy 与图像表示
- 入门操作:读取、显示和保存图像
- 图像的基本属性和像素操作
- 在图像上绘制图形和文字
- 基本的图像变换:缩放、裁剪、平移和旋转
- 颜色空间:BGR、灰度图和 HSV
- 图像阈值化:二值化和图像分割基础
- 图像平滑(滤波):去噪与模糊
- 边缘检测:Canny 算法
- 处理视频:读取、显示摄像头和视频文件
- 综合应用示例:简单颜色检测与对象追踪
- 综合应用示例:使用 Haarcascades 进行人脸检测
- 总结与进阶:下一步的学习方向
1. 引言:什么是 OpenCV?为什么选择 Python?
欢迎来到令人兴奋的计算机视觉(Computer Vision)世界!计算机视觉是一门研究如何让计算机“看懂”图像和视频的科学。它涉及从识别物体、检测人脸、自动驾驶到医疗影像分析等诸多领域。
而 OpenCV (Open Source Computer Vision Library) 是目前最流行、功能最强大的计算机视觉库之一。它最初由 Intel 开发,现已成为一个庞大的开源项目,包含了数百个优化过的计算机视觉算法,涵盖图像处理、物体识别、模式识别、三维重建等多个方向。OpenCV 支持多种编程语言,包括 C++, Java, MATLAB 等。
那么,为什么我们选择 Python 来学习和使用 OpenCV 呢?
- 易学易用: Python 语法简洁,学习曲线平缓,非常适合初学者。
- 丰富的生态系统: Python 拥有庞大的第三方库生态,特别是对于科学计算和数据处理领域,如 NumPy(数值计算)、Matplotlib(数据可视化)、SciPy(科学计算)等,它们与 OpenCV 的结合非常紧密且强大。
- 快速原型开发: Python 的动态特性和简洁语法使得快速构建原型和测试想法变得非常容易。
- 广泛的应用: Python 在人工智能、机器学习、数据科学等领域占据主导地位,而计算机视觉正是这些领域的重要组成部分。
因此,Python + OpenCV 是学习和实践计算机视觉的绝佳组合。本指南将带你从零开始,一步步掌握 OpenCV 的基本用法,为你打开计算机视觉的大门。
2. 环境搭建:Python、pip 和 OpenCV 的安装
在开始之前,你需要确保你的计算机上安装了 Python 和 pip
包管理器。如果你还没有安装,建议访问 Python 官方网站 (python.org) 下载并安装最新版本的 Python。安装时请务必勾选“Add Python to PATH”选项(Windows 用户)。
pip
通常会随着 Python 一起安装。你可以通过在命令行输入 python --version
和 pip --version
来检查它们是否安装成功并查看版本信息。
安装 OpenCV 库非常简单,只需打开你的命令行或终端,运行以下命令:
bash
pip install opencv-python
这个命令会从 Python Package Index (PyPI) 下载并安装 OpenCV 的核心模块。如果你还需要额外的 contrib 模块(包含一些非免费或实验性功能,但通常初学者不需要),可以使用:
bash
pip install opencv-contrib-python
强烈建议使用虚拟环境(Virtual Environment)。虚拟环境可以为你创建一个独立的 Python 环境,使得不同项目使用的库版本互不干扰。常用的虚拟环境工具有 venv
(Python 3.3+ 内置) 或 conda
。
使用 venv
创建虚拟环境(以 Windows 为例):
- 打开命令行,切换到你的项目文件夹。
- 创建虚拟环境:
python -m venv myenv
(myenv
是你虚拟环境的名字,可以随意更改) - 激活虚拟环境:
myenv\Scripts\activate
(Windows) 或source myenv/bin/activate
(macOS/Linux) - 激活后,命令提示符前会显示虚拟环境的名字
(myenv)
。现在在这个环境中安装 OpenCV:pip install opencv-python
完成安装后,你可以在 Python 交互环境中尝试导入 OpenCV 来验证是否安装成功:
python
import cv2
print(cv2.__version__)
如果没有报错并打印出版本号,恭喜你,OpenCV 已经安装成功了!
3. OpenCV 基础概念:图像是什么?NumPy 与图像表示
在计算机视觉中,图像不再是简单的图片,而是一堆数字的集合。
- 像素 (Pixel): 图像的最小单位。每个像素代表了图像中一个特定位置的颜色或灰度信息。
- 通道 (Channel): 描述像素颜色信息的方式。
- 灰度图像 (Grayscale Image): 每个像素只有一个通道,表示亮度信息,通常范围是 0(黑色)到 255(白色)。
- 彩色图像 (Color Image): 通常有三个通道,最常见的是 RGB (Red, Green, Blue)。然而,OpenCV 在默认情况下使用 BGR 顺序 (Blue, Green, Red)。这意味着一个彩色像素由三个数值表示,例如 (255, 0, 0) 在 RGB 中是红色,但在 OpenCV 的 BGR 中是蓝色。每个通道的数值通常在 0-255 之间。
NumPy 与图像表示:
OpenCV 使用 NumPy 数组 (ndarray) 来存储和处理图像数据。这是一个非常高效的多维数组库,与 Python 的列表相比,NumPy 数组在数值计算方面性能卓越。
- 灰度图像: 在 NumPy 中表示为一个二维数组(矩阵),形状为
(height, width)
,数据类型通常是uint8
(无符号 8 位整型,范围 0-255)。 - 彩色图像: 在 NumPy 中表示为一个三维数组,形状为
(height, width, channels)
,数据类型通常也是uint8
。对于 BGR 图像,channels
的值为 3。
例如,一个 100×200 像素的灰度图像在 NumPy 中形状是 (100, 200)
;一个 100×200 像素的彩色 BGR 图像形状是 (100, 200, 3)
。
这种 NumPy 数组的表示方式使得我们可以方便地使用 NumPy 提供的各种数学运算、切片和索引功能来操作图像。
4. 入门操作:读取、显示和保存图像
这是使用 OpenCV 进行图像处理的第一步。
“`python
import cv2
1. 读取图像
cv2.imread() 函数用于从文件加载图像。
参数1: 文件路径字符串
参数2: 读取方式标志 (可选)
cv2.IMREAD_COLOR (或 1): 加载彩色图像 (默认值)。忽略透明度。
cv2.IMREAD_GRAYSCALE (或 0): 加载灰度图像。
cv2.IMREAD_UNCHANGED (或 -1): 加载包含 alpha 通道的完整图像。
如果文件不存在或无法读取,函数返回 None。
img = cv2.imread(‘test_image.jpg’, cv2.IMREAD_COLOR)
检查图像是否成功加载
if img is None:
print(“错误:无法读取图像文件。请检查文件路径是否正确。”)
# 如果图像加载失败,通常需要退出程序或跳过后续处理
else:
print(“图像读取成功!”)
# 2. 显示图像
# cv2.imshow() 函数用于在一个窗口中显示图像。
# 参数1: 窗口名称 (字符串)
# 参数2: 要显示的图像 NumPy 数组
cv2.imshow('Loaded Image', img)
# cv2.waitKey() 函数用于等待键盘事件。
# 参数: 等待的毫秒数。
# 0: 表示无限期等待,直到用户按下任意键。
# 正整数: 表示等待指定的毫秒数,如果在这段时间内没有按键,则继续执行后面的代码。
# 返回值: 按下的键的 ASCII 值。
print("按任意键关闭图像窗口...")
cv2.waitKey(0)
# cv2.destroyAllWindows() 函数用于销毁所有创建的 OpenCV 窗口。
# cv2.destroyWindow('窗口名称') 可以只销毁指定窗口。
cv2.destroyAllWindows()
# 3. 保存图像
# cv2.imwrite() 函数用于将图像保存到文件。
# 参数1: 保存文件的路径 (字符串,包含文件扩展名,扩展名决定保存格式,如 .jpg, .png 等)
# 参数2: 要保存的图像 NumPy 数组
# 返回值: 布尔值,表示保存是否成功。
save_path = 'saved_image.png'
success = cv2.imwrite(save_path, img)
if success:
print(f"图像成功保存到 {save_path}")
else:
print("保存图像失败。")
注意:运行此代码前,请确保在同一目录下有一个名为 ‘test_image.jpg’ 的图片文件。
如果没有,请自行创建一个或修改文件路径。
“`
代码解释:
- 我们首先导入
cv2
库。 cv2.imread('test_image.jpg', cv2.IMREAD_COLOR)
尝试加载名为test_image.jpg
的彩色图像。- 我们通过
if img is None:
检查图像是否加载成功,这是非常重要的良好编程习惯。 cv2.imshow('Loaded Image', img)
在一个名为 ‘Loaded Image’ 的窗口中显示加载的图像。cv2.waitKey(0)
使程序暂停,等待用户按下任意键盘按键。如果参数是0
,程序会一直暂停直到按键。如果参数是N
(N > 0),程序会等待 N 毫秒。cv2.destroyAllWindows()
关闭所有由cv2.imshow()
打开的窗口。cv2.imwrite('saved_image.png', img)
将加载的图像保存为saved_image.png
文件。文件扩展名 (.png
) 决定了保存的格式。
5. 图像的基本属性和像素操作
加载图像后,我们可以访问它的属性并操作单个像素或区域。
“`python
import cv2
import numpy as np
img = cv2.imread(‘test_image.jpg’)
if img is None:
print(“错误:无法读取图像文件。”)
else:
print(“图像属性:”)
# img.shape 返回一个包含 (高度, 宽度, 通道数) 的元组。
# 如果是灰度图,则只有 (高度, 宽度)。
print(f”形状 (Shape): {img.shape}”)
# img.size 返回图像的总像素数 (高度 * 宽度 * 通道数)。
print(f"总像素数 (Size): {img.size}")
# img.dtype 返回图像像素的数据类型,通常是 uint8。
print(f"数据类型 (Data Type): {img.dtype}")
print("\n像素操作:")
# 访问像素值 (对于 BGR 图像): img[行索引, 列索引] 返回一个包含 (B, G, R) 值的数组
# 注意:OpenCV 使用 (行, 列) 或 (y, x) 坐标,而不是常见的 (x, y)。
# 行索引对应高度,列索引对应宽度。
pixel_color = img[100, 150] # 访问第100行,第150列的像素
print(f"像素 (100, 150) 的 BGR 值: {pixel_color}")
# 访问单个通道的像素值 (对于 BGR 图像): img[行索引, 列索引, 通道索引]
# 通道索引:0代表蓝色,1代表绿色,2代表红色
blue_value = img[100, 150, 0]
green_value = img[100, 150, 1]
red_value = img[100, 150, 2]
print(f"像素 (100, 150) 的 Blue 值: {blue_value}")
print(f"像素 (100, 150) 的 Green 值: {green_value}")
print(f"像素 (100, 150) 的 Red 值: {red_value}")
# 修改像素值 (对于 BGR 图像)
img[100, 150] = [255, 255, 255] # 将像素 (100, 150) 改为白色
print(f"修改后像素 (100, 150) 的 BGR 值: {img[100, 150]}")
# 修改单个通道的像素值
img[100, 151, 0] = 255 # 将像素 (100, 151) 的蓝色通道值设为 255
# 修改图像的一个区域 (使用 NumPy 切片)
# 将左上角 50x100 的区域染成黑色
img[0:50, 0:100] = [0, 0, 0]
# 分离和合并通道
b, g, r = cv2.split(img) # 分离为单独的灰度图像 (NumPy 数组)
# b 是蓝色通道的灰度图,g 是绿色通道的灰度图,r 是红色通道的灰度图
print(f"\n分离通道后,蓝色通道图片的形状: {b.shape}")
# 合并通道
# 注意:cv2.merge 期望一个通道列表或元组,顺序决定了最终图像的通道顺序
merged_img = cv2.merge([b, g, r]) # 按 BGR 顺序合并
merged_img_rgb = cv2.merge([r, g, b]) # 按 RGB 顺序合并 (但在 OpenCV 中显示时仍然是 BGR 解释)
# 显示分离和合并后的图像
cv2.imshow('Original Image', img)
cv2.imshow('Blue Channel', b) # 注意:灰度图显示时是灰度的
cv2.imshow('Merged BGR Image', merged_img) # 与原始图像相似 (除了修改的部分)
# cv2.imshow('Merged RGB Image', merged_img_rgb) # 颜色会看起来奇怪,因为 imshow 默认是 BGR
print("\n显示图像窗口...")
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
代码解释:
img.shape
、img.size
、img.dtype
是标准的 NumPy 数组属性,非常有用。- 通过 NumPy 的索引
img[row, col]
或img[row, col, channel]
可以访问和修改像素值。记住 OpenCV 是 (y, x) 或 (行, 列) 的坐标顺序。 - NumPy 的切片功能
img[y_start:y_end, x_start:x_end]
是操作图像区域的强大工具。 cv2.split(img)
将彩色图像分割成独立的 B、G、R 灰度通道图像。cv2.merge([b, g, r])
将单独的通道图像合并回一个彩色图像。注意合并的顺序很重要。
6. 在图像上绘制图形和文字
OpenCV 提供了一系列函数来在图像上绘制点、线、矩形、圆等基本图形以及文本。
“`python
import cv2
import numpy as np
创建一个空白的黑色图像作为画布
高度=500, 宽度=500, 3通道 (BGR), 数据类型=uint8
np.zeros 创建一个全为零的数组,对于 uint8 意味着黑色。
canvas = np.zeros((500, 500, 3), dtype=’uint8′)
绘制直线
cv2.line(图像, 起点坐标(x, y), 终点坐标(x, y), 颜色(B, G, R), 线条粗细)
cv2.line(canvas, (0, 0), (500, 500), (0, 255, 0), 5) # 绿色直线,从左上角到右下角
cv2.line(canvas, (500, 0), (0, 500), (0, 0, 255), 3) # 红色直线,从右上角到左下角
绘制矩形
cv2.rectangle(图像, 左上角坐标(x, y), 右下角坐标(x, y), 颜色(B, G, R), 线条粗细)
线条粗细为 -1 表示填充矩形
cv2.rectangle(canvas, (100, 100), (400, 400), (255, 0, 0), 2) # 蓝色矩形边框
cv2.rectangle(canvas, (150, 150), (350, 350), (0, 255, 255), -1) # 黄色填充矩形
绘制圆
cv2.circle(图像, 圆心坐标(x, y), 半径, 颜色(B, G, R), 线条粗细)
线条粗细为 -1 表示填充圆
cv2.circle(canvas, (250, 250), 75, (255, 255, 0), -1) # 青色填充圆
绘制点 (实际上是绘制一个半径为1或0的小圆或正方形)
cv2.circle(canvas, (50, 50), 3, (255, 255, 255), -1) # 白色填充点
或者直接修改像素值
canvas[50, 50] = [255, 255, 255] # 白色点
绘制文字
cv2.putText(图像, 文本内容, 左下角起始坐标(x, y), 字体, 字体缩放系数, 颜色, 线条粗细, 线条类型)
常用字体: cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_PLAIN, etc.
cv2.putText(canvas, ‘Hello OpenCV!’, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.putText(canvas, ‘Drawing Shapes’, (50, 480), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
显示绘制结果
cv2.imshow(‘Drawing on Canvas’, canvas)
print(“按任意键关闭绘图窗口…”)
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
代码解释:
- 我们首先创建了一个黑色的 NumPy 数组作为我们的画布。
cv2.line()
用于绘制直线,需要起点和终点的 (x, y) 坐标。cv2.rectangle()
用于绘制矩形,需要左上角和右下角的 (x, y) 坐标。cv2.circle()
用于绘制圆,需要圆心 (x, y) 坐标和半径。- 对于矩形和圆,将
thickness
参数设置为-1
可以实现填充效果。 cv2.putText()
用于在图像上添加文本。注意文本的起始坐标是文本框的左下角。
这些绘制函数非常实用,可以在图像上标记特定区域(如检测到的物体边框)、添加标签或创建简单的可视化效果。
7. 基本的图像变换:缩放、裁剪、平移和旋转
图像变换是改变图像大小、位置或方向的操作。
“`python
import cv2
import numpy as np
img = cv2.imread(‘test_image.jpg’)
if img is None:
print(“错误:无法读取图像文件。”)
else:
# 获取原始图像尺寸
height, width = img.shape[:2]
print(f”原始图像尺寸: 宽度={width}, 高度={height}”)
# 1. 缩放 (Resizing)
# cv2.resize(图像, 输出尺寸(宽度, 高度), fx, fy, interpolation)
# 输出尺寸: 可以直接指定 (width, height)
# fx, fy: 沿 x 和 y 方向的比例因子 (可以代替输出尺寸)
# interpolation: 插值方法,常用的有:
# cv2.INTER_AREA: 缩小图像时常用
# cv2.INTER_CUBIC: 放大图像时常用 (较慢)
# cv2.INTER_LINEAR: 默认方法,放大时常用 (速度快)
# 按比例缩放 (例如,缩小到原来的一半)
resized_img_half = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
print(f"缩放到一半尺寸: 宽度={resized_img_half.shape[1]}, 高度={resized_img_half.shape[0]}")
# 缩放到固定尺寸 (例如,300x200)
resized_img_fixed = cv2.resize(img, (300, 200), interpolation=cv2.INTER_LINEAR)
print(f"缩放到固定尺寸 (300x200): 宽度={resized_img_fixed.shape[1]}, 高度={resized_img_fixed.shape[0]}")
# 2. 裁剪 (Cropping) - 使用 NumPy 切片
# 裁剪通常不需要 OpenCV 函数,直接使用 NumPy 数组的切片功能即可
# 裁剪图像,只保留 (100, 50) 到 (400, 300) 的区域 (注意是行/高, 列/宽)
# 即从第 50 行到第 300 行,从第 100 列到第 400 列
cropped_img = img[50:301, 100:401]
print(f"裁剪后的尺寸: 宽度={cropped_img.shape[1]}, 高度={cropped_img.shape[0]}")
# 3. 平移 (Translation)
# 需要一个平移矩阵 M,形状通常是 2x3:
# M = [[1, 0, tx],
# [0, 1, ty]]
# tx 是沿 x 方向的平移量,ty 是沿 y 方向的平移量。
# cv2.warpAffine() 函数用于应用任意仿射变换 (包括平移、旋转、缩放等组合)。
tx, ty = 50, 100 # 向右平移 50 像素,向下平移 100 像素
M_translate = np.float32([[1, 0, tx], [0, 1, ty]])
# warpAffine(图像, 变换矩阵M, 输出图像尺寸(宽度, 高度))
translated_img = cv2.warpAffine(img, M_translate, (width, height)) # 输出尺寸与原图相同
# 4. 旋转 (Rotation)
# 可以使用 cv2.rotate() 进行 90, 180, 270 度的简单旋转
rotated_90_cw = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) # 顺时针旋转 90 度
rotated_180 = cv2.rotate(img, cv2.ROTATE_180) # 旋转 180 度
# 对于任意角度的旋转,需要先获取旋转矩阵
# cv2.getRotationMatrix2D(旋转中心(x, y), 旋转角度(度), 缩放因子)
center = (width // 2, height // 2) # 以图像中心为旋转中心
angle = 45 # 旋转 45 度
scale = 1.0 # 不进行缩放
M_rotate = cv2.getRotationMatrix2D(center, angle, scale)
# warpAffine 应用旋转矩阵
rotated_45 = cv2.warpAffine(img, M_rotate, (width, height)) # 输出尺寸与原图相同
# 显示结果
cv2.imshow('Original Image', img)
cv2.imshow('Resized (Half)', resized_img_half)
cv2.imshow('Resized (Fixed 300x200)', resized_img_fixed)
cv2.imshow('Cropped', cropped_img)
cv2.imshow('Translated', translated_img)
cv2.imshow('Rotated 90 CW', rotated_90_cw)
cv2.imshow('Rotated 45', rotated_45)
print("\n显示变换后的图像窗口...")
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
代码解释:
cv2.resize()
用于改变图像的尺寸。你可以指定目标尺寸或缩放比例。选择合适的插值方法可以获得更好的视觉效果。- 裁剪通过简单的 NumPy 切片
img[y_start:y_end, x_start:x_end]
实现,非常直观。 - 平移和任意角度旋转属于仿射变换,需要先计算出变换矩阵 (
M
),然后使用cv2.warpAffine()
应用这个矩阵。 cv2.getRotationMatrix2D()
方便地为旋转生成变换矩阵。cv2.rotate()
提供了固定的 90/180/270 度旋转,更简单。
8. 颜色空间:BGR、灰度图和 HSV
图像通常在不同的颜色空间中表示。除了我们熟悉的 BGR (OpenCV 默认) 和 RGB,HSV (Hue, Saturation, Value) 也是一个非常重要的颜色空间,特别适用于颜色检测和分割。
- BGR/RGB: 基于加色模型,通过混合不同强度的红、绿、蓝光来生成颜色。
- 灰度图: 只包含亮度信息,用于许多不依赖颜色信息的图像处理算法。
- HSV: 基于人类对颜色的感知。
- Hue (色调): 描述颜色的类型 (如红色、蓝色)。通常以角度表示,0-360度或 0-180度 (在 OpenCV 中)。
- Saturation (饱和度): 描述颜色的纯度或鲜艳程度。0 表示灰色,255 表示最鲜艳的颜色。
- Value (明度/亮度): 描述颜色的亮度。0 表示黑色,255 表示最亮。
HSV 空间对于颜色检测非常有用,因为色调 H 通道对光照变化不敏感,更容易分离出特定颜色。
“`python
import cv2
import numpy as np
img = cv2.imread(‘test_image.jpg’)
if img is None:
print(“错误:无法读取图像文件。”)
else:
# 颜色空间转换
# cv2.cvtColor(图像, 颜色空间转换标志)
# 常用的标志:
# cv2.COLOR_BGR2GRAY: BGR 转为灰度
# cv2.COLOR_GRAY2BGR: 灰度 转为 BGR (用于显示灰度图时仍然需要3通道)
# cv2.COLOR_BGR2HSV: BGR 转为 HSV
# cv2.COLOR_HSV2BGR: HSV 转为 BGR
# 转换为灰度图
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(f"灰度图形状: {gray_img.shape}") # 灰度图只有2维
# 转换为 HSV
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
print(f"HSV 图形状: {hsv_img.shape}") # HSV 图也是3维
# 分离 HSV 通道
h, s, v = cv2.split(hsv_img)
# 显示结果
cv2.imshow('Original BGR Image', img)
cv2.imshow('Grayscale Image', gray_img)
cv2.imshow('HSV Image', hsv_img)
cv2.imshow('Hue Channel', h) # 显示独立的通道 (会以灰度图形式显示)
cv2.imshow('Saturation Channel', s)
cv2.imshow('Value Channel', v)
print("\n显示不同颜色空间的图像...")
cv2.waitKey(0)
cv2.destroyAllWindows()
# 示例:检测特定颜色范围 (例如,蓝色)
# 在 HSV 空间中定义蓝色的范围
# 需要根据实际情况调整这些值,通常通过颜色拾取工具或实验确定。
# Hue 的范围在 OpenCV 中是 0-180,所以 180 对应 360 度
# 蓝色大致在 Hue 100-120 左右
lower_blue = np.array([100, 50, 50]) # 蓝色范围的下限 (H, S, V)
upper_blue = np.array([120, 255, 255]) # 蓝色范围的上限 (H, S, V)
# cv2.inRange() 函数用于根据像素值范围创建二值掩码 (Mask)
# 参数1: 输入图像 (通常是 HSV 图像)
# 参数2: 像素值范围的下限
# 参数3: 像素值范围的上限
# 返回值: 一个二值图像 (Mask),在指定范围内的像素为白色 (255),否则为黑色 (0)
blue_mask = cv2.inRange(hsv_img, lower_blue, upper_blue)
# 使用掩码提取原始图像中指定颜色的区域
# cv2.bitwise_and() 对两个图像进行按位与操作
# 参数1, 参数2: 输入图像
# mask: 可选的掩码。只有掩码中对应的像素为非零时,输出图像的对应像素才由输入图像的对应像素决定。
blue_segmentation = cv2.bitwise_and(img, img, mask=blue_mask)
cv2.imshow('Blue Mask', blue_mask) # 显示蓝色区域的二值掩码
cv2.imshow('Blue Segmentation', blue_segmentation) # 显示从原图中提取的蓝色区域
print("\n显示颜色检测结果...")
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
代码解释:
cv2.cvtColor()
是进行颜色空间转换的核心函数。cv2.split()
和cv2.merge()
同样适用于 HSV 图像的通道分离和合并。cv2.inRange()
是在 HSV 空间中进行颜色检测的关键函数。它根据指定的上下限阈值,生成一个二值掩码,该掩码标识出图像中属于该颜色范围的像素。cv2.bitwise_and()
结合原始图像和颜色掩码,可以提取出原始图像中符合颜色条件的区域。
9. 图像阈值化:二值化和图像分割基础
阈值化是一种简单的图像分割技术,它将图像像素根据其强度与某个阈值的关系,分为两个区域(通常是前景和背景)。最常见的是二值化,将图像转换为只有黑(0)和白(255)两个值的图像。
“`python
import cv2
import numpy as np
img = cv2.imread(‘test_image.jpg’, cv2.IMREAD_GRAYSCALE) # 读取为灰度图
if img is None:
print(“错误:无法读取图像文件。”)
else:
# 1. 简单阈值化
# cv2.threshold(输入图像, 阈值, 最大值, 阈值类型)
# 输入图像必须是灰度图像
# 阈值: 用于比较的固定值
# 最大值: 当像素值高于或低于阈值时赋给它的值 (通常是 255)
# 阈值类型 (常用的):
# cv2.THRESH_BINARY: 如果像素值 > 阈值,设为最大值;否则设为 0
# cv2.THRESH_BINARY_INV: 如果像素值 > 阈值,设为 0;否则设为最大值
# cv2.THRESH_TRUNC: 如果像素值 > 阈值,设为阈值;否则保持不变
# cv2.THRESH_TOZERO: 如果像素值 > 阈值,保持不变;否则设为 0
# cv2.THRESH_TOZERO_INV: 如果像素值 > 阈值,设为 0;否则保持不变
# 函数返回两个值:ret (使用的阈值) 和 thresholded_img (结果图像)
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 二值化,阈值127
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) # 反色二值化
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC) # 截断
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO) # 保持大于阈值
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV) # 保持小于阈值
cv2.imshow('Original Grayscale', img)
cv2.imshow('THRESH_BINARY', thresh1)
cv2.imshow('THRESH_BINARY_INV', thresh2)
cv2.imshow('THRESH_TRUNC', thresh3)
cv2.imshow('THRESH_TOZERO', thresh4)
cv2.imshow('THRESH_TOZERO_INV', thresh5)
print("\n显示简单阈值化结果...")
cv2.waitKey(0)
cv2.destroyAllWindows()
# 2. 自适应阈值化 (Adaptive Thresholding)
# 当图像光照不均匀时,固定阈值效果不好。自适应阈值化根据像素周围的小区域来计算不同的阈值。
# cv2.adaptiveThreshold(输入图像, 最大值, 自适应方法, 阈值类型, 块大小, C)
# 自适应方法:
# cv2.ADAPTIVE_THRESH_MEAN_C: 阈值是周围区域像素的平均值减去 C
# cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 阈值是周围区域像素加权平均值 (高斯窗口) 减去 C
# 块大小 (blockSize): 用于计算阈值的像素邻域的大小 (必须是奇数)
# C: 一个常数,从计算出的平均值或加权平均值中减去。
# 使用均值方法,块大小11,C=2
adaptive_mean = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
# 使用高斯方法,块大小11,C=2
adaptive_gaussian = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
cv2.imshow('Adaptive Mean Thresholding', adaptive_mean)
cv2.imshow('Adaptive Gaussian Thresholding', adaptive_gaussian)
print("\n显示自适应阈值化结果...")
cv2.waitKey(0)
cv2.destroyAllWindows()
# 3. Otsu's Binarization (大津法)
# 如果图像的像素直方图呈现双峰,Otsu 法可以自动找到最佳阈值。
# cv2.threshold() 函数可以与 cv2.THRESH_OTSU 标志一起使用。
# 当使用 Otsu 时,传递的阈值参数会被忽略,函数会计算并返回 Otsu 找到的最佳阈值。
# 注意:输入图像应该是单通道 (灰度)。如果不是,Otsu 可能无法正确工作。
# 在阈值类型中添加 cv2.THRESH_OTSU
ret_otsu, otsu_thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
print(f"Otsu 自动找到的最佳阈值: {ret_otsu}")
cv2.imshow('Otsu Thresholding', otsu_thresh)
print("\n显示 Otsu 阈值化结果...")
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
代码解释:
- 阈值化通常在灰度图上进行。
cv2.threshold()
是简单的固定阈值二值化,需要手动指定阈值。它有多种类型 (THRESH_BINARY
,THRESH_BINARY_INV
等)。cv2.adaptiveThreshold()
适用于光照不均匀的图像,它根据局部区域计算阈值。- Otsu’s Binarization (
cv2.THRESH_OTSU
) 会自动计算一个“最佳”全局阈值,适用于直方图呈双峰的图像。使用时,cv2.threshold()
的第二个参数(手动阈值)会被忽略。
10. 图像平滑(滤波):去噪与模糊
图像平滑通常用于去除图像中的噪声或模糊图像,以便后续处理(如边缘检测)。这通常通过卷积操作实现,使用一个“核”(kernel)或“窗口”在图像上滑动,计算中心像素的新值。
“`python
import cv2
import numpy as np
img = cv2.imread(‘test_image.jpg’)
if img is None:
print(“错误:无法读取图像文件。”)
else:
# 在进行滤波前,通常会先加入一些噪声来更好地展示滤波效果
# 这里简单创建一个噪声图像并叠加,实际中噪声可能来自传感器等
noise = np.random.normal(0, 20, img.shape).astype(‘uint8’) # 创建一些高斯噪声
# 注意:直接相加可能导致像素值溢出255,简单的clip处理或使用更高位深图像
noisy_img = cv2.add(img, noise) # 叠加噪声
# 1. 均值滤波 (Averaging)
# cv2.blur(图像, 核大小(宽度, 高度))
# 核大小是一个 (ksize_x, ksize_y) 的元组,表示核的宽度和高度。
# 核内的所有像素权重相等,新值是核内所有像素的平均值。
kernel_size = (5, 5) # 使用 5x5 的核
avg_blur = cv2.blur(noisy_img, kernel_size)
# 2. 高斯滤波 (Gaussian Blurring)
# cv2.GaussianBlur(图像, 核大小(宽度, 高度), sigmaX)
# 高斯滤波使用加权平均,离中心像素越近的像素权重越高。
# 核大小 (ksize_x, ksize_y): 核的大小,必须是奇数。
# sigmaX: 高斯核沿 X 方向的标准差。如果 sigmaY 为 0,则 sigmaY = sigmaX;如果两者都为 0,则根据核大小计算。
# 推荐只指定核大小和 sigmaX。
gaussian_blur = cv2.GaussianBlur(noisy_img, (5, 5), 0)
# 3. 中值滤波 (Median Blurring)
# cv2.medianBlur(图像, 核大小(ksize))
# 中值滤波将核内的所有像素排序,取中间值作为新值。
# 对椒盐噪声 (salt-and-pepper noise) 非常有效。
# 核大小 ksize 必须是一个大于 1 的奇数。
median_blur = cv2.medianBlur(noisy_img, 5) # 使用 5x5 的核 (ksize 指定边长)
# 4. 双边滤波 (Bilateral Filtering) - 保边去噪
# cv2.bilateralFilter(图像, d, sigmaColor, sigmaSpace)
# 双边滤波在平滑噪声的同时,能够保留边缘信息。速度较慢。
# d: 像素邻域的直径。如果 d <= 0,则从 sigmaSpace 计算。
# sigmaColor: 颜色空间的标准差。值越大,相差越大的颜色会被混合。
# sigmaSpace: 坐标空间的标准差。值越大,越远的像素会互相影响。
bilateral_filter = cv2.bilateralFilter(noisy_img, 9, 75, 75)
# 显示结果
cv2.imshow('Original Image', img)
cv2.imshow('Noisy Image', noisy_img) # 显示加噪后的图像
cv2.imshow('Averaging Blur', avg_blur)
cv2.imshow('Gaussian Blur', gaussian_blur)
cv2.imshow('Median Blur', median_blur)
cv2.imshow('Bilateral Filter', bilateral_filter)
print("\n显示滤波结果...")
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
代码解释:
- 我们先创建一个加噪的图像来演示滤波效果。
cv2.blur()
实现简单的均值滤波,所有邻域像素权重相等。cv2.GaussianBlur()
实现高斯滤波,使用加权平均,中心像素权重高。这是最常用的平滑方法之一。cv2.medianBlur()
实现中值滤波,对椒盐噪声特别有效。cv2.bilateralFilter()
是一种非线性滤波,它在平滑的同时会保留图像的边缘,效果通常比线性滤波器更好,但计算量更大。
选择哪种滤波方法取决于图像噪声的类型和期望的效果。
11. 边缘检测:Canny 算法
边缘是图像中像素强度发生剧烈变化的地方,它们通常对应着物体的轮廓或边界。边缘检测是图像处理中的一个重要步骤,常用于特征提取。Canny 边缘检测是其中一种经典且效果很好的算法。
Canny 算法主要包括以下几个步骤:
1. 高斯模糊: 消除图像噪声。
2. 计算梯度: 计算图像中每个像素的梯度幅值和方向。
3. 非极大值抑制: 细化边缘,只保留梯度方向上的局部最大值。
4. 双阈值检测: 使用两个阈值 (maxVal 和 minVal) 来确定最终的边缘。高于 maxVal 的是强边缘;介于 minVal 和 maxVal 之间的是弱边缘,如果它们与强边缘相连,则也被认为是边缘;低于 minVal 的会被抑制。
5. 边缘跟踪: 通过弱边缘连接强边缘,形成完整的边缘轮廓。
“`python
import cv2
import numpy as np
img = cv2.imread(‘test_image.jpg’, cv2.IMREAD_GRAYSCALE) # 通常在灰度图上进行边缘检测
if img is None:
print(“错误:无法读取图像文件。”)
else:
# 在进行 Canny 边缘检测之前,通常会先进行高斯模糊降噪
# GaussianBlur(输入图像, 核大小, sigmaX)
blurred_img = cv2.GaussianBlur(img, (5, 5), 0) # 使用 5×5 高斯核进行模糊
# Canny 边缘检测
# cv2.Canny(输入图像, minVal, maxVal)
# 输入图像: 8位单通道图像 (灰度图)
# minVal: 双阈值中的较低阈值
# maxVal: 双阈值中的较高阈值
# 建议 maxVal:minVal 的比例在 2:1 或 3:1 之间
edges = cv2.Canny(blurred_img, 100, 200) # minVal=100, maxVal=200
# 你也可以直接在原始灰度图上进行 Canny,但通常先模糊效果更好
# edges_direct = cv2.Canny(img, 100, 200)
# 显示结果
cv2.imshow('Original Grayscale', img)
cv2.imshow('Blurred Grayscale', blurred_img)
cv2.imshow('Canny Edges', edges)
# cv2.imshow('Canny Edges (Direct)', edges_direct)
print("\n显示 Canny 边缘检测结果...")
cv2.waitKey(0)
cv2.destroyAllWindows()
“`
代码解释:
- 边缘检测通常在灰度图像上进行。
- 我们先对图像进行高斯模糊,这有助于去除噪声,防止检测到伪边缘。
cv2.Canny()
函数执行 Canny 边缘检测算法。两个阈值minVal
和maxVal
是其关键参数,它们显著影响检测结果,需要根据具体图像进行调整。
Canny 边缘检测结果是一个二值图像,边缘像素为白色 (255),非边缘像素为黑色 (0)。
12. 处理视频:读取、显示摄像头和视频文件
OpenCV 不仅可以处理静态图像,还可以处理视频流,无论是来自摄像头还是视频文件。视频本质上是一系列连续的图像帧。
“`python
import cv2
1. 从摄像头读取视频流
cv2.VideoCapture(索引或文件路径)
参数可以是设备索引 (通常 0 代表默认摄像头) 或视频文件路径字符串。
cap = cv2.VideoCapture(0) # 尝试打开默认摄像头
检查摄像头是否成功打开
if not cap.isOpened():
print(“错误:无法打开摄像头。请检查设备索引或连接。”)
exit() # 如果打不开就退出程序
print(“按 ‘q’ 键退出视频窗口…”)
while True:
# 逐帧读取视频
# cap.read() 返回两个值:
# ret: 布尔值,如果正确读取帧,则为 True;否则为 False (例如,视频结束或读取错误)。
# frame: 读取到的图像帧 (一个 NumPy 数组)。
ret, frame = cap.read()
# 如果帧没有正确读取,则 ret 为 False,我们应该跳出循环
if not ret:
print("无法读取帧(视频文件已结束或出现错误)")
break
# 你可以在这里对每一帧进行图像处理操作
# 例如,将每一帧转换为灰度
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 显示原始帧和处理后的帧
cv2.imshow('Original Feed', frame)
cv2.imshow('Grayscale Feed', gray_frame) # 显示灰度帧
# 等待按键,参数表示等待时间(毫秒)
# 在视频播放中,通常等待一个短时间 (例如 1 毫秒) 以便显示连续的帧
# cv2.waitKey(1) 会在等待 1ms 后返回按下的键的 ASCII 值,如果没有按键,返回 -1
# ord('q') 返回字符 'q' 的 ASCII 值
if cv2.waitKey(1) & 0xFF == ord('q'):
break # 如果按下 'q' 键,退出循环
循环结束后,释放摄像头资源并关闭所有窗口
cap.release()
cv2.destroyAllWindows()
2. 从视频文件读取视频 (示例)
如果想读取视频文件,只需将 cv2.VideoCapture() 的参数改为文件路径
例如: cap = cv2.VideoCapture(‘my_video.mp4’)
后续读取和处理循环与摄像头类似。
注意:处理大型视频文件时,性能可能是一个考虑因素。
“`
代码解释:
cv2.VideoCapture(0)
创建一个VideoCapture
对象,用于连接到默认摄像头。如果参数是视频文件路径,则读取该文件。cap.isOpened()
检查是否成功打开摄像头或视频文件。while True:
循环用于连续读取视频帧。cap.read()
读取视频流中的下一帧。ret
表示是否成功读取,frame
是读取到的图像数据。- 在循环内,你可以对
frame
这个 NumPy 数组进行任何之前学过的图像处理操作。 cv2.imshow()
显示当前帧。cv2.waitKey(1)
用于控制视频播放的速度,等待 1 毫秒。如果在这 1 毫秒内检测到按键,就返回按键值。& 0xFF == ord('q')
是一个标准的跨平台方法,用于检查按下的键是否是 ‘q’。cap.release()
释放视频捕获对象占用的资源。cv2.destroyAllWindows()
关闭所有窗口。
13. 综合应用示例:简单颜色检测与对象追踪
我们将结合前面学到的颜色空间转换、阈值化和形态学操作(腐蚀和膨胀,用于处理二值图像中的小噪声和连接区域)来实现一个简单的颜色检测和追踪功能。
“`python
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print(“错误:无法打开摄像头。”)
exit()
print(“按 ‘q’ 键退出…”)
定义要检测的颜色范围 (例如,检测红色物体)
在 HSV 空间中定义红色有两个范围,因为 Hue 是一个圆环 (0 和 180 都代表红色)
这里以检测物体上的特定颜色为例,需要根据实际情况调整
例如,如果我们想检测一个红色球,可以在HSV拾取器的帮助下找到其HSV范围
lower_red1 = np.array([0, 100, 100])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 100, 100])
upper_red2 = np.array([180, 255, 255])
为了简化示例,我们检测一个容易拾取且连续的颜色范围,例如绿色
需要根据实际的绿色物体和光照条件来调整这些值
可以创建一个简单的HSV拾取器脚本来帮助确定这些范围
lower_green = np.array([40, 40, 40]) # 绿色下限 (H, S, V)
upper_green = np.array([80, 255, 255]) # 绿色上限 (H, S, V)
while True:
ret, frame = cap.read()
if not ret:
print(“无法读取帧”)
break
# 将帧从 BGR 转换为 HSV 颜色空间
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 根据定义的颜色范围创建二值掩码
# 在指定范围内的像素在 mask 中为白色(255),否则为黑色(0)
mask = cv2.inRange(hsv, lower_green, upper_green)
# 对掩码进行一些形态学操作以去除噪声和填充小孔
# 腐蚀 (Erosion): 移除图像中的小白点 (噪声)
# np.ones((5,5),np.uint8) 创建一个 5x5 的卷积核,元素都是 1
kernel = np.ones((5,5),np.uint8)
mask = cv2.erode(mask, kernel, iterations=1) # 腐蚀操作
# 膨胀 (Dilate): 恢复腐蚀后缩小的前景区域,并连接临近的前景区域
mask = cv2.dilate(mask, kernel, iterations=1) # 膨胀操作
# 可选:使用掩码提取原始图像中检测到的颜色区域
# result = cv2.bitwise_and(frame, frame, mask=mask)
# 查找掩码中的轮廓 (Contour)
# cv2.findContours() 函数用于查找二值图像中的轮廓
# 参数1: 输入的二值图像 (这里是处理后的 mask)
# 参数2: 轮廓检索模式 (例如 cv2.RETR_EXTERNAL 只检索外层轮廓)
# 参数3: 轮廓近似方法 (例如 cv2.CHAIN_APPROX_SIMPLE 压缩水平、垂直、对角线段,只保留端点)
# 返回值: contours (轮廓列表) 和 hierarchy (层级信息)
# 注意: findContours 在 OpenCV 3 和 4 中返回值的顺序略有不同,OpenCV 4 返回 contours, hierarchy
contours, hierarchy = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 遍历找到的轮廓
if len(contours) > 0:
# 找到最大的轮廓 (假设最大的轮廓是我们想要追踪的对象)
c = max(contours, key=cv2.contourArea)
# 计算最大轮廓的边界框 (bounding box)
# cv2.boundingRect() 返回 (x, y, w, h)
x, y, w, h = cv2.boundingRect(c)
# 在原始帧上绘制边界框
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # 绿色矩形
# 可选:计算轮廓的中心并绘制点
M = cv2.moments(c) # 计算轮廓的力矩
# 避免除以零的错误
if M["m00"] > 0:
center_x = int(M["m10"] / M["m00"])
center_y = int(M["m01"] / M["m00"])
center = (center_x, center_y)
cv2.circle(frame, center, 5, (0, 0, 255), -1) # 红色填充圆点
# 可以在此处添加追踪逻辑,例如记录中心点的位置
# 显示结果帧
cv2.imshow('Frame', frame)
cv2.imshow('Mask', mask) # 显示掩码
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
释放资源
cap.release()
cv2.destroyAllWindows()
“`
代码解释:
- 我们从摄像头捕获视频流。
- 每一帧都被转换为 HSV 颜色空间。
cv2.inRange()
根据预设的 HSV 范围创建二值掩码,分离出目标颜色区域。- 腐蚀 (
cv2.erode
) 和膨胀 (cv2.dilate
) 是基本的形态学操作,用于优化二值掩码,去除小噪声点并连接相近的区域。 cv2.findContours()
在处理后的掩码上查找对象的轮廓。- 我们找到最大的轮廓 (
cv2.contourArea
),并使用cv2.boundingRect()
计算其外接矩形。 - 最后,在原始帧上绘制这个边界框,实现简单的对象追踪的可视化。
这个示例展示了如何将颜色空间转换、阈值化、形态学操作和轮廓检测结合起来完成一个实际任务。
14. 综合应用示例:使用 Haarcascades 进行人脸检测
人脸检测是计算机视觉中最经典和广泛应用的任务之一。OpenCV 提供了预训练好的 Haar 特征分类器 (Haarcascades),可以方便地用于检测人脸、眼睛等。
“`python
import cv2
import numpy as np
加载预训练的 Haarcascade 分类器 XML 文件
这些 XML 文件通常位于 OpenCV 安装目录的 ‘haarcascades’ 子文件夹中
或者你可以在 OpenCV 的 GitHub 仓库中找到并下载它们:
https://github.com/opencv/opencv/tree/master/data/haarcascades
请将 ‘haarcascade_frontalface_default.xml’ 文件放到你的代码同级目录,
或指定其完整路径。
face_cascade = cv2.CascadeClassifier(‘haarcascade_frontalface_default.xml’)
如果你想检测眼睛,也可以加载眼睛的分类器
eye_cascade = cv2.CascadeClassifier(‘haarcascade_eye.xml’)
检查分类器是否成功加载
if face_cascade.empty():
print(“错误:无法加载 Haarcascade XML 文件。请检查文件路径。”)
exit()
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print(“错误:无法打开摄像头。”)
exit()
print(“按 ‘q’ 键退出…”)
while True:
ret, frame = cap.read()
if not ret:
print(“无法读取帧”)
break
# 人脸检测通常在灰度图上进行,以提高速度
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 执行人脸检测
# cascade.detectMultiScale(输入图像, scaleFactor, minNeighbors)
# 输入图像: 8位灰度图像
# scaleFactor: 图像缩小的比例。每次图像金字塔缩小时的比例因子。
# minNeighbors: 每个候选矩形应该保留的邻居个数。这个参数影响检测结果的数量和质量。值越大,误检越少,但可能漏检。
# 返回值: 检测到的物体矩形列表。每个矩形是 (x, y, w, h)。
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
# 遍历检测到的人脸矩形,并在原始帧上绘制
for (x, y, w, h) in faces:
# 在原始彩色帧上绘制矩形
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) # 蓝色矩形
# 如果想在检测到的人脸区域内检测眼睛,可以这样做:
# 只在当前人脸的区域内进行眼睛检测
# roi_gray = gray[y:y+h, x:x+w] # 灰度人脸区域
# roi_color = frame[y:y+h, x:x+w] # 彩色人脸区域
# eyes = eye_cascade.detectMultiScale(roi_gray)
# for (ex, ey, ew, eh) in eyes:
# cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 2) # 绿色眼睛矩形
# 显示结果帧
cv2.imshow('Face Detection', frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
释放资源
cap.release()
cv2.destroyAllWindows()
“`
代码解释:
- 我们首先使用
cv2.CascadeClassifier()
加载预训练的 Haarcascade XML 文件。 - 在视频循环中,我们将每一帧转换为灰度图。
cascade.detectMultiScale()
函数执行实际的检测。scaleFactor
和minNeighbors
是重要的参数,需要根据实际应用场景进行调整。- 函数返回一个列表,其中每个元素是一个 NumPy 数组,表示检测到的一个目标(人脸)的外接矩形
(x, y, w, h)
。 - 我们遍历这些矩形,并使用
cv2.rectangle()
在原始彩色帧上绘制它们。 - 示例中还注释掉了眼睛检测的代码,展示了如何在检测到的人脸区域内进一步检测眼睛。
Haarcascades 检测器相对简单快速,但对光照、角度、遮挡等比较敏感。尽管如此,它仍然是一个非常好的入门级人脸检测方法。
15. 总结与进阶:下一步的学习方向
恭喜你!通过学习本指南,你已经掌握了 Python OpenCV 的基本操作和核心概念,包括:
- 安装与环境配置
- 图像的读取、显示和保存
- 图像属性和像素操作
- 绘制图形和文字
- 图像变换(缩放、裁剪、平移、旋转)
- 颜色空间转换
- 图像阈值化
- 图像平滑(滤波)
- 边缘检测
- 视频处理(读取摄像头和文件)
- 简单的综合应用(颜色检测、人脸检测)
这仅仅是计算机视觉领域的冰山一角。OpenCV 库非常庞大,包含了更多强大的功能。接下来,你可以继续深入学习:
- 形态学操作: 开运算、闭运算、梯度、顶帽、黑帽等,用于处理二值图像。
- 图像梯度与边界: Sobel, Scharr, Laplacian 等算子。
- 轮廓: 更深入地学习轮廓的查找、分析、绘制和属性(面积、周长、凸包、拟合等)。
- 直方图: 图像像素值的分布,用于图像增强、对比度调整、直方图比较等。
- 模板匹配: 在大图像中查找小模板图像的位置。
- 特征检测与描述: SIFT, SURF, ORB, AKAZE 等算法,用于在不同图像中找到对应的关键点和描述符,常用于图像拼接、物体识别等。
- 物体检测: 更先进的方法如 HOG+SVM、SSD、YOLO 等。
- 物体追踪: KCF, MOSSE, CSRT 等追踪算法。
- 图像分割: GrabCut、分水岭算法等。
- 校准与三维重建: 摄像头标定、双目视觉、运动恢复结构 (SfM)。
- 机器学习模块: OpenCV 也集成了 SVM, K-Means 等机器学习算法。
- 深度学习模块 (DNN): 使用 OpenCV 部署和运行预训练的深度学习模型 (如分类、检测模型)。
推荐的学习资源:
- OpenCV 官方文档: 这是最权威的资源,包含函数的详细说明和示例代码 (docs.opencv.org).
- OpenCV-Python Tutorials: 官方提供的 Python 教程,覆盖了更多主题 (docs.opencv.org/4.x/d6/d00/tutorial_py_root.html).
- 其他在线教程和课程: Coursera, Udacity, Bilibili, YouTube 上有许多优秀的计算机视觉课程和 OpenCV 教程。
最重要的一点是: 动手实践!尝试用 OpenCV 解决一些小问题,修改示例代码,理解每个函数的作用和参数。祝你在计算机视觉的学习旅程中取得成功!