原文:基于OpenCV的视频流处理方法 - 2020.09.05
出处:人工智能技术干货
这里是关于基于OpenCV的视频文件和摄像头画面的读写操作方法.
OpenCV提供了 VideoCapture 类和 VideoWriter 类来支持各种格式的视频流,支持的格式类型会因系统的不同而有所变化,但基本上都是支持 avi格式的,且对于视频文件和摄像头画面的读写所用到的接口基本上都相同.
1. 获取VideoCapture类实例
不管是读取视频文件还是捕获摄像头画面,都使用到了VideoCapture类,但不同的是传入的形参不一样,如果传给VideoCapture类的是一个视频文件路径那么将是读取来自视频文件的画面,而如果传给VideoCapture类的是摄像头编号那么将是读取来自摄像头的画面,其使用示例如下:
# 获取VideoCapture类实例,读取视频文件
fcap = cv2.VideoCapture('demo.mp4')
# 读取摄像头画面
ccap = cv2.VideoCapture(0)
注,查看对于摄像头编号:
ls -al /dev/ | grep video
对于输出信息以video开头的其数字后缀即为可能的摄像头编号,如果一台电脑有多个摄像头设备,那么将会出现从0开始的多个摄像头编号。
2. 判断获取VideoCapture实例是否成功
如果传入无效的视频文件或摄像头编号,那么VideoCapture类将会在后续的read()接口返回(False,None),为了避免此类事件发生,可以通过VideoCapture类的 isOpened() 接口进行判断,该接口返回一个boolean值,正常获取实例返回True,否则返回False,其使用示例如下:
# 判断是否正确获取VideoCapture类实例
while fcap.isOpened():
# next step operation
3. 获取视频流信息
一般视频流主要的帧信息包含画面宽高还有帧率,对于视频文件,则会多出整个视频流多少帧,因此,一般主要关注这四个视频流信息即可,用到的则是VideoCapture类的get接口,其使用示例如下:
# 获取视频帧的宽
w = fcap.get(cv2.CAP_PROP_FRAME_WIDTH)
# 获取视频帧的高
h = fcap.get(cv2.CAP_PROP_FRAME_HEIGHT)
# 获取视频帧的帧率
fps = fcap.get(cv2.CAP_PROP_FPS)
获取到的帧率对于摄像头设备来说,如果所使用的终端不支持查询,那么将会返回0值,且该值也不是非常精确的。
# 获取视频流的总帧数
fcount = fcap.get(cv2.CAP_PROP_FRAME_COUNT)
注,这个是对视频文件才有意义,对于摄像头是没意义的,且以上获取到的返回信息均是浮点型的,注意转换为整型。
4. 获取帧画面
这个就相对简单,直接使用VideoCapture类的read接口即可,该接口会返回两个参数,第一个参数是读取成功与否标志位,成功为True否则为False,第二个参数则为具体的帧数据,其是一个numpy.ndarray的数组,其使用示例如下:
# 获取帧画面
success, frame = fcap.read()
但在读取过程中,有可能会存在失败的情况出现,一般是在第二次读取时放入一个while循环来保障整个读取顺利进行,如下:
# 判断读取视频流是否成功
while success:
success, frame = fcap.read()
# do something in here
5. 针对一组或多头摄像头特殊处理
当需要同步一组摄像头或一个多头(multihead)摄像头(例如立体摄像头或Kinect)时,read()方法就不太适用了,这时,一般使用 grab()和retrieve()方法代替它。
对于一组摄像头,其使用示例如下:
# 一组摄像头的特殊处理
success0 = fcap0.grab()
success1 = fcap1.grab()
if success0 and success1:
frame0 = fcap0.retrieve()
frame1 = fcap1.retrieve()
6. 跳到视频流某一帧
对于视频文件,有时候需要直接跳到某一个感兴趣的帧并从该帧开始读取数据,那么可以使用VideoCapture类的set接口,其使用示例如下:
# 跳到某一感兴趣帧并从此帧开始读取,如从第360帧开始读取
fcap.set(cv2.CAP_PROP_POS_FRAMES, 360)
success, frame = fcap.read()
7. 设置摄像头分辨率
opencv读取到的摄像头画面大小一般为默认的640x480,但这并不一定满足日常使用要求,比如摄像头支持超高清画面,那么就希望能捕获到超高清1920x1080的画面,好在VideoCapture类的set接口提供了相应的功能,其使用示例如下:
# 设置摄像头分辨率的高
fcap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
# 设置摄像头分辨率的宽
fcap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
8. 获取VideoWriter类实例
不管是对视频文件的再存储还是对摄像头画面的保存,都是用到了VideoWriter类,且传入的形参意义是一致的,都需要传入保存的文件名包含视频格式、指定视频编解码器、保存视频的帧率以及保存视频的分辨率. 一般来说,保存视频的帧率最好与读入的画面的帧率一致,但需要进行估计或使用计时器才会比较准确,而分辨率则可以更改,只是要求写入的帧其大小要与分辨率保持一致,其使用示例如下:
writer = cv2.VideoWriter(
'output.avi',
cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'),
30,
(1080, 1920))
注,必须要为VideoWriter类的构造函数传入所需的参数,且若指定的文件名已存在则会被直接覆盖.
9. VideoWriter类支持的视频编解码器
在构造VideoWriter类实例时,必须要指定视频编解码器,那么VideoWriter类都支持哪些视频编解码器呢?
通过cv2.VideoWriter_fourcc来指定具体使用的编解码器:
'I','4','2','0':该选项是一个未压缩的YUV颜色编码,兼容性好,但产生文件较大,文件扩展名为.avi
'P','T','M','I':该选项是MPEG-1编码类型,文件扩展名为.avi
'X','V','T','D':该选项是MPEG-4编码类型,得到的视频大小处于平均值,文件扩展名为.avi
'T','H','E','O':该选项是Ogg Vorbis,文件扩展名为.ogv
'F','L','V','1':该选项是一个flash视频,文件扩展名为.flv
一般常用的是 cv2.VideoWriter_fourcc('X','V','T','D'),mp4编码文件相对小一些,或cv2.VideoWriter_fourcc('I','4','2','0'),文件相对大一些,但为了缩小文件空间,可能还需要用到ffmpeg工具进一步压缩文件.
10. 保存帧数据
直接使用VideoWriter类的write接口即可,该接口一次可以保存一帧数据到指定文件中,其使用示例如下:
# 保存帧数据
writer.write(frame)
11. 释放资源
不管是VideoCapture类还是VideoWriter类,使用完了它们之后,都应该将它们释放掉,避免资源一直被占用,而这两个类都有提供了release()接口,只需直接调用即可释放资源,使用示例如下:
# 释放VideoCapture资源
fcap.release()
# 释放VideoWriter资源
writer.release()
12. 一个完整的示例代码
#!/usr/bin/python3
#!--*--coding: utf-8--*--
'''
use opencv3 to capture video frame, show and save its stream.
'''
import cv2
def stream_processing():
# 获取VideoCapture类实例,读取视频文件
fcap = cv2.VideoCapture('demo.mp4')
# 设置摄像头分辨率的高
fcap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
# 设置摄像头分辨率的宽
fcap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
# 跳到某一感兴趣帧并从此帧开始读取,如从第360帧开始读取
fcap.set(cv2.CAP_PROP_POS_FRAMES, 360)
# 获取视频帧的宽
w = fcap.get(cv2.CAP_PROP_FRAME_WIDTH)
# 获取视频帧的高
h = fcap.get(cv2.CAP_PROP_FRAME_HEIGHT)
# 获取视频帧的帧率
fps = fcap.get(cv2.CAP_PROP_FPS)
# 获取视频流的总帧数
fcount = fcap.get(cv2.CAP_PROP_FRAME_COUNT)
# 获取VideoWriter类实例
writer = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'), int(fps), (int(w), int(h)))
# 判断是否正确获取VideoCapture类实例
while fcap.isOpened():
# 获取帧画面
success, frame = fcap.read()
while success:
cv2.imshow("demo", frame) ## 显示画面
# 获取帧画面
success,frame = fcap.read()
# 保存帧数据
writer.write(frame)
if (cv2.waitKey(20) & 0xff) == ord('q'):
## 等待20ms并判断是按“q”退出,相当于帧率是50hz,注意waitKey只能传入整数,
break
# 释放VideoCapture资源
fcap.release()
# 释放VideoWriter资源
writer.release()
cv2.destroyAllWindows() ## 销毁所有opencv显示窗口
if __name__ == "__main__":
stream_processing()
以上就是opencv对视频文件和摄像头画面的读写操作了,比较系统全面. 这里记录学习、备忘.