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

别再只盯着YOLO了!用ByteTrack在Python里实现一个简易的车辆跟踪器(附完整代码)

用PythonByteTrack打造高精度车辆追踪系统从原理到实战在智能交通和视频监控领域目标追踪技术正发挥着越来越重要的作用。当我们需要分析交通流量、统计车辆类型或监测异常行为时仅仅依靠目标检测是远远不够的——我们还需要知道同一个目标在不同帧之间的对应关系。这就是多目标追踪(Multi-Object Tracking, MOT)技术的用武之地。1. 为什么选择ByteTrack进行车辆追踪1.1 目标追踪技术的演进目标追踪算法大致可以分为两类基于检测的追踪(Detection-Based Tracking)和联合检测追踪(Joint Detection and Tracking)。前者如经典的SORT和DeepSORT后者如FairMOT和CenterTrack。ByteTrack属于基于检测的追踪方法但它在处理低分检测框方面做出了创新。传统方法通常会忽略低置信度的检测框认为这些可能是误检。但ByteTrack的作者发现这些低分框中其实包含了很多被遮挡或模糊的真实目标。通过巧妙地利用这些低分框ByteTrack在保持高追踪精度的同时显著降低了ID切换(Identity Switch)的次数。1.2 ByteTrack的核心优势高低分框协同利用不像SORT只使用高分检测框ByteTrack分两阶段利用所有检测结果简单高效无需外观特征提取仅依赖运动信息(IoU)进行关联强鲁棒性对遮挡、模糊等挑战性场景有更好的适应性易于部署算法复杂度低适合实时应用场景下表对比了几种常见追踪算法的特点算法需要外观特征利用低分框典型FPSMOTASORT否否600.59DeepSORT是否400.61FairMOT是部分300.64ByteTrack否是500.662. 环境搭建与依赖安装2.1 创建Python虚拟环境为了避免依赖冲突我们首先创建一个干净的Python环境python -m venv byte-tracker source byte-tracker/bin/activate # Linux/Mac # 或者 byte-tracker\Scripts\activate # Windows2.2 安装必要库ByteTrack的核心依赖包括pip install numpy opencv-python scipy lap loguru pip install torch torchvision # 如果使用GPU请安装对应版本的torch对于目标检测部分我们将使用YOLOv5git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt提示建议使用Python 3.8或更高版本某些库在旧版本中可能存在兼容性问题3. 实现基础车辆追踪器3.1 构建ByteTrack核心类我们先实现ByteTrack的核心逻辑。创建一个byte_tracker.py文件import numpy as np from scipy.spatial.distance import cdist from collections import deque import lap class STrack: # 单个目标的追踪状态管理 def __init__(self, tlwh, score): self._tlwh np.asarray(tlwh, dtypenp.float32) self.kalman_filter None self.mean, self.covariance None, None self.is_activated False self.score score self.tracklet_len 0 self.state TrackState.New def predict(self): # Kalman滤波预测 if self.state ! TrackState.Tracked: return self.mean, self.covariance self.kalman_filter.predict( self.mean, self.covariance) staticmethod def multi_predict(stracks): # 批量预测 if len(stracks) 0: return multi_mean np.asarray([st.mean for st in stracks]) multi_covariance np.asarray([st.covariance for st in stracks]) for i, st in enumerate(stracks): if st.state ! TrackState.Tracked: multi_mean[i] st.mean multi_mean, multi_covariance stracks[0].kalman_filter.multi_predict( multi_mean, multi_covariance) for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)): stracks[i].mean mean stracks[i].covariance cov class ByteTracker: def __init__(self, track_thresh0.5, match_thresh0.8, frame_rate30): self.tracked_stracks [] # 已确认的轨迹 self.lost_stracks [] # 丢失的轨迹 self.removed_stracks [] # 移除的轨迹 self.frame_id 0 self.track_thresh track_thresh self.match_thresh match_thresh self.buffer_size int(frame_rate / 30.0 * 30) self.max_time_lost self.buffer_size def update(self, detections): self.frame_id 1 activated_stracks [] refind_stracks [] lost_stracks [] removed_stracks [] # 将检测结果分为高分和低分 scores np.array([d.score for d in detections]) remain_inds scores self.track_thresh inds_low scores 0.1 inds_high scores self.track_thresh inds_second np.logical_and(inds_low, inds_high) dets [detections[i] for i in range(len(detections)) if remain_inds[i]] dets_second [detections[i] for i in range(len(detections)) if inds_second[i]] # 第一步与高分检测框匹配 strack_pool joint_stracks(self.tracked_stracks, self.lost_stracks) STrack.multi_predict(strack_pool) dists matching.iou_distance(strack_pool, dets) matches, u_track, u_detection matching.linear_assignment(dists, threshself.match_thresh) # 更新匹配成功的轨迹 for itracked, idet in matches: track strack_pool[itracked] det dets[idet] if track.state TrackState.Tracked: track.update(det, self.frame_id) activated_stracks.append(track) else: track.re_activate(det, self.frame_id, new_idFalse) refind_stracks.append(track) # 第二步与低分检测框匹配 if len(dets_second) 0: 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, dets_second) matches, u_track, u_detection_second matching.linear_assignment(dists, thresh0.5) for itracked, idet in matches: track r_tracked_stracks[itracked] det dets_second[idet] if track.state TrackState.Tracked: track.update(det, self.frame_id) activated_stracks.append(track) else: track.re_activate(det, self.frame_id, new_idFalse) refind_stracks.append(track) # 处理未匹配的轨迹 for it in u_track: track r_tracked_stracks[it] if not track.state TrackState.Lost: track.mark_lost() lost_stracks.append(track) # 更新轨迹状态 self.tracked_stracks [t for t in self.tracked_stracks if t.state TrackState.Tracked] self.tracked_stracks joint_stracks(self.tracked_stracks, activated_stracks) self.tracked_stracks joint_stracks(self.tracked_stracks, refind_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) output_stracks [track for track in self.tracked_stracks if track.is_activated] return output_stracks3.2 集成YOLOv5检测器创建一个detector.py文件来封装YOLOv5检测器import torch from yolov5.models.experimental import attempt_load from yolov5.utils.general import non_max_suppression class YOLOv5Detector: def __init__(self, weights_pathyolov5s.pt, devicecuda:0): self.device torch.device(device) self.model attempt_load(weights_path, map_locationself.device) self.names self.model.module.names if hasattr(self.model, module) else self.model.names self.img_size 640 def detect(self, img, conf_thres0.25, iou_thres0.45): # 预处理 img torch.from_numpy(img).to(self.device) img img.float() / 255.0 if img.ndimension() 3: img img.unsqueeze(0) # 推理 pred self.model(img, augmentFalse)[0] # NMS pred non_max_suppression(pred, conf_thres, iou_thres, classesNone, agnosticFalse) # 后处理 detections [] for i, det in enumerate(pred): if det is not None and len(det): for *xyxy, conf, cls in det: x1, y1, x2, y2 [x.item() for x in xyxy] detections.append({ bbox: [x1, y1, x2, y2], score: conf.item(), class_id: int(cls), class_name: self.names[int(cls)] }) return detections4. 完整视频处理流程实现4.1 主程序框架创建一个main.py文件作为入口import cv2 import numpy as np from detector import YOLOv5Detector from byte_tracker import ByteTracker def process_video(input_path, output_path): # 初始化 detector YOLOv5Detector() tracker ByteTracker(track_thresh0.5, match_thresh0.8) cap cv2.VideoCapture(input_path) width int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps cap.get(cv2.CAP_PROP_FPS) fourcc cv2.VideoWriter_fourcc(*mp4v) out cv2.VideoWriter(output_path, fourcc, fps, (width, height)) frame_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break # 检测 detections detector.detect(frame) # 过滤只保留车辆类 (COCO数据集中车辆类ID为2,5,7) vehicle_dets [d for d in detections if d[class_id] in [2,5,7]] # 追踪 tracks tracker.update(vehicle_dets) # 可视化 for track in tracks: bbox track.tlwh cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[0]bbox[2]), int(bbox[1]bbox[3])), (0,255,0), 2) cv2.putText(frame, fID:{track.track_id}, (int(bbox[0]), int(bbox[1]-10)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2) out.write(frame) frame_count 1 print(fProcessed frame {frame_count}) cap.release() out.release() if __name__ __main__: process_video(input.mp4, output.mp4)4.2 参数调优指南ByteTrack的性能很大程度上依赖于几个关键参数检测阈值(track_thresh)默认0.5值越高误检越少但可能漏检交通场景建议0.4-0.6之间匹配阈值(match_thresh)默认0.8控制轨迹与检测框的匹配严格程度对于高速移动车辆可降低到0.7丢失帧数(buffer_size)默认30帧(1秒30fps)对于遮挡频繁的场景可适当增加注意这些参数需要根据具体场景进行调整建议先用小段视频测试不同参数组合的效果5. 高级功能扩展5.1 轨迹分析与统计我们可以扩展追踪器来收集交通统计数据class TrafficAnalyzer: def __init__(self): self.vehicle_count 0 self.vehicle_types {} self.speed_estimator {} def update(self, tracks, frame_id, fps): for track in tracks: if track.track_id not in self.speed_estimator: self.vehicle_count 1 self.speed_estimator[track.track_id] { prev_pos: track.tlwh[:2], prev_frame: frame_id, speed: 0 } else: # 计算速度 (像素/秒) curr_pos track.tlwh[:2] prev_pos self.speed_estimator[track.track_id][prev_pos] frames_passed frame_id - self.speed_estimator[track.track_id][prev_frame] if frames_passed 0: distance np.sqrt((curr_pos[0]-prev_pos[0])**2 (curr_pos[1]-prev_pos[1])**2) self.speed_estimator[track.track_id][speed] distance * fps / frames_passed self.speed_estimator[track.track_id][prev_pos] curr_pos self.speed_estimator[track.track_id][prev_frame] frame_id5.2 跨摄像头追踪要实现跨摄像头的车辆追踪我们需要考虑外观特征提取虽然ByteTrack本身不依赖外观特征但可以额外添加时空约束不同摄像头之间的车辆出现时间和位置关系轨迹匹配当车辆从一个摄像头视野进入另一个时进行ID传递class MultiCameraTracker: def __init__(self): self.camera_trackers {} # 每个摄像头独立的追踪器 self.global_tracks {} # 全局轨迹ID映射 def add_camera(self, cam_id, calib_paramsNone): self.camera_trackers[cam_id] { tracker: ByteTracker(), calib: calib_params, last_global_id: 0 } def update(self, cam_id, detections): local_tracks self.camera_trackers[cam_id][tracker].update(detections) # 将本地ID映射到全局ID global_tracks [] for track in local_tracks: if track.track_id not in self.camera_trackers[cam_id][local_to_global]: self.camera_trackers[cam_id][last_global_id] 1 global_id self.camera_trackers[cam_id][last_global_id] self.camera_trackers[cam_id][local_to_global][track.track_id] global_id self.global_tracks[global_id] { last_cam: cam_id, last_seen: time.time() } global_id self.camera_trackers[cam_id][local_to_global][track.track_id] global_tracks.append({ global_id: global_id, bbox: track.tlwh, features: self.extract_features(track) }) return global_tracks在实际交通监控项目中ByteTrack展现出了令人印象深刻的性能。特别是在处理车辆相互遮挡的场景时其高低分框协同匹配的策略显著减少了ID切换。一个实用的建议是对于固定摄像头场景可以预先标定场景中的消失点和尺度信息这将有助于更准确地估计车辆速度和行为分析。

