YOLO12实时视频流处理方案:逐帧检测与API集成指南

实时视频流处理是计算机视觉领域的核心应用场景之一,从安防监控到智能交通,从工业质检到互动直播,都需要系统能够快速、准确地识别视频中的目标。YOLO12作为Ultralytics在2025年推出的最新实时目标检测模型,凭借其高达131 FPS的推理速度和优化的检测精度,成为了视频流处理的理想选择。

本文将手把手教你如何将YOLO12模型与实时视频流处理相结合,从单张图片检测扩展到连续视频帧分析,并提供完整的API集成方案,让你能够快速构建自己的视频分析应用。

1. 理解实时视频流处理的核心挑战

1.1 视频流处理的特殊性

与静态图片检测不同,视频流处理面临几个独特挑战:

  • 连续性要求:视频是由连续的帧组成,处理必须跟上帧率,否则会出现延迟累积
  • 实时性约束:很多应用场景要求毫秒级响应,如自动驾驶、安防报警
  • 资源优化:连续处理需要高效利用计算资源,避免内存泄漏和性能下降
  • 上下文关联:相邻帧之间的目标应该有连续性,避免检测结果跳跃

1.2 YOLO12的视频处理优势

YOLO12特别适合视频流处理的原因:

  • 单阶段检测架构:端到端的前向传播,没有复杂的区域建议步骤,推理速度快
  • 多规格模型:从nano到xlarge五种规格,可以根据硬件性能选择合适模型
  • 轻量级设计:nano版仅5.6MB,370万参数,在边缘设备上也能流畅运行
  • 高帧率支持:在RTX 4090上,nano版可达131 FPS,轻松处理30FPS的视频流

2. 搭建视频流处理基础环境

2.1 环境准备与YOLO12部署

首先按照标准流程部署YOLO12镜像:

  1. 在镜像市场选择 ins-yolo12-independent-v1
  2. 点击"部署实例"创建运行环境
  3. 等待1-2分钟初始化完成
  4. 确认实例状态变为"已启动"

部署完成后,你可以通过 http://<实例IP>:7860 访问Web界面进行基本测试,确保模型正常工作。

2.2 安装视频处理依赖库

虽然YOLO12镜像已经包含了核心依赖,但视频处理需要额外的库。通过SSH连接到实例后,安装必要的包:

# 连接到你的实例
ssh root@<实例IP>

# 安装视频处理相关库
pip install opencv-python-headless
pip install numpy
pip install requests

OpenCV是视频处理的核心库,headless版本不需要图形界面,更适合服务器环境。

2.3 验证API接口可用性

在开始视频处理前,先测试API接口是否正常工作:

# 测试单张图片检测
curl -X POST "http://localhost:8000/predict" \
     -H "accept: application/json" \
     -F "file=@/root/test_image.jpg"

如果返回JSON格式的检测结果,说明API服务运行正常。

3. 实现逐帧视频处理方案

3.1 基础视频处理脚本

下面是一个完整的视频逐帧处理脚本,展示了如何读取视频、逐帧调用YOLO12 API、保存处理结果:

import cv2
import requests
import json
import time
from datetime import datetime

