DeepSORT多目标跟踪——算法流程与源码解析

news2024/11/29 2:34:17

一、目标检测与目标追踪

1. 目标检测

在目标检测任务中,主要目标是识别图像或视频帧中存在的物体的位置和类别信息。这意味着目标检测算法需要定位物体的边界框(Bounding Box)并确定每个边界框内的物体属于哪个类别(如人、汽车、狗等)。目标检测通常独立地处理每一帧图像,不考虑目标在不同帧之间的连续性。它通常用于静态图像的物体识别,也可以用于处理视频流中的每一帧以实现实时检测。

主流的目标检测算法:

  1. YOLO(You Only Look Once):YOLO是一种非常流行的目标检测算法,它具有快速的实时检测能力。YOLO将图像分成网格,并在每个网格上进行预测,以同时获得多个边界框和它们的类别概率。YOLO的特点是快速、准确,适用于实时应用。

  2. Faster R-CNN:Faster R-CNN采用了两阶段检测方法,其中第一阶段生成候选边界框,第二阶段对候选边界框进行分类和回归以获得最终检测结果。它的准确性较高,但速度相对较慢。

  3. SSD(Single Shot MultiBox Detector):SSD是一种单阶段检测算法,它在每个位置上使用多个不同尺度的卷积层来预测边界框和类别。它具有良好的平衡性能,既准确又相对快速。

  4. RetinaNet:RetinaNet结合了两阶段和单阶段方法的优点,使用了特殊的损失函数来解决类别不平衡问题,从而实现高效且准确的目标检测。

  5. Mask R-CNN:Mask R-CNN是Faster R-CNN的扩展版本,它不仅可以检测目标的边界框,还可以生成目标的精确掩模,用于实现实例分割任务,即将不同实例的像素分离开来。

  6. EfficientDet:EfficientDet是一种高效的目标检测算法,它基于EfficientNet的架构,并在目标检测中取得了很好的性能。它通过改进网络结构和训练策略来实现高效的目标检测。

目标检测示例:

目标检测

2. 目标追踪

与目标检测不同,目标跟踪的任务是在视频序列中跟踪特定物体的运动,即在不同的帧之间识别和跟踪同一物体。目标跟踪需要处理物体在时间上的连续性,通常涉及到将不同帧中的物体关联起来,以确保它们被正确标识为同一个物体。目标跟踪算法通常会分配唯一的ID给每个被跟踪的物体,以区分它们,并跟踪它们的位置随时间的变化。目标跟踪广泛用于视频监控、自动驾驶、人机互动等领域。

主流目标追踪算法:

  1. Kalman滤波器:Kalman滤波器是一个常用于目标追踪的线性动态系统估计方法。它可以预测目标的位置和速度,然后通过观测来不断更新估计。

  2. Particle Filter:粒子滤波器是一种用于非线性和非高斯问题的目标追踪方法。它通过随机粒子的采样来估计目标的状态。

  3. Mean-Shift:均值漂移算法是一种非参数化方法,它通过寻找目标在颜色空间中的密度峰值来进行目标追踪。

  4. KLT跟踪器:KLT(Kanade-Lucas-Tomasi)跟踪器是一种用于光流估计和目标追踪的方法,它可以用于跟踪特征点或区域。

  5. MIL(多实例学习):MIL是一种多目标追踪方法,它在每帧中检测多个候选目标并使用学习算法确定目标的状态。

  6. DeepSORT:DeepSORT是一种结合了深度学习和目标追踪的方法,它使用深度学习模型来提取目标特征,并使用关联算法来跟踪目标在视频中的位置。

  7. SORT:SORT(Simple Online and Realtime Tracking)是一种实时目标追踪方法,它通过匹配边界框和跟踪目标来实现目标追踪。

目标追踪示例:

目标追踪

从上面的视频示例中可以看到,检测到的每个目标都有一个指定的唯一的ID,当目标追踪出现遮挡或者丢失的之后,再次检测,还能匹配到原本的ID。

