☰
YOLOv5+DeepSORT飞鸟检测跟踪实战:从模型选型到工程避坑
2026/10/13 2:45:13 网站建设 项目流程

简介:这是一套结合YOLOv5与DeepSORT的飞鸟视觉检测与跟踪完整工程,面向目标检测、多目标跟踪及鸟类生态研究方向的开发者与学习者,可复现实时检测加稳定跟踪的全流程。包内共244个文件,涵盖Python源码、YAML模型配置、预训练权重(.pt/.t7)、训练可视化日志、Docker部署文件、示例图片与演示视频等,py脚本负责推理与训练,yaml文件定义网络结构,权重可直接用于推理。压缩包约212MB,已有824人学习下载。资源基于U-Net架构的YOLOv5实现鸟体定位,配合Siamese网络与卡尔曼滤波完成跨帧跟踪,并包含多尺度训练、数据增强等关键实现细节。项目把检测与跟踪任务解耦成两个可替换模块,便于研究者分别优化和扩展,整体目录结构清晰,适合理解检测与跟踪算法协同、仿照此类系统搭建个人项目。

1. 飞鸟视觉检测和跟踪:为什么必须 YOLOv5 加 DeepSORT 双段串联

傍晚水面上几十只鸟同时起飞,单帧检测器能把每只鸟都框出来,但下一秒所有框的编号全乱了——哪只是哪只根本对不上。这个项目解决的就是这个死穴:YOLOv5 负责逐帧找到鸟,DeepSORT 负责跨帧把同一只鸟认出来并维持编号。它不是单纯的检测Demo,而是一套完整的「检测 + 跟踪」双段管线,落地场景包括机场驱鸟、养殖场鸟类计数、候鸟迁徙观测。适合已经有检测基础、想做行为轨迹分析的开发者,也适合刚接触跟踪任务但不想从零啃论文的初学者——直接拿这套框架改数据就能跑通。

2. YOLOv5 检测端:模型选型、数据集标注与训练参数怎么落

2.1 模型选型:飞鸟这种小目标,s、m、l 到底该选哪个

YOLOv5 的 s/m/l/x 四档模型,本质区别是网络的深度和宽度系数。很多初学者一上来就选 yolov5s,理由是速度快,结果在飞鸟场景上漏检率直接崩掉。

飞鸟目标有两个特殊性:第一,鸟在画面里通常只有几十个像素,属于典型的小目标;第二,背景极其复杂,天空、树枝、水面、建筑外墙都有可能出现,模型需要更强的特征提取能力才能把鸟和背景区分开。我实测下来的建议是:

模型档位速度(640输入,GPU)小目标表现适用场景
yolov5s最快,约120 FPS漏检较多,远处小鸟基本丢快速试跑、验证流程
yolov5m中等,约80 FPS中等距离可稳定检出大多数飞鸟监控场景
yolov5l较慢,约45 FPS小目标明显改善精度优先、离线分析
yolov5x最慢,约25 FPS最好,但显存压力大科研级精度验证

我一般建议用 yolov5m 起步。如果显存够、对实时性没硬性要求,直接上 yolov5l。yolov5s 在飞鸟场景下基本是「能跑但不实用」的状态,别被它的速度迷惑。

2.2 数据集标注与训练:统一类别、锚框设置与训练命令

飞鸟检测的数据集质量直接决定后续跟踪的上限。标注时有一个关键决策:类别统一用一个bird还是按鸟种拆成多个类。我的建议是——除非你的场景真的需要区分鸟种,否则统一用bird单类别。原因很简单:DeepSORT 的跟踪效果依赖检测的稳定性,类别越多,模型分类出错概率越大,同一只鸟可能在「麻雀」和「鹬鸟」之间横跳,跟踪端会疯掉。

标注工具用 labelImg,输出 YOLO 格式的 txt 文件。目录结构按 YOLOv5 的标准来:

dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 对应标注txt │ └── val/ └── bird.yaml # 数据配置文件

bird.yaml的内容如下:

train: dataset/images/train val: dataset/images/val nc: 1 names: ['bird']

这里nc: 1对应单类别,names列表的索引必须和标注文件里的类别 ID 对齐。训练命令我用的是:

python train.py --img 640 --batch 16 --epochs 300 \ --data bird.yaml --weights yolov5m.pt --hyp hyp.scratch-low.yaml

