10

多目标追踪器:用 OpenCV 实现多目标追踪(C++/Python)

 3 years ago
source link: https://bbs.cvmart.net/articles/71
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

原文 :MultiTracker : Multiple Object Tracking using OpenCV (C++/Python)
链接:https://www.learnopencv.com/multitracker-multiple-object-tracking-using-opencv-c-python/

演示视频:https://v.qq.com/x/page/i0749xrzi37.html

在本文中,我们将谈到如何用 OpenCV 的多目标追踪API,通过使用 MultiTracker 类来实现。我们将分享用 C++ 和用 Python 实现的代码。在我们深入探讨细节之前,请检查下边列出来的之前的关于目标追踪的帖子,理解用 OpenCV 实现的单目标追踪器的基本原理。

  1. 用 OpenCV 实现目标追踪

2.GOTURN: 基于深度学习的目标追踪

为什么我们需要多目标追踪

大多数计算机视觉和机器学习的入门者都学习目标识别。如果你是一个初学者,你可能会思考我们到底为什么需要目标追踪。我们为什么不能只是在每一帧检测目标?我们来探索追踪很有用的几个原因。


首先,当视频的一帧中有多个目标(这里指人)时,追踪帮助建立帧之间的目标同一性。

第二,在一些情况下,目标检测可能失败但是仍有可能追踪目标,因为追踪考虑到目标在前一帧中的位置和外观。

第三,一些追踪算法是非常快的,因为他们做局部搜索而非全局搜索。所以通过每隔 n 帧做目标检测并在中间的帧做目标追踪,我们的系统可以获得很高的帧速。


所以,为什么不在第一次检测后无限期地跟踪这个对象呢?一个追踪算法有时可能会丢掉它所追踪的目标的轨迹。例如,当目标的移动太大,一个追踪算法可能不能保持一直追踪。所以现实中许多应用将检测和追踪一起使用。

在这个教程中,我们将只关注追踪这部分,我们将通过在对象周围放置一个边界框来指定想要跟踪的对象。

多目标追踪:OpenCV 的多目标追踪器

OpenCV 中的 MultiTracker 类提供了多目标追踪的实施方法。他是一个简单的实施方法因为他独立地处理被追踪的目标,不需要在多个被追踪对象之间做任何优化。让我们一步步查看代码,学习我们如何用 OpenCV 的多目标追踪 API。

为了能容易地跟着这个教程学习,请点击下面的按钮,下载代码。代码是免费的!

代码下载链接:https://bigvisionllc.leadpages.net/leadbox/143948b73f72a2%3A173c9390c346dc/5649050225344512/

第一步:创建单目标追踪器

一个多目标追踪器是由一系列简单的单目标追踪器组成的。一开始,我们先定义一个函数,用追踪器类型作为输入并创建一个追踪器对象。OpenCV 有八个不同的追踪器类型:BOOSTING, MTL, KCF, TLD, MEDIANFLOW, GOTURN, MOSSE, CSRT.

如果你想用 GOTURN 追踪器,请确保阅读这篇文章并下载caffe模型。在下面的代码中,给出追踪器类别的名字,我们返回追踪器对象。这个追踪器会用于多目标追踪器。


Python

from __future__ import print_function
import sys
import cv2
from random import randint

trackerTypes = ['BOOSTING', 'MIL', 'KCF','TLD', 'MEDIANFLOW', 'GOTURN', 'MOSSE', 'CSRT']

def createTrackerByName(trackerType):
 # Create a tracker based on tracker name
 if trackerType == trackerTypes[0]:
   tracker = cv2.TrackerBoosting_create()
 elif trackerType == trackerTypes[1]: 
   tracker = cv2.TrackerMIL_create()
 elif trackerType == trackerTypes[2]:
   tracker = cv2.TrackerKCF_create()
 elif trackerType == trackerTypes[3]:
   tracker = cv2.TrackerTLD_create()
 elif trackerType == trackerTypes[4]:
   tracker = cv2.TrackerMedianFlow_create()
 elif trackerType == trackerTypes[5]:
   tracker = cv2.TrackerGOTURN_create()
 elif trackerType == trackerTypes[6]:
   tracker = cv2.TrackerMOSSE_create()
 elif trackerType == trackerTypes[7]:
   tracker = cv2.TrackerCSRT_create()
 else:
   tracker = None
   print('Incorrect tracker name')
   print('Available trackers are:')
   for t in trackerTypes:
     print(t)

 return tracker