二. DeepSORT目标追踪

1.算法简介

目前,主流的目标跟踪算法大多采用"Tracking-by-Detection"策略,即依赖目标检测的结果来进行目标跟踪。DeepSORT是一个代表性的算法,它也采用了这一策略。DeepSORT对目标进行跟踪,每个边界框(bbox)的左上角数字用于标识唯一的目标ID。
DeepSORT的前身是SORT(Simple Online and Realtime Tracking)算法,SORT是一种目标跟踪器,由目标检测器和跟踪器组成。在SORT中,跟踪器的核心是卡尔曼滤波算法和匈牙利算法。它利用卡尔曼滤波来预测目标在下一帧的状态,然后使用匈牙利算法将这一状态与下一帧的检测结果进行匹配,以实现目标跟踪。如果目标在下一帧未被检测到,例如因受到遮挡或其他原因,卡尔曼滤波预测的状态信息将无法与检测结果匹配,导致跟踪片段提前结束。

  • 匈牙利算法可以确定当前帧的某个目标,是否与前一帧的某个目标相同。
  • 卡尔曼滤波可以基于目标前一时刻的位置,来预测当前时刻的位置,并且可以比传感器(在目标跟踪中即目标检测器,比如Yolo等)更准确的估计目标的位置。
    在这里插入图片描述

2.追踪流程

DeepSORT引入了深度学习中的重识别算法,以提取被检测物体的外观特征(低维向量表示)。在每一次检测和追踪后,它会执行外观特征提取,并将这些特征保存起来。随后的每一步,它都会计算当前帧中被检测物体的外观特征与先前存储的特征之间的相似度,这有助于避免漏检和身份丢失的情况。因此,DeepSORT不仅仅依赖于物体的速度和方向趋势来进行目标跟踪,还利用物体的外观特征来加强对是否为同一物体的判断。

这个过程有点像在每一帧中为每个被检测的物体创建一个独特的ID,并在后续帧中使用这些ID来识别和跟踪物体。这种方法提高了目标追踪的鲁棒性,因为即使目标在某些帧中被遮挡或重新出现,根据外观特征的相似性,DeepSORT仍能够正确地将其与之前的跟踪关联起来,从而保持目标的身份标识。

DeepSORT跟踪算法归纳为以下几个步骤:
在这里插入图片描述

3.算法实现

当使用DeepSORT进行目标跟踪时,正常分两个步骤:

3.1 目标检测

首先,使用常规的目标检测模型(比如YOLO)对单帧图像进行分析,检测到感觉兴趣的目标的坐标位置 ,如果上面的示例视频,首先要使用目标检测算法对图像的中的行人做检测。

3.2目标预测

在这一步中,使用卡尔曼滤波算法。它基于当前的一系列运动变量来预测下一时刻的运动变量。首次检测结果用于初始化卡尔曼滤波的运动变量。预测结果被分为两种状态:确认态(confirmed)和不确认态(unconfirmed)。新生成的跟踪对象处于不确认态,只有在与检测结果连续匹配一定次数后才能转化为确认态。而处于确认态的跟踪对象需要与检测结果连续匹配一定次数才会被删除。

接下来,需要将检测到的物体与预测的物体进行关联。DeepSORT使用匈牙利算法,并根据不同的代价函数来找到最佳匹配。如果卡尔曼滤波输出了确认态的预测结果,DeepSORT将使用马氏距离和余弦距离的级联方法来关联相关信息。马氏距离提供了不同状态下物体之间的距离信息。如果某次关联的马氏距离低于指定的阈值,那么运动状态的关联成功。但DeepSORT不仅考虑帧与帧之间框的距离,还考虑框内的外观特征以更好地进行关联匹配。因此,DeepSORT引入了外观特征余弦距离度量,通过一个重识别模型获取不同物体的特征向量,然后构建余弦距离代价函数,计算预测对象与检测对象的相似度。当两个预测框的马氏距离和外观特征余弦距离都小于一定阈值时,它们被认为是同一个物体。
在这里插入图片描述

