python实现人脸识别

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

提示:这里可以添加本文要记录的大概内容:

环境配置:

win11

usb摄像头

Python 3.11.9


提示:以下是本篇文章正文内容,下面案例可供参考

一、pandas是什么?

python实现简单的人脸识别供功能。

1.功能实现

1, 普通状态对人脸进行校验,打印识别度

2,rec name命令注册人脸

代码目录如下

代码如下(示例):

main.py

python 复制代码
# main.py
#from camera import Camera
#from face_detector import FaceDetector
#from face_recognition import FaceRecognizer
#from utils import draw_faces
#from config import WINDOW_NAME, EXIT_KEY, DEBUG
#import os
#import cv2
#
#def main():
#    print("🚀 人脸识别签到系统启动...")
#
#    # 初始化模块
#    cam = Camera()
#    detector = FaceDetector()
#    recognizer = FaceRecognizer()
#
#    try:
#        cam.start()
#
#        while True:
#            ret, frame = cam.read_frame()
#            if not ret:
#                print("❌ 摄像头读取失败")
#                break
#
#            # 检测人脸
#            faces, confidences = detector.detect(frame)
#
#            # 绘制人脸框
#            frame = draw_faces(frame, faces, confidences)
#
#            # 显示检测数量
#            cv2.putText(frame, f"Faces: {len(faces)}", (10, 30),
#                       cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
#
#            # 显示结果
#            cv2.imshow(WINDOW_NAME, frame)
#
#            # 按 'q' 退出
#            key = cv2.waitKey(1) & 0xFF
#            if key == ord(EXIT_KEY):
#                print("👋 退出程序")
#                break
#
#            # 调试:按 's' 保存第一张人脸
#            if DEBUG and key == ord('s') and len(faces) > 0:
#                from utils import save_face
#                save_face(frame, faces[0], "debug_face.jpg")
#
#    except KeyboardInterrupt:
#        print("\n👋 程序被用户中断")
#    except Exception as e:
#        print(f"❌ 错误: {e}")
#    finally:
#        cam.release()
#        cv2.destroyAllWindows()
#
#if __name__ == "__main__":
#    main()
    
    
    
    
# main.py
from camera import Camera
from face_detector import FaceDetector
from face_recognition import FaceRecognizer
from utils import draw_faces, save_face
from config import WINDOW_NAME, EXIT_KEY, DEBUG
import cv2
import threading
import time

# 全局变量
stop_input = False
latest_frame = None  # 用于命令线程获取最新帧
frame_lock = threading.Lock()  # 线程安全访问帧

# 识别结果输出防抖:记录上次输出时间,避免刷屏
last_recognition_time = 0
RECOGNITION_OUTPUT_INTERVAL = 2.0  # 每2秒最多输出一次


def command_input_thread(recognizer, detector):
    """命令行输入线程:处理人脸录入"""
    global stop_input, latest_frame, frame_lock

    while not stop_input:
        cmd = input("\n请输入命令 (rec <姓名>: 录入人脸, q: 退出): ").strip()
        
        if cmd.lower() == 'q':
            stop_input = True
            break
            
        elif cmd.startswith("rec "):
            name = cmd[4:].strip()
            if not name:
                print("❌ 姓名不能为空")
                continue
            print(f"📸 请对准摄像头,3秒内将自动拍摄...")

            # 等待 3 秒,让用户摆好姿势
            time.sleep(3)

            # 从共享变量获取最新一帧(线程安全)
            with frame_lock:
                frame_copy = latest_frame.copy() if latest_frame is not None else None

            if frame_copy is None:
                print("❌ 无法获取图像帧,请稍后再试")
                continue

            # 检测人脸
            faces, _ = detector.detect(frame_copy)
            if len(faces) == 0:
                print("❌ 未检测到人脸,请面对摄像头后重试")
                continue

            x1, y1, x2, y2 = faces[0]

            # 安全裁剪:防止坐标越界
            h, w = frame_copy.shape[:2]
            x1 = max(0, int(x1))
            y1 = max(0, int(y1))
            x2 = min(w, int(x2))
            y2 = min(h, int(y2))

            if x2 <= x1 or y2 <= y1:
                print("❌ 人脸框无效:坐标异常")
                continue

            face_img = frame_copy[y1:y2, x1:x2]

            # ✅ 关键检查:确保裁剪出的图像非空
            if face_img.size == 0 or face_img.shape[0] <= 10 or face_img.shape[1] <= 10:
                print("❌ 裁剪出的人脸图像无效或太小,无法录入")
                print(f"   人脸区域尺寸: {face_img.shape if face_img.size > 0 else 'empty'}")
                continue

            # ✅ 安全调用 add_face,防止异常中断线程
            try:
                recognizer.add_face(face_img, name)
                print(f"✅ 已录入人脸: {name}")
            except Exception as e:
                print(f"❌ 录入失败: {str(e)}")

        else:
            print("❌ 未知命令,支持: rec <姓名>, q")