相关文章:

别再只盯着YOLO了!用ByteTrack在Python里实现一个简易的车辆跟踪器(附完整代码)

用PythonByteTrack打造高精度车辆追踪系统:从原理到实战 在智能交通和视频监控领域,目标追踪技术正发挥着越来越重要的作用。当我们需要分析交通流量、统计车辆类型或监测异常行为时,仅仅依靠目标检测是远远不够的——我们还需要知道同一个目…...

开源PLC编程全指南:从入门到实战的工业控制开发详解

开源PLC编程全指南:从入门到实战的工业控制开发详解 【免费下载链接】OpenPLC_Editor 项目地址: https://gitcode.com/gh_mirrors/ope/OpenPLC_Editor 在工业自动化领域,开源技术正逐步打破传统PLC(可编程逻辑控制器)的封…...

STM32F103 CAN通信调试踩坑记:从时钟频率到波特率计算的实战避坑指南

STM32F103 CAN通信调试实战:时钟频率与波特率计算的深度解析 当你在调试STM32F103的CAN通信时,是否遇到过这样的场景:两块开发板硬件连接正确,代码逻辑看似无误,但通信就是无法建立?这往往是由于时钟频率差…...

GoJieba关键词提取教程:TextRank算法与权重计算原理

GoJieba关键词提取教程:TextRank算法与权重计算原理 【免费下载链接】gojieba "结巴"中文分词的Golang版本 项目地址: https://gitcode.com/gh_mirrors/go/gojieba GoJieba作为"结巴"中文分词的Golang版本,提供了高效的中文处…...

