基于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
相关产品推荐
相关产品推荐