def main():
    global stop_input, latest_frame, last_recognition_time

    print("🚀 人脸识别签到系统启动...")

    # 初始化模块
    cam = Camera()
    detector = FaceDetector()
    recognizer = FaceRecognizer()

    # 启动命令输入线程
    input_thread = threading.Thread(target=command_input_thread, args=(recognizer, detector))
    input_thread.daemon = True
    input_thread.start()

    try:
        cam.start()

        while True:
            if stop_input:
                print("👋 正在退出...")
                break

            ret, frame = cam.read_frame()
            if not ret:
                print("❌ 摄像头读取失败或帧为空")
                break

            # ✅ 更新共享帧(线程安全)
            with frame_lock:
                latest_frame = frame.copy()

            # 检测所有人脸
            faces, confidences = detector.detect(frame)

            # --- 终端输出识别信息(防抖)---
            current_time = time.time()
            if len(faces) > 0 and (current_time - last_recognition_time) > RECOGNITION_OUTPUT_INTERVAL:
                x1, y1, x2, y2 = faces[0]  # 只输出第一张人脸
                face_img = frame[y1:y2, x1:x2]
                name, confidence = recognizer.recognize(face_img)

                if name != "Unknown":
                    print(f"✅ 识别到: {name} (置信度: {confidence:.2f})")
                else:
                    print(f"❌ 未知人脸 (置信度: {confidence:.2f})")

                last_recognition_time = current_time  # 更新时间

            # --- 画面显示部分 ---
            # 识别每张人脸(用于画面显示)
            for i, (x1, y1, x2, y2) in enumerate(faces):
                face_img = frame[y1:y2, x1:x2]
                name, confidence = recognizer.recognize(face_img)
                label = f"{name} ({confidence:.2f})"
                cv2.putText(frame, label, (x1, y1 - 15),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)

            # 绘制人脸框
            frame = draw_faces(frame, faces, confidences)

            # 显示人数
            cv2.putText(frame, f"Faces: {len(faces)}", (10, 30),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)

            # 显示画面
            cv2.imshow(WINDOW_NAME, frame)

            # 按 'q' 退出
            key = cv2.waitKey(1) & 0xFF
            if key == ord(EXIT_KEY):
                print("👋 用户按键退出")
                break

            # 调试:保存人脸
            if DEBUG and key == ord('s') and len(faces) > 0:
                save_face(frame, faces[0], "debug_face.jpg")
                print("📸 调试人脸已保存: debug_face.jpg")

    except KeyboardInterrupt:
        print("\n👋 程序被用户中断 (Ctrl+C)")
    except Exception as e:
        print(f"❌ 程序异常: {e}")
    finally:
        stop_input = True
        cam.release()
        cv2.destroyAllWindows()


if __name__ == "__main__":
    main()

config.py

python 复制代码
# config.py
"""
项目配置文件
"""

# 摄像头设置
CAMERA_ID = 0
FRAME_WIDTH = 640
FRAME_HEIGHT = 480

