OpenCV Python:从零开始学习 – wiki基地


OpenCV Python:从零开始学习计算机视觉的强大之旅

计算机视觉,一个充满魔力和无限可能性的领域,它赋予计算机“看”的能力,理解并处理图像和视频数据。从自动驾驶汽车的感知系统,到智能手机的面部识别解锁,再到工业生产线上的缺陷检测,计算机视觉技术无处不在,深刻地改变着我们的生活和工作方式。

而在这片激动人心的领域中,有一个名字你必然会听到——OpenCV。

OpenCV (Open Source Computer Vision Library) 是一个开源的计算机视觉和机器学习软件库。它由英特尔公司发起并持续发展,现在由一家非盈利组织 OpenCV.org 维护。OpenCV 拥有 C++, Python, Java 等多种语言接口,几乎支持所有主流操作系统。凭借其强大的功能、高效的性能以及庞大的用户社区,OpenCV 已成为计算机视觉领域最受欢迎的工具之一。

为什么选择 Python + OpenCV?

OpenCV 最初用 C++ 编写,以追求极致的性能。然而,对于许多开发者,特别是初学者或者需要快速原型开发的场景,Python 是一个更具吸引力的选择。

  1. 易学易用: Python 语法简洁明了,上手快速,代码可读性强。这使得初学者能够更快地掌握 OpenCV 的基本用法。
  2. 丰富的生态: Python 拥有庞大的科学计算和数据处理库,如 NumPy、SciPy、Matplotlib 等,与 OpenCV 集成使用时,能够极大地扩展其功能,方便数据的可视化、分析和处理。
  3. 快速开发: Python 的动态特性和丰富的第三方库使得开发过程更加高效,可以快速构建、测试和迭代计算机视觉应用。
  4. 社区支持: Python 社区极为活跃,遇到问题时很容易找到解决方案和资源。

因此,将 OpenCV 的强大功能与 Python 的易用性相结合,成为了学习和实践计算机视觉的绝佳起点。本文将带你从零开始,一步步踏上 OpenCV Python 的学习之旅。

第一步:环境搭建 – 准备你的工具箱

在开始之前,你需要确保你的计算机上已经安装了 Python。推荐使用 Python 3.6 或更高版本。安装 Python 最简单的方法是下载并安装 Anaconda 或 Miniconda,它们不仅包含 Python,还集成了许多常用的科学计算库,包括 NumPy。

安装好 Python 环境后,安装 OpenCV Python 库就非常简单了。打开你的终端或命令提示符,运行以下命令:

bash
pip install opencv-python

如果你还需要使用 OpenCV 的一些非免费许可模块(例如 SIFT, SURF 等,尽管这些在较新版本中已经部分开放),可以安装 opencv-contrib-python,它包含了额外的贡献模块:

bash
pip install opencv-contrib-python

对于初学者,通常 opencv-python 就足够了。

为了方便展示图像,我们还需要安装 Matplotlib,一个强大的 Python 绘图库:

bash
pip install matplotlib

现在,你可以通过简单的测试来验证安装是否成功。打开 Python 解释器或创建一个 Python 文件,输入:

python
import cv2
print(cv2.__version__)

如果输出了 OpenCV 的版本号,恭喜你,环境搭建成功!

第二步:图像的本质 – 像素与数组

在计算机视觉中,图像不再是简单的画面,而是由无数个离散的点——像素(Pixel) 组成的数字矩阵。每个像素都携带着关于该点颜色和亮度的信息。

OpenCV 在 Python 中使用 NumPy 数组 来表示图像。这是一个非常重要的概念,因为它意味着你可以使用 NumPy 强大的数组操作功能来处理图像。

图像的表示

  • 灰度图像 (Grayscale Image): 每个像素只有一个值,表示其亮度(通常从 0 到 255)。0 表示黑色,255 表示白色。一个灰度图像在 NumPy 中是一个二维数组(高度 x 宽度)。
  • 彩色图像 (Color Image): 大多数彩色图像使用 BGR 颜色空间(不是常见的 RGB)。每个像素由三个值组成,分别代表蓝色、绿色和红色的亮度(通常从 0 到 255)。一个彩色图像在 NumPy 中是一个三维数组(高度 x 宽度 x 通道数),其中通道数通常是 3 (BGR)。

