OpenCV Python 入门教程 – wiki基地


OpenCV Python 入门教程:从零开始掌握计算机视觉基础

计算机视觉(Computer Vision)是人工智能领域的一个重要分支,旨在让计算机能够“看懂”图像和视频,并从中提取有用信息。而 OpenCV(Open Source Computer Vision Library)是目前最流行、功能最强大的开源计算机视觉库之一,它提供了丰富的函数和工具,涵盖了图像处理、对象检测、人脸识别、特征匹配等众多计算机视觉任务。

结合 Python 简洁易学的语法,OpenCV Python API 成为了许多开发者和研究人员快速入门和实现计算机视觉应用的优选组合。

本教程将带领你从零开始,一步步掌握 OpenCV Python 的基础知识和常用操作,为你打开计算机视觉的大门。

文章目录

  1. 引言:什么是 OpenCV?为什么选择 Python?
  2. 准备工作:安装 OpenCV Python
  3. 加载、显示与保存图像
    • 读取图像:cv2.imread()
    • 显示图像:cv2.imshow(), cv2.waitKey(), cv2.destroyAllWindows()
    • 保存图像:cv2.imwrite()
    • 图像的基本属性:形状、数据类型、像素值
    • 访问和修改像素值
  4. 图像基本操作与处理
    • 图像裁剪(ROI)
    • 图像缩放:cv2.resize()
    • 图像旋转:cv2.getRotationMatrix2D(), cv2.warpAffine()
    • 图像翻转:cv2.flip()
    • 图像通道分离与合并:cv2.split(), cv2.merge()
    • 图像加法(像素操作)
    • 图像混合(权重加法):cv2.addWeighted()
  5. 在图像上绘制图形与文本
    • 绘制直线:cv2.line()
    • 绘制矩形:cv2.rectangle()
    • 绘制圆形:cv2.circle()
    • 绘制多边形:cv2.polylines()
    • 添加文本:cv2.putText()
  6. 颜色空间转换与基础图像处理技术
    • 颜色空间:BGR 与 HSV
    • 颜色空间转换:cv2.cvtColor()
    • 图像阈值化:cv2.threshold(), cv2.adaptiveThreshold()
    • 图像平滑(模糊):cv2.blur(), cv2.GaussianBlur(), cv2.medianBlur()
  7. 处理视频:读取、显示、保存与摄像头捕获
    • 读取视频文件或摄像头:cv2.VideoCapture()
    • 读取视频帧:cap.read()
    • 显示视频帧
    • 释放视频对象:cap.release()
    • 保存视频:cv2.VideoWriter()
    • 实时摄像头捕获与处理示例
  8. 一个简单的应用示例:颜色检测与跟踪
  9. 进一步学习:OpenCV 的更多功能
  10. 总结与展望

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

什么是 OpenCV?

OpenCV(Open Source Computer Vision Library)是一个跨平台的开源计算机视觉库,由 Intel 公司发起并持续发展。它包含了 2500 多个经过优化的算法,几乎涵盖了计算机视觉和机器学习领域的各种常见算法。其 C++ API 功能最完整、性能最高,但同时也提供了 Python, Java 等多种语言的接口。OpenCV 不仅是一个算法库,更是一个生态系统,拥有庞大的用户社区和丰富的资源。

OpenCV 的应用场景非常广泛,包括但不限于:

  • 图像和视频分析(图像增强、去噪、特征提取)
  • 对象检测和识别(人脸、物体)
  • 姿态估计
  • 运动跟踪
  • 图像拼接和三维重建
  • 医学影像分析
  • 机器人视觉导航
  • 自动驾驶

为什么选择 Python?

OpenCV 提供了 Python API,这使得开发者可以使用 Python 来编写计算机视觉程序。相比于 C++,Python 具有以下优势:

  • 简洁易学: Python 语法简单,入门门槛低,可以快速编写代码。
  • 开发效率高: Python 的动态特性和丰富的第三方库(如 NumPy, Matplotlib)可以极大地提高开发效率。NumPy 数组与 OpenCV 图像格式天然兼容,这是 Python API 的一大优势。
  • 生态系统丰富: Python 拥有强大的科学计算和数据处理生态,可以方便地与机器学习框架(TensorFlow, PyTorch, scikit-learn)等结合使用。
  • 原型开发快速: Python 适合进行快速原型开发和实验。

虽然在处理大规模图像或对实时性要求极高的场景下,C++ 版本可能性能更优,但对于大多数学习、研究以及非极致性能需求的任务,Python 版本的 OpenCV 是一个非常便捷和高效的选择。

2. 准备工作:安装 OpenCV Python

在开始之前,你需要安装 Python 环境以及 OpenCV 库。推荐使用 Python 3.6 或更高版本。

安装 OpenCV Python 最简单的方式是使用 pip 包管理器。请打开你的终端或命令提示符,并运行以下命令:

bash
pip install opencv-python numpy

解释:

  • pip install opencv-python: 这个命令会安装主要的 OpenCV 模块。opencv-python 是官方维护的 Python 接口包。
  • numpy: OpenCV 在 Python 中使用 NumPy 数组来存储和处理图像数据。安装 NumPy 是必须的。

可选(如果你需要额外的功能):

有时候,你可能需要一些额外的贡献模块(contrib),例如 SIFT、SURF(这些是专利算法,不在主模块中)或者更高级的特征检测算法。你可以安装 opencv-contrib-python

