目标检测是计算机视觉领域的重要任务之一,而YOLO(You Only Look Once)系列算法因其速度和精度的平衡而广受欢迎。YOLOv8作为该系列的最新版本,在性能和易用性上都有了显著提升。本文将介绍如何利用YOLOv8进行视频中划定区域的目标统计计数,这是一个在实际应用中非常有用的功能,如交通流量统计、商场人流量监测等。
YOLOv8在以下几个方面进行了重要改进:
from ultralytics import YOLO
# 加载预训练模型
model = YOLO('yolov8n.pt') # 可以替换为yolov8s/m/l/x.pt
# 进行目标检测
results = model('image.jpg')
# 显示结果
results[0].show()
实现视频划定区域目标统计计数的基本流程:
import cv2
import numpy as np
from collections import defaultdict
from ultralytics import YOLO
class VideoROICounter:
def __init__(self, model_path, video_path, roi_points):
"""
初始化视频ROI计数器
:param model_path: YOLOv8模型路径
:param video_path: 视频文件路径
:param roi_points: ROI多边形顶点坐标列表
"""
self.model = YOLO(model_path)
self.cap = cv2.VideoCapture(video_path)
self.roi_points = np.array(roi_points, np.int32)
self.roi_mask = None
self.track_history = defaultdict(list)
self.in_count = 0
self.out_count = 0
self.prev_positions = {}
# 获取视频基本信息
self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.fps = self.cap.get(cv2.CAP_PROP_FPS)
# 初始化ROI mask
self._init_roi_mask()
def _init_roi_mask(self):
"""初始化ROI掩码"""
self.roi_mask = np.zeros((self.height, self.width), dtype=np.uint8)
cv2.fillPoly(self.roi_mask, [self.roi_points], 255)
def _is_inside_roi(self, point):
"""判断点是否在ROI内"""
return cv2.pointPolygonTest(self.roi_points, point, False) >= 0
def _draw_roi(self, frame):
"""在帧上绘制ROI"""
cv2.polylines(frame, [self.roi_points], True, (0, 255, 0), 2)
overlay = frame.copy()
cv2.fillPoly(overlay, [self.roi_points], (0, 255, 0))
frame = cv2.addWeighted(overlay, 0.1, frame, 0.9, 0)
return frame
def _update_counts(self, track_id, current_pos):
"""更新计数"""
if track_id in self.prev_positions:
prev_pos = self.prev_positions[track_id]
prev_inside = self._is_inside_roi(prev_pos)
curr_inside = self._is_inside_roi(current_pos)
if not prev_inside and curr_inside:
self.in_count += 1
elif prev_inside and not curr_inside:
self.out_count += 1
self.prev_positions[track_id] = current_pos
def process_video(self, output_path=None, classes_of_interest=None):
"""
处理视频并统计ROI内的目标
:param output_path: 输出视频路径,None则不保存
:param classes_of_interest: 感兴趣的类别列表,None则统计所有类别
"""
if output_path:
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_path, fourcc, self.fps, (self.width, self.height))
while self.cap.isOpened():
ret, frame = self.cap.read()
if not ret:
break
# 进行目标检测和跟踪
results = self.model.track(frame, persist=True, classes=classes_of_interest)
if results[0].boxes.id is not None:
boxes = results[0].boxes.xywh.cpu()
track_ids = results[0].boxes.id.int().cpu().tolist()
clss = results[0].boxes.cls.cpu().tolist()
for box, track_id, cls in zip(boxes, track_ids, clss):
x, y, w, h = box
center = (int(x), int(y))
# 更新轨迹历史
track = self.track_history[track_id]
track.append(center)
if len(track) > 30: # 保留最近的30个点
track.pop(0)
# 更新计数
self._update_counts(track_id, center)
# 绘制轨迹
points = np.array(track, dtype=np.int32).reshape((-1, 1, 2))
cv2.polylines(frame, [points], isClosed=False, color=(0, 255, 255), thickness=2)
# 绘制ROI和计数信息
frame = self._draw_roi(frame)
cv2.putText(frame, f"In: {self.in_count}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(frame, f"Out: {self.out_count}", (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 显示结果
cv2.imshow("ROI Counting", frame)
if output_path:
out.write(frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
self.cap.release()
if output_path:
out.release()
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
# 定义ROI多边形顶点(顺时针或逆时针顺序)
roi_points = [(300, 200), (800, 200), (900, 600), (200, 600)]
# 创建计数器实例
counter = VideoROICounter(
model_path="yolov8n.pt",
video_path="input_video.mp4",
roi_points=roi_points
)
# 处理视频(只统计人和车)
counter.process_video(
output_path="output_video.mp4",
classes_of_interest=[0, 2] # 0: person, 2: car in COCO dataset
)
可以扩展代码实现多个独立ROI的计数:
class MultiROICounter(VideoROICounter):
def __init__(self, model_path, video_path, roi_list):
"""
:param roi_list: 多个ROI的列表,每个ROI是一个顶点坐标列表
"""
super().__init__(model_path, video_path, [])
self.roi_list = [np.array(roi, np.int32) for roi in roi_list]
self.roi_masks = [self._create_roi_mask(roi) for roi in self.roi_list]
self.counters = [{'in': 0, 'out': 0} for _ in self.roi_list]
def _create_roi_mask(self, roi_points):
mask = np.zeros((self.height, self.width), dtype=np.uint8)
cv2.fillPoly(mask, [roi_points], 255)
return mask
def _is_inside_any_roi(self, point):
for i, roi in enumerate(self.roi_list):
if cv2.pointPolygonTest(roi, point, False) >= 0:
return i
return -1
# 重写_update_counts方法实现多ROI计数
# ...
改进计数逻辑,只统计特定方向移动的目标:
def _update_counts_with_direction(self, track_id, current_pos):
if track_id in self.prev_positions:
prev_pos = self.prev_positions[track_id]
prev_inside = self._is_inside_roi(prev_pos)
curr_inside = self._is_inside_roi(current_pos)
# 计算移动方向(只关心y轴方向移动)
direction = current_pos[1] - prev_pos[1]
if not prev_inside and curr_inside and direction < 0: # 向上移动进入
self.in_count += 1
elif prev_inside and not curr_inside and direction > 0: # 向下移动离开
self.out_count += 1
在交通监控视频中,统计特定车道内的车辆数量:
# 定义车道ROI
lane_roi = [(300, 400), (500, 400), (550, 600), (250, 600)]
counter = VideoROICounter(
model_path="yolov8l.pt", # 使用更大的模型提高车辆检测精度
video_path="traffic.mp4",
roi_points=lane_roi
)
# 只统计车辆类(COCO数据集中2,3,5,7等是车辆类)
counter.process_video(classes_of_interest=[2, 3, 5, 7])
统计商场入口进出人数:
# 定义门口区域ROI
entrance_roi = [(200, 300), (600, 300), (600, 500), (200, 500)]
counter = VideoROICounter(
model_path="yolov8s.pt", # 对小尺寸人检测足够
video_path="mall_entrance.mp4",
roi_points=entrance_roi
)
# 只统计人类(COCO class 0)
counter.process_video(classes_of_interest=[0])
本文详细介绍了如何利用YOLOv8实现视频中划定区域的目标统计计数。通过合理的ROI定义和跟踪计数逻辑,我们可以构建出实用的视频分析系统。未来可以进一步探索:
YOLOv8的强大检测和跟踪能力为视频分析应用提供了坚实基础,结合特定业务逻辑可以开发出各种实用的智能视频分析系统。