class VideoProcessor:
    def __init__(self, api_url="http://localhost:8000/predict"):
        self.api_url = api_url
        self.frame_count = 0
        self.total_processing_time = 0
        
    def process_video(self, video_path, output_path=None, frame_interval=1):
        """
        处理视频文件,逐帧调用YOLO12进行目标检测
        
        参数:
            video_path: 输入视频文件路径
            output_path: 输出视频文件路径(可选)
            frame_interval: 处理间隔,1表示每帧都处理,2表示每隔一帧处理
        """
        
        # 打开视频文件
        cap = cv2.VideoCapture(video_path)
        if not cap.isOpened():
            print(f"无法打开视频文件: {video_path}")
            return
            
        # 获取视频基本信息
        fps = int(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))
        
        print(f"视频信息: {width}x{height}, {fps}FPS, 总帧数: {total_frames}")
        
        # 如果需要保存处理结果,创建VideoWriter
        writer = None
        if output_path:
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
        
        # 逐帧处理
        frame_index = 0
        detection_results = []
        
        while True:
            ret, frame = cap.read()
            if not ret:
                break
                
            # 按间隔处理帧
            if frame_index % frame_interval == 0:
                # 调用YOLO12 API进行检测
                start_time = time.time()
                detections = self.detect_frame(frame)
                processing_time = time.time() - start_time
                
                # 记录处理时间
                self.total_processing_time += processing_time
                self.frame_count += 1
                
                # 在帧上绘制检测结果
                processed_frame = self.draw_detections(frame, detections)
                
                # 保存检测结果
                frame_result = {
                    "frame_index": frame_index,
                    "timestamp": frame_index / fps,
                    "processing_time": processing_time,
                    "detections": detections
                }
                detection_results.append(frame_result)
                
                # 显示处理进度
                if frame_index % 30 == 0:  # 每30帧显示一次进度
                    avg_time = self.total_processing_time / self.frame_count
                    remaining_frames = total_frames - frame_index
                    eta = remaining_frames * avg_time / frame_interval
                    print(f"处理进度: {frame_index}/{total_frames} | "
                          f"平均处理时间: {avg_time*1000:.1f}ms | "
                          f"预计剩余时间: {eta:.1f}秒")
                
                # 更新当前帧为处理后的帧
                frame = processed_frame
            
            # 写入输出视频
            if writer:
                writer.write(frame)
                
            frame_index += 1
        
        # 释放资源
        cap.release()
        if writer:
            writer.release()
            
        # 输出统计信息
        self.print_statistics(detection_results)
        
        return detection_results
    
    def detect_frame(self, frame):
        """调用YOLO12 API检测单帧"""
        # 将帧编码为JPEG格式
        _, img_encoded = cv2.imencode('.jpg', frame)
        
        # 准备API请求
        files = {'file': ('frame.jpg', img_encoded.tobytes(), 'image/jpeg')}
        
        try:
            response = requests.post(self.api_url, files=files, timeout=5)
            if response.status_code == 200:
                return response.json()
            else:
                print(f"API请求失败: {response.status_code}")
                return {"predictions": []}
        except Exception as e:
            print(f"检测失败: {str(e)}")
            return {"predictions": []}
    
    def draw_detections(self, frame, detections):
        """在帧上绘制检测框和标签"""
        predictions = detections.get("predictions", [])
        
        # 为不同类别定义颜色
        colors = {
            "person": (0, 255, 0),      # 绿色
            "car": (255, 0, 0),         # 蓝色
            "bicycle": (0, 255, 255),   # 黄色
            "dog": (255, 0, 255),       # 紫色
            "cat": (255, 255, 0)        # 青色
        }
        
        for pred in predictions:
            class_name = pred.get("class", "unknown")
            confidence = pred.get("confidence", 0)
            bbox = pred.get("bbox", [])
            
            if len(bbox) == 4:
                x1, y1, x2, y2 = map(int, bbox)
                
                # 获取类别颜色,默认白色
                color = colors.get(class_name, (255, 255, 255))
                
                # 绘制边界框
                cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
                
                # 绘制标签背景
                label = f"{class_name}: {confidence:.2f}"
                label_size, baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
                cv2.rectangle(frame, (x1, y1 - label_size[1] - 5), 
                            (x1 + label_size[0], y1), color, -1)
                
                # 绘制标签文字
                cv2.putText(frame, label, (x1, y1 - 5), 
                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
        
        # 添加帧信息
        cv2.putText(frame, f"Detections: {len(predictions)}", (10, 30),
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        
        return frame
    
    def print_statistics(self, detection_results):
        """输出处理统计信息"""
        if not detection_results:
            print("没有检测到任何结果")
            return
            
        total_detections = sum(len(frame["detections"].get("predictions", [])) 
                              for frame in detection_results)
        
        avg_processing_time = self.total_processing_time / self.frame_count if self.frame_count > 0 else 0
        avg_fps = 1 / avg_processing_time if avg_processing_time > 0 else 0
        
        print("\n" + "="*50)
        print("视频处理统计:")
        print(f"处理总帧数: {self.frame_count}")
        print(f"检测到目标总数: {total_detections}")
        print(f"平均处理时间: {avg_processing_time*1000:.1f}ms")
        print(f"平均处理帧率: {avg_fps:.1f} FPS")
        print(f"总处理时间: {self.total_processing_time:.1f}秒")
        print("="*50)

# 使用示例
if __name__ == "__main__":
    processor = VideoProcessor()
    
    # 处理本地视频文件
    results = processor.process_video(
        video_path="input_video.mp4",
        output_path="output_video.mp4",
        frame_interval=1  # 每帧都处理
    )
    
    # 保存检测结果到JSON文件
    with open("detection_results.json", "w") as f:
        json.dump(results, f, indent=2)

3.2 实时摄像头流处理

对于实时摄像头流,代码需要稍作调整:

class CameraProcessor(VideoProcessor):
    def __init__(self, api_url="http://localhost:8000/predict", camera_id=0):
        super().__init__(api_url)
        self.camera_id = camera_id
        self.running = False
        
    def start_stream(self, duration_seconds=None, show_preview=True):
        """开始处理摄像头实时流"""
        cap = cv2.VideoCapture(self.camera_id)
        if not cap.isOpened():
            print(f"无法打开摄像头 {self.camera_id}")
            return
            
        # 设置摄像头参数(可选)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        cap.set(cv2.CAP_PROP_FPS, 30)
        
        print(f"开始处理摄像头 {self.camera_id} 的实时流")
        print("按 'q' 键退出")
        
        self.running = True
        start_time = time.time()
        frame_count = 0
        
        while self.running:
            ret, frame = cap.read()
            if not ret:
                print("摄像头读取失败")
                break
                
            # 处理当前帧
            processing_start = time.time()
            detections = self.detect_frame(frame)
            processing_time = time.time() - processing_start
            
            # 绘制检测结果
            processed_frame = self.draw_detections(frame, detections)
            
            # 显示处理信息
            fps_text = f"FPS: {1/processing_time:.1f}" if processing_time > 0 else "FPS: N/A"
            cv2.putText(processed_frame, fps_text, (10, 60),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
            
            # 显示预览
            if show_preview:
                cv2.imshow('YOLO12 Real-time Detection', processed_frame)
                
                # 检查按键
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
            
            frame_count += 1
            
            # 检查处理时长
            if duration_seconds and (time.time() - start_time) > duration_seconds:
                break
        
        # 清理资源
        cap.release()
        if show_preview:
            cv2.destroyAllWindows()
            
        print(f"处理完成,共处理 {frame_count} 帧")
        
    def stop_stream(self):
        """停止摄像头流处理"""
        self.running = False

# 使用示例
if __name__ == "__main__":
    # 处理默认摄像头(ID=0)的实时流,持续30秒
    processor = CameraProcessor(camera_id=0)
    processor.start_stream(duration_seconds=30, show_preview=True)

4. 优化视频处理性能

4.1 多线程处理提升吞吐量

对于高帧率视频流,单线程处理可能成为瓶颈。下面是多线程处理的优化版本:

import threading
import queue
import time
from concurrent.futures import ThreadPoolExecutor

class MultiThreadVideoProcessor:
    def __init__(self, api_url="http://localhost:8000/predict", num_workers=4):
        self.api_url = api_url
        self.num_workers = num_workers
        self.frame_queue = queue.Queue(maxsize=50)
        self.result_queue = queue.Queue()
        self.running = False
        
    def process_video_parallel(self, video_path, output_path=None):
        """多线程并行处理视频"""
        
        # 打开视频文件
        cap = cv2.VideoCapture(video_path)
        fps = int(cap.get(cv2.CAP_PROP_FPS))
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        
        print(f"开始多线程处理,使用 {self.num_workers} 个工作线程")
        
        # 启动工作线程
        self.running = True
        workers = []
        for i in range(self.num_workers):
            worker = threading.Thread(target=self.worker_thread, args=(i,))
            worker.start()
            workers.append(worker)
        
        # 启动结果处理线程
        result_thread = threading.Thread(target=self.result_handler, 
                                        args=(output_path, fps, total_frames))
        result_thread.start()
        
        # 读取视频帧并放入队列
        frame_index = 0
        read_start = time.time()
        
        while True:
            ret, frame = cap.read()
            if not ret:
                break
                
            # 等待队列有空位
            while self.frame_queue.full():
                time.sleep(0.001)
                
            # 放入帧数据
            frame_data = {
                "frame": frame.copy(),
                "index": frame_index,
                "timestamp": frame_index / fps
            }
            self.frame_queue.put(frame_data)
            frame_index += 1
            
            # 显示读取进度
            if frame_index % 100 == 0:
                elapsed = time.time() - read_start
                print(f"已读取 {frame_index}/{total_frames} 帧 | "
                      f"读取速度: {frame_index/elapsed:.1f} FPS")
        
        # 视频读取完成,发送结束信号
        for _ in range(self.num_workers):
            self.frame_queue.put(None)
        
        # 等待所有工作线程完成
        for worker in workers:
            worker.join()
        
        # 发送结果处理结束信号
        self.result_queue.put(None)
        result_thread.join()
        
        cap.release()
        print("多线程处理完成")
    
    def worker_thread(self, worker_id):
        """工作线程:从队列获取帧并调用API"""
        while self.running:
            try:
                frame_data = self.frame_queue.get(timeout=1)
                if frame_data is None:  # 结束信号
                    break
                    
                # 调用API进行检测
                start_time = time.time()
                detections = self.detect_frame(frame_data["frame"])
                processing_time = time.time() - start_time
                
                # 将结果放入结果队列
                result = {
                    "frame": frame_data["frame"],
                    "index": frame_data["index"],
                    "timestamp": frame_data["timestamp"],
                    "detections": detections,
                    "processing_time": processing_time,
                    "worker_id": worker_id
                }
                self.result_queue.put(result)
                
            except queue.Empty:
                continue
            except Exception as e:
                print(f"工作线程 {worker_id} 出错: {str(e)}")
    
    def result_handler(self, output_path, fps, total_frames):
        """结果处理线程:按顺序处理检测结果"""
        # 初始化VideoWriter
        writer = None
        if output_path:
            # 需要先获取视频尺寸,这里假设为640x480
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            writer = cv2.VideoWriter(output_path, fourcc, fps, (640, 480))
        
        # 按帧索引排序的缓冲区
        result_buffer = {}
        next_index = 0
        processed_count = 0
        total_processing_time = 0
        
        while True:
            try:
                result = self.result_queue.get(timeout=5)
                if result is None:  # 结束信号
                    break
                
                # 存入缓冲区
                result_buffer[result["index"]] = result
                
                # 按顺序处理缓冲区的帧
                while next_index in result_buffer:
                    result = result_buffer.pop(next_index)
                    
                    # 绘制检测结果
                    processed_frame = self.draw_detections(
                        result["frame"], 
                        result["detections"]
                    )
                    
                    # 写入输出视频
                    if writer:
                        writer.write(processed_frame)
                    
                    # 更新统计
                    total_processing_time += result["processing_time"]
                    processed_count += 1
                    next_index += 1
                    
                    # 显示进度
                    if processed_count % 50 == 0:
                        avg_time = total_processing_time / processed_count
                        print(f"已处理 {processed_count}/{total_frames} 帧 | "
                              f"平均处理时间: {avg_time*1000:.1f}ms | "
                              f"队列大小: {self.frame_queue.qsize()}")
            
            except queue.Empty:
                if not self.running:
                    break
        
        # 清理
        if writer:
            writer.release()
        
        avg_fps = processed_count / total_processing_time if total_processing_time > 0 else 0
        print(f"\n处理完成统计:")
        print(f"总处理帧数: {processed_count}")
        print(f"总处理时间: {total_processing_time:.1f}秒")
        print(f"平均帧率: {avg_fps:.1f} FPS")
    
    def detect_frame(self, frame):
        """检测单帧(与之前相同)"""
        _, img_encoded = cv2.imencode('.jpg', frame)
        files = {'file': ('frame.jpg', img_encoded.tobytes(), 'image/jpeg')}
        
        try:
            response = requests.post(self.api_url, files=files, timeout=2)
            if response.status_code == 200:
                return response.json()
        except:
            pass
        return {"predictions": []}
    
    def draw_detections(self, frame, detections):
        """绘制检测结果(与之前相同)"""
        # 简化的绘制逻辑,实际使用时可以复用之前的draw_detections方法
        return frame

# 使用示例
if __name__ == "__main__":
    processor = MultiThreadVideoProcessor(num_workers=4)
    processor.process_video_parallel(
        video_path="input_video.mp4",
        output_path="output_parallel.mp4"
    )

4.2 帧采样与分辨率优化

对于实时性要求极高的应用,可以通过帧采样和降低分辨率来提升性能:

class OptimizedVideoProcessor(VideoProcessor):
    def __init__(self, api_url="http://localhost:8000/predict"):
        super().__init__(api_url)
        
    def process_with_optimizations(self, video_path, output_path=None, 
                                   target_fps=15, target_width=640):
        """
        优化版本:降低帧率和分辨率以提升处理速度
        
        参数:
            target_fps: 目标处理帧率(降低输入帧率)
            target_width: 目标宽度(保持宽高比)
        """
        cap = cv2.VideoCapture(video_path)
        original_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))
        
        # 计算缩放比例和目标尺寸
        scale = target_width / original_width
        target_height = int(original_height * scale)
        
        # 计算帧采样间隔
        frame_interval = max(1, int(original_fps / target_fps))
        
        print(f"优化设置:")
        print(f"  原始分辨率: {original_width}x{original_height}")
        print(f"  目标分辨率: {target_width}x{target_height}")
        print(f"  原始FPS: {original_fps}")
        print(f"  目标FPS: {target_fps}")
        print(f"  帧采样间隔: {frame_interval}")
        
        frame_index = 0
        processed_count = 0
        total_time = 0
        
        while True:
            ret, frame = cap.read()
            if not ret:
                break
                
            # 帧采样
            if frame_index % frame_interval != 0:
                frame_index += 1
                continue
            
            # 降低分辨率
            if target_width != original_width:
                frame = cv2.resize(frame, (target_width, target_height))
            
            # 检测
            start_time = time.time()
            detections = self.detect_frame(frame)
            processing_time = time.time() - start_time
            
            total_time += processing_time
            processed_count += 1
            
            # 显示实时统计
            if processed_count % 10 == 0:
                current_fps = processed_count / total_time
                print(f"已处理 {processed_count} 帧 | "
                      f"当前FPS: {current_fps:.1f} | "
                      f"最近处理时间: {processing_time*1000:.1f}ms")
            
            frame_index += 1
        
        cap.release()
        
        avg_fps = processed_count / total_time if total_time > 0 else 0
        print(f"\n优化处理完成:")
        print(f"  处理总帧数: {processed_count}")
        print(f"  总处理时间: {total_time:.1f}秒")
        print(f)  平均帧率: {avg_fps:.1f} FPS")
        print(f"  性能提升: {avg_fps/target_fps*100:.1f}% of target")

5. API集成与生产部署

5.1 构建完整的视频处理API服务

将视频处理功能封装为API服务,方便其他系统调用:

from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse, StreamingResponse
import uvicorn
import tempfile
import os

app = FastAPI(title="YOLO12 Video Processing API")

@app.post("/process_video")
async def process_video(
    file: UploadFile = File(...),
    frame_interval: int = 1,
    confidence_threshold: float = 0.25,
    output_format: str = "json"
):
    """
    处理上传的视频文件
    
    参数:
        file: 视频文件
        frame_interval: 帧采样间隔
        confidence_threshold: 置信度阈值
        output_format: 输出格式 (json/video)
    """
    
    # 保存上传的文件
    with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
        content = await file.read()
        tmp_file.write(content)
        video_path = tmp_file.name
    
    try:
        # 创建处理器实例
        processor = VideoProcessor()
        
        # 处理视频
        results = processor.process_video(
            video_path=video_path,
            frame_interval=frame_interval
        )
        
        # 根据阈值过滤结果
        filtered_results = []
        for frame_result in results:
            filtered_detections = [
                det for det in frame_result["detections"].get("predictions", [])
                if det.get("confidence", 0) >= confidence_threshold
            ]
            frame_result["detections"]["predictions"] = filtered_detections
            filtered_results.append(frame_result)
        
        # 返回结果
        if output_format == "json":
            return JSONResponse(content={
                "status": "success",
                "video_info": {
                    "frames_processed": len(filtered_results),
                    "total_detections": sum(
                        len(frame["detections"]["predictions"]) 
                        for frame in filtered_results
                    ),
                    "processing_time": processor.total_processing_time
                },
                "detections": filtered_results
            })
        else:
            # 生成处理后的视频文件
            output_path = video_path.replace(".mp4", "_processed.mp4")
            processor.process_video(
                video_path=video_path,
                output_path=output_path,
                frame_interval=frame_interval
            )
            
            # 返回视频文件
            def iterfile():
                with open(output_path, mode="rb") as f:
                    yield from f
            
            return StreamingResponse(
                iterfile(), 
                media_type="video/mp4",
                headers={"Content-Disposition": f"attachment; filename=processed_{file.filename}"}
            )
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
    finally:
        # 清理临时文件
        if os.path.exists(video_path):
            os.unlink(video_path)

@app.post("/realtime_stream")
async def realtime_stream(
    camera_url: str,
    duration: int = 30,
    model_size: str = "nano"
):
    """
    处理实时视频流
    
    参数:
        camera_url: 摄像头RTSP地址或本地摄像头ID
        duration: 处理时长(秒)
        model_size: 模型规格 (nano/small/medium/large/xlarge)
    """
    try:
        # 根据模型规格选择API端点
        api_url = f"http://localhost:8000/predict?model={model_size}"
        processor = CameraProcessor(api_url=api_url)
        
        # 启动实时处理
        camera_id = int(camera_url) if camera_url.isdigit() else camera_url
        processor.start_stream(duration_seconds=duration, show_preview=False)
        
        return JSONResponse(content={
            "status": "success",
            "message": f"实时流处理已启动,将持续{duration}秒",
            "camera": camera_url,
            "model": model_size
        })
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/system_status")
async def system_status():
    """获取系统状态信息"""
    import psutil
    import GPUtil
    
    # 获取CPU和内存信息
    cpu_percent = psutil.cpu_percent(interval=1)
    memory = psutil.virtual_memory()
    
    # 获取GPU信息(如果可用)
    gpu_info = []
    try:
        gpus = GPUtil.getGPUs()
        for gpu in gpus:
            gpu_info.append({
                "name": gpu.name,
                "load": gpu.load * 100,
                "memory_used": gpu.memoryUsed,
                "memory_total": gpu.memoryTotal,
                "temperature": gpu.temperature
            })
    except:
        gpu_info = []
    
    return JSONResponse(content={
        "cpu_usage": cpu_percent,
        "memory_usage": memory.percent,
        "memory_used_gb": memory.used / (1024**3),
        "memory_total_gb": memory.total / (1024**3),
        "gpus": gpu_info,
        "service": "YOLO12 Video Processing API",
        "status": "running"
    })

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8001)