bash
pip install opencv-contrib-python numpy

注意: 不要同时安装 opencv-pythonopencv-contrib-python,它们可能会冲突。通常,对于初学者,opencv-python 已经足够。

验证安装:

安装完成后,你可以打开 Python 解释器或者创建一个 Python 脚本来验证安装是否成功:

python
import cv2
print(cv2.__version__)

如果你能成功导入 cv2 并且打印出 OpenCV 的版本号,说明安装成功!

为了更好地管理项目依赖和避免不同项目之间的库冲突,强烈建议使用虚拟环境(Virtual Environment)。你可以使用 venvconda 创建虚拟环境,然后在虚拟环境中安装 OpenCV 和其他依赖。

例如,使用 venv

bash
python -m venv myenv # 创建一个名为 myenv 的虚拟环境
source myenv/bin/activate # 激活虚拟环境 (Linux/macOS)
myenv\Scripts\activate # 激活虚拟环境 (Windows)
pip install opencv-python numpy # 在激活的虚拟环境中安装

3. 加载、显示与保存图像

图像是计算机视觉的基础,我们首先学习如何在 OpenCV 中处理静态图像文件。

你需要准备一张图片文件,例如 input_image.jpg,放在你的 Python 脚本同一个目录下,或者提供图片的完整路径。

“`python
import cv2
import numpy as np # 虽然这里暂时不用numpy,但通常会一起导入

1. 读取图像

cv2.imread() 函数用于从指定文件加载图像。

第一个参数是文件路径。

第二个参数是标志位,指定读取图像的方式:

cv2.IMREAD_COLOR: 彩色图像,忽略透明度。默认值。

cv2.IMREAD_GRAYSCALE: 灰度图像。

cv2.IMREAD_UNCHANGED: 包含透明度通道的原始图像。

如果文件不存在或者格式不正确,函数会返回 None。

img = cv2.imread(‘input_image.jpg’, cv2.IMREAD_COLOR)

检查图像是否成功加载

if img is None:
print(“错误:无法加载图像文件。请检查路径和文件名。”)
else:
print(“图像加载成功!”)

# 2. 显示图像
# cv2.imshow() 函数用于在窗口中显示图像。
# 第一个参数是窗口名称(字符串),这个名称将显示在窗口的标题栏。
# 第二个参数是图像对象(NumPy 数组)。
cv2.imshow('Original Image', img)

# cv2.waitKey() 函数是键盘绑定函数。它需要一个以毫秒为单位的参数。
# 函数会等待指定的毫秒数,期间程序会暂停。
# 如果在这个时间内按下了任意键,函数会返回按键的 ASCII 值。
# 如果时间到期没有按下按键,函数返回 -1。
# 如果参数为 0,表示无限期等待,直到按下任意键。
# 这个函数是显示图像的关键,没有它,窗口会一闪而过。
print("按任意键关闭图像窗口...")
cv2.waitKey(0) # 等待直到按下任意键

# cv2.destroyAllWindows() 函数用于销毁所有我们创建的窗口。
# 如果你有很多窗口,这个函数非常有用。
# 你也可以使用 cv2.destroyWindow(winname) 来销毁指定的窗口。
cv2.destroyAllWindows()
print("图像窗口已关闭。")

# 3. 保存图像
# cv2.imwrite() 函数用于将图像保存到文件。
# 第一个参数是保存的文件路径(包括文件名和扩展名)。
# 第二个参数是要保存的图像对象。
# 扩展名(如 '.png', '.jpg', '.bmp')决定了保存的图像格式。
# 对于 JPEG 格式,可以指定压缩质量(0-100,越高越好)。
# 对于 PNG 格式,可以指定压缩级别(0-9,越高越好)。
# 函数返回一个布尔值,表示保存是否成功。
save_success = cv2.imwrite('output_image_copy.png', img)
if save_success:
    print("图像已成功保存为 output_image_copy.png")
else:
    print("保存图像失败!")

# 尝试保存为灰度图像(先转换为灰度再保存)
gray_img = cv2.imread('input_image.jpg', cv2.IMREAD_GRAYSCALE)
if gray_img is not None:
    save_gray_success = cv2.imwrite('output_image_gray.jpg', gray_img)
    if save_gray_success:
        print("灰度图像已成功保存为 output_image_gray.jpg")
    else:
        print("保存灰度图像失败!")
else:
     print("无法加载图像为灰度格式。")

“`

图像的基本属性:形状、数据类型、像素值

在 OpenCV 中,图像被表示为 NumPy 数组。因此,你可以使用 NumPy 的属性来获取图像的信息。

