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

Python+MTCNN+OpenCV多线程人脸检测:线程同步问题求助

问题描述

使用Python、OpenCV与MTCNN实现人脸检测,单线程方案运行正常但速度较慢,遂引入多线程优化性能。程序包含process_frame()函数及frame、frame_buffer两个变量,预期实现图像处理与视频流播放同步。目前发现process_frame()内的检测逻辑可生成带检测框的画面,但主循环仅能显示无检测框的原始视频流,怀疑是线程同步问题导致。已通过try-except捕获异常未发现问题,print确认ret为True,frame可正常获取摄像头帧,但无法定位问题。

相关代码
import cv2
import numpy as np
from mtcnn import MTCNN
import threading
import time
import tensorflow as tf
from threading import Lock
#import pdb


#pdb.set_trace()
lock = Lock()
cap = cv2.VideoCapture(0)
frame_buffer = []
frame = None


def process_frame():
    global frame_buffer, stop_event, lock, frame
    while not stop_event.is_set():
        try:
            lock.acquire()
            if frame is None:
                print("frame vide")
                continue
            print ("frame copié avec succes")
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            frame = cv2.resize(frame, (320,240))
            frame = frame_buffer
        except Exception as e:
            print("Une exception a été levée lors du traitement de l'image :", e)
            continue
        finally :
            lock.release()
        
        try:
            model = MTCNN(min_face_size=10)
            predictions = model.detect_faces(frame)
            print(f"{len(predictions)} visages détectés")
        except Exception as e:
            print("Une exception a été levée lors de la détection des visages :", e)
        
        try:
            predictions = [prediction for prediction in predictions if prediction['confidence'] > 0.5]
            print(f"{len(predictions)} visages filtrés")
        except Exception as e:
            print("Une exception a été levée lors de la filtration des prédictions :", e)
                
        for face in predictions:
            try:
                x, y, w, h = face['box']
                with lock :
                    cv2.rectangle(frame, (x,y), (x+w, y+h), (255, 0, 0), 2)
                    cv2.imwrite("parth/image_traitee_process_frame.jpg", frame_buffer,     [cv2.IMWRITE_JPEG_QUALITY, 100])
            except Exception as e:
                print("Une exception a été levée lors de l'ajout de l'annotation du rectangle :", e)
        print ("frame mis a jour avec succes")
        #frame_buffer = frame
        print ("Copie de la liste de frame : ", frame, "dans frame_buffer : ", frame_buffer)
        time.sleep(0.01)

stop_event = threading.Event()
thread = threading.Thread(target=process_frame)
thread.daemon = True
thread.start()

start_time = time.perf_counter()
frame_count = 0

def stop_thread():
    stop_event.set()
    thread.join()

 
