项目地址:
yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit
前面几篇里,这个项目已经从单张图片检测一路做到了:
Single Image
→ Batch Images
→ Video
→ Tracking
→ Line Crossing
→ ROI Analysis
→ Trajectory Heatmap
做到 ROI Analysis 以后,我已经能够回答:
哪些目标进入过指定区域?
区域里最多同时有多少目标?
每个目标在里面停留了多久?
但单个 ROI 还有一个明显的限制:
它只能告诉我目标"进过这里",却不能告诉我目标"从哪里去了哪里"。
所以这一次,我继续在 YOLO + ByteTrack 的基础上加入了第八种模式:
Multi-ROI Flow
用多个 Polygon ROI 统计目标在不同区域之间的真实流转关系。
一、从单 ROI 到多个 Zone
这一次不是只画一个区域,而是可以在视频第一帧上连续创建多个多边形 Zone。
例如:
Zone A
Zone B
Zone C
每个区域都可以自由画成 Polygon,而不是限制为矩形。

前端支持:
New Zone
Finish Zone
Undo Point
Delete Active Zone
Reset All Zones
所有顶点仍然使用归一化坐标:
0.0 ~ 1.0
所以浏览器预览尺寸变化不会影响真实视频里的区域位置。
二、还是用 Bottom-center 判断目标位置
这次继续沿用前面 Line Crossing、ROI、Heatmap 中已经使用过的:
Bottom-center Anchor
也就是目标框底边中点:
anchor_x = (x1 + x2) / 2anchor_y = y2
相比 bbox 中心,它更接近目标真正落在地面上的位置。
对于行人来说接近脚的位置,对于车辆来说也更接近实际道路位置。
所以判断"目标到底属于哪个区域"时更自然。
三、目标到底属于 Zone A、B 还是 C?
后端仍然使用:
cv2.pointPolygonTest()
判断一个 Track 的 anchor 是否位于 Polygon 内部。
每一个 Track 都会维护自己的区域状态,例如:
OUTSIDE
→ Zone A
→ Zone B
→ Zone C
→ OUTSIDE
于是现在不仅能记录:
ENTRY
EXIT
还能进一步得到:
Zone A → Zone B
Zone B → Zone C
这种真正的区域流转事件。
四、Border Tolerance 继续防止边界抖动
多个 Zone 的边界附近同样会遇到检测框抖动问题。
例如一个人在 A、B 边界附近:
A
B
A
B
A
如果每帧直接判断,就会生成大量假的 Transition。
所以这次仍然加入:
Border Tolerance
真实测试参数为:
8 px
目标进入边界缓冲区域时,不立即改变稳定 Zone。
只有真正进入另一个区域后,才确认状态变化。
五、Transition 是这次最核心的东西
假设某个 Track 的区域序列是:
Zone A
→ Zone B
→ Zone C
那么会生成:
A → B
B → C
两个 Transition。
如果是:
Zone A
→ OUTSIDE
→ Zone B
也不一定立刻断掉。
这次增加了:
Transition Gap
真实测试使用:
5 s
如果目标离开 A 后,在 5 秒内进入 B,仍然可以认为这是:
A → B
这样可以处理不同 Zone 之间留有空隙的情况。
六、同一区域再次进入不算 A→A
例如:
Zone A
→ OUTSIDE
→ Zone A
这代表目标第二次访问 Zone A。
所以:
Visits += 1
但是不会生成:
A → A
这种 Self Transition。
Transition Matrix 的对角线始终保持为 0。
七、真实测试:37 个目标产生了 33 次区域流转
这次仍然使用之前那段道路视频:
Resolution: 1920×1080
FPS: 29.97
Total Frames: 962
Duration: 32.10 s
参数:
Confidence: 0.50
Frame Interval: Every Frame
Border Tolerance: 8 px
Transition Gap: 5 s
Track Trails: ON
Trail Length: 30
完整处理:
962 / 962 Frames
100%
最终结果:
Unique Tracks = 37
Events = 66
Transitions = 33
Elapsed ≈ 86.6 s

相比之前的单 ROI,这一次最关键的变化就是:
Transitions > 0
说明系统已经真正统计到了不同区域之间的流动。
八、Transition Matrix:区域流转一眼看清
这次增加了一个很直观的:
Transition Matrix
横轴表示目标流向哪个 Zone,纵轴表示从哪个 Zone 出发。
真实测试中主要流转关系为:
B → C:16 次
C → B:11 次
A → B:5 次
C → A:1 次
加起来正好:
16 + 11 + 5 + 1 = 33
和顶部:
Transitions = 33
完全一致。

这比单纯看 Track ID 要直观很多。
一眼就能看到:
哪两个区域之间流动最频繁?
这次最明显的是:
B ↔ C
九、Top Routes:最常见的区域路线
除了 Matrix,这次还单独整理了:
Top Routes
真实结果:
B → C
Transitions: 16
Unique Tracks: 6
C → B
Transitions: 11
Unique Tracks: 5
A → B
Transitions: 5
Unique Tracks: 3
C → A
Transitions: 1
Unique Tracks: 1

这里有两个数字不能混淆。
例如:
B → C
Transitions = 16
Unique Tracks = 6
并不是有 16 个不同目标。
而是 6 个不同 Track 一共完成了 16 次 B→C。
同一个 Track 可以多次真实往返。
十、为什么 Average Transfer Time 基本是 0?
这次结果里一个比较明显的现象是:
Avg Transfer Time ≈ 0.00 s
这不是程序一定有问题。
因为我这次画的三个 Zone 本身存在明显重叠。

