拓冰建站拓冰建站
首页 / 资讯中心 / 正文

RetinaFace+FaceNet人脸识别实战:从检测对齐到128维特征比对

简介基于FaceNet与RetinaFace的人脸识别Python源码包面向希望在本地或在线场景中快速实现人脸检测、特征提取与身份比对的开发者也适合作为人脸识别课程设计或毕设参考。资源共28个文件包含14个Python程序文件覆盖检测网络、骨干网络、特征编码、预测脚本等完整流程2个pth模型权重与4个npy编码文件提供训练好的参数可直接加载使用另有4张示例图片、2个Markdown文档、1个txt依赖说明及1个字体文件压缩包整体23.44MB兼具完整性与便捷性。目前已有266人学习下载。源码采用清晰的分层结构将RetinaFace检测、FaceNet嵌入、人脸编码与比对逻辑分离便于理解原理与二次开发同时附带使用步骤、常见问题汇总和模型文件读者按说明即可快速启动识别任务既能满足入门学习需求也可作为中高级开发者构建在线人脸识别服务的基础代码库。1. 为什么我选RetinaFaceFaceNet这套组合做人脸识别如果你只做静态照片比对单人单张确实随便两个网络都能出结果但一旦进入真实场景比如摄像头下的在线识别最耗时间的其实不是特征计算而是人脸检测和关键点对齐。RetinaFace负责把你的脸精准框出来并给出双眼、鼻尖、两边嘴角五个关键点FaceNet再负责把对齐后的人脸编码成128维向量。两者分工明确RetinaFace解决“人脸在哪、脸歪没歪”的问题FaceNet负责“这个人是谁”。这套源码恰好把这些都放齐了并且还给了mobilenet和inception_resnetv1两个backbone可切换。5年以上经验的人可以直接看predict.py和encoding.py的交界处新手则可以按第3章的步骤跑通全流程。2. RetinaFace与FaceNet的模型分工和文件结构2.1 RetinaFace的检测分支不只是画个框RetinaFace基于RetinaNet的anchor-based思路但实际落地时真正带来提升的是它在分类和回归之外又加了一个关键点回归分支。这也解释了为什么源码的目录里既有retinaface.py又有nets_retinaface后者重写了包含FPN特征金字塔和SSH模块的检测头。anchors.py负责为每个特征图位置生成不同尺度的先验框box_utils.py负责把网络输出转成最终的人脸框。源包里的Retinaface_mobilenet0.25.pth是MobileNet 0.25倍宽度的轻量版意味着在CPU上也能维持可用的帧率。对于输入图片RetinaFace会先在多个尺度上提取特征。以下代码来自anchors.py的锚点生成思路实际作用是把网格中的每个位置映射回原图的归一化坐标import numpy as np def generate_anchors(fmap_size, stride, anchor_sizes, aspect_ratios): # fmap_size: 特征图尺寸 # stride: 当前层相对于原图的下采样倍数例如 8, 16, 32 anchors [] for scale in anchor_sizes: for ratio in aspect_ratios: w scale * np.sqrt(ratio) h scale / np.sqrt(ratio) for cy in range(fmap_size): for cx in range(fmap_size): anchors.append([ (cx 0.5) * stride, (cy 0.5) * stride, w, h ]) return np.array(anchors)这段代码生成的锚点坐标还不能直接用必须除以输入边长做归一化。参数stride对FPN中的P3、P4、P5层分别取8、16、32覆盖小脸和大脸。如果你在处理多人密集场景时发现漏检优先考虑有没有把输入分辨率调高而不是把anchor的尺寸改得过大否则训练时预设的比例会失配导致推理结果变差。提示RetinaFace的五个关键点命名顺序是左眼、右眼、鼻尖、左嘴角、右嘴角丢掉这个顺序就会在对齐阶段把图片翻转。2.2 FaceNet把对齐后的人脸变成128维向量FaceNet在源码里并不是直接对原始图片输出向量它要求输入一个固定尺寸的已对齐人脸图片。这里采用112×112并做标准归一化。检测阶段已经给出了五个关键点可以用相似变换把眼睛水平、人脸缩放后再送入FaceNet。如果没有这一步同一张脸换了个拍摄角度识别模型给出来的embedding距离可能比不同人还远。nets目录下的inception_resnetv1.py和mobilenet.py分别是两个特征提取网络。前者是Inception-ResNet-v1特征表达能力强适合GPU上离线批处理后者是MobileNet速度快但精度下降适合视频流实时识别。两个对应的编码文件inception_resnetv1_face_encoding.npy和mobilenet_face_encoding.npy要分开存因为维度虽然很可能都是128维但分布空间不是同一个混着用比对分数没有意义。2.3 源码目录的小地图路径所属阶段核心输入输出retinaface.py检测图片 - 框、关键点、置信度nets_retinaface/retinaface.py检测网络前向结构nets_retinaface/layers.py检测自定义FPN和SSH模块utils_bbox.py检测后处理解码、NMS、对齐矩阵anchors.py检测预处理网格生成锚点facenet.py识别对齐人脸 - 128维embeddingencoding.py注册批量生成npy编码文件predict.py推理图片/视频流里完成全流程config.py全局配置网络输入尺寸、置信度阈值这张表可以当作调试路标。如果检测框时好时坏改retinaface.py和config.py如果识别不准问题更多出在facenet.py、对齐逻辑和encoding.py注册的照片质量上。3. 本地跑通全流程环境准备、人脸注册与在线预测3.1 先搭好Python环境再谈推理资源里的requirements.txt包含了主要的依赖包括PyTorch、TorchVision、OpenCV、NumPy和Pillow。为了避免和本机其他项目相互影响我习惯先创建独立的Python虚拟环境python -m venv face_env # Windows face_env\Scripts\activate # macOS / Linux source face_env/bin/activate pip install -r requirements.txt安装时如果pip解析依赖很慢可以换成国内的镜像源。torch和torchvision版本不建议直接用pip install torch装最新版因为torchvision.transforms的一些接口在0.11之后对F.resize行为有调整源码里某些resize写法可能触发警告或结果不一致。装好后运行python -c import torch, torchvision; print(torch.__version__)确认没问题。3.2 用encoding.py建人脸底库face_dataset目录下已经放了两张示例图像obama_1.jpg和张学友_1.jpg。要注册更多人只需要往这个目录里再放以人名开头的图片。然后运行encoding.pypython encoding.py \ --dataset ./face_dataset \ --model_path ./Retinaface_mobilenet0.25.pth \ --output ./mobilenet_face_encoding.npy这段命令的作用是遍历face_dataset中的每张图片先用RetinaFace检测人脸并提取关键点再将人脸区域对齐到112×112交给MobileNet版本的FaceNet编码成128维向量最后写入mobilenet_face_encoding.npy。参数里--dataset和--model_path是必填项--output如果省略则使用默认文件名。如果你的显存足够也可以把model_path换成inception_resnetv1_face_encoding.npy对应的权重但必须同步修改predict.py中的backbone参数否则两套模型的变量命名空间不一致。3.3 运行predict.py进入在线识别流程源码里的predict.py是完整推理入口它既可以处理img/zhangxueyou.jpg这类静态图也可以打开摄像头做视频流识别。以摄像头模式为例# predict.py 中调用逻辑示意 cap cv2.VideoCapture(0) while True: ret, frame cap.read() result recognizer.recognize(frame) for name, score, box in result: cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2) cv2.putText(frame, f{name}:{score:.2f}, (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imshow(face recognition, frame) if cv2.waitKey(1) 0xFF ord(q): break打开摄像头时需要确认索引0是否对应你正在使用的设备。识别结果是一个列表每个元素包含姓名、相似度分数和检测框坐标。这里把相似度直接显示在框上方方便观察阈值是否合适。运行前先拿静态图python predict.py --img img/obama.jpg验证模型的输入输出是否正常避免一上来就调摄像头把问题混在一起。4. 从锚点解码到特征比对看穿predict.py的每一步4.1 输入预处理与锚点解码predict.py在送入模型前会把图片的短边缩放并填充到模型输入尺寸。这一操作很关键否则1200×800的原图直接送进MobileNet0.25这种轻量网络特征图上的锚点会稀疏到漏检。缩放完成后RetinaFace输出三个头的feature map需要通过box_utils.py里实现的解码函数换算成人脸框# box_utils.py 中的 decode def decode_boxes(loc, priors, variances): # loc: 网络回归分支输出 (N, 4) # priors: anchors.py生成的锚点 (N, 4)形式为[center_x, center_y, w, h] # variances: 用于缩放预测偏移原版取[0.1, 0.2] cx priors[:, 0] loc[:, 0] * variances[0] * priors[:, 2] cy priors[:, 1] loc[:, 1] * variances[1] * priors[:, 3] w priors[:, 2] * np.exp(loc[:, 2] * variances[1]) h priors[:, 3] * np.exp(loc[:, 3] * variances[1]) boxes np.stack([ cx - w / 2, cy - h / 2, cx w / 2, cy h / 2 ], axis-1) return boxes这里的loc[:, 2]和loc[:, 3]网络输出的尺度放缩值通常范围很小如果不做exp指数变换宽高会一直停留在锚点的原始尺度上框不准。variances控制着中心位置和尺寸的回归强度属于从训练配置里固定下来的先验值不能顺手改掉。如果你发现所有检测框都朝某个方向偏移说明输入归一化中心点与训练时不一致。4.2 NMS、关键点与仿射对齐网络在同一张脸上会输出大量候选框。先按置信度过滤一遍再做NMS合并重复框。utils_bbox.py中对NMS的实现采用纯NumPy循环小样本场景下足够但批量视频帧时可以考虑换成torchvision自带的nms来提速。对齐关键代码# 对齐关键代码 for bbox, landmark in zip(boxes, landmarks): # landmark是5个坐标点格式为[(x1,y1), (x2,y2), (x3,y3), (x4,y4), (x5,y5)] src_pts np.float32(landmark) dst_pts np.float32([[38.2946, 51.6963], [73.5318, 51.5014], [56.0252, 71.7366], [41.5493, 92.3655], [70.7299, 92.2041]]) affine cv2.estimateAffinePartial2D(src_pts, dst_pts)[0] aligned cv2.warpAffine(image, affine, (112, 112))dst_pts是标准人脸关键点模板它决定了瞳孔在哪里、嘴巴在哪里。这里不要随意改成其他坐标否则FaceNet训练时的人脸对齐分布会被破坏。如果识别率持续偏低优先检查对齐后的图片是否真的有水平双眼。4.3 128维向量的生成和身份相似度对齐后的112×112人脸图片会经过facenet.py中模型forward计算得到128维向量。这个向量在输出前已经做了L2归一化。然后进入和npy底库比对阶段# predict.py 中的相似度比对 def match_face(embedding, saved, threshold0.65): # embedding: (128,) # saved[encodings]: (K, 128)K为底库人数 # 因为向量是归一化的点积等于余弦相似度 scores saved[encodings] embedding idx int(np.argmax(scores)) max_score float(scores[idx]) if max_score threshold: return saved[names][idx], max_score return unknown, max_scorethreshold的设置直接影响误识率和拒识率。默认0.65在公开数据集上表现尚可但在角度差异大的摄像头画面中同一人相似度可能只有0.55。这时建议先收集一段真实视频统计分布再降阈值。注意unknown的返回不是匹配失败而是系统认为这个人与底库中所有身份的相似度都不够属于正常设计。5. 踩过坑之后模型切换、阈值校准和实时性调优5.1 从mobilenet换到inception_resnetv1时不只是换权重predict.py里如果只有一条--backbone参数千万别忽略它需要同时影响RetinaFace的模型路径和FaceNet的编码文件。原因是inception_resnetv1_face_encoding.npy与mobilenet_face_encoding.npy的向量空间不同哪怕它们都是128维同一张脸的余弦相似度也可能相差0.2以上。现场切换到Inception版后建议至少拍50张同一人的照片重新生成底库不要直接拿旧npy继续用。5.2 阈值设定的现场校准法与其拍脑袋定阈值不如在固定机位下录一分钟视频每隔5帧抽样一次得到正样本相似度序列。再找10名路人作为负样本记录最大相似度。两个分布的重叠区就是阈值选择区间。常见场景下Mobilenet的同一人相似度在0.55到0.85之间路人可能在0.2到0.5之间取0.65通常不会出错。但若现场逆光强烈正样本分布会整体下移需要把阈值调低到0.58左右同时增加人脸防伪模块来拉高安全基线。5.3 显存不足、漏检与关键点异常的应对如果程序在decode阶段报显存溢出可以先把输入尺寸从宽高都降到640×640这一步的影响比把batch size从1改成4更大。漏检则优先看置信度阈值是否卡得过高其次看输入尺寸是否太小。关键点异常表现为对齐后人脸右眼跑到左边这种问题几乎都出在landmark顺序上检查一下src_pts是否按左眼、右眼、鼻尖、左嘴角、右嘴角排列。最后在CPU机器上跑视频流时可以考虑用OpenCV的CAP_DSHOW方式打开摄像头并限制检测帧率比如每两帧跑一次检测识别结果沿用上一帧。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门