当前位置: 首页 > news >正文

部署并应用ByteTrack实现目标跟踪

尽管YOLOv8已经集成了ByteTrack算法,但在这里我还是想利用ByteTrack官网的代码,自己实现目标跟踪。

要想应用ByteTrack算法,首先就要从ByteTrack官网上下载并安装。虽然官网上介绍得很简单,只需要区区6行代码,但对于国内用户来说,要想安装ByteTrack,只要这些代码是万万不会成功的。我按照复现经典目标跟踪算法ByteTrack之路:调通第一个demo这个网站介绍的安装过程成功地实现了ByteTrack的部署。该博文介绍得很详细,我在这里就不再赘述了。下面我详细介绍如何应用ByteTrack。

我们首先给出ByteTrack的核心关键代码。

导入ByteTrack:

import sys
sys.path.append(f"D:/ByteTrack")
from yolox.tracker.byte_tracker import BYTETracker

D:\ByteTrack为下载ByteTrack时,其所在的目录。

下面设置ByteTrack的参数:

class BYTETrackerArgs:track_thresh: float = 0.25track_buffer: int = 30match_thresh: float = 0.8aspect_ratio_thresh: float = 3.0min_box_area: float = 1.0mot20: bool = False

track_thresh表示跟踪置信阈值。简单地说,该值越大,被赋予目标跟踪ID的数量越少,也就意味着系统会把不太确定的轨迹抛弃掉。默认值为0.5。

track_buffer用于保留丢失轨迹的帧数。对于没有出现的ID,最多保留该值的帧数。默认值为30。

match_thresh表示跟踪匹配阈值。该值越大,目标与轨迹越容易匹配上。默认值为0.8。

aspect_ratio_thresh表示目标边框长宽之比的阈值。目标长宽之比大于该值时会把该目标滤除掉,这是因为长宽比过大时,显然它不会是任何物体。默认值为1.6。

min_box_area表示目标边框的面积阈值。目标面积小于该值时会把该目标滤除掉。默认值为10。

mot20表示是否使用mot20数据集进行测试。默认值为False。

实例化ByteTrack,并带入参数:

byte_tracker = BYTETracker(BYTETrackerArgs(), frame_rate=fps)

frame_rate表示视频每秒传输的帧数。默认值为30。

得到目标ID:

tracks = byte_tracker.update(outputs, img_info=frame.shape, img_size=frame.shape)

outputs表示目标检测器的输出,ByteTrack需要先进行目标检测,然后才能利用ByteTrack算法实现跟踪,outputs应为二维数组,第一维表示目标,第二维表示该目标的信息,其前四个元素为目标边框的左上角和右下角的坐标,第5个元素为该目标的得分值,一般我们可以为该值赋予目标的置信值。

img_info表示输入视频图像的尺寸。

img_size表示输出图像的尺寸,如果不对视频图像的尺寸进行改变的话,就让该值等于img_info。

输出tracks即为目标跟踪的结果,我们先用print(tracks)看看它的输出:

[OT_2_(1-28), OT_3_(1-28), OT_4_(1-28), OT_7_(26-28)]

从中可以看出,我们共得到了4个目标跟踪结果,它们的ID分别为2、3、4和7,其中ID为2的目标在第1帧开始出现,28为当前帧数,即在第28帧时,我们使用了print(tracks)这个代码。

我们再看看tracks的几个重要属性:

print(tracks[0].tlbr)
print(tracks[0].tlwh)
print(tracks[0].track_id)
print(tracks[0].score)

输出为:

[     820.39      184.35      852.77       204.6]
[     820.39      184.35      32.382      20.246]
2
0.7806676

tlbr表示该目标边框的左上角和右下角坐标;tlwh表示该目标边框的左上角坐标和它的长宽;track_id表示该目标的ID;score表示该目标的得分值。

