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实现简单的人脸识别功能逻辑。

相关推荐
Liue612312311 小时前
胚胎显微图像检测与识别改进Grid-RCNN模型实现
python
gc_22991 小时前
学习python调用olefile库解析ole文件的基本用法
python·ole·olefile
-小麦子-2 小时前
Python 里的 range 是干嘛的?
开发语言·python
lly2024062 小时前
CSS 图像拼合技术
开发语言
承渊政道2 小时前
C++学习之旅【C++继承概念指南与核心内容介绍】
c语言·开发语言·c++·笔记·学习·visual studio
devlogix012 小时前
1 Numpy基础 & 安装
python
MemOS2 小时前
MemOS OpenClaw 插件测评结果来啦!Tokens 消耗降低 72%+
python·github
天空属于哈夫克32 小时前
Go 开发:企微外部群主动发送消息
开发语言·golang·企业微信
喵手2 小时前
Python爬虫实战:研究生招生简章智能采集系统 - 破解考研信息不对称的技术方案(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集研究生招生简章·考研信息不对称·采集考研信息数据csv导出