网络编程
位置:首页>> 网络编程>> 网络编程>> 基于OpenCV4.2实现单目标跟踪

基于OpenCV4.2实现单目标跟踪

作者:求则得之,舍则失之  发布时间:2022-04-06 07:58:17 

标签:Python,C++,OpenCV,单目标,跟踪

在本教程中,我们将学习使用OpenCV跟踪对象。OpenCV 3.0开始引入跟踪API。我们将学习如何和何时使用OpenCV 4.2中可用的8种不同的 * - BOOSTING, MIL, KCF, TLD, MEDIANFLOW, GOTURN, MOSSE和CSRT。我们还将学习现代跟踪算法背后的一般理论。

1.什么是目标跟踪

简单地说,在视频的连续帧中定位一个对象称为跟踪。

这个定义听起来很简单,但在计算机视觉和机器学习中,跟踪是一个非常广泛的术语,它包含了概念相似但技术不同的想法。例如,以下所有不同但相关的思想通常都是在对象跟踪中研究的

1.Dense Optical flow(稠密光流):这些算法有助于估计视频帧中每个像素的运动矢量。

2.Sparse optical flow(稀疏光流):这些算法,如Kanade-Lucas-Tomashi (KLT)特征 * ,跟踪图像中几个特征点的位置。

3.Kalman Filtering(卡尔曼滤波):一种非常流行的信号处理算法,用于基于先验运动信息预测运动目标的位置。该算法的早期应用之一是导弹制导!

4.Meanshift and Camshift:这些是定位密度函数最大值的算法。它们也被用于跟踪。

5.Single object trackers(单一对象 * ):在这类 * 中,第一帧使用一个矩形来标记我们想要跟踪的对象的位置。然后使用跟踪算法在随后的帧中跟踪目标。在大多数现实生活中的应用程序中,这些 * 是与对象检测器结合使用的。

6.Multiple object track finding algorithms(多目标追踪算法):在我们有快速目标检测器的情况下,在每帧中检测多个目标,然后运行轨迹查找算法来识别一帧中的哪个矩形对应于下一帧中的矩形是有意义的。

2.跟踪与检测

如果你曾经玩过OpenCV人脸检测,你知道它是实时工作的,你可以很容易地在每一帧中检测人脸。那么,为什么一开始就需要跟踪呢?让我们来探讨一下你可能想要在视频中跟踪对象而不仅仅是重复检测的不同原因。

1.跟踪比检测快:通常跟踪算法要比检测算法快。原因很简单。当您在跟踪前一帧中检测到的对象时,您会对该对象的外观有很多了解。你也知道在前一个坐标系中的位置以及它运动的方向和速度。所以在下一帧中,你可以利用所有这些信息来预测下一帧中物体的位置,并对物体的预期位置做一个小搜索来精确地定位物体。一个好的跟踪算法会使用它所拥有的关于目标的所有信息,而检测算法总是从头开始。因此,在设计一个高效的系统时,通常在每n帧上进行目标检测,在n-1帧之间使用跟踪算法。为什么我们不直接在第一帧检测目标,然后跟踪它呢?跟踪确实可以从它所拥有的额外信息中获益,但如果一个物体在障碍物后面停留了很长一段时间,或者它移动得太快,以至于跟踪算法无法跟上,你也会失去对它的跟踪。跟踪算法也经常会累积误差,跟踪对象的包围框会慢慢地偏离跟踪对象。我们会经常使用检测算法解决跟踪算法的这些问题。检测算法基于大数据训练,因此,他们对对象的一般类别有更多的了解。另一方面,跟踪算法更了解它们所跟踪的类的具体实例。

2.当检测失败时,跟踪可以提供帮助:如果你在视频中运行人脸检测器,而这个人的脸被物体遮挡,人脸检测器很可能会失败。一个好的跟踪算法将解决某种程度的遮挡。

