单目视频测速不准?我给 YOLO + ByteTrack 加了透视标定和鸟瞰图测速

项目地址:

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

也就是从"它现在在哪里、速度多快",继续往前一步:

预测它接下来可能往哪里走。

相关推荐
中国.NET研究协会5 小时前
SimdYolo:纯 C# YOLO 推理实现,CPU SIMD / Vulkan 架构与 PyTorch、ORT 实测
yolo·.net core
码农小旋风6 小时前
Claude Opus 5.5把视频制作变成Agent工作流
音视频
lank_M6 小时前
WebCodecs解码竖拍视频变横的?四个浏览器实测
图像处理·人工智能·计算机视觉·音视频
小幂同学7 小时前
图片、视频和PDF太大,电脑上怎么压缩更合适?
pdf·音视频·zip·解压缩软件·zip解压缩
cuijiecheng20187 小时前
音频降混是什么?从5.1声道到双声道,一篇讲清楚
音视频
码流子7 小时前
高速场景视频接入:GB/T 28181 级联与 RTSP 取流,一条链路讲清楚
大数据·数据库·人工智能·物联网·音视频
海水冷却7 小时前
WebRTC 视频通话:从裸写信令到 30 分钟上线
音视频·webrtc
hz567898 小时前
数据本地化音视频系统设计:数据存储、传输路径与访问权限管理
网络·安全·音视频·实时音视频·信息与通信
闭包不眠8 小时前
竖拍视频宽高显示1920×1080?两种存法怎么分辨
图像处理·人工智能·计算机视觉·音视频