项目地址:
https://atomgit.com/chen_jixi/yolo-object-detection-lab
前面一篇里,我已经给这个项目加入了 Speed Estimation,可以基于 ByteTrack 的连续轨迹计算目标在图像中的运动速度,也就是 px/s。
这种方式能够反映目标在画面里的相对运动快慢,但如果继续往下做,很快就会遇到一个问题:
图像里的 100 个像素,到底对应现实世界里的多少米?
如果没有真实尺度信息,就直接把像素速度换成 30 km/h、50 km/h,其实是不严谨的。
所以这一次,我继续升级 Speed Estimation,加入了:
Perspective 4-Point Calibration
↓
Homography
↓
Bird's-eye View
↓
m/s / km/h
也就是通过道路平面四点透视标定,把原始图像中的目标位置投影到一个近似真实尺度的鸟瞰平面,再进行速度估计。
一、为什么单纯的 px/s 还不够?
上一版速度估计的逻辑其实很直接。
ByteTrack 获得同一个目标在连续两帧中的位置,通过两点之间的像素距离除以时间差,就可以得到像素速度。
例如某个目标一秒移动了 80 个像素,那么速度就是:
80 px/s
这个结果本身没有问题。
但如果想进一步得到 m/s 或 km/h,就必须知道"1 个像素对应现实世界多少米"。
问题在于道路视频存在明显的透视关系。
远处的 100 px 和近处的 100 px,通常并不代表相同的真实距离。
因此,如果直接使用一个固定的 meters_per_pixel,只能在很小的局部区域内做近似。
这也是我这次加入四点透视标定的原因。
二、四点透视标定
现在 Speed Estimation 中增加了新的标定模式:
Perspective 4-Point
用户首先在视频第一帧的道路平面上依次点击四个点:
P1、P2、P3、P4
这四个点围成一个道路区域。

然后再输入这个区域在现实世界中的近似尺寸。
这次测试中,我设置为:
Width:6 m
Height:12 m
程序会把原图中的四边形映射到一个 6 m × 12 m 的矩形鸟瞰平面。
页面中也会直接生成 Bird's-eye Calibration Preview。

这样就完成了:
原始透视道路
↓
四点标定
↓
鸟瞰矩形平面
三、Homography 到底做了什么?
这里最核心的技术就是 Homography,也就是单应性变换。
简单来说,就是建立原始图像平面和鸟瞰道路平面之间的映射关系。
比如:
原图 P1 → 鸟瞰左上角
原图 P2 → 鸟瞰右上角
原图 P3 → 鸟瞰右下角
原图 P4 → 鸟瞰左下角
后端通过 OpenCV 的 cv2.getPerspectiveTransform() 计算单应矩阵。
得到这个矩阵以后,就可以把原图中的一个位置:
(x, y)
转换成鸟瞰平面中的:
(X, Y)
如果鸟瞰平面本身按米来定义,那么 X、Y 就可以进一步作为近似真实世界坐标使用。
四、目标位置依旧使用 Bottom-center
这一次没有改变目标的锚点定义。
继续使用:
Bottom-center Anchor
也就是目标检测框底边的中心点。
计算方式是:
anchor_x = (x1 + x2) / 2
anchor_y = y2
之所以一直使用 Bottom-center,是因为它比 Bounding Box 中心更接近目标与地面的接触位置。
对于行人来说,它更接近脚的位置。
对于车辆来说,它也更接近车辆实际所在的道路位置。
所以整个流程变成:
YOLO
↓
ByteTrack
↓
Bottom-center Anchor
↓
Homography
↓
Bird's-eye Coordinate
五、速度终于可以在米制平面里计算
完成透视投影以后,同一个 Track 在两个时间点上的位置就不再只是图像像素坐标,而是鸟瞰平面上的近似真实位置。
假设两个位置分别是:
P1 = (X1, Y1)
P2 = (X2, Y2)
那么就可以计算两点之间的平面距离。
然后:
真实距离 ÷ 时间差 = m/s
再乘以 3.6,就可以得到 km/h。
于是完整测速链路变成:
YOLO
↓
ByteTrack
↓
Track ID
↓
Bottom-center Anchor
↓
Homography
↓
Bird's-eye Coordinate
↓
Meter Distance
↓
m/s
↓
km/h
六、真实测试参数
这次继续使用之前一直测试的道路视频。
视频信息:
Resolution:1920×1080
FPS:29.97
Total Frames:962
Duration:32.10 s
标定参数:
Calibration Mode:Perspective 4-Point
Real Width:6 m
Real Height:12 m
测速参数:
Confidence Threshold:0.55
Frame Interval:Every Frame
Smoothing Window:5
Smoothing Method:Moving Average
Max Pixel Jump:200 px
Track Trails:ON
Trail Length:30

