【精选优质专栏推荐】


每个专栏均配有案例与图文讲解,循序渐进,适合新手与进阶学习者,欢迎订阅。

在这里插入图片描述

前言

数字视频与数字图像关系密切,因为它们由许多数字图像以快速连续的方式顺序显示,从而产生视觉数据运动的效果。

OpenCV 库提供了多种处理视频的方法,例如从不同来源读取视频数据以及访问它们的多个属性。

在本教程中,你将熟悉处理视频时最基本的 OpenCV 操作。

完成本教程后,你将了解:

  • 数字视频如何被构建为数字图像的近亲。

  • 如何从摄像头读取构成视频的图像帧。

  • 如何从保存的视频文件读取构成视频的图像帧。

本教程分为三部分:

  • 视频是如何构成的?

  • 从摄像头读取和显示图像帧

  • 从视频文件读取和显示图像帧

视频是如何构成的?

我们已经看到,数字图像由像素组成,每个像素由其在图像空间中的空间坐标以及其强度或灰度值表征。

我们还提到,包含单通道的灰度图像可以由一个二维函数 I(x, y) 描述,其中 x 和 y 表示上述空间坐标,而 I 在任何图像位置 (x, y) 的值表示像素强度。

RGB 图像则可以由三个这样的二维函数 IR(x, y)、IG(x, y) 和 IB(x, y) 描述,它们分别对应图像的红、绿、蓝通道。

在描述数字视频时,我们将添加一个额外的维度 t,它表示时间。这样做的原因是数字视频由在一段时间内快速连续显示的数字图像组成。在视频的上下文中,我们将这些图像称为图像帧。帧按连续方式显示的速率称为帧率,以每秒帧数(frames per second,FPS)计量。

因此,如果我们需要从灰度视频中取出某一特定时间 t 下的图像帧,我们会用函数 I(x, y, t) 来描述它,其中现在包含了时间维度。

同样,如果我们需要从 RGB 视频中取出某一特定时间 t 下的图像帧,我们会用三个函数 IR(x, y, t)、IG(x, y, t) 和 IB(x, y, t) 来描述它们,分别对应红、绿、蓝通道。

上述公式告诉我们,数字视频中包含的数据是随时间变化的,这意味着数据会随着时间改变。

简单来说,这意味着某个坐标为 (x, y) 的像素在时间 t 的强度值,可能会与时间 (t + 1) 的强度值不同。这种强度变化可能来自被记录物理场景的变化,也可能来自视频数据中的噪声(例如来自摄像头传感器本身)。

从摄像头读取和显示图像帧

要从电脑连接的摄像头或存储在硬盘上的视频文件读取图像帧,第一步是创建一个 VideoCapture 对象。所需参数要么是要读取的摄像头的 int 类型索引值,要么是视频文件名。

我们先从摄像头抓取图像帧开始。

如果你的计算机内置或连接了一个摄像头,则可以通过索引值 0 访问它。如果你还连接了其他摄像头并希望从中读取,则可使用索引值 1、2 等,取决于你拥有多少个摄像头。

from cv2 import VideoCapture

capture = VideoCapture(0)

在尝试读取和显示图像帧之前,有必要检查是否已成功建立与摄像头的连接。可以使用 capture.isOpened() 方法来完成,该方法在无法建立连接时返回 False:

if not capture.isOpened():
    print("Error establishing connection")

如果摄像头已成功连接,我们可以使用 capture.read() 方法继续读取图像帧,如下:

ret, frame = capture.read()

此方法返回下一帧图像 frame,以及一个布尔值 ret。如果成功抓取到图像帧,则 ret 为 True;如果方法返回空图像(例如摄像头已断开),则 ret 为 False。

使用 imshow 方法以与静态图像相同的方式显示抓取的图像帧:

from cv2 import imshow

if ret:
    imshow('Displaying image frames from a webcam', frame)

务必记住,在使用 OpenCV 时,每个图像帧都是以 BGR 色彩格式读取的。

在完整代码中,我们将把上述代码放入一个 while 循环中,该循环将持续从摄像头抓取图像帧直到用户终止它。为让用户能够终止 while 循环,我们将加入以下两行代码:

from cv2 import waitKey

if waitKey(25) == 27:
    break

