使用 Python 进行人脸识别的项目开发流程

人脸识别(Face Recognition)是计算机视觉领域中一项重要的技术,广泛应用于安防系统、身份验证、智能门禁、人脸打卡等场景。Python 以其丰富的第三方库和简洁语法,成为实现人脸识别的首选语言之一。

本文将带你从零开始,使用 Python 完成一个完整的人脸识别项目,涵盖以下内容:

  1. 环境准备与依赖安装
  2. 人脸检测(Face Detection)原理与实现
  3. 人脸特征提取(Face Embedding)
  4. 人脸识别(Face Recognition)逻辑设计
  5. 实战:构建一个“人脸识别打卡系统”
  6. 代码优化与注意事项
  7. 扩展建议与学习资源

一、项目目标

我们希望构建一个基于摄像头的人脸识别系统,具备以下功能:

  • 能够实时检测摄像头画面中的人脸;
  • 能够识别已知人员(例如:员工、学生)的身份;
  • 对未知人脸提示“未识别”;
  • 支持添加新的人脸到数据库中;
  • 系统运行稳定、响应快速。

最终效果:当你站在摄像头前,系统能自动识别出你是“张三”或“李四”,并打印欢迎信息。


二、技术选型与环境准备

1. 核心库介绍

我们将使用以下几个关键的 Python 库:

库名 作用
OpenCV (cv2) 图像处理、视频流读取、人脸检测
face_recognition 高级人脸识别库,基于 dlib,支持人脸定位与特征编码
numpy 数值计算,处理图像数组
os / pickle 文件操作与人脸数据持久化存储

2. 安装依赖

在终端中运行以下命令安装所需库:

pip install opencv-python face_recognition numpy

⚠️ 注意:face_recognition 库在 Windows 和 macOS 上安装较为顺利;Linux 用户可能需要先安装 dlib 的依赖(如 CMake、Boost 等)。推荐使用 Anaconda 或虚拟环境管理依赖。


三、项目结构设计

建议创建如下项目目录结构:

face_recognition_project/
│
├── known_faces/              # 存放已知人员的照片
│   ├── zhangsan.jpg
│   ├── lisi.jpg
│   └── wangwu.jpg
│
├── unknown_faces/            # 临时保存未知人脸截图(可选)
│
├── face_encodings.pkl        # 保存人脸特征编码的文件
│
├── face_names.pkl            # 保存对应人名的文件
│
└── face_recognition_app.py   # 主程序脚本

四、步骤详解与代码实现

第一步:加载并编码已知人脸

我们需要先将“known_faces”文件夹中的每张人脸图像进行处理,提取其面部特征编码(Face Encoding),并保存起来供后续比对使用。

# -*- coding: utf-8 -*-
"""
step1_load_known_faces.py
功能:遍历 known_faces 文件夹,提取所有人脸特征编码并保存到文件
"""

import os
import face_recognition
import pickle
import cv2

# 定义路径
KNOWN_FACES_DIR = "known_faces"
ENCODINGS_FILE = "face_encodings.pkl"
NAMES_FILE = "face_names.pkl"

def load_and_encode_known_faces():
    """
    加载已知人脸图像,提取特征编码,并保存到本地文件
    """
    print("正在加载并编码已知人脸...")

    # 用于存储所有人脸编码和对应姓名
    known_face_encodings = []
    known_face_names = []

    # 遍历 known_faces 目录下的所有图像文件
    for filename in os.listdir(KNOWN_FACES_DIR):
        if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
            # 获取完整路径
            filepath = os.path.join(KNOWN_FACES_DIR, filename)
            # 提取人名(去掉扩展名)
            name = os.path.splitext(filename)[0]

            # 读取图像
            image = face_recognition.load_image_file(filepath)

            # 检测人脸位置(一个人脸图像中可能有多张脸,我们只取第一张)
            face_locations = face_recognition.face_locations(image)
            if len(face_locations) == 0:
                print(f"⚠️  在 {filename} 中未检测到人脸")
                continue

            # 提取人脸特征编码(128维向量)
            face_encoding = face_recognition.face_encodings(image, face_locations)[0]

            # 添加到列表
            known_face_encodings.append(face_encoding)
            known_face_names.append(name)

            print(f"✅ 已处理:{filename} -> {name}")

    # 将编码和名字保存到本地文件,避免重复计算
    with open(ENCODINGS_FILE, 'wb') as f:
        pickle.dump(known_face_encodings, f)
    with open(NAMES_FILE, 'wb') as f:
        pickle.dump(known_face_names, f)

    print(f"\n🎉 共加载 {len(known_face_names)} 个已知人脸,数据已保存。")
    return known_face_encodings, known_face_names

# 测试运行
if __name__ == "__main__":
    encodings, names = load_and_encode_known_faces()
    print("已知人脸名单:", names)

