Python,Bytetrack 源码解读,参数,源码解释,逐句分析代码,目标追踪

news2024/11/18 13:56:16

文章目录

  • 1、得到索引
  • 2、高得分框参与匹配,可能会留下有匹配不了的框
  • 3、低得分框参与匹配
  • 4、处理 unconfirmed 匹配
  • 5、创建新的【STrack对象】
  • 6、扔掉太久没匹配到框的【STrack对象】
  • 7、输出追踪框

1、得到索引

self.args.track_thresh是轨迹阈值。轨迹的得分是iou得分乘以框的置信度。
remain_inds 是高得分的框的索引。
inds_second 是低得分的框的索引(介于0.1到self.args.track_thresh之间)。

        remain_inds = scores >= self.args.track_thresh
        
        inds_low = scores > 0.1
        inds_high = scores < self.args.track_thresh

        inds_second = np.logical_and(inds_low, inds_high)
        dets_second = bboxes[inds_second] # 低得分的框
        dets = bboxes[remain_inds]  # 高得分的框
        scores_keep = scores[remain_inds] # # 高得分的框的对应score
        scores_second = scores[inds_second] # 低得分的框的对应score

2、高得分框参与匹配,可能会留下有匹配不了的框

detections 中是高得分的框做成的STrack类。

        if len(dets) > 0:
            '''Detections'''
            detections = [STrack(STrack.tlbr_to_tlwh(tlbr), s) for
                          (tlbr, s) in zip(dets, scores_keep)]
        else:
            detections = []

之前没确认的STrack是unconfirmed。
之前处于追踪中的STrack是tracked_stracks。【track.is_activated】

        ''' Add newly detected tracklets to tracked_stracks'''
        unconfirmed = []
        tracked_stracks = []  # type: list[STrack]
        for track in self.tracked_stracks:
            if not track.is_activated:
                unconfirmed.append(track)
            else:
                tracked_stracks.append(track)

用跟踪着的track和lost的track都参与高分框detections 的匹配。
这一步是关键计算:dists = matching.fuse_score(dists, detections) # !! IOU分数乘以框的置信度分数!!

和所有的检测框计算iou的时候,用的是卡尔曼预测出的框。

        ''' Step 2: First association, with high score detection boxes'''
        strack_pool = joint_stracks(tracked_stracks, self.lost_stracks) # 跟踪着的track和lost的track
        # Predict the current location with KF
        STrack.multi_predict(strack_pool) # 更新mean和covariance
        dists = matching.iou_distance(strack_pool, detections) # 和所有的检测框计算iou,越小越匹配。1就是没有交合。
        if not self.args.mot20:
            dists = matching.fuse_score(dists, detections) # !! IOU分数乘以框的置信度分数!!

使用matching.linear_assignment进行匹配,之前的【STrack对象】是否能匹配上现在的【检测框】,依靠self.args.match_thresh 作为阈值。matches是配对了的,比如 [(1,2)] 就表示strack_pool中第1个【STrack对象】和detections中第2个检测框匹配上了。
显然,u_track就是剩下的没匹配成功的【STrack对象】序号,比如[4]。
u_detection 就是剩下的没匹配成功的detections中的检测框序号,比如[0]。

        matches, u_track, u_detection = matching.linear_assignment(dists, thresh=self.args.match_thresh) # matches是匹配的,u_track是没有匹配的track,u_detection是没有匹配的检测框

对匹配上的matches,如果track.state == TrackState.Tracked,更新【STrack对象】(卡尔曼的更新步骤)。

否则就是track.re_activate(det, self.frame_id, new_id=False)(里面也是卡尔曼的更新步骤)。

        for itracked, idet in matches:
            track = strack_pool[itracked]
            det = detections[idet]
            if track.state == TrackState.Tracked:
                track.update(detections[idet], self.frame_id)
                activated_starcks.append(track)
            else:
                track.re_activate(det, self.frame_id, new_id=False)
                refind_stracks.append(track)

3、低得分框参与匹配

r_tracked_stracks 中是在追踪中的【STrack对象】,而且高分框没匹配上剩下的。

        ''' Step 3: Second association, with low score detection boxes'''
        # association the untrack to the low score detections
        if len(dets_second) > 0:
            '''Detections'''
            detections_second = [STrack(STrack.tlbr_to_tlwh(tlbr), s) for
                                 (tlbr, s) in zip(dets_second, scores_second)]
        else:
            detections_second = []
        r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == TrackState.Tracked]
        dists = matching.iou_distance(r_tracked_stracks, detections_second)

