You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于YOLOv8+Redis的多IP摄像头检测脚本问题求助

多IP摄像头YOLOv8检测脚本问题修复与功能增强

问题概述

基于Ultralytics YOLOv8编写的多IP摄像头目标检测脚本存在以下问题:

  • Redis队列未按预期循环返回所有摄像头的帧,仅返回1-2个摄像头的帧
  • 未产生任何检测结果
  • 需要实现仅检测人体(person)并在检测到人体时保存图像的功能

问题修复与功能实现

1. Redis队列帧分布不均问题修复

原脚本中每个摄像头线程采用固定时间偏移捕获帧,多线程易出现时间冲突,导致部分摄像头的帧被连续消费,看起来仅返回少数摄像头的帧。同时原时间同步逻辑存在缺陷,易引发线程阻塞或帧捕获混乱。

修复方式:

  • 改用基于帧率的间隔捕获逻辑,每个摄像头线程独立控制捕获节奏,确保各摄像头的帧均匀进入队列
  • 设置摄像头缓冲区大小,降低帧读取延迟,减少丢帧情况

2. 无检测结果问题修复

原脚本错误加载模型配置文件(yolov8n.yaml)并重新训练,从头训练的小模型不具备有效检测能力;同时未对模型推理结果做解析处理,无法输出检测信息。

修复方式:

  • 加载YOLOv8预训练权重文件(yolov8n.pt),无需重新训练(有自定义数据集需求除外)
  • 新增检测结果解析逻辑,提取并处理人体检测信息

3. 仅检测人体并保存图像功能实现

  • 模型推理时指定classes=0(COCO数据集内person类别的ID为0),过滤仅保留人体检测结果
  • 解析检测结果,当检测到人体时保存带检测框的标注图像

修改后的完整代码

import os
import cv2
import time
import redis
import threading
from ultralytics import YOLO
from datetime import datetime

CAMERA_CONFIG = {
    "DiningHall2Lawn": "rtsp://", 
    "Lawn2MainGate": "rtsp://", 
    "Garage2Servant": "rtsp://", 
    "Servant2Garage": "rtsp://", 
    "Garage2MainGate": "rtsp://"
}

FRAMES_PER_SECOND = 3
CAPTURE_INTERVAL = 1.0 / FRAMES_PER_SECOND
BASE_IMAGE_SAVE_PATH = r'C:\Users\rosha\Downloads\objectdetection\savedimages'
DETECTION_SAVE_PATH = os.path.join(BASE_IMAGE_SAVE_PATH, "person_detections")
IMAGE_SAVE_PATH = {camera: os.path.join(BASE_IMAGE_SAVE_PATH, camera) for camera in CAMERA_CONFIG.keys()}

# 初始化Redis客户端,开启自动解码
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0, decode_responses=True)

def test_camera_connection(rtsp_url, camera_name):
    cap = cv2.VideoCapture(rtsp_url)
    if cap.isOpened():
        print(f"Successfully connected to camera {camera_name}.")
        cap.release()
        return True
    else:
        print(f"Failed to connect to camera {camera_name}.")
        return False

def capture_frames(rtsp_url, camera_name, redis_client):
    cap = cv2.VideoCapture(rtsp_url)
    camera_folder = IMAGE_SAVE_PATH[camera_name]
    if not os.path.exists(camera_folder):
        os.makedirs(camera_folder)

    # 设置摄像头缓冲区大小,减少帧延迟
    cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)

    try:
        while True:
            start_time = time.time()
            ret, frame = cap.read()
            if ret:
                timestamp = datetime.now().strftime("%Y%m%d%H%M%S_%f")[:-3]
                image_name = os.path.join(camera_folder, f"{camera_name}_{timestamp}.jpg")
                cv2.imwrite(image_name, frame)
                # 存入摄像头名+图像路径,方便后续追踪来源
                redis_client.rpush('camera_queue', f"{camera_name}|{image_name}")
            else:
                print(f"Failed to read frame for camera {camera_name}. Reconnecting...")
                cap.release()
                time.sleep(2)
                cap = cv2.VideoCapture(rtsp_url)
                continue
            
            # 控制捕获帧率,避免过快或过慢
            elapsed_time = time.time() - start_time
            sleep_time = max(CAPTURE_INTERVAL - elapsed_time, 0)
            time.sleep(sleep_time)
    except Exception as e:
        print(f"An error occurred with camera {camera_name}: {e}")
    finally:
        cap.release()

def process_images_from_queue(redis_client, model):
    # 创建人体检测结果保存目录
    if not os.path.exists(DETECTION_SAVE_PATH):
        os.makedirs(DETECTION_SAVE_PATH)
    
    while True:
        # 阻塞式获取队列元素,避免轮询浪费资源
        queue_item = redis_client.blpop('camera_queue', timeout=5)
        if not queue_item:
            continue
        
        _, item_data = queue_item
        camera_name, image_path = item_data.split('|', 1)
        try:
            # 仅检测人体类别(COCO数据集ID为0)
            results = model(image_path, classes=0, verbose=False)
            
            # 解析检测结果
            for result in results:
                if len(result.boxes) > 0:
                    # 检测到人体,保存带检测框的标注图像
                    timestamp = datetime.now().strftime("%Y%m%d%H%M%S_%f")[:-3]
                    save_name = os.path.join(DETECTION_SAVE_PATH, f"{camera_name}_person_{timestamp}.jpg")
                    annotated_frame = result.plot()
                    cv2.imwrite(save_name, annotated_frame)
                    print(f"Detected person in {camera_name}, saved to {save_name}")
            
            # 可选:删除原始捕获图像,节省存储空间
            # os.remove(image_path)
        except Exception as e:
            print(f"An error occurred while processing {image_path}: {e}")

def main():
    # 加载YOLOv8n预训练模型
    model = YOLO("yolov8n.pt")

    # 测试所有摄像头连接状态
    all_cameras_connected = all(test_camera_connection(url, name) for name, url in CAMERA_CONFIG.items())

    if all_cameras_connected:
        print('All cameras are connected. Capturing and processing has started')
        threads = []
        
        # 启动各摄像头的帧捕获线程
        for camera_name, rtsp_url in CAMERA_CONFIG.items():
            thread = threading.Thread(target=capture_frames, args=(rtsp_url, camera_name, redis_client), daemon=True)
            thread.start()
            threads.append(thread)
        
        # 启动帧处理线程
        process_thread = threading.Thread(target=process_images_from_queue, args=(redis_client, model), daemon=True)
        process_thread.start()
        threads.append(process_thread)
        
        # 保持主线程运行,可通过Ctrl+C终止
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("Stopping all threads...")
    else:
        print("Not all cameras could be connected. Exiting.")

if __name__ == "__main__":
    main()

关键修改说明

  • 模型加载:替换为预训练权重文件,移除无效的重新训练步骤
  • 帧捕获优化:基于帧率计算捕获间隔,设置摄像头缓冲区减少延迟,避免多线程时间冲突
  • 队列管理:使用blpop阻塞式读取队列,提升资源利用率;同时记录摄像头来源,方便追踪
  • 人体检测逻辑:指定检测类别过滤仅保留人体,解析结果后保存带标注的图像
  • 线程控制:设置守护线程,支持通过Ctrl+C优雅终止程序

内容的提问来源于stack exchange,提问作者peacelover007

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 03:02:02