📌 代码说明:

  • 使用 face_recognition.load_image_file() 读取图像;
  • face_recognition.face_locations() 检测人脸在图像中的位置(返回上下左右坐标);
  • face_recognition.face_encodings() 提取人脸的 128 维特征向量(称为 embedding),这是人脸识别的核心;
  • 使用 pickle 将编码序列化保存,提高下次启动效率。

💡 小贴士: 如果你添加了新人脸照片,只需重新运行此脚本即可更新数据库。


第二步:实时人脸识别主程序

接下来我们编写主程序,调用摄像头进行实时人脸识别。

# -*- coding: utf-8 -*-
"""
face_recognition_app.py
主程序:调用摄像头进行实时人脸识别
"""

import cv2
import face_recognition
import pickle
import os
import numpy as np

# 路径配置
ENCODINGS_FILE = "face_encodings.pkl"
NAMES_FILE = "face_names.pkl"
UNKNOWN_FACES_DIR = "unknown_faces"
TOLERANCE = 0.6  # 匹配阈值,越小越严格(推荐 0.4~0.6)

# 创建未知人脸保存目录
os.makedirs(UNKNOWN_FACES_DIR, exist_ok=True)

def load_known_faces():
    """
    从文件加载已知人脸编码和姓名
    """
    if not os.path.exists(ENCODINGS_FILE) or not os.path.exists(NAMES_FILE):
        print("❌ 找不到人脸编码文件,请先运行 load_known_faces.py")
        return [], []

    with open(ENCODINGS_FILE, 'rb') as f:
        known_encodings = pickle.load(f)
    with open(NAMES_FILE, 'rb') as f:
        known_names = pickle.load(f)

    print(f"✅ 成功加载 {len(known_names)} 个已知人脸。")
    return known_encodings, known_names

def main():
    """
    主函数:启动摄像头进行人脸识别
    """
    # 加载已知人脸数据
    known_face_encodings, known_face_names = load_known_faces()
    if len(known_face_encodings) == 0:
        print("⛔ 没有可用的已知人脸数据,程序退出。")
        return

    # 打开摄像头(默认摄像头为 0)
    video_capture = cv2.VideoCapture(0)
    if not video_capture.isOpened():
        print("❌ 无法打开摄像头,请检查设备连接。")
        return

    print("📸 摄像头已打开,正在识别人脸... 按 'q' 键退出。")

    frame_count = 0  # 帧计数器,用于降低处理频率
    save_unknown_counter = 0  # 控制未知人脸截图频率

    while True:
        # 读取一帧图像
        ret, frame = video_capture.read()
        if not ret:
            print("❌ 无法获取摄像头画面")
            break

        frame_count += 1

        # 为了提高性能,每 2 帧处理一次(跳帧)
        if frame_count % 2 != 0:
            # 仍显示图像
            cv2.imshow('Face Recognition', frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
            continue

        # 缩小图像以加快处理速度(可选)
        small_frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
        rgb_small_frame = cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB)

        # 检测当前帧中的人脸位置
        face_locations = face_recognition.face_locations(rgb_small_frame)
        face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)

        # 遍历每张检测到的人脸
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            # 将坐标还原到原始图像尺寸
            top *= 2
            right *= 2
            bottom *= 2
            left *= 2

            # 使用欧氏距离进行人脸比对
            matches = face_recognition.compare_faces(
                known_face_encodings, face_encoding, tolerance=TOLERANCE
            )
            face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
            best_match_index = np.argmin(face_distances) if len(face_distances) > 0 else -1

            name = "Unknown"
            if best_match_index >= 0 and matches[best_match_index]:
                name = known_face_names[best_match_index]
                print(f"✅ 识别成功:{name}")
            else:
                # 保存未知人脸截图(避免频繁保存)
                save_unknown_counter += 1
                if save_unknown_counter % 30 == 0:  # 每30次保存一次
                    unknown_img_path = os.path.join(UNKNOWN_FACES_DIR, f"unknown_{save_unknown_counter}.jpg")
                    cv2.imwrite(unknown_img_path, frame[top:bottom, left:right])
                    print(f"📸 已保存未知人脸截图:{unknown_img_path}")

            # 在图像上绘制矩形框和标签
            color = (0, 255, 0) if name != "Unknown" else (0, 0, 255)
            cv2.rectangle(frame, (left, top), (right, bottom), color, 2)
            cv2.rectangle(frame, (left, bottom - 20), (right, bottom), color, cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(frame, name, (left + 6, bottom - 6), font, 0.6, (255, 255, 255), 1)

        # 显示结果
        cv2.imshow('Face Recognition', frame)

        # 按 'q' 退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    # 释放资源
    video_capture.release()
    cv2.destroyAllWindows()
    print("👋 程序已退出。")

# 启动主程序
if __name__ == "__main__":
    main()

📌 代码详细说明:

1. 图像缩放优化
small_frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)

将图像缩小为原来的一半,显著提升处理速度,因为人脸检测和编码计算是计算密集型任务。

2. 跳帧处理
if frame_count % 2 != 0: continue