3.跟踪保护身份ID:对象检测的输出是一个包含对象的矩形数组。但是,该对象没有附加身份。例如,在下面的视频中,一个检测红点的探测器将输出与它在一帧中检测到的所有点相对应的矩形。在下一帧中,它将输出另一个矩形数组。在第一帧中,一个特定的点可能由数组中位置10的矩形表示,而在第二帧中,它可能位于位置17。当在帧上使用检测时,我们不知道哪个矩形对应哪个对象。另一方面,追踪提供了一种将这些点连接起来的方法!

3.使用OpenCV 4实现对象跟踪

OpenCV 4附带了一个跟踪API,它包含了许多单对象跟踪算法的实现。在OpenCV 4.2中有8种不同的 * 可用- BOOSTING, MIL, KCF, TLD, MEDIANFLOW, GOTURN, MOSSE,和CSRT。

注意: OpenCV 3.2实现了这6个 * - BOOSTING, MIL, TLD, MEDIANFLOW, MOSSE和GOTURN。OpenCV 3.1实现了这5个 * - BOOSTING, MIL, KCF, TLD, MEDIANFLOW。OpenCV 3.0实现了以下4个 * - BOOSTING, MIL, TLD, MEDIANFLOW。

在OpenCV 3.3中,跟踪API已经改变。代码检查版本,然后使用相应的API。

在简要描述这些算法之前,让我们先看看它们的设置和使用方法。在下面的注释代码中,我们首先通过选择 * 类型来设置 * ——BOOSTING、MIL、KCF、TLD、MEDIANFLOW、GOTURN、MOSSE或CSRT。然后我们打开一段视频,抓取一帧。我们定义了一个包含第一帧对象的边界框,并用第一帧和边界框初始化 * 。最后,我们从视频中读取帧,并在循环中更新 * ,以获得当前帧的新包围框。随后显示结果。

3.1使用OpenCV 4实现对象跟踪 C++代码

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

using namespace cv;
using namespace std;

// 转换为字符串
#define SSTR( x ) static_cast< std::ostringstream & >( ( std::ostringstream() << std::dec << x ) ).str()

int main(int argc, char **argv)
{
   // OpenCV 3.4.1中的 * 类型列表
   string trackerTypes[8] = {"BOOSTING", "MIL", "KCF", "TLD","MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"};
   // vector <string> trackerTypes(types, std::end(types));

// 创建一个 *
   string trackerType = trackerTypes[2];

Ptr<Tracker> tracker;

#if (CV_MINOR_VERSION < 3)
   {
       tracker = Tracker::create(trackerType);
   }
   #else
   {
       if (trackerType == "BOOSTING")
           tracker = TrackerBoosting::create();
       if (trackerType == "MIL")
           tracker = TrackerMIL::create();
       if (trackerType == "KCF")
           tracker = TrackerKCF::create();
       if (trackerType == "TLD")
           tracker = TrackerTLD::create();
       if (trackerType == "MEDIANFLOW")
           tracker = TrackerMedianFlow::create();
       if (trackerType == "GOTURN")
           tracker = TrackerGOTURN::create();
       if (trackerType == "MOSSE")
           tracker = TrackerMOSSE::create();
       if (trackerType == "CSRT")
           tracker = TrackerCSRT::create();
   }
   #endif
   // 读取视频
   VideoCapture video("videos/chaplin.mp4");

// 如果视频没有打开,退出
   if(!video.isOpened())
   {
       cout << "Could not read video file" << endl;
       return 1;
   }

// 读第一帧
   Mat frame;
   bool ok = video.read(frame);

// 定义初始边界框
   Rect2d bbox(287, 23, 86, 320);

// 取消注释下面的行以选择一个不同的边界框
   // bbox = selectROI(frame, false);
   // 显示边界框
   rectangle(frame, bbox, Scalar( 255, 0, 0 ), 2, 1 );

imshow("Tracking", frame);
   tracker->init(frame, bbox);

while(video.read(frame))
   {    
       // 启动定时器
       double timer = (double)getTickCount();

// 更新跟踪结果
       bool ok = tracker->update(frame, bbox);

// 计算每秒帧数(FPS)
       float fps = getTickFrequency() / ((double)getTickCount() - timer);

if (ok)
       {
           // 跟踪成功:绘制被跟踪对象
           rectangle(frame, bbox, Scalar( 255, 0, 0 ), 2, 1 );
       }
       else
       {
           // 跟踪失败
           putText(frame, "Tracking failure detected", Point(100,80), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,255),2);
       }

// 在帧上显示 * 类型
       putText(frame, trackerType + " Tracker", Point(100,20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(50,170,50),2);

// 帧显示FPS
       putText(frame, "FPS : " + SSTR(int(fps)), Point(100,50), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(50,170,50), 2);

// 显示帧
       imshow("Tracking", frame);

// 按ESC键退出。
       int k = waitKey(1);
       if(k == 27)
       {
           break;
       }

}
}

