再进行视频读写的情况下,需要创建一个VideoCapture对象
# 获取视频对象
cap = cv.VideoCapture(filepath)
# filepath:视频文件路径
# 释放对象
cap.release()
获取视频的某些属性
retval = cap.get(propId)
# propId是0-18的数字,每个数字标识视频不同属性
常见的propId
索引 | flags | 意义 |
---|---|---|
0 | cv.CAP_PORP_POS_MSEC | 视频当前位置 |
1 | cv.CAP_PORP_POS_FRAMES | 从0开始索引帧位置 |
2 | cv.CAP_PORP_POS_AVI_RATIO | 视频文件的相对位置(0表示开始,1表示结束) |
3 | cv.CAP_PORP_FRAME_WIDTH | 视频流的帧宽度 |
4 | cv.CAP_PORP_FRAME_HEUGHT | 视频流的帧高度 |
5 | cv.CAP_PORP_FPS | 帧率 |
6 | cv.CAP_PORP_FOURCC | 编解码器四字符代码 |
7 | cv.CAP_PORP_FRAME_COUNT | 视频文件的帧 |
修改视频属性
cap.set(propId, value)
# propId:属性的索引
# value:修改后的属性值
判断视频是否读取成功
isornot = cap.isOpened()
# 读取成功返回true,否则返回false
获取视频一帧图像
ret, frame = cap.read()
# ret:获取成功返回true,失败返回false
# frame:获取某一帧的图像,通过cv.imshow()显示
代码测试:(大家可以去录制个视频,改一下路径就可以跑起来了,结果我就不放了!)
import cv2 as cv
# 读取视频文件
cap = cv.VideoCapture('video/1.mp4')
# 判断是否读取成功
while(cap.isOpened()):
# 获取一帧图像
ret, frame = cap.read()
if ret:
cv.imshow('frame', frame)
# 每250ms显示一帧,按q+shift退出
if(cv.waitKey(250) & 0xFF == ord('q')):
break
# 释放资源
cap.release()
cv.destroyAllWindows()
视频写入对象
out = cv.VideoWriter(filename, fourcc, fps, frameSize)
# filename:视频保存位置
# fourcc:指定视频编辑解码器的4字节代码
# fps:帧率
# frameSize:帧大小
out.write() # 将某一帧的图像写入视频
out.release() # 释放资源
设置视频的编解码器
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)
代码测试
import cv2 as cv
# 读取视频文件
cap = cv.VideoCapture('video/1.mp4')
# 获取图像的属性(宽和高),并将其转换成整数
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
# 创建保存视频对象,设置编码格式,帧率,图像的宽高
out = cv.VideoWriter('out.avi', cv.VideoWriter_fourcc('M', 'J', 'P', 'G'), 10, (frame_width, frame_height))
# 判断是否读取成功
while(cap.isOpened()):
# 获取一帧图像
ret, frame = cap.read()
if ret:
out.write(frame)
else:
break
# 释放资源
cap.release()
out.release()
cv.destroyAllWindows()
# 视频读取
cap = cv.VideoCapture('out.avi')
while(cap.isOpened()):
ret, frame = cap.read()
if ret:
cv.imshow('frame' ,frame)
if cv.waitKey(25) & 0xFF==ord("q"):
break
cap.release()
cv.destroyAllWindows()
原理:利用概率密度的梯度爬升来寻找局部最优解。就是在给定的一组数据的密度分布中找到局部极值。下图的(蓝到绿)
利用meanshift进行视频追踪
- 在图像上选定一个目标区域。
- 计算选定区域的直方图分布(一般是HSV色彩空间的直方图)。
- 对下一帧图像b同样计算直方图分布。
- 计算图像b当中与选定区域直方图分布最为相似的区域,使用meanshift算法将选定区域沿着最相似的部分进行移动,直到找到最相似的区域,便完成了在图像b中的目标追踪。
- 重复3-4,直到完成视频目标追踪。
通常情况下我们使用直方图反向投射得到的图像和第一帧目标对象的起始位置,当目标对象的移动会反映到直方图反向投影图中,meanshift算法就把我们的窗口移动到反向投影图像中灰度密度醉倒的区域。
直方图反向投影流程
例:有一张100×100的输入图像,和一张10×10的的模板图像,查找情况如下:
- 从左上角开始,切割一块(0,0)到(10,10)的图像
- 生成临时图像的直方图
- 用临时图像的直方图和模板图像的直方图进行对比,记结果为c
- 直方图比对结果c,就是结果图像(0,0)处的像素值
- 切割输入图像从(0,1)至(10,11),对比直方图,并记入到结果图像
- 重复1-5直到输入图像的右下角,就形成了直方图的反向投影
API
cv.meanShift(probImage, window, criteria)
# probImage :ROI区域,即目标直方图的反向投影
# window:初始化搜索窗口(定义ROI的rect)
# criteria:确定窗口搜索停止的准则,主要迭代次数达到设置的最大值,窗口中心的漂移值大于某个设定的限值等。
实现meanShift的流程
- 读取视频文件:cv.videoCapture()
- 感兴趣区域设置(追踪区域):获得某一帧图像,并设置目标区域,即感兴趣区域
- 计算直方图:计算感兴趣区域的HSV直方图,并进行归一化
- 目标追踪:设置窗口搜索停止条件,直方图反向投影,进行目标追踪,并在目标位置绘制矩形
cv.calcBackProject([img], [通道], 直方图, bin, 组距)
代码测试
import cv2 as cv
# 1、获取视频对象
cap = cv.VideoCapture('video/1.mp4')
# 2、获取第一帧图像,并指定目标位置
ret, frame = cap.read()
# 目标位置(行高列宽)
# 第一帧(需要自己定义),后面帧数的目标是根据直方图反向投影算出来的
r, h, c, w = 197, 141, 0, 208
track_window = (c, r, w, h) # 追踪的窗口
# 指定目标的感兴趣区域(追踪的区域)
roi = frame[r:r+h, c:c+w]
# 3、计算直方图
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# 计算直方图
roi_hist = cv.calcHist([hsv_roi], [0], None, [180], [0, 180])
# 归一化
cv.normalize(roi_hist, roi_hist, 0, 255, cv.NORM_MINMAX)
# 4、目标追踪
# 设置窗口搜索终止条件:最大迭代次数,窗口中心飘逸最小值
term_crit = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1)
while(True):
ret, frame = cap.read()
if ret:
# 将获得的视频帧数进行转换(变成HSV格式)
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 反向投影
dst = cv.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
# meanshift追踪
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# 在视频上绘制追踪位置
x,y,w,h = track_window
img = cv.rectangle(frame, (x,y), (x+w, y+h), 255,2)
cv.imshow('frame', img)
if(cv.waitKey(60)&0xFF==ord('q')):
break
# 释放资源
cap.release()
cv.destroyAllWindows()
一种连续自适应的MeanShift算法,是对MeanShift算法的改进,可随着跟踪目标的大小变化实时调整搜索窗口的大小。
原理:首先应用meanshift算法,一旦meanshift收敛,更新窗口大小,计算最佳拟合椭圆的方向,从而根据目标的位置和大小跟新搜索窗口。
代码和meanshift相似,只要把meanshift略作修改即可
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# 绘制
x,y,w,h = track_window
img = cv.rectangle(frame, (x,y), (x+w, y+h), 255,2)
改成
ret, track_window = cv.Camshift(dst, track_window, term_crit)
# 绘制(获得的是点)
pts = cv.boxPoints(ret)
pts = np.int0(pts)
img = cv.polylines(frame, [pts], Ture, 255, 2)
代码测试
import cv2 as cv
import numpy as np
# 1、获取视频对象
cap = cv.VideoCapture('video/1.mp4')
# 2、获取第一帧图像,并指定目标位置
ret, frame = cap.read()
# 目标位置(行高列宽)
r, h, c, w = 197, 141, 0, 208
track_window = (c, r, w, h)
# 指定目标的感兴趣区域
roi = frame[r:r+h, c:c+w]
# 3、计算直方图
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# 计算直方图
roi_hist = cv.calcHist([hsv_roi], [0], None, [180], [0, 180])
# 归一化
cv.normalize(roi_hist, roi_hist, 0, 255, cv.NORM_MINMAX)
# 4、目标追踪
# 设置窗口搜索终止条件:最大迭代次数,窗口中心飘逸最小值
term_crit = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1)
while(True):
ret, frame = cap.read()
if ret:
# 反向投影
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
# meanshift追踪
ret, track_window = cv.CamShift(dst, track_window, term_crit)
# 在视频上绘制追踪位置
pts = cv.boxPoints(ret)
pts = np.int0(pts)
img = cv.polylines(frame, [pts], True, 255, 2)
cv.imshow('frame', img)
if(cv.waitKey(60)&0xFF==ord('q')):
break
cap.release()
cv.destroyAllWindows()
优点 | 缺点 | |
---|---|---|
meanShift | 简单,迭代次数小 | 无法解决目标遮挡问题,并且不能适应运动目标的形状和大小变化 |
Camshift | 可适应运动目标大小形状的改变,具有较好的跟踪效果 | 背景色和目标颜色接近时,容易使目标的区域变大,最终可能导致目标跟踪丢失 |
经过快一个星期的学习,OpenCV的基本知识学习告捷了,接下来是进行相应的实战演练进行巩固!加油!