本节目标:
- 学会读取、显示和保存视频
- 学会从摄像头捕获视频并显示
- 将会学到以下几个函数:
cv2.VideoCapture()、cv2.VideoWrite()
原文地址:Getting Started with Videos
从摄像头捕获视频
通常,我们需要从摄像设备捕获在线的视频流,Opencv为此提供了非常简便的操作接口。这里我将使用我的笔记本内置的摄像头,然后捕获视频,转换成灰度视频,并显示。
为了捕获视频,首先你需要创建一个VideoCapture
对象,它的构造参数既可以是设备索引(device index)也可以是视频文件名。设备索引是一个数字用以指定是哪个摄像头,通常情况下设备都装载一个摄像头,所以只需要简单的传递参数0(或者-1)就可以了,你可以选择传1或者其他来选择第二个摄像设备。之后你就能逐帧捕获视频了。最后,不要忘记释放设备资源。代码如下
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
# 逐帧捕获视频
ret, frame = cap.read()
# 对帧操作: 转成灰度图
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
# 显示视频帧
cv2.imshow('frame',gray)
# 1毫秒显示逐帧显示,按q退出
if cv2.waitKey(1) == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
cap.read()
返回一个bool类型的结果,用来表示视频帧是否被正确读取,True正确。故你可以通过检查这个返回值来检查视频的结尾。
有时,cap
可能没有获取到初始化好的设备,这种情况下,这个代码将会显示error,你可以用过调用方法:cap.isOpened()
来检查设备是否已经初始化完成。如果返回True表示完成,否则你需要手动调用cap.open()
来打开它。
cap = cv2.VideoCapture(0)
if (not cap.isOpened()):
cap.open(0)
cap.read()
# ... some other operations
你也可以通过方法cap.get(propId)
访问该视频的其他熟悉,此处propId是一个从0到18的数字。每一个数字代表一个属性(若该属性在该video可用的话)。全部的属性标识见下表。你可以通过方法cap.set(propId,value)
修改这些属性值,参数value就是你想要修改的目标值。
propId | property |
---|---|
CV_CAP_PROP_POS_MSEC | 当前帧在视频中的位置(毫秒)或是在线捕获视频的时间戳 |
CV_CAP_PROP_POS_FRAMES | 以0开始的下一个被解码(视频文件)/ 捕获(在线视频流)的帧的索引 |
CV_CAP_PROP_POS_AVI_RATIO | 当前帧的相对位置,0表示视频开始帧,-1表示视频结尾 |
CV_CAP_PROP_FRAME_WIDTH | 视频流中一帧图像的宽度 |
CV_CAP_PROP_FRAME_HEIGHT | 视频流中一帧图像的高度 |
CV_CAP_PROP_FPS | 帧率,FPS |
CV_CAP_PROP_FOURCC | 编译码的4字符码【1】 |
CV_CAP_PROP_FRAME_COUNT | 视频文件的总帧数 |
CV_CAP_PROP_FORMAT | retrieve() 返回的Mat对象的格式 |
CV_CAP_PROP_MODE | 后端特定值,用来指示当前捕获模式 |
CV_CAP_PROP_BRIGHTNESS | 图像的亮度(仅相机设备可用) |
CV_CAP_PROP_CONTRAST | 图像的对比度(仅相机设备可用) |
CV_CAP_PROP_SATURATION | 图像的饱和度(仅相机设备可用) |
CV_CAP_PROP_HUE | 图像的色调(仅相机设备可用) |
CV_CAP_PROP_GAIN | 图像的增益(仅相机设备可用) |
CV_CAP_PROP_EXPOSURE | 图像的曝光(仅相机设备可用) |
CV_CAP_PROP_CONVERT_RGB | 一个bool类型的标志用来指示是否图像需要被转换成RGB图像(通道顺序) |
CV_CAP_PROP_WHITE_BALANCE_U | 白平衡的U值(仅支持DC1394 v2.x) |
CV_CAP_PROP_WHITE_BALANCE_V | 白平衡的V值(仅支持DC1394 v2.x) |
CV_CAP_PROP_RECTIFICATION | 立体相机的校正标志 |
CV_CAP_PROP_ISO_SPEED | 相机的ISO(感光速率) |
CV_CAP_PROP_BUFFERSIZE | 当前存于缓冲中的帧的总数 |
若出现error,请在其他应用中使用摄像头以确保设备是可以正常工作的
播放视频文件
这和使用摄像设备捕获实时视频流一样,只需要将camera的参数从index变成视频文件名即可。同样,我们还是推荐在播放视频帧的时候,使用cv2.waitKey()
并设置合适的时间参数,如果设置太小,视频会加速播放,太大又会视频变慢。通常25milliseconds是比较合适的值。
import numpy as np
import cv2
cap = cv2.VideoCapture('image\\test2.mp4')
while(cap.isOpened()):
ret, frame = cap.read()
# 这里必须判断视频是否读取完毕,否则最后一帧播放出现问题
if ret == True:
#gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#cv2.imshow('frame', gray)
cv2.imshow('frame',frame)
if cv2.waitKey(1) == ord('q'):
break
else:
break
cap.release()
cv2.destroyAllWindows()
- 问题1:
cap = cv2.VideoCapture('image\\test2.mp4')
,这个路径一定要小心,路径的分隔符最好写成‘\\’形式,而不要写成image/test2.mp4
,我的理解是,这个路径会送到ffmpeg中,ffmpeg是c++实现的库,它在拼接路径的时候不能识别第二种在Python中常用的路径形式。 - 问题2:英文原文给出的demo代码中,没有对ret做判断,这会导致视频播放最后一帧出错,因为视频最后会捕获到一个空帧,此时再操作frame,就会error。而ret是一个标志,指示这个frame是否有效。false表示frame为空,也可认为是视频结尾。
请确保安装好了合适版本的ffmpeg或是gstreamer。大多数时候在视频捕获、播放中的那些令人头疼的错误是源自于ffmpeg/gstreamer安装不当。
保存视频
当我们捕获到视频,逐帧处理之后,我们可能想要去保存视频。若是图片,简单的调用cv2.imwrite()
即可。那么视频就需要稍微复杂一些了。
我们需要先创建一个VideoWrtie
对象,需要指定输出的文件名(如output.avi)。之后我们应该指定FourCC码(下文会说到其细节)。然后是FPS以及帧的大小(frame size),最后是标志isColor
,若是True,表示编码的时候得到彩色帧,否则是灰度帧。
FourCC是一种4字节码,用于去指定视屏的编码解码器。在fourcc.org可以找到一系列可用的码值,它依赖于具体的平台,笔者的平台可用以下编解码器
- Fedora:DIVX, XVID, MJPG, X264, WMV1, WMV2. (XVID更好, MJPG会产生很大的视频文件 ,X264得到小文件的视频 )
- Windows:DIVX
FourCC码可以在调用函数的时候这样传递,以MJPG为例。cv2.VideoWriter_fourcc('M','J','P','G')
或者cv2.VideoWriter_fourcc(*'MJPG)
【Wiki】FourCC
下面的代码演示从相机捕获视频,然后逐帧反正之后保存。
cv2.flip(frame, 0)
,第一个参数为帧对象,第二个是反正操作标志,取值如下
- 1,水平反转
- 0,垂直翻转
- -1,水平垂直翻转
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
# 定义编解码器,创建VideoWriter对象
fourcc = cv2.VideoWriter_fourcc(*'XVID')
# 参数依次为:out_file_name, codec, fps, frame_size, isColor(default=True)
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while (cap.isOpened()):
ret, frame = cap.read()
if ret == True:
# 垂直翻转
frame = cv2.flip(frame, 0)
# 将数据帧写入out对象(VideoWriter对象)
out.write(frame)
# 播放帧
cv2.imshow('frame', frame)
if cv2.waitKey(1) == ord('q'):
break
else:
break
# 释放资源
cap.release()
out.release()
cv2.destroyAllWindows()