«

实战指南:Python + YOLOv8 实现 CPU 版视频目标检测

从0至1 • 1 个月前 • 147 次点击 • 灵感迸发


实战指南:Python + YOLOv8 实现 CPU 版视频目标检测

引言

在计算机视觉领域,模型训练完成后,我们通常会得到 .pt 格式的模型文件。评估模型效果最直观的方式是对 .mp4 视频进行实时检测。然而,很多开发者在测试时会遇到两个核心挑战:一是如何快速构建完整的视频检测流程,包括模型加载、推理、可视化和结果保存;二是在无 GPU 环境下,如何强制使用 CPU 完成推理并优化性能。

本文将提供一份全面的实战指南,从环境搭建到代码实现,从性能优化到实际应用,帮助开发者快速掌握 CPU 环境下的 YOLOv8 视频检测技术。

一、项目规划与准备

1.1 适用场景分析

本方案适用于以下场景:

1.2 模型选择指南

YOLOv8 系列提供了多种规格的模型,在 CPU 环境下,推荐使用以下轻量级模型:

模型 大小 精度 CPU 推理速度 适用场景
YOLOv8n 3.2MB 37.3% 最快 资源受限设备
YOLOv8s 11.2MB 44.9% 较快 一般 CPU 环境
YOLOv8m 25.9MB 50.2% 中等 高性能 CPU
YOLOv8l 43.7MB 52.9% 较慢 多核心高性能 CPU
YOLOv8x 68.2MB 53.9% 最慢 不推荐在 CPU 上使用

选择建议:在 CPU 环境下,优先选择 YOLOv8n 或 YOLOv8s,以平衡速度和精度。

1.3 环境搭建

1.3.1 依赖库安装

# 基础安装命令
pip install ultralytics torch opencv-python numpy

# 推荐版本安装(指定版本避免兼容性问题)
pip install ultralytics==8.1.0 torch==2.1.0 opencv-python==4.8.1.78 numpy==1.24.4

1.3.2 验证安装

# 验证 PyTorch 安装
python -c "import torch; print('PyTorch version:', torch.__version__); print('CPU available:', torch.cuda.is_available())"

# 验证 YOLOv8 安装
python -c "from ultralytics import YOLO; print('YOLOv8 imported successfully')"

# 验证 OpenCV 安装
python -c "import cv2; print('OpenCV version:', cv2.__version__)"

二、核心技术实现

2.1 项目结构

yolo-video-detector/
├── models/                # 模型文件目录
│   └── yolov8n.pt         # YOLOv8 模型文件
├── videos/                # 视频文件目录
│   └── test_video.mp4     # 待检测视频
├── outputs/               # 输出结果目录
│   └── detected_video.mp4 # 检测结果视频
├── detector.py            # 核心检测脚本
├── utils.py               # 工具函数
└── README.md              # 项目说明

2.2 核心检测脚本

import cv2
import torch
from ultralytics import YOLO
import os
import time