有了目标ID,我们就可以为视频添加各类信息,如为目标添加ID和类别,以及绘制目标边框。我们可以直接利用tracks完成上述操作,但这里会有几个问题:第一由tracks得到的目标边框没有由outputs得到的目标边框准确;第二tracks没有目标类别信息。因此在这里我们还是利用outputs为目标添加各类信息,它要解决的问题是目标的ID是什么。

我们只需比较outputs和tracks的目标尺寸,完成匹配成对,就可以为outputs中的目标赋予ID。我们利用IOU算法来实现尺寸比较,为此我们编写下面函数:

def iou(box: np.ndarray, boxes: np.ndarray):# 计算交集xy_max = np.minimum(boxes[:, 2:], box[2:])xy_min = np.maximum(boxes[:, :2], box[:2])inter = np.clip(xy_max-xy_min, a_min=0, a_max=np.inf)inter = inter[:, 0]*inter[:, 1]# 计算面积area_boxes = (boxes[:, 2]-boxes[:, 0])*(boxes[:, 3]-boxes[:, 1])area_box = (box[2]-box[0])*(box[3]-box[1])return inter/(area_box+area_boxes-inter)

对于这个函数我们不做过多解释,它实现一对多的计算。下面给出它的应用:

for track in tracks:box_iou = iou(track.tlbr, outputs[:,:4])maxindex  = np.argmax(box_iou)newoutput = np.append(outputs[maxindex], track.track_id)print(newoutput)

输出为:

[820.86      184.48      852.67      204.75     0.78067    2    2]
[766.21      212.08      808.44       246.9     0.73741    2    3]
[479.06       178.3      517.84      217.07     0.68729    2    4]
[508.42      147.98      529.87      165.88     0.62819    2    7]

每行的最后一个元素就是它的ID。

ByteTrack严重依赖于目标检测器的准确性。ByteTrack利用每个目标的得分值来计算目标跟踪,并赋予ID。我们一般都是把目标检测得到的置信值作为这个得分值传递给ByteTrack,作为其计算的依据。因此当置信值偏低,并且track_thresh偏高时,会出现tracks得到的目标少于outputs的目标,也就出现了有一些目标没有被赋予ID。

为了减少这类问题出现,我们可以人为的为目标置信值赋予更高的值(充分信任目标检测器),然后再传给ByteTrack,即

for output in outputs:output[4] = 0.95

应用ByteTrack进行目标跟踪的关键部分我们都解释清楚了,下面就给出完整的代码,在这里,我们仍然选择YOLOv8作为目标检测器,除了YOLO易于实现外,另一个原因是它的输出与ByteTrack所要求的数据输入的格式完全相同:

import numpy as np
import cv2
from ultralytics import YOLO
import sys
sys.path.append(f"D:/ByteTrack")
from yolox.tracker.byte_tracker import BYTETrackerclass BYTETrackerArgs:track_thresh: float = 0.25   track_buffer: int = 30   match_thresh: float = 0.8   aspect_ratio_thresh: float = 3.0min_box_area: float = 1.0mot20: bool = False   model = YOLO('yolov8l.pt')cap = cv2.VideoCapture("D:/track/Highway Traffic.mp4")
fps = cap.get(cv2.CAP_PROP_FPS)
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fNUMS = cap.get(cv2.CAP_PROP_FRAME_COUNT)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
videoWriter = cv2.VideoWriter("D:/track/mytrack.mp4", fourcc, fps, size)byte_tracker = BYTETracker(BYTETrackerArgs(),frame_rate= fps)def box_label(image, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)):p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))cv2.rectangle(image, p1, p2, color, thickness=1, lineType=cv2.LINE_AA)if label:w, h = cv2.getTextSize(label, 0, fontScale=2 / 3, thickness=1)[0]  outside = p1[1] - h >= 3p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3cv2.rectangle(image, p1, p2, color, -1, cv2.LINE_AA)cv2.putText(image,label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),0, 2/3, txt_color, thickness=1, lineType=cv2.LINE_AA)def iou(box: np.ndarray, boxes: np.ndarray):xy_max = np.minimum(boxes[:, 2:], box[2:])xy_min = np.maximum(boxes[:, :2], box[:2])inter = np.clip(xy_max-xy_min, a_min=0, a_max=np.inf)inter = inter[:, 0]*inter[:, 1]area_boxes = (boxes[:, 2]-boxes[:, 0])*(boxes[:, 3]-boxes[:, 1])area_box = (box[2]-box[0])*(box[3]-box[1])return inter/(area_box+area_boxes-inter)while cap.isOpened():success, frame = cap.read()if success:        results = model(frame,conf=0.5)outputs = results[0].boxes.data.cpu().numpy()if outputs is not None:for output in outputs:output[4] = 0.95tracks = byte_tracker.update(outputs[:,:5], img_info=frame.shape, img_size=frame.shape)for track in tracks:box_iou = iou(track.tlbr, outputs[:,:4])maxindex  = np.argmax(box_iou)if outputs[maxindex, 5] == 2:box_label(frame, outputs[maxindex], '#'+str(track.track_id)+' car' , (167, 146, 11))elif outputs[maxindex, 5] == 5:box_label(frame, outputs[maxindex], '#'+str(track.track_id)+' bus', (186, 55, 2))elif outputs[maxindex, 5] == 7:box_label(frame, outputs[maxindex], '#'+str(track.track_id)+' truck', (19, 222, 24))cv2.putText(frame, "https://blog.csdn.net/zhaocj", (25, 50),cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)cv2.imshow("ByteTrack", frame)videoWriter.write(frame)if cv2.waitKey(1) & 0xFF == ord("q"):breakelse:breakcap.release()
videoWriter.release()
cv2.destroyAllWindows()

结果为:

ByteTrack

我们也可以再看一个示例:

people

相关文章:

部署并应用ByteTrack实现目标跟踪

尽管YOLOv8已经集成了ByteTrack算法,但在这里我还是想利用ByteTrack官网的代码,自己实现目标跟踪。 要想应用ByteTrack算法,首先就要从ByteTrack官网上下载并安装。虽然官网上介绍得很简单,只需要区区6行代码,但对于国…...

MacOS怎么配置JDK环境变量

1 输入命令看是否配置了JDk 的环境变量:echo $JAVA_HOME 要是什么也没输出 证明是没配置 2 输入命令编辑 sudo vim ~/.bash_profile 然后按 i ,进入编辑模式,粘贴下面的代码,注意:JAVA_HOME后面路径需要改成自己的版…...

Spring Boot 开发16个实用的技巧

当涉及到使用Spring Boot开发应用程序时,以下是16个实用的技巧: 1. **使用Spring Initializr**:Spring Initializr是一个快速创建Spring Boot项目的工具,可以帮助您选择项目依赖和生成项目骨架。 2. **自动配置**:Sp…...

《机器学习实战》学习记录-ch2

PS: 个人笔记&#xff0c;建议不看 原书资料&#xff1a;https://github.com/ageron/handson-ml2 2.1数据获取 import pandas as pd data pd.read_csv(r"C:\Users\cyan\Desktop\AI\ML\handson-ml2\datasets\housing\housing.csv")data.head() data.info()<clas…...

lv7 嵌入式开发-网络编程开发 07 TCP服务器实现

目录 1 函数介绍 1.1 socket函数 与 通信域 1.2 bind函数 与 通信结构体 1.3 listen函数 与 accept函数 2 TCP服务端代码实现 3 TCP客户端代码实现 4 代码优化 5 练习 1 函数介绍 其中read、write、close在IO中已经介绍过&#xff0c;只需了解socket、bind、listen、acc…...

mysql技术文档--阿里巴巴java准则《Mysql数据库建表规约》--结合阿丹理解尝试解读--国庆开卷