目标可能在同一帧附近直接完成:
Zone B → Zone C
而不是:
B
→ OUTSIDE
→ 等待几秒
→ C
所以 Direct Transition 的 transfer time 很接近 0。
如果后续把 Zone 之间留出明显间隔,再运行一次,就更容易看到非零的:
Transfer Time
十一、Flow Map 比表格更直观
这次我觉得最好看的功能其实是:
Flow Map
系统会在原视频画面上:
- 画出 Zone A / B / C
- 标出各区域
- 在 Zone 之间绘制方向箭头
- 根据 Transition Count 调整箭头粗细

比如:
B ─────────▶ C
如果 B→C 次数最多,对应箭头也会更明显。
这样区域之间的流动关系,不看数字也能快速理解。
十二、Track Journey:每个目标到底怎么走的?
除了整体流量,还可以继续追踪每一个 Track。
例如某个目标可能经历:
A → B → C
另一个可能是:
C → B
甚至还会出现:
A → B → C → B → C ...

这就是:
Track Journey
它记录的是单个目标真实访问过的 Zone 序列。
OUTSIDE 不会出现在最终 Journey 中,连续重复的同一个 Zone 也会被压缩。
例如:
A → A → A → B → B → C
最后保存为:
A → B → C
十三、每个 Zone 仍然有完整的 ROI 统计
Multi-ROI 并没有丢掉之前 ROI Analysis 的能力。
每个 Zone 仍然会统计:
Unique Tracks
Entries
Exits
Visits
Current Occupancy
Max Occupancy
Total Dwell
Average Dwell
Max Dwell
所以现在不仅知道:
A→B 有多少个目标
还能够继续知道:
Zone A 一共被多少目标访问?
Zone B 最多同时有多少目标?
Zone C 里的平均停留时间是多少?
这就把:
区域状态分析
和:
区域流转分析
组合在了一起。
十四、第一次看到目标就在 Zone 里怎么办?
这次也继续保留了:
first_seen_inside
逻辑。
如果 Track 第一次被检测到的时候已经在 Zone C:
UNKNOWN → C
它会被计入:
Unique Tracks in C
但不会凭空生成:
OUTSIDE → C
这种不存在的 Entry。
更不会伪造一个 Transition。
十五、结果视频也会实时显示当前 Zone
最终输出视频中会同时显示:
Bounding Box
Track ID
Polygon Zones
Current Zone
Occupancy
Transition

这样如果统计结果里出现:
B → C
可以直接回到视频里观察目标到底是怎么完成这次区域切换的。
十六、支持 4 种数据导出
这次一共支持:
Export Flow JSON
Export Transitions CSV
Export Journeys CSV
Export Matrix CSV
Flow JSON
保存完整分析:
video metadata
settings
zones
summary
zone summary
transition matrix
top routes
tracks
journeys
transitions
Transitions CSV
一条 Transition 一行。
适合后续统计:
Track
Class
From Zone
To Zone
Time
Transfer Time
Journeys CSV
一个 Track 一行:
Track ID
Class
Journey
Visits
Transitions
Total Dwell
Matrix CSV
可以直接导入 Excel:
from_zone,A,B,C
A,0,5,0
B,0,0,16
C,1,11,0
导出全部基于已经完成的真实结果,不会重新跑 YOLO。
十七、这次测试增加到了 314 个
新增 Multi-ROI Flow 后:
pytest
314 passed
前端:
196 assertions passed
新增的测试主要覆盖:
Polygon ROI
Border Tolerance
First Seen Inside
Entry / Exit
Transition Gap
Journey
Dwell
Occupancy
Transition Matrix
Top Routes
Flow Map
Export
Stale Result Reset
YOLO Error
而且之前的:
Single Image
Batch Images
Video
Tracking
Line Crossing
ROI
Heatmap
都没有回归。
十八、从"目标检测"走到"目标流动分析"
现在回头看整个项目,已经走了很长一段:
YOLO Detection
↓
Video Detection
↓
ByteTrack
↓
Unique Objects
↓
Line Crossing
↓
ROI Occupancy
↓
Dwell Time
↓
Trajectory Heatmap
↓
Multi-ROI Flow
最开始只能回答:
画面里有什么?
后来可以回答:
哪些框其实是同一个目标?
再后来:
它有没有经过这里?
有没有进入这个区域?
停留了多久?
现在终于可以进一步回答:
它从哪个区域去了哪个区域?
总结
这次真实实验中:
962 Frames
37 Unique Tracks
66 Zone Events
33 Transitions
其中:
B → C:16
C → B:11
A → B:5
C → A:1
整个分析链路变成:
YOLO
↓
ByteTrack
↓
Track ID
↓
Bottom-center Anchor
↓
Multiple Polygon Zones
↓
Entry / Exit
↓
Transition Gap
↓
Journey
↓
Transition Matrix
↓
Top Routes
↓
Flow Map
相比传统 YOLO 只输出 Bounding Box,现在这个项目已经开始从:
目标检测
逐渐走向:
视频行为与空间流动分析
项目地址:
yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit
下一步如果继续做,我准备在现有 Tracking + Zone Flow 的基础上尝试:
速度估计
也就是进一步回答:
目标不仅从哪里去了哪里,它到底移动得有多快。