DeepSORT之所以引入这种级联方法,是因为在物体运动状态变化较剧烈的情况下,基于目标状态之间的关联可能不可靠。例如,当一个人在奔跑时,如果相机是静止的或与人的运动方向相反,相机中的人在每帧之间的运动状态会有很大的差异。这种情况下,运动的不确定性增加,先验状态与目标检测之间的匹配差异较大。为了弥补这一缺陷,DeepSORT采用特征相似距离关联。然而,在目标的运动状态变化不剧烈的情况下,马氏距离成为更好的数据关联度量选择。

在数据关联的第二步,DeepSORT计算了不确认态下的预测框和未被上一步级联方法匹配的检测框之间的IOU交并比。然后,使用匈牙利算法来找到最佳匹配的IOU结果。如果预测框和检测框的IOU低于阈值,它们的关联将被删除。
最后,利用当前帧的关联结果来更新所有分配了ID的跟踪对象的状态。这有助于保持跟踪对象的准确性和一致性。

三、源码解析

DeepSORT在每一帧的处理流程如下:

1.检测

在每一帧中,目标检测器识别并提取出边界框(bbox),这些边界框表示在当前帧中检测到的目标物体。

 def detect(self,cv_src):
        boxes, scores, class_ids = self.detector(cv_src)

        pred_boxes = []
        for i in range(len(boxes)):
            x1,y1 = int(boxes[i][0]),int(boxes[i][1])
            x2,y2 = int(boxes[i][2]),int(boxes[i][3])
            lbl = class_names[class_ids[i]]
            # print(class_ids[i])

            # if lbl in ['person','sack','elec','bag','box','caron']:
            #     continue

            pred_boxes.append((x1,y1,x2,y2,lbl,class_ids[i]))

        return cv_src,pred_boxes

2. 生成detections

从这些检测到的边界框中,生成称为"detections"的目标检测结果。每个detection通常包含有关目标的信息,如边界框坐标和可信度分数。

#  deep_sort.py
def update(self, bbox_xywh, confidences, ori_img):
    self.height, self.width = ori_img.shape[:2]
    # 提取每个bbox的feature
    features = self._get_features(bbox_xywh, ori_img)
    # [cx,cy,w,h] -> [x1,y1,w,h]
    bbox_tlwh = self._xywh_to_tlwh(bbox_xywh)
    # 过滤掉置信度小于self.min_confidence的bbox,生成detections
    detections = [Detection(bbox_tlwh[i], conf, features[i]) for i,conf in enumerate(confidences) if conf > self.min_confidence]
    # NMS (这里self.nms_max_overlap的值为1,即保留了所有的detections)
    boxes = np.array([d.tlwh for d in detections])
    scores = np.array([d.confidence for d in detections])
    indices = non_max_suppression(boxes, self.nms_max_overlap, scores)
    detections = [detections[i] for i in indices]
    ...

3. 卡尔曼滤波预测

对于已知的跟踪对象(“tracks”),在下一帧中进行卡尔曼滤波预测,以估计其新的位置和速度。

#  track.py
def predict(self, kf):
    """Propagate the state distribution to the current time step using a 
       Kalman filter prediction step.
    Parameters
    ----------
    kf: The Kalman filter.
    """
    self.mean, self.covariance = kf.predict(self.mean, self.covariance)  # 预测
    self.age += 1  # 该track自出现以来的总帧数加1
    self.time_since_update += 1  # 该track自最近一次更新以来的总帧数加1

4.使用匈牙利算法将预测后的tracks和当前帧中的detections进行匹配