采用更低的阈值进行选取matching.linear_assignment(dists, thresh=0.5 )。

        matches, u_track, u_detection_second = matching.linear_assignment(dists, thresh=0.5)

能匹配上的依旧激活。


        for itracked, idet in matches:
            track = r_tracked_stracks[itracked]
            det = detections_second[idet]
            if track.state == TrackState.Tracked:
                track.update(det, self.frame_id)
                activated_starcks.append(track)
            else:
                track.re_activate(det, self.frame_id, new_id=False)
                refind_stracks.append(track)


对高分框和低分框都匹配不上的【STrack对象】,放入lost_stracks。
到这里,对上一帧出现的所有【STrack对象】,都已经有了归宿,要么去activated_starcks、要么去refind_stracks、要么来到这里的lost_stracks。

        for it in u_track:
            track = r_tracked_stracks[it]
            if not track.state == TrackState.Lost:
                track.mark_lost()
                lost_stracks.append(track)

4、处理 unconfirmed 匹配

什么要分高得分框匹配和低得分框匹配?因为可以分得更准。

unconfirmed 是之前的状态不满足:track.state == TrackState.Tracked。

u_detection 中是高得分的框,而且在第一次匹配中没有匹配到【STrack对象】剩下来的。

这里尝试进行将 unconfirmed 与 u_detection 进行匹配,碰碰运气。这样可能匹配上,就不用重新给这个框一个新的ID。

这里使用 thresh=0.7,这些thresh其实都是一个重要的超参,你的检测模型低置信度,那这些thresh超参都需要调整,而不是只调整最开始那个。

        '''Deal with unconfirmed tracks, usually tracks with only one beginning frame'''
        detections = [detections[i] for i in u_detection]
        dists = matching.iou_distance(unconfirmed, detections)
        if not self.args.mot20:
            dists = matching.fuse_score(dists, detections)
        matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.7)

处理匹配上的(matches)。

        for itracked, idet in matches:
            unconfirmed[itracked].update(detections[idet], self.frame_id)
            activated_starcks.append(unconfirmed[itracked])

对于不确信的u_unconfirmed,这次高分框匹配不到的话,将u_unconfirmed中的【STrack对象】加入到removed_stracks。

        for it in u_unconfirmed:
            track = unconfirmed[it]
            track.mark_removed()
            removed_stracks.append(track)

5、创建新的【STrack对象】

得分高的框,匹配不到任何已有的【STrack对象】,直接分配一个新的ID让这个框成为【STrack对象】,加入到activated_starcks。

        """ Step 4: Init new stracks"""
        for inew in u_detection:
            track = detections[inew]
            if track.score < self.det_thresh:
                continue
            track.activate(self.kalman_filter, self.frame_id)
            activated_starcks.append(track)

6、扔掉太久没匹配到框的【STrack对象】

上一帧的【STrack对象】,匹配不到框,就会成为lost_stracks。
在这里遍历lost_stracks中的【STrack对象】,太久都无法找到框匹配,超出定义的buffer_size,会扔到removed_stracks中。

        """ Step 5: Update state"""
        for track in self.lost_stracks:
            if self.frame_id - track.end_frame > self.max_time_lost:
                track.mark_removed()
                removed_stracks.append(track)

7、输出追踪框

self.tracked_stracks 中是上一帧的所有【STrack对象】,在这一帧里面,有的被分入unconfirmed中,而unconfirmed中没有匹配高分框的话就会标志被标志移除【见4、处理 unconfirmed 匹配】。所以下面这句话就挑选中上一帧还处于追踪的【STrack对象】。

self.tracked_stracks = [t for t in self.tracked_stracks if t.state == TrackState.Tracked]

下面这句就将这一帧能被追踪的所有【STrack对象List】activated_starcks 加到一起。

在这一帧里面,什么条件才能被加入到activated_starcks:

1、 高分框和上一帧的【STrack对象List】tracked_stracks匹配成功,满足track.state == TrackState.Tracked。

2、低分框和上一帧的【STrack对象List】tracked_stracks匹配成功,以更低松懈阈值匹配,满足track.state == TrackState.Tracked。

3、高分框和上一帧的【STrack对象List】unconfirmed匹配成功,以更低松懈阈值。

4、啥都没落着的高分框,自成一个【STrack对象】,加入到activated_starcks。

self.tracked_stracks = joint_stracks(self.tracked_stracks, activated_starcks)

下面这句就将这一帧能被追踪的所有【STrack对象List】refind_stracks加到一起。

