使⽤OpenCV进⾏⽬标跟踪(C++Python)
rectangle函数opencv
译⾃:,有删改。
本教程中,我们将学习OpenCV 3中新引⼊的⼀些⽬标跟踪API,包括BOOSTING, MIL, KCF, TLD, MEDIANFLOW和GOTURN,此外还将介绍现代跟踪算法中的⼀般理论。
什么是⽬标跟踪?
简单来说,在视频的连续帧中定位⽬标即为⽬标跟踪。
该定义听起来很直接,在计算机视觉和机器学习中,跟踪是⼀个⾮常宽泛的概念,涵盖各种概念上相似但技术实现上不同的理论。例如,以下理论均会⽤于⽬标跟踪的研究:
1. 密集光流:⽤于估计视频帧中每个像素的运动⽮量。
2. 稀疏光流:如Kanade-Lucas-Tomashi(KLT)特征跟踪器,⽤于跟踪图像中⼏个特征点的位置。
3. 卡尔曼滤波:⼀种⾮常受欢迎的信号处理算法,⽤于根据先前的运动信息预测移动物体的位置,该算法的早期应⽤之⼀是导弹制导,
指导11号登⽉舱降落到⽉球的机载计算机也搭载了卡尔曼滤波器。
4. Meanshift和Camshift:⽤于定位密度函数的最⼤值,通常也⽤于跟踪。
5. 单⽬标跟踪器:在这类跟踪器中,使⽤矩形标记我们要跟踪的对象在帧中的位置,然后使⽤跟踪算法在随后的帧中跟踪对象。在⼤多
数实际应⽤中,跟踪器与检测器是⼀起使⽤的。
6. 多⽬标追踪算法:使⽤快速⽬标检测器检测每个帧中的多个物体,然后使⽤跟踪算法识别⼀个帧中的哪个矩形与下⼀个帧中的矩形相
对应。
⽬标跟踪与⽬标检测
如果你玩过OpenCV⼈脸检测,就会发现它可以实时轻松检测到⼈脸。我们来探讨⼀下,为什么我们需要跟踪⽽不是重复不断地进⾏⽬标检测。
跟踪⽐检测快:通常跟踪算法⽐检测算法快。原因很简单,在跟踪前⼀帧中检测到的对象时,会知晓很多关于对象外观的信息,并且还1、跟踪⽐检测快
知道前⼀帧中的位置以及其运动的⽅向和速度。因此,在下⼀帧中,可以使⽤所有这些信息预测下⼀帧中对象的位置,并在对象的预期位置周围进⾏⼩范围搜索以精确定位对象。⼀个好的跟踪算法能够使⽤跟踪⽬标的所有历史信息,⽽检测算法对每⼀帧总是需要从头开始。因此,在设计⾼效检测系统时,通常在每n帧运⾏⼀次⽬标检测,⽽跟踪算法应⽤于其余n-1帧。为什么不简单地检测第⼀帧中的对象,随后进⾏跟踪呢?跟踪算法确实得益于其拥有的额外信息,但如果被跟踪物体长时间受到障碍物遮挡,或者物体移动太快以⾄跟踪算法⽆法跟上时,就可能会丢失跟踪对象的信息。跟踪算法的累积误差也会使被跟踪物体的边界框逐渐偏离正在跟踪的物体。为了解决跟踪算法中这些问题,就需要每隔⼀段时间运⾏⼀次检测算法。检测算法使⽤⼤量对象实例进⾏训练,因此,他们对该对象⼀般情况拥有更多的了解,⽽跟踪算法通常只了解跟踪对象的特定实例。
当检测失败时,跟踪可以提供帮助:如果运⾏⼈脸检测器时⼈脸受到障碍物遮挡,那么⼈脸检测器很可能会失效,⽽⼀个好的跟踪算2、当检测失败时,跟踪可以提供帮助
法,能够处理⼀定程度的物体遮挡。在下⾯的视频中(YouTube视频⾃⾏脑补),MIL算法的作者Boris Babenko博⼠演⽰MIL跟踪器如何在⽬标受到遮挡情况下进⾏⼯作。
跟踪保留标识:⽬标检测的输出是包含对象的矩形数组,但是并没有为对象附加标识。例如,在下⾯的视频中(⾃⾏脑补,⽩⾊底⽚上3、跟踪保留标识
⼀运动的红点),红点检测器将输出帧中检测到的所有点对应的矩形,⽽下⼀帧⼜会输出另⼀个矩形阵列。在第⼀帧中,⼀个特定的点可能由阵列中位置10处的矩形表⽰,⽽在第⼆帧中,它可能位于位置17处。进⾏⽬标检测时,我们并不知道哪个矩形对应哪个对象,⽽跟踪则可以提供各个点的运动轨迹。
OpenCV 3 ⽬标跟踪API
OpenCV 3引⼊了新的跟踪API,其中包含了许多单⽬标跟踪算法的实现。在OpenCV 3.2中有6种不同的跟踪器 –
BOOSTING,MIL,KCF,TLD,MEDIANFLOW和GOTURN。
注:OpenCV 3.1实现了5种跟踪器 – BOOSTING,MIL,KCF,TLD,MEDIANFLOW。 OpenCV 3.0实现了4种跟踪器 –BOOSTING,MIL,TLD,MEDIANFLOW。
注:由于OpenCV 3.3中,跟踪API发⽣变化,因此代码将检查OpenCV版本,然后使⽤相应的API。
在我们提供算法的简要说明之前,我们先来看看代码的设置和使⽤。在下⾯的代码注释中,我们⾸先通过选择跟踪器类型 –BOOSTING,MIL,KCF,TLD,MEDIANFLOW或GOTURN来设置跟踪器。然后,打开⼀个视频,并抓取第⼀帧,为第⼀帧定义⼀个包含对象的边界框,并⽤第⼀帧和边界框初
始化跟踪器。最后,我们从视频中读取帧,并在循环中更新跟踪器以获取当前帧的新边界框,随后显⽰跟踪结果。
C++
#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/core/ocl.hpp>
using namespace cv;
using namespace std;
// Convert to string
#define SSTR( x ) static_cast< std::ostringstream & >( \
( std::ostringstream() << std::dec << x ) ).str()
int main(int argc, char **argv)
{
// List of tracker types in OpenCV 3.2
// NOTE : GOTURN implementation is buggy and does not work.
string trackerTypes[6] = {"BOOSTING", "MIL", "KCF", "TLD","MEDIANFLOW", "GOTURN"};
// vector <string> trackerTypes(types, std::end(types));
// Create a tracker
string trackerType = trackerTypes[2];
Ptr<Tracker> tracker;
#if (CV_MINOR_VERSION < 3)
{
tracker = Tracker::create(trackerType);
}
#else
{
if (trackerType == "BOOSTING")
tracker = TrackerBoosting::create();
if (trackerType == "MIL")
tracker = TrackerMIL::create();
if (trackerType == "KCF")
tracker = TrackerKCF::create();
if (trackerType == "TLD")
tracker = TrackerTLD::create();
if (trackerType == "MEDIANFLOW")
tracker = TrackerMedianFlow::create();
if (trackerType == "GOTURN")
tracker = TrackerGOTURN::create();
}
#endif
// Read video
VideoCapture video("videos/chaplin.mp4");
// Exit if video is not opened
if(!video.isOpened())
{
cout << "Could not read video file" << endl;
cout << "Could not read video file" << endl;
return1;
}
// Read first frame
Mat frame;
bool ok = ad(frame);
// Define initial boundibg box
Rect2d bbox(287, 23, 86, 320);
// Uncomment the line below to select a different bounding box
bbox = selectROI(frame, false);
/
/ Display bounding box.
rectangle(frame, bbox, Scalar( 255, 0, 0 ), 2, 1 );
imshow("Tracking", frame);
tracker->init(frame, bbox);
ad(frame))
{
// Start timer
double timer = (double)getTickCount();
// Update the tracking result
bool ok = tracker->update(frame, bbox);
// Calculate Frames per second (FPS)
float fps = getTickFrequency() / ((double)getTickCount() - timer);
if (ok)
{
// Tracking success : Draw the tracked object
rectangle(frame, bbox, Scalar( 255, 0, 0 ), 2, 1 );
}
else
{
// Tracking failure detected.
putText(frame, "Tracking failure detected", Point(100,80), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,255),2);        }
// Display tracker type on frame
putText(frame, trackerType + " Tracker", Point(100,20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(50,170,50),2);
// Display FPS on frame
putText(frame, "FPS : " + SSTR(int(fps)), Point(100,50), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(50,170,50), 2);
// Display frame.
imshow("Tracking", frame);
// Exit if ESC pressed.
int k = waitKey(1);
if(k == 27)
{
break;
}
}
}
Python
import cv2
import sys
(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
if __name__ == '__main__' :
# Set up tracker.
# Instead of MIL, you can also use
tracker_types = ['BOOSTING', 'MIL','KCF', 'TLD', 'MEDIANFLOW', 'GOTURN']    tracker_type = tracker_types[2]
if int(minor_ver) < 3:
tracker = cv2.Tracker_create(tracker_type)
else:
if tracker_type == 'BOOSTING':
tracker = cv2.TrackerBoosting_create()
if tracker_type == 'MIL':
tracker = cv2.TrackerMIL_create()
if tracker_type == 'KCF':
tracker = cv2.TrackerKCF_create()
if tracker_type == 'TLD':
tracker = cv2.TrackerTLD_create()
if tracker_type == 'MEDIANFLOW':
tracker = cv2.TrackerMedianFlow_create()
if tracker_type == 'GOTURN':
tracker = cv2.TrackerGOTURN_create()
# Read video
video = cv2.VideoCapture("videos/chaplin.mp4")
# Exit if video not opened.
if not video.isOpened():
print"Could not open video"
# Read first frame.
ok, frame = ad()
if not ok:
print'Cannot read video file'
# Define an initial bounding box
bbox = (287, 23, 86, 320)
# Uncomment the line below to select a different bounding box
bbox = cv2.selectROI(frame, False)
# Initialize tracker with first frame and bounding box
ok = tracker.init(frame, bbox)
while True:
# Read a new frame
ok, frame = ad()
if not ok:
break
# Start timer
timer = TickCount()
# Update tracker
ok, bbox = tracker.update(frame)
# Calculate Frames per second (FPS)
fps = TickFrequency() / (TickCount() - timer);
# Draw bounding box
if ok:
# Tracking success
p1 = (int(bbox[0]), int(bbox[1]))
p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
else :
# Tracking failure
cv2.putText(frame, "Tracking failure detected", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2)
# Display tracker type on frame
cv2.putText(frame, tracker_type + " Tracker", (100,20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50),2);
# Display FPS on frame
cv2.putText(frame, "FPS : " + str(int(fps)), (100,50), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50), 2);
# Display result
cv2.imshow("Tracking", frame)
# Exit if ESC pressed
k = cv2.waitKey(1) & 0xff
if k == 27 : break
⽬标跟踪算法
在本节中,我们将深⼊了解⼀些不同的跟踪算法。⽬标不是要对每个跟踪器有深⼊的理论理解,⽽是要从实际应⽤的⾓度理解它们。
⾸先解释⼀下追踪背后的⼀些⼀般原则。⽬标跟踪的⽬标是在当前帧中到⼀个对象,前提是我们已
经在所有(或⼏乎所有)前⼀帧中成功跟踪了对象。
由于我们已经完成对象追踪直到当前帧,所以我们会知道⽬标是如何移动的。换句话说,我们知道运动模型的参数。运动模型只是⼀种抽象的说法,即知晓前⼀帧中物体的位置和速度(速度+运动⽅向)。如果对象的其他信息位置,则可以基于当前的运动模型预测⽬标在下⼀帧中的位置,且该位置可能⽆限接近⽬标真实位置。
但实际上除了⽬标的运动信息外,我们还知道每个先前帧中⽬标的外观。换句话说,我们可以构建⼀个外观模型来编码对象的外观。该外观模型可⽤于搜索运动模型预测的位置的较⼩邻域,以更准确地预测对象的位置。
运动模型预测对象的⼤致位置,外观模型则可以调整该预测,以提供基于外观的更精确的估计。
如果对象⾮常简单并且外观⽆变化,我们可以使⽤⼀个简单的模板作为外观模型并查该模板。但是,实际应⽤中并⾮如此,对象的外观可能会发⽣显著变化。为了解决这个问题,在许多现代跟踪器中,外观模型是⼀种以在线学习⽅式进⾏训练的分类器。不懂没关系,让我们简单地解释⼀下。
分类器的⼯作是将图像的矩形区域分类为⽬标或背景。分类器将图像块作为输⼊并返回介于0和1之间的分数,以指⽰图像块包含⽬标的概率。如果绝对确定图像块是背景,返回为0,相反,如果确定图像块是⽬标时,返回为1。
在机器学习中,我们使⽤“在线”⼀词来指代在运⾏时即时训练的算法。离线分类器可能需要数千个⽰例来训练分类器,但在线分类器通常在运⾏时使⽤少数⽰例进⾏训练即可。
分类器通过正⾯(⽬标)和负⾯(背景)样本进⾏训练,例如如果你想建⽴⼀个检测猫的分类器,你可以使⽤成千上万个不包含猫和包含猫的图像进⾏训练。这样分类器就会学会区分什么是猫,什么不是。你可以在此了解更多图像分类的知识()。构建在线分类器则⽆需拥有如此⼤量的正负样本。
让我们看看不同的跟踪算法是如何解决这个在线学习这个问题的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。