这是DeepSORT中的核心步骤。DeepSORT使用匈牙利算法来将预测的tracks和当前帧的detections进行匹配。这个匹配可以采用两种级联方法:首先,通过计算马氏距离来估算预测对象与检测对象之间的关联,如果马氏距离小于指定的阈值,则将它们匹配为同一目标。其次,DeepSORT还使用外观特征余弦距离度量,通过一个重识别模型获得不同物体的特征向量,然后构建余弦距离代价函数,以计算预测对象与检测对象的相似度。这两个代价函数的结果都趋向于小,如果边界框接近且特征相似,则将它们匹配为同一目标。

#  tracker.py
def _match(self, detections):
    def gated_metric(racks, dets, track_indices, detection_indices):
        """
        基于外观信息和马氏距离,计算卡尔曼滤波预测的tracks和当前时刻检测到的detections的代价矩阵
        """
        features = np.array([dets[i].feature for i in detection_indices])
        targets = np.array([tracks[i].track_id for i in track_indices]
 # 基于外观信息,计算tracks和detections的余弦距离代价矩阵
        cost_matrix = self.metric.distance(features, targets)
 # 基于马氏距离,过滤掉代价矩阵中一些不合适的项 (将其设置为一个较大的值)
        cost_matrix = linear_assignment.gate_cost_matrix(self.kf, cost_matrix, tracks, 
                      dets, track_indices, detection_indices)
        return cost_matrix

    # 区分开confirmed tracks和unconfirmed tracks
    confirmed_tracks = [i for i, t in enumerate(self.tracks) if t.is_confirmed()]
    unconfirmed_tracks = [i for i, t in enumerate(self.tracks) if not t.is_confirmed()]

    # 对confirmd tracks进行级联匹配
    matches_a, unmatched_tracks_a, unmatched_detections = \
        linear_assignment.matching_cascade(
            gated_metric, self.metric.matching_threshold, self.max_age,
            self.tracks, detections, confirmed_tracks)

    # 对级联匹配中未匹配的tracks和unconfirmed tracks中time_since_update为1的tracks进行IOU匹配
    iou_track_candidates = unconfirmed_tracks + [k for k in unmatched_tracks_a if
                                                 self.tracks[k].time_since_update == 1]
    unmatched_tracks_a = [k for k in unmatched_tracks_a if
                          self.tracks[k].time_since_update != 1]
    matches_b, unmatched_tracks_b, unmatched_detections = \
        linear_assignment.min_cost_matching(
            iou_matching.iou_cost, self.max_iou_distance, self.tracks,
            detections, iou_track_candidates, unmatched_detections)
 
    # 整合所有的匹配对和未匹配的tracks
    matches = matches_a + matches_b
    unmatched_tracks = list(set(unmatched_tracks_a + unmatched_tracks_b))
    
    return matches, unmatched_tracks, unmatched_detections


# 级联匹配源码  linear_assignment.py
def matching_cascade(distance_metric, max_distance, cascade_depth, tracks, detections, 
                     track_indices=None, detection_indices=None):
    ...
    unmatched_detections = detection_indice
    matches = []
    # 由小到大依次对每个level的tracks做匹配
    for level in range(cascade_depth):
 # 如果没有detections,退出循环
        if len(unmatched_detections) == 0:  
            break
 # 当前level的所有tracks索引
        track_indices_l = [k for k in track_indices if 
                           tracks[k].time_since_update == 1 + level]
 # 如果当前level没有track,继续
        if len(track_indices_l) == 0: 
            continue
  
 # 匈牙利匹配
        matches_l, _, unmatched_detections = min_cost_matching(distance_metric, max_distance, tracks, detections, 
                                                               track_indices_l, unmatched_detections)
        
 matches += matches_l
 unmatched_tracks = list(set(track_indices) - set(k for k, _ in matches))
    return matches, unmatched_tracks, unmatched_detections

5. 卡尔曼滤波更新

匹配后,DeepSORT使用检测到的detections来更新每个已知的跟踪对象的状态,例如位置和速度。这有助于保持跟踪对象的准确性和连续性。

def update(self, detections):
    """Perform measurement update and track management.
    Parameters
    ----------
    detections: List[deep_sort.detection.Detection]
                A list of detections at the current time step.
    """
    # 得到匹配对、未匹配的tracks、未匹配的dectections
    matches, unmatched_tracks, unmatched_detections = self._match(detections)

    # 对于每个匹配成功的track,用其对应的detection进行更新
    for track_idx, detection_idx in matches:
        self.tracks[track_idx].update(self.kf, detections[detection_idx])
    
	# 对于未匹配的成功的track,将其标记为丢失
	for track_idx in unmatched_tracks:
        self.tracks[track_idx].mark_missed()
	
    # 对于未匹配成功的detection,初始化为新的track
    for detection_idx in unmatched_detections:
        self._initiate_track(detections[detection_idx])
    
	...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1172263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-WebSocket浏览器-服务器双向通信

文章目录 WebSocket 介绍入门案例 WebSocket 介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。 应用场景: 视…

基于 LangChain 构建 LLM 应用程序设计与实践

▼最近直播超级多,预约保你有收获 近期直播:《基 LangChain大模型架构案例实践》 —1— LangChain 是什么? LangChain 和 Semantic Kernel 是当前比较受欢迎的两款 LLM 应用开发框架。 LangChain 作为一个大语言模型应用开发框架,…

C++ 多态 纯干货讲解 复制可调试(1)

💯 博客内容:多态 😀 作  者:陈大大陈 🚀 个人简介:一个正在努力学技术的准C后端工程师,专注基础和实战分享 ,欢迎私信! 💖 欢迎大家:这里是CSD…

0007Java安卓程序设计-ssm基于Android的校园新闻管理系统

文章目录 **摘** **要**目 录开发环境 编程技术交流、源码分享、模板分享、网课教程 🐧裙:776871563 摘 要 网络的广泛应用给生活带来了十分的便利。所以把校园新闻管理与现在网络相结合,利用java技术建设校园新闻管理系统app,实…

【漏洞复现】Drupal_小于7.32版本 _“Drupalgeddon” SQL注入漏洞(CVE-2014-3704)

感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 说明内容漏洞编号CVE-2014-3704漏洞名称Drupal “Drupalgeddon” SQL注入漏洞漏洞…

搬家两年随笔

不知不觉中,我已经搬到这个地方两年多了。 回首这段时间,我感触颇深。 尽管这里地理位置较为偏僻,交通不是特别方便,但环境优美,绿树成荫,空气清新。 只是相对于之前的生活环境,这里离上班的地方…

【Linux】基本命令

和光同尘_我的个人主页 一直游到海水变蓝。 --余华 Linux基本命令 &#x1f56f;️前言基础指令lspwdcdtouchmkdirrmdirrmman**cp**mvcat**less指令**输出重定向 >追加重定向 >>输入重定向 <headtail&#xff08;查看日志常用&#xff09;管道时间相关指令dateca…

五:Day11_SpringMVC03

一、拦截器 SpringMVC给出了拦截器来实现单元方法的拦截&#xff0c;拦截器的执行是在DispatcherServlet之后和单元方法之前的。 注意&#xff1a;只有URL匹配到了控制单元&#xff0c;拦截器才能生效。 2. 使用拦截器 2.1 创建拦截器类 public class MyInterceptor implem…

S5PV210(十):LCD

本文主要探讨210的LCD相关知识。 LCD LCD称液晶(透光背光呈色),可在电信号驱动下使液晶分子旋转,呈现不同的颜色(被动发光) lcd接口为TTL接口(5V为1&#xff0c;0V为0),不能传输太远,远距离传输方式:SoC(TTL) ->VGA-> LCD(TTL) 其他显设备:CRT(…

Java基础篇 | 多线程详解

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

【数据结构】深入浅出讲解计数排序【图文详解,搞懂计数排序这一篇就够了】

计数排序 前言一、计数排序算法核心思路映射 概念补充绝对映射相对映射 二、计数排序算法核心实现步骤三、码源详解四、效率分析&#xff08;1&#xff09;时间复杂度 — O&#xff08;Max&#xff08;N&#xff0c;range&#xff09;&#xff09;&#xff08;2&#xff09;空间…

74HC138逻辑芯片

文章目录 74系列逻辑芯片——74HC138基础信息描述特征应用范围 功能信息封装引脚基本电路 扩展性能分析 74系列逻辑芯片——74HC138 基础信息 描述 74HC138器件设计用于需要极短传播延迟时间的高性能存储器解码或数据路由应用&#xff1b;在高性能存储系统中&#xff0c;可使用…

【C语言】扫雷游戏的一步一步的实现

文章目录 一、扫雷游戏分析和设计1.1 扫雷游戏的功能说明1.2 游戏的分析和设计1.2.1 数据结构的分析1.2.2 ⽂件结构设计 二、扫雷游戏代码实现总结 一、扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 • 使⽤控制台实现经典的扫雷游戏 • 游戏可以通过菜单实现继续玩或者退出游…

时序分解 | Matlab实现EMD经验模态分解时间序列信号分解

时序分解 | Matlab实现EMD经验模态分解时间序列信号分解 目录 时序分解 | Matlab实现EMD经验模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现EMD经验模态分解时间序列信号分解 Matlab语言 算法新颖小众&#xff0c;用的人很少&#xf…

面试10000次依然会问的【ReentrantLock】,你还不会?

引言 在并发编程的世界中&#xff0c;ReentrantLock扮演着至关重要的角色。它是一个实现了重入特性的互斥锁&#xff0c;提供了比synchronized关键字更加灵活的锁定机制。ReentrantLock属于java.util.concurrent.locks包&#xff0c;是Java并发API的一部分。 与传统的synchro…

如何使用CodeceptJS、Playwright和GitHub Actions构建端到端测试流水线

介绍 端到端测试是软件开发的一个重要方面&#xff0c;因为它确保系统的所有组件都能正确运行。CodeceptJS是一个高效且强大的端到端自动化框架&#xff0c;与Playwright 结合使用时&#xff0c;它成为自动化Web、移动甚至桌面 (Electron.js) 应用程序比较好用的工具。 在本文中…

2023-11 | 短视频批量下载/爬取某个用户的所有视频 | Python

这里以鞠婧祎的个人主页为demo https://www.douyin.com/user/MS4wLjABAAAACV5Em110SiusElwKlIpUd-MRSi8rBYyg0NfpPrqZmykHY8wLPQ8O4pv3wPL6A-oz 【2023-11-4 23:02:52 星期六】可能后面随着XX的调整, 方法不再适用, 请注意 找到接口 找到https://www.douyin.com/aweme/v1/web/…

rust入门基础案例:猜数字游戏

案例出处是《Rust权威指南》&#xff0c;书中有更加详细的解释。从这个例子中&#xff0c;我们可以了解到 rust 的两个操作&#xff1a; 如何从控制台读取用户输入rust 如何生成随机数 代码格式化 编译器可在保存时对代码做格式化处理&#xff0c;底层调用 rustfmt 来实现&a…

【gpt redis】原理篇

用的黑马程序员redis课程的目录&#xff0c;但是不想听讲了。后续都是用gpt文档获取的。 1.课程介绍(Av766995956,P145) 2.Redis数据结构-动态字符串(Av766995956,P146) sds 1M是个界限 其实他是个由c语言实现的结构体 有这么几个参数 len alloc flag char[] len是实际长度 …

【漏洞复现】Apache Log4j Server 反序列化命令执行漏洞(CVE-2017-5645)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、深度利用1、反弹Shell 说明内容漏洞编号CVE-2017-5645漏洞名称Log4j Server …