OpenCV目标跟踪算法的使用大概可以分为以下几个步骤:
创建MultiTracker对象. # trackers = cv2.legacy.MultiTracker_create()
读取视频或摄像头数据. # cap = cv2.VideoCapture('./videos/soccer_02.mp4')
框选ROI区域 # roi = cv2.selectROI('frame', frame, showCrosshair = True)
给MultiTracker对象添加实际的追踪算法. # success, boxes = trackers.update(frame)
对每一帧进行进行目标追踪.
光流估计:(稀疏光流估计算法为Lucas-Kanade算法, 比较经典)
随机颜色:
color = np.random.randint(0, 255, (100, 3))
OpenCV上有八种不同的目标追踪算法:
BOOSTING Tracker:和Haar cascades(AdaBoost)背后所用的机器学习算法相同,但是距其诞生已有十多年了。这一追踪器速度较慢,并且表现不好。(最低支持OpenCV 3.0.0)
MIL Tracker:比上一个追踪器更精确,但是失败率比较高。(最低支持OpenCV 3.0.0)
KCF Tracker:比BOOSTING和MIL都快,但是在有遮挡的情况下表现不佳。(最低支持OpenCV 3.1.0)
CSRT Tracker:比KCF稍精确,但速度不如后者。(最低支持OpenCV 3.4.2)
MedianFlow Tracker:出色的跟踪故障报告。当运动是可预测的并且没有遮挡时,效果非常好,但是对于快速跳动或快速移动的物体,模型会失效。(最低支持OpenCV 3.0.0)
TLD Tracker:在多帧遮挡下效果最好。但是TLD的误报非常多,所以不推荐。(最低支持OpenCV 3.0.0)
MOSSE Tracker:速度真心快,但是不如CSRT和KCF的准确率那么高,如果追求速度选它准没错。(最低支持OpenCV 3.4.1)
GOTURN Tracker:这是OpenCV中唯一一深度学习为基础的目标检测器。它需要额外的模型才能运行。(最低支持OpenCV 3.2.0)
OpenCV目标跟踪算法的使用大概可以分为以下几个步骤:
创建MultiTracker对象.
读取视频或摄像头数据.
框选ROI区域
给MultiTracker对象添加实际的追踪算法.
对每一帧进行进行目标追踪.
下面是一个使用例子:
import cv2
import numpy as np
# 创建对象, multitracker 在4.5后换了地方
# cv2.legacy
# 定义OpenCV的七种追踪算法
OPENCV_OBJECT_TRACKERS = {
'boosting' : cv2.legacy.TrackerBoosting_create,
'csrt' : cv2.legacy.TrackerCSRT_create,
'kcf' : cv2.legacy.TrackerKCF_create,
'mil' : cv2.legacy.TrackerMIL_create,
'tld' : cv2.legacy.TrackerTLD_create,
'medianflow' : cv2.legacy.TrackerMedianFlow_create,
'mosse' :cv2.legacy.TrackerMOSSE_create
}
# 追踪器
trackers = cv2.legacy.MultiTracker_create()
cap = cv2.VideoCapture('./videos/soccer_02.mp4')
while True:
flag, frame = cap.read()
if frame is None:
break
# 变为黑白的
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 目标追踪, # 第一帧追踪时为空,跳过
success, boxes = trackers.update(frame)
# 绘制追踪到的矩形区域
for box in boxes:
# print('读取成功')
# box是float的数据类型
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow('frame', frame)
key = cv2.waitKey(100)
if key == ord('s'):
# 框选ROI区域
roi = cv2.selectROI('frame', frame, showCrosshair = True, fromCenter = False)# 后两个参数的含义,是否包含中间十字和从中心点开始画
# print(roi)
# 创建实际的追踪器
tracker = OPENCV_OBJECT_TRACKERS['boosting']()
trackers.add(tracker, frame, roi)
elif key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
光流,顾名思义,光的流动。比如人眼感受到的夜空中划过的流星。在计算机视觉中,定义图像中对象的移动,这个移动可以是相机移动或者物体移动引起的。具体是指,视频图像的一帧中的代表同一对象(物体)像素点移动到下一帧的移动量,使用二维向量表示.
根据是否选取图像稀疏点进行光流估计,可以将光流估计分为稀疏光流和稠密光流
OpenCV中提供了光流估计的接口,包括稀疏光流估计算法cv2.calcOpticalFlowPyrLK(),和稠密光流估计cv2.calcOpticalFlowFarneback()。其中稀疏光流估计算法为Lucas-Kanade算法,该算法为1981年由Lucas和Kanade两位科学家提出的,最为经典也较容易理解的算法.
为了将光流估计进行建模,Lucas-Kanade做了三个重要的假设:
亮度恒定:同一点随着时间的变化,其亮度不会发生改变。
小运动:随着时间的变化不会引起位置的剧烈变化,只有小运动情况下才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数。
空间一致:一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。所以需要连立n多个方程求解。
cv2.calcOpticalFlowPyrLK(): 参数:
prevImage 前一帧图像
nextImage 当前帧图像
prevPts 待跟踪的特征点向量
winSize 搜索窗口的大小
maxLevel 最大的金字塔层数
返回:
nextPts 输出跟踪特征点向量
status 特征点是否找到,找到的状态为1,未找到的状态为0
import numpy as np
import cv2
cap = cv2.VideoCapture('test.avi')
# 角点检测所需参数
feature_params = dict( maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7)
# lucas kanade参数
lk_params = dict( winSize = (15,15),
maxLevel = 2)
# 随机颜色条
color = np.random.randint(0,255,(100,3))
# 拿到第一帧图像
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 返回所有检测特征点,需要输入图像,角点最大数量(效率),品质因子(特征值越大的越好,来筛选)
# 距离相当于这区间有比这个角点强的,就不要这个弱的了
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
# 创建一个mask
mask = np.zeros_like(old_frame)
while(True):
ret,frame = cap.read()
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 需要传入前一帧和当前图像以及前一帧检测到的角点
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray,
p0, None, **lk_params)
# st=1表示
good_new = p1[st==1]
good_old = p0[st==1]
# 绘制轨迹
for i,(new,old) in enumerate(zip(good_new,good_old)):
a,b = new.ravel()
c,d = old.ravel()
mask = cv2.line(mask, (a,b),(c,d), color[i].tolist(), 2)
frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1)
img = cv2.add(frame,mask)
cv2.imshow('frame',img)
k = cv2.waitKey(150) & 0xff
if k == 27:
break
# 更新
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2)
cv2.destroyAllWindows()
cap.release()