☰
同一辆车为什么被算几百次?我给 YOLO 加了 ByteTrack 目标跟踪
2026/10/11 4:32:05 网站建设 项目流程

项目地址:
yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit

上一篇我已经把 YOLO 从图片检测扩展到了视频检测。

当时视频检测已经可以完成:

Video Upload → YOLO Detection → Video Progress → Detection Video → Video Summary → JSON Export

但做完以后有一个很明显的问题。

比如一辆车连续出现在几十甚至几百帧中,普通 YOLO 会在每一帧都检测到它。

于是统计结果可能变成:

Car ×100

但实际上,它可能从头到尾都是同一辆车。

所以这一次,我继续使用码道 Agent,在原来的 YOLO 视频检测项目上加入了:

YOLO11n + ByteTrack

让模型不只是知道:

这一帧有什么?

还能够进一步判断:

这一帧里的这辆车,和上一帧的是不是同一个目标?


一、Detection 和 Tracking 到底差在哪?

普通目标检测的逻辑是:

Frame 1 → Car Frame 2 → Car Frame 3 → Car Frame 4 → Car

YOLO 并不知道这四个 Car 是不是同一辆。

因此统计时可能得到:

Detection Events = 4

而目标跟踪加入以后,会给目标一个持续存在的 ID。

例如:

Frame 1 → Car #7 Frame 2 → Car #7 Frame 3 → Car #7 Frame 4 → Car #7

虽然产生了 4 次 Detection Event,但真正的唯一目标只有:

Unique Objects = 1

这就是这一次加入 ByteTrack 最核心的原因。


二、加入第四种模式:Tracking

现在项目顶部已经有四种模式:

Single Image Batch Images Video Tracking

Tracking 模式继续复用之前的视频上传和 metadata 读取能力。

我这次测试的视频参数为:

Resolution: 1920 × 1080 FPS: 29.97 Total Frames: 962 Duration: 32.10 s

Tracking Settings 中增加了:

  • Confidence Threshold
  • Frame Interval
  • Show Track Trails
  • Trail Length

这次实际设置为:

Confidence = 0.50 Frame Interval = Every Frame Show Track Trails = ON Trail Length = 30


三、ByteTrack 是怎么接进来的?

这次没有再引入 DeepSORT 或其他新的 Tracking 框架。

直接使用 Ultralytics 已经集成的:

ByteTrack

核心思路类似:

model.track( frame, persist=True, tracker="bytetrack.yaml", conf=confidence, device=device)

其中比较关键的是:

persist=True

它让 tracker 在连续视频帧之间保留跟踪状态。

这样同一个目标在下一帧再次出现时,就有机会继续使用原来的:

track_id

例如:

Car #4 Person #6 Bicycle #15

而不是每一帧都当成新的目标。


四、Track ID 才是 Unique Count 的基础

这次后端会从 ByteTrack 返回结果中读取:

boxes.id

再把它转换成真实的:

track_id

然后维护一个集合:

unique_track_ids = set()

每出现一个新的有效 ID:

unique_track_ids.add(track_id)

最终:

Unique Objects = len(unique_track_ids)

所以如果:

Car #7

连续出现 200 帧:

Detection Events += 200 Unique Objects += 1

这就和之前的视频检测统计产生了本质区别。


五、真实结果:3989 次检测,最终只有 37 个唯一目标

这次完整处理了:

962 / 962 Frames Progress: 100%

页面最终统计:

Detection Events: 3989 Unique Objects: 37

也就是说,YOLO 在视频帧中一共产生了:

3989 次检测事件

但 ByteTrack 根据 Track ID 去重以后,真正跟踪到的唯一目标数量是:

37

这就是 Detection 和 Tracking 最直观的区别。


六、37 个 Unique Objects 分别是什么?

Tracking Summary 会按照类别继续统计唯一目标。

这次真实结果为:

ClassUnique Objects
Car18
Person16
Bicycle3

刚好:

18 + 16 + 3 = 37

这比上一篇单纯看到大量 Detection Event 更容易解释实际场景。

以前只能说:

Car 被检测了很多次。

现在可以进一步说:

ByteTrack 最终关联出了 18 个不同的 Car Track。


七、为什么还要保留 Detection Events?

既然已经有了 Unique Objects,是不是 Detection Events 就没用了?

其实不是。

两者代表不同的信息。

Detection Events

表示:

所有检测帧中,一共出现了多少次目标框

它更能反映目标在视频中的持续时间和检测频率。

Unique Objects

表示:

基于 ByteTrack Track ID 去重后的目标数量

例如:

Car #7

出现 150 帧。

那么:

Detection Events = 150 Unique Objects = 1

所以这次页面同时保留:

Detection Event Summary Unique Object Summary

两种统计,而不是用一个数字替代另一个。


八、给每个目标画上运动轨迹

这次另外加入了一个比较直观的功能:

Track Trails

