程序代码篇---face_recognition库实现的人脸检测系统

以下是一个基于face_recognition库的人脸管理系统,支持从文件夹加载人脸数据、实时识别并显示姓名,以及动态添加新人脸。系统采用模块化设计,代码结构清晰,易于扩展。

一、系统架构

face_recognition_system/
├── faces/                  # 人脸数据库(按姓名命名子文件夹)
│   ├── person1/
│   │   ├── photo1.jpg
│   │   ├── photo2.jpg
│   ├── person2/
│   │   ├── photo1.jpg
├── main.py                 # 主程序
├── face_manager.py         # 人脸管理模块
└── utils.py                # 工具函数

二、核心代码实现

1. 人脸管理模块(face_manager.py)
import os
import pickle
import face_recognition
import cv2
from typing import List, Dict, Tuple

class FaceManager:
    def __init__(self, data_dir="faces", encodings_file="face_encodings.pkl"):
        """初始化人脸管理器"""
        self.data_dir = data_dir  # 人脸数据库目录
        self.encodings_file = encodings_file  # 编码数据缓存文件
        self.known_face_encodings = []  # 已知人脸编码
        self.known_face_names = []  # 对应的姓名
        self.load_known_faces()  # 加载已知人脸数据

    def load_known_faces(self):
        """从文件或目录加载已知人脸数据"""
        # 优先从缓存文件加载
        if os.path.exists(self.encodings_file):
            try:
                with open(self.encodings_file, "rb") as f:
                    data = pickle.load(f)
                    self.known_face_encodings = data["encodings"]
                    self.known_face_names = data["names"]
                    print(f"从缓存加载了 {len(self.known_face_names)} 个人脸")
                    return
            except Exception as e:
                print(f"加载缓存失败: {e},将从文件重新加载")

        # 从目录加载并生成缓存
        self._load_faces_from_directory()
        self._save_face_encodings()

    def _load_faces_from_directory(self):
        """从目录加载人脸数据"""
        print(f"正在从 {self.data_dir} 加载人脸数据...")
        if not os.path.exists(self.data_dir):
            os.makedirs(self.data_dir)
            return

        for person_name in os.listdir(self.data_dir):
            person_dir = os.path.join(self.data_dir, person_name)
            if not os.path.isdir(person_dir):
                continue

            # 遍历每个人的照片
            for filename in os.listdir(person_dir):
                image_path = os.path.join(person_dir, filename)
                if not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
                    continue

                try:
                    # 加载图像并提取人脸编码
                    image = face_recognition.load_image_file(image_path)
                    face_encodings = face_recognition.face_encodings(image)

                    if len(face_encodings) == 0:
                        print(f"警告: 在 {image_path} 中未检测到人脸")
                        continue

                    # 通常一张照片只有一个人脸
                    face_encoding = face_encodings[0]
                    self.known_face_encodings.append(face_encoding)
                    self.known_face_names.append(person_name)
                    print(f"已加载 {person_name} 的人脸: {filename}")
                except Exception as e:
                    print(f"处理 {image_path} 时出错: {e}")

        print(f"共加载了 {len(self.known_face_names)} 个人脸")

    def _save_face_encodings(self):
        """保存人脸编码到缓存文件"""
        data = {
            "encodings": self.known_face_encodings,
            "names": self.known_face_names
        }
        with open(self.encodings_file, "wb") as f:
            pickle.dump(data, f)
        print(f"已保存人脸编码到 {self.encodings_file}")

    def recognize_faces(self, frame) -> List[Tuple[str, Tuple[int, int, int, int]]]:
        """
        在给定帧中识别人脸
        
        返回: 包含(姓名, 人脸位置)的列表
        """
        # 转换为RGB(face_recognition使用RGB格式)
        rgb_frame = frame[:, :, ::-1]
        
        # 检测人脸位置和编码
        face_locations = face_recognition.face_locations(rgb_frame)
        face_encodings = face_recognition.face_encodings(rgb_frame, face_locations)
        
        results = []
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            # 与已知人脸比对
            matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding)
            name = "Unknown"
            
            # 如果有匹配,找出最佳匹配
            if True in matches:
                face_distances = face_recognition.face_distance(self.known_face_encodings, face_encoding)
                best_match_index = int(face_distances.argmin())
                if matches[best_match_index]:
                    name = self.known_face_names[best_match_index]
            
            results.append((name, (top, right, bottom, left)))
        
        return results

    def add_new_face(self, person_name: str, image_path: str) -> bool:
        """
        添加新人脸到数据库
        
        参数:
            person_name: 人名
            image_path: 人脸照片路径
        
        返回:
            是否成功添加
        """
        # 检查照片中是否有人脸
        image = face_recognition.load_image_file(image_path)
        face_encodings = face_recognition.face_encodings(image)
        
        if len(face_encodings) == 0:
            print(f"错误: 在 {image_path} 中未检测到人脸")
            return False
        
        if len(face_encodings) > 1:
            print(f"警告: 在 {image_path} 中检测到多个人脸,仅使用第一个")
        
        # 创建用户目录
        person_dir = os.path.join(self.data_dir, person_name)
        os.makedirs(person_dir, exist_ok=True)
        
        # 复制照片到用户目录
        import shutil
        filename = os.path.basename(image_path)
        new_image_path = os.path.join(person_dir, filename)
        shutil.copyfile(image_path, new_image_path)
        
        # 更新人脸数据
        self.known_face_encodings.append(face_encodings[0])
        self.known_face_names.append(person_name)
        self._save_face_encodings()
        
        print(f"成功添加 {person_name} 的人脸")
        return True