“`python
import cv2

img = cv2.imread(‘input_image.jpg’, cv2.IMREAD_COLOR)

if img is not None:
# 图像的形状 (高度, 宽度, 通道数)
# 对于彩色图像 (BGR),形状是 (rows, cols, 3)
# 对于灰度图像,形状是 (rows, cols)
print(f”图像形状 (高度, 宽度, 通道数): {img.shape}”)

# 像素总数 (高度 * 宽度 * 通道数)
print(f"像素总数: {img.size}")

# 图像的数据类型 (通常是 uint8,表示无符号 8 位整数,即像素值在 0-255 之间)
print(f"数据类型: {img.dtype}")

# 访问单个像素值 (BGR 顺序)
# 访问 (行, 列) 处的像素。OpenCV 坐标是 (x, y),对应 NumPy 索引 (y, x)
# 例如,访问第 100 行,第 50 列的像素 (y=100, x=50)
pixel = img[100, 50]
print(f"像素 (100, 50) 的 BGR 值: {pixel}")

# 访问单个通道的像素值
# 例如,访问同一像素的蓝色通道值
blue_value = img[100, 50, 0] # B通道在索引 0
green_value = img[100, 50, 1] # G通道在索引 1
red_value = img[100, 50, 2] # R通道在索引 2
print(f"像素 (100, 50) 的 B 值: {blue_value}, G 值: {green_value}, R 值: {red_value}")

# 修改像素值
# 将像素 (100, 50) 设置为黑色
img[100, 50] = [0, 0, 0]
print(f"修改后像素 (100, 50) 的 BGR 值: {img[100, 50]}")

# 注意:直接访问/修改像素通常用于小范围操作。
# 对于大范围操作(如区域设为某种颜色),NumPy 的切片操作效率更高。
# 例如,将图像左上角 100x100 的区域设置为蓝色
img[0:100, 0:100] = [255, 0, 0] # B=255, G=0, R=0

cv2.imshow('Modified Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“无法加载图像。”)
“`

重要概念:OpenCV 的坐标系统

在 OpenCV 中,图像的坐标原点默认在左上角。X 轴向右延伸,Y 轴向下延伸。当你访问像素时,通常使用 (y, x)(row, col) 的形式,这与 NumPy 数组的索引方式 array[row, col] 是一致的。然而,当使用某些函数(如绘制函数、点坐标)时,参数可能是 (x, y) 的顺序。请务必留意函数的文档说明。

4. 图像基本操作与处理

本节介绍如何在像素层面和区域层面进行图像操作,如裁剪、缩放、旋转等。

