Supervision 车辆速度估算实战:基于检测 + ByteTrack 与透视变换的实时测速指南

发布时间:2026/9/8 19:33:36
Supervision 车辆速度估算实战:基于检测 + ByteTrack 与透视变换的实时测速指南 Supervision 车辆速度估算实战基于检测 ByteTrack 与透视变换的实时测速指南【免费下载链接】supervisionWe write your reusable computer vision tools. 项目地址: https://gitcode.com/GitHub_Trending/su/supervision本篇技术指南基于 supervision 仓库中的官方速度估算示例examples/speed_estimation编写。它演示了如何将目标检测、ByteTrack 多目标跟踪与单应性透视变换结合起来从普通道路交通视频中估算车辆速度并通过 supervision 的标注工具实时绘制每车实时 km/h标签与运动轨迹。读完本文你将掌握该示例的核心标定原理、四种检测后端RF-DETR / Roboflow Inference / Ultralytics / 遗留 YOLO-NAS的完整运行方式以及每个命令行参数与关键源码逻辑的含义并能够把同样的点位移 时间 换算套路迁移到自己的测速或计数项目中。示例的整体思路速度不是测出来的而是算出来的单目摄像头没有深度信息无法直接从像素位移换算出物理速度。因此该示例采用的是一条工程上成熟的路线先在视频帧中检测并跟踪车辆把每个跟踪 ID 的锚点车辆底部中心投影到真实世界的地平面坐标系再用地平面坐标的位移除以时间得到速度。整体流程可以拆分为以下环节逐帧推理由目标检测模型输出Detections检测框 类别 置信度区域过滤只保留落在多边形区域PolygonZone即画面中靠近摄像机的路面车道区内的检测多目标跟踪用sv.ByteTrack为每个车辆分配稳定 ID坐标投影取每个检测框的 BOTTOM_CENTER 锚点经单应矩阵投影到虚拟俯视地面坐标系滑动缓冲以defaultdict(lambda: deque(maxlenfps))为每个 tracker_id 保存最近约 1 秒的投影纵坐标历史速度换算用历史首尾点的距离差 / 经历时间 × 3.6 得到 km/h可视化把测得的#tracker_id speed km/h标签、检测框和轨迹叠加回原帧。这一思路完全由 supervision 公开 API 拼装而成不依赖任何私有逻辑追踪 ID 由 ByteTrack 提供见 ByteTrack 核心实现其update_with_detections负责在帧间维护轨迹关联见 core.py。选择检测后端四种变体的取舍示例目录在examples/speed_estimation/下提供了四个结构几乎一致的入口脚本方便在同一套下游代码上替换检测模型脚本检测后端状态说明rfdetr_example.pyRF-DETRRFDETRMedium推荐predict直接返回Detections无需转换步骤并内置VEHICLE_CLASS_IDS过滤 with_nmsinference_example.pyRoboflow Inferenceget_roboflow_model支持需要 Roboflow API Key默认模型rfdetr-smallultralytics_example.pyUltralytics YOLO默认yolo11x.pt支持使用Detections.from_ultralytics转换结果yolo_nas_example.pyYOLO-NASsuper-gradients遗留参考README 明确不推荐新项目使用仅保留作为参照推荐 RF-DETR 的核心原因在 README 中写得很清楚其predict方法的返回结果已经是Detections对象省去了从框架结果到 supervision 格式的转换适配层。而其他框架则需要显式转换例如 Ultralytics 侧调用sv.Detections.from_ultralytics(result)见 ultralytics_example.py、Inference 侧调用sv.Detections.from_inference(results)见 inference_example.py。从源码结构看RF-DETR 变体还额外做了两件增强见 rfdetr_example.pyVEHICLE_CLASS_IDS [3, 4, 6, 8]将检测结果限定在 COCO 车辆类别3 为 car、4 为 motorcycle、6 为 bus、8 为 truck避免行人等非机动车目标进入测速流程用detections.with_nms(thresholdiou_threshold)做类内 NMS 后再进入区域过滤与跟踪。这意味着如果你想测速的目标不是车辆或你只关心其中某一类车改动VEHICLE_CLASS_IDS即可完成筛选无需触碰任何 supervision 代码。环境准备与依赖安装安装步骤遵循 README 的顺序三条命令各司其职# 1. 克隆仓库并进入示例目录--depth 1 只拉最新快照 git clone --depth 1 -b develop https://github.com/roboflow/supervision.git cd supervision/examples/speed_estimation # 2.可选创建并激活隔离的 Python 虚拟环境 uv venv source .venv/bin/activate # 3. 安装示例依赖 uv pip install -r requirements.txtrequirements.txt 同时包含了四套变体的依赖supervision核心库提供跟踪、标注、视频 IO 等能力rfdetr/ultralytics/inference分别对应 RF-DETR、Ultralytics 与 Roboflow Inference 三种检测后端super-gradients3.5.0仅被遗留脚本yolo_nas_example.py使用README 注释已标明该依赖服务于 legacy 参照如果不需要跑 YOLO-NAS 变体可以不安装jsonargparse[signatures]示例脚本统一用它基于函数签名自动生成 CLI 参数requests/tqdm用于下面的视频资源下载。依赖就绪后下载示例视频用于测试的vehicles.mp4python video_downloader.pyvideo_downloader.py 内部会创建data/目录并调用 supervision 的资源下载能力download_assets(VideoAssets.VEHICLES)把官方示例视频拉取到data/vehicles.mp4。该能力的实现在 src/supervision/assets/downloader.py视频后处理与编码均通过 supervision 的 Video API如 src/supervision/utils/video.py完成。脚本参数详解从--device到--iou_threshold示例脚本不使用手写 argparse而是让jsonargparse.auto_cli(main, ...)根据main()的函数签名自动生成参数见任意示例脚本的__main__段。因此每个参数都对应main的一个带默认值的入参README 对各参数的定义如下参数适用脚本必填默认值含义--source_video_path全部是—待分析的输入视频路径是全部推理与测速的数据来源--target_video_path全部否—标注结果视频的保存路径若缺省则不落盘改为实时窗口显示RF-DETR 变体在target_video_pathNone时直接进入sv.ImageWindow展示分支见 rfdetr_example.py--source_weights_path历史说明保留——原文档中定义为 YOLO 权重路径当前版本各脚本已改为从依赖模型工厂自动加载权重此参数主要面向自行指定权重的定制场景--confidence_threshold全部否0.3置信度过滤阈值决定模型需要多确信才认定目标同时透传给 ByteTrack 的track_activation_threshold--iou_threshold全部否0.7NMS 的 IoU 阈值用于抑制重叠框、区分不同目标--devicerfdetr / ultralytics否cpu计算设备可选cpu、mps、cuda--roboflow_api_keyinference 变体否二选一环境变量Roboflow API Key不传时回退读取ROBOFLOW_API_KEY环境变量两者皆无则抛错退出--model_idinference 变体否rfdetr-small指定 Roboflow 模型 ID值得注意的实现细节有两个分别对应推理与跟踪两个阶段置信度阈值是双重透传的它既传给模型推理如 Ultralytics 的model(frame, conf...)见 ultralytics_example.py又被用作 ByteTrack 的track_activation_threshold见各示例中的sv.ByteTrack(frame_ratevideo_info.fps, track_activation_thresholdconfidence_threshold)。跟踪器只对高于该阈值的检测激活新轨迹避免低置信度的碎片检测把轨迹 ID 打乱。RF-DETR 变体的main返回签名略有不同其target_video_path默认值就是None实时预览模式因此它天然支持不保存、仅预览而 inference/ultralytics/yolo_nas 变体的签名要求显式传入输出路径。运行前留意这一点即可。关于推理设备RF-DETR 变体通过RFDETRMedium(devicedevice)传入cpu/mps/cuda若机器没有 GPU默认cpu即可完成全部流程只是推理速率取决于硬件。运行四种变体一条命令跑通测速管线在完成数据下载后可直接按以下命令运行下文默认已下载data/vehicles.mp4输出写入data/vehicles-result.mp4。RF-DETR推荐python rfdetr_example.py \ --source_video_path data/vehicles.mp4 \ --target_video_path data/vehicles-result.mp4 \ --confidence_threshold 0.3 \ --iou_threshold 0.5Roboflow Inferencepython inference_example.py \ --roboflow_api_key ROBOFLOW_API_KEY \ --source_video_path data/vehicles.mp4 \ --target_video_path data/vehicles-result.mp4 \ --confidence_threshold 0.3 \ --iou_threshold 0.5不传--roboflow_api_key时脚本会尝试读取ROBOFLOW_API_KEY环境变量两者均缺失会直接抛出ValueError见 inference_example.py。UltralyticsYOLOv8 / YOLO11python ultralytics_example.py \ --source_video_path data/vehicles.mp4 \ --target_video_path data/vehicles-result.mp4 \ --confidence_threshold 0.3 \ --iou_threshold 0.5YOLO-NAS遗留参照不推荐新项目python yolo_nas_example.py \ --source_video_path data/vehicles.mp4 \ --target_video_path data/vehicles-result.mp4 \ --confidence_threshold 0.3 \ --iou_threshold 0.5运行结束后在输出视频中可以看到每个跟踪框上实时刷新的#ID 数字 km/h标签与两秒长度的轨迹拖尾trace_lengthint(video_info.fps * 2)约合 2 秒。若--target_video_path缺省程序会弹出一个标题为frame的窗口实时预览按q即可结束。标定原理SOURCE 与 TARGET 为什么决定测速准确性README 使用醒目提醒IMPORTANT强调如果要把脚本用于你自己的视频SOURCE与TARGET必须针对每一个摄像头视角单独重新标定。这是整个测速管线中最关键也最容易被忽视的一步。以 ultralytics_example.py 中的定义为模板SOURCE np.array([[1252, 787], [2298, 803], [5039, 2159], [-550, 2159]]) TARGET_WIDTH 25 TARGET_HEIGHT 250 TARGET np.array( [ [0, 0], [TARGET_WIDTH - 1, 0], [TARGET_WIDTH - 1, TARGET_HEIGHT - 1], [0, TARGET_HEIGHT - 1], ] )标定的物理解释SOURCE是图像坐标系中的四个点通常取自路面上的一个四边形如两条车道线在远处、近处与画面的四个交点它应恰好覆盖车辆以接近匀速行驶通过的一段真实路面TARGET是这四个点对应的地平面俯视坐标。这里把四边形拉直成宽 25、高 250 的竖直长条——这是一个有物理含义的选择若该四边形对应真实世界的一条长约 25 米、横跨约 2.5 米数值需按实际画面语义理解的路段则y轴恰好与行车方向对齐。两点之间的真实长度可以在源码之外通过测量路段得到据此推算出比例尺投影后的y位移即近似真实世界中的纵向位移。测速只用了投影后的y见下文速度计算逻辑因此行车方向与四边形长边一致时结果才可靠。ViewTransformer把像素点投影到地平面速度计算的前提是每个跟踪框在哪个地平面位置。示例用一个不到二十行的ViewTransformer完成这件事各脚本实现一致参考 rfdetr_example.pyclass ViewTransformer: def __init__(self, source: np.ndarray, target: np.ndarray) - None: source source.astype(np.float32) target target.astype(np.float32) self.m cv2.getPerspectiveTransform(source, target) def transform_points(self, points: np.ndarray) - np.ndarray: if points.size 0: return points reshaped_points points.reshape(-1, 1, 2).astype(np.float32) transformed_points cv2.perspectiveTransform(reshaped_points, self.m) return transformed_points.reshape(-1, 2)它的原理建立在 OpenCV 的单应矩阵上cv2.getPerspectiveTransform(source, target)由四组对应点解出 3×3 透视变换矩阵m——这是把斜视角路面矫正为俯视图的核心假设即路面近似为平面cv2.perspectiveTransform把任意像素坐标批量投影到目标地平面坐标系空输入直接短路返回避免空检测帧触发底层异常。在推理循环中投影的输入点取的是检测框底部中心锚点车辆与地面接触点points detections.get_anchors_coordinates(anchorsv.Position.BOTTOM_CENTER) points view_transformer.transform_points(pointspoints).astype(int)get_anchors_coordinates是Detections的标准锚点方法见 src/supervision/detection/core.py。选 BOTTOM_CENTER 而非框中心是刻意的同一辆车即便框高随远近变化其底边仍贴地投影误差远小于质心。速度计算逻辑位移、时间窗口与 km/h 换算跟踪 ID 稳定后每个 ID 的投影纵坐标被写入一个最近 1 秒滑动窗口coordinates defaultdict(lambda: deque(maxlenint(video_info.fps)))deque(maxlenfps)使得缓冲区自动只保留最近约fps个观测——也就是内存中始终只有每个目标最近 1 秒的运动历史超期数据自动丢弃不会无限膨胀。三个变体ultralytics/inference/yolo_nas在主循环内使用同一套朴素算法以 ultralytics_example.py 为例for tracker_id in detections.tracker_id: if len(coordinates[tracker_id]) video_info.fps / 2: labels.append(f#{tracker_id}) else: coordinate_start coordinates[tracker_id][-1] coordinate_end coordinates[tracker_id][0] distance abs(coordinate_start - coordinate_end) time len(coordinates[tracker_id]) / video_info.fps speed distance / time * 3.6 labels.append(f#{tracker_id} {int(speed)} km/h)其中用到的常量换算关系是观测不足半秒 fps / 2个样本时只显示#tracker_id不估算速度避免起步/刚入镜的车辆测出瞬时噪声值位移取缓冲区最后一个与第一个观测的投影纵坐标差deque[-1]是最新值即最近约 1 秒的净位移时间 样本数 ÷ fps米/秒到千米/小时需要乘以3.6因为 1 m/s 3.6 km/h。RF-DETR 变体对这一逻辑做了更严谨的重构把公式提取成独立函数见 rfdetr_example.pydef calculate_speed(distance: float, elapsed_frames: int, fps: float) - float: if elapsed_frames 1: raise ValueError(At least one elapsed frame is required to calculate speed.) elapsed_time elapsed_frames / fps return distance / elapsed_time * 3.6区别在于RF-DETR 变体的历史缓冲保存的是(frame_index, y)二元组计算时使用history[-1][0] - history[0][0]真实帧号差作为时间跨度而非简单用样本数量折算。这意味着当检测存在丢帧时RF-DETR 变体的时间估计仍能反映真实经过的帧间隔而按样本数折算的朴素版本会把丢帧后的观测间隔当成正常间隔。仓库为此专门写了回归测试 tests/test_speed_estimation_example.py 验证三种情形calculate_speed(distance14, elapsed_frames14, fps30) 108.0无丢帧时的标准换算calculate_speed(distance14, elapsed_frames2, fps30) 756.0同样位移、帧间隔更短代表丢帧后速率显著上升elapsed_frames 0时抛出ValueError杜绝除零。用 supervision 组件把结果画回视频测速标签算好后叠加与输出完全交给 supervision 的标注器与视频组件这一段在四种变体中几乎一致。以 ultralytics_example.py 为例一个完整的可视化栈由四层组成自适应文字与线宽sv.calculate_optimal_line_thickness与sv.calculate_optimal_text_scale根据video_info.resolution_wh自动推导合适尺寸实现见 src/supervision/draw/utils.py避免 4K 视频上出现过细线、低清视频上出现大字的比例失衡逐帧的推理 → 过滤 → 跟踪 → 标注循环detections sv.Detections.from_ultralytics(result) detections detections[polygon_zone.trigger(detections)] detections byte_track.update_with_detections(detectionsdetections)polygon_zone.trigger(detections)返回布尔掩码只保留落在路面多边形内的检测实现见 src/supervision/detection/tools/polygon_zone.py随后byte_track.update_with_detections完成跨帧关联与 ID 分配见 src/supervision/tracker/byte_tracker/core.py。三个标注器叠加annotated_frame trace_annotator.annotate(sceneannotated_frame, detectionsdetections) annotated_frame box_annotator.annotate(sceneannotated_frame, detectionsdetections) annotated_frame label_annotator.annotate( sceneannotated_frame, detectionsdetections, labelslabels)BoxAnnotator画检测框LabelAnnotator以BOTTOM_CENTER位置把#ID 速度 km/h文本贴在框底TraceAnnotator用最近fps × 2帧的锚点画出轨迹拖尾让人一眼看出车辆运动方向。写盘与实时预览sv.VideoSink(target_video_path, video_info)按源视频的帧率与分辨率把标注帧编码为输出文件当没有目标路径时RF-DETR 变体则改用sv.ImageWindow(frame)弹出实时窗口按q键退出。把示例改造成你自己的测速项目想把这个示例用在自有摄像头视角上只需做四处调整且大部分改动都不涉及 supervision 内部重新标定SOURCE在你的视频首帧上框选一段真实路面建议选取车辆以大致恒定速度通过、且长度可通过实测量得的一段路面得到四个图像坐标替换到每个脚本顶部的常量中修正地平面比例确认TARGET的宽高比与这段路面的真实长宽成比例必要时修改TARGET_WIDTH / TARGET_HEIGHT使投影后的纵向位移更贴近真实米数确认类别列表若用 RF-DETR 变体按需增删VEHICLE_CLASS_IDSCOCO 中 3car、4motorcycle、6bus、8truck例如只测私家车可改为[3]按硬件选择--device与模型有 GPU 用cudaMac 可试mps追求精度把置信度下限从 0.3 调高画面内车辆过密时适当降低--iou_threshold以抑制重叠误检。将测得的每车速度用于超速告警、路段平均车速统计或流量分析时建议理解一个前提该方案的速度精度上限取决于标定四边形与真实路段的贴合度、跟踪 ID 在遮挡下是否稳定以及车辆是否近似直线通过标定区域——任何破坏地平面假设的强坡度路面或频繁变道都会放大误差。因此官方教程也强调 SOURCE/TARGET 必须按每个摄像头视角单独调整见 README 中的 IMPORTANT 提示。许可证说明该示例集成了多个独立组件各自遵循不同许可证动手集成前需要分别确认RF-DETR推荐变体所采用的检测模型遵循宽松的 Apache-2.0 许可证Ultralytics / YOLOv8ultralytics变体遵循 AGPL-3.0 许可证商业闭源使用前需评估其传染性条款supervision本示例中承担跟踪、区域过滤、标注与视频处理的基础库遵循 MIT 许可证可自由用于商业项目YOLO-NAS 与super-gradients作为遗留参考依赖仅存在于需要运行yolo_nas_example.py的场景。README 同时建议把 examples/speed_estimation/README.md 视为使用入口其中保留着官方配套的视频演示与逐步讲解指引仓库根目录的 LICENSE.md 与 README.md 则提供了 supervision 库本身的完整授权与生态说明。【免费下载链接】supervisionWe write your reusable computer vision tools. 项目地址: https://gitcode.com/GitHub_Trending/su/supervision创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考