YOLOv8 车辆速度估计实战:透视变换、跟踪与速度计算
这篇教程根据我复现车辆速度估计流程时整理,重点演示视频准备、检测跟踪、透视变换、距离映射和速度估算。
本文整理自我的学习和项目复现过程,尽量按实操顺序保留 notebook 的关键步骤,同时把数据集获取方式调整为适合中文教程发布的写法。
本文会重点跑通以下流程:
- 安装 YOLOv8 和 Supervision
- 准备车辆视频
- 配置道路透视区域和真实尺寸
- 将画面坐标映射到鸟瞰坐标
- 基于跟踪轨迹估算车辆速度
如果你正在系统学习目标检测、实例分割、OCR、多目标跟踪或视觉大模型,建议收藏本文;配套 notebook、示例图片和运行环境说明后续会继续整理。如果环境配置卡住,可以在评论区说明具体报错。
📚 文章目录
⚙️ 检查环境
先确认 GPU 和基础运行环境。
!nvidia-smi
🧩 安装依赖
安装 YOLOv8 和 Supervision。
!pip install -q supervision "ultralytics<=8.3.40"
# 关闭 Ultralytics 匿名同步
!yolo settings sync=False
📚 导入依赖
导入视频处理、检测、跟踪和速度计算模块。
import cv2
import numpy as np
import supervision as sv
from tqdm import tqdm
from ultralytics import YOLO
from supervision.assets import VideoAssets, download_assets
from collections import defaultdict, deque
🎬 准备车辆视频
下载或准备车辆行驶视频。
download_assets(VideoAssets.VEHICLES)
🔧 设置检测参数
配置模型、阈值、输入分辨率和视频路径。
SOURCE_VIDEO_PATH = "vehicles.mp4"
TARGET_VIDEO_PATH = "vehicles-result.mp4"
CONFIDENCE_THRESHOLD = 0.3
IOU_THRESHOLD = 0.5
MODEL_NAME = "yolov8x.pt"
MODEL_RESOLUTION = 1280
📐 定义透视区域
在道路画面中定义源四边形和真实世界尺寸。
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],
])
frame_generator = sv.get_video_frames_generator(source_path=SOURCE_VIDEO_PATH)
frame_iterator = iter(frame_generator)
frame = next(frame_iterator)
🖼️ 查看透视区域
把透视区域画在原始帧上,确认范围合理。
annotated_frame = frame.copy()
annotated_frame = sv.draw_polygon(scene=annotated_frame, polygon=SOURCE, color=sv.Color.red(), thickness=4)
sv.plot_image(annotated_frame)

🗺️ 坐标变换工具
定义透视变换类,将图像坐标转成鸟瞰坐标。
class 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)
view_transformer = ViewTransformer(source=SOURCE, target=TARGET)
🚗 估算车辆速度
运行 YOLOv8 检测、跟踪和速度计算,并输出结果视频。
model = YOLO(MODEL_NAME)
video_info = sv.VideoInfo.from_video_path(video_path=SOURCE_VIDEO_PATH)
frame_generator = sv.get_video_frames_generator(source_path=SOURCE_VIDEO_PATH)
# tracer initiation
byte_track = sv.ByteTrack(
frame_rate=video_info.fps, track_thresh=CONFIDENCE_THRESHOLD
)
# annotators configuration
thickness = sv.calculate_dynamic_line_thickness(
resolution_wh=video_info.resolution_wh
)
text_scale = sv.calculate_dynamic_text_scale(
resolution_wh=video_info.resolution_wh
)
bounding_box_annotator = sv.BoundingBoxAnnotator(
thickness=thickness
)
label_annotator = sv.LabelAnnotator(
text_scale=text_scale,
text_thickness=thickness,
text_position=sv.Position.BOTTOM_CENTER
)
trace_annotator = sv.TraceAnnotator(
thickness=thickness,
trace_length=video_info.fps * 2,
position=sv.Position.BOTTOM_CENTER
)
polygon_zone = sv.PolygonZone(
polygon=SOURCE,
frame_resolution_wh=video_info.resolution_wh
)
coordinates = defaultdict(lambda: deque(maxlen=video_info.fps))
# open target video
with sv.VideoSink(TARGET_VIDEO_PATH, video_info) as sink:
# loop over source video frame
for frame in tqdm(frame_generator, total=video_info.total_frames):
result = model(frame, imgsz=MODEL_RESOLUTION, verbose=False)[0]
detections = sv.Detections.from_ultralytics(result)
# filter out detections by class and confidence
detections = detections[detections.confidence > CONFIDENCE_THRESHOLD]
detections = detections[detections.class_id != 0]
# filter out detections outside the zone
detections = detections[polygon_zone.trigger(detections)]
# refine detections using non-max suppression
detections = detections.with_nms(IOU_THRESHOLD)
# pass detection through the tracker
detections = byte_track.update_with_detections(detections=detections)
points = detections.get_anchors_coordinates(
anchor=sv.Position.BOTTOM_CENTER
)
# calculate the detections position inside the target RoI
points = view_transformer.transform_points(points=points).astype(int)
# store detections position
for tracker_id, [_, y] in zip(detections.tracker_id, points):
coordinates[tracker_id].append(y)
# format labels
labels = []
for tracker_id in detections.tracker_id:
if len(coordinates[tracker_id]) < video_info.fps / 2:
labels.append(f"#{tracker_id}")
else:
# calculate speed
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")
# annotate frame
annotated_frame = frame.copy()
annotated_frame = trace_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = bounding_box_annotator.annotate(
scene=annotated_frame, detections=detections
)
annotated_frame = label_annotator.annotate(
scene=annotated_frame, detections=detections, labels=labels
)
# add frame to target video
sink.write_frame(annotated_frame)
📌 小结
这篇教程完整整理了 YOLOv8 车辆速度估计 的核心复现流程。实际操作时,建议先确认 GPU、依赖版本、数据集路径和模型权重路径,再逐段运行 notebook。
后续我会继续按源项目顺序整理同系列中的目标检测、实例分割、OCR、多目标跟踪和视觉大模型教程。

被折叠的 条评论
为什么被折叠?