七、962 帧完整处理成功
这次修复以后,整个视频完整跑到了:
962 / 962 Frames
100%
最终统计:
Unique Tracks:37
Tracks With Speed:34
Processing Time:63.4 s

说明这一次已经不是单纯前端显示了四点标定,而是 Perspective Calibration 真正进入了视频测速流程。
八、终于不是 px/s 了
上一版真实运行时,Summary 还是:
No Calibration
Average Speed:80.8 px/s
而这一次已经明确显示:
Perspective 4-Point
最终得到:
Average Speed:0.93 m/s
约 3.4 km/h
Max Speed:119.0 km/h

这说明四点 Homography 已经真正参与速度计算。
九、为什么平均速度只有 3.4 km/h?
这里要注意一个统计口径问题。
这次参与统计的不只是汽车,还包括 Person 等其他 Track。
所以:
0.93 m/s ≈ 3.4 km/h
代表的是当前有效 Track 的整体平均结果,并不是"平均车速"。
如果只筛选 Car 类别再单独统计,得到的结果会是另外一个口径。
这也是为什么做视频分析时,不能只看一个总平均值,还要结合类别和单 Track 数据一起看。
十、119 km/h 能直接当成真实车速吗?
不能。
这次虽然已经使用了四点 Homography,但它仍然属于:
单目视频
人工道路平面标定
近似真实尺寸
最终结果仍然会受到很多因素影响。
比如:
标定点位置是否准确、6 m × 12 m 是否符合真实道路尺寸、目标是否真的位于同一平面、Bounding Box 是否抖动、Track 是否发生重关联,以及目标是不是靠近透视变换边缘。
尤其在 Homography 的边缘区域,一个很小的像素抖动,映射到鸟瞰平面后可能被放大成明显的米制位移。
所以更准确的说法应该是:
基于四点道路平面标定得到的速度估计结果。
而不是:
准确测得某辆车时速 119 km/h。
这一点我觉得反而很重要。
做出了 km/h,不代表就自动变成了专业测速仪。
十一、继续保留异常值过滤
上一版 Speed Estimation 中已经加入了 Max Pixel Jump。
这次继续保留。
实际设置为:
200 px
如果某个 Track 在相邻帧突然发生明显的大范围位置跳变,就不会直接把这次异常位移纳入正常速度统计。
这次还有一个细节:
异常跳变仍然在原图像素空间判断,而真正的速度则在鸟瞰平面中计算。
也就是说:
原图坐标 → 判断 Track 是否异常跳变
鸟瞰坐标 → 计算真实平面距离和速度
这样可以减少透视变换把原始小误差进一步放大的问题。
十二、Bird's-eye View 不只是一个预览
这次 Result Video 右上角还加入了一个 Bird's-eye View inset。

它会显示目标在鸟瞰平面中的投影位置和运动轨迹。
原图中,远处目标很小、近处目标很大。
但经过 Homography 后,它们都被映射到统一的道路平面中。
这样速度计算和轨迹观察都会更加直观。
十三、Result Video 同时显示原图和鸟瞰轨迹
最终视频中仍然保留了:
Bounding Box、Track ID、Class、Confidence、Trajectory、Speed
同时右上角增加 Bird's-eye View。

这样如果某个 Track 出现明显的异常速度,就可以直接回看:
是目标真的运动得很快,还是检测框发生了明显跳变?
相比只看最终表格,这种方式更容易排查问题。
十四、Track Speed Table
Speed Estimation 仍然会保存每个 Track 的独立统计。
包括:
Track ID、Class、Frames、Average Speed、Max Speed、Distance、Duration