注意:除了需要 include opencv2 / opencv.hpp 之外,你还需要 include opencv2 / tracking.hpp 。

#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>

using namespace cv;
using namespace std;

vector<string> trackerTypes = {"BOOSTING", "MIL", "KCF", "TLD", "MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"}; 

// create tracker by name
Ptr<Tracker> createTrackerByName(string trackerType) 
{
 Ptr<Tracker> tracker;
 if (trackerType ==  trackerTypes[0])
   tracker = TrackerBoosting::create();
 else if (trackerType == trackerTypes[1])
   tracker = TrackerMIL::create();
 else if (trackerType == trackerTypes[2])
   tracker = TrackerKCF::create();
 else if (trackerType == trackerTypes[3])
   tracker = TrackerTLD::create();
 else if (trackerType == trackerTypes[4])
   tracker = TrackerMedianFlow::create();
 else if (trackerType == trackerTypes[5])
   tracker = TrackerGOTURN::create();
 else if (trackerType == trackerTypes[6])
   tracker = TrackerMOSSE::create();
 else if (trackerType == trackerTypes[7])
   tracker = TrackerCSRT::create();
 else {
   cout << "Incorrect tracker name" << endl;
   cout << "Available trackers are: " << endl;
   for (vector<string>::iterator it = trackerTypes.begin() ; it != trackerTypes.end(); ++it)
     std::cout << " " << *it << endl;
 }
 return tracker;
}

第二步:读取视频的第一帧

一个多目标追踪器需要两个输入

1.视频的一帧

2.你想要追踪的所有目标的位置(边界框)

给定这些信息,追踪器会在多有子序列帧中追踪这些特定目标的位置。

在下面的代码中,我们先用 VidoeCapture 类加载视频,读取第一帧。这一帧将会用于之后的 MultiTracker 的初始化。


Python

# Set video to load
 videoPath = "videos/run.mp4"

 # Create a video capture object to read videos
 cap = cv2.VideoCapture(videoPath)

 # Read first frame
 success, frame = cap.read()
 # quit if unable to read the video file
 if not success:
   print('Failed to read video')
   sys.exit(1)
// set default values for tracking algorithm and video
 string videoPath = "videos/run.mp4";

 // Initialize MultiTracker with tracking algo
 vector<Rect> bboxes;

 // create a video capture object to read videos
 cv::VideoCapture cap(videoPath);
 Mat frame;

 // quit if unabke to read video file
 if(!cap.isOpened()) 
 {
   cout << "Error opening video file " << videoPath << endl;
   return -1;
 }

 // read first frame
 cap >> frame;

第三步:在第一帧中定位物体

接下来,我们需要在第一帧中定位我们想要追踪的物体。位置是一个简单的边界框。OpenCV 提供了一个叫做 selectROI 的功能,它可以弹出一个 GUI 来选择边界框(也叫做感兴趣的区域(ROI))。在C++版本中,selectROI允许你得到多个边界框,但在 Python 版本中,它会只返回一个边界框。所以,在 Python 版本中,我们需要一个循环来得到多个边界框。

对于每个目标,我们还会选择随机的颜色来显示边界框。下面就是实现代码。


Python

## Select boxes
bboxes = []
colors = [] 

# OpenCV's selectROI function doesn't work for selecting multiple objects in Python
# So we will call this function in a loop till we are done selecting all objects
while True:
 # draw bounding boxes over objects
 # selectROI's default behaviour is to draw box starting from the center
 # when fromCenter is set to false, you can draw box starting from top left corner
 bbox = cv2.selectROI('MultiTracker', frame)
 bboxes.append(bbox)
 colors.append((randint(0, 255), randint(0, 255), randint(0, 255)))
 print("Press q to quit selecting boxes and start tracking")
 print("Press any other key to select next object")
 k = cv2.waitKey(0) & 0xFF
 if (k == 113):  # q is pressed
   break

