实战:使用 Python 进行人脸识别的项目开发
库名作用OpenCVcv2图像处理、视频流读取、人脸检测高级人脸识别库,基于 dlib,支持人脸定位与特征编码numpy数值计算,处理图像数组ospickle文件操作与人脸数据持久化存储。
使用 Python 进行人脸识别的项目开发流程
人脸识别(Face Recognition)是计算机视觉领域中一项重要的技术,广泛应用于安防系统、身份验证、智能门禁、人脸打卡等场景。Python 以其丰富的第三方库和简洁语法,成为实现人脸识别的首选语言之一。
本文将带你从零开始,使用 Python 完成一个完整的人脸识别项目,涵盖以下内容:
- 环境准备与依赖安装
- 人脸检测(Face Detection)原理与实现
- 人脸特征提取(Face Embedding)
- 人脸识别(Face Recognition)逻辑设计
- 实战:构建一个“人脸识别打卡系统”
- 代码优化与注意事项
- 扩展建议与学习资源
一、项目目标
我们希望构建一个基于摄像头的人脸识别系统,具备以下功能:
- 能够实时检测摄像头画面中的人脸;
- 能够识别已知人员(例如:员工、学生)的身份;
- 对未知人脸提示“未识别”;
- 支持添加新的人脸到数据库中;
- 系统运行稳定、响应快速。
最终效果:当你站在摄像头前,系统能自动识别出你是“张三”或“李四”,并打印欢迎信息。
二、技术选型与环境准备
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.jpglisi.jpgwangwu.jpg
✅ 建议:使用正面、清晰、光照均匀的照片,避免戴帽子或墨镜。
步骤 2:生成人脸编码
运行以下命令生成编码文件:
python step1_load_known_faces.py
输出示例:
正在加载并编码已知人脸...
✅ 已处理:zhangsan.jpg -> zhangsan
✅ 已处理:lisi.jpg -> lisi
🎉 共加载 2 个已知人脸,数据已保存。
此时会生成 face_encodings.pkl 和 face_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 快速入门》 |
更多推荐
所有评论(0)