对于每个 Track ID,会根据 Bounding Box 中心点计算:

cx = (x1 + x2) / 2 cy = (y1 + y2) / 2

然后保存最近若干个位置。

这次设置:

Trail Length = 30

也就是每个目标最多保留最近 30 个中心点。

最后通过 OpenCV 把这些点连接起来。

因此在结果视频中,除了:

Bounding Box Class Track ID Confidence

还可以看到黄色的运动轨迹。

这比普通 YOLO 的矩形框更容易观察目标到底从哪里移动到哪里。


九、Active Tracks 和 Unique Seen 不是一个概念

Tracking Progress 中还有两个容易混淆的值:

Active Tracks Unique Seen

比如这次任务结束后页面显示:

Active Tracks: 0 Unique Seen: 37

这并不代表跟踪失败。

因为:

Active Tracks

代表的是当前帧仍然活跃的目标数量。

任务结束以后,最后一帧如果没有活跃目标,它自然可能变成 0。

而:

Unique Seen

代表整个视频处理过程中累计见过多少个不同 Track ID。

所以这次真正要看的累计结果是:

Unique Seen = 37

十、Track List 能看到每个目标的生命周期

Tracking 模式还增加了一个:

Track List

每个唯一目标都会保存自己的生命周期信息:

Track ID Class First Frame Last Frame Frames Seen Avg Confidence Max Confidence

例如:

#1 Car Frames: 0 → 79 Seen: 78 Avg Confidence: ... Max Confidence: ...

这样就不只是知道“有 18 辆车”,还可以进一步查看:

某一辆车是什么时候第一次出现的?
持续了多少帧?
整个跟踪过程中置信度怎么样?

这已经开始有一点真正视频分析系统的感觉了。


十一、新任务必须重置 Tracker

做 Tracking 时还有一个需要特别注意的问题:

Tracker 本身是有状态的。

因为 ByteTrack 要记住:

上一帧有哪些 Track ID

所以如果:

Video A

处理完以后直接开始:

Video B

但没有清空 tracker,那么第二个任务可能继承第一个视频中的状态。

因此这次专门增加了:

reset_tracker()

每一个新的 Tracking Job 开始以前,都先把旧 tracker 状态清掉。

这样:

Tracking Job 1

和:

Tracking Job 2

就是完全独立的。

同时也不会影响原来的普通 Video Detection。


十二、导出 Tracking JSON 和 CSV

跟踪完成以后,现在可以导出两类结果。

Export Tracking JSON

JSON 会保存:

Detection Events Unique Objects Unique Class Summary Detection Class Summary Tracks

每个 Track 里面还有:

track_id class_name first_frame last_frame frames_seen avg_confidence max_confidence

结构类似:

{ "detection_events": 3989, "unique_objects": 37, "unique_class_summary": { "car": 18, "person": 16, "bicycle": 3 } }

Export Tracks CSV

CSV 则是一条 Track 一行。

例如:

track_id,class_name,first_frame,last_frame,frames_seen,... 1,car,0,79,78,... 2,person,0,72,57,...

相比把视频每一帧全部导出来,这种形式更适合分析:

一个独立目标的完整生命周期。


十三、测试也增加到了 126 个

在加入 Tracking 之前,整个项目已经有:

77 passed

这次又增加了大量 ByteTrack 相关测试。

最终:

126 passed

前端逻辑测试:

72 assertions passed

新增测试主要覆盖:

  • tracker reset
  • Track ID 解析
  • boxes.id is None
  • Unique Track 去重
  • 同一 ID 多帧只统计一次
  • Detection Events
  • Unique Class Summary
  • Trail Length
  • First / Last Frame
  • Frames Seen
  • Avg Confidence
  • Max Confidence
  • Active Tracks
  • Tracking JSON
  • Tracks CSV
  • 新 Job 不继承旧 tracker
  • 原 Video / Batch / Single Image 不回归

总结

这一篇之后,项目已经不只是:

YOLO Detection

而是进一步进入了:

YOLO Detection ↓ ByteTrack ↓ Track ID ↓ Cross-frame Association ↓ Unique Object Count ↓ Trajectory Trail ↓ Track Lifecycle

这次真实测试中:

962 Frames 3989 Detection Events 37 Unique Objects

其中:

Car ×18 Person ×16 Bicycle ×3

也就是说,之前那些成百上千次重复检测,现在终于可以通过 Track ID 关联成真正的独立目标。

从:

这一帧有一辆车

变成:

这是之前那辆 Car #7,它现在移动到了这里。

这就是从Object Detection到Multi-Object Tracking最直观的一步。

项目地址:

yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit

下一篇就可以继续解决一个更实际的问题:

既然已经知道每辆车是谁,那能不能统计到底有多少辆车真正经过某一条线?

下一步:

YOLO + ByteTrack + Line Crossing

做真正的行人 / 车流过线计数。

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

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

立即咨询