BeRoot项目架构揭秘:模块化设计的权限提升检测系统

BeRoot项目架构揭秘:模块化设计的权限提升检测系统 【免费下载链接】BeRoot Privilege Escalation Project - Windows / Linux / Mac 项目地址: https://gitcode.com/gh_mirrors/be/BeRoot BeRoot是一款强大的跨平台权限提升检测系统,支持Windows…...

Windows服务器上Veritas NetBackup 10.1保姆级安装指南(含用户权限配置避坑)

Windows服务器上Veritas NetBackup 10.1保姆级安装指南(含用户权限配置避坑) 在企业级数据备份领域,Veritas NetBackup一直是行业标杆级解决方案。作为一款成熟的企业级备份软件,NetBackup 10.1版本在Windows服务器环境下的安装配…...

Kettle新手必看:从零开始安装配置Pentaho Data Integration(附MySQL驱动避坑指南)

Kettle实战入门:从零搭建ETL开发环境与MySQL连接全攻略 开篇:为什么选择Kettle作为你的第一个ETL工具? 第一次接触数据集成领域时,面对五花八门的ETL工具列表,很多开发者都会感到无从下手。作为一个从传统数据库管理…...

别再手动截图了!用Java POI把商品图片和详情一键导出到Excel(附完整工具类)