def detect_model_on_video(
    model_path, 
    video_path, 
    output_path="output.mp4", 
    conf_threshold=0.5,
    resize=None,  # 可选:(width, height),缩小输入尺寸提升速度
    classes=None,  # 可选:[0, 1, 2],只检测特定类别
    show_preview=True  # 是否显示实时预览
):
    """
    加载.pt模型(强制CPU)检测.mp4视频,保存带检测结果的视频

    参数说明:
    model_path: str, .pt模型文件路径(如'best.pt')
    video_path: str, 待检测的.mp4视频路径(如'test_video.mp4')
    output_path: str, 检测结果视频保存路径
    conf_threshold: float, 置信度阈值(0-1),过滤低置信度检测结果
    resize: tuple, 可选,调整输入视频尺寸 (width, height)
    classes: list, 可选,只检测指定类别索引
    show_preview: bool, 是否显示实时检测预览
    """
    # 1. 校验文件是否存在
    if not os.path.exists(model_path):
        raise FileNotFoundError(f"模型文件不存在:{model_path}")
    if not os.path.exists(video_path):
        raise FileNotFoundError(f"视频文件不存在:{video_path}")

    # 2. 确保输出目录存在
    output_dir = os.path.dirname(output_path)
    if output_dir and not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # 3. 强制使用CPU
    device = 'cpu'
    print(f"当前强制使用计算设备:{device}")

    # 4. 加载模型
    model = YOLO(model_path)
    model.to(device)

    # 5. 打开视频文件
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        raise RuntimeError(f"无法打开视频:{video_path}")

    # 6. 获取视频参数
    fps = int(cap.get(cv2.CAP_PROP_FPS))
    original_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    original_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    # 7. 计算输出尺寸
    if resize:
        output_width, output_height = resize
    else:
        output_width, output_height = original_width, original_height

    # 8. 创建视频写入器
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_path, fourcc, fps, (output_width, output_height))

    # 9. 逐帧处理
    frame_count = 0
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    start_time = time.time()

    print(f"开始检测视频(CPU模式),共 {total_frames} 帧,请稍候...")

    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        # 调整输入尺寸(如果指定)
        if resize:
            frame = cv2.resize(frame, resize)

        # 单帧推理
        frame_start = time.time()
        results = model(frame, conf=conf_threshold, device=device, classes=classes)
        frame_end = time.time()

        # 计算单帧处理时间
        frame_time = (frame_end - frame_start) * 1000  # 转换为毫秒
        fps_current = 1 / (frame_end - frame_start + 1e-6)

        # 绘制检测结果
        annotated_frame = results[0].plot()

        # 添加性能信息
        cv2.putText(
            annotated_frame,
            f'CPU Inference: {frame_time:.1f}ms ({fps_current:.1f} FPS)',
            (10, 30),
            cv2.FONT_HERSHEY_SIMPLEX,
            1,
            (0, 255, 0),
            2
        )

        # 写入结果视频
        out.write(annotated_frame)

        # 实时预览
        if show_preview:
            cv2.imshow('YOLOv8 Detection (CPU)', annotated_frame)

            # 按q键退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                print("用户手动终止检测")
                break

        frame_count += 1

        # 打印进度
        if frame_count % 10 == 0:
            progress = (frame_count / total_frames) * 100
            elapsed_time = time.time() - start_time
            estimated_total = elapsed_time / (frame_count / total_frames)
            remaining_time = estimated_total - elapsed_time

            print(f"进度: {progress:.1f}% | 已处理: {frame_count}/{total_frames}帧 | "
                  f"耗时: {elapsed_time:.1f}s | 预计剩余: {remaining_time:.1f}s")

    # 10. 释放资源
    cap.release()
    out.release()
    if show_preview:
        cv2.destroyAllWindows()

    # 11. 输出检测结果
    total_time = time.time() - start_time
    average_fps = frame_count / total_time

    print("\n检测完成!")
    print(f"结果视频保存路径:{output_path}")
    print(f"总共处理帧数:{frame_count}")
    print(f"总耗时:{total_time:.2f}秒")
    print(f"平均帧率:{average_fps:.2f} FPS")
    print(f"平均每帧处理时间:{1000/average_fps:.2f} 毫秒")

if __name__ == "__main__":
    # 配置参数
    config = {
        "model_path": "models/yolov8n.pt",
        "video_path": "videos/test_video.mp4",
        "output_path": "outputs/detected_video.mp4",
        "conf_threshold": 0.5,
        "resize": (640, 480),  # 缩小尺寸提升速度
        "classes": None,  # 检测所有类别
        "show_preview": True
    }

    try:
        detect_model_on_video(**config)
    except Exception as e:
        print(f"检测出错:{e}")
        import traceback
        traceback.print_exc()

2.3 工具函数模块

# utils.py
import os
import cv2
import numpy as np

def get_video_info(video_path):
    """
    获取视频基本信息
    """
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        raise RuntimeError(f"无法打开视频:{video_path}")

    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    duration = total_frames / fps

    cap.release()

    return {
        "fps": fps,
        "width": width,
        "height": height,
        "total_frames": total_frames,
        "duration": duration
    }

