编辑
2021-11-07
客户端技术
00
请注意,本文编写于 694 天前,最后修改于 113 天前,其中某些信息可能已经过时。

目录

OpenCV 的跟踪算法
Boosting
CSRT
GOTURN
KCF
MedianFlow
MIL
MOSSE
TLD
如何选择
KCF跟踪器示例

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跟踪器示例

cpp
#include <iostream> #include <string> #include <opencv2/opencv.hpp> #include <opencv2/tracking.hpp> #include <opencv2/core/ocl.hpp>

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

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

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

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

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

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

从视频流循环帧分析:

cpp
while (video.read(frame)) { // Start timer 开始计时 double timer = cv::getTickCount(); // Update the tracking result 跟新跟踪器算法 if(tracker->update(frame, bbox)) { std::cout << "Update the tracking result" << std::endl; } // Calculate Frames per second (FPS) 计算FPS float fps = cv::getTickFrequency() / ((double) cv::getTickCount() - timer); if (ok) { // Tracking success : Draw the tracked object 如果跟踪到目标画框 rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2, 1); } else { // Tracking failure detected. 没有就输出跟踪失败 putText(frame, "Tracking failure detected", cv::Point(100, 80), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2); } // Display FPS on frame 表示FPS putText(frame, "FPS : " + std::to_string(int(fps)), cv::Point(100, 50), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2); // Display frame. cv::imshow("OpenCV Tracking", frame); // Exit if ESC pressed. int k = cv::waitKey(1); if (k == 27) { break; } }

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

完整代码如下:

cpp
#include <iostream> #include <string> #include <opencv2/opencv.hpp> #include <opencv2/tracking.hpp> #include <opencv2/core/ocl.hpp> int main() { cv::Ptr<cv::TrackerKCF> tracker; // KCF跟踪器 cv::Mat frame; // 保存每帧图像 tracker = cv::TrackerKCF::create(); // 读取视频数据 cv::VideoCapture video("/Users/zchanglin/Downloads/tracker.mp4"); // 读取摄像头数据 //cv::VideoCapture video(0); // 创建窗口 static const std::string kWinName = "object tracking in OpenCV"; cv::namedWindow(kWinName,cv::WINDOW_AUTOSIZE); if(!video.isOpened()){ std::cout <<"Could not read video file"<< std::endl; return -1; } bool ok = video.read(frame); // 画框选中跟踪目标 cv::Rect2i bbox = cv::selectROI(frame, false); cv::rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2, 1); // 跟踪器初始化 tracker->init(frame, bbox); while (video.read(frame)) { // Start timer 开始计时 double timer = cv::getTickCount(); // Update the tracking result 跟新跟踪器算法 if(tracker->update(frame, bbox)) { std::cout << "Update the tracking result" << std::endl; } // Calculate Frames per second (FPS) 计算FPS float fps = cv::getTickFrequency() / ((double) cv::getTickCount() - timer); if (ok) { // Tracking success : Draw the tracked object 如果跟踪到目标画框 rectangle(frame, bbox, cv::Scalar(255, 0, 0), 2, 1); } else { // Tracking failure detected. 没有就输出跟踪失败 putText(frame, "Tracking failure detected", cv::Point(100, 80), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2); } // Display FPS on frame 表示FPS putText(frame, "FPS : " + std::to_string(int(fps)), cv::Point(100, 50), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 255),2); // Display frame. cv::imshow("OpenCV Tracking", frame); // Exit if ESC pressed. int k = cv::waitKey(1); if (k == 27) { break; } } // 释放跟踪器 tracker.release(); return 0; }

下面是CMakeLists.txt

cmake
cmake_minimum_required(VERSION 3.19) project(TrackerDemo) set(CMAKE_CXX_STANDARD 11) find_package(OpenCV REQUIRED) include_directories(OpenCV_INCLUDE) add_executable(TrackerDemo main.cpp) target_link_libraries(TrackerDemo PUBLIC ${OpenCV_LIBS})

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

本文作者:Tim

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!