3.2使用OpenCV 4实现对象跟踪 Python代码

import cv2
import sys

(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')

if __name__ == '__main__' :

# 建立 *
   # 除了MIL之外,您还可以使用

tracker_types = ['BOOSTING', 'MIL','KCF', 'TLD', 'MEDIANFLOW', 'GOTURN', 'MOSSE', 'CSRT']
   tracker_type = tracker_types[2]

if int(minor_ver) < 3:
       tracker = cv2.Tracker_create(tracker_type)
   else:
       if tracker_type == 'BOOSTING':
           tracker = cv2.TrackerBoosting_create()
       if tracker_type == 'MIL':
           tracker = cv2.TrackerMIL_create()
       if tracker_type == 'KCF':
           tracker = cv2.TrackerKCF_create()
       if tracker_type == 'TLD':
           tracker = cv2.TrackerTLD_create()
       if tracker_type == 'MEDIANFLOW':
           tracker = cv2.TrackerMedianFlow_create()
       if tracker_type == 'GOTURN':
           tracker = cv2.TrackerGOTURN_create()
       if tracker_type == 'MOSSE':
           tracker = cv2.TrackerMOSSE_create()
       if tracker_type == "CSRT":
           tracker = cv2.TrackerCSRT_create()

# 读取视频
   video = cv2.VideoCapture("videos/chaplin.mp4")

# 如果视频没有打开,退出。
   if not video.isOpened():
       print "Could not open video"
       sys.exit()

# 读第一帧。
   ok, frame = video.read()
   if not ok:
       print('Cannot read video file')
       sys.exit()

# 定义一个初始边界框
   bbox = (287, 23, 86, 320)

# 取消注释下面的行以选择一个不同的边界框
   # bbox = cv2.selectROI(frame, False)

# 用第一帧和包围框初始化 *
   ok = tracker.init(frame, bbox)

while True:
       # 读取一个新的帧
       ok, frame = video.read()
       if not ok:
           break

# 启动计时器
       timer = cv2.getTickCount()

# 更新 *
       ok, bbox = tracker.update(frame)

# 计算帧率(FPS)
       fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer);

# 绘制包围框
       if ok:
           # 跟踪成功
           p1 = (int(bbox[0]), int(bbox[1]))
           p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
           cv2.rectangle(frame, p1, p2, (255,0,0), 2, 1)
       else :
           # 跟踪失败
           cv2.putText(frame, "Tracking failure detected", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2)

# 在帧上显示 * 类型名字
       cv2.putText(frame, tracker_type + " Tracker", (100,20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50),2);

# 在帧上显示帧率FPS
       cv2.putText(frame, "FPS : " + str(int(fps)), (100,50), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50), 2);

# 显示结果
       cv2.imshow("Tracking", frame)

# 按ESC键退出
       k = cv2.waitKey(1) & 0xff
       if k == 27 : break

4.跟踪算法解析