“`python
import cv2
import numpy as np

img = cv2.imread(‘input_image.jpg’, cv2.IMREAD_COLOR)

if img is None:
print(“错误:无法加载图像文件。”)
else:
# 4.1 图像裁剪 (Region of Interest – ROI)
# 利用 NumPy 切片非常方便。
# roi = image[start_row:end_row, start_col:end_col]
# 假设我们想裁剪图像中从第 50 行到第 200 行,从第 100 列到第 300 列的区域
roi = img[50:201, 100:301]

cv2.imshow('Original', img)
cv2.imshow('ROI', roi)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 4.2 图像缩放 (Resizing)
# cv2.resize(src, dsize, fx=0, fy=0, interpolation)
# src: 输入图像
# dsize: 目标图像尺寸 (width, height) 元组。如果设置 None,则使用 fx, fy。
# fx, fy: 沿水平和垂直方向的缩放因子。如果设置 dsize,则 fx, fy 会自动计算。
# interpolation: 插值方法。常用的有:
#    cv2.INTER_AREA: 用于缩小图像,像素面积关系。
#    cv2.INTER_CUBIC: 4x4 像素邻域的立方插值,用于放大图像,效果较好但速度慢。
#    cv2.INTER_LINEAR: 双线性插值,默认方法,用于放大或缩小,速度较快。
#    cv2.INTER_NEAREST: 最近邻插值,速度最快但效果最差。

# 按固定尺寸缩放 (例如,缩放到 300x200 像素)
resized_fixed = cv2.resize(img, (300, 200), interpolation=cv2.INTER_LINEAR)
cv2.imshow('Resized (Fixed 300x200)', resized_fixed)
cv2.waitKey(0)

# 按比例缩放 (例如,缩小到原来的一半)
# 获取原始图像的高度和宽度
height, width = img.shape[:2]
scale_factor = 0.5
new_width = int(width * scale_factor)
new_height = int(height * scale_factor)
resized_scaled = cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_AREA) # 缩小用 INTER_AREA
cv2.imshow('Resized (Scaled 0.5)', resized_scaled)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 4.3 图像旋转 (Rotating)
# OpenCV 没有直接的旋转函数,需要先计算旋转矩阵,然后进行仿射变换。
# cv2.getRotationMatrix2D(center, angle, scale)
# center: 旋转中心 (x, y) 元组。
# angle: 旋转角度,以度为单位,正值表示逆时针旋转,负值表示顺时针。
# scale: 缩放因子,通常为 1.0 表示不缩放。
# cv2.warpAffine(src, M, dsize)
# src: 输入图像
# M: 2x3 的变换矩阵
# dsize: 输出图像尺寸 (width, height)。

# 获取图像中心点
(h, w) = img.shape[:2]
center = (w // 2, h // 2) # 注意:getRotationMatrix2D 的中心是 (x, y) 顺序

# 获取旋转 45 度的仿射变换矩阵,不缩放
M = cv2.getRotationMatrix2D(center, 45, 1.0)

# 应用旋转
# 注意:旋转后图像可能会超出原始尺寸,需要指定输出尺寸。这里使用原始尺寸,超出部分会被裁剪。
rotated = cv2.warpAffine(img, M, (w, h))
cv2.imshow('Rotated 45 Degrees (Cropped)', rotated)
cv2.waitKey(0)

# 如果想包含整个旋转后的图像,需要计算新的尺寸
# 更复杂的旋转需要计算新边界,这里只展示基础旋转。

# 4.4 图像翻转 (Flipping)
# cv2.flip(src, flipCode)
# src: 输入图像
# flipCode: 翻转方向。
#    0: 沿 X 轴翻转 (垂直翻转)
#    > 0 (例如 1): 沿 Y 轴翻转 (水平翻转)
#    < 0 (例如 -1): 沿 X 轴和 Y 轴同时翻转 (水平垂直都翻转)

flipped_vertical = cv2.flip(img, 0)
flipped_horizontal = cv2.flip(img, 1)
flipped_both = cv2.flip(img, -1)

cv2.imshow('Flipped Vertically', flipped_vertical)
cv2.imshow('Flipped Horizontally', flipped_horizontal)
cv2.imshow('Flipped Both', flipped_both)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 4.5 图像通道分离与合并
# 对于彩色图像,每个像素由 BGR 三个通道组成。
# cv2.split() 将多通道图像分割成独立的单通道图像数组。
# cv2.merge() 将多个单通道图像合并成一个多通道图像。

b, g, r = cv2.split(img) # 返回一个包含 B, G, R 通道数组的列表

# 显示某个通道 (灰度图像)
cv2.imshow('Blue Channel', b)
cv2.imshow('Green Channel', g)
cv2.imshow('Red Channel', r)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 合并通道 (例如,创建一个只有红色通道的彩色图像)
# 需要创建与图像尺寸相同的零矩阵作为 B 和 G 通道
zeros = np.zeros(img.shape[:2], dtype="uint8")
red_only = cv2.merge([zeros, zeros, r]) # B, G, R 顺序

cv2.imshow('Red Only Image', red_only)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 4.6 图像加法 (像素操作)
# OpenCV 的加法是饱和操作 (Saturated Operation),即结果不会超过 255。
# NumPy 的加法是模操作 (Modulo Operation),即结果会取模 256。
# 对于图像像素 (uint8),OpenCV 的加法通常是更期望的结果。

# 创建一个与原图大小相同的常量图像(例如,所有像素值都为 50)
M = np.ones(img.shape, dtype="uint8") * 50

added_cv = cv2.add(img, M) # OpenCV 加法
added_np = img + M       # NumPy 加法

cv2.imshow('Original', img)
cv2.imshow('Added (OpenCV)', added_cv)
cv2.imshow('Added (NumPy)', added_np) # 可能会出现溢出变黑的情况
cv2.waitKey(0)
cv2.destroyAllWindows()

# 4.7 图像混合 (权重加法)
# cv2.addWeighted(src1, alpha, src2, beta, gamma)
# 计算 dst = src1 * alpha + src2 * beta + gamma
# src1, src2: 输入图像,必须具有相同的尺寸和通道数。
# alpha, beta: 分别是 src1 和 src2 的权重。
# gamma: 添加到结果中的标量。
# alpha + beta 通常等于 1,用于实现图像透明度混合效果。

# 加载第二张图像 (确保尺寸与第一张相同,或者进行缩放)
# 为了演示,我们使用原图的一份复制,或者加载另一张同样尺寸的图片
# 假设我们加载另一张名为 'second_image.jpg' 的图片,并且它与 input_image.jpg 尺寸相同
# second_img = cv2.imread('second_image.jpg', cv2.IMREAD_COLOR)
# 如果第二张图片尺寸不同,需要先缩放:
# second_img_resized = cv2.resize(second_img, (img.shape[1], img.shape[0]))
# 这里为了简单,我们自己创建一个相同尺寸的图像,例如填充纯色
second_img = np.full_like(img, (0, 255, 255), dtype=np.uint8) # 创建一个与img同形同类型的黄色图像

if second_img is not None and img.shape == second_img.shape:
    # 混合两张图像,第一张权重 0.7,第二张权重 0.3,gamma 为 0
    blended = cv2.addWeighted(img, 0.7, second_img, 0.3, 0)
    cv2.imshow('Original 1', img)
    cv2.imshow('Original 2 (Yellow)', second_img)
    cv2.imshow('Blended Image (70% Original + 30% Yellow)', blended)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
else:
    print("用于混合的第二张图像加载失败或尺寸不匹配。")

“`

5. 在图像上绘制图形与文本

OpenCV 提供了一系列函数,可以在图像上绘制点、线、矩形、圆形、文本等基本图形。这些功能常用于标注、可视化或者创建简单的图形界面元素。

注意: 绘制函数通常会直接修改输入的图像数组。如果你不想修改原始图像,请先创建一份副本:image_copy = image.copy()