在这一帧里面,什么条件才能被加入到refind_stracks:

1、 高分框和上一帧的【STrack对象List】tracked_stracks匹配成功,不满足track.state == TrackState.Tracked,重新激活。

2、低分框和上一帧的【STrack对象List】tracked_stracks匹配成功,以更低松懈阈值匹配,不满足track.state == TrackState.Tracked,重新激活。

self.tracked_stracks = joint_stracks(self.tracked_stracks, refind_stracks)

更新 lost_stracks ,lost_stracks 里面有被追踪起来的就删除掉。

self.lost_stracks = sub_stracks(self.lost_stracks, self.tracked_stracks)
self.lost_stracks.extend(lost_stracks)
self.lost_stracks = sub_stracks(self.lost_stracks, self.removed_stracks)
self.removed_stracks.extend(removed_stracks)

去除重叠对象。

self.tracked_stracks, self.lost_stracks = remove_duplicate_stracks(self.tracked_stracks, self.lost_stracks)

【STrack对象List】中被激活的会输出出去。这里比较坑比的一点是,只有一个帧的高分检测目标框,不会是一个activated的状态(除非是整个过程的第一帧)。

 output_stracks = [track for track in self.tracked_stracks if track.is_activated]

坑比的一点,请看vcr:
在这里插入图片描述

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

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

相关文章

RabbitMQ: topic 结构