例如,一个 100×200 像素的灰度图像是一个形状为 (100, 200) 的 NumPy 数组。
一个 100×200 像素的彩色图像是一个形状为 (100, 200, 3) 的 NumPy 数组。

访问像素值

你可以像访问 NumPy 数组元素一样访问图像的像素值:

“`python

假设 img 是一个 OpenCV 读取的图像 (NumPy 数组)

访问灰度图像在 (y, x) 位置的像素值

注意:NumPy 索引是 [行, 列],对应图像的 [y, x]

gray_pixel_value = img[y, x]

访问彩色图像在 (y, x) 位置的像素的某个通道值

假设是 BGR 图像

blue_value = img[y, x, 0] # B 通道
green_value = img[y, x, 1] # G 通道
red_value = img[y, x, 2] # R 通道

访问彩色图像在 (y, x) 位置的完整像素值 (BGR 数组)

color_pixel_bgr = img[y, x]
“`

你可以通过修改这些值来改变图像的像素。

第三步:图像的基本操作 – 加载、显示与保存

这是学习 OpenCV 最基础的操作。

加载图像

使用 cv2.imread() 函数加载图像。

“`python
import cv2

读取彩色图像 (默认)

img_color = cv2.imread(‘path/to/your/image.jpg’)

读取灰度图像

img_gray = cv2.imread(‘path/to/your/image.jpg’, cv2.IMREAD_GRAYSCALE)

读取包含 Alpha 通道的图像 (png 等)

img_alpha = cv2.imread(‘path/to/your/image.png’, cv2.IMREAD_UNCHANGED)

检查图像是否成功加载

if img_color is None:
print(“Error: Could not load image.”)
else:
print(“Image loaded successfully.”)
print(“Image shape:”, img_color.shape) # 输出 (height, width, channels)
print(“Image data type:”, img_color.dtype) # 输出 uint8 (通常)
print(“Image size (total pixels):”, img_color.size) # 输出 height * width * channels
“`

注意:cv2.imread() 的第一个参数是文件路径。请确保路径正确。如果图像文件不存在,imread() 会返回 None

显示图像

OpenCV 提供了一个简单的窗口来显示图像,使用 cv2.imshow()

“`python
import cv2

img = cv2.imread(‘path/to/your/image.jpg’)

if img is not None:
cv2.imshow(‘My Image Window’, img) # 第一个参数是窗口名称,第二个是图像数组

# cv2.waitKey() 是一个键盘绑定函数。它的参数是等待键盘按下的毫秒数。
# 如果参数是 0,它将无限期地等待击键。
# 如果是其他正整数,它会等待指定的毫秒数,超时则继续执行。
# 它返回按下的键的 ASCII 值。
cv2.waitKey(0) # 等待任意键按下

# cv2.destroyAllWindows() 用于销毁所有我们创建的窗口。
# cv2.destroyWindow(windowname) 用于销毁指定名称的窗口。
cv2.destroyAllWindows()

else:
print(“Error: Could not load image.”)
“`

这是一个基本流程:加载图像 -> 显示图像 -> 等待按键 -> 关闭窗口。在实际应用中,waitKey 常用于暂停程序,等待用户输入,或在视频播放中控制帧率。

使用 Matplotlib 显示图像:

在脚本或 Jupyter Notebook 中,使用 Matplotlib 显示图像可能更方便。但请记住,Matplotlib 默认使用 RGB 顺序,而 OpenCV 使用 BGR。你需要进行颜色通道转换。

“`python
import cv2
import matplotlib.pyplot as plt

img = cv2.imread(‘path/to/your/image.jpg’)

if img is not None:
# OpenCV 读取的是 BGR 格式,Matplotlib 需要 RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

plt.imshow(img_rgb)
plt.title('Image from Matplotlib')
plt.axis('off') # 不显示坐标轴
plt.show()

else:
print(“Error: Could not load image.”)
“`

保存图像

使用 cv2.imwrite() 函数保存图像。

“`python
import cv2

img = cv2.imread(‘path/to/your/image.jpg’)

if img is not None:
# 保存为 PNG 格式
cv2.imwrite(‘saved_image.png’, img)

# 保存为 JPG 格式,可以指定质量 (0-100, 越高质量越好,文件越大)
cv2.imwrite('saved_image_high_quality.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])

print("Image saved successfully.")

else:
print(“Error: Could not load image for saving.”)
“`