# 模型路径
MODEL_DIR = "models"
FACE_DETECTOR_PB = "opencv_face_detector_uint8.pb"
FACE_DETECTOR_PBTXT = "opencv_face_detector.pbtxt"

# 检测参数
CONFIDENCE_THRESHOLD = 0.5

# 人脸保存路径
SAVE_DIR = "data/faces"

# 显示设置
WINDOW_NAME = "人脸识别签到系统"
SHOW_CONFIDENCE = True
EXIT_KEY = 'q'  # 按 q 退出
DEBUG = True  # 设为 True 才能使用 's' 键保存

camera.py

python 复制代码
# camera.py
import cv2
from config import CAMERA_ID, FRAME_WIDTH, FRAME_HEIGHT

class Camera:
    def __init__(self):
        self.cap = None

    def start(self):
        """启动摄像头"""
        self.cap = cv2.VideoCapture(CAMERA_ID)
        if not self.cap.isOpened():
            raise Exception(f"无法打开摄像头 {CAMERA_ID}")
        
        # 设置分辨率
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, FRAME_WIDTH)
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT)
        
        # 再次获取实际分辨率(有些摄像头不支持精确设置)
        actual_width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
        actual_height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
        
        print(f"✅ 摄像头 {CAMERA_ID} 已启动,分辨率: {int(actual_width)}x{int(actual_height)}")

    def read_frame(self):
        """读取一帧图像"""
        ret, frame = self.cap.read()
        if not ret:
            print("⚠️ 摄像头读取失败,可能被占用或驱动问题")
        return ret, frame

    def release(self):
        """释放摄像头"""
        if self.cap:
            self.cap.release()
            print("📷 摄像头已释放")

utils.py

python 复制代码
# utils.py
import cv2
from config import SHOW_CONFIDENCE

def draw_faces(frame, faces, confidences=None):
    """在图像上绘制人脸框"""
    for i, (x1, y1, x2, y2) in enumerate(faces):
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
        if SHOW_CONFIDENCE and confidences:
            label = f"{confidences[i]:.2f}"
            cv2.putText(frame, label, (x1, y1-10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
    return frame

def save_face(frame, face_box, save_path):
    """保存人脸图像"""
    x1, y1, x2, y2 = face_box
    face_img = frame[y1:y2, x1:x2]
    cv2.imwrite(save_path, face_img)
    print(f"📸 人脸已保存: {save_path}")

face_detector.py

python 复制代码
# face_detector.py
import cv2
import os
from config import MODEL_DIR, CONFIDENCE_THRESHOLD

class FaceDetector:
    def __init__(self):
        pbtxt = os.path.join(MODEL_DIR, "opencv_face_detector.pbtxt")
        pb = os.path.join(MODEL_DIR, "opencv_face_detector_uint8.pb")
        
        if not os.path.exists(pbtxt) or not os.path.exists(pb):
            raise FileNotFoundError(
                f"❌ 模型文件未找到!\n"
                f"请确保 '{MODEL_DIR}' 目录下有以下文件:\n"
                f"  - opencv_face_detector.pbtxt\n"
                f"  - opencv_face_detector_uint8.pb"
            )

        self.net = cv2.dnn.readNetFromTensorflow(pb, pbtxt)
        self.confidence_threshold = CONFIDENCE_THRESHOLD
        print("✅ DNN 人脸检测器已加载")

    def detect(self, frame):
        """
        检测人脸
        返回: (faces: [(x1,y1,x2,y2)], confidences: [float])
        """
        h, w = frame.shape[:2]
        blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], False, False)
        self.net.setInput(blob)
        detections = self.net.forward()

        faces = []
        confidences = []

        for i in range(detections.shape[2]):
            confidence = detections[0, 0, i, 2]
            if confidence > self.confidence_threshold:
                x1 = int(detections[0, 0, i, 3] * w)
                y1 = int(detections[0, 0, i, 4] * h)
                x2 = int(detections[0, 0, i, 5] * w)
                y2 = int(detections[0, 0, i, 6] * h)
                faces.append((x1, y1, x2, y2))
                confidences.append(confidence)

        return faces, confidences

