掌握YOLOv8:从视频目标检测到划定区域统计计数的实用指南

文章目录

    • 1. 引言
    • 2. YOLOv8基础回顾
      • 2.1 YOLOv8的核心改进
      • 2.2 YOLOv8的基本使用
    • 3. 视频划定区域目标统计计数实现
      • 3.1 总体思路
      • 3.2 详细实现代码
      • 3.3 代码解析
    • 4. 进阶改进方向
      • 4.1 多区域计数
      • 4.2 方向敏感计数
      • 4.3 性能优化技巧
    • 5. 实际应用案例
      • 5.1 交通流量统计
      • 5.2 商场入口人流统计
    • 6. 总结与展望

1. 引言

目标检测是计算机视觉领域的重要任务之一,而YOLO(You Only Look Once)系列算法因其速度和精度的平衡而广受欢迎。YOLOv8作为该系列的最新版本,在性能和易用性上都有了显著提升。本文将介绍如何利用YOLOv8进行视频中划定区域的目标统计计数,这是一个在实际应用中非常有用的功能,如交通流量统计、商场人流量监测等。

2. YOLOv8基础回顾

2.1 YOLOv8的核心改进

YOLOv8在以下几个方面进行了重要改进:

  1. 更高效的网络架构:采用了新的骨干网络和特征金字塔结构
  2. 改进的损失函数:使用了更合理的分类和回归损失组合
  3. 增强的训练策略:包括更好的数据增强和优化器设置
  4. 简化的API:提供了更友好的用户接口

2.2 YOLOv8的基本使用

from ultralytics import YOLO

# 加载预训练模型
model = YOLO('yolov8n.pt')  # 可以替换为yolov8s/m/l/x.pt

# 进行目标检测
results = model('image.jpg')

# 显示结果
results[0].show()

3. 视频划定区域目标统计计数实现

3.1 总体思路

实现视频划定区域目标统计计数的基本流程:

  1. 加载YOLOv8模型
  2. 读取视频流
  3. 定义感兴趣区域(ROI)
  4. 对每一帧进行目标检测
  5. 统计进入/离开ROI的目标数量
  6. 可视化结果并输出统计信息

3.2 详细实现代码

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
    )

3.3 代码解析

  1. ROI定义:使用多边形顶点定义感兴趣区域,可以是不规则形状
  2. 目标跟踪:利用YOLOv8的跟踪功能保持目标ID一致性
  3. 进出判断:通过比较目标当前位置与前一帧位置判断进出ROI
  4. 轨迹可视化:绘制目标的运动轨迹便于分析
  5. 类别过滤:可以指定只统计特定类别的目标

4. 进阶改进方向

4.1 多区域计数

可以扩展代码实现多个独立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计数
    # ...

4.2 方向敏感计数

改进计数逻辑,只统计特定方向移动的目标:

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

4.3 性能优化技巧

  1. ROI内检测:只在ROI区域内进行目标检测
  2. 帧采样:对高帧率视频进行适当采样
  3. 模型量化:使用量化后的模型提高推理速度
  4. 多线程处理:分离IO和计算任务

5. 实际应用案例

5.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])

5.2 商场入口人流统计

统计商场入口进出人数:

# 定义门口区域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])

6. 总结与展望

本文详细介绍了如何利用YOLOv8实现视频中划定区域的目标统计计数。通过合理的ROI定义和跟踪计数逻辑,我们可以构建出实用的视频分析系统。未来可以进一步探索:

  1. 结合深度信息实现更精确的3D空间计数
  2. 集成更复杂的行为分析算法
  3. 开发基于Web的交互式ROI定义工具
  4. 优化算法实现实时高清视频处理

YOLOv8的强大检测和跟踪能力为视频分析应用提供了坚实基础,结合特定业务逻辑可以开发出各种实用的智能视频分析系统。

在这里插入图片描述

你可能感兴趣的:(YOLO,音视频,目标检测,yolov8)