opencv学习笔记(十九):视频操作入门
第十九章: 视频操作入门
- 一段视频是由很多张静态图片组成的,很多个静态图像组成一段视频。
- 一个静态图像我们称为一帧,每一帧都是一张静止图像。帧可以以固定的时间间隔从视频中提取,然后对其使用图像处理的方法进行处理,就达到了处理视频的目的。
- 帧数指每秒传输的静态画面的数量,也可以理解为图形处理器每秒刷新的次数,通常也称为帧率、刷新率,用fps(Frames Per Second)表示。
- 人类视觉系统每秒可处理10到12个图像并单独感知它们,当很多帧连续快速显示时,人类就会形成运动错觉。所以,帧数越高画面就越流畅,可以产生更平滑和更逼真的动画。当前我们的视频一般是24帧、30帧,就是一秒显示24张或者30张图片。如果玩游戏我们一般都追求4k60帧,就是一秒钟显示60张图片。
opencv提供了cv2.VideoCapture类和cv2.VideoWriter类来处理各种类型的视频文件
注意这两个API是类不是函数,所以名字的写法和函数不一样,全部单词的首字母都用大写,不仅是C、C++,几乎所有的语言写类的时候,类名的首字母都要大写。
一、读取视频
1、从摄像设备中实时读取视频流 cv=cv2.VideoCapture(0)
假如我们要捕获电脑摄像头视频,这个函数可以帮我们打开摄像头并完成摄像头的初始化工作。
函数参数用数字来表示不同的摄像设备(摄像头)的ID编号。默认值是-1,表示随机选择一个摄像头。如果你电脑上有多个摄像头,比如电脑自带一个,你又插了一个USB的摄像头,那么你电脑就会自动给USB的摄像头一个ID编号,如果0表示的是第一个电脑内置的摄像头,1就表示USB的摄像头,如果有多个就以此类推。如果只有一个摄像头就可以用0也可以用-1作为这个摄像头的ID号。
2、直接读取磁盘上已有视频文件 cv=cv2.VideoCapture(文件路径)
#例19.1 从笔记本摄像头里读取视频
import cv2
#创建显示窗口
cv2.namedWindow(my video, cv2.WINDOW_NORMAL) #可以更改窗口大小的
cv2.resizeWindow(my video, 240, 280) #设置一个小窗口
cap = cv2.VideoCapture(0) #打开摄像头并初始化,我笔记本就自带一个摄像头,所以参数填0
#循环读取摄像头的每一帧
while True: #C
ret, frame = cap.read() #对象cap有一个.read()方法 A
if not ret: #我们可以根据ret做一个判断,if not ret就是没读到数据,就是if true
break #没读到数据就直接退出
cv2.imshow(my video, frame) #如果读到数据就在窗口显示数据
#设置退出
key = cv2.waitKey(10) #这里参数不能填0,因为B
if key == ord(q):
break
cap.release() #释放资源,释放摄像头对象
cv2.destroyAllWindows()
这个程序就是读取摄像头数据,可以自动打开你的摄像头设备,并读取视频数据,这个功能在物联网设备里用的很多,非常实用,现在很多摄像头都自带一些机器视觉的功能,比如拍到人就自动的把人脸框出来,这些功能都是通过opencv来做的。还有比如无人车、机械臂等如果安装了摄像头,我们都可以通过一行代码cap = cv2.VideoCapture()直接就打开它了。
A:这个方法可以帮我们读取一帧数据就返回一个标记和这帧数据本身(就是一幅图像数据),当这个标记是True就是读到了一帧数据,False就表示没读到数据
B:如果填0,while True第一轮循环,如果读了一帧数据,就会一直只显示这一帧数据,一直等键盘响应,键盘不响应就一直卡在这行代码上,这不是我们想要的结果。如果我们把参数设置为1,就是等待1毫秒就往下执行,这样视频就播放的非常快,如果我们设置为1000就是等待1秒再显示下一帧图片,这样视频就非常不流畅,所以我们这里取10这么一个间隔。一般情况下这个参数设置25即可。
C:这里也可以写成:while cap.isOpened():如果摄像头打开失败,cap.isOpened()就返回False, whiel false,就直接不循环了,直接跳过这个循环代码块直接执行cap.release()和cv2.destroyAllWindows()。所以如果读不出数据,窗口就闪退了。
#例19.2 读取磁盘上已有的视频文件
import cv2
cv = cv2.VideoCapture(rC:\Users\25584\Desktop\VID_20190106_114412.mp4) #直接打开已有视频文件
while True:
ret, frame = cv.read()
if not ret:
break
cv2.imshow(my baby, frame)
key = cv2.waitKey(1000//30) #设置不同的参数,视频播放的速度不一样 A
if key == ord(q):
break
cv2.destroyAllWindows()
A:假如我的视频是一秒钟30帧,那么我想正常速度展示这个视频,这个参数应该怎样设置呢,waitKey()里的参数只能设置为整数,所以要一秒显示30帧,就要设置为1000//30,表示整除,向下取整。
3、cv2.VideoCapture类小结
- 构造函数cv2.VideoCapture()是用来打开摄像头并完成摄像头的初始化工作。或者是用来初始化视频文件的。
函数的语法格式:捕获对象 = cv2.VidelCapture(摄像头ID编号) 或者是:捕获对象 = cv2.VidelCapture(文件路径) - cv2.VideoCapture.isOpened()函数
如果我们不知道初始化是否成功,我们可以使用cv2.VideoCapture.isOpened()函数来判断初始化是否成功。
语法格式:ret = cv2.VideoCapture.isOpened(),如果成功,返回值ret为True,反之为false。 - cv2.VideoCapture.open()函数
如果初始化失败,我们还可以使用cv2.VideoCapture.open()函数去打开摄像头或者视频文件。
语法格式:ret = cv2.VideoCapture.open(index or filename),如果能成功打开,返回值ret为True。 - 摄像头成功打开后就可以从摄像头中捕获帧信息了,用函数cv2.VideoCapture.read()
语法格式:ret, image = cv2.VideoCapture.read(),如果捕获成功,ret为true,image为捕获的图像帧,如果捕获不成功,ret为False,image为空。 - 释放资源cv2.VideoCapture.release()函数
不需要摄像头时要关闭摄像头。 - 获取类对象属性
假设一个类对象cv = cv2.VideoCapture()
cv.get(cv2.CAP_PROP_FRAME_WIDTH) #就能获取当前帧对象的宽度,参数的默认值是3
cv.get(cv2.CAP_PROP_FRAME_HEIGHT) #就能获取当前帧对象的高度,参数的默认值是4
语法格式:cv2.VideoCapture.get(propID),参数propID对应着上面类对象cv的属性,当propID=cv2.CAP_PROP_FRAME_WIDTH,或者直接写cv2.VideoCapture.get(3),就可以获得帧的宽度。cv2.VideoCapture.get(4)就可以获得帧的高度。propID可以从0取到40,就是有41种属性,以后用到什么属性去百度查找即可。 - 设置类对象属性cv2.VideoCapture.set(propID,value)
ret = cv.set(cv2.CAP_PROP_FRAME_WIDTH, 640) #将当前帧对象的宽度设置为640个像素
ret = cv.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) #将当前帧对象的高度设置为480个像素
4、cv2.VideoCapture.grab()函数和cv2.VideoCapture.retrieve()函数
如果只需要读取一个摄像头的视频数据,用函数cv2.VideoCapture.read()就可以了。但是如果要同步一组或一个多头摄像头(multihead,比如立体摄像头或Kinect)的视频数据时,就不能用cv2.VideoCapture.read()了,就需要用cv2.VideoCapture.grab()函数和cv2.VideoCapture.retrieve()函数 - 函数cv2.VideoCapture.grab()用来指向下一帧,语法格式:ret = cv2.VideoCapture.grab(),如果该函数成功指向下一帧,则返回True。
- 函数cv2.VideoCapture.retrieve()用来解码并返回cv2.VideoCapture.grab()捕获的视频帧。其语法格式是:ret, image = cv2.VideoCapture.retrieve(),
如果解码失败,返回值ret=false,image为一个空图像, 否则,ret=true, image为解码成功的视频帧。
二、保存视频
上面的操作是读取视频文件或者打开摄像头读取实时视频流,那读出的视频如何保存?opencv中的cv2.VideoWriter类可以帮我们将图片序列保存成视频文件,也可以修改视频的各种属性,还可以对视频类型进行转换。 - <VideoWriter object> = cv2.VideoWriter(filename, fourcc, fps, frameSize[, isColor])
filename:指定输出目标视频的存放路径和文件名。如果指定的文件名已经存在,则会覆盖这个文件。
fourcc:视频编/解码类型(格式)。这个参数用cv2.VideoWriter_fourcc()来指定视频编解码格式。通俗的说就是进行解包操作。
cv2.VideoWriter_fourcc(I,4,2,0)表示未压缩的YUV颜色编码格式。生成的文件扩展名为.avi格式。
cv2.VideoWriter_fourcc(P,I,M,I)表示MPEG-1编码类型。生成的文件扩展名为.avi格式。
cv2.VideoWriter_fourcc(*mp4v),这种是简洁的写法,表示把mp4v格式的视频数据解包成m、p、4、v,就是相当于把原来的视频数组分解成m、p、4、v
其他更多的的参数自己上网查询。
如果参数fourcc=-1,则呈现运行时会弹出一个对话框,在对话框中用户可以根据自己的需要选择合适的压缩程序和压缩质量。
fps:帧速率。表示一秒钟保存20张图片。这个参数可以设置20,30都没问题。
frameSize:每一帧的长和宽。也就是摄像头的分辨率。这个参数非常关键,如果写错就保存不成功。
isColor:表示是否为彩色图像。
#例19.3 使用cv2.VideoWriter类保存摄像头视频文件
import cv2
cap = cv2.VideoCapture(0) #打开摄像头
vw = cv2.VideoWriter(rC:\Users\25584\Desktop\myvideo.mp4, cv2.VideoWriter_fourcc(*mp4v), 60, (640,480)) #实例化一个读取并保存视频的类 A
while cap.isOpened(): #用循环去接受摄像头的每一帧
ret, frame = cap.read() #读取每一帧
if not ret: #如果没接受到就直接退出
print(can not get the video, exit...)
break
vw.write(frame) #写入每一帧视频数据,但是要注意这里没有写到磁盘,入缓存里面去了
cv2.imshow(my video, frame) #写的每一帧数据先展示一下
if cv2.waitKey(1) == ord(q):
break
cap.release()
vw.release() #把缓存里面的数据写入磁盘,释放缓存。
cv2.destroyAllWindows()
A:参数fourcc用的解码是mp4v,那么第一个参数filename就也要用.mp4格式的。
视频格式除了mp4格式外还有avi等格式,avi格式的文件比较大一些,早期的视频文件都是avi格式,后来才出现mp4技术,mp4技术的压缩比高一些,视频文件就比较小一些。
如果是avi格式的视频,这里的参数fourcc=cv2.VideoWriter_fourcc(*XVID)。如果fourcc是avi格式,参数filename就也得是avi格式,就是也要保存为avi格式。
第4个参数,分辨率的设置,你可以先打开你的笔记本摄像头-设置-图片质量,可以看到你的摄像头是1280x720,但其实是640x480的。
#例19.4 提取视频的Canny边缘检测结果
import cv2
cap = cv2.VideoCapture(rC:\Users\25584\Desktop\myvideo.mp4)
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.Canny(frame, 100, 200)
cv2.imshow(canny, frame)
key = cv2.waitKey(25)
if key == 27 : #ESC键
break
cap.release()
vw.release()
cv2.destroyAllWindows()
说明:第二十章是案例,我就不打算上传了。