第一个参数是保存的文件路径(文件名决定格式),第二个参数是要保存的图像数组。

第四步:图像基本操作进阶 – 裁剪、缩放与通道分离合并

基于图像作为 NumPy 数组的特性,我们可以进行各种数组操作。

图像裁剪 (Cropping)

裁剪就是提取图像的一个矩形区域,这通过 NumPy 的数组切片实现。

“`python
import cv2

img = cv2.imread(‘path/to/your/image.jpg’)

if img is not None:
# 假设我们要裁剪从 (x1, y1) 到 (x2, y2) 的区域
# 在 NumPy 数组中,索引是 [行范围, 列范围]
# 行对应 y 坐标,列对应 x 坐标
y1, y2 = 50, 200 # 垂直范围
x1, x2 = 100, 300 # 水平范围

cropped_img = img[y1:y2, x1:x2]

cv2.imshow('Original Image', img)
cv2.imshow('Cropped Image', cropped_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load image.”)
“`

记住:img[y1:y2, x1:x2] 表示从第 y1 行到第 y2-1 行,以及从第 x1 列到第 x2-1 列的区域。

图像缩放 (Resizing)

使用 cv2.resize() 函数可以改变图像的尺寸。

“`python
import cv2

img = cv2.imread(‘path/to/your/image.jpg’)

if img is not None:
# 1. 按指定尺寸缩放 (width, height)
resized_fixed = cv2.resize(img, (300, 200)) # 注意是 (width, height)

# 2. 按比例因子缩放
# fx 和 fy 分别是水平和垂直方向的缩放因子
scale_factor = 0.5
resized_scaled = cv2.resize(img, None, fx=scale_factor, fy=scale_factor)

# 插值方法 (interpolation)
# 对于放大,推荐使用 cv2.INTER_CUBIC 或 cv2.INTER_LINEAR
# 对于缩小,推荐使用 cv2.INTER_AREA
resized_cubic = cv2.resize(img, None, fx=1.5, fy=1.5, interpolation=cv2.INTER_CUBIC)

cv2.imshow('Original Image', img)
cv2.imshow('Resized Fixed', resized_fixed)
cv2.imshow('Resized Scaled', resized_scaled)
cv2.imshow('Resized Cubic', resized_cubic)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load image.”)
“`

图像通道分离与合并

对于彩色图像 (BGR),你可以分离出其单独的通道,或者将独立的通道合并成一个彩色图像。

“`python
import cv2

img = cv2.imread(‘path/to/your/color_image.jpg’)

if img is not None and len(img.shape) == 3: # 确保是彩色图像
# 分离通道
b, g, r = cv2.split(img)

# 你也可以通过索引直接访问通道 (但这样返回的是一个视图,不是副本)
# b = img[:, :, 0]
# g = img[:, :, 1]
# r = img[:, :, 2]

# 显示单独的通道 (它们是灰度图像)
cv2.imshow('Blue Channel', b)
cv2.imshow('Green Channel', g)
cv2.imshow('Red Channel', r)

# 合并通道 (注意顺序仍然是 BGR)
merged_img = cv2.merge([b, g, r])
cv2.imshow('Merged Image', merged_img)

# 合并时可以改变通道顺序,例如变成 RGB (虽然 imshow 还是按 BGR 渲染)
merged_rgb = cv2.merge([r, g, b]) # 现在内部数据是 RGB 顺序

cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load color image or it’s not color.”)
“`

通道分离常用于处理图像的某个特定颜色分量,而通道合并则可以将处理后的分量重新组合。

第五步:在图像上绘制 – 添加形状与文本

OpenCV 提供了一系列函数,让你可以在图像上绘制点、线、矩形、圆等基本形状,以及添加文本。这些函数通常会直接修改原始图像数组,如果你想保留原始图像,请先创建一个副本 (img.copy())。