“`python
import cv2
import numpy as np

创建一个空白的黑色图像作为画布 (高 500, 宽 800)

canvas = np.zeros((500, 800, 3), dtype=”uint8″) # 3通道黑色图像

5.1 绘制直线

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

img: 绘制的目标图像

pt1, pt2: 直线起点和终点坐标 (x, y) 元组

color: 线条颜色 (BGR 元组)

thickness: 线条粗细(像素)

lineType: 线条类型 (可选),如 cv2.LINE_4, cv2.LINE_8 (默认), cv2.LINE_AA (抗锯齿)

cv2.line(canvas, (100, 50), (700, 50), (0, 0, 255), 3) # 红色粗线

5.2 绘制矩形

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

img: 绘制目标图像

pt1: 矩形左上角坐标 (x, y)

pt2: 矩形右下角坐标 (x, y)

color, thickness, lineType: 同上。thickness=-1 表示填充整个矩形。

cv2.rectangle(canvas, (100, 100), (400, 300), (0, 255, 0), 2) # 绿色边框矩形
cv2.rectangle(canvas, (450, 100), (750, 300), (255, 0, 0), -1) # 蓝色填充矩形

5.3 绘制圆形

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

img: 绘制目标图像

center: 圆心坐标 (x, y)

radius: 半径

color, thickness, lineType: 同上。thickness=-1 表示填充圆形。

cv2.circle(canvas, (400, 400), 80, (0, 255, 255), 5) # 黄色边框圆
cv2.circle(canvas, (150, 400), 50, (255, 255, 0), -1) # 青色填充圆

5.4 绘制多边形

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

img: 绘制目标图像

pts: 多边形顶点的数组,形状为 (n, 1, 2),其中 n 是顶点数,2 是 (x, y) 坐标

isClosed: 如果为 True,绘制封闭的多边形(连接最后一个点到第一个点);如果为 False,绘制一系列不封闭的线段。

color, thickness, lineType: 同上。

定义一个三角形的顶点

pts_triangle = np.array([[600, 350], [700, 450], [500, 450]], np.int32)

调整数组形状以符合函数要求 [[x1, y1]], [[x2, y2]], …

pts_triangle = pts_triangle.reshape((-1, 1, 2))
cv2.polylines(canvas, [pts_triangle], True, (255, 255, 255), 3) # 白色三角形

5.5 添加文本

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

img: 绘制目标图像

text: 要绘制的文本字符串

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

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

fontScale: 字体缩放因子

color, thickness, lineType: 同上。

bottomLeftOrigin: 如果为 True,图像数据原点在左下角。默认 False (原点在左上角)。

font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(canvas, ‘Hello OpenCV!’, (50, 50), font, 1, (255, 255, 255), 2, cv2.LINE_AA) # 白色文本
cv2.putText(canvas, ‘Drawing Demo’, (50, 480), font, 0.8, (100, 200, 250), 1, cv2.LINE_AA) # 浅蓝色文本

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

6. 颜色空间转换与基础图像处理技术

颜色空间(Color Space)是描述颜色的数学模型。最常见的是 BGR(或 RGB)颜色空间,它基于光的三原色。但对于某些任务,如颜色分割或图像分析,转换为其他颜色空间(如 HSV)可能更方便。

基础图像处理技术如阈值化和平滑是许多更高级计算机视觉任务的基础预处理步骤。

“`python
import cv2
import numpy as np

img = cv2.imread(‘input_image.jpg’, cv2.IMREAD_COLOR)

if img is None:
print(“错误:无法加载图像文件。”)
else:
cv2.imshow(‘Original Image’, img)
cv2.waitKey(0)

# 6.1 颜色空间转换
# cv2.cvtColor(src, code, dstCn)
# src: 输入图像
# code: 转换代码,例如 cv2.COLOR_BGR2GRAY, cv2.COLOR_BGR2HSV 等。
# dstCn: 目标通道数。如果为 0,则自动确定。

# 将彩色图像转换为灰度图像
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Grayscale Image', gray_img)
cv2.waitKey(0)

# 将彩色图像转换为 HSV 图像
# HSV 颜色空间将颜色分解为 色调 (Hue), 饱和度 (Saturation), 亮度/明度 (Value)
# H: 0-179 (OpenCV 中 H 的范围是 0-179,而不是标准的 0-360)
# S: 0-255
# V: 0-255
# HSV 空间对于基于颜色的对象检测和跟踪非常有用,因为它将颜色信息 (H) 与亮度信息 (V) 分开。
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow('HSV Image', hsv_img) # HSV 图像直接显示通常是黑白的,因为它显示的是 V 通道或结合 H/S
cv2.waitKey(0)
cv2.destroyAllWindows()

# 6.2 图像阈值化 (Thresholding)
# 阈值化是一种简单的图像分割方法,将图像转换为二值图像(黑白图像)。
# 像素值大于阈值的设为一个值,小于或等于阈值的设为另一个值。
# cv2.threshold(src, thresh, maxval, type)
# src: 输入图像,必须是单通道(灰度)图像。
# thresh: 阈值。
# maxval: 当像素值超过(或低于,取决于类型)阈值时赋给该像素的值。
# type: 阈值类型。常用的有:
#    cv2.THRESH_BINARY: pixel > thresh ? maxval : 0
#    cv2.THRESH_BINARY_INV: pixel > thresh ? 0 : maxval (阈值反转)
#    cv2.THRESH_TRUNC: pixel > thresh ? thresh : pixel (截断)
#    cv2.THRESH_TOZERO: pixel > thresh ? pixel : 0 (高于阈值的保持不变,低于的设为 0)
#    cv2.THRESH_TOZERO_INV: pixel > thresh ? 0 : pixel (低于阈值的保持不变,高于的设为 0)

# 转换为灰度图像进行阈值化
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 简单阈值化 (例如,阈值设为 127,最大值设为 255)
# 返回值有两个:retVal (使用的阈值) 和 thresholded_image
ret, thresh_binary = cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY)
ret, thresh_binary_inv = cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh_trunc = cv2.threshold(gray_img, 127, 255, cv2.THRESH_TRUNC)
ret, thresh_tozero = cv2.threshold(gray_img, 127, 255, cv2.THRESH_TOZERO)
ret, thresh_tozero_inv = cv2.threshold(gray_img, 127, 255, cv2.THRESH_TOZERO_INV)

