数字视频与数字图像是近亲,因为它们由许多数字图像连续快速播放组成,从而产生运动视觉数据的效果。
OpenCV库提供了几种处理视频的方法,例如从不同源读取视频数据以及访问其多个属性。
在本教程中,您将熟悉处理视频时最基本的OpenCV操作。
完成本教程后,您将了解:
- 数字视频如何被构成为数字图像的近亲。
- 构成视频的图像帧如何从摄像头读取。
- 构成视频的图像帧如何从已保存的视频文件中读取。
通过我的书《OpenCV 机器学习》启动您的项目。它提供了带有可用代码的自学教程。
让我们开始吧。

使用OpenCV读取和显示视频
照片由Thomas William拍摄,部分权利保留。
教程概述
本教程分为三个部分;它们是:
- 视频是如何构成的?
- 从摄像头读取和显示图像帧
- 从视频文件中读取和显示图像帧
视频是如何构成的?
我们已经知道,数字图像由像素组成,每个像素都以其在图像空间内的空间坐标和其强度或灰度值来表征。
我们还提到,由单个通道组成的灰度图像可以由二维函数I(x, y)描述,其中x和y表示上述空间坐标,并且在任何图像位置(x, y)处I的值表示像素强度。
反过来,RGB图像可以由三个这样的二维函数IR(x, y), IG(x, y), 和IB(x, y)描述,分别对应于其红色、绿色和蓝色通道。
在描述数字视频时,我们将添加一个额外的维度t,表示时间。这样做的原因是数字视频由在一段时间内连续快速显示的数字图像组成。在视频的上下文中,我们将这些图像称为图像帧。帧连续显示的速率称为帧率,以每秒帧数(简称FPS)为单位。
因此,如果我们选择一个特定时间实例t的灰度视频中的一帧图像,我们将用函数I(x, y, t)来描述它,该函数现在包含时间维度。
同样,如果我们选择一个特定时间实例t的RGB视频中的一帧图像,我们将用三个函数来描述它:IR(x, y, t), IG(x, y, t), 和IB(x, y, t),分别对应于其红色、绿色和蓝色通道。
我们的描述告诉我们,数字视频中包含的数据是随时间变化的,这意味着数据会随时间改变。
简而言之,这意味着具有坐标(x, y)的像素在时间实例t的强度值可能与其在另一个时间实例(t + 1)的强度值不同。这种强度值的变化可能源于正在记录的物理场景正在变化,也可能源于视频数据中存在的噪声(例如,源自摄像头传感器本身)。
从摄像头读取和显示图像帧
要从连接到您计算机的摄像头或存储在硬盘上的视频文件中读取图像帧,我们的第一步是创建一个VideoCapture
对象来工作。必需的参数是要从中读取的摄像头的索引值int
,或者是视频文件名。
让我们首先从摄像头捕获图像帧。
如果您有内置或连接到计算机的网络摄像头,则可以使用索引0
。如果您有其他连接的摄像头希望从中读取,则可以使用索引1
、2
等,具体取决于您有多少可用摄像头。
1 2 3 |
from cv2 import VideoCapture capture = VideoCapture(0) |
在尝试读取和显示图像帧之前,检查与摄像头的连接是否成功建立是明智的。可以使用capture.isOpened()
方法来完成此目的,如果连接未能建立,则返回False
。
1 2 |
if not capture.isOpened(): print("Error establishing connection") |
如果摄像头已成功连接,我们就可以使用capture.read()
方法来读取图像帧,如下所示:
1 |
ret, frame = capture.read() |
此方法在frame
中返回下一帧图像,以及一个布尔值ret
,如果成功捕获到图像帧,则为True
;如果方法返回空图像,则为False
。后者可能发生,例如,如果摄像头被断开连接。
使用imshow
方法以与处理静态图像相同的方式显示捕获的图像框架:
1 2 3 4 |
from cv2 import imshow if ret: imshow('Displaying image frames from a webcam', frame) |
请始终记住,在使用OpenCV时,每个图像帧都以BGR颜色格式读取。
在完整的代码列表中,我们将上述代码放在一个while
循环中,该循环将持续从摄像头捕获图像帧,直到用户终止它。为了让用户终止while
循环,我们将包含以下两行代码:
1 2 3 4 |
from cv2 import waitKey if waitKey(25) == 27: break |
在这里,waitKey
函数会停止并等待键盘事件指定的毫秒数。它返回按下键的代码,或者如果在指定时间结束之前没有生成键盘事件,则返回-1。在我们的特定情况下,我们将时间窗口指定为25毫秒,并且我们正在检查ASCII代码27
,它对应于按下Esc
键。当按下Esc
键时,while
循环将被break
命令终止。
我们将包含的最后几行代码用于停止视频捕获、释放内存以及关闭用于图像显示的窗口。
1 2 3 4 |
from cv2 import destroyAllWindows capture.release() destroyAllWindows() |
在某些笔记本电脑上,当使用视频捕获时,您会看到内置摄像头旁边有一个小LED灯亮起。您需要停止视频捕获才能关闭该LED。无论您的程序是否正在从摄像头读取,您都需要停止视频捕获。在其他程序可以使用您的网络摄像头之前,您也必须停止视频捕获。
完整的代码清单如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
from cv2 import VideoCapture, imshow, waitKey, destroyAllWindows # 创建视频捕获对象 capture = VideoCapture(0) # 检查摄像头连接是否已建立 if not capture.isOpened(): print("Error establishing connection") while capture.isOpened(): # 读取一帧图像 ret, frame = capture.read() # 如果成功捕获到一帧图像,则显示它 if ret: imshow('Displaying image frames from a webcam', frame) # 如果按下Esc键,则终止while循环 if waitKey(25) == 27: break # 释放视频捕获并关闭显示窗口 capture.release() destroyAllWindows() |
想开始学习 OpenCV 机器学习吗?
立即参加我的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
从视频文件中读取和显示图像帧
另外,也可以从硬盘上存储的视频文件中读取图像帧。OpenCV支持多种视频格式。为此,我们将修改代码以指定视频文件的路径而不是摄像头的索引。
我下载了这个视频,将其重命名为Iceland.mp4,并将其保存在名为Videos的本地文件夹中。
我从本地驱动器上显示的视频属性中可以看到,该视频包含1920 x 1080像素的图像帧,其帧率为25 fps。
要读取此视频的图像帧,我们将修改以下代码行如下:
1 |
capture = VideoCapture('Videos/Iceland.mp4') |
还可以获取捕获对象的多个属性,例如图像帧的宽度和高度以及帧率。
1 2 3 4 5 |
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) |
完整的代码清单如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
from cv2 import (VideoCapture, imshow, waitKey, destroyAllWindows, CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT, CAP_PROP_FPS) # 创建视频捕获对象 capture = VideoCapture('Videos/Iceland2.mp4') # 检查摄像头连接是否已建立 if not capture.isOpened(): print("Error opening video file") else: # 获取视频属性并打印它们 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(): # 读取一帧图像 ret, frame = capture.read() # 如果成功捕获到一帧图像,则显示它 if ret: imshow('Displaying image frames from video file', frame) # 如果按下Esc键,则终止while循环 if waitKey(25) == 27: break # 释放视频捕获并关闭显示窗口 capture.release() destroyAllWindows() |
视频有一个时间维度。但在OpenCV中,您一次处理一帧。这可以使视频处理与图像处理保持一致,以便您可以将技术从一个应用到另一个。
我们可以在while
循环中包含其他代码行,以处理capture.read()
方法捕获的每一帧图像。一个例子是将每个BGR图像帧转换为灰度,为此我们可以使用与我们将静态图像转换时使用的相同cvtColor
方法。
1 2 3 |
from cv2 import COLOR_BGR2GRAY frame = cvtColor(frame, COLOR_BGR2GRAY) |
您还能想到哪些其他转换可以应用于图像帧?
进一步阅读
如果您想深入了解此主题,本节提供了更多资源。
书籍
- 使用 Python 精通 OpenCV 4, 2019.
网站
- OpenCV,https://opencv.ac.cn/
总结
在本教程中,您熟悉了处理视频时最基本的OpenCV操作。
具体来说,你学到了:
- 数字视频如何被构成为数字图像的近亲。
- 构成视频的图像帧如何从摄像头读取。
- 构成视频的图像帧如何从已保存的视频文件中读取。
你有什么问题吗?
在下面的评论中提出您的问题,我将尽力回答。
暂无评论。