以下是一个基于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
键退出。
四、系统特性
- 自动缓存机制:首次加载人脸后会生成缓存文件,后续启动直接读取缓存,提升启动速度。
- 动态扩展:支持随时添加新人脸,无需重启程序即可生效。
- 用户友好:交互式界面,提供清晰的操作指引。
- 模块化设计:代码结构清晰,易于维护和扩展。
五、扩展建议
- 添加人脸删除功能:支持从数据库中删除指定人员的人脸数据。
- 优化识别速度:降低视频流处理帧率,或仅对关键帧进行人脸识别。
- 增加人脸识别阈值调整:根据实际环境调整相似度阈值,提高准确率。
- 添加日志记录:记录识别历史和操作日志,便于审计。
- 开发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
键退出程序。
三、注意事项
-
照片要求:
- 每个人的照片应放在以其姓名命名的单独文件夹中。
- 照片中应只包含清晰可见的单个人脸。
- 照片格式支持 JPG、PNG 等常见格式。
-
性能优化:
- 如需提高识别速度,可降低视频分辨率:
video_capture.set(3, 640); video_capture.set(4, 480)
。 - 减少
faces
目录中的照片数量或人物数量。
- 如需提高识别速度,可降低视频分辨率:
-
识别准确率:
- 调整
TOLERANCE
值(默认 0.6)可平衡准确率和召回率。 - 为每个人提供多角度、不同光照条件下的多张照片可提高识别准确率。
- 调整
四、扩展建议
- 批量添加人脸:编写脚本自动从大型照片库中提取人脸并分类。
- 保存识别记录:将识别结果保存到日志文件或数据库。
- 添加报警功能:当检测到未知人脸时触发警报。
- 支持视频文件输入:除摄像头外,支持对视频文件进行人脸识别。
这个简化版程序适合快速搭建人脸门禁、考勤系统等原型,通过简单修改即可满足基本需求。