cv2.imshow('Original Grayscale', gray_img)
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(0)
cv2.destroyAllWindows()

# 自适应阈值化 (Adaptive Thresholding)
# 简单阈值化使用一个固定的阈值,对于光照不均的图像效果不好。
# 自适应阈值化根据像素的局部区域计算阈值。
# cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
# src: 输入图像,必须是 8 位单通道图像。
# maxValue: 赋给满足条件的像素的值。
# adaptiveMethod: 自适应阈值计算方法。
#    cv2.ADAPTIVE_THRESH_MEAN_C: 邻域像素的平均值减去 C。
#    cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 邻域像素的高斯加权平均值减去 C。
# thresholdType: 阈值类型,只能是 cv2.THRESH_BINARY 或 cv2.THRESH_BINARY_INV。
# blockSize: 用于计算阈值的像素邻域的大小(必须是奇数)。
# C: 常量,从平均值或加权平均值中减去的数值。

# 使用 ADAPTIVE_THRESH_MEAN_C, blockSize 11, C 2
adapt_mean = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                                   cv2.THRESH_BINARY, 11, 2)

# 使用 ADAPTIVE_THRESH_GAUSSIAN_C, blockSize 11, C 2
adapt_gaussian = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                       cv2.THRESH_BINARY, 11, 2)

cv2.imshow('Original Grayscale', gray_img)
cv2.imshow('Adaptive Mean Thresholding', adapt_mean)
cv2.imshow('Adaptive Gaussian Thresholding', adapt_gaussian)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 6.3 图像平滑 (模糊)
# 平滑操作(也称滤波)用于减少图像噪声,通常通过在像素邻域内应用卷积核来实现。
# cv2.filter2D() 可以应用自定义的 2D 卷积核,但 OpenCV 提供了一些常用的内置平滑函数。

# 平均模糊 (Averaging Blur)
# cv2.blur(src, ksize)
# src: 输入图像
# ksize: 卷积核大小 (width, height) 元组。例如 (5, 5)。
blur_avg = cv2.blur(img, (5, 5))
cv2.imshow('Original', img)
cv2.imshow('Averaging Blur (5x5)', blur_avg)
cv2.waitKey(0)

# 高斯模糊 (Gaussian Blur)
# cv2.GaussianBlur(src, ksize, sigmaX, sigmaY=0, borderType=cv2.BORDER_DEFAULT)
# src: 输入图像
# ksize: 高斯核大小 (width, height) 元组,必须是奇数。
# sigmaX: X 方向的高斯核标准差。
# sigmaY: Y 方向的标准差。如果为 0,则 sigmaY = sigmaX。如果都为 0,则根据核大小计算。
# 高斯模糊是图像去噪的常用方法,尤其是对于高斯噪声。
blur_gaussian = cv2.GaussianBlur(img, (5, 5), 0)
cv2.imshow('Gaussian Blur (5x5)', blur_gaussian)
cv2.waitKey(0)

# 中值模糊 (Median Blur)
# cv2.medianBlur(src, ksize)
# src: 输入图像,必须是 1、3 或 4 通道。
# ksize: 中值滤波核的孔径大小,必须是大于 1 的奇数。
# 中值模糊对于去除椒盐噪声 (Salt-and-Pepper Noise) 特别有效,因为它用邻域像素的中值代替中心像素值。
blur_median = cv2.medianBlur(img, 5)
cv2.imshow('Median Blur (5)', blur_median)
cv2.waitKey(0)
cv2.destroyAllWindows()

“`

7. 处理视频:读取、显示、保存与摄像头捕获

OpenCV 不仅能处理静态图像,还能方便地处理视频流,无论是读取视频文件还是捕获摄像头输入。视频本质上是一系列连续的图像帧。

“`python
import cv2
import numpy as np

7.1 读取视频文件或摄像头

cv2.VideoCapture()

参数可以是视频文件的路径(字符串),或者是设备的索引号(整数)。

索引号通常 0 表示默认摄像头,1 表示第二个摄像头,以此类推。

对于视频文件,确保文件路径正确。对于摄像头,确保摄像头可用且驱动正确安装。

cap = cv2.VideoCapture(‘input_video.mp4’) # 尝试读取视频文件

cap = cv2.VideoCapture(0) # 尝试读取默认摄像头

检查视频是否成功打开

if not cap.isOpened():
print(“错误: 无法打开视频文件或摄像头。”)
exit() # 如果无法打开,退出程序

7.2 获取视频属性 (可选)

cap.get(propId) 可以获取视频的各种属性。propId 是一个整数标识符。

常用的属性 ID:

cv2.CAP_PROP_FRAME_WIDTH: 帧的宽度

cv2.CAP_PROP_FRAME_HEIGHT: 帧的高度

cv2.CAP_PROP_FPS: 视频帧率 (Frames Per Second)

cv2.CAP_PROP_FRAME_COUNT: 视频总帧数 (对于某些格式可能不准确)

cv2.CAP_PROP_FOURCC: 视频编码器 FourCC 代码

cv2.CAP_PROP_POS_FRAMES: 当前帧的索引 (从 0 开始)

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)
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT) # 可能返回 -1 或不准确

print(f”视频宽度: {frame_width}”)
print(f”视频高度: {frame_height}”)
print(f”视频帧率 (FPS): {fps}”)
print(f”视频总帧数: {frame_count}”)

7.3 读取并显示视频帧

