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跟踪器示例
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/core/ocl.hpp>
首先要创建一个跟踪器并实例化,本文仅使用KCF跟踪器作为示例:
cv::Ptr<cv::TrackerKCF> tracker; // KCF跟踪器
tracker = cv::TrackerKCF::create();
初始化视频流,并定义显示窗口:
// 读取视频数据
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区域:
// 画框选中跟踪目标
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;
}
}
因为每一帧对于跟踪器都会重新计算跟踪目标的位置。使用update()方法将定位对象的新位置,并返回true
和边界box对象,通过确保跟踪器是新的,如果选择了某个对象,我们需要更新该对象的位置。
完整代码如下:
#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_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/1c67a7edc7c1b1a575d07105a1f53c92.mp4