在本节中,我们将深入研究不同的跟踪算法。我们的目标不是对每一个 * 都有一个深刻的理论理解,而是从实际的角度来理解它们。

让我首先解释一些跟踪的一般原则。在跟踪中,我们的目标是在当前帧中找到一个对象,因为我们已经成功地在所有(或几乎所有)之前的帧中跟踪了这个对象。

因为我们一直跟踪对象直到当前帧,所以我们知道它是如何移动的。换句话说,我们知道运动模型的参数。运动模型只是一种花哨的说法,表示你知道物体在前几帧中的位置和速度(速度+运动方向)。如果你对物体一无所知,你可以根据当前的运动模型预测新的位置,你会非常接近物体的新位置。

但我们有比物体运动更多的信息。我们知道物体在之前的每一帧中的样子。换句话说,我们可以构建一个对对象的外观进行编码的外观模型。该外观模型可用于在运动模型预测的小邻域内搜索位置,从而更准确地预测物体的位置。

运动模型预测了物体的大致位置。外观模型对这个估计进行微调,以提供基于外观的更准确的估计。

如果对象非常简单,并且没有太多改变它的外观,我们可以使用一个简单的模板作为外观模型,并寻找该模板。然而,现实生活并没有那么简单。物体的外观会发生巨大的变化。为了解决这个问题,在许多现代 * 中,这个外观模型是一个以在线方式训练的分类器。别慌!让我用更简单的术语解释一下。

分类器的工作是将图像中的矩形区域分类为物体或背景。分类器接收图像patch作为输入,并返回0到1之间的分数,表示图像patch包含该对象的概率。当完全确定图像patch是背景时,分数为0;当完全确定patch是对象时,分数为1。

在机器学习中,我们用&ldquo;在线&rdquo;这个词来指在运行时进行动态训练的算法。离线分类器可能需要数千个示例来训练一个分类器,但在线分类器通常在运行时使用很少的示例进行训练。

通过向分类器输入正(对象)和负(背景)的例子来训练分类器。如果您想要构建一个用于检测猫的分类器,您可以使用数千张包含猫的图像和数千张不包含猫的图像来训练它。这样分类器学会区分什么是猫,什么不是。在构建一个在线分类器时,我们没有机会拥有数千个正面和负面类的例子。

让我们看看不同的跟踪算法是如何处理在线训练的这个问题的。

4.1 BOOSTING Tracker

该 * 基于AdaBoost的在线版本&mdash;&mdash;基于HAAR级联的人脸检测器内部使用的算法。这个分类器需要在运行时用对象的正面和反面例子进行训练。将用户提供的初始包围盒(或其他目标检测算法提供的初始包围盒)作为目标的正例,将包围盒外的许多图像patch作为背景。

给定一个新的帧,分类器在前一个位置附近的每个像素上运行,并记录分类器的得分。对象的新位置是分数最高的位置。现在分类器又多了一个正样本。当更多的帧进来时,分类器就会用这些额外的数据更新。

优点:没有。 这个算法已经有10年的历史了,而且运行良好,但我找不到使用它的好理由,特别是当基于类似原则的其他高级 * (MIL, KCF)可用时。
缺点:跟踪性能平庸。 它不能可靠地知道何时跟踪失败了。

4.2 MIL Tracker

这个 * 在思想上与上述的BOOSTING * 相似。最大的区别是,它不是只考虑对象的当前位置作为一个正样本,而是在当前位置周围的一个小领域中寻找几个潜在的正样本。你可能会认为这不是一个好主意,因为在大多数这些&ldquo;正样本&rdquo;的例子中,物体不是居中的。

这就是多实例学习 (MIL) 的用武之地。在 MIL 中,您不指定正面和负面示例,而是指定正面和负面&ldquo;袋子&rdquo;。正面&ldquo;袋子&rdquo;中的图像集合并不都是正例。取而代之的是,正面袋子中只有一张图像需要是正面的例子!