视频是一个帧序列,我们需要在一个循环中逐帧读取和处理。

cap.read() 方法会读取下一帧。它返回一个元组 (ret, frame)。

ret: 布尔值,表示是否成功读取帧 (True 表示成功,False 表示视频结束或读取失败)。

frame: 读取到的帧,是一个 NumPy 数组(图像数据)。如果读取失败,frame 是 None。

while True:
ret, frame = cap.read()

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

    # 显示原始帧或处理后的帧
    cv2.imshow('Video Frame', frame)
    # cv2.imshow('Grayscale Frame', gray_frame)

    # cv2.waitKey(delay) 控制帧之间显示的时间。
    # delay 参数以毫秒为单位。
    # 如果 delay 为 1,表示等待 1ms。这通常用于实时视频流(如摄像头),以尽快显示下一帧。
    # 如果 delay 设置为 int(1000/fps),可以尝试按照视频的实际帧率播放。
    # 按下 'q' 键退出循环
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
else:
    # 如果 ret 为 False,表示视频结束或读取失败
    print("视频播放结束或读取失败。")
    break

7.4 释放视频对象和销毁窗口

在循环结束后,释放 VideoCapture 对象和所有 OpenCV 窗口。

cap.release()
cv2.destroyAllWindows()

print(“视频播放完成。”)

7.5 保存视频 (从摄像头捕获并保存,或处理后保存)

cv2.VideoWriter(filename, fourcc, fps, frameSize)

filename: 输出视频文件的路径。

fourcc: FourCC 代码,一个 4 字节的代码,指定视频编码器。

不同的操作系统和安装的编解码器支持的 FourCC 不同。

常见的 FourCC:

– ‘XVID’ (或 cv2.VideoWriter_fourcc(*’XVID’)) 通常是一个不错的选择。

– ‘MJPG’ (或 cv2.VideoWriter_fourcc(*’MJPG’))

– ‘DIVX’ (或 cv2.VideoWriter_fourcc(*’DIVX’))

– ‘X264′ (或 cv2.VideoWriter_fourcc(*’X264’)) 可能需要安装额外的库。

FourCC 代码通常需要写成 cv2.VideoWriter_fourcc(*’CODE’) 的形式。

fps: 输出视频的帧率。

frameSize: 输出视频的帧尺寸 (width, height) 元组。必须与要写入的帧尺寸一致。

假设我们要从摄像头捕获并保存视频

cap_save = cv2.VideoCapture(0) # 打开默认摄像头

if not cap_save.isOpened():
print(“错误: 无法打开摄像头进行保存。”)
else:
# 获取摄像头帧的尺寸和帧率
save_width = int(cap_save.get(cv2.CAP_PROP_FRAME_WIDTH))
save_height = int(cap_save.get(cv2.CAP_PROP_FRAME_HEIGHT))
save_fps = cap_save.get(cv2.CAP_PROP_FPS) # 摄像头帧率可能不是整数或固定值,可以自己指定一个常用值如 20.0 或 30.0

# 定义 FourCC 和创建 VideoWriter 对象
# 对于 .avi 文件,XVID 或 MJPG 常用
# 对于 .mp4 文件,X264 常用,但可能需要更多配置或库
fourcc = cv2.VideoWriter_fourcc(*'XVID') # 使用 XVID 编码器保存为 .avi 文件
out = cv2.VideoWriter('output_camera.avi', fourcc, 20.0, (save_width, save_height)) # 指定帧率 20.0

print("正在从摄像头捕获并保存视频,按 'q' 停止...")

