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

人脸识别程序截图功能异常:无法抓取帧错误求助

人脸识别程序问题排查与修复

问题概述

基于OpenCV与face_recognition库开发的人脸识别程序,预期实现:

  • 识别本地images目录(人脸数据库)中的人脸;
  • 未识别到人脸时,启动10秒计时器,计时结束自动截取摄像头画面。

目前存在两个问题:

  1. 执行截图时抛出can't grab frame错误,尽管最终能完成截图;
  2. 未检测到人脸时,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

问题根源分析

  1. 帧抓取错误原因:

    • 多进程共享同一个VideoCapture实例,主进程和子进程同时读取摄像头资源,引发冲突导致帧抓取失败;
    • take_screenshot函数中提前调用cap.release()释放摄像头,破坏了主进程的摄像头连接。
  2. 分支触发异常原因:

    • 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()

修复说明

  1. 解决帧抓取错误:

    • 移除多进程逻辑,改用主进程内部的计时状态管理,避免摄像头资源冲突;
    • take_screenshot直接使用主进程传递的当前帧,无需重复读取摄像头,也不再提前释放摄像头资源。
  2. 修复分支触发异常:

    • 将未检测到人脸的逻辑移到循环外部,通过face_detected标记判断是否检测到人脸,确保未检测到人脸时立即触发计时;
    • 添加is_counting状态标记,避免重复启动计时,同时在检测到人脸时重置计时状态。
  3. 其他优化:

    • 增加人脸编码时的异常处理,避免单张图片无人脸导致程序崩溃;
    • 添加退出逻辑,按下q键可正常退出程序并释放资源;
    • 实时显示剩余计时时间,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:30:58