print('Selected bounding boxes {}'.format(bboxes))
// Get bounding boxes for first frame
// selectROI's default behaviour is to draw box starting from the center
// when fromCenter is set to false, you can draw box starting from top left corner
bool showCrosshair = true;
bool fromCenter = false;
cout << "\n==========================================================\n";
cout << "OpenCV says press c to cancel objects selection process" << endl;
cout << "It doesn't work. Press Escape to exit selection process" << endl;
cout << "\n==========================================================\n";
cv::selectROIs("MultiTracker", frame, bboxes, showCrosshair, fromCenter);

// quit if there are no objects to track
if(bboxes.size() < 1)
 return 0;

vector<Scalar> colors;  
getRandomColors(colors, bboxes.size());

getRandomColors 函数相当简单

// Fill the vector with random colors
void getRandomColors(vector<Scalar>& colors, int numColors)
{
 RNG rng(0);
 for(int i=0; i < numColors; i++)
   colors.push_back(Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255))); 
}

第三步:初始化多目标追踪器

直到目前,我们读到了第一帧并且得到了目标周围的边界框。这些是我们需要初始化多目标追踪器所需的全部信息。


我们首先创建一个 MuliTracker 对象并且增加和单个目标追踪器一样多的边界框。在这个例子中,我们用 CSRT 单目标追踪器,但是你尝试可以通过将 trackerTyper 变量改变为这篇文章一开始提到的8种追踪器中的一种,来尝试其使用他类型的追踪器。CSRT 追踪器不是最快的,但它在我们尝试的许多情况下都能生成最好的结果。


你可以用封装在同一个 MultiTracker 中的不同的追踪器,但是当然,这意义不大。


MultiTracker 类是一个简单的单目标追踪器的封装器。我们从前边的文章种知道,初始化单目标追踪器,我们需要视频第一帧和用来标定我们想要追踪的目标位置的边界框。多目标追踪器将这些信息传递给它内部封装的单目标追踪器。


Python

# Specify the tracker type
trackerType = "CSRT"   

# Create MultiTracker object
multiTracker = cv2.MultiTracker_create()

# Initialize MultiTracker 
for bbox in bboxes:
 multiTracker.add(createTrackerByName(trackerType), frame, bbox)
// Specify the tracker type
string trackerType = "CSRT";
// Create multitracker
PtrmultiTracker = cv::MultiTracker::create();

// Initialize multitracker
for(int i=0; i < bboxes.size(); i++)
 multiTracker->add(createTrackerByName(trackerType), frame, Rect2d(bboxes[i]));

第四步:更新多目标追踪器并展示结果

最后,我们的多目标追踪器已经准备好了,我们可以在新的帧中追踪多个目标。我们用 MultiTracker 类中的 update 的方法来定位新一帧中的目标。每个用来追踪目标的边界框都用不同颜色来画。


python

# Process video and track objects
while cap.isOpened():
 success, frame = cap.read()
 if not success:
   break

 # get updated location of objects in subsequent frames
 success, boxes = multiTracker.update(frame)

 # draw tracked objects
 for i, newbox in enumerate(boxes):
   p1 = (int(newbox[0]), int(newbox[1]))
   p2 = (int(newbox[0] + newbox[2]), int(newbox[1] + newbox[3]))
   cv2.rectangle(frame, p1, p2, colors[i], 2, 1)

 # show frame
 cv2.imshow('MultiTracker', frame)

 # quit on ESC button
 if cv2.waitKey(1) & 0xFF == 27:  # Esc pressed
   break
while(cap.isOpened()) 
{
 // get frame from the video
 cap >> frame;

 // Stop the program if reached end of video
 if (frame.empty()) break;

 //Update the tracking result with new frame
 multiTracker->update(frame);

 // Draw tracked objects
 for(unsigned i=0; igetObjects().size(); i++)
 {
   rectangle(frame, multiTracker->getObjects()[i], colors[i], 2, 1);
 }

 // Show frame
 imshow("MultiTracker", frame);

 // quit on x button
 if  (waitKey(1) == 27) break;

}

来源:AI研习社

微信公众号: 极市平台(ID: extrememart )
每天推送最新CV干货


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK