利用OpenCV读取和写入视频

Posted 卓晴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用OpenCV读取和写入视频相关的知识,希望对你有一定的参考价值。

简 介: 本文介绍了如何对三种不同的视频源(视频文件, 序列图片以及网络摄像头)来进行读取显示视频,使用视频捕获对象。 也给出了如何通过视频捕获对象获取视频流的重要元数据。 介绍了使用视频写入对象把视频流写入磁盘文件。

关键词 OpenCVVideo

前 言 目 录
Contents
读取视频 从视频文件读取 从视频序列中读取 从网络摄像头读取 写入视频 读写错误 视频读取 视频写入 小 结

 

§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读取和写入视频的主要内容,如果未能解决你的问题,请参考以下文章

详细讲解用Python把动漫视频生成字符的完整步骤

opencv标定板像素尺寸

opencv学习笔记02-视频读取与写入

二.使用 OpenCV 读写视频

如何利用OpenCV读取,显示以及写入图片?

利用opencv带你玩转人脸识别-上篇(读取图片,灰度转换,尺寸修改,绘制矩形快速入门)