电商后台自动化利器:Java POI实现商品图文一键导出Excel实战指南 每次运营同事催你要商品数据报表时,还在手工复制粘贴图片吗?作为经历过这种折磨的开发者,我深知电商系统中商品信息导出的痛点——尤其是当需要将主图、详情图等多…...

FreeRTOS信号量卡死?STM32CubeMX这个坑我帮你踩过了

FreeRTOS信号量卡死问题深度解析与STM32CubeMX最佳实践 1. 问题现象与初步排查 在嵌入式开发中,FreeRTOS与STM32CubeMX的组合堪称黄金搭档,但这对组合也暗藏玄机。最近遇到一个诡异现象:首次下载程序后系统无响应,二次下载却能正…...

OpenClaw异常处理:Qwen2.5-VL-7B任务中断自动恢复方案

OpenClaw异常处理:Qwen2.5-VL-7B任务中断自动恢复方案 1. 当自动化遇上不稳定:我的深夜崩溃实录 凌晨2点17分,我的显示器突然亮起——OpenClaw正在执行的周报生成任务中断了。这个本该在后台安静运行的自动化流程,因为Qwen2.5-V…...

Windows更新修复神器:Reset Windows Update Tool智能诊断与深度修复全攻略

Windows更新修复神器:Reset Windows Update Tool智能诊断与深度修复全攻略 【免费下载链接】Reset-Windows-Update-Tool Troubleshooting Tool with Windows Updates (Developed in Dev-C). 项目地址: https://gitcode.com/gh_mirrors/re/Reset-Windows-Update-To…...

