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跟踪器作为示例:
初始化视频流,并定义显示窗口:
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);
从视频流循环帧分析:
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