“`python
import cv2
import numpy as np

创建一个空白的黑色图像 (500×500 像素,3 通道,uint8 类型)

img = np.zeros((500, 500, 3), dtype=np.uint8)

绘制一条线

参数: 图像, 起点坐标 (x, y), 终点坐标 (x, y), 颜色 (B, G, R), 线条粗细

cv2.line(img, (0, 0), (500, 500), (255, 0, 0), 5) # 蓝色对角线

绘制一个矩形

参数: 图像, 左上角坐标 (x, y), 右下角坐标 (x, y), 颜色 (B, G, R), 线条粗细 (-1 表示填充)

cv2.rectangle(img, (100, 100), (400, 400), (0, 255, 0), 3) # 绿色矩形框

绘制一个圆

参数: 图像, 圆心坐标 (x, y), 半径, 颜色 (B, G, R), 线条粗细 (-1 表示填充)

cv2.circle(img, (250, 250), 100, (0, 0, 255), -1) # 红色填充圆

绘制文本

参数: 图像, 文本字符串, 文本左下角坐标 (x, y), 字体类型, 字体缩放比例, 颜色 (B, G, R), 文本粗细, 线条类型

font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, ‘Hello OpenCV!’, (50, 50), font, 1, (255, 255, 255), 2, cv2.LINE_AA) # 白色文本

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

掌握这些绘图函数,你就可以在图像上标记对象、绘制 ROI (Region of Interest) 等。

第六步:图像基本处理 – 颜色空间转换、阈值化与平滑

图像处理是计算机视觉的核心环节之一。通过各种处理技术,我们可以改善图像质量、提取有用信息、准备图像用于后续分析等。

颜色空间转换 (Color Space Conversion)

除了 BGR 和灰度,OpenCV 支持多种颜色空间,如 HSV (色相、饱和度、亮度)、LAB 等。HSV 颜色空间在基于颜色分割对象时非常有用,因为它将颜色信息 (Hue) 与亮度信息 (Value) 分离。

“`python
import cv2

img = cv2.imread(‘path/to/your/color_image.jpg’)

if img is not None:
# 转换为灰度图像
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 转换为 HSV 图像
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 转换为 LAB 图像
lab_img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

cv2.imshow('Original BGR', img)
cv2.imshow('Grayscale', gray_img)
# 注意:HSV 和 LAB 通常显示为彩色图像,但通道含义不同
cv2.imshow('HSV', hsv_img)
cv2.imshow('LAB', lab_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load image.”)
“`

cv2.cvtColor() 函数使用 cv2.COLOR_ 开头的标志指定转换类型。

阈值化 (Thresholding)

阈值化是将灰度图像转换为二值图像(只有黑白两个颜色)的过程。通过设定一个阈值,像素值高于阈值的变为一个值(如白色),低于阈值的变为另一个值(如黑色)。这在分割前景与背景时非常有用。

“`python
import cv2

img_gray = cv2.imread(‘path/to/your/image.jpg’, cv2.IMREAD_GRAYSCALE)

if img_gray is not None:
# 简单的全局阈值化
# cv2.threshold(src, thresh, maxval, type)
# 返回值: ret (使用的阈值), dst (二值图像)
ret, binary_img = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
# THRESH_BINARY: pix > thresh -> maxval, else 0
# THRESH_BINARY_INV: pix > thresh -> 0, else maxval
# THRESH_TRUNC: pix > thresh -> thresh, else pix
# THRESH_TOZERO: pix > thresh -> pix, else 0
# THRESH_TOZERO_INV: pix > thresh -> 0, else pix

# 适应性阈值化 (Adaptive Thresholding)
# 当图像光照不均匀时,全局阈值效果不好,可以使用适应性阈值
# cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
# adaptiveMethod: ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C
# blockSize: 用于计算阈值的邻域大小 (奇数)
# C: 从平均值或加权平均值中减去的常数
adaptive_mean = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                                      cv2.THRESH_BINARY, 11, 2)
adaptive_gaussian = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                          cv2.THRESH_BINARY, 11, 2)

cv2.imshow('Original Grayscale', img_gray)
cv2.imshow('Binary Threshold', binary_img)
cv2.imshow('Adaptive Mean Threshold', adaptive_mean)
cv2.imshow('Adaptive Gaussian Threshold', adaptive_gaussian)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load grayscale image.”)
“`

适应性阈值化会根据像素的局部邻域计算阈值,因此更能适应光照变化。

图像平滑 (Smoothing) / 模糊 (Blurring)

平滑或模糊是一种常见的图像预处理技术,主要用于减少图像噪声。它通过将像素的颜色值与其周围像素的颜色值进行平均来达到目的。

“`python
import cv2

