利用OpenCV读取和写入视频
Posted 卓晴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用OpenCV读取和写入视频相关的知识,希望对你有一定的参考价值。
简 介: 本文介绍了如何对三种不同的视频源(视频文件, 序列图片以及网络摄像头)来进行读取显示视频,使用视频捕获对象。 也给出了如何通过视频捕获对象获取视频流的重要元数据。 介绍了使用视频写入对象把视频流写入磁盘文件。
关键词
: OpenCV,Video
§00 前 言
本文摘录自 Reading and Writing Videos using OpenCV 。对于视频在OpenCV中的读取和写入进行了介绍。
在OpenCV中读取和写入视频与对 图片的操作 很相似。视频无非就是一系列的图片组成,它们被称为 “帧” (frame)。所以,你所做的就是通过循环将一个是视频流中的所有的帧都进行处理,每次处理一帧。 这里我们演示如何从文件中读取、在屏幕上显示和向文件中写入视频流,除此之外还有对视频序列、网络摄像头做同样的操作。
同时对于在操作中可能出现的错误进行深入探讨,以帮助你找到解决的办法。
首先,下面的代码显示了对于一个视频的读取过程,包含有从文件中读取、显示视频的主要函数。在进一步的调试操作过程中,我们对所使用到的函数进一步进行讨论。
import cv2
# Create a video capture object, in this case we are reading the video from a file
vid_capture = cv2.VideoCapture('Resources/Cars.mp4')
if (vid_capture.isOpened() == False):
print("Error opening the video file")
# Read fps and frame count
else:
# Get frame rate information
# You can replace 5 with CAP_PROP_FPS as well, they are enumerations
fps = vid_capture.get(5)
print('Frames per second : ', fps,'FPS')
# Get frame count
# You can replace 7 with CAP_PROP_FRAME_COUNT as well, they are enumerations
frame_count = vid_capture.get(7)
print('Frame count : ', frame_count)
while(vid_capture.isOpened()):
# vid_capture.read() methods returns a tuple, first element is a bool
# and the second is frame
ret, frame = vid_capture.read()
if ret == True:
cv2.imshow('Frame',frame)
# 20 is in milliseconds, try to increase the value, say 50 and observe
key = cv2.waitKey(20)
if key == ord('q'):
break
else:
break
# Release the video capture object
vid_capture.release()
cv2.destroyAllWindows()
在OpenCV Viode I/O中,我们将讨论的主要函数有:
1. cv2.VideoCapture :建立视频捕获对象,它可以形成视频流以及进行显示;
2. cv2.VideoWriter::将输出的视频存储在文件目录中;
3. 此外,我们还需要用到 cv2.imshow(), cv2.waitKey(),get()等函数。get()函数用于获取视频元数据的参数,比如图像帧的高、宽、帧率(fps)等。
下面 Cars.MP4是后面实验中所使用到的视频文件。将通过程序读取并显示它。
▲ 图1 Car.mp4实验视频
首先,你需要确定你已经正确安装 OpenCV。下面通过导入OpenCV库。
# Import libraries
import cv2
§01 读取视频
1.1 从视频文件读取
下面的代码使用VideoCapture()创建一个 VideoCapture对象,我们将使用他来读取视频文件内容。函数调用格式:
VideoCapture(path, apiPreference)
第一个参数是视频文件名称和路径。第二个参数是选项,表明API的参数。后面将通过实例来说明改参数用途。 如果想进一步了解 apiPreference,可以参照VIdeoCaptureAPIs相链接的官方文档。
# Create a video capture object, in this case we are reading the video from a file
vid_capture = cv2.VideoCapture('Resources/Cars.mp4')
在下面的例子中,你将使用所有相同的视频文件,它可以在下载代码目录中找到。
现在你已经有了一个视频捕获对象,使用isOpened() 来确认该 视频文件被正确打开。 函数 IsOpened() 返回一个bool量表示视频流是否可以使用,如果视频流打开错误你将得到一个错误信息。 错误信息可能代表很多情况,其中一个错误情况就是整个视频被破坏了,或者其中一些帧被破坏了。
下面假设视频文件正确被打开,使用 get() 函数来获得视频流的重要参数。请注意对于网络摄像头,这个函数不能够被使用。 get() 函数有一个参数,可以从下面列出的选项中选取。在下面的例子中,我们使用了单个数值参数5和7,他们对应的是帧率(CAP_PRO_FPS)以及帧数(CAP_PROP_FRAME_COUNT),作为参数使用数值或者对应的名称都是可以的。
在获取了视频元数据参数之后,我们可以从文件中读取每一帧图像。建了一个循环过程,uiys vid_capture.read() 来读取一帧视频流图像。
函数 vid_capture.read() 返回一个元组,第一个参数是一个bool量,接下来是读取的视频数据。 如果第一个Bool量为True,表明视频流中包含着能够被读取的图像帧。
如果存在被读取的图像帧,可以使用 imshow() 在窗口中显示当前的图像帧,否则调出循环。注意, 你也需要使用 waitKey() 来在没帧图像显示之间停止20ms。通过调研 waitKey()来监视用户键盘操作。比如按动 “q” 来退出循环。
while(vid_capture.isOpened()):
# vCapture.read() methods returns a tuple, first element is a bool
# and the second is frame
ret, frame = vid_capture.read()
if ret == True:
cv2.imshow('Frame',frame)
k = cv2.waitKey(20)
# 113 is ASCII code for q key
if k == 113:
break
else:
break
一旦所有的视频帧都读取完毕之后,程序退出循环,你释放视频捕获对象(vid_capture),关闭窗口,具体代码如下:
# Release the objects
vid_capture.release()
cv2.destroyAllWindows()
1.2 从视频序列中读取
从图片序列中处理图像帧与视频流处理过程很相像。只是指明被读取的图像文件即可。
下面的代码中:
- 你继续使用视频捕获对象;
- 只不过对视频文件使用图像序列进行替换:
- 使用 %04d表示一个四位证书序列名称,代表图片的名称(Cars0001.jpg, Cars0002.jpg等等)
- 如果使用 %02d则是两位整数代表不同的图片名称,比如 Race_Cars_%02d.jpg,代表着:Race_Cars_01.jpg,Race_Cars_02.jpg等等
vid_capture = cv2.VideoCapture('Resources/Image_sequence/Cars%04d.jpg')
1.3 从网络摄像头读取
读取网络摄像头中的视频流流与前面讨论的方法很接近。为什么这样容易了,这得感谢OpenCV对于视频捕获类定义的灵活性,它通过一系列的重载函数,方便接收不同的输入参数。替代指明视频文件,或者图片序列参数,你仅需要给定一个视频捕获设备索引,如下所示:
- 如果你的系统内置有网络摄像头,摄像头的设备索引号位“0”
- 如果你的计算机系统有多个摄像头,其余的摄像头的索引号递增,比如1,2, 等
vid_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
参数CAP_DSHOW是啥玩意?这是一个车选项参数,并不是必须的。 CAP_DSHOW是另外一个 视频捕获API性能参数,表示通过视频输入直接显示。
§02 写入视频
下面让我们看看如何把视频写入文件。就像视频读入一下,我们也可以吧来自于不同源(视频文件, 图片序列以及网络摄像头)的视频写入一个文件。此间,我们需要做:
- 使用 get() 获取视频帧的尺寸,高,宽
- 初始化视频捕获对象(前面已经讨论过),使用页面描述的任何一种方式吧视频流读入内存。
- 建议视频写入对象;
- 使用视频写入对象吧视频流存入磁盘;
继续前面的应用例子,首先通过get()函数获取视频帧的尺寸:
# Obtain frame size information using get() method
frame_width = int(vid_capture.get(3))
frame_height = int(vid_capture.get(4))
frame_size = (frame_width,frame_height)
fps = 20
正如前面所讨论的那样, 来自于 VideoCapture() 类的 get()成员函数需要:
- 单个参数列表中的选项让我们获取与视频帧相关的元数据参数。
可用的元数据是可扩展的,如下:
- 这里,你可以得到图像真的尺寸,通过(CAP_PROP_FRAME_WIDTH)3 和(CAP_PROP_FRAME_HEIGHT)4。在下面视频写入的时候你需要这些参数。
为了将视频写入文件,你首先需要创建一个视频写入对象 VideoWriter() 类,下面代码所示:
VideoWriter(filename, apiPreference, fourcc, fps, frameSize[, isColor])
函数 VideoWriter() 需要下面的参数:
- filename:输出视频文件的文件名称(带有路径信息)
- apiPerference: API backends 标示;
- fourcc: 4-字符编码代号, 用于压缩图像帧(fourcc)
- fps:创建视频流的帧率
- frame_size: 视频帧的尺寸
- isColor: 非零,编码器将进行彩色变化,要么就进行灰白图像编码(这仅仅在Windows下能够使用)
下面的代码通过VideoWriter()类创建了视频写入对象 output。通过一个方便的函数获取的4-字节的编码代号,这用于VideoWriter的第二个参数。
- VideoWriter_fourcc(‘M’, ‘J’, ‘P’, ‘G’) in Python.
- VideoWriter::fourcc(‘M’, ‘J’, ‘P’, ‘G’) in C++.
视频编码指明了视频压缩方式。它将没有压缩的视频形成压缩后的数据,或者对压缩数据进行解码, 在创建“AVI”,或者"MP4" 时,下面的4-字符代码将会被使用。
- AVI: cv2.VideoWriter_fourcc(‘M’,‘J’,‘P’,‘G’)
- MP4: cv2.VideoWriter_fourcc(*‘XVID’)
函数的后面的参数:帧率和帧尺寸就比较好理解了。
# Initialize video writer object
output = cv2.VideoWriter('Resources/output_video_from_file.avi', cv2.VideoWriter_fourcc('M','J','P','G'), 20, frame_size)
现在你已经创建了视频写入对象,使用它可以将视频流写入磁盘文件。在代码中每一次写入一帧图像。现在,你可以将AVI的视频文件写入磁盘,帧率为20fps。 请注意代码中是如何对前面代码进行循环实现写入过程的。
while(vid_capture.isOpened()):
# vid_capture.read() methods returns a tuple, first element is a bool
# and the second is frame
ret, frame = vid_capture.read()
if ret == True:
# Write the frame to the output files
output.write(frame)
else:
print(‘Stream disconnected’)
break
最后,你需要释放视频捕获对象和写入对象。
# Release the objects
vid_capture.release()
output.release()
§03 读写错误
3.1 视频读取
在读取视频中,如果视频文件路径错误,或者文件格式损坏、缺失,程序就会抛出错误。这就为什么需要在while循环过程中通过if语句进行判断
if ret== True
这也就是在读取的图像帧存在着再进行处理。下面的例子就是显示了在这种情况下碰到的出错现象:
cap_gstreamer.cpp:890: error: (-2) GStreamer: unable to start pipeline in function
3.1.1 路径错误
如果你所给的视频文件的路径错误,在使用VideoCapture()类的时候程序并不给出任何错误提示。知道你后面试图对视频帧进行操作的场吧才会碰到错误。你同样可以使用 if 程序块来进行检查视频文件读取过程是否正确,正如前面所做的那样。
Error opening the video file
3.2 视频写入
视频写入过程会出现各种错误。最常见的是 frame size error 或者 api preference error。 如果视频帧的尺寸与视频不符合,有的时候我们从目录中读取图片是空的,者都会产生frame size error。
使用numpy中的 shape 确认 图像帧的尺寸,注意需要将数据按照OpenCV的格式进行调整:OpenCV 将返回 height x width x channels。
如果出现 api preference error,我们需要将 CAP_ANY标志传入 VideoCapture()参数。在前面网络摄像头代码中,使用CAP_DHOW来避免出现的警告错误。
下面是一个错误日志中的例子:
When CAP_DSHOW is not passed:
[WARN:0]...cap_msmf.cpp(438) …. terminating async callback
When frame size is not correct:
cv2.error: OpenCV(4.5.2) :-1: error: (-5:Bad argument) in function 'VideoWriter'
> Overload resolution failed:
> - Can't parse 'frameSize'. Sequence item with index 0 has a wrong type
> - VideoWriter() missing required argument 'frameSize' (pos 5)
> - VideoWriter() missing required argument 'params' (pos 5)
> - VideoWriter() missing required argument 'frameSize' (pos 5)
※ 小 结 ※
本文介绍了如何对三种不同的视频源(视频文件, 序列图片以及网络摄像头)来进行读取显示视频,使用视频捕获对象。 也给出了如何通过视频捕获对象获取视频流的重要元数据。 介绍了使用视频写入对象把视频流写入磁盘文件。
除了上述介绍的基本过程之外,你也可以在处理视频过程中通过对视频帧图像进行缩放、使用文字或者几何形状进行标注等。
现在你知道的如何对于视频进行读写,对于之后利用OpenCV处理与图像相关的任务时将会由很大的帮助。
■ 相关文献链接:
● 相关图表链接:
以上是关于利用OpenCV读取和写入视频的主要内容,如果未能解决你的问题,请参考以下文章