Cursor Pro免费激活终极指南:三步实现AI编程助手无限使用

Cursor Pro免费激活终极指南:三步实现AI编程助手无限使用 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached your …...

终极免费图像浏览器:ImageGlass的90+格式支持与专业体验完整指南

终极免费图像浏览器:ImageGlass的90格式支持与专业体验完整指南 【免费下载链接】ImageGlass 🏞 A lightweight, versatile image viewer 项目地址: https://gitcode.com/gh_mirrors/im/ImageGlass 你是否经常遇到Windows自带照片查看器无法打开专…...

从Gradio报错到成功对话:LLaVA-v1.5-7b网页端部署的保姆级排错指南

从Gradio报错到成功对话:LLaVA-v1.5-7b网页端部署的保姆级排错指南 当你终于完成LLaVA-v1.5-7b模型的基础部署,准备在网页端大展身手时,Gradio界面却给你泼了一盆冷水——各种报错接踵而至。别担心,这不是你一个人的战斗。本文将带…...

010 Editor破解指南:从安装到激活的完整步骤

1. 010 Editor简介与破解前的准备 010 Editor是一款功能强大的十六进制编辑器,广泛应用于逆向工程、文件分析和数据恢复等领域。它的二进制编辑能力和模板解析功能深受安全研究人员和开发者的喜爱。不过正版软件价格较高,个人用户可能会考虑寻找替代方案…...

【研报291】2026年全球新车研究:超跑与高端新车动态

本报告提供限时下载,请查看文后提示以下仅为报告部分内容:摘要:2026 年全球汽车行业迎来密集的新车发布周期,涵盖豪华超跑、主流纯电车型、入门平价电动车等全品类产品,包括宝马新 NEUE KLASSE 平台车型、保时捷纯电卡…...

tao-8k嵌入模型实战效果:基于Xinference的文本聚类与去重案例

tao-8k嵌入模型实战效果:基于Xinference的文本聚类与去重案例 1. 引言:从海量文本中快速找到“同类项” 想象一下,你手头有成千上万条用户评论、新闻摘要或产品描述。你想知道哪些内容是相似的,哪些是重复的,或者想把…...

【研报290】通宝光电深度报告:汽车电子的升级之路

本报告提供限时下载,请查看文后提示以下仅为报告部分内容:摘要:通宝光电作为国家级专精特新小巨人企业,从 LED 封装起步,深耕汽车车灯领域二十余年,牵头起草了 LED 车灯行业标准,依托光学、热学…...

B站字幕高效解决方案:从下载到应用的全流程指南

B站字幕高效解决方案:从下载到应用的全流程指南 【免费下载链接】BiliBiliCCSubtitle 一个用于下载B站(哔哩哔哩)CC字幕及转换的工具; 项目地址: https://gitcode.com/gh_mirrors/bi/BiliBiliCCSubtitle 1. 解决B站字幕获取难题 在视频内容消费日益增长的今…...

音乐标签管理革命:3个步骤让你的本地音乐库焕然一新

音乐标签管理革命:3个步骤让你的本地音乐库焕然一新 【免费下载链接】music-tag-web 音乐标签编辑器,可编辑本地音乐文件的元数据(Editable local music file metadata.) 项目地址: https://gitcode.com/gh_mirrors/mu/music-ta…...

