YOLOv10车辆测速实战:Python+OpenCV构建智能交通监控系统
YOLOv10车辆测速实战:Python+OpenCV构建智能交通监控系统
在现代交通管理中,计算机视觉技术正扮演着越来越重要的角色。利用摄像头和算法实现车辆速度监测,已经从实验室走向了实际应用。本文将通过YOLOv10目标检测模型和OpenCV图像处理库,构建一个可运行的车辆测速系统,整个过程仅需基础Python开发环境即可完成。
1. 环境初始化与模型加载
首先需要搭建Python运行环境。推荐使用虚拟环境来隔离项目依赖,避免与系统其他Python版本产生冲突。以下命令序列用于创建并激活环境:
conda create --name speed_cv python=3.10 --yes
conda activate speed_cv
pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118
pip install ultralytics opencv-python-headless==4.8.0
上述命令假设使用CUDA 11.8的GPU环境。若使用CPU版本,可将PyTorch安装命令替换为:
pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cpu
YOLOv10提供了多种预训练模型,选择取决于计算资源与精度需求:
- yolov10n.pt:Nano版本,适合边缘设备或实时处理
- yolov10s.pt:Small版本,精度与速度均衡
- yolov10m.pt / yolov10l.pt / yolov10x.pt:大型模型,适合服务器级精度需求
加载模型并验证其基本功能:
from ultralytics import YOLO
# 初始化模型(以nano为例)
detector = YOLO('yolov10n.pt')
import cv2
import numpy as np
# 创建随机图像测试模型是否可用
fake_frame = np.random.randint(0, 256, (480, 640, 3), dtype=np.uint8)
output = detector(fake_frame)
print("检测结果:", output[0].boxes)
2. 视频流处理核心
车辆测速需要处理连续的视频帧,而非单张图片。OpenCV的VideoCapture类能高效读取视频文件或摄像头流。以下代码实现视频帧的循环处理:
def analyze_video(source_path, save_path=None):
"""
读取视频文件,逐帧进行目标检测
:param source_path: 输入视频路径
:param save_path: 可选,输出视频路径
"""
capture = cv2.VideoCapture(source_path)
if not capture.isOpened():
raise RuntimeError("无法打开视频源" + source_path)
# 获取视频参数
frame_rate = capture.get(cv2.CAP_PROP_FPS)
frame_width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
if save_path:
video_writer = cv2.VideoWriter(
save_path,
cv2.VideoWriter_fourcc(*'mp4v'),
frame_rate,
(frame_width, frame_height)
)
while True:
ret, frame = capture.read()
if not ret:
break # 读取完毕
# 使用YOLOv10检测
results = detector(frame)
# 后续将在此处添加测速逻辑
if save_path:
# 将检测结果绘制在帧上
annotated_frame = results[0].plot()
video_writer.write(annotated_frame)
# 显示实时画面(可选)
cv2.imshow("Detection", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
capture.release()
if save_path:
video_writer.release()
cv2.destroyAllWindows()
# 调用示例
analyze_video("traffic_clip.mp4", "output.mp4")
3. 速度计算原理与实现
计算车辆速度需要两个关键数据:
- 车辆在画面中的位移量
- 相邻帧之间的时间差
实际部署时,通常需要在场景中设置一个已知长度的参考物,比如车道标线或固定距离的标记。但作为演示,我们可以简化为利用车辆在画面中的像素位移和帧率估算相对速度。
以下代码实现了基于中心点跟踪的速度估算:
class SpeedTracker:
def __init__(self, pixels_per_meter=100, fps=30):
self.pixels_per_meter = pixels_per_meter # 像素与米的比例
self.fps = fps
self.previous_positions = {} # 存储车辆ID与上一帧位置
def calculate_speed(self, car_id, current_bbox):
"""
根据车辆边界框计算速度
:param car_id: 车辆唯一标识
:param current_bbox: [x1, y1, x2, y2]
:return: 速度(公里/小时)或None
"""
# 计算当前中心点
current_center_x = (current_bbox[0] + current_bbox[2]) / 2
current_center_y = (current_bbox[1] + current_bbox[3]) / 2
if car_id in self.previous_positions:
prev_x, prev_y = self.previous_positions[car_id]
# 计算像素位移
dx = current_center_x - prev_x
dy = current_center_y - prev_y
pixel_distance = np.sqrt(dx**2 + dy**2)
# 转换为实际距离(米)
real_distance = pixel_distance / self.pixels_per_meter
# 计算时间间隔(秒)
time_interval = 1.0 / self.fps
# 速度(米/秒)
speed_mps = real_distance / time_interval
# 转换为公里/小时
speed_kmh = speed_mps * 3.6
# 更新位置
self.previous_positions[car_id] = (current_center_x, current_center_y)
return speed_kmh
else:
# 首次出现,记录位置
self.previous_positions[car_id] = (current_center_x, current_center_y)
return None
# 在视频处理循环中使用
tracker = SpeedTracker(pixels_per_meter=100, fps=frame_rate)
while True:
ret, frame = capture.read()
if not ret:
break
results = detector(frame)
boxes = results[0].boxes.xyxy.cpu().numpy()
ids = results[0].boxes.id.cpu().numpy() # 假设yolo返回ID
for box, obj_id in zip(boxes, ids):
speed = tracker.calculate_speed(int(obj_id), box)
if speed is not None:
cv2.putText(frame, f"{speed:.1f} km/h",
(int(box[0]), int(box[1])-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
# 显示和保存帧
注意:上述代码假设YOLOv10输出车辆跟踪ID(通过track方法获得)。实际使用时,需要将detector(frame)改为detector.track(frame, persist=True)来启用跟踪功能。
4. 完整测速系统集成
将所有组件整合到一个函数中:
def speed_monitor_system(video_path, output_path, ref_distance=5.0, ref_pixels=200):
"""
完整车辆测速系统
:param video_path: 输入视频路径
:param output_path: 输出视频路径
:param ref_distance: 参考距离(米)
:param ref_pixels: 参考距离对应的像素数
"""
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
# 计算像素每米比例
ppm = ref_pixels / ref_distance
tracker = SpeedTracker(pixels_per_meter=ppm, fps=fps)
# 初始化写入器
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
writer = cv2.VideoWriter(output_path, fourcc, fps,
(int(cap.get(3)), int(cap.get(4))))
while True:
success, frame = cap.read()
if not success:
break
# 执行跟踪检测
results = detector.track(frame, persist=True)
if results[0].boxes is not None and results[0].boxes.id is not None:
boxes = results[0].boxes.xyxy.cpu().numpy()
ids = results[0].boxes.id.cpu().numpy()
cls = results[0].boxes.cls.cpu().numpy()
for i in range(len(boxes)):
if cls[i] == 2: # 只检测车辆(COCO类别中车辆类为2)
speed_val = tracker.calculate_speed(int(ids[i]), boxes[i])
if speed_val:
cv2.putText(frame, f"Speed: {speed_val:.1f} km/h",
(int(boxes[i][0]), int(boxes[i][1])-20),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
writer.write(frame)
cv2.imshow("Speed Monitor", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
writer.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
speed_monitor_system("highway.mp4", "highway_speed_output.mp4")
5. 校准与优化提示
实际部署中,测速精度高度依赖场景校准:
- 参考距离:在画面中标记一段已知长度的道路(如10米),并测量对应像素数
- 透视校正:车辆在不同纵向位置像素尺度不同,可引入透视变换矩阵进行补偿
- 稳定跟踪:使用卡尔曼滤波或排序算法减少ID切换带来的速度波动