查看: 5473|回复: 0

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

[复制链接]
  • TA的每日心情
    开心
    2019-11-4 13:48
  • 签到天数: 14 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2018-10-18 15:00:38 | 显示全部楼层 |阅读模式
    分享到:
    https://v.qq.com/x/page/i0749xrzi37.html

    在本文中,我们将谈到如何用 OpenCV 的多目标追踪API,通过使用 MultiTracker 类来实现。我们将分享用 C++ 和用 Python 实现的代码。

    在我们深入探讨细节之前,请检查下边列出来的之前的关于目标追踪的帖子,理解用 OpenCV 实现的单目标追踪器的基本原理。

    1. 用 OpenCV 实现目标追踪

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

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

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

    然我们来探索追踪很有用的几个原因。

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

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

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

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

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

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

    OpenCV 中的 MultiTracker 类提供了多目标追踪的实施方法。他是一个简单的实施方法因为他独立地处理被追踪的目标,不需要对被追踪对象做任何优化。

    让我们一步步查看代码,学习我们如何用 OpenCV 的多目标追踪 API。

    下载代码

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


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

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

    如果你想用 GOTURN 追踪器,请确保阅读这篇文章并i喜爱在caffe模型。

    在下面的代码中,给出追踪器类别的名字,我们返回追踪器对象。这个追踪器会用于多目标追踪器。

    Python
    1. from __future__ import print_function
    2. import sys
    3. import cv2
    4. from random import randint

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

    6. def createTrackerByName(trackerType):
    7.   # Create a tracker based on tracker name
    8.   if trackerType == trackerTypes[0]:
    9.     tracker = cv2.TrackerBoosting_create()
    10.   elif trackerType == trackerTypes[1]:
    11.     tracker = cv2.TrackerMIL_create()
    12.   elif trackerType == trackerTypes[2]:
    13.     tracker = cv2.TrackerKCF_create()
    14.   elif trackerType == trackerTypes[3]:
    15.     tracker = cv2.TrackerTLD_create()
    16.   elif trackerType == trackerTypes[4]:
    17.     tracker = cv2.TrackerMedianFlow_create()
    18.   elif trackerType == trackerTypes[5]:
    19.     tracker = cv2.TrackerGOTURN_create()
    20.   elif trackerType == trackerTypes[6]:
    21.     tracker = cv2.TrackerMOSSE_create()
    22.   elif trackerType == trackerTypes[7]:
    23.     tracker = cv2.TrackerCSRT_create()
    24.   else:
    25.     tracker = None
    26.     print('Incorrect tracker name')
    27.     print('Available trackers are:')
    28.     for t in trackerTypes:
    29.       print(t)
    30.    
    31.   return tracker
    复制代码


    C++
    注意:除了需要 include opencv2 / opencv.hpp 之外,你还需要 include opencv2 / tracking.hpp 。
    1. #include <opencv2/opencv.hpp>
    2. #include <opencv2/tracking.hpp>

    3. using namespace cv;
    4. using namespace std;

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

    6. // create tracker by name
    7. Ptr<Tracker> createTrackerByName(string trackerType)
    8. {
    9.   Ptr<Tracker> tracker;
    10.   if (trackerType ==  trackerTypes[0])
    11.     tracker = TrackerBoosting::create();
    12.   else if (trackerType == trackerTypes[1])
    13.     tracker = TrackerMIL::create();
    14.   else if (trackerType == trackerTypes[2])
    15.     tracker = TrackerKCF::create();
    16.   else if (trackerType == trackerTypes[3])
    17.     tracker = TrackerTLD::create();
    18.   else if (trackerType == trackerTypes[4])
    19.     tracker = TrackerMedianFlow::create();
    20.   else if (trackerType == trackerTypes[5])
    21.     tracker = TrackerGOTURN::create();
    22.   else if (trackerType == trackerTypes[6])
    23.     tracker = TrackerMOSSE::create();
    24.   else if (trackerType == trackerTypes[7])
    25.     tracker = TrackerCSRT::create();
    26.   else {
    27.     cout << "Incorrect tracker name" << endl;
    28.     cout << "Available trackers are: " << endl;
    29.     for (vector<string>::iterator it = trackerTypes.begin() ; it != trackerTypes.end(); ++it)
    30.       std::cout << " " << *it << endl;
    31.   }
    32.   return tracker;
    33. }
    复制代码


    第二步:读取视频的第一帧
    一个多目标追踪器需要两个输入   

    1. 视频的一帧
    2. 你想要追踪的所有目标的位置(边界框)
    给定这些信息,追踪器会在多有子序列帧中追踪这些特定目标的位置。

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

    Python

    1. # Set video to load
    2.   videoPath = "videos/run.mp4"
    3.   
    4.   # Create a video capture object to read videos
    5.   cap = cv2.VideoCapture(videoPath)

    6.   # Read first frame
    7.   success, frame = cap.read()
    8.   # quit if unable to read the video file
    9.   if not success:
    10.     print('Failed to read video')
    11.     sys.exit(1)
    复制代码


    C++
    1. // set default values for tracking algorithm and video
    2.   string videoPath = "videos/run.mp4";
    3.    
    4.   // Initialize MultiTracker with tracking algo
    5.   vector<Rect> bboxes;

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

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

    15.   // read first frame
    16.   cap >> frame;
    复制代码


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

    接下来,我们需要在第一帧中定位我们想要追踪的物体。位置是一个简单的边界框。
    OpenCV 提供了一个叫做 selectROI 的功能,它可以弹出一个 GUI 来选择边界框(也叫做感兴趣的区域(ROI))。

    在C++版本中,selectROI允许你得到多个边界框,但在 Python 版本中,它会只返回一个边界框。所以,在 Python 版本中,我们需要一个循环来得到多个边界框。

    对于每个目标,我们还会选择随机的颜色来显示边界框。

    下面就是实现代码。

    Python

    1. ## Select boxes
    2. bboxes = []
    3. colors = []

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

    18. print('Selected bounding boxes {}'.format(bboxes))
    复制代码


    C++
    1. // Get bounding boxes for first frame
    2. // selectROI's default behaviour is to draw box starting from the center
    3. // when fromCenter is set to false, you can draw box starting from top left corner
    4. bool showCrosshair = true;
    5. bool fromCenter = false;
    6. cout << "\n==========================================================\n";
    7. cout << "OpenCV says press c to cancel objects selection process" << endl;
    8. cout << "It doesn't work. Press Escape to exit selection process" << endl;
    9. cout << "\n==========================================================\n";
    10. cv::selectROIs("MultiTracker", frame, bboxes, showCrosshair, fromCenter);

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

    14. vector<Scalar> colors;  
    15. getRandomColors(colors, bboxes.size());
    复制代码


    getRandomColors 函数相当简单
    1. // Fill the vector with random colors
    2. void getRandomColors(vector<Scalar>& colors, int numColors)
    3. {
    4.   RNG rng(0);
    5.   for(int i=0; i < numColors; i++)
    6.     colors.push_back(Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255)));
    7. }
    复制代码


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

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

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

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

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

    Python

    1. # Specify the tracker type
    2. trackerType = "CSRT"   

    3. # Create MultiTracker object
    4. multiTracker = cv2.MultiTracker_create()

    5. # Initialize MultiTracker
    6. for bbox in bboxes:
    7.   multiTracker.add(createTrackerByName(trackerType), frame, bbox)
    复制代码


    C++

    1. // Specify the tracker type
    2. string trackerType = "CSRT";
    3. // Create multitracker
    4. Ptr<MultiTracker> multiTracker = cv::MultiTracker::create();

    5. // Initialize multitracker
    6. for(int i=0; i < bboxes.size(); i++)
    7.   multiTracker->add(createTrackerByName(trackerType), frame, Rect2d(bboxes[i]));
    复制代码


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

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

    python

    1. # Process video and track objects
    2. while cap.isOpened():
    3.   success, frame = cap.read()
    4.   if not success:
    5.     break
    6.    
    7.   # get updated location of objects in subsequent frames
    8.   success, boxes = multiTracker.update(frame)

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

    14.   # show frame
    15.   cv2.imshow('MultiTracker', frame)
    16.    

    17.   # quit on ESC button
    18.   if cv2.waitKey(1) & 0xFF == 27:  # Esc pressed
    19.     break
    复制代码


    C++
    1. while(cap.isOpened())
    2. {
    3.   // get frame from the video
    4.   cap >> frame;

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

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

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

    14.   // Show frame
    15.   imshow("MultiTracker", frame);
    16.    
    17.   // quit on x button
    18.   if  (waitKey(1) == 27) break;
    19.    
    20. }
    复制代码


    翻译.燕婕   校对.酱番梨

    原文http://www.gair.link/page/TextTranslation/1047
    2018-10-19_161605.png
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-4-16 15:16 , Processed in 0.135343 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.