img = cv2.imread(‘path/to/your/noisy_image.jpg’)

if img is not None:
# 均值滤波 (Averaging)
# 使用一个核,核内所有像素的平均值作为中心像素的新值
# 核大小 (ksize)
blur_avg = cv2.blur(img, (5, 5)) # 5×5 核

# 高斯滤波 (Gaussian Blur)
# 使用高斯函数作为权重,距离中心越近的像素权重越大
# 高斯滤波在去噪的同时能更好地保留边缘
# ksize: 高斯核大小 (奇数)。sigmaX, sigmaY: 高斯函数在 X 和 Y 方向的标准差 (如果为 0,会根据核大小计算)
blur_gaussian = cv2.GaussianBlur(img, (5, 5), 0)

# 中值滤波 (Median Blur)
# 使用核内像素的中值作为中心像素的新值。对椒盐噪声效果特别好
# ksize: 核大小 (奇数)
blur_median = cv2.medianBlur(img, 5)

# 双边滤波 (Bilateral Filter)
# 在空间距离的同时考虑颜色相似度,能保留边缘信息
# 参数: src, d(邻域直径), sigmaColor(颜色空间标准差), sigmaSpace(坐标空间标准差)
blur_bilateral = cv2.bilateralFilter(img, 9, 75, 75)

cv2.imshow('Original', img)
cv2.imshow('Average Blur', blur_avg)
cv2.imshow('Gaussian Blur', blur_gaussian)
cv2.imshow('Median Blur', blur_median)
cv2.imshow('Bilateral Filter', blur_bilateral)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load image.”)
“`

选择哪种滤波方法取决于具体的应用场景和噪声类型。

第七步:边缘检测 – Canny 边缘检测器

边缘是图像中像素值发生剧烈变化的地方,它们通常对应于物体轮廓、纹理变化等。边缘检测是许多计算机视觉任务的关键步骤。

OpenCV 提供了多种边缘检测算法,其中 Canny 边缘检测器 是最常用且效果最好的之一。

“`python
import cv2

img = cv2.imread(‘path/to/your/image.jpg’, cv2.IMREAD_GRAYSCALE) # Canny 通常在灰度图上操作

if img is not None:
# Canny 边缘检测
# cv2.Canny(image, threshold1, threshold2[, apertureSize[, L2gradient]])
# threshold1 和 threshold2 是两个滞后阈值。
# 推荐 threshold1:threshold2 的比例在 1:2 或 1:3 之间。
edges = cv2.Canny(img, 100, 200) # 假设低阈值100,高阈值200

cv2.imshow('Original Grayscale', img)
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load image.”)
“`

Canny 算法是一个多级算法,它包括高斯模糊降噪、计算梯度强度和方向、非极大值抑制以及使用双阈值进行边缘连接等步骤。调整 threshold1threshold2 参数可以控制检测到的边缘数量。

第八步:轮廓处理 – 识别与绘制对象边界

轮廓是连接具有相同颜色或强度值的连续点的曲线。在二值图像中,轮廓可以用来表示物体的边界。轮廓处理是对象检测、识别和分析的重要基础。

“`python
import cv2

img_gray = cv2.imread(‘path/to/your/objects_image.jpg’, cv2.IMREAD_GRAYSCALE)

if img_gray is not None:
# 为了找到轮廓,通常需要先进行阈值化或 Canny 边缘检测,得到二值图像
ret, thresh = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV) # 背景黑色,前景白色

# 查找轮廓
# cv2.findContours(image, mode, method)
# image: 8位单通道图像 (通常是二值图像)
# mode: 轮廓检索模式 (如 cv2.RETR_EXTERNAL, cv2.RETR_LIST, cv2.RETR_TREE)
# method: 轮廓逼近方法 (如 cv2.CHAIN_APPROX_NONE, cv2.CHAIN_APPROX_SIMPLE)
# 返回值: contours (找到的轮廓列表), hierarchy (轮廓的层级关系)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 创建一个彩色图像用于绘制轮廓
img_color = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)

