部署并应用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

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

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

相关文章

【计算机网络笔记八】应用层(五)HTTPS

什么是 HTTPS HTTPS 解决了 HTTP 不安全的问题 HTTP 整个传输过程数据都是明文的,任何人都能够在链路中截获、修改或者伪造请求/响应报文,数据不具有可信性。 ① HTTPS 使用加密算法对报文进行加密,黑客截获了也看不懂 ② HTTP…

Play Beyond:Sui让优秀的游戏变得更好

自问世以来,视频游戏就紧随着文化产业发展。从Pong和Space Invaders的时代到Animal Crossing和Among Us,伟大的游戏总有能力吸引玩家,并推动娱乐产业发展。根据Grand View Research的数据,全球视频游戏市场在2022年估计为2170.6亿…

CUDA C编程权威指南:1.1-CUDA基础知识点梳理

主要整理了N多年前(2013年)学习CUDA的时候开始总结的知识点,好长时间不写CUDA代码了,现在LLM推理需要重新学习CUDA编程,看来出来混迟早要还的。 1.CUDA 解析:2007年,NVIDIA推出CUDA&#xff08…

java练习 day5

一、Nim 游戏 1、题目链接 点击跳转到题目位置 2、代码 class Solution {public boolean canWinNim(int n) {if(n % 4 0){return false;}return true;} }3、知识点 (1) 通过模拟来寻找 规律。 二、区域和检索 - 数组不可变 1、题目链接 点击跳转到题目位置 2、代码 …

Docker 日志管理 - ELK

Author:rab 目录 前言一、Docker 日志驱动二、ELK 套件部署三、Docker 容器日志采集3.1 部署 Filebeat3.2 配置 Filebeat3.3 验证采集数据3.4 Kibana 数据展示3.4.1 创建索引模式3.4.2 Kibana 查看日志 总结 前言 如何查看/管理 Docker 运行容器的日志?…

.netcore worker service在一个worker里执行多个任务的方法

最近,有一个项目要使用 Windows 服务,来做为一个软件项目的载体。我想了想,都已经到了跨平台的时代了,会不会有替换 Windows 服务的技术出现呢?于是,在网络上疯狂的搜索了一番,真实皇天不负苦心…

图像拼接后丢失数据,转tiff报错rasterfile failed: an unknown

图像拼接后丢失数据 不仅是数据丢失了,还有个未知原因报错 部分数据存在值不存在的情况 原因 处理遥感数据很容易,磁盘爆满了 解决方案 清理一些无用数据,准备买个2T的外接硬盘用着了。 然后重新做处理

redis高可用(主从复制,哨兵,集群)

目录 一、主从复制: 1.主从复制介绍: 2.主从复制的作用: 3.主从复制流程: 4.搭建Redis 主从复制: 4.1 环境准备: 4.2 安装redis: 4.3 master节点修改 Redis 配置文件: 4.4 slave节点…

Linux学习之悟空派上实现OLED的无线网IP及CPU温度显示【守护进程】

起因 最近各种网购平台似乎都在推送99元的悟空派全志H3的开发板,出于好奇就买了一块来试试水,由于这块板子基本上和orangepi-Zero的硬件结构一模一样,所以设备树、boot这些就用orangepi现成的部件了。 因为本人比较喜欢使用SSH操作&#xff…

Java方法案例

设计一个方法用于遍历数组,要求遍历的结果是在一行上的。例如:[11,22,33,44,55] public class index {public static void main(String[] args) {int[] arr {11,22,33,44,55};printArr(arr);}public static void printArr(int[] arr){System.out.print…

C++——list(2)

作者:几冬雪来 时间:2023年9月28日 内容:C——list内容讲解 目录 前言: list的const迭代器: const的iterator: const迭代器: operator->: 拷贝构造: 迭代器接口补充&…

QT实验之闪烁灯

QT实验之闪烁灯 在QT中,使用QPainter来实现闪烁灯效果的一种方法是使用QTimer来周期性地改变灯的状态。 首先,你需要一个QPixmap对象,它包含了灯的图片。然后,使用QTimer来周期性地切换灯的状态。当灯的状态改变时,你需…

(c语言)位操作符

#include<stdio.h> //位操作符(只针对整数进行操作) //& - 按(二进制)位与 (有0则0) //| - 按(二进制)位或 (有1则1) //^ - 按(二进制)位异或 (不同为1,相同为0) int main() { int a 3; int b -5; //00000000000000000000000000000011——3的补码 …

【408复习】在b站开播通知

从今天开始制作408的讲解课程&#xff0c;欢迎大家来围观&#xff1b; 视频讲解的方式&#xff0c;在b站讲解&#xff0c; 账号&#xff1a;JustinAndCindy 参考书目&#xff1a; 操作系统 《计算机操作系统》第四版 汤小丹 数据结构 《无》 计算机网络 谢希仁 计算机组成原理 …

torch.randn()函数

torch.randn函数是PyTorch中用于生成具有正态分布&#xff08;均值为0&#xff0c;标准差为1&#xff09;的随机数的函数。它可以用于创建具有指定形状的张量&#xff0c;并且张量中的每个元素都是独立的随机数&#xff0c;遵循标准正态分布&#xff08;均值为0&#xff0c;标准…

【LittleXi】【MIT6.S081-2020Fall】Lab: locks

【MIT6.S081-2020Fall】Lab: locks 【MIT6.S081-2020Fall】Lab: locks内存分配实验内存分配实验准备实验目的1. 举一个例子说明修改前的**kernel/kalloc.c**中如果没有锁会导致哪些进程间竞争(races)问题2. 说明修改前的kernel/kalloc.c中锁竞争contention问题及其后果3. 解释a…

Elasticsearch安装访问

Elasticsearch 是一个开源的、基于 Lucene 的分布式搜索和分析引擎&#xff0c;设计用于云计算环境中&#xff0c;能够实现实时的、可扩展的搜索、分析和探索全文和结构化数据。它具有高度的可扩展性&#xff0c;可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个…

【云笔记篇】Microsoft OneNote笔记插件推荐OneMore

【云笔记篇】Microsoft OneNote笔记插件推荐OneMore OneMore插件是一款非常强大&#xff0c;多达一百多个扩展功能的OneNote笔记插件&#xff0c;而且免费开源&#xff0c;不断更新的优秀插件—【蘇小沐】 1、实验 【OneMore官网&#xff1a;OneMore - a OneNote add-in (on…

C++——类和对象

文章目录 1.面向过程和面向对象的区别2.类的定义3.类的特点3.1封装性3.2继承性3.3多态性 4.类的访问限定符及封装4.1访问限定符4.2封装 5.类的作用域6.类的实例化7.计算类对象大小8.this指针8.1this指针的引出8.2this指针的特性 9.类的6个默认成员函数9.1默认构造函数9.1.1概念…