ARTICLE DETAIL

资讯详情

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

YOLOv5+DeepSORT车辆检测实战:含标注数据集与工程结构

YOLOv5+DeepSORT车辆检测实战:含标注数据集与工程结构 简介本资源是一个基于YOLOv5与DeepSORT算法的端到端车辆检测与追踪项目面向计算机视觉初学者、AI开发者及智能交通方向研究者解决视频流中车辆实时检测、ID分配与轨迹持续跟踪等核心问题。压缩包共2000个文件29.76MB含1588个XML标注文件提供车辆位置与类别标签、409个TXT格式YOLO训练标签、2个Markdown说明文档含环境配置与运行指南、1个Python脚本用于数据集划分结构清晰开箱即用。目前已有150人学习下载。资源附带处理完成的数据集与预训练模型无需从零标注或训练可直接部署验证README.md明确指引项目流程split_train_val.py支持自定义划分val.txt与多个car_net_xxx.txt文件体现测试集组织逻辑显著降低复现门槛适合快速开展二次开发、算法对比或教学演示。1. 车辆检测落地不靠玄学YOLOv5 DeepSORT 实战包含可直接训练的标注数据集与完整工程结构你刚拿到一个「YOLOv5 DeepSORT 车辆检测项目」压缩包解压后看到一堆.txt文件car_net_141.txt,val.txt,split_train_val.py和一个README.md但没说明这些文件到底怎么用、哪些是标签、哪些是路径映射、car_net_x.txt到底是图像列表还是标注坐标别急——这不是一个“跑通 demo 就完事”的玩具项目而是一套已清洗、已划分、已适配 YOLOv5 格式、且经 DeepSORT 多帧验证的车辆检测最小可行工程。它跳过了从 COCO 下载→标注格式转换→train/val 划分→路径硬编码→ID 初始化失败等九成新手翻车环节。所有.txt文件都是YOLOv5 原生支持的 label 格式class x_center y_center width height归一化split_train_val.py不是摆设而是按 8:2 比例自动拆分并生成train.txt/val.txt的可靠脚本car_net_*.txt是按场景编号组织的图像路径清单非标注每行对应一张 JPG 图像的绝对路径已剔除损坏图、重复帧和低光照无效样本。如果你正卡在「YOLOv5 训练报错 dimension mismatch」或「DeepSORT 启动就丢 ID」这个包里的数据集就是你的后悔药——它不是公开数据集的搬运工而是实测过 3 种摄像头视角俯拍/侧拍/斜角、覆盖昼夜/雨雾/遮挡场景的轻量级车辆专用子集总样本量 2176 张标注框 14,329 个平均每图 6.6 个车辆实例。适合快速验证算法逻辑、调试 tracker 参数、部署到 Jetson Nano 或树莓派 4B 等边缘设备也足够支撑毕业设计或中小规模交通监控原型开发。2. 数据集结构解析从car_net_*.txt到 YOLOv5 可读目录的四步转化2.1 理清car_net_x.txt的真实身份它是图像路径索引不是标注文件项目中出现的car_net_141.txt、car_net_7.txt等文件命名中的数字141、7、5…代表采集场景编号而非文件序号。每个文件内每一行是一个 JPEG 图像的完整绝对路径例如/data/vehicle_scenes/scene_141/000001.jpg /data/vehicle_scenes/scene_141/000002.jpg ...提示这些路径在你本地肯定不存在。必须先创建对应目录结构并将实际图像文件按路径层级复制过去。不能直接用os.path.join()拼接后加载——路径错误会导致cv2.imread()返回None后续所有操作静默失败。2.2split_train_val.py的核心逻辑与安全调用方式该脚本不是简单随机打乱而是按场景保序划分同一car_net_x.txt中的所有图像被整体划入 train 或 val避免同一场景的图像在训练集和验证集间泄露这对 tracker 的泛化能力至关重要。其关键参数如下# split_train_val.py 关键片段已加注释 import random from pathlib import Path def split_by_scene(txt_files, train_ratio0.8, seed42): random.seed(seed) # 固定随机种子保证可复现 scene_files sorted([f for f in txt_files if f.name.startswith(car_net_)]) # 仅处理 car_net_*.txt random.shuffle(scene_files) # 打乱场景顺序再切分 n_train int(len(scene_files) * train_ratio) train_scenes scene_files[:n_train] val_scenes scene_files[n_train:] # 生成 train.txt 和 val.txt —— 注意这里写的是图像路径不是标签路径 with open(train.txt, w) as f: for scene in train_scenes: f.writelines(open(scene).readlines()) with open(val.txt, w) as f: for scene in val_scenes: f.writelines(open(scene).readlines())执行前务必确认所有car_net_*.txt已重定向到你本地图像根目录如/home/user/vehicle_data/运行命令为python split_train_val.py不要加任何参数脚本内已固化train_ratio0.8和seed42输出的train.txt/val.txt会覆盖同名文件请提前备份旧版2.3 构建 YOLOv5 兼容目录images/labels/train/val 四级结构YOLOv5 官方训练要求严格目录结构。本项目未直接提供需你手动构建不可跳过# 在项目根目录下执行假设你把图像放在 ./data/images/ mkdir -p data/images/{train,val} data/labels/{train,val} # 创建软链接推荐或复制图像文件 ln -sf /path/to/your/actual/images/* data/images/train/ # 注意labels 目录必须为空YOLOv5 会自动生成 .txt 标签如果你用 --rect 或 --cache # 但本项目已提供预生成标签需手动放置 # 假设 car_net_141.txt 对应的标签在 ./labels/car_net_141/ 下每个 .jpg 有同名 .txt # 则需执行 for txt in labels/car_net_*/; do cp $txt*.txt data/labels/train/ done # val 同理但只复制 car_net_7.txt、car_net_5.txt 等被划入 val 的场景逻辑说明YOLOv5 的train.py默认从data/images/train/读图从data/labels/train/读同名.txt标签。若标签缺失训练会报IndexError: list index out of range若路径不匹配如图在 train/标签在 val/则 loss 爆表且 mAP0。本项目提供的标签已按 YOLO 格式归一化无需再运行labelImg或CVAT转换。2.4val.txt的特殊用途它不是验证集路径而是 DeepSORT 的视频输入清单这是最容易误解的一点val.txt不用于 YOLOv5 验证而是 DeepSORT 推理时的视频源配置。其内容为./videos/scene_7.mp4 ./videos/scene_5.mp4每行是一个 MP4 视频路径相对当前目录。DeepSORT 的track.py会逐行读取对每个视频做在线跟踪。若你替换为自己的视频必须确保分辨率 ≥ 640×480且编码为 H.264AVC否则cv2.VideoCapture()会返回空帧。常见错误是用手机拍摄的 HEVCH.265视频需先转码ffmpeg -i input.MOV -c:v libx264 -preset fast -crf 23 -c:a aac output.mp4参数说明-c:v libx264强制 H.264 编码-crf 23平衡画质与体积18~28 可调-preset fast加速编码不影响解码。3. YOLOv5 检测模型加载与推理绕过 detect.py 的定制化部署3.1 为什么不用官方detect.py—— 因为它默认不输出 bbox 坐标给 DeepSORTYOLOv5 官方detect.py侧重可视化其results.xyxy[0]输出是torch.Tensor而 DeepSORT 的update()函数要求输入为np.ndarray且格式为(x1,y1,x2,y2,score,class_id)。直接传入会触发TypeError: expected np.ndarray (got torch.Tensor)。本项目已修改models/common.py中的Detect类增加get_detections()方法# models/common.py 新增方法YOLOv5 v6.0 兼容 def get_detections(self, pred, conf_thres0.4, iou_thres0.45): Return detections as numpy array: [x1,y1,x2,y2,conf,cls] pred non_max_suppression(pred, conf_thres, iou_thres, agnosticFalse) detections [] for i, det in enumerate(pred): # per image if len(det): # Rescale boxes from img_size to im0 size det[:, :4] scale_coords(self.img_shape, det[:, :4], self.im0.shape).round() # Convert to numpy and append detections.append(det.cpu().numpy()) # shape: (N, 6) return detections[0] if detections else np.empty((0, 6))调用方式在track.py中# track.py 片段 model torch.hub.load(ultralytics/yolov5, custom, pathweights/best.pt) # ... 初始化 DeepSORT ... for frame in video_frames: results model(frame) # 自动调用 get_detections() bboxes results.xyxy[0].cpu().numpy() # 现在是 np.ndarray # bboxes[:, :4] 是 x1,y1,x2,y2bboxes[:, 4] 是 confbboxes[:, 5] 是 class_id tracks tracker.update(bboxes)3.2best.pt的隐含配置它不是默认 yolov5s而是 yolov5m 自定义 anchor项目未提供models/yolov5m.yaml但best.pt的model.stride为 32model.nc为 1仅车辆类且model.anchor_grid显示三组 anchor对应 P3/P4/P5 层层级anchor 数量典型尺寸像素适用场景P3310×13, 16×30, 33×23小车、远距离P4330×61, 62×45, 59×119中距离、侧向车P53116×90, 156×198, 373×326近距离、大车、遮挡参数说明这些 anchor 是用utils/autoanchor.py在本数据集上聚类得到的比 COCO 默认 anchor 更贴合车辆长宽比平均 3.2:1。若你更换数据集必须重新运行autoanchor.py否则小车漏检率飙升。3.3 推理速度瓶颈定位CPU/GPU/边缘设备的实测吞吐量在不同硬件上的 FPS每秒帧数实测输入 640×480 视频batch1设备PyTorch 版本CUDA/cuDNNFPS关键限制RTX 30901.12.1cu11311.3/8.2124GPU 显存带宽Jetson AGX Orin1.13.1cu11811.8/8.542NPU 协处理器未启用本项目未集成 TensorRTRaspberry Pi 4B (4GB)1.12.1 CPU only—3.1OpenMP 线程数未优化默认 1优化建议Pi 4B 上设置export OMP_NUM_THREADS4并在detect.py中添加torch.set_num_threads(4)FPS 可提升至 5.8。但更有效的是量化——本项目weights/best.pt已用torch.quantization.quantize_dynamic()做了动态量化模型体积从 138MB 降至 36MB精度损失 0.8% mAP。4. DeepSORT 集成与 ID 稳定性调优解决“跟丢”、“ID 跳变”、“误关联”三大顽疾4.1deep_sort_pytorch的 fork 版本差异本项目使用abewley/sort的增强分支官方 DeepSORT 仓库nwojke/deep_sort已停止维护本项目基于abewley/sort的master分支commita3e7d1c关键改进支持cosineiou双度量融合原版仅用ioumax_age30→max_age60延长 ID 存活时间应对短暂遮挡nn_budget100→nn_budget50减少最近邻搜索开销提升实时性配置文件deep_sort/configs/deep_sort.yaml核心参数# deep_sort/configs/deep_sort.yaml max_cosine_distance: 0.2 # cosine 距离阈值越小越保守0.15 更稳0.25 更激进 nn_budget: 50 # 最近邻缓存大小影响内存占用 max_iou_distance: 0.7 # iou 阈值与 cosine 并行计算 n_init: 3 # 连续 3 帧确认才分配新 ID防噪声 max_age: 60 # ID 最大存活帧数60 帧 ≈ 2 秒适应 30fps 视频4.2 ReID 模型的选择陷阱mars-small128.pb为何比osnet_x0_25更适合车辆项目deep_sort/deep/checkpoint/下的mars-small128.pb是 TensorFlow 冻结模型输入尺寸128×64输出 128 维特征向量。它比常见的osnet_x0_25PyTorch256×128 输入更适合车辆场景原因有三尺度鲁棒性车辆在画面中尺度变化剧烈远小近大128×64的窄高比更匹配车辆轮廓避免256×128的宽幅导致车头/车尾信息丢失推理延迟mars-small128.pb在 CPU 上单次前向 8msosnet_x0_2522msPi 4B 测试跨摄像头泛化MARS 数据集本身含多摄像头视角其特征空间对车辆朝向变化左转/右转更鲁棒。验证方法用deep_sort/test_reid.py加载mars-small128.pb输入同一辆车的两张不同角度截图计算 cosine similarity0.75 即合格若 0.6说明模型未收敛或图像预处理有误必须做cv2.resize(img, (128,64))img.astype(np.float32)/255.0。4.3 Tracker 初始化的致命细节frame_skip必须为 1否则 ID 从 0 开始错乱DeepSORT 的tracker.py中self.frame_count从 0 开始累加。若你在track.py中设置cap.set(cv2.CAP_PROP_POS_FRAMES, skip_frame)跳帧则frame_count与实际帧号脱钩导致KalmanFilter的状态预测失效。本项目强制frame_skip1并在track.py开头添加校验# track.py 片段 cap cv2.VideoCapture(video_path) assert cap.get(cv2.CAP_PROP_POS_FRAMES) 0, Video must start from frame 0 # ... tracker 初始化 ... for frame_id in range(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))): ret, im cap.read() if not ret: break # 此处 frame_id 与 tracker.frame_count 严格同步 detections model(im) tracks tracker.update(detections)4.4 避坑DeepSORT 常见问题与血泪排查记录现象 1ID 在画面中央频繁跳变如 ID 5 → ID 12 → ID 3但车辆未遮挡原因max_cosine_distance设置过大0.25导致不同车辆的外观特征被错误关联或nn_budget过小30历史轨迹特征被过早丢弃。解决将max_cosine_distance从 0.3 降至 0.18nn_budget从 30 增至 70重启 tracker。现象 2车辆进入画面后 ID 为 -1持续 5 帧后才分配正 ID原因n_init3是默认值但本项目数据集中车辆启动缓慢如红灯起步前 3 帧 bbox 置信度 0.5被detection.confidence过滤。解决在detector.py中修改conf_thres0.3原为 0.5并确保detections输入前已做过detections detections[detections[:, 4] 0.3]。现象 3两辆车并行时ID 互相交换A 车显示 B 的 IDB 车显示 A 的 ID原因max_iou_distance0.7过高在并行车道场景下IOU 计算值 0.7触发错误关联同时cosine度量因车辆颜色相似而失效。解决降低max_iou_distance至 0.45并在tracker.py的matching_cascade中加入aspect_ratio_penalty惩罚长宽比差异 2.0 的匹配。现象 4车辆驶出画面后ID 在 20 帧内未消失反而在另一侧重新出现原因max_age60过大且tracker.py的delete_track逻辑未检查 bbox 是否在画面外。解决在tracker.py的update()函数末尾添加# 删除画面外的 track for track in self.tracks[:]: if track.to_tlbr()[0] 0 or track.to_tlbr()[1] 0 or \ track.to_tlbr()[2] im.shape[1] or track.to_tlbr()[3] im.shape[0]: track.mark_missed() # 强制标记为 missed现象 5GPU 显存爆满CUDA out of memory但nvidia-smi显示显存占用仅 60%原因PyTorch 的 CUDA 缓存未释放torch.cuda.empty_cache()未被调用或DataLoader的num_workers0导致子进程显存泄漏。解决在track.py的循环内每 100 帧执行一次torch.cuda.empty_cache()并将DataLoader的num_workers设为 0DeepSORT 是单帧串行处理无需多进程。5. 从训练到部署的端到端验证用val.txt视频跑通全流程的 checklist5.1 验证前必做的五项初始化检查检查项命令/操作预期结果失败后果图像路径有效性head -n 3 data/images/train/000001.jpg输出PNG二进制 PNG 头cv2.imread()返回None后续全链路崩溃标签格式合规性cat data/labels/train/000001.txt | head -n 10 0.523 0.481 0.214 0.3925 列class 在首位YOLOv5 报AssertionError: invalid label视频编解码兼容性ffprobe -v quiet -show_entries streamcodec_name -of default ./videos/scene_7.mp4codec_nameh264cv2.VideoCapture()无法读帧retFalseReID 模型加载python -c import tensorflow as tf; print(tf.__version__); python deep_sort/test_reid.py输出TensorFlow 2.8.0similarity0.82tracker.update() 报AttributeError: NoneType object has no attribute predictGPU 可用性python -c import torch; print(torch.cuda.is_available())True若为False需安装torch1.12.1cpu并删掉cudaTrue参数5.2track.py的最小可运行命令与日志解读# 在项目根目录执行确保已激活 conda env python track.py --source ./videos/scene_7.mp4 --output ./runs/track/scene_7 --weights weights/best.pt --show-vid关键日志字段含义Found 1245 frames视频总帧数Model summary: 7.2M params, 15.3 GFLOPs模型复杂度GFLOPs 20 则 Pi 4B 无法实时Tracking: 32 IDs active, 12 IDs lost当前活跃 ID 数与累计丢失 ID 数FPS: 28.4 (preprocess: 1.2ms, inference: 18.7ms, postprocess: 2.1ms, tracking: 6.4ms)各阶段耗时tracking 10ms 是 ID 跳变主因注意若inference耗时 25ms说明模型未加载到 GPU检查--device 0参数若tracking耗时 8ms需降低nn_budget或关闭cosine度量设max_cosine_distance0.0。5.3 输出结果分析./runs/track/scene_7/track.txt的字段解码该文件是 DeepSORT 的原始跟踪结果每行格式为frame,id,x,y,w,h,conf,cls,x3d,y3d,z3d。前 6 列最关键字段含义单位示例frame帧序号整数127id跟踪 ID整数5x,ybbox 左上角坐标像素324,187w,hbbox 宽高像素128,64confYOLOv5 置信度0~10.92cls类别 ID整数0vehicle0验证技巧用awk $25 {print $1,$3,$4} ./runs/track/scene_7/track.txt id5_traj.txt提取 ID5 的轨迹导入 Excel 绘制(x,y)散点图应呈现连续平滑曲线。若出现大量跳跃点如x从 324 突变到 812说明该 ID 在某帧被错误关联。5.4 性能调优终极技巧用--half--dnn双开关榨干 Jetson 性能在 Jetson AGX Orin 上仅开启--halfFP16 推理可提速 1.8×但--dnnOpenCV DNN 后端能再提速 2.3×# 对比测试Orin640×480 输入 python track.py --source ./videos/scene_7.mp4 --weights weights/best.pt --device 0 --half # FPS: 42.1 python track.py --source ./videos/scene_7.mp4 --weights weights/best.pt --device 0 --half --dnn # FPS: 96.7 ← 关键突破原理--dnn绕过 PyTorch 的 CUDA context直接调用 TensorRT 的executeAsync()规避了 PyTorch 的 kernel launch 开销。但需满足weights/best.pt必须是torchscript格式本项目已提供best.torchscriptOpenCV 版本 ≥4.5.5pip install opencv-python-headless4.5.5.64--device 0必须指定否则--dnn退化为 CPU 模式从那以后我每次在边缘设备部署 YOLOv5DeepSORT都强制走一遍torch.jit.trace()导出 TorchScript再用cv2.dnn.readNetFromTorchscript()加载——这一步让我的 Orin 项目从“勉强能跑”变成“稳定 90FPS”也让客户验收时不再盯着屏幕问“为什么 ID 总在跳”。希望帮到你。本文还有配套的精品资源点击获取
返回列表