人脸识别程序截图功能异常:无法抓取帧错误求助
人脸识别程序问题排查与修复
问题概述
基于OpenCV与face_recognition库开发的人脸识别程序,预期实现:
- 识别本地
images目录(人脸数据库)中的人脸; - 未识别到人脸时,启动10秒计时器,计时结束自动截取摄像头画面。
目前存在两个问题:
- 执行截图时抛出
can't grab frame错误,尽管最终能完成截图; - 未检测到人脸时,else分支未立即触发,再次将人脸对准摄像头时才执行该分支逻辑。
错误日志
[ WARN:0@7.686] global D:\a\opencv-python\opencv-python\opencv\modules\videoio\src\cap_msmf.cpp (1752) CvCapture_MSMF::grabFrame videoio(MSMF): can't grab frame. Error: -1072875772 [ WARN:0@7.741] global D:\a\opencv-python\opencv-python\opencv\modules\videoio\src\cap_msmf.cpp (1752) CvCapture_MSMF::grabFrame videoio(MSMF): can't grab frame. Error: -1072875772
问题根源分析
帧抓取错误原因:
- 多进程共享同一个
VideoCapture实例,主进程和子进程同时读取摄像头资源,引发冲突导致帧抓取失败; take_screenshot函数中提前调用cap.release()释放摄像头,破坏了主进程的摄像头连接。
- 多进程共享同一个
分支触发异常原因:
- else分支嵌套在人脸匹配的循环内部,仅当检测到人脸但匹配数据库失败时才会执行;完全未检测到人脸时,循环不会执行,自然不会触发else逻辑;
- 多进程调用的
camera()函数依赖全局变量img,但多进程间内存不共享,导致子进程无法获取实时画面;time.sleep(0)无实际作用,反而可能影响主循环效率。
修改后的完整代码
import cv2 import numpy as np import matplotlib.pyplot as plt import face_recognition import os import time path = 'images' images = [] classNames = [] myList = os.listdir(path) cap = cv2.VideoCapture(0) print(myList) for cls in myList: curImg = cv2.imread(f'{path}/{cls}') images.append(curImg) classNames.append(os.path.splitext(cls)[0]) print(classNames) def take_screenshot(frame): # 使用主进程传递的帧进行截图,避免重复读取摄像头 img1 = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) plt.imshow(img1) plt.title("Camera image") img_name = f"opencv_frame_{time.time()}.png" cv2.imwrite(img_name, frame) print(f"Screenshot saved as {img_name}") plt.xticks([]) plt.yticks([]) plt.show() def countdown(u): while u: mins, sec = divmod(u, 60) timer = '{:02d}:{:02d}'.format(mins, sec) print(timer, end='\r') time.sleep(1) u -= 1 def findEncodings(images): encodeList = [] for img in images: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 处理可能的人脸检测失败情况 encodes = face_recognition.face_encodings(img) if encodes: encodeList.append(encodes[0]) else: print(f"Warning: No face found in {img}") return encodeList # 初始化人脸编码 encodeListKnown = findEncodings(images) print("Encoding Complete.") # 状态标记:是否正在计时 is_counting = False count_start_time = 0 # 主循环 while True: success, img = cap.read() if not success or img is None: print("Failed to grab frame") continue imgS = cv2.resize(img, (0, 0), None, 0.25, 0.25) imgS = cv2.cvtColor(imgS, cv2.COLOR_BGR2RGB) faceCurFrame = face_recognition.face_locations(imgS) encodeCurFrame = face_recognition.face_encodings(imgS, faceCurFrame) face_detected = False # 处理检测到的人脸 for encodeFace, faceLoc in zip(encodeCurFrame, faceCurFrame): face_detected = True matches = face_recognition.compare_faces(encodeListKnown, encodeFace) faceDis = face_recognition.face_distance(encodeListKnown, encodeFace) matchIndex = np.argmin(faceDis) if matches[matchIndex]: name = classNames[matchIndex].upper() print(name) y1, x2, y2, x1 = faceLoc y1, x2, y2, x1 = y1 * 4, x2 * 4, y2 * 4, x1 * 4 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.rectangle(img, (x1, y2 - 35), (x2, y2), (0, 255, 0), cv2.FILLED) cv2.putText(img, name, (x1 + 6, y2 - 6), cv2.FONT_HERSHEY_COMPLEX, 0.9, (255, 255, 255), 2) # 匹配失败的情况也标记为检测到人脸 else: print("Unknown face detected") y1, x2, y2, x1 = faceLoc y1, x2, y2, x1 = y1 * 4, x2 * 4, y2 * 4, x1 * 4 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.rectangle(img, (x1, y2 - 35), (x2, y2), (0, 0, 255), cv2.FILLED) cv2.putText(img, "UNKNOWN", (x1 + 6, y2 - 6), cv2.FONT_HERSHEY_COMPLEX, 0.9, (255, 255, 255), 2) # 处理未检测到人脸的逻辑 if not face_detected: if not is_counting: print("No face detected, starting 10-second countdown...") is_counting = True count_start_time = time.time() else: elapsed = time.time() - count_start_time if elapsed >= 10: print("Countdown finished, taking screenshot...") take_screenshot(img) is_counting = False else: # 显示剩余时间 remaining = 10 - int(elapsed) print(f"Time remaining: {remaining}s", end='\r') else: # 检测到人脸,重置计时状态 is_counting = False # 实时显示摄像头画面 cv2.imshow('Webcam', img) # 按下q键退出程序 if cv2.waitKey(1) & 0xFF == ord('q'): break # 释放资源 cap.release() cv2.destroyAllWindows()
修复说明
解决帧抓取错误:
- 移除多进程逻辑,改用主进程内部的计时状态管理,避免摄像头资源冲突;
take_screenshot直接使用主进程传递的当前帧,无需重复读取摄像头,也不再提前释放摄像头资源。
修复分支触发异常:
- 将未检测到人脸的逻辑移到循环外部,通过
face_detected标记判断是否检测到人脸,确保未检测到人脸时立即触发计时; - 添加
is_counting状态标记,避免重复启动计时,同时在检测到人脸时重置计时状态。
- 将未检测到人脸的逻辑移到循环外部,通过
其他优化:
- 增加人脸编码时的异常处理,避免单张图片无人脸导致程序崩溃;
- 添加退出逻辑,按下
q键可正常退出程序并释放资源; - 实时显示剩余计时时间,提升用户体验。
内容的提问来源于stack exchange,提问作者ARKAJEET BASAK
相关产品推荐
相关产品推荐