不是每一帧都进行识别,而是每隔一帧处理一次,防止 CPU 过载。

3. 人脸比对机制
matches = face_recognition.compare_faces(...)
face_distances = face_recognition.face_distance(...)
best_match_index = np.argmin(face_distances)
  • compare_faces 判断是否匹配(返回布尔值列表);
  • face_distance 返回与每个已知人脸的欧氏距离,越小越相似;
  • 取最小距离的索引作为最佳匹配。
4. 绘制识别结果

使用 OpenCV 在图像上绘制绿色框(识别成功)或红色框(未知),并显示姓名。

5. 未知人脸截图

系统会自动将识别为“Unknown”的人脸截图保存到 unknown_faces/ 文件夹,便于后期人工审核并加入数据库。


五、完整项目运行流程

步骤 1:准备已知人脸图像

known_faces/ 文件夹中放入你的照片,命名如:

  • zhangsan.jpg
  • lisi.jpg
  • wangwu.jpg

✅ 建议:使用正面、清晰、光照均匀的照片,避免戴帽子或墨镜。

步骤 2:生成人脸编码

运行以下命令生成编码文件:

python step1_load_known_faces.py

输出示例:

正在加载并编码已知人脸...
✅ 已处理:zhangsan.jpg -> zhangsan
✅ 已处理:lisi.jpg -> lisi
🎉 共加载 2 个已知人脸,数据已保存。

此时会生成 face_encodings.pklface_names.pkl 两个文件。

步骤 3:启动人脸识别

运行主程序:

python face_recognition_app.py

摄像头启动后,将脸对准摄像头,系统会实时识别并标注姓名。

按键盘 q 键退出程序。


六、常见问题与调试技巧

❌ 问题1:无法打开摄像头

可能原因:

  • 摄像头被其他程序占用;
  • 没有权限访问摄像头(尤其在 Mac 或 Linux 上);
  • 驱动未安装。

解决方法:

  • 关闭其他使用摄像头的程序(如 Zoom、微信);
  • 在终端运行 ls /dev/video*(Linux)查看设备;
  • 尝试更换摄像头索引:video_capture = cv2.VideoCapture(1)

❌ 问题2:识别率低或误识别

原因分析:

  • 光照不足或过曝;
  • 图像模糊或角度偏斜;
  • TOLERANCE 设置过高(默认 0.6,可调低至 0.4 提高精度);
  • 训练图像质量差。

优化建议:

  • 使用多张不同角度的照片进行训练;
  • load_and_encode_known_faces.py 中为同一个人添加多张照片,并分别编码加入列表;
  • 调整 TOLERANCE 参数测试效果。

❌ 问题3:程序运行卡顿

解决方案:

  • 继续降低图像分辨率(如 fx=0.25);
  • 增加跳帧频率(如每 3 帧处理一次);
  • 使用 GPU 加速(需安装支持 CUDA 的 dlib 版本);
  • 改用更轻量模型(如 MTCNN + ArcFace)。

七、代码优化与最佳实践

✅ 1. 使用类封装提升可维护性

我们可以将整个系统封装成一个类,便于扩展功能:

class FaceRecognitionSystem:
    def __init__(self, known_dir="known_faces", tolerance=0.6):
        self.known_dir = known_dir
        self.tolerance = tolerance
        self.known_encodings = []
        self.known_names = []
        self.load_known_faces()

    def load_known_faces(self):
        # 同上...
        pass

    def recognize_from_camera(self):
        # 同上...
        pass

✅ 2. 支持动态添加新用户

可以增加一个函数,允许用户按下某个键(如 a)时,将当前人脸保存为新用户:

if cv2.waitKey(1) & 0xFF == ord('a'):
    # 捕获当前人脸并保存为新用户
    name = input("请输入姓名:")
    # 保存图像并重新编码

✅ 3. 数据库存储替代 pickle

对于生产环境,建议使用 SQLite 或 MongoDB 存储人脸编码和元数据,支持增删改查。


八、扩展功能建议

🔹 功能1:人脸打卡记录

将识别结果写入 CSV 文件,记录时间、姓名、是否成功:

import csv
from datetime import datetime

with open('attendance.csv', 'a') as f:
    writer = csv.writer(f)
    writer.writerow([name, datetime.now(), "Success"])

🔹 功能2:Web 版人脸识别(Flask + HTML)

使用 Flask 构建 Web 接口,前端上传图像或调用摄像头,后端返回识别结果。

🔹 功能3:活体检测(Anti-Spoofing)

防止照片欺骗,可通过眨眼检测、头部晃动等方式判断是否为真人。


九、学习资源推荐

资源类型 推荐内容
📘 官方文档 face_recognition 官网
📘 OpenCV 教程 OpenCV-Python 官方教程
🎥 视频课程 B站搜索“Python 人脸识别实战”
🧪 Kaggle 项目 搜索 “Face Recognition” 数据集与 Notebook
📚 书籍 《Python 计算机视觉编程》、《OpenCV 4 快速入门》

更多推荐