项目地址yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit前面几篇里这个项目已经从目标检测一路做到Single Image → Batch Images → Video → Tracking → Line Crossing → ROI Analysis → Trajectory Heatmap → Multi-ROI Flow做到这里之后已经可以回答目标是什么是不是同一个目标有没有经过某条线进入了哪个区域在区域里停留多久从哪个区域去了哪里但还有一个很自然的问题这个目标到底移动得有多快所以这一次我继续在 YOLO ByteTrack 的基础上加入第九种模式Speed Estimation用 Track ID 的连续位置变化估计目标在视频中的运动速度。一、这次做的不是“直接算车速”先说明一个很重要的问题。普通单目视频里如果没有真实世界尺度标定就不能直接把像素位移写成30 km/h 50 km/h因为图像里100 px到底代表 1 米还是 10 米取决于摄像机位置、透视关系和目标所在区域。所以这次我把速度分成两种Pixel Speed单位px/s以及完成尺度标定之后才允许使用的m/s km/h这次真实运行结果主要使用的是px/s 模式这样不会为了得到“看起来像真实车速”的数字而强行换算。二、速度怎么从 ByteTrack 轨迹算出来ByteTrack 已经能够给每个目标一个稳定的 Track ID。例如person #13 car #15 car #16每一帧都会得到这个目标新的位置。和前面 ROI、Line Crossing 一样这次仍然使用Bottom-center Anchor也就是 Bounding Box 底部中心anchor_x (x1 x2) / 2 anchor_y y2原因也很直接对于行人它更接近脚落在地面的位置对于车辆它也更接近车辆在道路上的实际位置。三、Pixel Speed 的计算假设同一个 Track 在两帧中的位置分别是P1 (x1, y1) P2 (x2, y2)像素位移distance_px sqrt((x2 - x1)^2 (y2 - y1)^2)再根据两帧时间差delta_time计算speed_px_s distance_px / delta_time最终单位就是px/s比如80 px/s表示这个目标在图像平面中每秒大约移动 80 个像素。四、为什么不能直接显示单帧速度目标检测框不是完全稳定的。即使目标实际运动很平滑bbox 也可能出现左抖一下 右抖一下 突然跳几像素如果直接拿每两帧的位置算速度就很容易出现30 42 38 160 35 40这种明显抖动。所以这次加入了Smoothing Window我实际使用5并采用Moving Average也就是用最近 5 个速度样本求平均。最终页面主要展示的是Smoothed Speed而不是完全依赖单帧瞬时值。五、还需要过滤异常位移除了普通抖动还有一种更严重的情况Track 在某一帧突然跳到了很远的位置。这种情况可能来自检测框变化 遮挡 Track 重关联 目标重新出现所以这次增加了Max Pixel Jump真实测试设置为200 px如果单帧位置跳变超过这个范围就不会直接把这次异常位移纳入正常速度统计。这样可以避免一个异常帧把整个 Track 的平均速度拉得特别高。六、真实测试参数这次继续使用前面几篇一直测试的道路视频Resolution: 1920×1080 FPS: 29.97 Total Frames: 962 Duration: 32.10 s实际参数Confidence Threshold: 0.55 Frame Interval: Every Frame Smoothing Window: 5 Smoothing Method: Moving Average Max Pixel Jump: 200 px Speed Threshold: 0 Display Unit: px/s Show Track Trails: ON Trail Length: 30七、962 帧完整跑完最终运行结果962 / 962 Frames 100%处理时间74.9 sTracking 得到Unique Tracks: 37 Tracks With Speed: 34也就是说整个视频一共识别到 37 个不同 Track其中 34 个拥有足够连续的位置数据可以计算速度。八、真实速度统计结果最终 Speed SummaryUnique Tracks: 37 Tracks With Speed: 34 Average Pixel Speed: 80.8 px/s Max Pixel Speed: 2047.1 px/s这里要注意2047.1 px/s不是2047 km/h它只是图像平面中的像素速度峰值。像素速度会受到透视关系 目标距离摄像机远近 检测框尺寸变化 目标运动方向等因素影响。所以它更适合同一视频 同一区域 相同摄像机条件下的相对速度分析。九、每一个 Track 都能单独看速度这次页面还增加了Track Speed Table每个目标都会记录Track ID Class Frames Average Speed Max Speed Distance Duration例如一个 Car Track 可以看到Average: xx px/s Max: xx px/s Distance: xxx px Duration: x.xx s而 Person、Car 等不同类别也可以分别查看。这就比单纯显示Current Speed更有用因为它能够看完整个 Track 生命周期中的运动情况。十、结果视频里可以直接看实时速度最终 Result Video 中仍然保留Bounding Box Track ID Class Confidence Trajectory Speed这样在视频播放过程中就能看到目标位置变化和速度统计同步更新。相比只看最后的表格视频更容易判断为什么这个 Track 某一段速度突然变快某个目标是不是因为靠近摄像机导致像素速度明显增加十一、为什么有 37 个 Track但只有 34 个能算速度因为速度至少需要两个有效时间点如果某个目标只出现很短时间例如1 帧或者缺乏连续有效位置那么就没有办法计算Δdistance / Δtime所以Unique Tracks和Tracks With Speed本来就不应该完全相同。这次实际是37 ↓ 34属于正常结果。十二、还加入了两点尺度标定除了 px/s这次也实现了一个简单的Two-point Calibration用户可以在视频首帧上点击P1 P2然后输入这两个点在真实世界中的已知距离。例如5 m系统就可以得到一个局部近似比例meters_per_pixel之后理论上可以把px/s转换为m/s km/h不过这种两点标定需要谨慎使用。因为道路视频存在明显透视远处 100 px和近处 100 px通常并不代表相同的真实距离。所以这种方法更适合在比较小的局部平面区域内做近似尺度估计。如果需要更严格的真实速度还应该进一步加入Homography Perspective Transform Birds-eye View做道路平面标定。十三、为什么这篇主要展示 px/s这也是我这次刻意保留的设计。如果没有足够可靠的标定信息程序就只显示px/s而不是自动生成一个xx km/h因为后者看起来更“高级”但很容易产生误导。这次真实测试中Average Pixel Speed 80.8 px/s这个数字虽然不是实际道路车速但它是从真实 Track 位置和真实视频 FPS 中计算出来的。它可以用于同一场景下比较快慢 发现异常运动 轨迹行为分析 后续标定前的数据验证十四、Speed Threshold这次还预留了Speed Threshold可以设置100 px/s或者标定以后设置10 km/h当 Track 超过阈值时可以生成SPEED_THRESHOLD Event不过这次实际测试中设置为0所以Speed Events 0没有启用阈值事件。这里也没有把它写成“超速事件”因为除非用户提供真实道路限速和可靠标定否则这里只能叫Speed Threshold Event而不是交通意义上的“超速”。十五、支持结果导出Speed Estimation 也支持Export Speed JSON Export Tracks CSV Export Speed Events CSVJSON 可以保存video metadata settings calibration speed summary tracks eventsTracks CSV 则适合后续用Excel Pandas继续统计平均速度 最大速度 运动距离 持续时间而且导出使用的是当前已经完成的结果不会重新跑 YOLO。十六、391 个测试全部通过加入 Speed Estimation 以后项目完整测试增加到391 pytest passed前端257 assertions passed新增测试主要覆盖Bottom-center Anchor Pixel Distance px/s delta_time Moving Average Median Outlier Filter Track History Average / Max Speed Distance Calibration m/s km/h Threshold Event 0 Track Stale Result Reset YOLODetectorError而之前所有模式Single Image Batch Images Video Tracking Line Crossing ROI Heatmap Multi-ROI都没有回归。十七、现在这个项目已经有 9 种模式做到这里以后整个项目已经变成Single Image ↓ Batch Images ↓ Video Detection ↓ ByteTrack Tracking ↓ Line Crossing ↓ ROI Occupancy Dwell ↓ Trajectory Heatmap ↓ Multi-ROI Flow ↓ Speed Estimation每一步其实都在回答一个新的问题。最开始这是什么后来它是不是刚才那个目标再后来它去了哪里经过了哪里在哪里停留最常走哪些区域从哪个区域去了哪个区域而现在它移动得有多快总结这次真实运行结果962 Frames 37 Unique Tracks 34 Tracks With Speed Average Pixel Speed: 80.8 px/s Max Pixel Speed: 2047.1 px/s Processing Time: 74.9 s整个速度估计链路可以概括成YOLO ↓ ByteTrack ↓ Track ID ↓ Bottom-center Anchor ↓ Frame-to-frame Distance ↓ delta_time ↓ Pixel Speed ↓ Smoothing ↓ Outlier Filtering ↓ Speed Statistics同时还加入了可选的两点尺度标定为后续px/s → m/s → km/h预留了接口。项目地址yolo-object-detection-lab:基于 FastAPI 与 Ultralytics YOLO 的图像目标检测实验项目 - AtomGit下一步如果继续往下做我准备进一步加入Perspective Calibration Birds-eye View把现在的像素速度估计继续往更接近真实世界尺度的运动分析推进。