阿丹&#xff1a; 国庆快乐呀大家&#xff01; 在项目开始前一个好的设计、一个健康的表关系&#xff0c;不仅会让开发变的有趣舒服&#xff0c;也会在后期的维护和升级迭代中让系统不断的成长。那么今天就认识和解读一下阿里的准则&#xff01;&#xff01; 建表规约 表达是…...

Qt+openCV学习笔记(十六)Qt6.6.0rc+openCV4.8.1+emsdk3.1.37编译静态库

前言&#xff1a; 有段时间没来写文章了&#xff0c;趁编译库的空闲&#xff0c;再写一篇记录文档 WebAssembly的发展逐渐成熟&#xff0c;即便不了解相关技术&#xff0c;web前端也在不经意中使用了相关技术的库&#xff0c;本篇文档记录下如何编译WebAssembly版本的openCV&…...

JUC第十四讲:JUC锁: ReentrantReadWriteLock详解

JUC第十四讲&#xff1a;JUC锁: ReentrantReadWriteLock详解 本文是JUC第十四讲&#xff1a;JUC锁 - ReentrantReadWriteLock详解。ReentrantReadWriteLock表示可重入读写锁&#xff0c;ReentrantReadWriteLock中包含了两种锁&#xff0c;读锁ReadLock和写锁WriteLock&#xff…...

在vue3中使用vite-svg-loader插件

vite-svg-loader插件可以让我们像使用vue组件那样使用svg图&#xff0c;使用起来超级方便。 安装 npm install vite-svg-loader --save-dev使用 import svgLoader from vite-svg-loaderexport default defineConfig({plugins: [vue(), svgLoader()] })组件里使用 在路径后加…...

国庆10.4

QT实现TCP服务器客户端 服务器 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器头文件 #include <QTcpSocket> //客户端头文件 #include <QList> //链表容器 #include <QMe…...

2023/8/12 下午8:41:46 树状控件guilite

2023/8/12 下午8:41:46 树状控件guilite 2023/8/12 下午8:42:08 树状控件(Tree View)是一种常见的图形用户界面(GUI)元素,它通常用于显示层次结构数据或文件系统的目录结构。Guilite 是一个轻量级的跨平台 GUI 库,支持多种控件,包括树状控件。 在 Guilite 中使用树状…...

BL808学习日志-2-LVGL for M0 and D0

一、lvgl测试环境 对拿到的M1S_DOCK开发板进行开发板测试&#xff0c;博流的官方SDK是支持M0和D0两个内核都进行测试的&#xff1b;但是目前只实现了M0的LVGLBenchmark&#xff0c;测试D0内核中发现很多莫名其妙的问题。一会详细记录。 使用的是开发板自带的SPI显示屏&#xff…...

treectrl类封装 2023/8/13 下午4:07:35

2023/8/13 下午4:07:35 treectrl类封装 2023/8/13 下午4:07:53 TreeCtrl 类是一个常用的图形用户界面控件,用于实现树形结构的展示和交互。以下是一个简单的 TreeCtrl 类的封装示例: python import wxclass MyTreeCtrl(wx.TreeCtrl):def __init__(self, parent):super()…...

Android学习之路(20) 进程间通信

IPC IPC为 (Inter-Process Communication) 缩写&#xff0c;称为进程间通信或跨进程通信&#xff0c;指两个进程间进行数据交换的过程。安卓中主要采用 Binder 进行进程间通信&#xff0c;当然也支持其他 IPC 方式&#xff0c;如&#xff1a;管道&#xff0c;Socket&#xff0…...

机器学习——KNN算法流程详解(以iris为例)

、 目 录 前情说明 问题陈述 数据说明 KNN算法流程概述 代码实现 运行结果 基于可视化的改进 可视化代码 全部数据可视化总览 分类投票结果 改进后最终代码 前情说明 本书基于《特征工程入门与入门与实践》庄家盛 译版P53页K最近邻&#xff08;KNN&#xff09;算…...

国庆假期day5