5.2 生产环境部署配置

对于生产环境,需要添加监控、日志和错误处理:

# config.py - 配置文件
import os
from datetime import datetime

class Config:
    # API配置
    API_HOST = os.getenv("API_HOST", "0.0.0.0")
    API_PORT = int(os.getenv("API_PORT", 8001))
    
    # YOLO12服务配置
    YOLO_API_URL = os.getenv("YOLO_API_URL", "http://localhost:8000/predict")
    
    # 视频处理配置
    MAX_VIDEO_SIZE = int(os.getenv("MAX_VIDEO_SIZE", 100 * 1024 * 1024))  # 100MB
    SUPPORTED_FORMATS = [".mp4", ".avi", ".mov", ".mkv"]
    MAX_PROCESSING_TIME = int(os.getenv("MAX_PROCESSING_TIME", 300))  # 5分钟
    
    # 性能配置
    DEFAULT_FRAME_INTERVAL = int(os.getenv("DEFAULT_FRAME_INTERVAL", 1))
    DEFAULT_CONFIDENCE = float(os.getenv("DEFAULT_CONFIDENCE", 0.25))
    MAX_WORKERS = int(os.getenv("MAX_WORKERS", 4))
    
    # 日志配置
    LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
    LOG_FILE = os.getenv("LOG_FILE", f"logs/yolo12_video_{datetime.now().strftime('%Y%m%d')}.log")
    
    # 存储配置
    UPLOAD_FOLDER = os.getenv("UPLOAD_FOLDER", "/tmp/yolo12_uploads")
    OUTPUT_FOLDER = os.getenv("OUTPUT_FOLDER", "/tmp/yolo12_outputs")
    
    @classmethod
    def validate(cls):
        """验证配置"""
        os.makedirs(cls.UPLOAD_FOLDER, exist_ok=True)
        os.makedirs(cls.OUTPUT_FOLDER, exist_ok=True)
        os.makedirs(os.path.dirname(cls.LOG_FILE), exist_ok=True)
        
        print(f"配置加载完成:")
        print(f"  API地址: {cls.API_HOST}:{cls.API_PORT}")
        print(f"  YOLO服务: {cls.YOLO_API_URL}")
        print(f"  上传目录: {cls.UPLOAD_FOLDER}")
        print(f)  输出目录: {cls.OUTPUT_FOLDER}")