# 绘制所有找到的轮廓
# cv2.drawContours(image, contours, contourIdx, color, thickness)
# contourIdx: 要绘制的轮廓索引 (-1 表示绘制所有轮廓)
cv2.drawContours(img_color, contours, -1, (0, 255, 0), 2) # 绿色线条

# 遍历并处理单个轮廓
for i, cnt in enumerate(contours):
    # 计算轮廓的面积
    area = cv2.contourArea(cnt)
    print(f"轮廓 {i} 的面积: {area}")

    # 计算轮廓的周长 (True 表示闭合轮廓)
    perimeter = cv2.arcLength(cnt, True)
    print(f"轮廓 {i} 的周长: {perimeter}")

    # 获取轮廓的最小外接矩形 (可以不是水平的)
    # rotated_rect = cv2.minAreaRect(cnt)
    # box = cv2.boxPoints(rotated_rect)
    # box = np.int0(box)
    # cv2.drawContours(img_color, [box], 0, (0, 0, 255), 2) # 红色矩形

    # 获取轮廓的水平外接矩形 (Bounding Box)
    x, y, w, h = cv2.boundingRect(cnt)
    cv2.rectangle(img_color, (x, y), (x+w, y+h), (255, 0, 0), 2) # 蓝色矩形

    # 计算轮廓的中心矩和中心
    # M = cv2.moments(cnt)
    # if M["m00"] != 0:
    #     cx = int(M["m10"] / M["m00"])
    #     cy = int(M["m01"] / M["m00"])
    #     cv2.circle(img_color, (cx, cy), 5, (0, 255, 255), -1) # 黄色中心点

cv2.imshow('Original Grayscale', img_gray)
cv2.imshow('Threshold Image', thresh)
cv2.imshow('Contours', img_color)
cv2.waitKey(0)
cv2.destroyAllWindows()

else:
print(“Error: Could not load image.”)
“`

cv2.findContours() 函数的参数和返回值比较重要,尤其是不同的检索模式和逼近方法会影响找到的轮廓数量和点的表示。cv2.RETR_EXTERNAL 只查找最外层的轮廓,cv2.CHAIN_APPROX_SIMPLE 会压缩水平、垂直和对角线段上的点,只保留端点。

通过计算轮廓的面积、周长、外接矩形等属性,我们可以对物体进行简单的分析和筛选。

第九步:处理视频 – 捕获、显示与保存

OpenCV 不仅能处理静态图像,还能轻松处理视频流,无论是来自文件还是摄像头。

播放视频文件

“`python
import cv2

创建一个VideoCapture对象

cap = cv2.VideoCapture(‘path/to/your/video.mp4’) # 或者使用 0 来捕获摄像头

检查是否成功打开视频/摄像头

if not cap.isOpened():
print(“Error: Could not open video or camera.”)
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)
print(f”视频宽度: {frame_width}, 高度: {frame_height}, 帧率: {fps}”)

while cap.isOpened():
    # cap.read() 返回两个值:
    # 1. ret: 一个布尔值,表示是否成功读取到帧
    # 2. frame: 读取到的帧 (一个NumPy数组)
    ret, frame = cap.read()

    # 如果成功读取到帧
    if ret:
        # 在这里可以对每一帧进行处理 (例如,灰度转换、边缘检测等)
        # gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 显示处理后的帧 (或者原始帧)
        cv2.imshow('Video Frame', frame) # 显示彩色帧
        # cv2.imshow('Video Frame (Gray)', gray_frame) # 显示灰度帧

        # 设置帧之间的延迟,控制播放速度
        # 参数是毫秒。如果设置为 1,大约是 1000ms / 1ms = 1000 帧/秒 (太快)
        # 通常设置为一个与帧率相关的数值,例如 1000 / fps
        # 如果按下 'q' 键,退出循环
        if cv2.waitKey(25) & 0xFF == ord('q'): # 等待25ms,如果按下'q'键
            break
    else:
        # 如果没有读取到帧 (视频结束)
        break

# 释放VideoCapture对象
cap.release()
# 销毁所有窗口
cv2.destroyAllWindows()

“`

这个循环会一帧一帧地读取视频,并在窗口中显示。cv2.waitKey() 的参数决定了每一帧显示的时间,从而控制播放速度。

录制视频文件

你可以将处理后的视频帧保存为一个新的视频文件。

“`python
import cv2

cap = cv2.VideoCapture(0) # 从摄像头捕获

