opencv--视频操作
Posted 为算法工程师
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv--视频操作相关的知识,希望对你有一定的参考价值。
opencv--视频操作
1 视频读写
1.1 从文件中读取视频并播放
在OpenCV中我们要获取一个视频,需要创建一个VideoCapture对象,指定你要读取的视频文件:
- 创建读取视频的对象
cap = cv.VideoCapture(filepath)
参数:
- filepath: 视频文件路径
- 视频的属性信息
获取视频的某些属性,retval = cap.get(propId)
参数:- propId: 从0到18的数字,每个数字表示视频的属性。常用属性有:
- 修改视频的属性信息,
cap.set(propId,value)
参数:- proid: 属性的索引,与上面的表格相对应
- value: 修改后的属性值
- propId: 从0到18的数字,每个数字表示视频的属性。常用属性有:
- 判断图像是否读取成功,
isornot = cap.isOpened()
若读取成功则返回true,否则返回False - 获取视频的一帧图像,
ret, frame = cap.read()
参数:- ret: 若获取成功返回True,获取失败,返回False
- Frame: 获取到的某一帧的图像
- 调用
cv.imshow()
显示图像,在显示图像时使用cv.waitkey()
设置适当的持续时间,如果太低视频会播放的非常快,如果太高就会播放的非常慢,通常情况下我们设置25ms就可以了。 - 最后,调用
cap.realease()
将视频释放掉
import numpy as np
import cv2 as cv
# 1.获取视频对象
cap = cv.VideoCapture('DOG.wmv')
# 2.判断是否读取成功
while(cap.isOpened()):
# 3.获取每一帧图像
ret, frame = cap.read()
# 4. 获取成功显示图像
if ret == True:
cv.imshow('frame',frame)
# 5.每一帧间隔为25ms
if cv.waitKey(25) & 0xFF == ord('q'):
break
# 6.释放视频对象
cap.release()
cv.destoryAllwindows()
1.2 保存视频
在OpenCV中我们保存视频使用的是VedioWriter对象,在其中指定输出文件的名称,如下所示:
-
创建视频写入的对象,
out = cv2.VideoWriter(filename,fourcc, fps, frameSize)
参数:- filename:视频保存的位置
- fourcc:指定视频编解码器的4字节代码
- fps:帧率
- frameSize:帧大小
-
设置视频的编解码器,
retval = cv2.VideoWriter_fourcc( c1, c2, c3, c4 )
参数:- c1,c2,c3,c4: 是视频编解码器的4字节代码,在fourcc.org中找到可用代码列表,与平台紧密相关,常用的有:在Windows中:DIVX(.avi)
在OS中:MJPG(.mp4),DIVX(.avi),X264(.mkv)。
- c1,c2,c3,c4: 是视频编解码器的4字节代码,在fourcc.org中找到可用代码列表,与平台紧密相关,常用的有:在Windows中:DIVX(.avi)
-
利用
cap.read()
获取视频中的每一帧图像,并使用out.write()
将某一帧图像写入视频中。 -
使用
cap.release()
和out.release()
释放资源。
import cv2 as cv
import numpy as np
# 1. 读取视频
cap = cv.VideoCapture("DOG.wmv")
# 2. 获取图像的属性(宽和高,),并将其转换为整数
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
# 3. 创建保存视频的对象,设置编码格式,帧率,图像的宽高等
out = cv.VideoWriter('outpy.avi',cv.VideoWriter_fourcc('M','J','P','G'), 10, (frame_width,frame_height))
while(True):
# 4.获取视频中的每一帧图像
ret, frame = cap.read()
if ret == True:
# 5.将每一帧图像写入到输出文件中
out.write(frame)
else:
break
# 6.释放资源
cap.release()
out.release()
cv.destroyAllWindows()
2 视频追踪
2.1 meanshift
2.1.1 meanshift原理
meanshift算法的原理很简单。假设你有一堆点集,还有一个小的窗口,这个窗口可能是圆形的,现在你可能要移动这个窗口到点集密度最大的区域当中。
最开始的窗口是蓝色圆环的区域,命名为C1。蓝色圆环的圆心用一个蓝色的矩形标注,命名为C1_o。
而窗口中所有点的点集构成的质心在蓝色圆形点C1_r处,显然圆环的形心和质心并不重合。所以,移动蓝色的窗口,使得形心与之前得到的质心重合。在新移动后的圆环的区域当中再次寻找圆环当中所包围点集的质心,然后再次移动,通常情况下,形心和质心是不重合的。不断执行上面的移动过程,直到形心和质心大致重合结束。 这样,最后圆形的窗口会落到像素分布最大的地方,也就是图中的绿色圈,命名为C2。
meanshift算法除了应用在视频追踪当中,在聚类,平滑等等各种涉及到数据以及非监督学习的场合当中均有重要应用,是一个应用广泛的算法。
图像是一个矩阵信息,如何在一个视频当中使用meanshift算法来追踪一个运动的物体呢? 大致流程如下:
- 首先在图像上选定一个目标区域
- 计算选定区域的直方图分布,一般是HSV色彩空间的直方图。
- 对下一帧图像b同样计算直方图分布。
- 计算图像b当中与选定区域直方图分布最为相似的区域,使用meanshift算法将选定区域沿着最为相似的部分进行移动,直到找到最相似的区域,便完成了在图像b中的目标追踪。
- 重复3到4的过程,就完成整个视频目标追踪。
通常情况下我们使用直方图反向投影得到的图像和第一帧目标对象的起始位置,当目标对象的移动会反映到直方图反向投影图中,meanshift 算法就把我们的窗口移动到反向投影图像中灰度密度最大的区域了。如下图所示:
直方图反向投影的流程是:
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
- 从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
- 生成临时图像的直方图;
- 用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
- 直方图对比结果c,就是结果图像(0,0)处的像素值;
- 切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
- 重复1~5步直到输入图像的右下角,就形成了直方图的反向投影。
2.1.1 meanshift实现
在OpenCV中实现Meanshift的API是:cv.meanShift(probImage, window, criteria)
参数:
- probImage: ROI区域,即目标的直方图的反向投影
- window: 初始搜索窗口,就是定义ROI的rect
- criteria: 确定窗口搜索停止的准则,主要有迭代次数达到设置的最大值,窗口中心的漂移值大于某个设定的限值等。
实现Meanshift的主要流程是:
- 读取视频文件:
cv.videoCapture()
- 感兴趣区域设置:获取第一帧图像,并设置目标区域,即感兴趣区域
- 计算直方图:计算感兴趣区域的HSV直方图,并进行归一化
- 目标追踪:设置窗口搜索停止条件,直方图反向投影,进行目标追踪,并在目标位置绘制矩形框。
import numpy as np
import cv2 as cv
# 1.获取图像
cap = cv.VideoCapture('DOG.wmv')
# 2.获取第一帧图像,并指定目标位置
ret,frame = cap.read()
# 2.1 目标位置(行,高,列,宽)
r,h,c,w = 197,141,0,208
track_window = (c,r,w,h)
# 2.2 指定目标的感兴趣区域
roi = frame[r:r+h, c:c+w]
# 3. 计算直方图
# 3.1 转换色彩空间(HSV)
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# 3.2 去除低亮度的值
# mask = cv.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
# 3.3 计算直方图
roi_hist = cv.calcHist([hsv_roi],[0],None,[180],[0,180])
# 3.4 归一化
cv.normalize(roi_hist,roi_hist,0,255,cv.NORM_MINMAX)
# 4. 目标追踪
# 4.1 设置窗口搜索终止条件:最大迭代次数,窗口中心漂移最小值
term_crit = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )
while(True):
# 4.2 获取每一帧图像
ret ,frame = cap.read()
if ret == True:
# 4.3 计算直方图的反向投影
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# 4.4 进行meanshift追踪
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# 4.5 将追踪的位置绘制在视频上,并进行显示
x,y,w,h = track_window
img2 = cv.rectangle(frame, (x,y), (x+w,y+h), 255,2)
cv.imshow('frame',img2)
if cv.waitKey(60) & 0xFF == ord('q'):
break
else:
break
# 5. 资源释放
cap.release()
cv.destroyAllWindows()
2.2 Camshift
上面的结果,有一个问题,就是检测的窗口的大小是固定的,而狗狗由近及远是一个逐渐变小的过程,固定的窗口是不合适的。所以我们需要根据目标的大小和角度来对窗口的大小和角度进行修正。CamShift可以帮我们解决这个问题。
CamShift算法全称是“Continuously Adaptive Mean-Shift”(连续自适应MeanShift算法),是对MeanShift算法的改进算法,可随着跟踪目标的大小变化实时调整搜索窗口的大小,具有较好的跟踪效果。
Camshift算法首先应用meanshift,一旦meanshift收敛,它就会更新窗口的大小,还计算最佳拟合椭圆的方向,从而根据目标的位置和大小更新搜索窗口。如下图所示:
Camshift在OpenCV中实现时,只需将上述的meanshift函数改为Camshift函数即可:
import numpy as np
import cv2 as cv
# 1.获取图像
cap = cv.VideoCapture('DOG.wmv')
# 2.获取第一帧图像,并指定目标位置
ret,frame = cap.read()
# 2.1 目标位置(行,高,列,宽)
r,h,c,w = 197,141,0,208
track_window = (c,r,w,h)
# 2.2 指定目标的感兴趣区域
roi = frame[r:r+h, c:c+w]
# 3. 计算直方图
# 3.1 转换色彩空间(HSV)
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# 3.2 去除低亮度的值
# mask = cv.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
# 3.3 计算直方图
roi_hist = cv.calcHist([hsv_roi],[0],None,[180],[0,180])
# 3.4 归一化
cv.normalize(roi_hist,roi_hist,0,255,cv.NORM_MINMAX)
# 4. 目标追踪
# 4.1 设置窗口搜索终止条件:最大迭代次数,窗口中心漂移最小值
term_crit = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )
while(True):
# 4.2 获取每一帧图像
ret ,frame = cap.read()
if ret == True:
# 4.3 计算直方图的反向投影
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
#进行camshift追踪
ret, track_window = cv.CamShift(dst, track_window, term_crit)
# 绘制追踪结果
pts = cv.boxPoints(ret)
pts = np.int0(pts)
img2 = cv.polylines(frame,[pts],True, 255,2)
cv.imshow('frame',img2)
if cv.waitKey(60) & 0xFF == ord('q'):
break
else:
break
# 5. 资源释放
cap.release()
cv.destroyAllWindows()
2.3算法总结
Meanshift和camshift算法都各有优势,自然也有劣势:
- Meanshift算法:简单,迭代次数少,但无法解决目标的遮挡问题并且不能适应运动目标的的形状和大小变化。
- camshift算法:可适应运动目标的大小形状的改变,具有较好的跟踪效果,但当背景色和目标颜色接近时,容易使目标的区域变大,最终有可能导致目标跟踪丢失。
opencv18视频操作
目录
- 一、视频基础
- 二、摄像头初始化 / 视频文件初始化
- 三、检查摄像头/视频文件、打开摄像头/视频文件
- 四、播放视频文件(捕获帧)
- 五、关闭摄像头 / 释放摄像头
- 六、获取属性、设置属性
- 七、保存视频
- 八、视频基础操作
例子中用到的视频:点击查看
一、视频基础
1、视频:视频是由一帧一帧的图像组成的。当连续的图像变化每秒超过24帧(frame)画面以上时,根据视觉暂留原理,人眼无法辨别单幅的静态画面;看上去是平滑连续的视觉效果,这样连续的画面叫做视频
2、视频处理:从视频中抽取出独立的帧(图像),使用图像处理的方法处理对其进行处理,以达到处理视频的目的。
3、帧速率:1秒内所出现的帧数,单位为:帧/秒,英文为 FPS (Frames Per Second)
二、摄像头初始化 / 视频文件初始化
cv2.VideoCapture() 函数既可以完成摄像头的初始化,也能用于视频文件的初始化
场景1、使用摄像头
cap = cv2.VideoCapture(摄像头ID号) # 初始化摄像头
参数 “摄像头ID号”:摄像设备(摄像头)的 ID 编号,默认值为 -1,表示随机选取一个摄像头。
- 如果有多个摄像头,则用数字 “0”, “1” 等表示
- 如果只有一个摄像头,既可以使用“0”也可以使用“-1” 作为摄像头ID号
返回值 “cap”:cv2.VideoCapture 类的对象
场景2、使用视频文件
cap = cv2.VideoCapture(“文件名”) # 初始化视频文件
三、检查摄像头/视频文件、打开摄像头/视频文件
一般情况下,使用 cv2.VideoCapture() 函数即可完成摄像头的初始化,有时为了防止初始化发生错误,可以使用 cv2.VideoCapture.isOpened() 函数来检查初始化是否成功。
return_value = cv2.VideoCapture.isOpened()
返回值 “return_value”:布尔值,若为 Ture,初始化成功; 为False,初始化不成功
如果摄像头初始化失败,可以使用函数 cv2.VideoCapture.open() 打开摄像头,
retval = cv2.VideoCapture.open(index) # index:摄像头ID号
retval = cv2.VideoCapture.open(“filename”) # filename:文件名
四、播放视频文件(捕获帧)
摄像头初始化成功后,就可以从摄像头中捕获帧信息了,然后我们连续的展示捕获到的帧图像,就好像是在连续的播放视频
return_value, image = cv2.VideoCapture.read()
返回值:
return_value:是否捕获成功。捕获成功值为True, 不成功为 False
image:捕获到的帧
五、关闭摄像头 / 释放摄像头
cap.release()
举例
import cv2
cap = cv2.VideoCapture(0) # 使用摄像头
# cap = cv2.VideoCapture('traffic.avi') # 使用视频文件
while cap.isOpened():
ret, frame = cap.read()
cv2.imshow('frame', gray)
if cv2.waitKey(1) == 27: # ESC键
break
cap.release()
cv2.destroyAllWindows()
六、获取属性、设置属性
获取属性
return_value = cv2.VideoCapture.get(propId) # propId 属性参数
设置属性
return_value = cv2.VideoCapture.get(propId) # propId 属性参数
属性枚举值:官方文档点击查看
举例:查看帧的高度与宽度
import cv2
cap = cv2.VideoCapture('traffic.avi') # 使用视频文件
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 获取帧的宽度
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 获取帧的高度
print(width)
print(height)
while cap.isOpened():
ret, frame = cap.read()
cv2.imshow('frame', frame)
if cv2.waitKey(1) == 27:
break
cap.release()
cv2.destroyAllWindows()
举例:检测摄像头的帧率
import cv2
import time
cap = cv2.VideoCapture(0)
t = 0.1
counter = 0
fps = 0
start_time = time.time()
while (True):
ret, frame = cap.read()
# 测帧率
counter += 1
if (time.time() - start_time) > t:
fps = counter / (time.time() - start_time)
fps = str(fps)
counter = 0
start_time = time.time()
cv2.putText(frame, "FPS 0".format(fps), (10, 30), 1, 1.5, (255, 0, 255), 2)
cv2.imshow('frame', frame)
if cv2.waitKey(1) == 27:
break
cap.release()
cv2.destroyAllWindows()
七、保存视频
保存视频的三个步骤: (1)创建对象 (2)写入视频 (3)释放对象
# 1、创建对象
(filename, fourcc, fps, frameSize, isColor)
# 2、写入视频
out.write(frame) # 帧图片要求是BGR格式
# 3、释放对象
out.release()
构造函数 cv2.VideoWriter
用来创建对象 ,实现初始化工作
cv2.VideoWriter(filename, fourcc, fps, frameSize, isColor)
参数:
- filename:视频的存放路径和文件名,如果文件名已经存在,则覆盖这个文件
- fourcc:视频编码/解码类型(格式)
opencv中用函数cv2.VideoWriter_fourcc()来指定视频编码格式,该函数有4个字符参数,这4个字符参数构成了编码器/解码器的4字标记,每个编码器都有一个这样的标记;
|
下面列出几个常用的标记:
(1)cv2.VideoWriter fourcc(‘I’, ‘4’, ‘2’, ‘0’)表示未压缩的 YUV 颜色编码格式,色度子采样为4:2:0。该编码格式具有较好的兼容性,但产生的文件较大,文件扩展名为.avi(2)cv2 VideoWriter fourcc(‘P’, ‘I’, ‘M’, ‘I’)表示 MPEG-1编码类型,生成的文件的扩展名
为.avi(3)cv2.VideoWriter fourcc(‘X’, ‘V’, ‘I’, ‘D’)表示MPEG-4 编码类型。如果希望得到的视频大小为平均值,可以选用这个参数组合。该组合生成的文件的扩展名为.avi
(4)·cv2.VideoWriter fourcc(‘T’, ‘H’, ‘E’, ‘O’)表示OggVorbis编码类型,文件的扩展名为.ogv
(5)cv2.VideoWriter fourcc(‘F’, ‘L’, ‘V’, ‘I’)表示 Flash 视频,生成的文件的扩展名为.fly
- fps:frames per second 帧速率
- frameSize:每一帧的长和宽
- isColor:表示是否为彩色图像, 默认为 True
通常写法举例:
cv2.VideoWriter("output.avi", fourcc=-1, fps=20, frameSize=(1024, 768), isColor=True)
举例
import cv2
cap = cv2.VideoCapture('traffic.avi')
fourcc = cv2.VideoWriter_fourcc(*'XVID')
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
out = cv2.VideoWriter('flip.avi', fourcc, 30, (int(width), int(height)), True)
while cap.isOpened():
ret, frame = cap.read()
if ret==True:
flip_frame = cv2.flip(frame, 0)
out.write(flip_frame)
cv2.imshow("flip_frame", flip_frame)
if cv2.waitKey(1)==27:
break
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
八、视频基础操作
import cv2
cap = cv2.VideoCapture('traffic.avi') # 使用视频文件
while cap.isOpened():
ret, frame = cap.read()
if ret == True:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
result = cv2.flip(gray, 0)
cv2.imshow('result image', result)
if cv2.waitKey(1) == 27:
break
else:
break
cap.release()
cv2.destroyAllWindows()
import cv2
cap = cv2.VideoCapture(0) # 使用摄像头
while cap.isOpened():
ret, frame = cap.read()
img = cv2.Canny(frame, 100, 200)
cv2.imshow('img', img)
if cv2.waitKey(1) == 27:
break
cap.release()
cv2.destroyAllWindows()
以上是关于opencv--视频操作的主要内容,如果未能解决你的问题,请参考以下文章
计算机视觉OpenCV 4高级编程与项目实战(Python版):操作像素