while(cap_save.isOpened()):
    ret, frame = cap_save.read()
    if ret:
        # 在这里可以对帧进行处理,例如转换为灰度再保存
        # gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 注意:如果要保存灰度视频,VideoWriter 需要额外的标志或 FourCC 可能不同,并且 frameSize 需要是单通道的(这通常不是 VideoWriter 支持的标准方式)。
        # 通常我们将灰度帧转换为 3 通道再保存为彩色视频文件。
        # gray_frame_bgr = cv2.cvtColor(gray_frame, cv2.COLOR_GRAY2BGR)
        # out.write(gray_frame_bgr)

        # 直接写入原始彩色帧
        out.write(frame)

        cv2.imshow('Recording...', frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# 释放资源
cap_save.release()
out.release() # 释放 VideoWriter 对象
cv2.destroyAllWindows()
print("视频录制完成。")

“`

8. 一个简单的应用示例:颜色检测与跟踪

这个示例将结合之前学到的知识,演示如何在一个视频流(例如摄像头)中检测并标记出特定颜色的物体。

“`python
import cv2
import numpy as np

打开摄像头

cap = cv2.VideoCapture(0)

if not cap.isOpened():
print(“错误: 无法打开摄像头。”)
else:
# 定义要检测的颜色的 HSV 范围
# 例如,检测蓝色
# 这些值是根据常见的蓝色范围估计的,可能需要根据实际光照和物体颜色进行调整
# H (色调) 的范围是 [0, 179]
# S (饱和度) 的范围是 [0, 255]
# V (明度) 的范围是 [0, 255]
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([140, 255, 255])

print("正在进行蓝色检测,按 'q' 停止...")

while True:
    ret, frame = cap.read()

    if ret:
        # 1. 将帧从 BGR 转换为 HSV 颜色空间
        hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

        # 2. 根据定义的 HSV 范围创建掩码 (Mask)
        # cv2.inRange() 函数会根据指定的颜色范围创建一个二值图像 (掩码)。
        # 掩码中,颜色在范围内的像素值为 255 (白色),不在范围内的像素值为 0 (黑色)。
        mask = cv2.inRange(hsv_frame, lower_blue, upper_blue)

        # 3. 对原始图像和掩码进行按位与操作,提取出目标颜色区域
        # cv2.bitwise_and(src1, src2, mask=None)
        # 结果图像的像素值 = src1 的像素值 & src2 的像素值
        # 如果提供了 mask,则只处理 mask 中像素值为非零的位置。
        # 通过将原始帧与掩码进行按位与操作,我们可以保留原始帧中与掩码白色区域对应的像素。
        # 掩码的白色区域 (255) 与原始帧的像素值进行按位与运算,结果是原始像素值。
        # 掩码的黑色区域 (0) 与原始帧的像素值进行按位与运算,结果是 0 (黑色)。
        result = cv2.bitwise_and(frame, frame, mask=mask)

        # (可选) 查找并绘制检测到的物体的轮廓 (Contours)
        # 轮廓是具有相同颜色或强度的连续点曲线。
        # cv2.findContours(image, mode, method)
        # image: 输入的二值图像 (掩码)。
        # mode: 轮廓检索模式 (例如 cv2.RETR_EXTERNAL 只检测最外层轮廓)。
        # method: 轮廓逼近方法 (例如 cv2.CHAIN_APPROX_SIMPLE 压缩水平、垂直和对角线段)。
        # 返回值是 (contours, hierarchy),contours 是一个包含所有轮廓的列表。
        # Note: findContours 会修改输入的图像,如果你想保留原始掩码,请先复制:mask.copy()
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # cv2.drawContours(image, contours, contourIdx, color, thickness)
        # image: 绘制轮廓的目标图像 (通常是原始图像)。
        # contours: 包含轮廓的列表 (findContours 的返回值)。
        # contourIdx: 要绘制的轮廓的索引(-1 表示绘制所有轮廓)。
        # color, thickness: 绘制轮廓的颜色和粗细。

        # 在原始帧上绘制找到的轮廓 (例如,绘制绿色轮廓,粗细 2)
        cv2.drawContours(frame, contours, -1, (0, 255, 0), 2)

        # 显示原始帧 (带有轮廓),掩码,和提取结果
        cv2.imshow('Original Frame (with Contours)', frame)
        cv2.imshow('Mask (Blue)', mask)
        cv2.imshow('Detected Blue Objects', result)

        # 按下 'q' 键退出循环
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()
print("蓝色检测演示结束。")

“`

这个简单的例子展示了如何结合颜色空间转换、阈值化、按位运算和轮廓检测来实现一个基础的颜色检测功能。你可以尝试调整 lower_blueupper_blue 的值来检测不同的颜色。查找 HSV 颜色对照表可以帮助你找到特定颜色的 HSV 范围。

9. 进一步学习:OpenCV 的更多功能

本教程只涵盖了 OpenCV Python 的基础入门知识。OpenCV 库包含了极其丰富的功能,你可以进一步探索以下主题:

  • 图像滤波和边缘检测: Sobel, Scharr, Laplacian, Canny 边缘检测器。
  • 形态学操作: 膨胀、腐蚀、开运算、闭运算等,用于图像去噪、分割等。
  • 特征检测与描述: Harris 角点检测、SIFT, SURF, ORB 特征点检测与描述。
  • 特征匹配与对象查找: 暴力匹配、FLANN 匹配、单应性变换。
  • 对象检测: Haar Cascades(用于人脸、眼睛等)、HOG + SVM、DNN 模块(集成深度学习模型如 YOLO, SSD)。
  • 物体跟踪: Mean Shift, Camshift, 光流法、跟踪器 API (GOTURN, MOSSE 等)。
  • 相机标定: 估算相机内参和外参,用于去除畸变和三维重建。
  • 图像拼接: 将多张图像合成一张全景图。
  • 机器学习模块: 集成了一些传统的机器学习算法(KNN, SVM, 决策树等)。

OpenCV 官方文档 (docs.opencv.org) 是最全面和权威的资源。此外,网上也有大量的教程、博客和项目示例供你学习和参考。

10. 总结与展望

通过本教程的学习,你应该已经掌握了 OpenCV Python 的基本用法,包括图像的加载、显示、保存,基本的像素和区域操作,图像绘制,颜色空间转换,以及基础的图像处理技术如阈值化和平滑。你还学习了如何处理视频流和捕获摄像头输入,并通过一个简单的颜色检测示例了解了如何将这些基础知识应用于实际问题。

计算机视觉是一个充满活力和挑战的领域。OpenCV 作为你的工具箱,为你提供了实现各种计算机视觉应用的强大能力。从这里开始,你可以深入研究某个特定领域(如人脸识别、目标跟踪、姿态估计等),或者将 OpenCV 与深度学习框架结合,探索更先进的计算机视觉技术。

持续学习和实践是掌握计算机视觉的关键。尝试修改本教程中的代码,应用到不同的图像和视频上,或者挑战自己实现一些更复杂的图像处理或分析任务。

祝你在计算机视觉的学习和实践旅程中一切顺利!


希望这篇详细的教程能帮助你入门 OpenCV Python!

发表评论

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

滚动至顶部