if not cap.isOpened():
print(“Error: Could not open camera.”)
else:
# 获取帧的宽度、高度
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = 20 # 设定录制帧率 (通常低于摄像头原始帧率以避免跳帧)

# 定义视频编码器和创建 VideoWriter 对象
# VideoWriter_fourcc(*'XVID') 是一个常用的编码器
# 可以尝试其他编码器,如 'MJPG', 'X264' 等,兼容性可能不同
fourcc = cv2.VideoWriter_fourcc(*'XVID')
# cv2.VideoWriter(filename, fourcc, fps, frameSize[, isColor])
out = cv2.VideoWriter('output.avi', fourcc, fps, (frame_width, frame_height))

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

    if ret:
        # 在这里对帧进行处理 (可选)
        #例如,翻转帧
        # frame = cv2.flip(frame, 0) # 垂直翻转
        # frame = cv2.flip(frame, 1) # 水平翻转

        # 将处理后的帧写入文件
        out.write(frame)

        # 显示帧 (可选)
        cv2.imshow('Recording...', frame)

        # 如果按下 'q' 键,停止录制
        if cv2.waitKey(1) & 0xFF == ord('q'): # 等待1ms
            break
    else:
        break

# 释放VideoCapture和VideoWriter对象
cap.release()
out.release()
# 销毁所有窗口
cv2.destroyAllWindows()
print("Video recording finished.")

“`

使用 cv2.VideoWriter_fourcc() 指定视频编码器,cv2.VideoWriter() 创建写入对象,然后在一个循环中不断读取帧并用 out.write() 写入。

第十步:进一步探索 – 更多有趣的功能

OpenCV 的功能远不止于此。当你掌握了上述基础知识后,可以进一步学习:

  1. 特征检测与匹配: SIFT, SURF (专利限制,在新版本中部分可用或有免费替代品如 ORB, AKAZE), Shi-Tomasi 角点检测等,用于在不同图像中找到对应的点。
  2. 对象识别: 基于机器学习的方法,如支持向量机 (SVM)、K 近邻 (KNN),或者更高级的深度学习模型(OpenCV 提供了 DNN 模块,可以加载 TensorFlow, PyTorch 等框架训练的模型)。
  3. 目标跟踪: 在视频序列中跟踪特定对象,如 Mean-Shift, CamShift, 光流法 (Optical Flow) 等。
  4. 校准与姿态估计: 相机标定,畸变校正,单应性 (Homography) 和基本矩阵 (Fundamental Matrix) 计算等。
  5. 立体视觉与三维重建: 使用多幅图像获取深度信息。
  6. 图像分割: GrabCut 算法,分水岭算法 (Watershed) 等。
  7. 机器学习模块: OpenCV 也包含一些传统的机器学习算法实现。

学习资源推荐

  • OpenCV 官方文档: 这是最权威的资源,详细介绍了每个函数的功能和参数。
  • OpenCV 官方教程: 在官方网站上有基于 C++ 和 Python 的详细教程,覆盖了从基础到进阶的许多主题。
  • 书籍: 如《学习 OpenCV 3 中文版》、《OpenCV 4 计算机视觉项目实战》等。
  • 在线课程: Coursera, Udacity, Bilibili 等平台上有许多高质量的 OpenCV Python 课程。
  • GitHub: 搜索相关的项目,查看别人的代码,学习实践经验。

总结

从零开始学习 OpenCV Python 是一段充满挑战但非常有益的旅程。本文带你了解了 OpenCV 的基本概念、环境搭建、图像的 NumPy 表示、基本的图像加载、显示、保存、像素访问、绘图,以及一些基础的图像处理技术如颜色空间转换、阈值化、平滑、边缘检测和轮廓处理,最后还简要介绍了视频处理。

计算机视觉是一个实践性很强的领域。理论知识固然重要,但动手实践、编写代码、亲自观察不同参数的效果才是掌握 OpenCV 的关键。

不要害怕错误,多尝试,多查阅资料。从简单的例子开始,逐渐挑战更复杂的任务。随着你的学习深入,你会发现 OpenCV 的强大之处以及计算机视觉的无限魅力。

祝你在 OpenCV Python 的学习道路上取得成功!开始编码吧!


发表评论

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

滚动至顶部