几个关键参数说明:

  • --img 640:训练输入分辨率。飞鸟目标小,可以试 768 或 896 提升小目标检出率,但显存占用会明显上涨。8GB 显存跑 640 + batch 16 是极限,再大就爆显存。
  • --batch 16:根据显存动态调整。显存不够就降到 8,但训练时间会翻倍。
  • --hyp hyp.scratch-low.yaml:这个容易被忽略。飞鸟遮挡多、目标小,低数据增强策略比默认的 hyp.scratch.yaml 更稳,因为过度的 mosaic 增强会把小鸟切碎,让模型学到错误的特征。

训练完成后,用runs/train/exp/weights/best.pt做推理。注意,YOLOv5 默认从最后一轮保存last.pt,从验证集最优保存best.pt,飞鸟场景一定用best.pt,last.pt往往过拟合了。

2.3 检测推理输出:置信度、NMS 和输出格式

检测端推理命令:

python detect.py --weights best.pt --source test.mp4 \ --conf-thres 0.25 --iou-thres 0.45 --classes 0

--classes 0表示只输出类别 ID 为 0 的目标(即 bird),这个参数在项目里很关键——如果数据集里混了其他类别的目标,不加限制会把无关目标全框出来,跟踪端会把这些噪声当成轨迹去追。

--conf-thres和--iou-thres的搭配需要调。飞鸟场景我建议conf-thres设在 0.2 到 0.3 之间。太高会把远处的小鸟直接滤掉,太低会把云朵、树叶晃动的误检框放进来。iou-thres保持 0.45 就行,改动它对结果影响不大。

detect.py 的输出是带标注的视频和标签文件,但对跟踪来说,我们真正需要的是检测框的坐标数据。所以整合时不用 detect.py,而是直接用model()的推理结果——这个在后面整合章节展开。

3. DeepSORT 跟踪端:卡尔曼滤波、级联匹配与参数玄学

3.1 跟踪为什么不能只靠检测:ID 切换、遮挡与漏检

直接对检测框做帧间匹配会出两个问题:一是漏检时轨迹直接断掉,二是遮挡后重新出现的鸟会被当成新目标。DeepSORT 解决这个问题靠的是两个核心部件:卡尔曼滤波负责预测目标下一帧的位置,匈牙利匹配负责把预测位置和当前检测框做最优关联。

飞鸟场景里 DeepSORT 的「预测」能力格外重要。鸟的飞行速度快,帧间位移大,如果只靠检测框的 IOU 匹配,相邻两帧的框稍微拉开一点就匹配不上。卡尔曼滤波会根据历史运动速度预测出下一帧的期望位置,即使检测框有轻微偏移,也能通过预测位置拉回关联。

3.2 DeepSORT 核心参数:max_age、min_hits、iou_threshold 与特征匹配

直接看实际操作。初始化 DeepSORT 跟踪器时,这些参数我全部调过:

from deep_sort import DeepSort deepsort = DeepSort( model_path='ckpt.t7', # ReID 特征提取模型权重 max_dist=0.2, # 级联匹配的余弦距离阈值 min_confidence=0.3, # 检测框置信度下限 nms_max_overlap=1.0, # 检测框 NMS 阈值 max_iou_distance=0.7, # IOU 匹配阈值 max_age=50, # 轨迹最大存活帧数 n_init=3, # 轨迹初始化所需最少匹配次数 nn_budget=100 # 外观特征队列长度 )

飞鸟场景下这几个参数必须调:

  • max_age=50:默认值通常是 30。鸟飞出画面后再回来是常事,max_age太小会导致轨迹刚丢失就被删掉,等鸟回来时 ID 已经换了。50 意味着轨迹在目标消失后还能保持 50 帧(约 2 秒),足够处理大部分短遮挡。
  • n_init=3:默认 3 没问题。它是「连续匹配到几次才确认这条轨迹」,太小会把误检框当成真轨迹,太大会让真实轨迹长时间处于未确认状态。
  • max_iou_distance=0.7:IOU 匹配的阈值,越高越宽松。飞鸟帧间位移大,默认 0.7 偏严格,如果发现同一条轨迹断成两段,可以放宽到 0.8。
  • nn_budget=100:每条轨迹保存最近 100 帧的外观特征。飞鸟的类内差异大,但运动模式单调,主要靠位置和速度信息匹配,外观特征只是辅助。

3.3 特征提取与外观匹配:ReID 模型选轻量还是重型

DeepSORT 的model_path指向的是一个 ReID 模型,作用是把检测框裁出来的图像映射成一个特征向量,用于计算两帧目标的外观相似度。原版用的是 mars-small128,在行人场景表现不错,但用在不同物种上效果会打折。

飞鸟的长相高度相似,同一种鸟之间外观特征差异极小,ReID 模型能给到的区分度有限。这块有两个选择:

一是直接用原版 mars-small128,省事,推理快,但匹配时主要靠运动和位置信息。二是换更强的 ReID 模型(比如 osnet),特征区分度提升明显,但每帧多一次额外的特征提取推理,FPS 会掉 15% 到 20%。

我的建议是先用原版跑通,如果发现「两只鸟交错飞行后 ID 互换了」,再考虑换 ReID 模型。不要一上来就上重型模型,跟踪器里玄学成分很高,先建立基线再逐项优化才是正路。

4. 检测与跟踪串联:数据流设计、类别过滤与 ROI 边界

4.1 整体数据流:从检测框到轨迹 ID 的衔接逻辑

把这个项目拆开看,YOLOv5 和 DeepSORT 是两个独立的模块,中间靠一个数据结构衔接。整个流程是:

  1. 读入一帧图像,YOLOv5 模型前向推理,得到检测框列表。
  2. 每个检测框做类别过滤和置信度过滤,只保留bird类且置信度达标的框。
  3. 检测框从xyxy格式转换为xywh格式(DeepSORT 要求中心点坐标 + 宽高)。
  4. 调用deepsort.update(),把检测框数据喂入,内部完成卡尔曼预测 + 级联匹配 + 轨迹更新。
  5. 从返回值中取出轨迹 ID、框坐标和置信度,画到画布上。

这个流程里最容易出错的是坐标格式和坐标系的统一。YOLOv5 的model()输出的是归一化坐标,DeepSORT 需要的是像素坐标,中间漏掉反归一化这一步,跟踪器会收到一堆接近 0 的小框,完全匹配不上。

4.2 工程实现:整合代码与关键参数

直接贴一份我调通的整合代码,基于 YOLOv5 的detect.py改的:

import cv2 import torch import numpy as np # 加载 YOLOv5 模型(用训练好的 best.pt) model = torch.hub.load('yolov5', 'custom', path='best.pt', force_reload=True) model.conf = 0.25 # 检测置信度阈值 model.iou = 0.45 # NMS IOU 阈值 model.classes = [0] # 只保留 bird 类别 from deep_sort import DeepSort deepsort = DeepSort( model_path='ckpt.t7', max_dist=0.2, min_confidence=0.3, max_iou_distance=0.7, max_age=50, n_init=3, nn_budget=100 ) def xyxy_to_xywh(det): # YOLO 输出为 xyxy 像素坐标,转为 DeepSORT 需要的 xywh x1, y1, x2, y2 = det[:4] w, h = x2 - x1, y2 - y1 return [x1, y1, w, h] cap = cv2.VideoCapture('input.mp4') while cap.isOpened(): ret, frame = cap.read() if not ret: break # 1. YOLOv5 推理 results = model(frame) dets = results.xyxy[0].cpu().numpy() # 像素坐标、置信度、类别 # 2. 过滤并转换检测框 bboxes_xywh = [] confs = [] for det in dets: if det[5] != 0: # 类别过滤 continue bboxes_xywh.append(xyxy_to_xywh(det)) confs.append(det[4]) # 3. 喂给 DeepSORT if len(bboxes_xywh) > 0: tracks = deepsort.update( np.array(bboxes_xywh), np.array(confs), frame ) else: tracks = deepsort.update([], [], frame) # 4. 绘制跟踪结果 for track in tracks: track_id, x1, y1, x2, y2 = track cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, f'ID:{track_id}', (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow('Bird Tracking', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

这段代码有四个关键点必须说清楚:

  • model.conf、model.iou、model.classes这三个属性是 YOLOv5 推理后处理的控制开关。model.classes = [0]的作用不是训练时的类别过滤,而是推理输出时只保留类别 ID 为 0 的检测框,能从源头减少跟踪端的噪声输入。
  • xyxy_to_xywh函数里的格式转换不能省。YOLOv5 的检测框是[x1, y1, x2, y2](左上角和右下角像素坐标),DeepSORT 接收的是[cx, cy, w, h](中心点坐标 + 宽高)。写错坐标格式是整合阶段最高频的 bug,表现为跟踪器输出为空或者轨迹乱跳。
  • deepsort.update()在无检测框时也要调用,传入空列表。卡尔曼滤波需要空帧来完成「预测」步骤——目标当前没有检测到,但根据历史运动轨迹,预测位置要持续更新,这样目标重新出现时才能接上 ID。
  • 返回值track是[track_id, x1, y1, x2, y2],注意这里的坐标是整数,但 DeepSORT 内部计算用的是浮点,画框时如果不转int,某些版本的 OpenCV 会报类型错误。

4.3 场景约束:ROI 区域与轨迹统计边界

实际部署时很少对全画面做跟踪。机场驱鸟只需要关注跑道周边,养殖场只需要统计投喂区附近的活动。这时候加一个 ROI 遮罩,只对进入指定区域的检测框做跟踪。

import cv2 import numpy as np # 定义一个多边形 ROI,比如跑道端头区域 roi_points = np.array([[200, 300], [800, 300], [1000, 700], [100, 700]], dtype=np.int32) def is_inside_roi(x1, y1, x2, y2): cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 # cv2.pointPolygonTest 返回 1 表示在多边形内 return cv2.pointPolygonTest(roi_points, (cx, cy), False) >= 0 # 在主循环中,喂给 deepsort 之前加一层过滤 filtered_bboxes = [det for det in dets if is_inside_roi(det[0], det[1], det[2], det[3])]

ROI 过滤放在 YOLOv5 推理之后、DeepSORT 更新之前。这样做有两个好处:一是减少了跟踪器的计算压力,二是避免 ROI 外的背景目标干扰轨迹匹配。注意pointPolygonTest的坐标系要和视频帧对齐,用原图分辨率直接画点,不要经过缩放。

5. 避坑排查:飞鸟场景下五个最高频的翻车点

5.1 坑一:高空远处飞鸟根本检测不到

现象:视频里肉眼能看到远处有鸟飞过,但检测框一直不出现,跟踪结果自然是空的。

原因:飞鸟目标在画面中只有 10 到 20 个像素,YOLOv5 的 640 输入分辨率会把这种小目标缩得更小,特征图上的响应弱到被 NMS 直接滤掉。

解决:两条路。一是提高推理分辨率,--img 896甚至 1280,小目标像素占比变大,检出率明显上升,代价是推理时间翻倍。二是做图像切片推理——把原图切成 2x2 或 3x3 的块,分别推理后合并结果,相当于在不增加全局分辨率的情况下放大局部区域。我倾向后者,因为切片推理对飞鸟这种稀疏目标场景很友好,每块区域的鸟密度低,检测精度不会因为目标密集而下降。

5.2 坑二:跟踪 ID 频繁跳变,一只鸟被拆成三段

现象:同一只鸟匀速飞过画面,但画面上显示 ID 从 5 变成 12 又变成 27,轨迹断成好几截。

原因:最常见的是检测端间歇性漏检。某几帧鸟被树枝遮挡,检测框消失,max_age内如果没接上,轨迹就会被标记为删除。等鸟重新出现时,DeepSORT 匹配不到旧轨迹,只能建新 ID。另一个原因是max_iou_distance太严,帧间位移大的时候预测框和检测框的 IOU 低于阈值,匹配失败。

解决:先确认检测端漏检率——把model.conf调低到 0.15 看漏检是否缓解。如果检测稳定,再把max_age加到 80,max_iou_distance放到 0.8。跟踪参数要配合检测端一起调,单独调跟踪器收效甚微。

5.3 坑三:训练时 loss 不降,模型完全学不到特征

现象:训练到 100 轮,box_loss 和 cls_loss 停在某个数值附近波动,验证集 mAP 几乎为 0。

原因:飞鸟数据集里大量图片是「一大片天空 + 一只小到看不见的鸟」,标注框占整图面积的比例极小。YOLOv5 的默认锚框是针对 COCO 数据集设计的,在训练初期通过 k-means 重算锚框时,如果数据集里大框极少,算出来的锚框全偏向小尺寸,梯度更新不稳定。

解决:先检查标注质量——用可视化脚本把标注框画回图片上,确认框的边界是否贴合鸟的轮廓。然后在训练命令里加上--noautoanchor参数,并手动检查runs/train/exp/anchors.png里锚框分布。如果发现锚框尺寸严重偏移,可以强制指定小尺寸锚框:在bird.yaml里加anchors字段覆盖默认值。这个操作比较敏感,具体数值要根据自己数据集的框尺寸分布来定,不要照抄网上任何人的参数。

5.4 坑四:白天晚上检测效果悬殊,傍晚置信度暴跌

现象:白天正常,一到傍晚或阴天,检测框开始抖动,跟踪 ID 频繁切换。

原因:飞鸟场景的数据集多半是在白天采集的。傍晚光照弱、对比度低、鸟的轮廓和天空融在一起,模型提取不到有效的边缘特征,置信度整体下降,conf-thres = 0.25会滤掉大量真实检测框。

解决:收集傍晚和逆光场景的数据重新训练,或者做数据增强时加大亮度扰动和对比度扰动的权重。如果不想重新训练,可以按时间段动态调整model.conf——白天用 0.25,傍晚自动降到 0.1。我实际测试过,这个「分时段置信度」策略比统一阈值稳定很多,代价是有时会把云朵误检进来,需要在 ROI 里设置天空区域排除。

5.5 坑五:批量视频处理时内存爆炸

现象:跑了几百帧后内存占用持续上涨,最后进程被杀掉。

原因:cv2.VideoCapture读取视频时,如果后面处理速度跟不上,帧队列会一直累积。加上 YOLOv5 推理每帧都会创建新的 tensor 对象,某些版本的 PyTorch 在 CPU 和 GPU 间拷贝数据时存在隐性内存泄漏。

解决:强制限制读取帧率,处理速度是多快就读多快,不让帧堆积:

cap = cv2.VideoCapture('input.mp4') fps = cap.get(cv2.CAP_PROP_FPS) skip_frames = int(fps / 25) # 每 25 帧处理 1 帧 frame_count = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break frame_count += 1 if frame_count % skip_frames != 0: continue # 正常推理和跟踪

这个处理方式本质是跳帧,把视频从 50 FPS 降到 25 FPS 再处理,内存压力减半。另外记住在每轮循环末尾调用del frame和torch.cuda.empty_cache(),能显著缓解长视频处理时的显存碎片问题。

6. 把这套系统推向实用:跳帧策略、TensorRT 加速与轨迹行为分析

完整跑通检测加跟踪之后,下一个问题是:怎么让它更快、更实用。

速度瓶颈主要在 YOLOv5 推理端。DeepSORT 的卡尔曼滤波和匹配计算量很小,ReID 特征提取才是第二瓶颈。我的优化路径是先量化,再上 TensorRT。先把 YOLOv5 的 FP16 精度打开——PyTorch 的 AMP 模式在保持精度基本不变的情况下,推理速度能提升 40% 左右。如果还嫌慢,把模型导出成 TensorRT engine,在 Jetson 或带 Tensor Core 的 GPU 上能再快一倍。导出命令是:

python export.py --weights best.pt --include engine --device 0 --half

导完后加载 engine 文件,推理方式和 PyTorch 模型完全一致,但延迟大幅下降。DeepSORT 的 ReID 特征提取同样可以转 TensorRT,但收益没那么大,建议先只加速 YOLOv5 部分。

精度验证方面,单纯看画面「感觉跟踪挺稳」是不够的。我习惯用 MOT 指标里的 IDF1 和 ID Switch 数来量化评估——IDF1 衡量轨迹匹配的准确率,ID Switch 统计 ID 跳变次数。做法是把跟踪结果保存成 MOT 格式的 txt,然后用公开的评估工具对比真值。跑一遍评估你会吃惊地发现,肉眼看着没问题的跟踪结果,ID Switch 可能已经上百次了。从那以后我每次做目标跟踪项目,都强制自己先跑一遍 MOT 评估再做优化,而不是凭肉眼判断好坏。

最后的进阶玩法是把轨迹数据用起来。飞鸟检测跟踪的终极价值不在视频里画几个框,而在轨迹分析——统计每天经过 ROI 的鸟数量、平均停留时长、飞行方向偏好,甚至用每条轨迹的坐标序列做聚类,识别出鸟群的主飞行通道。这些分析不需要额外模型,用轨迹点的(x, y, frame_id)序列就能完成。做法是把 DeepSORT 输出的轨迹按track_id分组,提取每条轨迹的起点、终点、速度和长度,存成 CSV,后续用 pandas 就能做行为分析。这能做的事很多,编排飞行时间、喂食区域热点、驱鸟策略效果评估,都在这一步落地。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询