作业&#xff1a;请写出七层模型及每一层的功能&#xff0c;请绘制三次握手四次挥手的流程图 1.OSI七层模型&#xff1a; 应用层--------提供函 表示层--------表密缩 会话层--------会话 传输层--------进程的接收和发送 网络层--------寻主机 数据链路层----相邻节点的可靠传…...

ES6中的let、const

let ES6中新增了let命令&#xff0c;用来声明变量&#xff0c;和var类似但是也有一定的区别 1. 块级作用域 只能在当前作用域内使用&#xff0c;各个作用域不能互相使用&#xff0c;否则会报错。 {let a 1;var b 1; } console.log(a); // 会报错 console.log(b); // 1为什…...

Python 列表操作指南3

示例&#xff0c;将新列表中的所有值设置为 ‘hello’&#xff1a; newlist [hello for x in fruits]表达式还可以包含条件&#xff0c;不像筛选器那样&#xff0c;而是作为操纵结果的一种方式&#xff1a; 示例&#xff0c;返回 “orange” 而不是 “banana”&#xff1a; …...

三个要点,掌握Spring Boot单元测试

单元测试是软件开发中不可或缺的重要环节&#xff0c;它用于验证软件中最小可测试单元的准确性。结合运用Spring Boot、JUnit、Mockito和分层架构&#xff0c;开发人员可以更便捷地编写可靠、可测试且高质量的单元测试代码&#xff0c;确保软件的正确性和质量。 一、介绍 本文…...

【nginx】Nginx配置:

文章目录 一、什么是Nginx&#xff1a;二、为什么使用Nginx&#xff1a;三、如何处理请求&#xff1a;四、什么是正向代理和反向代理&#xff1a;五、nginx 启动和关闭&#xff1a;六、目录结构&#xff1a;七、配置文件nginx.conf&#xff1a;八、location&#xff1a;九、单页…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...

【Java多线程从青铜到王者】单例设计模式(八)

wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本&#xff0c;sleep也是可以指定时间的&#xff0c;也就是说时间一到就会解除阻塞&#xff0c;继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒)&#xff0c;wait能被notify提前唤醒&#xf…...

简单介绍C++中 string与wstring

在C中&#xff0c;string和wstring是两种用于处理不同字符编码的字符串类型&#xff0c;分别基于char和wchar_t字符类型。以下是它们的详细说明和对比&#xff1a; 1. 基础定义 string 类型&#xff1a;std::string 字符类型&#xff1a;char&#xff08;通常为8位&#xff09…...

当下AI智能硬件方案浅谈

背景&#xff1a; 现在大模型出来以后&#xff0c;打破了常规的机械式的对话&#xff0c;人机对话变得更聪明一点。 对话用到的技术主要是实时音视频&#xff0c;简称为RTC。下游硬件厂商一般都不会去自己开发音视频技术&#xff0c;开发自己的大模型。商用方案多见为字节、百…...

spring boot使用HttpServletResponse实现sse后端流式输出消息

1.以前只是看过SSE的相关文章&#xff0c;没有具体实践&#xff0c;这次接入AI大模型使用到了流式输出&#xff0c;涉及到给前端流式返回&#xff0c;所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...

比特币:固若金汤的数字堡垒与它的四道防线

第一道防线&#xff1a;机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”&#xff08;Hashing&#xff09;就是一种军事级的加密术&#xff08;SHA-256&#xff09;&#xff0c;能将信函内容&#xff08;交易细节&#xf…...

宠物车载安全座椅市场报告:解读行业趋势与投资前景

一、什么是宠物车载安全座椅&#xff1f; 宠物车载安全座椅是一种专为宠物设计的车内固定装置&#xff0c;旨在保障宠物在乘车过程中的安全性与舒适性。它通常由高强度材料制成&#xff0c;具备良好的缓冲性能&#xff0c;并可通过安全带或ISOFIX接口固定于车内。 近年来&…...