# monitor.py - 监控模块
import time
import threading
import logging
from dataclasses import dataclass
from typing import Dict, List
from datetime import datetime

@dataclass
class ProcessingMetrics:
    """处理指标"""
    start_time: float
    end_time: float = 0
    frames_processed: int = 0
    total_detections: int = 0
    avg_processing_time: float = 0
    errors: List[str] = None
    
    def __post_init__(self):
        if self.errors is None:
            self.errors = []
    
    def complete(self):
        """标记处理完成"""
        self.end_time = time.time()
    
    @property
    def duration(self):
        """处理时长"""
        if self.end_time > 0:
            return self.end_time - self.start_time
        return time.time() - self.start_time
    
    @property
    def fps(self):
        """处理帧率"""
        if self.duration > 0:
            return self.frames_processed / self.duration
        return 0

class PerformanceMonitor:
    """性能监控器"""
    
    def __init__(self):
        self.metrics: Dict[str, ProcessingMetrics] = {}
        self.lock = threading.Lock()
        self.logger = logging.getLogger(__name__)
    
    def start_job(self, job_id: str):
        """开始监控任务"""
        with self.lock:
            self.metrics[job_id] = ProcessingMetrics(start_time=time.time())
            self.logger.info(f"开始监控任务: {job_id}")
    
    def update_job(self, job_id: str, frames: int = 1, detections: int = 0):
        """更新任务指标"""
        with self.lock:
            if job_id in self.metrics:
                self.metrics[job_id].frames_processed += frames
                self.metrics[job_id].total_detections += detections
    
    def add_error(self, job_id: str, error: str):
        """记录错误"""
        with self.lock:
            if job_id in self.metrics:
                self.metrics[job_id].errors.append(error)
                self.logger.error(f"任务 {job_id} 错误: {error}")
    
    def complete_job(self, job_id: str):
        """完成任务监控"""
        with self.lock:
            if job_id in self.metrics:
                self.metrics[job_id].complete()
                metrics = self.metrics[job_id]
                self.logger.info(
                    f"任务完成: {job_id} | "
                    f"时长: {metrics.duration:.1f}s | "
                    f"帧数: {metrics.frames_processed} | "
                    f"FPS: {metrics.fps:.1f} | "
                    f"检测数: {metrics.total_detections}"
                )
    
    def get_metrics(self, job_id: str) -> Dict:
        """获取任务指标"""
        with self.lock:
            if job_id in self.metrics:
                m = self.metrics[job_id]
                return {
                    "job_id": job_id,
                    "duration": m.duration,
                    "frames_processed": m.frames_processed,
                    "total_detections": m.total_detections,
                    "fps": m.fps,
                    "errors": m.errors,
                    "is_completed": m.end_time > 0
                }
        return {}
    
    def get_all_metrics(self) -> List[Dict]:
        """获取所有任务指标"""
        with self.lock:
            return [self.get_metrics(job_id) for job_id in self.metrics.keys()]