face_recognition.py

python 复制代码
# face_recognition.py
import os
import cv2
import numpy as np
import time

KNOWN_FACES_DIR = "known_faces"
os.makedirs(KNOWN_FACES_DIR, exist_ok=True)

class FaceRecognizer:
    def __init__(self, db_path="known_faces"):
        self.db_path = db_path
        self.known_encodings = []
        self.known_names = []
        self.known_images = []
        print("✅ 人脸比对模块已加载")

    def add_face(self, face_img, name):
        """录入新的人脸"""
        if face_img is None or face_img.size == 0 or len(face_img.shape) != 3:
            raise ValueError("❌ 无效的人脸图像:为空或格式错误")
        if face_img.shape[0] < 20 or face_img.shape[1] < 20:
            raise ValueError("❌ 人脸图像太小,无法处理")

        if not os.path.exists(self.db_path):
            os.makedirs(self.db_path)

        timestamp = int(time.time())
        filename = f"{name}_{timestamp}.jpg"
        path = os.path.join(self.db_path, filename)

        success = cv2.imwrite(path, face_img)
        if not success:
            raise IOError(f"❌ 无法保存图像: {path}")

        # 提取特征
        resized = cv2.resize(face_img, (32, 32))
        gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
        flattened = gray.flatten().astype(np.float32)
        normalized = flattened / 255.0

        self.known_encodings.append(normalized)
        self.known_names.append(name)
        self.known_images.append(face_img.copy())

        print(f"✅ 已录入人脸: {name} (保存为 {path}),特征维度: {normalized.shape}")

    def recognize(self, face_image):
        """识别人脸"""
        if len(self.known_encodings) == 0:
            return "未知", 0.0

        if face_image is None or face_image.size == 0:
            print("⚠️ 识别失败:输入图像为空")
            return "未知", 0.0

        h, w = face_image.shape[:2]
        if h < 10 or w < 10:
            print(f"⚠️ 图像太小: {w}x{h}")
            return "未知", 0.0

        try:
            resized = cv2.resize(face_image, (32, 32))
            gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
            test_vec = gray.flatten().astype(np.float32) / 255.0
        except Exception as e:
            print(f"❌ 图像预处理失败: {e}")
            return "未知", 0.0

        best_name = "未知"
        best_similarity = 0.0

        for name, enc in zip(self.known_names, self.known_encodings):
            dot = np.dot(test_vec, enc)
            norm_test = np.linalg.norm(test_vec)
            norm_enc = np.linalg.norm(enc)
            if norm_test == 0 or norm_enc == 0:
                continue
            similarity = dot / (norm_test * norm_enc)
            confidence = (similarity + 1) / 2  # 转到 [0,1]

            if confidence > best_similarity:
                best_similarity = confidence
                best_name = name

        THRESHOLD = 0.85
        if best_similarity < THRESHOLD:
            return "未知", 0.0

        return best_name, best_similarity

总结

以上介绍了python实现简单的人脸识别功能逻辑。

相关推荐
人间打气筒(Ada)1 分钟前
如何基于 Go-kit 开发 Web 应用:从接口层到业务层再到数据层
开发语言·后端·golang
2501_924952694 分钟前
代码生成器优化策略
开发语言·c++·算法
wan9yu12 分钟前
为什么你需要给 LLM 的数据"加密"而不是"脱敏"?我写了一个开源工具
python
清风徐来QCQ15 分钟前
八股文(1)
java·开发语言
lsx20240618 分钟前
网站主机技术
开发语言
摇滚侠21 分钟前
你是一名 java 程序员,总结定义数组的方式
java·开发语言·python
xyq202430 分钟前
Vue3 条件语句详解
开发语言
这个名有人用不39 分钟前
解决 uv 虚拟环境使用 pip 命令提示command not found的办法
python·pip·uv·claude code
浩浩kids1 小时前
R•Homework
开发语言·r语言