while True:
    
    ret, frame = cap.read()
    if ret:
        frame_buffer = frame.copy()
    else :
        frame = frame_buffer
    frame = frame_buffer
    print ("valeur de frame_buffer après cap.read : ", frame_buffer)
    print (cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    print (cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    cv2.imwrite("parth/image_traitee_boucle_while.jpg", frame_buffer, [cv2.IMWRITE_JPEG_QUALITY, 50])
    
    if not ret:
        break
    lock.acquire()
    #frame_buffer = frame
    lock.release()
    frame_count += 1
    elapsed_time = time.perf_counter() - start_time
    if elapsed_time > 1.0:
        print("Frames par seconde : ", frame_count / elapsed_time)
        start_time = time.perf_counter()
        frame_count = 0
    lock.acquire()
    #print(frame_buffer.shape)
    cv2.imshow("Output", frame_buffer)
    lock.release()
    key = cv2.waitKey(1) & 0xFF
    if key == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()
stop_thread()
问题分析与修复

你的代码存在多个逻辑错误,导致处理后的帧无法传递到主循环显示:

核心错误点

  1. 变量赋值逻辑完全颠倒:process_frame中执行frame = frame_buffer,导致处理的是旧缓冲区数据,且处理后的帧从未更新到共享缓冲区。
  2. MTCNN重复初始化:每次循环都创建MTCNN实例,这会严重拖慢处理速度,应该只初始化一次。
  3. 颜色空间未适配显示要求:MTCNN需要RGB格式,但OpenCV显示依赖BGR,处理后未转换回对应格式,导致检测框显示异常。
  4. 线程同步范围错误:共享缓冲区的读写未完全被锁保护,且处理后的帧未正确写入frame_buffer。
  5. 主循环变量混乱:frame和frame_buffer的赋值逻辑混乱,导致主循环始终读取原始帧。

修复后的代码

import cv2
import numpy as np
from mtcnn import MTCNN
import threading
import time
from threading import Lock

lock = Lock()
cap = cv2.VideoCapture(0)
# 初始化共享缓冲区为None
frame_buffer = None
stop_event = threading.Event()
# 提前初始化MTCNN模型,避免重复创建开销
detector = MTCNN(min_face_size=10)

def process_frame():
    global frame_buffer, stop_event, lock
    while not stop_event.is_set():
        current_frame = None
        # 线程安全读取共享缓冲区的帧
        with lock:
            if frame_buffer is None:
                time.sleep(0.01)
                continue
            # 复制帧避免直接修改共享数据
            current_frame = frame_buffer.copy()
        
        # 颜色空间转换:BGR转RGB(适配MTCNN要求)
        rgb_frame = cv2.cvtColor(current_frame, cv2.COLOR_BGR2RGB)
        # 人脸检测与结果过滤
        try:
            predictions = detector.detect_faces(rgb_frame)
            predictions = [p for p in predictions if p['confidence'] > 0.5]
            print(f"检测到{len(predictions)}张人脸")
        except Exception as e:
            print(f"检测出错:{e}")
            continue
        
        # 绘制检测框(在复制的帧上操作,不影响原始数据)
        for face in predictions:
            x, y, w, h = face['box']
            # 确保坐标为整数,避免OpenCV报错
            x, y, w, h = int(x), int(y), int(w), int(h)
            cv2.rectangle(current_frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
        
        # 线程安全写入处理后的帧到共享缓冲区
        with lock:
            frame_buffer = current_frame
        time.sleep(0.01)

# 启动处理线程
thread = threading.Thread(target=process_frame)
thread.daemon = True
thread.start()

start_time = time.perf_counter()
frame_count = 0

def stop_thread():
    stop_event.set()
    thread.join()

while True:
    ret, original_frame = cap.read()
    if not ret:
        break
    
    # 线程安全写入原始帧到共享缓冲区
    with lock:
        frame_buffer = original_frame.copy()
    
    # 统计帧率
    frame_count += 1
    elapsed_time = time.perf_counter() - start_time
    if elapsed_time > 1.0:
        print(f"帧率:{frame_count / elapsed_time:.2f} FPS")
        start_time = time.perf_counter()
        frame_count = 0
    
    # 线程安全读取并显示处理后的帧
    with lock:
        if frame_buffer is not None:
            cv2.imshow("人脸检测结果", frame_buffer)
    
    # 退出逻辑
    key = cv2.waitKey(1) & 0xFF
    if key == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()
stop_thread()

修复说明

  • 模型预初始化:在启动线程前创建MTCNN实例,避免重复初始化的巨大开销。
  • 清晰的数据流:主循环将原始帧写入frame_buffer,处理线程读取、处理后写回缓冲区,主循环读取缓冲区显示,形成完整闭环。
  • 适配颜色空间:仅在检测时转换为RGB,绘制框直接在原始BGR帧的副本上操作,无需额外转换即可正常显示。
  • 线程安全操作:所有对frame_buffer的读写都通过with lock保证原子性,避免数据竞争。
  • 变量职责明确:区分原始帧original_frame和共享缓冲区frame_buffer,避免赋值冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 22:00:58