生产者 package com.qf.mq2302.topic;import com.qf.mq2302.utils.MQUtils; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection;public class Pubisher {public static final String EXCHANGE_NAME"mypubilisher";public static void ma…

c++通过tensorRT调用模型进行推理

模型来源&#xff1a; 算法工程师训练得到的onnx模型 c对模型的转换&#xff1a; 拿到onnx模型后&#xff0c;通过tensorRT将onnx模型转换为对应的engine模型&#xff0c;注意&#xff1a;训练用的tensorRT版本和c调用的tensorRT版本必须一致。 如何转换&#xff1a; 算法工…

Json“牵手”亚马逊商品详情数据方法,亚马逊商品详情API接口,亚马逊API申请指南

亚马逊平台是美国最大的一家网络电子商务公司&#xff0c;亚马逊公司是1995年成立&#xff0c;刚开始只做网上书籍售卖业务&#xff0c;后来扩展到了其他产品。现在已经是全世界商品品种最多的网上零售商和第二互联网公司&#xff0c;亚马逊是北美洲、欧洲等地区的主流购物平台…

为什么5G 要分离 CU 和DU?(4G分离RRU 和BBU)

在 Blog 一文中&#xff0c;5G--BBU RRU 如何演化到 CU DU&#xff1f;_5g rru_qq_38480311的博客-CSDN博客 解释了4G的RRU BBU 以及 5G CU DU AAU&#xff0c;主要是讲了它们分别是什么。但是没有讲清楚 为什么&#xff0c;此篇主要回答why。 4G 为什么分离基站为 RRU 和 BBU…

什么是原生IP?原生IP与住宅IP有何区别?

相信许多做跨境的都会接触到IP代理&#xff0c;比如电商平台、社媒平台、收款平台等等&#xff0c;都会检测IP。那也会经常听到一些词汇&#xff1a;原生IP、住宅IP&#xff0c;这两者之间有什么区别呢&#xff1f;什么业务需要用到呢&#xff1f;接下来带大家具体了解一下。 什…

React Antd可编辑单元格,非官网写法,不使用可编辑行和form验证

antd3以上的写法乍一看还挺复杂&#xff0c;自己写了个精简版 没用EditableRowCell的结构&#xff0c;也不使用Context、高阶组件等&#xff0c;不使用form验证 最终效果&#xff1a; class EditableCell extends React.Component {state {editing: false};toggleEdit () &…

SFUD固件移植

SFUD作用 SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多&#xff0c;各个 Flash 的规格及命令存在差异&#xff0c; SFUD 就是为了解决这些 Flash 的差异现状而设计&#xff0c;让我们的产品能够支持不同品牌及规格的 Flash&#xff0c;提高…

Android 修改代码后不生效问题的终极方案

前言&#xff1a; 最近遇到几个项目&#xff0c;都出现了代码修改后&#xff0c;直接点studio上的run&#xff0c;跑起来后代码没生效&#xff0c;如果重新clean rebuild可以生效&#xff0c;但是这太浪费时间了。网上找了各种方案&#xff0c;前面几个项目&#xff0c;有的是可…

手写Spring:第19章-JDBC功能整合

文章目录 一、目标&#xff1a;JDBC功能整合二、设计&#xff1a;JDBC功能整合三、实现&#xff1a;JDBC功能整合3.1 工程结构3.2 整合JDBC功能核心类图3.3 数据源操作3.3.1 数据源操作抽象类3.3.2 JDBC 工具类 3.4 数据库执行3.4.1 语句处理器接口3.4.2 结果处理器接口3.4.3 行…

嵌入式Linux驱动开发(LCD屏幕专题)(四)

单Buffer的缺点与改进方法 1. 单Buffer的缺点 如果APP速度很慢&#xff0c;可以看到它在LCD上缓慢绘制图案 即使APP速度很高&#xff0c;LCD控制器不断从Framebuffer中读取数据来显示&#xff0c;而APP不断把数据写入Framebuffer 假设APP想把LCD显示为整屏幕的蓝色、红色 很…

线程池的实现

目录 一、线程池的实现 1.什么是线程池 2.设计线程类 3.设计线程池类 4.运行 5.RAII加锁改造 二、利用单例模式改造线程池 1.复习 2.饿汉模式 3.懒汉模式 关于系统编程的知识我们已经学完了&#xff0c;最后我们需要利用之前写过的代码实现一个线程池&#xff0c;彻底…

如何理解张量、张量索引、切片、张量维度变换

Tensor 张量 Tensor&#xff0c;中文翻译“张量”&#xff0c;是一种特殊的数据结构&#xff0c;与数组和矩阵非常相似。在 PyTorch 中&#xff0c;使用张量对模型的输入和输出以及模型的参数进行编码。 Tensor 是一个 Python Class。PyTorch 官方文档中定义“Tensor&#xff0…

Datawhale × 和鲸科技丨《2023 中国人工智能人才学习白皮书》发布!

2023 是一个历史性的年份&#xff0c;它标志着人工智能技术的崛起与普及&#xff0c;这一年里&#xff0c;AI 不仅在科技、经济、社会、文化等各个领域取得突破性的进展&#xff0c;也在人类日常生活中扮演愈加重要的角色。随着人工智能时代的加速到来&#xff0c;我国 AI 人才…

msvcp140.dll丢失的有哪些解决方法,丢失msvcp140.dll是什么意思

在我们的日常生活中&#xff0c;电脑问题是无处不在的&#xff0c;而msvcp140.dll丢失又是其中比较常见的一种。msvcp140.dll是Microsoft Visual C运行时库的一部分&#xff0c;它包含了一些重要的函数和资源。当这个文件丢失时&#xff0c;可能会导致电脑出现各种问题&#xf…

链路追踪Skywalking快速入门

目录 1 Skywalking概述1.1 微服务系统监控三要素1.2 什么是链路追踪1.2.1 链路追踪1.2.2 OpenTracing1、数据模型&#xff1a;2、核心接口语义 1.3 常见APM系统1.4 Skywalking介绍1、SkyWalking 核心功能&#xff1a;2、SkyWalking 特点&#xff1a;3、Skywalking架构图&#x…

mysql之DML的select分组排序

目录 一、创建表employee和department表 1.创建department表 2.创建employee表 3.给employee表格和department表格建立外键 4.给department插入数据 5.给employee表插入数据 6.删除名字为那个的数据 二、分组查询和排序查询&#xff0c;以及对数据的处理&#xff08;av…

ARM/X86工业级数据采集 (DAQ) 与控制产品解决方案

I/O设备&#xff0c;包括信号调理模块、嵌入式PCI/PCIE卡、便携式USB模块、DAQ嵌入式计算机、模块化DAQ系统&#xff0c;以及DAQNavi/SDK软件开发包和DAQNavi/MCM设备状态监测软件。 工业I/O产品适用于各种工业自动化应用&#xff0c;从机器自动化控制、测试测量到设备状态监测…

Java“牵手”京东商品详情数据,京东商品详情API接口,京东API接口申请指南

京东平台商品详情接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取京东商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品详情接口API是一种用于获取电商平台上商品详情数据的接口&#xff0c;通过…

kuiper安装

1:使用docker方式安装 docker pull lfedge/ekuiper:latest docker run -p 9081:9081 -d --name kuiper -e MQTT_SOURCE__DEFAULT__SERVERtcp://127.0.0.1:1883 lfedge/ekuiper:latest这样就安装好了&#xff0c;但是操作只能通过命令完成&#xff0c;如果想要通过页面来操作&…