OpenCV目标跟踪

Opencv作为图像处理开源库包含了Object Tracking目标追踪的一些API,使用Opencv能够方便快捷的编写目标追踪程序。OpenCV实现多种目标跟踪算法,只需要选择对应的跟踪器即可实现目标跟踪。纯应用而已,暂且不谈理论相关的内容。Demo是使用KCF跟踪器对视频中的汽车进行目标跟踪。

OpenCV 的跟踪算法

Opencv4.0目前包含了8种目标追踪算法:

Boosting

基于在线的AdaBoost, 这个分类器需要对对象的正、负例进行训练。用户提供的初始化框来作为对象的正例,并将边界框外的图像块作为背景。没有优点。这个算法已经有10年的历史了,不再推荐使用。

CSRT

判别性相关滤波器。 优点:精确度比KCF稍高。 缺点:速度不如KCF块。

GOTURN

在跟踪器类的所有跟踪算法中,这是唯一基于卷积神经网络(CNN)的算法。也是唯一一个使用离线训练的模型,因此它比其他跟踪器更快。从Opencv文档可以看出该算法对视角变化、光照、变形都具有很好的鲁棒性,但是对于遮挡性能较差。

KCF

这个跟踪器基于前面两个跟踪器中提出的想法。该跟踪器在MIL跟踪器中使用的多个正样本具有较大的重叠区域。 优点:精度和速度都比MIL好,建议在大多数应用程序中使用该算法。 缺点:还是完全遮挡

MedianFlow

经过测试发现这个跟踪器在小范围运动情况下表现最好。即使跟踪失败了还能继续跟踪,这个跟踪器知道什么时候失败。 优点:跟踪失败报告,小范围运动下表现好。缺点:大范围运动,该算法失灵。

MIL

这个跟踪器与上面描述的boost跟踪器类似。最大的区别是,它不是只考虑对象的当前位置作为正类,还考虑当前位置邻域范围的潜在位置作为正类。 优点: 性能很好。它不boosting跟踪器那样,在部分遮挡下依然表现挺佳。但效果不如KCF好。 缺点:跟踪失败没有可靠的报告。完全遮挡的话性能差

MOSSE

如果对速度要求非常高,MOSSE可能是你的更好选择。 优点:速度比CSRT和KCF都要快。 缺点:精度没有CSRT和KCF高。

TLD

TLD stands for Tracking, learning and detection. 如果有一个视频序列,对象隐藏在另一个对象后面,这个跟踪器可能是一个不错的选择。 优点:在多帧的情况下,在遮挡的情况下工作最好。此外,该算法能很好的应对尺度变化。缺点:大量的假判定得其几乎无法使用。

如何选择

当需要更高的目标跟踪精度并可承受较慢的fps吞吐量时,请使用 CSRT

当需要更快的FPS吞吐量,但可以允许对象跟踪精度稍低时,请使用 KCF

当需要纯速度快时使用 MOSSE

KCF跟踪器示例

1#include <iostream>
2#include <string>
3#include <opencv2/opencv.hpp>
4#include <opencv2/tracking.hpp>
5#include <opencv2/core/ocl.hpp>

首先要创建一个跟踪器并实例化,本文仅使用KCF跟踪器作为示例:

1cv::Ptr<cv::TrackerKCF> tracker; // KCF跟踪器
2tracker = cv::TrackerKCF::create();

初始化视频流,并定义显示窗口:

 1// 读取视频数据
 2cv::VideoCapture video("/Users/xxx/Downloads/tracker.mp4");
 3// 读取摄像头数据
 4//cv::VideoCapture video(0);
 5
 6// 创建窗口
 7static const std::string kWinName = "object tracking in OpenCV";
 8cv::namedWindow(kWinName,cv::WINDOW_AUTOSIZE);
 9if(!video.isOpened()){
10  std::cout <<"Could not read video file"<< std::endl;
11  return -1;
12}

为了能够在视频中选取跟踪目标,需要定义一个初始化ROI区域:

1// 画框选中跟踪目标
2cv::Rect2i bbox = cv::selectROI(frame, false);
3cv::rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2, 1);
1// 跟踪器初始化
2tracker->init(frame, bbox);