在我们的示例中,一个正面袋子包含以对象当前位置为中心的patch,以及它周围的一个小邻域中的patch。即使被跟踪对象的当前位置不准确,当来自当前位置附近的样本被放入正面袋子中时,这个正面袋子很有可能包含至少一个对象很好地居中的图像。

优点:性能很好。 它不像BOOSTING * 那样漂移,并且在部分遮挡下做了合理的工作。如果你正在使用OpenCV 3.0,这可能是你可用的最好的 * 。但是,如果您使用的是更高的版本,请考虑KCF。

缺点: 无法可靠地报告跟踪失败。不能从完全遮挡中恢复。

4.3 KCF Tracker

KFC 代表Kernelized Correlation Filters(Kernelized相关性过滤器)。该 * 建立在前两个 * 中提出的想法之上。该 * 利用了 MIL * 中使用的多个正样本具有较大重叠区域的事实。这种重叠数据导致了一些很好的数学特性,该 * 利用这些特性使跟踪更快、更准确。

优点:准确性和速度都优于 MIL,它报告的跟踪失败比 BOOSTING 和 MIL 更好。 如果您使用的是 OpenCV 3.1 及更高版本,我建议将其用于大多数应用程序。
缺点: 不能从完全遮挡中恢复。

4.4 TLD Tracker

TLD 代表跟踪、学习和检测。顾名思义,这个 * 将长期跟踪任务分解为三个部分&mdash;&mdash;(短期)跟踪、学习和检测。从作者的论文中,&ldquo; * 逐帧跟踪对象。检测器定位到目前为止已观察到的所有外观,并在必要时纠正 * 。

学习估计检测器的错误并对其进行更新以避免将来出现这些错误。&rdquo;这个 * 的输出往往会有点跳跃。例如,如果您正在跟踪行人并且场景中有其他行人,则此 * 有时可以临时跟踪与您打算跟踪的行人不同的行人。从积极的方面来说,这条轨迹似乎可以在更大的范围、运动和遮挡范围内跟踪对象。如果您有一个对象隐藏在另一个对象后面的视频序列,则此 * 可能是一个不错的选择。

优点:在多个帧的遮挡下效果最佳。此外,跟踪最好的规模变化。

缺点:大量的误报使得它几乎无法使用。

4.5 MEDIANFLOW Tracker

在内部,该 * 在时间上向前和向后跟踪对象,并测量这两个轨迹之间的差异。最小化这种 ForwardBackward 误差使他们能够可靠地检测跟踪失败并在视频序列中选择可靠的轨迹。

在我的测试中,我发现该 * 在运动可预测且较小时效果最佳。与其他 * 即使在跟踪明显失败时仍继续运行不同,该 * 知道跟踪何时失败。

优点:出色的跟踪失败报告。当运动是可预测的并且没有遮挡时效果很好。

缺点:在大运动下失败。

4.6 GOTURN tracker

在 * 类的所有跟踪算法中,这是唯一一种基于卷积神经网络 (CNN) 的算法。从 OpenCV 文档中,我们知道它&ldquo;对视点变化、光照变化和变形具有鲁棒性&rdquo;。但它不能很好地处理遮挡。

注意:GOTURN 是基于 CNN 的 * ,使用 Caffe 模型进行跟踪。 Caffe 模型和 proto 文本文件必须存在于代码所在的目录中。这些文件也可以从 opencv_extra 存储库下载、连接并在使用前提取。

4.7 MOSSE tracker

最小输出平方误差和 (MOSSE) 使用自适应相关性进行对象跟踪,在使用单帧初始化时会产生稳定的相关性滤波器。 MOSSE * 对光照、比例、姿势和非刚性变形的变化具有鲁棒性。它还根据峰值旁瓣(peak-to-sidelobe)比检测遮挡,这使 * 能够在对象重新出现时暂停并从中断的地方恢复。 MOSSE * 还以更高的 fps(450 fps 甚至更高)运行。除此之外,它还非常容易执行,与其他复杂 * 一样准确,而且速度更快。但是,在性能尺度上,它落后于基于深度学习的 * 。