完成 Perspective Calibration 后,这些数据已经不再只是 px/s 和 pixel distance。
而可以进一步得到:
m/s、km/h、meter distance
这样后面就可以继续分析某个目标的完整运动过程。
十五、四点标定也不是随便点四下
为了避免无效标定,这次还加入了多项校验。
包括四个点数量是否合法、Width 和 Height 是否大于 0、坐标是否在有效范围、Polygon 是否出现明显自交、Homography 是否有效,以及投影结果是否出现 NaN 或 Inf。
例如四个点画成一个明显的"蝴蝶结"自交形状,就不能继续作为合法道路平面使用。
这样可以减少输入错误导致的异常速度。
十六、开发过程中还踩了一个 FastAPI 参数坑
这一版第一次真实运行时,遇到了一个比较隐蔽的问题。
当时前端已经能够正常显示:
Perspective 4-Point
6 m × 12 m
Bird's-eye Preview
但视频真正处理完成以后,Summary 却还是:
No Calibration
80.8 px/s
也就是说:
前端标定成功了,但后台测速根本没用上这份标定。
后来排查发现问题出在 FastAPI 的参数解析。
前端使用 FormData 提交 calibration、points、width、height 等数据。
但是后端接口没有给这些参数增加 Form() 标注。
FastAPI 就把这些字段当成 query parameter 去读取。
于是:
前端 multipart form-data
↓
后端按 query parameter 查找
↓
找不到 calibration
↓
使用默认空字符串
↓
自动进入 No Calibration
↓
继续输出 px/s
最后把 Speed API 的相关参数全部改成 Form 参数以后,Perspective Calibration 才真正参与 Job。
十七、禁止 Silent Fallback
这个问题还暴露出了另外一个设计缺陷。
如果用户已经明确选择:
Perspective 4-Point
但是四点参数没有正确传到后端,程序不应该偷偷变回:
No Calibration
因为这样用户很容易误以为自己看到的是透视测速结果。
所以现在逻辑变成:
用户选择 Perspective 模式
↓
标定数据完整
↓
正常执行 Perspective Speed
如果参数缺失:
直接 Validation Error
而不是静默退回 px/s。
这个细节对于结果可信度非常重要。
十八、测试增加到 428 个
这一版 Perspective Speed 修复完成后:
428 pytest passed
前端:
288 assertions passed
新增测试主要覆盖:
4-point validation、clockwise / counter-clockwise、Homography、Bird's-eye Projection、Meter Distance、m/s、km/h、Invalid Width / Height、Self-intersection、NaN / Inf Protection、Perspective Export、FormData Payload、FastAPI Form Parsing,以及 Silent Fallback Prevention。
而之前已有的所有模式也没有回归。
十九、从像素速度到鸟瞰图测速
Speed Estimation 到现在已经经历了三个阶段。
第一阶段:
Pixel Speed
↓
px/s
不需要标定,但是只能反映图像中的相对运动速度。
第二阶段:
Simple 2-Point Calibration
↓
meters_per_pixel
↓
m/s / km/h
适合比较局部的小范围近似。
第三阶段:
Perspective 4-Point
↓
Homography
↓
Bird's-eye Plane
↓
Meter Distance
↓
m/s / km/h
这一次开始真正处理单目道路视频里的透视问题。
总结
这次真实实验最终得到:
962 Frames
37 Unique Tracks
34 Tracks With Speed
Calibration:Perspective 4-Point
Real Plane:6 m × 12 m
Average Speed:0.93 m/s
约 3.4 km/h
Max Estimated Speed:119.0 km/h
Processing Time:63.4 s
完整技术链路已经变成:
YOLO
↓
ByteTrack
↓
Bottom-center Anchor
↓
4-Point Calibration
↓
Homography
↓
Bird's-eye Coordinate
↓
Meter Distance
↓
Smoothing
↓
Outlier Filtering
↓
m/s
↓
km/h
相比上一版只能回答:
目标每秒移动多少像素?
这一版已经开始尝试回答:
目标在道路平面上大约移动了多少米,又有多快?
当然,它依然只是基于单目视频和人工标定的速度估计。
如果继续提高精度,还需要更加准确的道路尺寸、更加严格的相机与道路平面标定,以及更稳定的地面接触点和轨迹滤波。
项目地址:
https://atomgit.com/chen_jixi/yolo-object-detection-lab
下一步我准备继续在现有 ByteTrack + Bird's-eye 坐标基础上做:
Trajectory Prediction
也就是从"它现在在哪里、速度多快",继续往前一步:
预测它接下来可能往哪里走。