ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python+YOLOv5+DeepSORT实现车流量统计与轨迹显示系统

Python+YOLOv5+DeepSORT实现车流量统计与轨迹显示系统 简介本资源是一套基于Python实现的YOLOv5与DeepSORT融合的车流量统计与轨迹可视化完整源码工程面向智能交通、计算机视觉初学者及算法工程师解决视频流中车辆检测、ID追踪、跨线计数与运动轨迹绘制等核心问题。压缩包共169个文件包含55个核心Python脚本含模型加载、推理、跟踪、统计与绘图模块、21个YAML配置文件定义模型参数、追踪阈值与区域设置、61个pyc编译文件、3个MP4测试视频及2个预训练.pt模型整体大小为133.44MB。已有2841人学习下载资源结构清晰涵盖从数据预处理、YOLOv5检测、DeepSORT多目标追踪到轨迹热力图与计数曲线可视化的全流程代码附带README说明、LICENSE授权文件及Dockerfile容器化支持便于快速部署与二次开发。 做车流量统计这个需求我这两年被问过不下几十次。大多数刚入门的朋友拿YOLOv5对单帧图片做检测数出画面里几个框就以为实现了车流统计结果一放到真实视频里要么同一辆车被反复计数要么车一被遮挡跟踪就断了统计出来的数据完全没法用。这篇文章要讲的是真正能落地的做法用Python把YOLOv5和DeepSORT组合起来做一套完整的车流量统计与轨迹显示系统。它解决的不只是“车在哪”的问题而是“同一辆车从进入画面到离开画面”的完整跟踪包括目标检测、目标跟踪、轨迹记录、虚拟线圈计数几个核心环节。适合已经跑通基础目标检测、想往多目标跟踪方向进阶的开发者参考也适合毕业设计、课题研究或者实际项目预研直接用。这篇文章会从原理讲到代码再到实测调优的坑整体偏工程实践不是纯理论科普。我尽量把关键决策背后的理由说清楚这样你做完这套系统之后遇到换场景、换数据集的情况也知道该从哪里下手改。1. 车流统计为什么不能只靠“单帧检测”1.1 单帧检测解决不了的跨帧问题先厘清一个概念车流统计的标准定义是“单位时间内通过某个断面或区域的车辆数”。它天然是一个视频任务不是单张图片任务。单帧检测能给你什么就是当前这一刻画面里有几辆车、每辆车的位置框、置信度、类别。但如果你把视频每一帧都独立检测一遍然后直接把框的数量加总结果会非常离谱。原因很简单一辆车在视频里会连续出现几十上百帧每一帧它都是一个独立的检测结果逐帧累加等于把同一辆车数了几十遍。更麻烦的是你没法知道第10帧里出现在画面中央的那辆白色轿车是不是就是第9帧画面左侧的那辆。车辆的位置是连续的但每一帧的检测框之间没有绑定关系这种“谁是同一个目标”的问题单靠检测器无法回答。这是所有视频分析类项目都要面对的核心问题跨帧数据关联。1.2 YOLOv5负责“看见”DeepSORT负责“记住”这套系统的标准分工是YOLOv5做目标检测输出每一帧中车辆的边界框、置信度、类别DeepSORT做多目标跟踪把这些检测结果按时间顺序关联起来给每个目标分配一个唯一ID。你可以这样理解YOLOv5是眼睛负责在每一帧画面里找出“哪里有车”DeepSORT是短期记忆负责判断“这辆车我上一帧见过没有它现在跑到哪里去了”。有了这个ID你才能做后续一系列事情统计这辆车有没有通过检测线、它从画面上方开过来还是从下方开过来、它的行驶轨迹是什么样。DeepSORT的名字来自SORTSimple Online and Realtime TrackingDeep前缀代表它引入了外观特征ReID特征来做目标重识别。这个能力非常关键当车辆被遮挡后又出现或者检测框短暂丢失时它可以通过外观特征把同一个目标重新认出来避免ID跳变。1.3 这套系统的完整功能清单跑完这套系统你最终能得到类似下面的输出视频画面中每辆车的实时检测框框上标注类别和置信度每个目标持续稳定的唯一ID从进入画面到离开画面不会随意变化每条轨迹的历史路径点连线也就是轨迹显示检测线或检测区域的过车计数区分通行方向可选的统计数据导出比如每分钟通过多少辆车。这套输出的应用场景很直接城市路口流量统计、高速公路断面车流量采集、园区出入车辆记录、停车场车位区域车辆进出分析基本都能覆盖。2. 系统整体架构YOLOv5与DeepSORT的分工逻辑2.1 主流程检测、关联、计数、绘制整个系统的主流程可以用一段伪代码非常清晰地表达while 视频未结束: 读取一帧画面 YOLOv5 检测 - 得到候选车辆框 过滤出车辆类别car, bus, truck, motorcycle DeepSORT 关联 - 得到带ID的跟踪结果 虚拟检测线判断 - 更新计数 绘制检测框、ID、轨迹线、统计信息 写入输出视频听起来不复杂但每个环节都藏了不少细节。检测环节要考虑置信度阈值、类别过滤关联环节要处理检测框和跟踪轨迹的匹配问题计数环节要防止重复计数、识别通行方向绘制环节要管理轨迹点数、颜色分配、绘制顺序。任何一环出了问题整体效果都会很糟糕。2.2 DeepSORT内部到底做了什么DeepSORT的核心是一个状态预测加数据关联的过程我尽量用直白的话拆开讲。第一步是卡尔曼滤波预测。每个正在跟踪的目标系统会用一个状态向量描述它的位置和速度卡尔曼滤波根据目标的历史运动规律预测它在下一帧可能出现的位置和大小。这个预测不是瞎猜而是建立在“车辆运动是连续且平滑的”这个假设上。第二步是外观特征提取。检测器给出的每个新检测框会通过一个ReID特征提取网络生成一个特征向量。这个向量相当于目标的“外貌指纹”用来在目标丢失后重新识别它。第三步是数据关联。DeepSORT会把卡尔曼滤波预测的位置和实际检测框的位置做代价计算把外观特征距离也纳入代价矩阵然后用匈牙利算法求解最优匹配。匹配结果分成三类匹配上的目标更新状态没有匹配上的检测框作为新目标初始化长时间没有匹配上的跟踪轨迹则被删除。这三步每帧都执行一遍。所以DeepSORT的计算量不是一次性任务而是随着视频帧数线性累加这也是为什么在CPU上跑容易掉帧的原因之一。2.3 为什么选YOLOv5配DeepSORT而不是其他方案先说结论这套组合不一定是最先进的但一定是上手最快、参考资料最全、最容易改造成实际项目的方案之一。YOLOv5的生态成熟度在目标检测领域是数一数二的。托chub加载、自定义数据集训练、ONNX和TensorRT导出每一步都有大量现成案例。相比YOLOv8v5的老项目积累更多很多做跟踪的老代码都基于v5的接口改起来省事。DeepSORT也类似虽然现在ByteTrack、OC-SORT这些新方案在某些场景下效果更好但DeepSORT的ReID外观特征在车辆被遮挡后恢复跟踪的能力依然很强而且代码逻辑清晰、易于二次开发对于学习和工程落地都是合适的选择。如果你的项目对精度要求极高可以考虑把检测器换成YOLOv8或RT-DETR跟踪器换成StrongSORT或BoT-SORT但整体架构思路是共通的。真正理解了这套组合换组件对你来说只是接口替换的问题。3. 环境搭建与版本选型跑通系统的完整避坑记录3.1 Python版本和虚拟环境怎么选兼容性最好的Python版本是3.8或3.9。不要图新用3.12部分依赖库比如lap、scipy在Windows下对老版本支持更稳定编译问题会少很多。我建议用conda或者uv创建独立环境避免把系统Python搞乱。conda create -n traffic python3.9 -y conda activate traffic如果你用的是uv命令会更简洁uv venv traffic --python 3.9 source traffic/bin/activate用独立环境的好处是不同项目的依赖互相隔离装坏了直接删掉重来不用折腾系统环境。这一步非常建议新手不要跳过。3.2 PyTorch和CUDA版本对应关系PyTorch的安装是最容易出问题的环节核心原则是先确认你的显卡驱动支持哪个CUDA版本再装对应版本的PyTorch。不需要装完整的CUDA ToolkitPyTorch自带的cudatoolkit就够用了。以PyTorch 2.0为例官方安装命令可以直接在PyTorch官网生成。CPU版本和GPU版本的命令不一样一定要看清。# GPU版本示例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # CPU版本示例 pip install torch torchvision装完之后强烈建议验证一下GPU是否可用python -c import torch; print(torch.cuda.is_available())输出True说明GPU可用False则说明装成了CPU版本或者CUDA版本不匹配。这一步验证能帮你排除后面一半以上的环境问题。3.3 源码结构、依赖安装与模型权重这个项目需要两个代码来源一个是YOLOv5仓库一个是DeepSORT的PyTorch实现。结构上建议这样组织traffic/ ├── yolov5/ # YOLOv5官方仓库 │ ├── weights/ │ │ └── yolov5s.pt │ └── ... ├── deep_sort/ # DeepSORT PyTorch实现 │ ├── deep_sort_pytorch/ │ │ ├── configs/ │ │ │ └── deep_sort.yaml │ │ └── ... └── main.py # 主程序依赖安装方面YOLOv5自带的requirements.txt基本覆盖了检测部分DeepSORT还需要额外装scipy、lap等包。Windows下lap包偶尔会遇到编译失败一个比较省事的办法是直接找预编译的wheel包安装或者用conda安装conda install -c conda-forge lap模型权重方面YOLOv5官方预训练权重yolov5s.pt是在COCO数据集上训练的COCO里面本身就包含car、bus、truck、motorcycle这些车辆类别。也就是说只要你的应用场景是常规道路车辆完全不需要自己训练模型直接用预训练权重就可以跑起来。只有当你需要识别特殊车辆类型比如集装箱卡车、特殊工程车或者场景与常规道路差异很大比如无人机俯拍视角、夜间高架才需要考虑用自定义数据集微调。4. 核心模块代码拆解从检测框到带ID的跟踪结果4.1 检测器封装一行代码加载YOLOv5YOLOv5官方仓库提供了torch.hub加载方式配合本地权重文件可以这样加载import torch model torch.hub.load(ultralytics/yolov5, custom, pathyolov5/weights/yolov5s.pt, force_reloadFalse) model.conf 0.35 # 置信度阈值 model.iou 0.45 # NMS IoU阈值 model.classes [2, 5, 7] # COCO类别car2, bus5, truck7注意model.classes的过滤非常关键。COCO有80个类别如果不加过滤行人、自行车、猫狗都会被检测出来不但白白消耗算力还会干扰跟踪结果。如果有摩托车需求类别索引是3电动车或者三轮车在COCO里没有对应类别需要自己训练。推理时可以直接传一个numpy数组BGR格式的帧返回的结果对象里可以方便地取出坐标、置信度、类别results model(frame) df results.pandas().xyxy[0] # 转成DataFrame包含xmin, ymin, xmax, ymax, confidence, class这里有个细节results.pandas()返回的是Pandas DataFrame遍历时建议用iterrows()帧率要求高时可以用results.xyxy[0]转成numpy再手动处理性能会好一些。4.2 DeepSORT跟踪器初始化与参数含义DeepSORT的PyTorch实现有许多版本接口略有差异但整体用法类似。我用最常见的一版举例from deep_sort import DeepSort deepsort DeepSort( model_pathdeep_sort/deep_sort_pytorch/configs/deep_sort.yaml, max_dist0.2, # 特征距离阈值超过此值不匹配 min_confidence0.3, # 检测置信度低于此值的框不参与跟踪 max_age30, # 目标丢失后最多保留30帧 n_init3, # 新目标连续匹配3帧才确认为正式轨迹 nn_budget100, # 特征库大小超过则更新策略改变 )这五个参数建议花点时间理解因为它们直接决定跟踪效果的风格。max_dist越小匹配越严格ID切换越少但也更容易漏匹配max_age越大目标被遮挡后恢复的可能性越大但也更容易出现轨迹和错误的检测框重新关联。后面章节我会给一组实测表现不错的组合。4.3 主循环把检测结果送入DeepSORTDeepSORT的update方法需要将检测框转换成[x中心, y中心, 宽, 高]的格式同时传入置信度和类别import cv2 import numpy as np cap cv2.VideoCapture(test.mp4) fps int(cap.get(cv2.CAP_PROP_FPS)) width int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) writer cv2.VideoWriter(output.mp4, cv2.VideoWriter_fourcc(*mp4v), fps, (width, height)) while cap.isOpened(): ret, frame cap.read() if not ret: break # 检测 results model(frame) df results.pandas().xyxy[0] # 整理检测结果 xywhs [] confs [] clss [] for _, row in df.iterrows(): x1, y1, x2, y2 int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) w, h x2 - x1, y2 - y1 cx, cy x1 w / 2, y1 h / 2 xywhs.append([cx, cy, w, h]) confs.append(float(row[confidence])) clss.append(int(row[class])) # 跟踪 outputs deepsort.update( np.array(xywhs) if xywhs else np.empty((0, 4)), np.array(confs) if confs else np.empty((0,)), np.array(clss) if clss else np.empty((0,)), frame, )这里有一个必须处理的边界情况如果某一帧没有检测到任何车辆xywhs是空列表直接传入会导致类型转换异常所以要先判空并构造空数组。outputs返回的结构一般是每个元素的(bbox, track_id, cls)列表bbox是[x1, y1, x2, y2]格式。后续绘制、计数都用它。4.4 绘制检测框、ID标签与统计信息拿到带ID的跟踪结果后绘制就比较直观了。我会将绘制封装成单独的函数保持主循环干净def draw_boxes(frame, outputs, color_pool, counter): for track in outputs: bbox, track_id, cls track[:3] x1, y1, x2, y2 [int(v) for v in bbox] color color_pool[track_id % len(color_pool)] cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) label fID:{track_id} cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) # 绘制虚拟检测线 cv2.line(frame, (0, line_y), (width, line_y), (0, 0, 255), 2) # 绘制计数面板 cv2.putText(frame, fCount: {counter}, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 255), 2)颜色分配我用的是预定义颜色池按ID取模。ID是整数且会复用到很大的值取模比随机生成更稳定保证同一个ID的框和轨迹颜色一致。5. 车流量统计的核心实现虚拟检测线与ID去重5.1 虚拟检测线怎么设计车流量统计最常见的方法是在画面中画一条虚拟检测线然后判断车辆的中心点是否跨过这条线。之所以用中心点而不是框的某个边角是因为中心点在跟踪过程中最稳定不容易因为检测框抖动而误判。检测线通常是一条水平或接近水平的线段在代码里可以用一个y坐标line_y加起点和终点的x坐标表示。比如路口场景中如果摄像头固定检测线可以放在画面中车辆必须经过的位置。判断一个目标是否跨线本质是判断前后两帧的目标中心点y坐标与line_y的相对位置变化def is_crossed(prev_cy, cur_cy, line_y, direction): if direction top_to_bottom: # 从上往下比如离开路口 return prev_cy line_y cur_cy if direction bottom_to_top: # 从下往上比如进入路口 return prev_cy line_y cur_cy return False这个函数有个好处即使车辆移动速度很快两帧之间跨越了多行像素也能正确触发。用“前一帧在线的一侧、当前帧在另一侧”来判断比“当前帧在线附近”更可靠。5.2 过线计数与通行方向判定计数逻辑需要为每个跟踪ID记录它上一次的中心点y坐标。我通常这样维护prev_positions {} # track_id - (cx, cy) counted_ids {} # track_id - True用来防止重复计数 total_count 0 # 每帧跟踪结果更新后 for track in outputs: bbox, track_id, _ track[:3] cx, cy int((bbox[0] bbox[2]) / 2), int((bbox[1] bbox[3]) / 2) if track_id in prev_positions: prev_cy prev_positions[track_id][1] if track_id not in counted_ids: if is_crossed(prev_cy, cy, line_y, top_to_bottom): total_count 1 counted_ids[track_id] True elif is_crossed(prev_cy, cy, line_y, bottom_to_top): total_count 1 counted_ids[track_id] True prev_positions[track_id] (cx, cy)counted_ids存在的意义是防止重复计数。如果没有这个集合车辆在检测线上来回抖动或者DeepSORT偶尔给同一个目标换了ID都会导致同一辆车被多次记录。加上计数集合后每个ID只允许触发一次跨线计数之后再出现在线附近也不再计数。5.3 方向计数和ID去重的实战细节如果你想分别统计两个方向的车流可以在计数时拆开count_up 0 # 向上 count_down 0 # 向下 if is_crossed(prev_cy, cy, line_y, top_to_bottom): count_down 1 elif is_crossed(prev_cy, cy, line_y, bottom_to_top): count_up 1注意这里方向判断依赖prev_cy和cur_cy因此如果跟踪ID在跨线中间发生切换ID Switch前一帧的AID和当前帧的BID对不上计数会漏。缓解办法是多保留一帧历史位置或者增加一个“跨线确认帧数”的缓冲当目标连续N帧满足跨线条件时才计数避免因为单帧抖动造成误判。我在实际项目中用的方案是目标中心点在检测线附近连续3帧以上才确认过线。代价是计数响应会延迟几帧但对离线统计来说完全可接受。crossing_candidates {} # track_id - 连续满足跨线的帧数 # 每帧更新 if is_near_line(cy, line_y, tolerance15): crossing_candidates[track_id] crossing_candidates.get(track_id, 0) 1 if crossing_candidates[track_id] 3 and track_id not in counted_ids: if direction_ok(prev_cy, cy, line_y): total_count 1 counted_ids[track_id] True else: crossing_candidates[track_id] 0这个方案在实际道路视频中明显更稳特别是车辆经过检测线时速度较慢、中心点上下抖动比较多的场景。5.4 多车道和区域计数的扩展思路检测线方案在单车道或断面流量统计中够用但实际路口往往需要多条车道分开统计。扩展方式有两种一种是在同一画面中画多条平行检测线每条线独立维护一套计数逻辑。比如画面左边一条线、右边一条线或者入口一条、出口一条。代码上把计数逻辑封装成Counter类实例化多个对象就行。另一种是直接把“线”扩展成“区域”或“多边形”判断目标中心点是否进入和离开这个区域。典型场景是停车场出入口车从画面下方进入区域再离开区域才算一次完整进出。实现上是维护“每个ID当前是否在区域内”的状态进入时置为True离开时计数加一。这个方案更灵活但对区域边界的定义需要仔细调参避免车停在边界处反复进出。6. 轨迹显示与可视化还原车辆的行驶路径6.1 轨迹数据用什么结构保存轨迹显示的核心是保存每个目标最近的若干个中心点然后用线把它们连起来。我推荐用collections.deque因为它支持maxlen限制超过长度自动丢弃最老的点非常契合“最近N帧轨迹”的需求。from collections import deque, defaultdict trails defaultdict(lambda: deque(maxlen30))maxlen的取值需要权衡。30意味着大约1秒的轨迹假设30fps视觉上比较干净。如果设得太长比如100画面上会拖出很长的尾巴在路口容易显得很乱如果太短比如5轨迹就几乎不成形。我的习惯是先设30然后根据实际观感微调。6.2 每帧更新和绘制轨迹在每帧跟踪结果出来后把每个ID的中心点追加到对应的deque里然后遍历所有轨迹绘制for track in outputs: bbox, track_id, _ track[:3] cx int((bbox[0] bbox[2]) / 2) cy int((bbox[1] bbox[3]) / 2) trails[track_id].append((cx, cy)) for track_id, pts in trails.items(): if len(pts) 2: continue color color_pool[track_id % len(color_pool)] cv2.polylines(frame, [np.array(pts, dtypenp.int32)], isClosedFalse, colorcolor, thickness2)有一个性能上的细节如果画面中目标数量很多比如几十辆车每帧都遍历所有轨迹并绘制polylines开销会比较大。可以用一个掩码只绘制在画面范围内的点或者每隔几帧清理一下已经丢失很久的轨迹数据。6.3 轨迹断点与跳变问题实际运行中轨迹偶尔会断裂DeepSORT在目标被遮挡后会生成一条新轨迹因为ID变了在画面上表现为轨迹线突然从中间断掉换了一根新颜色的线继续。这种情况首先可以通过调大max_age缓解目标丢失后保留轨迹的时间更长如果遮挡时间短它还能恢复成原ID。但max_age调得太大会带来另一个问题长时间离线恢复时目标已经移动很远卡尔曼滤波预测的位置和实际位置差异很大反而容易匹配错目标。所以max_age一般设在20到50之间比较平衡。还有一个经常被忽略的点DeepSORT对“新目标”有n_init参数控制通常要连续匹配3帧才确认正式轨迹。新目标在n_init期间也会被输出但ID不稳定。如果你发现轨迹两侧出现细碎的小线段可能就是新目标尚未确认时的不稳定轨迹可以在代码中过滤掉仍未确认的轨迹或者干脆等确认后再开始记录轨迹点。7. 实测效果评估与调优参数调整和踩坑记录7.1 不同场景下的跟踪稳定性表现我在城市快速路、高速收费站、十字路口三种场景下都跑过这套系统表现差异很大。城市快速路最顺利车辆行驶方向相对一致遮挡少Detection和Tracking都很稳定计数准确率能到95%以上。十字路口最麻烦车辆会减速、停车、转弯运动模型不再符合匀速直线假设卡尔曼滤波的预测能力下降ID切换明显增多。高速收费站的难点在于车辆排队时大量目标静止DeepSORT对静止目标的外观特征依赖更高有时会把排队中位置靠得很近的车当成同一辆。所以这套系统的效果和使用场景高度相关。如果你的场景是低速、密集、多遮挡要有心理准备ID切换是不可避免的需要通过参数调优尽量减少。7.2 关键参数调整速查表以下是我实测下来的一组参数基准以及不同问题的调整方向参数作用推荐值遇到问题时怎么调model.conf检测置信度阈值0.35漏检就降低到0.25误检增多就提高到0.45model.iouNMS的IoU阈值0.45重叠目标被合并就降低出现重复框就提高max_dist外观特征距离阈值0.2ID切换频繁就调小到0.15轨迹断裂就调大到0.3min_confidence参与跟踪的最低置信度0.3遮挡场景可降到0.1让低置信度检测参与匹配max_age轨迹最大丢失帧数30遮挡恢复需求大就调大到50卡顿场景调小到20n_init新轨迹确认帧数3轨迹碎片多就提高到5需要快速响应就降到2nn_budget特征库容量100目标种类复杂多样就调大到200调整优先级建议先调检测器的conf和iou再调跟踪器的max_dist和max_age。因为跟踪的上限取决于检测的质量检测都不稳定跟踪再怎么调也白搭。7.3 性能分析与推理加速性能取决于硬件。我的实测数据供参考GTX 1660 Super显卡、1080p视频、YOLOv5s模型推理加跟踪综合帧率大约35到45 fps换成纯CPUi7-10700帧率掉到8到12 fps基本无法实时。如果GPU帧率不够优先做三件事第一开启YOLOv5半精度推理。在GPU上调用model.half()可以让检测速度提升将近一倍。model.half() # 模型参数和输入都转成fp16第二缩小输入尺寸。YOLOv5默认推理尺寸640改成416或512对帧率提升非常明显。代价是小目标的检测能力会下降需要对比验证。model.imgsz 416第三降采样视频帧。如果视频源是2K或4K可以先用cv2.resize将帧缩放到1080p甚至720p再做检测和跟踪。这样对检测精度影响不大但速度提升明显。DeepSORT本身也吃性能但优化空间有限。一个常见做法是每隔一帧调用deepsort.update这相当于把跟踪帧率减半计数精度会有一定损失但如果你只需要统计结果不太在意平滑轨迹可以接受。7.4 调试中最常遇到的四个问题调试过程中有几个高频问题我直接给出排查路径。检测不到车辆。先看单帧检测输出不经过跟踪直接跑YOLOv5确认能否检出。如果单帧能检出但跟踪时没有输出检查类别过滤和多类别传入是否正确。同一辆车出现多个ID。这是ID Switch通常由遮挡或检测框抖动引起。先调大max_dist再调大max_age最后检查检测器是否在连续帧中给同一辆车输出大小剧烈变化的框。如果检测框本身忽大忽小可以用跟踪器的平滑输出而不是直接用原始检测框。计数明显偏大。大概率是车辆在检测线上反复抖动触发多次计数。检查counted_ids逻辑是否正确或者增加跨线确认缓冲帧数。程序跑着跑着内存越来越大。基本可以确定是trails字典只增不减丢失的轨迹ID没有被清理。建议在每帧更新时把长时间未出现的ID从trails和prev_positions中删除或者定期清理。8. 你拿到这套系统后还能扩展什么方向到这里一套完整的YOLOv5-DeepSORT车流量统计与轨迹显示系统已经可以跑起来了。在收尾之前我再分享几个我实际做过的扩展方向每一个都比基础版有更实际的应用价值。第一个是统计结果导出。把每帧的过车记录时间、方向、ID写入CSV或SQLite并按分钟、小时聚合产出流量报表。这个在交通调研类项目中几乎必做实现成本很低只需在计数器更新后加几行写库代码。第二个是平均车速估计。如果你在同一画面中设置两条距离已知的虚拟检测线记录同一个ID先后跨过两条线的时间差结合两条线的像素距离和摄像头标定信息就可以估算车辆速度。这个功能比单纯统计车流量更能打动甲方。第三个是边缘设备部署。YOLOv5可以导出ONNX再转成TensorRTDeepSORT虽然转换麻烦一些但也可以整体搬到Jetson或者RK3588这类嵌入式平台。部署到边缘之后整个系统才真正具备工程落地的价值。最后再分享一个我踩过多次的教训做这个项目时不要一上来就追求模型精度。先用预训练权重加默认参数把整条链路跑通确认检测、跟踪、计数、绘制各环节都没问题再回头调精度和速度。很多人第一步就卡在“想训练一个完美的车辆检测模型”上结果连视频都还没有跑起来。项目能完整跑通比任何一个单点做到极致都重要。本文还有配套的精品资源点击获取
返回列表