2. 工具函数(utils.py)
import cv2

def draw_faces_on_frame(frame, faces):
    """在帧上绘制人脸边界框和姓名"""
    for name, (top, right, bottom, left) in faces:
        # 绘制边界框
        cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
        
        # 绘制姓名标签
        cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 255, 0), cv2.FILLED)
        font = cv2.FONT_HERSHEY_DUPLEX
        cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
    
    return frame

def capture_face_from_camera(output_path="temp_face.jpg"):
    """从摄像头捕获人脸照片"""
    video_capture = cv2.VideoCapture(0)
    
    print("请将人脸对准摄像头,按 's' 保存照片,按 'q' 退出")
    
    while True:
        ret, frame = video_capture.read()
        if not ret:
            print("无法获取视频帧")
            break
        
        cv2.imshow("Capture Face", frame)
        
        key = cv2.waitKey(1) & 0xFF
        if key == ord('s'):  # 按 's' 保存照片
            cv2.imwrite(output_path, frame)
            print(f"照片已保存到 {output_path}")
            break
        elif key == ord('q'):  # 按 'q' 退出
            break
    
    video_capture.release()
    cv2.destroyAllWindows()
    
    return output_path if os.path.exists(output_path) else None
3. 主程序(main.py)
import cv2
from face_manager import FaceManager
from utils import draw_faces_on_frame, capture_face_from_camera

def main():
    # 初始化人脸管理器
    face_manager = FaceManager()
    
    # 主循环
    video_capture = cv2.VideoCapture(0)
    
    print("\n=== 人脸识别系统 ===")
    print("按 'a' 添加新人脸")
    print("按 'q' 退出程序")
    
    while True:
        ret, frame = video_capture.read()
        if not ret:
            print("无法获取视频帧")
            break
        
        # 识别人脸
        faces = face_manager.recognize_faces(frame)
        
        # 在帧上绘制人脸
        frame_with_faces = draw_faces_on_frame(frame, faces)
        
        # 显示结果
        cv2.imshow('人脸识别系统', frame_with_faces)
        
        # 按键处理
        key = cv2.waitKey(1) & 0xFF
        if key == ord('q'):  # 按 'q' 退出
            break
        elif key == ord('a'):  # 按 'a' 添加新人脸
            add_new_face_interactive(face_manager)
    
    # 清理资源
    video_capture.release()
    cv2.destroyAllWindows()

def add_new_face_interactive(face_manager):
    """交互式添加新人脸"""
    print("\n=== 添加新人脸 ===")
    person_name = input("请输入姓名: ").strip()
    
    if not person_name:
        print("姓名不能为空")
        return
    
    print("准备从摄像头捕获人脸照片...")
    temp_image_path = "temp_face.jpg"
    image_path = capture_face_from_camera(temp_image_path)
    
    if image_path:
        success = face_manager.add_new_face(person_name, image_path)
        if success:
            print(f"已成功添加 {person_name} 的人脸")
        else:
            print(f"添加 {person_name} 的人脸失败")
        
        # 删除临时文件
        if os.path.exists(temp_image_path):
            os.remove(temp_image_path)

if __name__ == "__main__":
    main()

三、使用方法

1. 环境准备
pip install face_recognition opencv-python pickle
2. 目录结构

手动创建faces目录,按以下结构存放人脸照片:

faces/
├── person1/
│   ├── photo1.jpg
│   ├── photo2.jpg
├── person2/
│   ├── photo1.jpg
3. 运行程序
python main.py
4. 功能说明
  • 实时识别:程序启动后自动打开摄像头,实时识别人脸并显示姓名。
  • 添加新人脸:按键盘a键,输入姓名后从摄像头捕获人脸照片添加到数据库。
  • 退出程序:按键盘q键退出。

四、系统特性

  1. 自动缓存机制:首次加载人脸后会生成缓存文件,后续启动直接读取缓存,提升启动速度。
  2. 动态扩展:支持随时添加新人脸,无需重启程序即可生效。
  3. 用户友好:交互式界面,提供清晰的操作指引。
  4. 模块化设计:代码结构清晰,易于维护和扩展。

五、扩展建议

  1. 添加人脸删除功能:支持从数据库中删除指定人员的人脸数据。
  2. 优化识别速度:降低视频流处理帧率,或仅对关键帧进行人脸识别。
  3. 增加人脸识别阈值调整:根据实际环境调整相似度阈值,提高准确率。
  4. 添加日志记录:记录识别历史和操作日志,便于审计。
  5. 开发Web界面:通过Flask或FastAPI开发Web界面,实现远程管理和查看。

这个系统适合小型企业考勤、家庭安防监控等场景,通过简单的扩展可以满足更复杂的需求。

