
利用 YOLO 和 OpenCV 实现车辆计数系统:从原理到实践
利用 YOLO 和 OpenCV 实现车辆计数系统:从原理到实践
引言
在当今的智能交通管理、停车场监控等场景中,准确地统计车辆数量是一项至关重要的任务。传统的车辆计数方法往往效率低下且容易出错,而计算机视觉技术的发展为我们提供了更高效、准确的解决方案。本文将详细介绍如何结合 YOLO(You Only Look Once)目标检测算法和 OpenCV 库,实现一个简单而实用的车辆计数系统。
技术背景
YOLO 算法
YOLO 是一种快速且高效的目标检测算法,它能够在图像或视频中实时检测出多个目标。YOLO 将目标检测问题转化为一个回归问题,通过一次前向传播就能预测出目标的边界框和类别。不同版本的 YOLO 不断迭代优化,如我们使用的 YOLOv8,具有更快的检测速度和更高的准确率,并且提供了预训练模型,方便我们快速应用到实际项目中。
OpenCV 库
OpenCV 是一个广泛应用于计算机视觉领域的开源库,它提供了丰富的图像处理和计算机视觉算法。在我们的车辆计数系统中,OpenCV 主要用于视频读取、图像预处理、绘制检测结果等操作,是实现整个系统不可或缺的工具。
系统实现步骤
1. 环境准备与模型加载
首先,我们需要安装所需的库,主要包括 ultralytics 和 opencv-python。可以使用以下命令进行安装:
pip install ultralytics opencv-python
然后加载预训练的 YOLOv8 模型:
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
这里我们选择了 yolov8n.pt 模型,它是 YOLOv8 系列中的轻量级模型,适合在 CPU 或 AMD 核显上运行,速度较快。
2. 视频读取与处理
使用 OpenCV 打开视频文件,并逐帧进行处理:
import cv2
cap = cv2.VideoCapture("car two.mp4")
if not cap.isOpened():
print("错误:无法打开视频文件,请检查路径!")
exit()
3. 鼠标交互功能
为了实现用户自定义检测区域,我们添加了鼠标交互功能,允许用户通过鼠标绘制多边形区域,只对该区域内的车辆进行计数。以下是相关代码:
# 鼠标画圈相关全局变量
drawing = False
current_line = []
polygons = []
# 鼠标回调函数:画闭合多边形(仅边框,不填充)
def draw_callback(event, x, y, flags, param):
global drawing, current_line
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
current_line = [(x, y)]
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
current_line.append((x, y))
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
if len(current_line) > 2:
polygons.append(current_line.copy())
current_line = []
# 判断点是否在任意多边形内(用于只统计圈内车辆)
def is_point_in_polygon(point):
if not polygons:
return True # 未画圈时,默认统计所有车辆
for poly in polygons:
pts = np.array(poly, np.int32)
if cv2.pointPolygonTest(pts, point, False) >= 0:
return True
return False
cv2.namedWindow("YOLOv8 车辆计数", cv2.WINDOW_NORMAL)
cv2.resizeWindow("YOLOv8 车辆计数", 960, 540)
cv2.setMouseCallback("YOLOv8 车辆计数", draw_callback)
4. 目标检测与车辆计数
在主循环中,我们对每一帧视频进行处理,使用 YOLOv8 模型进行车辆检测,并根据条件进行车辆计数:
car_count = 0
counted_box_ids = set() # 存储已计数车辆的唯一ID
while True:
ret, frame = cap.read()
if not ret:
break # 视频播放完毕,退出循环
# YOLOv8 推理(仅检测车辆类:2=汽车,5=巴士,7=卡车)
results = model(frame, classes=[2, 5, 7], conf=0.3, verbose=False)
for r in results:
boxes = r.boxes
for box in boxes:
# 获取检测框坐标
x1, y1, x2, y2 = map(int, box.xyxy[0])
w, h = x2 - x1, y2 - y1
# 过滤过小的检测框(避免误检)
if w < MIN_W or h < MIN_H:
continue
# 计算检测框中心点
center_x = (x1 + x2) // 2
center_y = (y1 + y2) // 2
center_point = (center_x, center_y)
# 检查中心点是否在多边形内
if not is_point_in_polygon(center_point):
continue
# 绘制检测框和中心点
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.circle(frame, center_point, 5, (0, 0, 255), -1)
# 检查中心点是否通过检测线
if abs(center_y - LINE_Y) <= OFFSET and box.id not in counted_box_ids:
car_count += 1
counted_box_ids.add(box.id)
# 绘制检测线
cv2.line(frame, (0, LINE_Y), (frame.shape[1], LINE_Y), (255, 0, 0), 2)
# 显示车辆计数
cv2.putText(frame, f"Car Count: {car_count}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 绘制多边形
for poly in polygons:
pts = np.array(poly, np.int32).reshape((-1, 1, 2))
cv2.polylines(frame, [pts], True, (255, 255, 0), 2)
# 显示结果
cv2.imshow("YOLOv8 车辆计数", frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
代码解释
- 模型推理:
model(frame, classes=[2, 5, 7], conf=0.3, verbose=False)对当前帧进行推理,只检测汽车(类别 2)、巴士(类别 5)和卡车(类别 7),置信度阈值设为 0.3。 - 过滤小目标:通过设定
MIN_W和MIN_H过滤掉过小的检测框,避免误检。 - 中心点计算:计算检测框的中心点,用于判断车辆是否通过检测线。
- 多边形区域判断:使用
is_point_in_polygon函数判断车辆中心点是否在用户绘制的多边形区域内。 - 车辆计数:当车辆中心点通过检测线且未被计数时,车辆计数加 1。
总结
通过结合 YOLO 和 OpenCV,我们成功实现了一个简单的车辆计数系统。该系统不仅可以准确地统计车辆数量,还允许用户自定义检测区域,增加了系统的灵活性和实用性。较上一篇只利用OpenCV进行车辆检测,这里引用YOLOv8模型,更加精确与方便。
在未来,我们可以进一步优化该系统,例如:
- 提高检测的准确性,通过调整模型参数或使用更强大的预训练模型。
- 增加更多的功能,如车辆速度检测、车型分类等。
- 实现多摄像头的车辆计数,扩大监控范围。
通过行车检测项目,已经完成了对OpenCV的基本学习,下一步将对模型训练与算法进行学习。