@[TOC](OpenCV入门(十七)快速学会OpenCV 16 视频处理)
作者:Xiou
OpenCV的视频I/O模块提供了一组用于读写视频或图像序列的类和函数。该模块将cv::VideoCapture和cv::VideoWriter类作为一层接口面向用户,这两个类下面是很多不同种类的后端视频I/O API,有效地屏蔽了后端视频I/O的差异性,简化了用户层的编程。
在OpenCV中,视频的读操作是通过VideoCapture类来完成的,视频的写操作是通过VideoWriter类来实现的。
1.构造VideoCapture对象
类VideoCapture既支持从视频文件(.AVI、*.MP4、.MPG等格式)读取,也支持直接从摄像机(比如计算机自带的摄像头)中读取。要想获取视频,需要先创建一个VideoCapture对象。创建VideoCapture对象有以下三种方式:
(1)从视频文件中读取视频。如果是从文件(.MPG或.AVI格式)中读取视频,那么在定义对象的时候可以把视频文件的路径作为参数传给构造函数。对象创建以后,OpenCV将会打开该视频文件并做好准备读取它。如果打开成功,就可以开始读取视频的帧。
VideoCapture提供了成员函数isOpened来判断是否成功打开,若成功,则返回True(建议在打开视频或摄像头时都使用该成员函数判断是否成功打开)。下面的构造函数用于读取视频文件:
VideoCapture(filename) → <VideoCapture object>
其中,参数filename是视频文件的文件名(可以包含路径),如果不包含路径,就在当前路径下打开文件。比如,我们定义一个VideoCapture对象并打开d盘上的test.avi文件:
cv2.VideoCapture capture("d:/test.avi"); //从视频文件读取
(2)从摄像机中读取视频。如果是从摄像机中读取视频,那么这种情况下我们会给出一个标识符,用于表示我们想要访问的摄像机,及其与操作系统的握手方式。
对于摄像机而言,这个标志符就是一个标志数字:如果只有1个摄像机,就是0;如果系统中有多个摄像机,那么只要将其向上增加即可。
标识符的另外一部分是摄像机域(camera domain),用于表示摄像机的类型,这个域值可以是下面任一预定义常量。读取摄像头中的视频的构造函数如下:
cv2.VideoCapture(device) → <VideoCapture object>
其中,参数device表示要打开的视频捕获设备(摄像头)的ID,如果使用默认后端的默认摄像头,就只需传递0。
(3)不带参数构造一个VideoCapture对象。使用类VideoCapture的不带参数的构造函数来创建一个VideoCapture对象,然后用成员函数open来打开一个视频文件或摄像头。open函数声明如下:
VideoCapture.open(filename) → retval
VideoCapture.open(device) → retval
两个open函数的参数和前面的两个构造函数的参数含义一样。
当我们打开一个视频文件或摄像头视频后,可以用成员函数isOpened来判断是否打开成功。函数声明如下:
VideoCapture.isOpened() → retval
打开成功就返回True,否则返回False。
import cv2
cap = cv2.VideoCapture("./Demo.avi")
if cap.isOpened(): # 当成功打开视频时cap.isOpened()返回True,否则返回False
...
2.构造VideoWriter对象
OpenCV中的cv2.VideoWriter类可以将图片序列保存成视频文件,也可以修改视频的各种属性,还可以完成对视频类型的转换。
penCV为cv2.VideoWriter类提供了构造函数,用它来实现初始化工作。该函数的语法格式是:
<VideoWriter object> = cv2.VideoWriter( filename, fourcc, fps, frameSize[,isColor] )
式中:
● filename指定输出目标视频的存放路径和文件名。如果指定的文件名已经存在,则会覆盖这个文件。
● fourcc表示视频编/解码类型(格式)。在OpenCV中用函数cv2.VideoWriter_fourcc()来指定视频编码格式。cv2.VideoWriter_fourcc()有4个字符参数。这4个字符参数构成了编/解码器的“4字标记”,每个编/解码器都有一个这样的标记。下面列出几个常用的标记。
● cv2.VideoWriter_fourcc(‘I’, ‘4’, ‘2’,’0′)表示未压缩的YUV颜色编码格式,色度子采样为4:2:0。该编码格式具有较好的兼容性,但产生的文件较大,文件扩展名为.avi。
● cv2.VideoWriter_fourcc(‘P’, ‘I’, ‘M’,’I’)表示MPEG-1编码类型,生成的文件的扩展名为.avi。
● cv2.VideoWriter_fourcc(‘X’, ‘V’, ‘I’,’D’)表示MPEG-4编码类型。如果希望得到的视频大小为平均值,可以选用这个参数组合。该组合生成的文件的扩展名为.avi。
● cv2.VideoWriter_fourcc(‘T’, ‘H’, ‘E’,’O’)表示Ogg Vorbis编码类型,文件的扩展名为.ogv。
● fps为帧速率。
● frameSize为每一帧的长和宽。
● isColor表示是否为彩色图像。
下面的语句完成了cv2.VideoWriter类的初始化工作:
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, 20, (1024,768))
cv2.VideoWriter类中的函数cv2.VideoWriter.write()用于写入下一帧视频。该函数的语法格式为:
None=cv2.VideoWriter.write(image)
代码实例:使用cv2.VideoWriter类保存摄像头视频文件。
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('I', '4', '2', '0')
out = cv2.VideoWriter('output.avi', fourcc, 20, (640,480))
while(cap.isOpened()):
ret, frame = cap.read()
if ret==True:
out.write(frame)
cv2.imshow('frame', frame)
if cv2.waitKey(1) == 27:
break
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
● 设置好要保存的具体文件名,例如:filename=”out.avi”。
● 使用cv2.VideoWriter_fourcc()确定编/解码的类型,例如:fourcc =cv2.VideoWriter_fourcc(*’XVID’)。
● 确定视频的帧速率,例如:fps=20。
● 确定视频的长度和宽度,例如:size=(640,480)。
运行上述程序,程序就会捕获当前摄像头的视频内容,并将其保存在当前目录下名为“output.avi”的视频文件中。
3.视频操作基础
视频是由视频帧构成的,将视频帧从视频中提取出,对其使用图像处理的方法进行处理,就可以达到处理视频的目的。
3.1 读取视频帧
要播放视频,肯定要先读取每一帧的视频图像再显示出来。读取方法是使用函数read,该函数声明如下:
VideoCapture.read([image]) → retval, image
其中,参数image用来存放读取到的当前视频帧。如果读取成功就返回True,否则返回False。
vc = cv2.VideoCapture(“my.mp4”)
rval, frame = vc.read()
while success:
cv2.setWindowTitle("test", "MyTest") # 设置标题
frame = cv2.resize(frame, (960, 540)) # 根据视频帧大小进行缩放
cv2.imshow('windows', frame) # 显示
cv2.waitKey(int(1000 / int(fps))) # 设置延迟时间
success, frame = video.read() # 获取下一帧
video.release()
3.2 播放视频文件
播放视频文件的基本步骤就是先构造VideoCapture对象,然后打开视频文件,接着用一个循环逐帧读取并显示读取到的视频帧,再间隔一段时间读取下一个视频帧并显示,依次循环,直到全部视频帧读取完毕。
import numpy as np
import cv2
video = cv2.VideoCapture('sea.mp4')
# 获得码率及尺寸
fps = video.get(cv2.CAP_PROP_FPS)
size = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fNUMS = video.get(cv2.CAP_PROP_FRAME_COUNT)
# 读帧
success, frame = video.read()
while success:
cv2.setWindowTitle("test", "I love sea.") # 设置标题
frame = cv2.resize(frame, (960, 540)) # 根据视频帧大小进行缩放
cv2.imshow('windows', frame) # 显示
cv2.waitKey(int(1000 / int(fps))) # 设置延迟时间
success, frame = video.read() # 获取下一帧
video.release()
sea.mp4位于当前工程目录下。在上述代码中,我们首先定义了一个VideoCapture对象video,并传入了视频文件的文件名,因为没有加路径,所以sea.mp4要放在当前工程目录下才会被找到。然后利用成员函数get获得码率及尺寸,再用read读取帧,并用一个while不停地读取下一帧,当success为False时说明视频帧没有了,也就是播放完毕了,此时跳出循环。
每一帧其实就是一幅图片,可以用imshow函数把图片显示出来,速度快了以后多幅图片连续显示,看起来就是视频了。这里我们设置每个帧显示之间的时间间隔是int(1000/int(fps))毫秒。
值得注意的是,OpenCV是一个视觉库,目前不支持视频中的声音处理,因此播放的视频没有声音。如果需要声音,则需要使用ffpyplayer。
3.3 获取和设置视频属性
类VideoCapture的成员函数get可以用来获取视频文件的一些属性,比如帧数。该函数声明如下:
VideoCapture.get(propId) → retval
其中,参数propId表示要获取的属性ID,通常取值是一个宏,比如CAP_PROP_FRAME_COUNT表示获取视频帧数,宏CAP_PROP_FRAME_COUNT的值是7。其他常用属性及其用法如下:
cv2.VideoCapture.get(0) ; //视频文件的当前位置(播放)以毫秒为单位
cv2.VideoCapture.get(1) ; //基于以0开始的被捕获或解码的帧索引
cv2.VideoCapture.get(2) ; //视频文件的相对位置(播放):0表示电影开始,1表示影片的结尾
cv2.VideoCapture.get(3) ; //在视频流的帧的宽度
cv2.VideoCapture.get(4) ; //在视频流的帧的高度
cv2.VideoCapture.get(5) ; //帧速率
cv2.VideoCapture.get(6) ; //编解码的4字符代码
cv2.VideoCapture.get(7) ; //视频文件中的帧数
cv2.VideoCapture.get(8) ; //返回对象的格式
cv2.VideoCapture.get(9) ; //返回后端特定的值,指示当前捕获模式
cv2.VideoCapture.get(10) ; //图像的亮度(仅适用于照相机)
cv2.VideoCapture.get(11) ; //图像的对比度(仅适用于照相机)
cv2.VideoCapture.get(12) ; //图像的饱和度(仅适用于照相机)
cv2.VideoCapture.get(13) ; //色调图像(仅适用于照相机)
cv2.VideoCapture.get(14) ; //图像增益(仅适用于照相机)(Gain在摄影中表示白平衡提升)
cv2.VideoCapture.get(15) ; //曝光(仅适用于照相机)
cv2.VideoCapture.get(16) ; //指示是否应将图像转换为RGB布尔标志
MyVideoCapture.get(17) ; //暂时不支持
cv2.VideoCapture.get(18); //立体摄像机的矫正标注(DC1394 v.2.x后端支持这个功能)
有获取属性的函数,自然也有设置属性的函数,具体函数声明如下:
VideoCapture.set(propId, value) → retval
其中,参数propId表示要设置的属性ID;value表示要设置的属性值。
4.播放摄像头视频
播放摄像头视频和播放视频文件类似,也是通过类VideoCapture来实现的,只不过open的时候是传入摄像头的索引号,如果计算机中插入一个摄像头,那么open的第一个参数通常是700,比如:
cap=cv2.VideoCapture.open(700, CAP_DSHOW);
打开成功后,就可以一帧一帧地读取并一帧一帧地播放了,其实就是在一个循环里间隔地显示一幅一幅的视频帧图片,间隔时间短,就像是在看视频。
最后播放完毕后,释放资源,比如:
cap.release()
代码实例:
import cv2 as cv
#打开摄像头 默认为700
cap = cv.VideoCapture(700)
cap.set(cv.CAP_PROP_FRAME_WIDTH,320)
cap.set(cv.CAP_PROP_FRAME_HEIGHT,240)
while True:
#每次读取一帧摄像头或者视频
ret,frame = cap.read()
#将一帧frame显示出来,第一个参数为窗口名
cv.imshow('frame',frame)
#每次等待1ms 当esc按键被按下时退出显示
#ESC按键对应的键值为27
if(cv.waitKey(1)&0xff) == 27:
break
#常规操作 释放资源
cap.release()
cv.destroyAllWindows()
运行结果:
这个视频是彩色的,下面我们来播放黑白视频。
代码实例:
import numpy as np
import cv2
capture = cv2.VideoCapture(700)
# 获取 capture 的一些属性
frame_width = capture.get(cv2.CAP_PROP_FRAME_WIDTH)
frame_height = capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = capture.get(cv2.CAP_PROP_FPS)
print(frame_width, frame_height, fps)
if capture.isOpened() is False:
print('Error openning the camera')
frame_index = 0
while capture.isOpened():
ret, frame = capture.read()
if ret:
# 显示摄像头捕获的帧
cv2.imshow('Input frame from the camera', frame)
# 把摄像头捕捉到的帧转换为灰度
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 显示处理后的帧
cv2.imshow('Grayscale input camera', gray_frame)
# cv2.waitKey()这个函数是在一个给定的时间内(单位ms)等待用户按键触发
# 如果用户没有按下按键,则继续等待(循环)
if (cv2.waitKey(10) & 0xFF) == ord('q'):
break
if (cv2.waitKey(10) & 0xFF) == ord('c'):
frame_name = f'camera_frame_{frame_index}.png'
gray_frame_name = f'grayscale_camera_frame_{frame_index}.png'
cv2.imwrite(frame_name, frame)
cv2.imwrite(gray_frame_name, gray_frame)
frame_index += 1
else:
break
capture.release()
cv2.destroyAllWindows()
运行结果: