文章目录
YOLOv8改进 | 进阶实战篇:利用YOLOv8进行视频划定区域目标统计计数
1. 引言
在计算机视觉领域,实时目标检测一直是研究的热点。YOLO(You Only Look Once)系列作为其中的佼佼者,以其速度和精度的平衡著称。YOLOv8作为最新版本,在性能和易用性上都有了显著提升。本文将深入探讨如何利用YOLOv8实现视频中划定区域的目标统计计数,这是一个在实际应用中非常有价值的场景,如交通流量统计、商场人流量监测等。
2. YOLOv8基础回顾
2.1 YOLOv8架构概述
YOLOv8采用了一种新的骨干网络和neck设计,相比前代在精度和速度上都有提升。其主要特点包括:
- 更高效的CSP结构
- 改进的PANet neck
- Anchor-free检测头
- 更优的损失函数设计
2.2 YOLOv8的安装与基本使用
# 安装Ultralytics包
pip install ultralytics
# 基本检测示例
from ultralytics import YOLO
# 加载预训练模型
model = YOLO('yolov8n.pt') # 使用nano版本
# 进行检测
results = model('image.jpg')
results[0].show()
3. 视频划定区域目标统计的实现
3.1 核心思路
实现视频划定区域目标统计需要以下几个关键步骤:
- 视频帧读取与处理
- 使用YOLOv8进行目标检测
- 定义感兴趣区域(ROI)
- 目标与ROI的位置关系判断
- 计数逻辑实现
- 结果可视化
3.2 完整实现代码
import cv2
import numpy as np
from ultralytics import YOLO
from collections import defaultdict
class VideoROICounter:
def __init__(self, model_path='yolov8n.pt', classes=None):
self.model = YOLO(model_path)
self.classes = classes # 指定要统计的类别
self.roi = None # 感兴趣区域
self.counts = defaultdict(int) # 计数结果
self.track_history = defaultdict(list) # 跟踪历史
def set_roi(self, points):
"""设置多边形ROI区域"""
self.roi = np.array(points, np.int32)
self.roi = self.roi.reshape((-1, 1, 2))
def is_inside_roi(self, x, y):
"""判断点是否在ROI内"""
if self.roi is None:
return True
return cv2.pointPolygonTest(self.roi, (x, y), False) >= 0
def process_frame(self, frame):
"""处理单帧图像"""
# 执行检测
results = self.model.track(frame, persist=True, classes=self.classes)
# 获取检测结果
boxes = results[0].boxes.xywh.cpu()
track_ids = results[0].boxes.id.int().cpu().tolist() if results[0].boxes.id is not None else []
clss = results[0].boxes.cls.cpu().tolist()
# 绘制ROI区域
if self.roi is not None:
cv2.polylines(frame, [self.roi], True, (0, 255, 0), 2)
# 处理每个检测结果
for box, track_id, cls_id in zip(boxes, track_ids, clss):
x, y, w, h = box
center = (int(x), int(y))
# 检查是否在ROI内
if self.is_inside_roi(center[0], center[1]):
# 更新跟踪历史
track = self.track_history[track_id]
track.append(center)
if len(track) > 30: # 保留最近的30个点
track.pop(0)
# 绘制轨迹
points = np.array(track, dtype=np.int32).reshape((-1, 1, 2))
cv2.polylines(frame, [points], False, (0, 255, 255), 2)
# 绘制边界框
cv2.rectangle(frame,
(int(x - w/2), int(y - h/2)),
(int(x + w/2), int(y + h/2)),
(0, 255, 0), 2)
# 更新计数
if len(track) == 1: # 新进入的目标
class_name = self.model.names[int(cls_id)]
self.counts[class_name] += 1
# 显示计数结果
for i, (class_name, count) in enumerate(self.counts.items()):
cv2.putText(frame, f"{class_name}: {count}", (10, 30 + i * 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
return frame
def process_video(self, video_path, output_path=None):
"""处理整个视频"""
cap = cv2.VideoCapture(video_path)
if output_path:
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
processed_frame = self.process_frame(frame)
if output_path:
out.write(processed_frame)
cv2.imshow('ROI Counter', processed_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
if output_path:
out.release()
cv2.destroyAllWindows()
# 使用示例
if __name__ == "__main__":
# 创建计数器实例
counter = VideoROICounter(model_path='yolov8n.pt', classes=[0]) # 只统计人
# 设置ROI区域 (四个点坐标)
roi_points = [(300, 200), (800, 200), (900, 600), (200, 600)]
counter.set_roi(roi_points)
# 处理视频
counter.process_video('people_walking.mp4', 'output.mp4')
4. 代码深度解析
4.1 关键组件分析
- ROI定义与判断:使用OpenCV的
pointPolygonTest
函数判断目标中心点是否在多边形区域内 - 目标跟踪:利用YOLOv8内置的跟踪功能,通过
model.track()
实现 - 计数逻辑:当新目标首次出现在ROI内时进行计数
- 可视化:绘制ROI边界、目标轨迹和计数结果
4.2 性能优化技巧
- ROI预处理:将ROI转换为numpy数组并reshape,提高处理效率
- 轨迹长度限制:只保留最近的30个轨迹点,避免内存过度消耗
- 类别过滤:通过
classes
参数只检测感兴趣的类别
5. 实际应用扩展
5.1 多区域计数
可以扩展代码实现多个ROI区域的独立计数:
class MultiROICounter(VideoROICounter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.rois = [] # 多个ROI区域
self.roi_counts = [] # 每个ROI的计数
def add_roi(self, points, roi_id):
"""添加一个ROI区域"""
roi = np.array(points, np.int32).reshape((-1, 1, 2))
self.rois.append((roi_id, roi))
self.roi_counts.append(defaultdict(int))
def process_frame(self, frame):
results = self.model.track(frame, persist=True, classes=self.classes)
boxes = results[0].boxes.xywh.cpu()
track_ids = results[0].boxes.id.int().cpu().tolist() if results[0].boxes.id is not None else []
clss = results[0].boxes.cls.cpu().tolist()
# 绘制所有ROI
for roi_id, roi in self.rois:
cv2.polylines(frame, [roi], True, (0, 255, 0), 2)
cv2.putText(frame, f"ROI {roi_id}", tuple(roi[0][0]),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
for box, track_id, cls_id in zip(boxes, track_ids, clss):
x, y, w, h = box
center = (int(x), int(y))
# 检查每个ROI
for i, (roi_id, roi) in enumerate(self.rois):
if cv2.pointPolygonTest(roi, center, False) >= 0:
# 更新该ROI的计数
class_name = self.model.names[int(cls_id)]
if track_id not in self.track_history:
self.roi_counts[i][class_name] += 1
# 绘制特定ROI的颜色
cv2.rectangle(frame,
(int(x - w/2), int(y - h/2)),
(int(x + w/2), int(y + h/2)),
(0, 0, 255), 2)
break
# 显示每个ROI的计数
for i, (roi_id, _) in enumerate(self.rois):
for j, (class_name, count) in enumerate(self.roi_counts[i].items()):
cv2.putText(frame, f"ROI{roi_id} {class_name}: {count}",
(10, 30 + i * 60 + j * 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
return frame
5.2 方向判断与流量统计
通过分析目标轨迹,可以判断其移动方向并实现进出流量统计:
def process_frame_with_direction(self, frame):
# ... (前面的检测代码相同)
for box, track_id, cls_id 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:
track.pop(0)
# 计算移动方向
if len(track) > 1:
prev_point = track[-2]
curr_point = track[-1]
dx = curr_point[0] - prev_point[0]
dy = curr_point[1] - prev_point[1]
# 判断进出方向
if self.is_inside_roi(curr_point[0], curr_point[1]):
if not self.is_inside_roi(prev_point[0], prev_point[1]):
# 进入ROI
class_name = self.model.names[int(cls_id)]
self.entry_counts[class_name] += 1
else:
if self.is_inside_roi(prev_point[0], prev_point[1]):
# 离开ROI
class_name = self.model.names[int(cls_id)]
self.exit_counts[class_name] += 1
# ... (剩余的可视化代码)
6. 总结与展望
本文详细介绍了如何利用YOLOv8实现视频中划定区域的目标统计计数。通过结合目标检测、跟踪和几何计算,我们构建了一个实用的视频分析工具。这种技术可以广泛应用于各种场景:
- 交通管理:统计交叉路口的车流量
- 零售分析:统计商店入口的顾客数量
- 安全监控:检测禁区内的入侵者
- 城市管理:统计公共场所的人流密度
未来可以进一步改进的方向包括:
- 集成更复杂的行为分析
- 添加深度学习分类器对目标进行更细粒度的分类
- 优化算法以适应更高分辨率的视频流
- 开发基于Web的交互式界面,让用户可以动态调整ROI区域
YOLOv8的强大性能为这些应用提供了坚实的基础,开发者可以根据具体需求灵活扩展本文介绍的方法。