# 全局监控器实例
monitor = PerformanceMonitor()

6. 实际应用场景与优化建议

6.1 安防监控系统集成

对于安防监控场景,需要特别关注实时性和准确性:

class SecurityMonitor:
    """安防监控专用处理器"""
    
    def __init__(self, api_url="http://localhost:8000/predict"):
        self.api_url = api_url
        self.alert_rules = {
            "person": {"min_confidence": 0.7, "alert_message": "检测到人员入侵"},
            "car": {"min_confidence": 0.8, "alert_message": "检测到车辆进入"},
            "knife": {"min_confidence": 0.6, "alert_message": "检测到危险物品"},
            "gun": {"min_confidence": 0.6, "alert_message": "检测到武器"}
        }
        self.alert_history = []
    
    def process_security_feed(self, video_source, alert_callback=None):
        """
        处理安防视频流,触发警报
        
        参数:
            video_source: 视频源(摄像头ID或RTSP地址)
            alert_callback: 警报回调函数
        """
        processor = CameraProcessor(api_url=self.api_url)
        
        def process_frame_with_alerts(frame, detections):
            """处理帧并检查警报"""
            alerts = []
            predictions = detections.get("predictions", [])
            
            for pred in predictions:
                class_name = pred.get("class", "")
                confidence = pred.get("confidence", 0)
                
                # 检查是否符合警报规则
                if class_name in self.alert_rules:
                    rule = self.alert_rules[class_name]
                    if confidence >= rule["min_confidence"]:
                        alert = {
                            "timestamp": datetime.now().isoformat(),
                            "class": class_name,
                            "confidence": confidence,
                            "message": rule["alert_message"],
                            "bbox": pred.get("bbox", [])
                        }
                        alerts.append(alert)
                        
                        # 记录警报历史
                        self.alert_history.append(alert)
                        
                        # 触发回调
                        if alert_callback:
                            alert_callback(alert)
            
            # 在帧上绘制警报信息
            if alerts:
                frame = self.draw_alerts(frame, alerts)
            
            return frame, alerts
        
        # 修改CameraProcessor的绘制逻辑
        original_draw = processor.draw_detections
        
        def custom_draw(frame, detections):
            frame, alerts = process_frame_with_alerts(frame, detections)
            frame = original_draw(frame, detections)
            
            # 在帧上添加警报计数
            if alerts:
                alert_text = f"Alerts: {len(alerts)}"
                cv2.putText(frame, alert_text, (10, 90),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            
            return frame
        
        processor.draw_detections = custom_draw
        
        # 开始处理
        processor.start_stream(duration_seconds=None, show_preview=True)
    
    def draw_alerts(self, frame, alerts):
        """在帧上绘制警报信息"""
        for i, alert in enumerate(alerts):
            # 绘制红色边界框
            bbox = alert.get("bbox", [])
            if len(bbox) == 4:
                x1, y1, x2, y2 = map(int, bbox)
                cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 3)
                
                # 添加警报文本
                alert_text = f"ALERT: {alert['class']} ({alert['confidence']:.2f})"
                cv2.putText(frame, alert_text, (x1, y1 - 10),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
        
        return frame
    
    def get_alert_summary(self, hours=24):
        """获取指定时间内的警报摘要"""
        cutoff_time = datetime.now().timestamp() - (hours * 3600)
        
        recent_alerts = [
            alert for alert in self.alert_history
            if datetime.fromisoformat(alert["timestamp"]).timestamp() > cutoff_time
        ]
        
        summary = {}
        for alert in recent_alerts:
            class_name = alert["class"]
            if class_name not in summary:
                summary[class_name] = 0
            summary[class_name] += 1
        
        return {
            "total_alerts": len(recent_alerts),
            "by_class": summary,
            "time_period_hours": hours
        }

# 使用示例
def alert_handler(alert):
    """警报处理函数"""
    print(f"[ALERT] {alert['timestamp']}: {alert['message']}")
    print(f"        类别: {alert['class']}, 置信度: {alert['confidence']:.2f}")
    
    # 这里可以添加实际警报逻辑,如:
    # - 发送邮件通知
    # - 触发声音警报
    # - 保存截图
    # - 调用其他系统API

if __name__ == "__main__":
    monitor = SecurityMonitor()
    monitor.process_security_feed(
        video_source=0,  # 默认摄像头
        alert_callback=alert_handler
    )

6.2 性能优化建议总结

根据实际使用经验,以下优化建议可以显著提升视频处理性能:

  1. 选择合适的模型规格

    • 实时监控:使用nano或small版本
    • 离线分析:根据精度要求选择medium/large版本
    • 边缘设备:必须使用nano版本
  2. 调整处理参数

    • 降低输入分辨率(如从1080p降到720p)
    • 增加帧采样间隔(如每2帧处理1帧)
    • 调整置信度阈值,减少后处理时间
  3. 硬件优化

    • 使用GPU加速(确保CUDA正确配置)
    • 增加系统内存,减少磁盘IO
    • 使用SSD存储,加快视频读取速度
  4. 软件优化

    • 使用多线程/多进程并行处理
    • 批量处理API请求,减少网络开销
    • 缓存检测结果,避免重复计算

7. 总结

通过本文的详细指南,你已经掌握了使用YOLO12进行实时视频流处理的核心技术。从基础的逐帧检测到高级的多线程优化,从简单的脚本到完整的API服务,这些方案可以满足不同场景的需求。

关键要点回顾:

  1. 逐帧处理是基础:通过OpenCV读取视频帧,调用YOLO12 API进行检测,再重新组合成视频
  2. 性能优化很重要:多线程处理、帧采样、分辨率调整都能显著提升处理速度
  3. API集成是关键:将视频处理功能封装为API服务,方便系统集成和扩展
  4. 实际应用需定制:不同场景(安防、质检、分析)需要不同的处理逻辑和优化策略

YOLO12的实时目标检测能力为视频分析应用提供了强大的基础。无论是构建安防监控系统、开发智能交通应用,还是创建内容分析平台,这套方案都能为你提供可靠的技术支持。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