Flask-SocketIO集成Yoloface:scan_face()仅首次调用有效求助
问题:Flask-SocketIO集成YOLO人脸识别仅首次请求生效
我开发的Flask服务端集成了SocketIO,Android客户端消息收发正常。但引入YOLO实现人脸识别后,scan_face()函数仅在第一次响应客户端scan_request事件时能返回正确结果;后续客户端发送scan_request时,该函数完全不执行,断点调试也被跳过,服务端仅持续监听请求。
控制台输出
flask run --host='0.0.0.0' ----- info ----- [i] Source of the camera: 0 [i] Path to output directory: outputs/ ########################################################### ==> Skipping create the outputs/ directory... * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://x.x.x.x:5000 * Running on http://x.x.x.x:5000 Press CTRL+C to quit x.x.x.x - - [25/Mar/2024 23:29:04] "GET /socket.io/?EIO=4&transport=polling HTTP/1.1" 200 - Client connected to default namespace x.x.x.x - - [25/Mar/2024 23:29:04] "POST /socket.io/?EIO=4&transport=polling&sid=iiua90AsbvoVBspJAAAA HTTP/1.1" 200 - x.x.x.x - - [25/Mar/2024 23:29:04] "GET /socket.io/?EIO=4&transport=polling&sid=iiua90AsbvoVBspJAAAA HTTP/1.1" 200 - x.x.x.x - - [25/Mar/2024 23:29:04] "GET /socket.io/?EIO=4&transport=polling&sid=iiua90AsbvoVBspJAAAA HTTP/1.1" 200 - x.x.x.x - - [25/Mar/2024 23:29:04] "POST /socket.io/?EIO=4&transport=polling&sid=iiua90AsbvoVBspJAAAA HTTP/1.1" 200 - check1 Received scan request: Scanne Person Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway. 200 227 254 [i] ==> # detected faces: 1 ############################################################ value1 2gesichter check2 person sent 2 check1 Received scan request: Scanne Person check1 Received scan request: Scanne Person
关键代码片段
scan_request事件处理器
@socketio.on('scan_request') def handle_scan_request(message): print('check1') print(f"Received scan request: {message}") scan_result = scan_face() # 仅首次调用生效 print('check2') if scan_result == 1: get_person_by_id() elif scan_result == 0: person_not_detected()
scan_face()函数实现
import argparse import sys import os from utils import * SRC = 0 OUTPUT_DIR = 'outputs/' MODEL_CFG = './cfg/yolov3-face.cfg' MODEL_WEIGHTS = './model-weights/yolov3-wider_16000.weights' print('----- info -----') print('[i] Source of the camera: ', SRC) print('[i] Path to output directory: ', OUTPUT_DIR) print('###########################################################\n') if not os.path.exists(OUTPUT_DIR): print('==> Creating the {} directory...'.format(OUTPUT_DIR)) os.makedirs(OUTPUT_DIR) else: print('==> Skipping create the {} directory...'.format(OUTPUT_DIR)) net = cv2.dnn.readNetFromDarknet(MODEL_CFG, MODEL_WEIGHTS) net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) def scan_face(): wind_name = 'face detection using YOLOv3' cv2.namedWindow(wind_name, cv2.WINDOW_NORMAL) cap = cv2.VideoCapture(SRC) value = None while True: has_frame, frame = cap.read() blob = cv2.dnn.blobFromImage(frame, 1 / 255, (IMG_WIDTH, IMG_HEIGHT), [0, 0, 0], 1, crop=False) net.setInput(blob) outs = net.forward(get_outputs_names(net)) faces = post_process(frame, outs, CONF_THRESHOLD, NMS_THRESHOLD) print('[i] ==> # detected faces: {}'.format(len(faces))) print('#' * 60) info = [ ('number of faces detected', '{}'.format(len(faces))) ] for (i, (txt, val)) in enumerate(info): text = '{}: {}'.format(txt, val) cv2.putText(frame, text, (10, (i * 20) + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, COLOR_RED, 2) cv2.imshow(wind_name, frame) value = len(faces) print("value" + str(value)) if value == 0: print('0gesicht') break elif value == 0: # 逻辑重复 print('1gesicht') break else: print('2gesichter') break cap.release() cv2.destroyAllWindows() if value == 0: return 0 else: return 1
问题原因与解决方案
核心问题
- 摄像头资源锁定:首次调用后,摄像头设备未被完全释放,后续无法重新打开,导致
cap.read()失败,函数卡在循环中无法退出。 - 主线程阻塞:
cv2.imshow()等GUI操作占用主线程资源,Flask-SocketIO单线程模式下,后续请求无法被处理。 - 逻辑冗余:
scan_face()中判断分支重复,导致value==1的情况无法被正确识别。
修复步骤
- 移除GUI相关操作:服务端不需要可视化窗口,删除
cv2.namedWindow()、cv2.imshow()代码,避免主线程被阻塞。 - 确保摄像头资源完全释放:使用
try-finally块保证摄像头被释放,添加cv2.waitKey(1)强制刷新OpenCV资源:
def scan_face(): cap = None try: cap = cv2.VideoCapture(SRC) if not cap.isOpened(): print("[!] 无法打开摄像头") return 0 has_frame, frame = cap.read() if not has_frame: print("[!] 无法读取摄像头帧") return 0 blob = cv2.dnn.blobFromImage(frame, 1 / 255, (IMG_WIDTH, IMG_HEIGHT), [0, 0, 0], 1, crop=False) net.setInput(blob) outs = net.forward(get_outputs_names(net)) faces = post_process(frame, outs, CONF_THRESHOLD, NMS_THRESHOLD) print('[i] ==> # detected faces: {}'.format(len(faces))) print('#' * 60) value = len(faces) print("value" + str(value)) finally: if cap is not None: cap.release() cv2.destroyAllWindows() cv2.waitKey(1) # 强制释放资源 return 0 if value ==0 else 1
- 用后台线程处理人脸识别:避免阻塞SocketIO事件主线程,修改事件处理器:
from threading import Thread @socketio.on('scan_request') def handle_scan_request(message): print('check1') print(f"Received scan request: {message}") # 启动后台线程执行检测 Thread(target=process_scan).start() def process_scan(): scan_result = scan_face() print('check2') if scan_result == 1: get_person_by_id() elif scan_result == 0: person_not_detected()
- 修复逻辑错误:修正重复的判断分支:
if value == 0: print('0gesicht') break elif value == 1: print('1gesicht') break else: print('2gesichter') break
- 使用异步服务器:替换默认开发服务器,使用
eventlet提升并发能力,修改启动方式:
if __name__ == '__main__': socketio.run(create_app(), host='0.0.0.0', debug=True)
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

