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

使用OpenCV+ESP32-CAM时Detection窗口无法加载卡顿问题求助

问题原因与修复方案

核心问题分析

  • 多进程与OpenCV GUI冲突:OpenCV的窗口操作依赖进程内的GUI上下文,ProcessPoolExecutor创建的子进程无法正确初始化和渲染窗口,直接导致detection窗口无响应。
  • 重复请求摄像头资源:两个进程同时频繁请求ESP32-CAM的图片URL,会占满摄像头的网络带宽,引发请求阻塞;再加上目标检测本身耗时,最终导致进程卡死。

修复方案

改用多线程+共享图片缓存的方式,既解决GUI渲染问题,又避免重复请求摄像头,同时提升整体性能:

修改后的代码

import cv2
import cvlib as cv
import urllib.request
import numpy as np
from cvlib.object_detection import draw_bbox
import threading
from queue import Queue

url = 'http://192.168.137.237/cam-hi.jpg'
image_queue = Queue(maxsize=1)  # 用队列共享最新的摄像头图片

def fetch_frames():
    """专门负责从摄像头拉取图片的线程"""
    while True:
        try:
            img_resp = urllib.request.urlopen(url, timeout=5)
            imgnp = np.array(bytearray(img_resp.read()), dtype=np.uint8)
            im = cv2.imdecode(imgnp, -1)
            # 队列满时替换旧图,只保留最新帧
            if not image_queue.empty():
                try:
                    image_queue.get_nowait()
                except:
                    pass
            image_queue.put(im)
        except Exception as e:
            print(f"拉取图片出错: {e}")

def show_live():
    """显示实时画面的线程"""
    cv2.namedWindow("live transmission", cv2.WINDOW_AUTOSIZE)
    while True:
        if not image_queue.empty():
            im = image_queue.get()
            cv2.imshow('live transmission', im)
        if cv2.waitKey(5) == ord('q'):
            break
    cv2.destroyAllWindows()
    exit()

def show_detection():
    """显示检测结果的线程"""
    cv2.namedWindow("detection", cv2.WINDOW_AUTOSIZE)
    while True:
        if not image_queue.empty():
            im = image_queue.get()
            # 执行目标检测
            bbox, label, conf = cv.detect_common_objects(im)
            im = draw_bbox(im, bbox, label, conf)
            cv2.imshow('detection', im)
        if cv2.waitKey(5) == ord('q'):
            break
    cv2.destroyAllWindows()
    exit()

if __name__ == '__main__':
    print("started")
    # 启动拉取图片的守护线程
    fetch_thread = threading.Thread(target=fetch_frames, daemon=True)
    fetch_thread.start()
    # 启动两个显示线程
    live_thread = threading.Thread(target=show_live)
    detection_thread = threading.Thread(target=show_detection)
    live_thread.start()
    detection_thread.start()
    # 等待线程结束
    live_thread.join()
    detection_thread.join()

关键修改点

  1. 替换多进程为多线程:用threading.Thread替代ProcessPoolExecutor,所有线程共享同一进程的GUI上下文,OpenCV窗口可正常渲染。
  2. 单线程拉取图片:单独开辟线程负责摄像头图片获取,避免重复请求占用带宽,用队列缓存最新帧供两个显示线程调用。
  3. 队列缓存机制:通过Queue限制存储最新一帧,避免队列积压导致的延迟问题。

额外优化建议

  • 若目标检测帧率过低,可将ESP32-CAM的请求路径从cam-hi.jpg改为cam-lo.jpg,降低图片分辨率以减少处理耗时。
  • 可进一步完善异常处理逻辑,应对网络波动等突发情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:44:54