4.8 CSRT tracker

在DCF-CSR (Discriminative Correlation Filter with Channel and Spatial Reliability, DCF-CSR)中,我们使用空间可靠性映射来调整滤波器的支持度,使其适应帧中被选择区域的跟踪部分。这确保了所选区域的放大和定位,并改进了对非矩形区域或对象的跟踪。它只使用2个标准特性(hog和Colornames)。它也运行在一个相对较低的fps (25 fps),但提供了较高的目标跟踪精度。

来源:https://blog.csdn.net/weixin_43229348/article/details/123322583

0
投稿

猜你喜欢

  • 最近一段时间一直在研究yolo物体检测,基于网络上很少有yolo的分类预训练和yolo9000的联合数据的训练方法,经过本人的真实实验,对这
  • 如果需要对列表中的每个元素执行相同操作,这时就需要遍历列表的所有元素。books=['半生缘','往事并不如烟
  • LRULRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访
  • 1. 引言本文为介绍流行的数独游戏的系列文章中的第一篇。更具体地说,我们如何构建一个脚本来解决数独难题,本文的重点在于介绍用于构建数独求解器
  • 本文实例为大家分享了python实现双链表的具体代码,供大家参考,具体内容如下实现双链表需要注意的地方1、如何插入元素,考虑特殊情况:头节点
  • 前言如何通过python实现邮件解析?邮件的格式十分复杂,主要是mime协议,本文主要是从实现出发,具体原理可以自行研究。一、安装通过mai
  •  <!-- #include file="../conn.asp" --&
  • 九宫格是一种比较古老的设计,它最基本的表现其实就像是一个三行三列的表格。其实它最初是在window的c/s结构中用得比较多,比如我们经常看到
  • 堆是一种特殊的树形结构, 堆中的数据存储满足一定的堆序。堆排序是一种选择排序, 其算法复杂度, 时间复杂度相对于其他的排序算法都有很大的优势
  • 本文实例为大家分享了python实现图像拼接的具体代码,供大家参考,具体内容如下一、效果 二、代码1、单张图片拼接# 图片拼接fr
  • 分享两个常用的代码生成工具:gormgenhandlergengormgen基于 MySQL 数据表结构进行生成 3 个文件:生成表的 st
  • 单张人脸关键点检测定义可视化图像函数导入三维人脸关键点检测模型导入可视化函数和可视化样式读取图像将图像模型输入,获取预测结果BGR转RGB将
  • 使用场景:1) 爬虫设置ip代理池时验证ip是否有效2)进行压测时,进行批量请求等等场景grequests 利用 requests和geve
  • 本文实例总结了Python常用的小技巧。分享给大家供大家参考。具体分析如下:1. 获取本地mac地址:import uuidmac = uu
  • 一朋友委托我帮他投票,地址在: http://publish.sina.com.cn/04/13/413/search.php 投票的链接是
  • 前言最近在使用pytorch的时候,模型训练时,不需要使用forward,只要在实例化一个对象中传入对应的参数就可以自动调用 forward
  • 在树莓派自带得python IDE Thonny中写如下代码,并在树莓派上插上usb摄像头import cv2cap=cv2.VideoCa
  • 随着网络技术的不断发展,网络应用已经渗透到人类社会的各个角落。作为网络世界的支撑点的网站,更是人们关注的热点:政府利用网站宣传自己的施政纲领
  • 在SQL Server中进行开发会让你身处险地,并且寻找快速解决方案。我们编辑了前十名关于SQL Server开发的常见问题。对常见的针对表
  • 一、效果快放10倍总共分为三部分,左上角的正文,下方的心形和右下角的署名特别需要注意的一点是这种东西不但要装Python,还与分辨率有关(换
手机版 网络编程 asp之家 www.aspxhome.com