Understat:突破足球数据壁垒的异步采集方案 | 开发者实战指南

Understat:突破足球数据壁垒的异步采集方案 | 开发者实战指南 【免费下载链接】understat An asynchronous Python package for https://understat.com/. 项目地址: https://gitcode.com/gh_mirrors/un/understat 问题发现:足球数据采集的隐形障碍…...

3分钟掌握PHP高效IP地址定位技巧:ip2region完全使用指南

3分钟掌握PHP高效IP地址定位技巧:ip2region完全使用指南 【免费下载链接】ip2region PHP版本的离线IP地址定位库 项目地址: https://gitcode.com/gh_mirrors/ip2/ip2region 在当今互联网应用中,IP地址定位是许多业务场景的基础功能,无…...

3步解锁Cursor Pro:面向开发者的AI编程助手无限使用指南

3步解锁Cursor Pro:面向开发者的AI编程助手无限使用指南 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reached your t…...

SAP ME21N采购订单增强报错?手把手教你调试ME_PROCESS_PO_CUST(附完整代码)

SAP ME21N采购订单增强报错全流程诊断指南 当SAP系统中的ME21N采购订单增强突然报错时,那种面对红色错误消息却无从下手的挫败感,每个ABAP开发者都深有体会。不同于普通的程序错误,ME_PROCESS_PO_CUST这类标准增强点的报错往往涉及采购订单核…...

保姆级教程:在Ubuntu上为龙芯2K0300开发板配置交叉编译环境(含完整测试流程)

保姆级教程:在Ubuntu上为龙芯2K0300开发板配置交叉编译环境(含完整测试流程) 刚拿到龙芯2K0300开发板的开发者们,往往会在环境配置阶段遇到各种"拦路虎"。本文将手把手带你完成从工具链部署到"Hello World"验…...

从VARCHAR到NVARCHAR2:MySQL表结构迁移OpenGauss必须掌握的10个数据类型转换细节

从VARCHAR到NVARCHAR2:MySQL表结构迁移OpenGauss必须掌握的10个数据类型转换细节 在数据库国产化浪潮中,将MySQL迁移至OpenGauss已成为许多企业的技术刚需。作为PostgreSQL系数据库的代表,OpenGauss在语法规则、存储机制等方面与MySQL存在显著…...

实战:用C语言为嵌入式Linux设备(如NVIDIA Jetson)编写蓝牙SPP数据透传服务

实战:用C语言为嵌入式Linux设备(如NVIDIA Jetson)编写蓝牙SPP数据透传服务 在工业物联网和智能硬件开发中,蓝牙串口协议(SPP)因其低功耗、稳定可靠的特点,成为设备间无线通信的首选方案之一。想…...

Android设备唯一标识终极指南:从IMEI到OAID的完整解决方案(附代码)

Android设备唯一标识终极指南:从IMEI到OAID的完整解决方案(附代码) 在移动应用开发中,设备唯一标识是许多业务场景的基础需求——从用户设备绑定、反作弊系统到精准数据分析都离不开它。但Android生态的碎片化让这个"简单&qu…...

【NOIP】1999真题解析 luogu-P1015 回文数 | GESP四、五级以上可练习

NOIP 1999 普及组真题,主要考察字符串处理、高精度加法以及任意进制的进位规则。解题的核心是将数字看作字符串处理,在循环累加中验证回文特征。适合GESP四、五级以上考生练习。题目难度⭐⭐☆☆☆,洛谷难度等级普及−。 luogu-P1015 [NOIP …...

Cadence IC618/Spectre231安装避坑指南:详解License配置、环境变量隔离与依赖检查

Cadence IC618/Spectre231深度配置实战:从环境隔离到长期稳定运行的进阶指南 在芯片设计领域,Cadence工具链的稳定运行直接关系到项目进度与设计质量。许多工程师在完成基础安装后,常会遇到许可证报错、环境冲突、工具崩溃等"疑难杂症&q…...