def extract_video_frames(video_path, output_dir, interval=1):
    """
    从视频中提取帧
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    cap = cv2.VideoCapture(video_path)
    frame_count = 0
    extracted_count = 0

    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        if frame_count % interval == 0:
            output_path = os.path.join(output_dir, f"frame_{extracted_count:04d}.jpg")
            cv2.imwrite(output_path, frame)
            extracted_count += 1

        frame_count += 1

    cap.release()
    return extracted_count

def batch_process_videos(video_dir, model_path, output_dir, **detect_kwargs):
    """
    批量处理文件夹中的所有视频
    """
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    video_files = [f for f in os.listdir(video_dir) if f.endswith('.mp4')]

    for video_file in video_files:
        video_path = os.path.join(video_dir, video_file)
        output_path = os.path.join(output_dir, f"detected_{video_file}")

        print(f"\n处理视频:{video_file}")
        try:
            from detector import detect_model_on_video
            detect_model_on_video(
                model_path=model_path,
                video_path=video_path,
                output_path=output_path,
                **detect_kwargs
            )
        except Exception as e:
            print(f"处理视频 {video_file} 时出错:{e}")

    print(f"\n批量处理完成,共处理 {len(video_files)} 个视频")

三、实战应用示例

3.1 基础应用:交通场景检测

3.1.1 配置与运行

# 交通场景检测配置
config = {
    "model_path": "models/yolov8n.pt",
    "video_path": "videos/traffic.mp4",
    "output_path": "outputs/traffic_detected.mp4",
    "conf_threshold": 0.4,
    "resize": (640, 480),
    "classes": [2, 3, 5, 7],  # 只检测车辆相关类别
    "show_preview": True
}

# 运行检测
detect_model_on_video(**config)

3.1.2 检测效果

原始视频:城市交通场景,包含汽车、公交车、行人等
检测结果

3.2 高级应用:批量视频处理

3.2.1 批量处理脚本

# batch_process.py
from utils import batch_process_videos

# 批量处理配置
batch_config = {
    "video_dir": "videos/",
    "model_path": "models/yolov8n.pt",
    "output_dir": "outputs/batch_results/",
    "conf_threshold": 0.5,
    "resize": (640, 480),
    "show_preview": False  # 批量处理时关闭预览
}

# 运行批量处理
batch_process_videos(**batch_config)

3.2.2 应用场景

3.3 实用技巧:性能测试

3.3.1 CPU 性能测试脚本

# performance_test.py
import time
from detector import detect_model_on_video

# 测试不同模型在 CPU 上的性能
test_videos = ["videos/test_video_10s.mp4"]  # 10秒测试视频
models = ["yolov8n.pt", "yolov8s.pt", "yolov8m.pt"]

results = []

for model in models:
    print(f"\n测试模型:{model}")
    start_time = time.time()

    try:
        detect_model_on_video(
            model_path=f"models/{model}",
            video_path=test_videos[0],
            output_path=f"outputs/test_{model}_result.mp4",
            conf_threshold=0.5,
            resize=(640, 480),
            show_preview=False
        )
        end_time = time.time()
        results.append({"model": model, "time": end_time - start_time})
    except Exception as e:
        print(f"测试失败:{e}")

# 输出性能对比
print("\n=== CPU 性能测试结果 ===")
for result in results:
    print(f"{result['model']}: {result['time']:.2f}秒")

四、常见问题与解决方案

4.1 性能问题

问题 原因 解决方案
CPU 推理速度太慢 模型太大或视频分辨率过高 1. 使用更小的模型(YOLOv8n)
2. 降低视频分辨率
3. 减少检测类别
内存占用过高 视频分辨率过高或模型过大 1. 降低视频分辨率
2. 使用轻量级模型
3. 确保逐帧处理,不缓存视频
程序崩溃 内存不足 1. 减小输入尺寸
2. 使用更小的模型
3. 增加虚拟内存

4.2 技术问题

问题 原因 解决方案
视频无法打开 路径错误或编码不支持 1. 检查视频路径
2. 尝试转换视频编码
3. 使用绝对路径
检测框不准确 置信度阈值设置不当 1. 调整 conf_threshold 参数
2. 使用更适合的模型
保存的视频无法播放 编码格式不兼容 1. 尝试使用不同的编码:'avc1' 或 'XVID'
2. 检查输出路径权限
预览窗口无响应 处理速度过慢 1. 关闭实时预览
2. 降低视频分辨率
3. 使用更快的模型

4.3 环境问题

问题 原因 解决方案
依赖库安装失败 网络问题或版本冲突 1. 使用国内镜像源
2. 安装指定版本
3. 创建虚拟环境
PyTorch 无法导入 版本不兼容 1. 安装与系统匹配的版本
2. 检查 Python 版本兼容性
OpenCV 错误 缺少依赖 1. 安装完整版本:pip install opencv-python-headless
2. 检查系统依赖

五、代码优化与进阶技巧

5.1 代码优化建议

  1. 使用上下文管理器

    class VideoCapture:
       def __init__(self, path):
           self.path = path
           self.cap = None
    
       def __enter__(self):
           self.cap = cv2.VideoCapture(self.path)
           return self.cap
    
       def __exit__(self, exc_type, exc_val, exc_tb):
           if self.cap:
               self.cap.release()
  2. 多线程处理

    import threading
    from queue import Queue
    
    def process_frames(queue, model, device):
       while not queue.empty():
           frame = queue.get()
           # 处理帧
           results = model(frame, device=device)
           # 处理结果
           queue.task_done()
  3. 缓存优化

    # 预分配内存
    import numpy as np
    
    # 预分配帧缓冲区
    frame_buffer = np.zeros((height, width, 3), dtype=np.uint8)

5.2 进阶应用场景

  1. 实时摄像头检测

    # 实时摄像头检测
    cap = cv2.VideoCapture(0)  # 0 表示默认摄像头
  2. 视频流检测

    # RTSP 流检测
    rtsp_url = "rtsp://username:password@camera_ip:554/stream1"
    cap = cv2.VideoCapture(rtsp_url)
  3. 与其他系统集成

    • 与 Flask/Django 集成,提供 Web 接口
    • 与消息队列集成,处理异步检测任务
    • 与数据库集成,存储检测结果

六、总结与展望

6.1 核心收获

  1. 完整的视频检测流程:掌握了从模型加载、视频处理到结果保存的完整流程
  2. CPU 环境优化:学会了在 CPU 环境下优化 YOLOv8 推理性能
  3. 工程化实践:掌握了编写鲁棒、高效的计算机视觉脚本的技巧
  4. 实际应用能力:能够将目标检测技术应用到实际场景中

6.2 未来发展方向

  1. 模型压缩:使用模型量化、剪枝等技术进一步减小模型大小
  2. 推理加速:集成 OpenVINO、ONNX Runtime 等推理加速引擎
  3. 边缘部署:将优化后的模型部署到更广泛的边缘设备
  4. 多模态融合:结合音频、文本等信息,提升检测效果
  5. 自动化标注:利用检测结果辅助视频标注,提高数据处理效率

6.3 学习资源推荐

七、项目部署指南

7.1 离线部署

  1. 打包依赖

    # 导出依赖
    pip freeze > requirements.txt
    
    # 下载依赖包(用于离线安装)
    pip download -r requirements.txt -d packages/
  2. 离线安装

    # 在目标机器上安装
    pip install --no-index --find-links=packages/ -r requirements.txt

7.2 脚本打包

使用 PyInstaller 打包为可执行文件:

# 安装 PyInstaller
pip install pyinstaller

# 打包脚本
pyinstaller --onefile --name yolo_detector detector.py

# 运行打包后的程序
./dist/yolo_detector

7.3 Docker 部署

# Dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD ["python", "detector.py"]
# 构建镜像
docker build -t yolo-video-detector .

# 运行容器
docker run --rm -v ./videos:/app/videos -v ./outputs:/app/outputs yolo-video-detector

个人矩阵

  • 抖音账号:从 0 至 1(日常分享实操、效率工具教程)
  • 微信公众号:从 0 至 1(可通过该渠道获取完整代码包及EXE程序)
  • 博客网站:www.from0to1.cn(持续更新实战教程、技术干货内容)
  • GitHub账号:https://github.com/mtnljbydd(开源更多实用工具脚本及项目工程)

扫描二维码,在手机上阅读
文章目录


    收藏
    还没收到回复
    请先 登录 再回复