这里,waitKey 函数会暂停并等待指定毫秒数的键盘事件。它返回按下键的代码,如果在指定时间内没有键盘事件,则返回 -1。在本例中,我们指定了 25ms,并检测 ASCII 码为 27 的按键,对应 Esc 键。当 Esc 键被按下时,while 循环被 break 终止。

最后几行代码用于停止视频捕获、释放内存并关闭用于显示图像的窗口:

from cv2 import destroyAllWindows

capture.release()
destroyAllWindows()

在一些笔记本电脑中,当视频捕获被使用时,你会看到内置摄像头旁边的 LED 灯亮起。必须停止视频捕获以关闭该 LED。即使你的程序没有在读取摄像头,也需要停止视频捕获,否则其他程序无法使用该摄像头。

完整代码如下:

from cv2 import VideoCapture, imshow, waitKey, destroyAllWindows

# Create video capture object
capture = VideoCapture(0)

# Check that a camera connection has been established
if not capture.isOpened():
    print("Error establishing connection")

while capture.isOpened():

    # Read an image frame
    ret, frame = capture.read()

    # If an image frame has been grabbed, display it
    if ret:
        imshow('Displaying image frames from a webcam', frame)

    # If the Esc key is pressed, terminate the while loop
    if waitKey(25) == 27:
        break

# Release the video capture and close the display window
capture.release()
destroyAllWindows()

从视频文件读取和显示图像帧

也可以从硬盘上存储的视频文件读取图像帧。OpenCV 支持多种视频格式。为此,我们将修改代码,使其指定视频文件路径,而不是摄像头索引。

下载这个视频,将其重命名为 Iceland.mp4,并保存到名为 Videos 的本地文件夹。

从本地驱动器显示的视频属性中,我可以看到该视频由尺寸为 1920 x 1080 像素的图像帧组成,并以 25 fps 的帧率运行。

为了读取该视频的图像帧,我们要将代码行修改如下:

capture = VideoCapture('Videos/Iceland.mp4')

还可以获取 capture 对象的多个属性,例如图像帧的宽度、高度以及帧率:

from cv2 import CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT, CAP_PROP_FPS

frame_width = capture.get(CAP_PROP_FRAME_WIDTH)
frame_height = capture.get(CAP_PROP_FRAME_HEIGHT)
fps = capture.get(CAP_PROP_FPS)

完整代码如下:

from cv2 import (VideoCapture, imshow, waitKey, destroyAllWindows,
CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT, CAP_PROP_FPS)

# Create video capture object
capture = VideoCapture('Videos/Iceland2.mp4')

# Check that a camera connection has been established
if not capture.isOpened():
    print("Error opening video file")

else:
    # Get video properties and print them
    frame_width = capture.get(CAP_PROP_FRAME_WIDTH)
    frame_height = capture.get(CAP_PROP_FRAME_HEIGHT)
    fps = capture.get(CAP_PROP_FPS)

    print("Image frame width: ", int(frame_width))
    print("Image frame height: ", int(frame_height))
    print("Frame rate: ", int(fps))

while capture.isOpened():

    # Read an image frame
    ret, frame = capture.read()

    # If an image frame has been grabbed, display it
    if ret:
        imshow('Displaying image frames from video file', frame)

    # If the Esc key is pressed, terminate the while loop
    if waitKey(25) == 27:
        break

# Release the video capture and close the display window
capture.release()
destroyAllWindows()

视频具有时间维度。但在 OpenCV 中,你一次处理一帧。这使视频处理与图像处理保持一致,因此可以复用图像处理技术。

我们可以在 while 循环中加入其他代码,以在 capture.read() 获取帧后处理每一帧。例如,可以将每个 BGR 图像帧转换为灰度图像,我们可以使用与静态图像相同的 cvtColor 方法:

from cv2 import COLOR_BGR2GRAY

frame = cvtColor(frame, COLOR_BGR2GRAY)

总结

在本教程中,你熟悉了处理视频时最基本的 OpenCV 操作。

具体来说,你学习了:

  • 数字视频如何被构建为数字图像的近亲。

  • 如何从摄像头读取构成视频的图像帧。

  • 如何从保存的视频文件读取构成视频的图像帧。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