使用Python实现RetinafaceCurricularFace人脸识别系统想自己动手搭建一个能认人的AI系统吗比如让电脑认出摄像头前的你是不是本人或者从一堆照片里快速找到某个特定的人。今天咱们就来聊聊怎么用Python结合两个很厉害的模型——Retinaface和CurricularFace从头到尾搭一个这样的人脸识别系统。听起来好像很高深但其实跟着步骤走你会发现并没有想象中那么难。整个过程就像搭积木我们把负责“找人脸”的模块和负责“认人脸”的模块组合起来再写点代码把它们串起来一个基础的系统就成型了。我会把每一步的代码都贴出来你复制过去基本就能跑起来。1. 准备工作环境和模型在开始写代码之前我们得先把“战场”布置好。这包括安装必要的Python库以及准备好我们要用的核心模型。1.1 安装必要的Python库打开你的终端或命令提示符用pip安装下面这几个库。它们是我们这个项目的基石。pip install opencv-python pip install numpy pip install torch torchvision pip install insightface pip install onnxruntime # 或者 onnxruntime-gpu如果你有GPU的话简单解释一下它们都是干嘛的opencv-python (cv2)这是计算机视觉的“瑞士军刀”我们主要用它来读取图片、显示图片、调用摄像头。numpyPython里处理数组和矩阵计算的核心库我们的图片数据在程序里就是以numpy数组的形式存在的。torch (PyTorch)一个非常流行的深度学习框架我们的CurricularFace模型很可能就是基于它构建的。insightface一个非常强大的人脸分析工具箱。它里面就包含了RetinaFace人脸检测模型我们直接调用就行省去了自己训练模型的麻烦。onnxruntime一个用于运行ONNX格式模型的高性能推理引擎。很多预训练模型为了部署方便都会导出为ONNX格式。1.2 理解核心模型Retinaface 和 CurricularFace在动手写代码前花两分钟了解一下这两个“主角”是怎么工作的后面用起来会更得心应手。你可以把整个识别过程想象成两个步骤找人Retinaface给你一张班级合照你的眼睛会先扫一遍把每个同学的脸都找出来并且框出来。Retinaface干的就是这个活它是一个非常精准的单阶段人脸检测器能在图片里把所有人脸的位置和大小边界框找出来顺带还能定位眼睛、鼻子、嘴角这些关键点。认人CurricularFace找到脸之后我们需要判断这张脸是谁。CurricularFace是一个人脸识别模型它的任务是把一张人脸图片转换成一个“特征向量”。你可以把这个向量想象成一个人的“数字身份证”是一串有高有低的数字。同一个人的不同照片转换出来的向量会非常相似不同人的向量则差异很大。我们就是通过比较这些向量的相似度来判断是不是同一个人。所以流程就是输入图片 → Retinaface检测并裁剪出人脸 → CurricularFace将人脸转为特征向量 → 与数据库中已知的特征向量比较 → 输出识别结果。2. 第一步用Retinaface检测人脸现在我们开始写第一个核心功能从一张图片里把脸找出来。我们会用到insightface库提供的现成模型。import cv2 import numpy as np import insightface from insightface.app import FaceAnalysis # 初始化人脸分析应用这里我们指定使用RetinaFace检测模型 # 模型会自动从云端下载到本地第一次运行时会需要一点时间 app FaceAnalysis(namebuffalo_l) # buffalo_l 是一个包含RetinaFace的模型包 app.prepare(ctx_id0, det_size(640, 640)) # ctx_id0 表示使用第一个GPU或CPUdet_size是检测尺寸 def detect_faces(image_path): 检测图片中的所有人脸 参数: image_path: 输入图片的路径 返回: faces: 检测到的人脸信息列表 img: 读取的原始图像用于后续绘制 # 读取图片 img cv2.imread(image_path) if img is None: print(f错误无法读取图片 {image_path}) return None, None # 使用RetinaFace进行人脸检测 faces app.get(img) print(f在图片中检测到 {len(faces)} 张人脸) # 遍历每张检测到的人脸打印基本信息 for i, face in enumerate(faces): bbox face.bbox.astype(int) # 人脸边界框 [x1, y1, x2, y2] landmarks face.kps.astype(int) # 人脸5个关键点左眼、右眼、鼻子、左嘴角、右嘴角 print(f 人脸 {i1}: 位置 {bbox}, 置信度 {face.det_score:.3f}) return faces, img # 测试一下检测函数 if __name__ __main__: # 替换成你自己的图片路径 test_image test_photo.jpg detected_faces, original_image detect_faces(test_image) # 如果检测到人脸就在图片上画出来看看 if detected_faces and original_image is not None: img_draw original_image.copy() for face in detected_faces: bbox face.bbox.astype(int) # 用绿色矩形框出人脸 cv2.rectangle(img_draw, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 2) # 用红色点标出关键点 landmarks face.kps.astype(int) for (x, y) in landmarks: cv2.circle(img_draw, (x, y), 2, (0, 0, 255), -1) # 显示结果 cv2.imshow(Detected Faces, img_draw) cv2.waitKey(0) # 按任意键关闭窗口 cv2.destroyAllWindows()运行这段代码如果一切顺利你应该能看到打开的图片上人脸被绿色框框了出来眼睛鼻子位置还有红点。这就证明我们的“找人”模块工作正常了。3. 第二步对齐人脸并提取特征检测到人脸后直接裁剪下来送给识别模型可能效果不好因为人脸可能是歪的。所以最佳实践是先进行“对齐”也就是根据眼睛的位置把人脸“摆正”裁剪出一个标准正脸图。然后再用CurricularFace模型从这个标准图中提取特征向量。3.1 人脸对齐与裁剪insightface库的检测结果里已经包含了用于对齐的关键点我们可以直接用它的工具函数来处理。from insightface.model_zoo import get_model from insightface.utils import face_align import os # 初始化CurricularFace识别模型这里假设模型是ONNX格式并放在本地 # 你需要先下载对应的模型文件例如 curricularface.onnx model_path ./models/curricularface.onnx if not os.path.exists(model_path): print(f警告未找到模型文件 {model_path}请先下载。) # 在实际项目中这里可以添加自动下载模型的代码 # 例如从ModelScope或Hugging Face下载 # 加载识别模型 recognition_model get_model(model_path, providers[CPUExecutionProvider]) # 使用CPU推理 recognition_model.prepare(ctx_id0) # 同样指定运行设备 def align_and_extract(image_path, save_cropFalse): 检测人脸对齐并提取特征向量 参数: image_path: 输入图片路径 save_crop: 是否保存对齐后的人脸图片用于调试 返回: features: 提取到的特征向量列表每张人脸一个 aligned_imgs: 对齐后的人脸图片列表 # 1. 检测人脸 faces, img detect_faces(image_path) if not faces: print(未检测到人脸无法提取特征。) return [], [] features [] aligned_imgs [] for i, face in enumerate(faces): # 2. 人脸对齐利用5个关键点将人脸变换到112x112的标准尺寸 # face_align.norm_crop 函数内部完成了仿射变换和裁剪 aligned_face face_align.norm_crop(img, landmarkface.kps, image_size112) aligned_imgs.append(aligned_face) if save_crop: # 保存对齐后的图片看看效果 crop_path faligned_face_{i}.jpg cv2.imwrite(crop_path, aligned_face) print(f已保存对齐人脸到: {crop_path}) # 3. 提取特征向量 # 模型通常需要将图片数据归一化并转换维度 (H,W,C) - (1,C,H,W) face_input cv2.cvtColor(aligned_face, cv2.COLOR_BGR2RGB) # 转为RGB face_input np.transpose(face_input, (2, 0, 1)) # 转为 (C, H, W) face_input np.expand_dims(face_input, axis0) # 增加批次维度 - (1, C, H, W) face_input (face_input / 255.0).astype(np.float32) # 归一化到[0,1] # 运行模型推理得到特征向量 feat recognition_model.get_feat(face_input)[0] # 取第一个批次的结果 features.append(feat) print(f 人脸 {i1}: 特征向量维度 {feat.shape}) return features, aligned_imgs # 测试特征提取 if __name__ __main__: test_features, test_aligned align_and_extract(test_photo.jpg, save_cropTrue) if test_features: print(f成功提取到 {len(test_features)} 个特征向量。) print(f第一个向量的前5个值: {test_features[0][:5]})对齐这一步非常关键它消除了姿势和轻微旋转的影响能显著提升后续识别的准确率。保存下来的aligned_face.jpg应该是一张正脸的、大小统一的人脸图片。3.2 特征比对与识别有了特征向量如何判断两张脸是不是同一个人呢最常用的方法是计算余弦相似度。简单理解就是计算两个向量的夹角余弦值。这个值越接近1说明两个向量方向越一致即两张脸越相似。import numpy as np def calculate_cosine_similarity(feat1, feat2): 计算两个特征向量之间的余弦相似度 参数: feat1, feat2: 两个特征向量 (np.ndarray) 返回: similarity: 余弦相似度范围[-1, 1]越接近1越相似 # 归一化向量单位化 feat1_norm feat1 / np.linalg.norm(feat1) feat2_norm feat2 / np.linalg.norm(feat2) # 计算点积即余弦相似度 similarity np.dot(feat1_norm, feat2_norm) return similarity def recognize_face(unknown_feat, known_feats_dict, threshold0.3): 将未知人脸特征与已知人脸库进行比对识别 参数: unknown_feat: 未知人脸的特征向量 known_feats_dict: 已知人脸库格式为 {人名: 特征向量, ...} threshold: 判定为同一人的相似度阈值需要根据模型调整 返回: name: 识别出的人名若低于阈值则返回Unknown max_similarity: 最高的相似度值 best_name Unknown max_similarity -1.0 for name, known_feat in known_feats_dict.items(): sim calculate_cosine_similarity(unknown_feat, known_feat) if sim max_similarity: max_similarity sim best_name name # 如果最高相似度低于阈值则认为是陌生人 if max_similarity threshold: best_name Unknown return best_name, max_similarity # 模拟一个简单的人脸数据库 known_faces_db { 张三: np.random.randn(512).astype(np.float32), # 假设是张三的特征 李四: np.random.randn(512).astype(np.float32), # 假设是李四的特征 } # 模拟一个待识别的特征这里用随机数代替实际应从图片提取 unknown_feature np.random.randn(512).astype(np.float32) # 进行识别 identified_name, score recognize_face(unknown_feature, known_faces_db, threshold0.3) print(f识别结果: {identified_name}, 相似度: {score:.4f})这里的threshold阈值是个重要的超参数。设得太高可能会把本人拒之门外假阴性设得太低又容易把别人错认成本人假阳性。通常需要用一个测试集来调整找到最佳值。对于CurricularFace这类现代模型阈值可能在0.2到0.4之间。4. 第三步搭建完整的人脸识别系统我们把前面所有的积木拼起来创建一个FaceRecognitionSystem类。这个类会管理已知人脸数据库并对外提供注册录入和识别两个核心接口。import pickle import os class FaceRecognitionSystem: def __init__(self, detection_modelbuffalo_l, recognition_model_path./models/curricularface.onnx): 初始化人脸识别系统 参数: detection_model: 人脸检测模型名称 recognition_model_path: 人脸识别模型路径 # 1. 初始化检测器 self.detector FaceAnalysis(namedetection_model) self.detector.prepare(ctx_id0, det_size(640, 640)) # 2. 初始化识别器 self.recognizer get_model(recognition_model_path, providers[CPUExecutionProvider]) self.recognizer.prepare(ctx_id0) # 3. 初始化人脸数据库内存字典 磁盘文件 self.known_faces {} # 格式: {name: feature_vector} self.db_file face_database.pkl self.load_database() # 4. 识别阈值 self.threshold 0.35 # 需要根据你的模型和场景调整 def load_database(self): 从磁盘加载已知人脸数据库 if os.path.exists(self.db_file): with open(self.db_file, rb) as f: self.known_faces pickle.load(f) print(f已从 {self.db_file} 加载 {len(self.known_faces)} 个已知人脸。) else: print(未找到现有数据库将创建新库。) def save_database(self): 保存已知人脸数据库到磁盘 with open(self.db_file, wb) as f: pickle.dump(self.known_faces, f) print(f数据库已保存至 {self.db_file}) def register_face(self, image_path, name): 注册新的人脸人脸录入 参数: image_path: 包含人脸的图片路径 name: 该人脸对应的姓名 返回: success: 是否注册成功 # 提取图片中的特征 features, _ self._extract_features_from_image(image_path) if not features: print(f注册失败在 {image_path} 中未检测到人脸。) return False if len(features) 1: print(f警告在 {image_path} 中检测到多张人脸将使用置信度最高的一张进行注册。) # 这里可以添加逻辑选择最大脸或最清晰的脸简单起见用第一张 chosen_feat features[0] else: chosen_feat features[0] # 存入数据库 self.known_faces[name] chosen_feat self.save_database() print(f成功注册人脸: {name}) return True def _extract_features_from_image(self, image_path): 内部方法从单张图片中提取所有人脸特征 img cv2.imread(image_path) if img is None: return [], [] faces self.detector.get(img) if not faces: return [], [] features [] aligned_faces [] for face in faces: # 对齐人脸 aligned_face face_align.norm_crop(img, landmarkface.kps, image_size112) aligned_faces.append(aligned_face) # 提取特征 face_input cv2.cvtColor(aligned_face, cv2.COLOR_BGR2RGB) face_input np.transpose(face_input, (2, 0, 1)) face_input np.expand_dims(face_input, axis0) face_input (face_input / 255.0).astype(np.float32) feat self.recognizer.get_feat(face_input)[0] features.append(feat) return features, aligned_faces def recognize_image(self, image_path): 识别一张图片中的所有人脸 参数: image_path: 待识别的图片路径 返回: results: 识别结果列表每个元素为 (人脸位置bbox, 姓名, 相似度) img_with_box: 画好框和姓名的图片 img cv2.imread(image_path) if img is None: print(f无法读取图片: {image_path}) return [], None faces self.detector.get(img) if not faces: print(未检测到人脸。) return [], img results [] img_draw img.copy() for face in faces: bbox face.bbox.astype(int) # 对齐并提取特征 aligned_face face_align.norm_crop(img, landmarkface.kps, image_size112) face_input cv2.cvtColor(aligned_face, cv2.COLOR_BGR2RGB) face_input np.transpose(face_input, (2, 0, 1)) face_input np.expand_dims(face_input, axis0) face_input (face_input / 255.0).astype(np.float32) unknown_feat self.recognizer.get_feat(face_input)[0] # 与数据库比对 name, similarity self._recognize_single_face(unknown_feat) results.append((bbox, name, similarity)) # 在图片上绘制结果 label f{name} ({similarity:.2f}) color (0, 255, 0) if name ! Unknown else (0, 0, 255) cv2.rectangle(img_draw, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2) cv2.putText(img_draw, label, (bbox[0], bbox[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) return results, img_draw def _recognize_single_face(self, unknown_feat): 内部方法识别单个人脸特征 best_name Unknown max_similarity -1.0 for name, known_feat in self.known_faces.items(): sim calculate_cosine_similarity(unknown_feat, known_feat) if sim max_similarity: max_similarity sim best_name name if max_similarity self.threshold: best_name Unknown return best_name, max_similarity # 让我们来实际运行一下这个系统 if __name__ __main__: # 1. 初始化系统 print(正在初始化人脸识别系统...) system FaceRecognitionSystem() # 2. 注册人脸假设你有一张名为zhangsan.jpg的张三的照片 # system.register_face(zhangsan.jpg, 张三) # system.register_face(lisi.jpg, 李四) # 3. 识别一张新图片 test_img group_photo.jpg recognition_results, annotated_image system.recognize_image(test_img) if annotated_image is not None: print(识别完成) for bbox, name, sim in recognition_results: print(f 位置{bbox}: {name} (相似度: {sim:.3f})) # 显示结果图片 cv2.imshow(Recognition Result, annotated_image) cv2.waitKey(0) cv2.destroyAllWindows() # 也可以保存结果 # cv2.imwrite(result.jpg, annotated_image)这个类已经是一个功能完整的系统核心了。你可以通过register_face方法录入家人、朋友或同事的照片然后使用recognize_image方法让系统识别新照片里的人是谁。5. 让系统更实用优化与扩展建议搭好了基础系统我们来看看怎么让它变得更快、更准、更好用。这里有几个方向你可以尝试。关于速度如果你有英伟达的GPU一定要利用起来。在初始化模型时把ctx_id参数设为你的GPU ID例如ctx_id0代表第一块GPU并将onnxruntime的provider改为CUDAExecutionProvider推理速度会有数量级的提升。对于实时视频识别还可以考虑使用多线程让检测和识别并行进行。关于准确率数据库里的特征质量决定了一切。建议用同一个人多张不同角度、不同光线的清晰正脸照片来注册然后取它们特征向量的平均值作为最终存档这样比单张照片更稳定。识别阈值threshold需要你用一些正样本本人和负样本他人图片来测试调整找到一个误认和拒识都相对较少的平衡点。关于扩展现在的数据库是简单的字典加文件存储。如果人脸数量很多比如上千每次识别都做全量比对会很慢。可以考虑使用专门的向量数据库比如FAISSFacebook AI Similarity Search它能对高维向量进行快速近似最近邻搜索非常适合海量人脸检索的场景。最后如果你想跳过环境配置和模型下载的麻烦快速体验一个更完善、带界面的系统可以留意一些云平台提供的预置镜像。这些镜像把环境、模型甚至示例代码都打包好了一键就能拉起一个可运行的服务非常适合快速原型验证和学习。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。