引言
对象跟踪API在OpenCV3.0中被最先引用,现在我们的OpenCV已经发展到4.4版本,产生了许多不同的跟踪器,今天就来学习一下利用OpenCV-python的多对象追踪器。
本文仅提供代码和基本代码思路,关于跟踪技术以后有时间研读一下再更新。
为什么开发对象跟踪技术
我们知道,OpenCV为我们提供了多种多样的对象检测技术,例如最简单的凸多边形检测,角点检测,甚至与深度学习相关的人脸检测,对象识别等等,那么对象跟踪有什么意义呢?他的优缺点如下:
1. 对象跟踪比识别技术要快得多
首先,利用跟踪算法我们在每一帧中都可以利用上一帧跟踪对象的外部特征,以及前几帧跟踪对象的位置,速度去对本一帧的对象进行预测,这可比根据机器学习的训练模型去逐像素检测,或者利用损失函数判断等算法要简单。这使得同样的机能的机器,跟踪算法的运算量通常小于检测。
然而因此跟踪算法也有缺点,例如跟踪对象被其他物体遮盖时,我们容易丢失跟踪目标。其次,跟踪框(后文的boundingbox)的边界常常会逐渐累积误差,使得其精确度慢慢降低。
后续编辑中我会上传精确度损失的实例。
2.跟踪可以在检测失败时发挥作用
检测由于各种原因(防止过拟合/未识别),有时候也会失败。导致人脸识别时我们的面部如果被部分遮挡/角度不正对镜头时会失败,然而只要视频流是连续的,即是面部被部分遮挡疑惑转过角度,一般情况下追踪算法可以一定程度上对跟踪对象的位置不至于丢失,可以和检测合作,同时运行。
3.对象跟踪仅识别矩形
待补充
对象跟踪步骤
1. 创建单个对象跟踪器(Tracker)
多对象跟踪器只是单个对象跟踪器的集合。我们首先定义一个函数,该函数将跟踪器类型作为输入并创建一个跟踪器对象。OpenCV具有8种不同的跟踪器类型:BOOSTING,MIL,KCF,TLD,MEDIANFLOW,GOTURN,MOSSE和CSRT。
我们定义一个跟踪器函数createTrackerByName:
import cv2 as cv
import sys
from random import randint
# A list to restore the names of the trackers
trackerTypes=['BOOSTING', 'MIL', 'KCF','TLD', 'MEDIANFLOW', 'GOTURN', 'MOSSE', 'CSRT']
# The function to create the tracker according to its name
def createTrackerByName(trackerType):
if trackerType == trackerTypes[0]:
tracker=cv.TrackerBoosting_create()
elif trackerType == trackerTypes[1]:
tracker = cv.TrackerMIL_create()
elif trackerType == trackerTypes[2]:
tracker = cv.TrackerKCF_create()
elif trackerType == trackerTypes[3]:
tracker = cv.TrackerTLD_create()
elif trackerType == trackerTypes[4]:
tracker = cv.TrackerMedianFlow_create()
elif trackerType == trackerTypes[5]:
tracker = cv.TrackerGOTURN_create()
elif trackerType == trackerTypes[6]:
tracker = cv.TrackerMOSSE_create()
elif trackerType == trackerTypes[7]:
tracker = cv.TrackerCSRT_create()
else:
tracker = None
print('Incorrect tracker name')
print('Available trackers are:')
for t in trackerTypes:
print(t)
return tracker
通过输入跟踪器名称,返回对应类型的跟踪器。
2. 读取视频的初始帧
多对象跟踪器(MultiTracker)需要两个输入:
- 初始视频帧
- 通过边界框(boundingbox)确定的对象位置
来看代码:
if __name__ == '__main__':
print("The Default tracking algo is CSRT \n"
"Available tracking algos are: \n")
for i in trackerTypes:
print(i)
# The default tracker is CSRT
trackerType = "CSRT"
# Capture frames from the camera
cap=cv.VideoCapture(0)
# Read the current frame
ret,frame=cap.read()
# Detect if the read was successful
if not ret:
print("Failed to capture frames from the camera.")
sys.exit(1)
我们建立了程序入口,并设定默认追踪算法为CSRT算法(注:GOTURN算法需要安装配置Caffe)。从摄像头读取初始帧
3.在初始帧中找到对象
OpenCV提供了一个名为selectROI的功能,该功能会弹出一个GUI以选择边界框(也称为关注区域(ROI))。
注:
这里,在learn-opencv的网站教程learn-opencv的网站教程中指出,对于OpenCV-python,selectROI函数一次只能选定一个boundingbox,因此采用了while循环去多次选取boundingbox。
笔者在实操代码过程中发现OpenCV4.4带有自带函数selectROIs,可以直接多选boundingbox并且将其存入数组中,需要注意的是两个函数返回值的数据类型不同:
- selectROI返回一个4数据的元组
- selectROIs返回矩阵 [a b c d]
这个数据类型差异导致如果直接套用demo,在后面会遇到bug,需要调试一下。
selectROi返回四个值的元组,分别代表boundingbox的左上角的x,y坐标以及宽,高
代码如下(若要参考selectROI的while循环代码,请参考上述链接教程,后续追踪输入稍有差异):
# Create bounding Boxes
bboxes=[]
# Createrandom colors
colors=[]
# According to the learnOpencv, in python only 1 ROI could be selected at one time,
# But OpenCV4.4 has a function calledselectROIs? I'd try it and succeed!
# It returns bboxes : like [[a1 a2 a3 a4],[b1 b2 b3 b4]... ...]
# selectROI returns a tuple(a1, a2, a3, a4)
# These 4 numbers for a ROI is represent x, y of the upper-left point and width, height
bboxes=cv.selectROIs('MultiTrackers',frame)
for i in bboxes:
colors.append((randint(64,255),randint(64,255),randint(64,255)))
# Print the infomations of the selected bboxes
print('Selected bounding boxes {}'.format(bboxes))
我们建立了bboxes列表存储boundingboxes,并用colors列表为每一个bbox配置随机的边框颜色。
4. 初始化多对象追踪器
我们利用cv2.MultiTracker_create()函数创建多对象追踪器。
来看代码:
# Create multiTracker objet
multiTracker=cv.MultiTracker_create()
for bbox in bboxes:
# trackerType has been initialized
# Due to the form of the value returned by selectROIs, we build a tuple as the last input parameter of multiTracker.add
multiTracker.add(createTrackerByName(trackerType),frame,tuple(bbox)
由于MultiTracker.add的位置参数只接收tuple输入,我们进行了强制类型转换。
5. 刷新多对象追踪器并绘出boundingbox
我们使用MultiTracker类的update方法在新框架中定位对象。每个跟踪对象的每个边界框均使用之前随机的不同的颜色绘制。
来看代码:
# Process the video & track objects
while cap.isOpened():
ret,frame=cap.read()
if not ret:
break
# Update new boxes position
ret, boxes= multiTracker.update(frame)
# Definate p1,p2 (upper-left and lower-right of the boxex)
for i,newbox in enumerate(boxes):
p1 = (int(newbox[0]),int(newbox[1]))
p2 = (int(newbox[0] + newbox[2]), int(newbox[1] + newbox[3]))
# Draw the box with a random color
cv.rectangle(frame,p1,p2,colors[i],2,1)
# Display the frame
cv.imshow("MultiTracker", frame)
# Quit when Esc is pressed
if cv.waitKey(1) & 0xFF ==27:
break
比较简单,newbox的4个量的意义前面已经有所说明,用multiTracker.update进行确定即可。
(MultiTracker是一个类,update是该类的一个方法)
后续可能上传运行结果