从视频流循环帧分析:

 1while (video.read(frame)) {
 2  // Start timer 开始计时
 3  double timer = cv::getTickCount();
 4
 5  // Update the tracking result 跟新跟踪器算法
 6  if(tracker->update(frame, bbox)) {
 7    std::cout << "Update the tracking result" << std::endl;
 8  }
 9  // Calculate Frames per second (FPS) 计算FPS
10  float fps = cv::getTickFrequency() / ((double) cv::getTickCount() - timer);
11
12  if (ok) {
13    // Tracking success : Draw the tracked object 如果跟踪到目标画框
14    rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2, 1);
15  } else {
16    // Tracking failure detected. 没有就输出跟踪失败
17    putText(frame, "Tracking failure detected", cv::Point(100, 80),
18            cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2);
19  }
20
21  // Display FPS on frame 表示FPS
22  putText(frame, "FPS : " + std::to_string(int(fps)), cv::Point(100, 50),
23          cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2);
24
25  // Display frame.
26  cv::imshow("OpenCV Tracking", frame);
27  // Exit if ESC pressed.
28  int k = cv::waitKey(1);
29  if (k == 27) {
30    break;
31  }
32}

因为每一帧对于跟踪器都会重新计算跟踪目标的位置。使用update()方法将定位对象的新位置,并返回true和边界box对象,通过确保跟踪器是新的,如果选择了某个对象,我们需要更新该对象的位置。

完整代码如下:

 1#include <iostream>
 2#include <string>
 3#include <opencv2/opencv.hpp>
 4#include <opencv2/tracking.hpp>
 5#include <opencv2/core/ocl.hpp>
 6
 7int main() {
 8    cv::Ptr<cv::TrackerKCF> tracker; // KCF跟踪器
 9    cv::Mat frame; // 保存每帧图像
10
11    tracker = cv::TrackerKCF::create();
12    // 读取视频数据
13    cv::VideoCapture video("/Users/zchanglin/Downloads/tracker.mp4");
14    // 读取摄像头数据
15    //cv::VideoCapture video(0);
16    // 创建窗口
17    static const std::string kWinName = "object tracking in OpenCV";
18    cv::namedWindow(kWinName,cv::WINDOW_AUTOSIZE);
19    if(!video.isOpened()){
20        std::cout <<"Could not read video file"<< std::endl;
21        return -1;
22    }
23    bool ok = video.read(frame);
24    // 画框选中跟踪目标
25    cv::Rect2i bbox = cv::selectROI(frame, false);
26    cv::rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2, 1);
27
28
29    // 跟踪器初始化
30    tracker->init(frame, bbox);
31
32    while (video.read(frame)) {
33        // Start timer 开始计时
34        double timer = cv::getTickCount();
35
36        // Update the tracking result 跟新跟踪器算法
37        if(tracker->update(frame, bbox)) {
38            std::cout << "Update the tracking result" << std::endl;
39        }
40        // Calculate Frames per second (FPS) 计算FPS
41        float fps = cv::getTickFrequency() / ((double) cv::getTickCount() - timer);
42
43        if (ok) {
44            // Tracking success : Draw the tracked object 如果跟踪到目标画框
45            rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2, 1);
46        } else {
47            // Tracking failure detected. 没有就输出跟踪失败
48            putText(frame, "Tracking failure detected", cv::Point(100, 80),
49                    cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2);
50        }
51
52        // Display FPS on frame 表示FPS
53        putText(frame, "FPS : " + std::to_string(int(fps)), cv::Point(100, 50),
54                cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2);
55
56        // Display frame.
57        cv::imshow("OpenCV Tracking", frame);
58        // Exit if ESC pressed.
59        int k = cv::waitKey(1);
60        if (k == 27) {
61            break;
62        }
63    }
64
65    // 释放跟踪器
66    tracker.release();
67    return 0;
68}

下面是CMakeLists.txt

 1cmake_minimum_required(VERSION 3.19)
 2project(TrackerDemo)
 3
 4set(CMAKE_CXX_STANDARD 11)
 5
 6find_package(OpenCV REQUIRED)
 7
 8include_directories(OpenCV_INCLUDE)
 9
10add_executable(TrackerDemo main.cpp)
11
12target_link_libraries(TrackerDemo PUBLIC ${OpenCV_LIBS})

Demo视频外链: https://img.zouchanglin.cn/tracker-demo.mp4