This commit is contained in:
2025-09-03 16:22:21 +08:00
parent b7773f5f00
commit d83923d06b
5 changed files with 211 additions and 342 deletions

View File

@ -4,10 +4,12 @@ import numpy as np
import insightface
from insightface.app import FaceAnalysis
class FaceRecognizer:
"""
封装InsightFace人脸识别功能支持从文件夹加载已知人脸。
"""
def __init__(self, known_faces_dir: str):
self.known_faces_dir = known_faces_dir
self.app = self._initialize_insightface()
@ -16,40 +18,30 @@ class FaceRecognizer:
self._load_known_faces()
def _initialize_insightface(self):
"""
初始化InsightFace FaceAnalysis应用。
默认使用CPU如果检测到CUDA会自动使用GPU。
"""
print("正在初始化InsightFace人脸识别引擎...")
"""初始化InsightFace FaceAnalysis应用"""
print("初始化InsightFace引擎...")
try:
# 默认模型是 'buffalo_l',包含检测、对齐、识别功能
# 如果需要更小的模型,可以尝试 'buffalo_s' 或 'buffalo_m'
# ctx_id=0 表示使用GPUctx_id=-1 表示使用CPU
# InsightFace会自动检测CUDA并选择GPU所以通常不需要手动设置ctx_id
app = FaceAnalysis(name='buffalo_l', root='~/.insightface') # 模型下载到用户目录
app.prepare(ctx_id=0, det_size=(640, 640)) # det_size影响检测性能和精度
print("InsightFace人脸识别引擎初始化成功。")
app = FaceAnalysis(name='buffalo_l', root='~/.insightface')
app.prepare(ctx_id=0, det_size=(640, 640))
print("InsightFace引擎初始化完成")
return app
except Exception as e:
print(f"InsightFace人脸识别引擎初始化失败: {e}")
print("确保已安装insightface和onnxruntime并且模型文件已下载或可访问")
print(f"InsightFace初始化失败: {e}")
print("检查依赖是否安装及模型是否可访问")
return None
def _load_known_faces(self):
"""
扫描已知人脸目录,加载每个人的照片并计算人脸特征。
"""
"""加载已知人脸特征"""
if not os.path.exists(self.known_faces_dir):
print(f"警告: 已知人脸目录 '{self.known_faces_dir}' 不存在。请创建并放入照片。")
print(f"已知人脸目录不存在,已创建: {self.known_faces_dir}")
os.makedirs(self.known_faces_dir, exist_ok=True)
return
print(f"正在加载已知人脸特征: '{self.known_faces_dir}'...")
print(f"从目录加载人脸特征: {self.known_faces_dir}")
for person_name in os.listdir(self.known_faces_dir):
person_dir = os.path.join(self.known_faces_dir, person_name)
if os.path.isdir(person_dir):
print(f" 加载人物: {person_name}")
print(f"处理人物: {person_name}")
embeddings = []
for filename in os.listdir(person_dir):
if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
@ -57,131 +49,91 @@ class FaceRecognizer:
try:
img = cv2.imread(image_path)
if img is None:
print(f" 警告: 无法读取图片 '{image_path}',已跳过")
print(f"无法读取图片: {image_path},已跳过")
continue
# 查找人脸并提取特征
faces = self.app.get(img)
if faces:
# 通常一张照片只有一个人脸,取第一个
embeddings.append(faces[0].embedding)
print(f" 成功提取 '{filename}' 的人脸特征。")
print(f"提取特征成功: {filename}")
else:
print(f" 警告: 在图片 '{filename}' 中未检测到人脸,已跳过")
print(f"未检测到人脸: {filename},已跳过")
except Exception as e:
print(f" 处理图片 '{image_path}' 时发生错误: {e}")
print(f"处理图片出错 {image_path}: {e}")
if embeddings:
# 将多张照片的特征取平均,作为该人物的最终特征
self.known_faces_embeddings[person_name] = np.array(embeddings).mean(axis=0)
self.known_faces_names.append(person_name)
print(f" 人物 '{person_name}' 加载完成,共 {len(embeddings)} 张照片")
print(f"人物 {person_name} 加载完成,共 {len(embeddings)} 张照片")
else:
print(f" 警告: 人物 '{person_name}' 没有有效的人脸特征,已跳过")
print(f"已知人脸加载完成{len(self.known_faces_names)} 个人物。")
print(f"人物 {person_name} 无有效特征,已跳过")
print(f"人脸加载完成{len(self.known_faces_names)} ")
def recognize(self, frame, threshold=0.4):
"""
在视频帧中识别人脸。
Args:
frame: 输入的图像帧 (NumPy数组, BGR格式)。
threshold (float): 识别相似度阈值。0.0到1.0,越高越严格。
Returns:
tuple[bool, str|None, float|None]: 是否检测到已知人脸,检测到的人名,以及相似度。
"""
"""识别人脸并返回结果"""
if not self.app or not self.known_faces_names:
return False, None, None
faces = self.app.get(frame) # 在帧中检测并提取所有人的脸
faces = self.app.get(frame)
if not faces:
return False, None, None
for face in faces:
# 遍历已知人脸,进行比对
for known_name in self.known_faces_names:
known_embedding = self.known_faces_embeddings[known_name]
# --- 关键修改:手动计算余弦相似度 ---
# 确保embedding是float32类型避免潜在的类型不匹配问题
embedding1 = face.embedding.astype(np.float32)
embedding2 = known_embedding.astype(np.float32)
# 计算点积
dot_product = np.dot(embedding1, embedding2)
# 计算L2范数向量长度
norm_embedding1 = np.linalg.norm(embedding1)
norm_embedding2 = np.linalg.norm(embedding2)
# 避免除以零
if norm_embedding1 == 0 or norm_embedding2 == 0:
similarity = 0.0
else:
similarity = dot_product / (norm_embedding1 * norm_embedding2)
# -------------------------------------
similarity = 0.0 if (norm_embedding1 == 0 or norm_embedding2 == 0) else (
dot_product / (norm_embedding1 * norm_embedding2)
)
if similarity >= threshold:
print(f"!!! 人脸识别检测到已知人物: '{known_name}' (相似度: {similarity:.4f}) !!!")
return True, known_name, similarity # 只要检测到一个就立即返回
return False, None, None # 没有检测到已知人脸
print(f"检测到已知人物: {known_name} (相似度: {similarity:.4f})")
return True, known_name, similarity
return False, None, None
# def test_single_image(self, image_path: str, threshold=0.4):
# """
# 测试单张图片的人脸识别效果
#
# Args:
# image_path: 图片路径
# threshold: 识别阈值
#
# Returns:
# tuple[bool, str|None, float|None]: 是否检测到已知人脸,检测到的人名,以及相似度
# """
# if not os.path.exists(image_path):
# print(f"错误: 图片 '{image_path}' 不存在")
# return False, None, None
#
# # 读取图片
# frame = cv2.imread(image_path)
# if frame is None:
# print(f"错误: 无法读取图片 '{image_path}'")
# return False, None, None
#
# # 调用识别方法
# result, name, similarity = self.recognize(frame, threshold)
#
# # 显示结果
# if result:
# print(f"测试结果: 在图片中识别到 {name},相似度: {similarity:.4f}")
#
# # 绘制识别结果并显示图片
# faces = self.app.get(frame)
# for face in faces:
# bbox = face.bbox.astype(int)
# # 绘制 bounding box
# cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 2)
# # 绘制姓名和相似度
# text = f"{name}: {similarity:.2f}"
# cv2.putText(frame, text, (bbox[0], bbox[1] - 10),
# cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
#
# # 显示图片
# cv2.imshow('Recognition Result', frame)
# print("按任意键关闭图片窗口...")
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# else:
# print("测试结果: 未在图片中识别到已知人脸")
#
# return result, name, similarity
def test_single_image(self, image_path: str, threshold=0.4):
"""测试单张图片识别"""
if not os.path.exists(image_path):
print(f"图片不存在: {image_path}")
return False, None, None
frame = cv2.imread(image_path)
if frame is None:
print(f"无法读取图片: {image_path}")
return False, None, None
result, name, similarity = self.recognize(frame, threshold)
if result:
print(f"识别结果: {name} (相似度: {similarity:.4f})")
faces = self.app.get(frame)
for face in faces:
bbox = face.bbox.astype(int)
cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 2)
text = f"{name}: {similarity:.2f}"
cv2.putText(frame, text, (bbox[0], bbox[1] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
cv2.imshow('识别结果', frame)
print("按任意键关闭窗口...")
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print("未识别到已知人脸")
return result, name, similarity
# if __name__ == "__main__":
# # 初始化人脸识别器,指定已知人脸目录
# recognizer = FaceRecognizer(known_faces_dir="known_faces")
#
# # 测试单张图片
# test_image_path = r"F:\OCR\RapidOCR-main\known_faces\B\14sino-qiu02-master1050.jpg" # 替换为你的测试图片路径
# if __name__ == "__main__":
# recognizer = FaceRecognizer(known_faces_dir="known_faces")
# test_image_path = r"F:\OCR\RapidOCR-main\known_faces\B\14sino-qiu02-master1050.jpg"
# recognizer.test_single_image(test_image_path, threshold=0.4)