以下是简化版的人脸检测与识别程序,只需手动将人脸照片放入指定文件夹(按人名命名),程序启动时自动加载所有人脸数据并进行实时识别。代码结构简单,易于使用:

一、核心代码(main.py)

import os
import cv2
import face_recognition

# 配置参数
FACES_DIR = "faces"  # 人脸照片存放目录
UNKNOWN_LABEL = "Unknown"  # 未知人脸标签
TOLERANCE = 0.6  # 人脸识别阈值,越小越严格

def load_known_faces():
    """从指定目录加载已知人脸数据"""
    known_face_encodings = []
    known_face_names = []
    
    print(f"正在从 {FACES_DIR} 加载人脸数据...")
    if not os.path.exists(FACES_DIR):
        os.makedirs(FACES_DIR)
        print(f"已创建人脸目录: {FACES_DIR}")
        return known_face_encodings, known_face_names
    
    # 遍历目录中的所有人名文件夹
    for person_name in os.listdir(FACES_DIR):
        person_dir = os.path.join(FACES_DIR, person_name)
        if not os.path.isdir(person_dir):
            continue
            
        # 遍历每个人的照片
        for filename in os.listdir(person_dir):
            image_path = os.path.join(person_dir, filename)
            if not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
                continue
                
            try:
                # 加载图像并提取人脸编码
                image = face_recognition.load_image_file(image_path)
                face_encodings = face_recognition.face_encodings(image)
                
                if len(face_encodings) == 0:
                    print(f"警告: 在 {image_path} 中未检测到人脸")
                    continue
                    
                # 通常一张照片只有一个人脸
                face_encoding = face_encodings[0]
                known_face_encodings.append(face_encoding)
                known_face_names.append(person_name)
                print(f"已加载 {person_name} 的人脸: {filename}")
            except Exception as e:
                print(f"处理 {image_path} 时出错: {e}")
                
    print(f"共加载了 {len(known_face_names)} 个人脸")
    return known_face_encodings, known_face_names

def main():
    # 加载已知人脸数据
    known_face_encodings, known_face_names = load_known_faces()
    
    # 打开摄像头
    video_capture = cv2.VideoCapture(0)
    
    print("\n=== 人脸识别系统 ===")
    print(f"已加载 {len(known_face_names)} 个已知人脸")
    print("按 'q' 退出程序")
    
    while True:
        # 读取一帧视频
        ret, frame = video_capture.read()
        if not ret:
            print("无法获取视频帧")
            break
            
        # 转换为RGB格式(face_recognition使用RGB,OpenCV使用BGR)
        rgb_frame = frame[:, :, ::-1]
        
        # 检测当前帧中的所有人脸位置和编码
        face_locations = face_recognition.face_locations(rgb_frame)
        face_encodings = face_recognition.face_encodings(rgb_frame, face_locations)
        
        # 遍历检测到的人脸
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            # 与已知人脸比对
            matches = face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance=TOLERANCE)
            name = UNKNOWN_LABEL
            
            # 如果有匹配,找出最佳匹配
            if True in matches:
                face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
                best_match_index = int(face_distances.argmin())
                if matches[best_match_index]:
                    name = known_face_names[best_match_index]
            
            # 在人脸周围绘制边界框和标签
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
            cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 255, 0), cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
        
        # 显示结果
        cv2.imshow('人脸识别系统', frame)
        
        # 按 'q' 键退出循环
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    # 释放资源
    video_capture.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

二、使用方法

1. 环境准备
pip install face_recognition opencv-python
2. 目录结构

手动创建faces目录,按以下结构存放人脸照片:

faces/
├── person1/
│   ├── photo1.jpg  # 确保照片中只有person1的清晰人脸
├── person2/
│   ├── photo1.jpg
│   ├── photo2.jpg
3. 运行程序
python main.py
4. 功能说明
  • 程序启动时自动加载faces目录下的所有人脸数据。
  • 打开摄像头进行实时人脸检测和识别,显示匹配的姓名或 "Unknown"。
  • q键退出程序。

三、注意事项

  1. 照片要求

    • 每个人的照片应放在以其姓名命名的单独文件夹中
    • 照片中应只包含清晰可见的单个人脸
    • 照片格式支持 JPG、PNG 等常见格式。
  2. 性能优化

    • 如需提高识别速度,可降低视频分辨率video_capture.set(3, 640); video_capture.set(4, 480)
    • 减少faces目录中的照片数量或人物数量
  3. 识别准确率

    • 调整TOLERANCE(默认 0.6)可平衡准确率和召回率。
    • 为每个人提供多角度、不同光照条件下的多张照片可提高识别准确率。

四、扩展建议

  1. 批量添加人脸:编写脚本自动从大型照片库中提取人脸并分类。
  2. 保存识别记录:将识别结果保存到日志文件或数据库。
  3. 添加报警功能:当检测到未知人脸时触发警报
  4. 支持视频文件输入:除摄像头外,支持对视频文件进行人脸识别

这个简化版程序适合快速搭建人脸门禁、考勤系统等原型,通过简单修改即可满足基本需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
OSZAR »