Retinaface+CurricularFace详细步骤:修改源码支持返回人脸坐标与置信度
如果你用过Retinaface+CurricularFace这个镜像,可能会发现一个问题:它默认只输出两张图片的相似度分数,告诉你是不是同一个人。但在很多实际项目里,我们不仅想知道是不是同一个人,还想知道人脸在图片里的具体位置,以及检测的把握有多大。
比如,在做考勤系统时,你不仅需要识别员工,还需要记录人脸在监控画面中的位置,方便后续分析。或者在做身份核验时,你需要知道系统检测到的人脸框有多准,用置信度来过滤掉那些模糊不清、检测不准的图片。
今天这篇文章,我就带你一步步修改这个镜像的推理源码,让它不仅能告诉你“是不是同一个人”,还能返回每张图片里检测到的人脸坐标(x, y, w, h)和对应的置信度。整个过程就像给一个现成的工具加装几个实用的小零件,让它的功能更强大、更贴合我们的工程需求。
1. 理解现有流程:从图片到相似度
在动手修改之前,我们得先搞清楚原来的代码是怎么工作的。这样改起来心里才有底,不会把东西搞坏。
1.1 默认推理脚本的核心逻辑
镜像里预置的inference_face.py脚本,其工作流程可以概括为以下几步:
- 加载模型:同时加载RetinaFace人脸检测模型和CurricularFace人脸特征提取模型。
- 读取图片:根据命令行参数或默认路径,读取两张待比对的图片。
- 人脸检测与对齐:对每张图片,使用RetinaFace模型找出其中最大的人脸,然后根据关键点(如眼睛、鼻子、嘴角)进行“对齐”,把人脸转成正脸姿态。注意:这一步其实已经得到了人脸的坐标和置信度,但原来的代码没有把它们返回给我们。
- 特征提取:将对齐后的人脸图片,送入CurricularFace模型,提取出一个固定长度的特征向量(可以理解为人脸的“数字指纹”)。
- 相似度计算:计算两个特征向量之间的余弦相似度。
- 输出结果:将相似度与预设的阈值(默认0.4)比较,输出“同一人”或“不同人”的结论。
问题的关键就在第3步。RetinaFace在检测时,会输出人脸框的坐标和属于人脸的置信度,但这些信息在后续流程中被“消化”掉了,只用于内部的对齐操作,最终没有暴露给用户。
1.2 我们需要修改什么
我们的目标很明确:在完成人脸对齐和特征提取的同时,把RetinaFace检测到的原始信息——人脸框的左上角坐标(x1, y1)、右下角坐标(x2, y2)以及检测置信度(score)——也保存下来,并最终和相似度结果一起输出。
这不会影响原本的识别功能,只是让程序“多说”一些信息。
2. 环境准备与代码定位
修改代码前,确保你已经站在了正确的起跑线上。
2.1 进入工作环境
首先,启动你的Retinaface+CurricularFace镜像。当容器运行起来后,打开终端,执行以下命令进入工作目录并激活Python环境:
cd /root/Retinaface_CurricularFace conda activate torch25这两步确保了所有依赖库(如PyTorch, ModelScope)都已就位,并且我们位于正确的代码目录下。
2.2 找到关键源码文件
我们需要修改的核心文件就是inference_face.py。用你喜欢的文本编辑器(如vim,nano)打开它:
vim inference_face.py或者
nano inference_face.py接下来,我们将在这个文件中进行手术刀式的精准修改。
3. 分步修改推理脚本
我们将按照代码的执行顺序,一步步添加返回人脸框和置信度的功能。请跟随我的步骤,仔细操作。
3.1 第一步:修改特征提取函数,让它“记住”检测结果
首先,我们需要找到负责单张图片处理的函数。通常,这个函数会完成“检测->对齐->提取特征”这一系列动作。在inference_face.py中,我们寻找一个类似get_feature或process_image的函数。
假设我们找到了一个函数,它的大致结构如下:
def get_face_feature(self, img): # 使用RetinaFace检测人脸 dets = self.retinaface_detector.detect(img) # 假设dets包含检测结果 # 这里进行人脸对齐和裁剪 aligned_face = self.align_face(img, dets) # 提取特征 feature = self.recognition_model(aligned_face) return feature我们的修改目标是:让这个函数在返回特征(feature)的同时,也返回检测到的人脸框和置信度。
修改后的函数可能长这样:
def get_face_feature_and_box(self, img): """ 处理单张图片,返回人脸特征、人脸框坐标和置信度。 如果未检测到人脸,返回None。 """ # 使用RetinaFace检测人脸 dets = self.retinaface_detector.detect(img) # 检查是否检测到人脸 if dets is None or len(dets) == 0: return None, None, None # 通常,dets是一个数组,每一行代表一个检测框:[x1, y1, x2, y2, score] # 我们取置信度最高的那个人脸(原逻辑是取最大人脸,这里用最高置信度近似) best_det = max(dets, key=lambda x: x[4]) # 假设第5列是置信度 # 解析坐标和置信度 x1, y1, x2, y2, score = best_det bbox = [int(x1), int(y1), int(x2), int(y2)] # 转换为整数 # 使用这个检测框进行人脸对齐和裁剪 aligned_face = self.align_face(img, best_det) # 提取特征 feature = self.recognition_model(aligned_face) # 返回特征、人脸框和置信度 return feature, bbox, score关键改动说明:
- 函数重命名:将函数名改为
get_face_feature_and_box,明确其新增的功能。 - 解析检测结果:从
dets中提取出置信度最高的检测框,并解析出x1, y1, x2, y2, score。 - 多返回值:函数现在返回三个值:特征向量
feature、人脸框列表bbox、置信度score。
注意:以上代码是一个通用示例。实际代码中,
dets的数据结构、align_face函数的调用方式可能略有不同。你需要根据inference_face.py中的实际代码进行适配。核心思想是拦截并保存dets中的原始信息。
3.2 第二步:修改主推理逻辑,收集并输出新信息
接下来,我们需要修改调用上述函数的主逻辑部分。通常是main函数或脚本直接执行的代码块。
原来的逻辑可能是:
# 处理第一张图片 feature1 = model.get_face_feature(img1) # 处理第二张图片 feature2 = model.get_face_feature(img2) # 计算相似度 sim_score = cosine_similarity(feature1, feature2) print(f”相似度: {sim_score:.4f}“)修改后的逻辑应该是:
# 处理第一张图片,同时获取框和置信度 feature1, bbox1, score1 = model.get_face_feature_and_box(img1) if feature1 is None: print(“错误:在第一张图片中未检测到人脸。”) return # 处理第二张图片,同时获取框和置信度 feature2, bbox2, score2 = model.get_face_feature_and_box(img2) if feature2 is None: print(“错误:在第二张图片中未检测到人脸。”) return # 计算相似度 sim_score = cosine_similarity(feature1, feature2) # 打印详细结果 print(“\n” + “=”*50) print(“人脸识别与检测详细结果”) print(“=”*50) print(f”图片1 - 人脸位置: {bbox1}“) print(f”图片1 - 检测置信度: {score1:.4f}“) print(f”图片2 - 人脸位置: {bbox2}“) print(f”图片2 - 检测置信度: {score2:.4f}“) print(f”\n人脸特征相似度: {sim_score:.4f}“) print(f”判定阈值: {args.threshold}“) if sim_score > args.threshold: print(“结论: 同一人”) else: print(“结论: 不同人”) print(“=”*50)关键改动说明:
- 接收多返回值:调用修改后的函数,接收特征、框、置信度三个值。
- 增加错误处理:检查是否成功检测到人脸,避免后续步骤出错。
- 丰富输出信息:将人脸框坐标、检测置信度、相似度、阈值和最终结论清晰、结构化地打印出来,一目了然。
3.3 第三步:考虑将结果以结构化格式返回(进阶)
对于需要集成到其他系统的场景,纯文本输出可能不方便。我们可以考虑将结果封装成字典或JSON格式。
在主逻辑的最后,我们可以添加:
# 将结果组织成字典 result = { “image1”: { “bbox”: bbox1, “detection_score”: float(score1), # 转换为Python float类型 }, “image2”: { “bbox”: bbox2, “detection_score”: float(score2), }, “similarity”: float(sim_score), “is_same_person”: sim_score > args.threshold, “threshold”: args.threshold } # 如果需要,可以打印JSON格式 import json print(json.dumps(result, indent=2))这样,其他程序就可以很方便地解析这个JSON字符串来获取所有信息了。
4. 测试修改后的脚本
修改完成后,最重要的一步就是测试,确保我们的改动没有引入错误,并且新功能工作正常。
4.1 使用默认图片测试
首先,运行最简单的命令,使用镜像自带的示例图片:
python inference_face.py如果一切正常,你应该能看到比原来更丰富的输出,除了相似度,还包括两张图片各自的人脸框坐标和检测置信度。
4.2 使用自定义图片测试
找两张包含人脸的图片,最好是正面、清晰的。使用绝对路径运行脚本:
python inference_face.py -i1 /path/to/your/photo1.jpg -i2 /path/to/your/photo2.jpg观察输出:
- 人脸框坐标是否合理?(是否准确地框住了人脸)
- 检测置信度是否在0到1之间?(通常越清晰、越正面,置信度越高,接近1)
- 相似度判断是否符合你的预期?
4.3 测试边界情况
尝试一些有挑战性的图片,检验程序的健壮性:
- 无人脸图片:输入一张风景照,程序应该给出明确的错误提示,而不是崩溃。
- 多人脸图片:输入一张合影。根据我们的修改,程序会选择置信度最高的那张脸。你需要确认这个逻辑是否符合你的应用场景。如果不符合,你可以回头修改
get_face_feature_and_box函数,改为选择面积最大((x2-x1)*(y2-y1))的脸,这与原镜像“检测最大人脸”的逻辑更一致。
5. 修改总结与应用建议
回顾一下,我们通过三个主要步骤增强了Retinaface+CurricularFace镜像的功能:
- 解剖流程:理解了原脚本“检测->对齐->特征提取->比对”的流水线,并定位了信息丢失的环节(检测结果未被保存)。
- 手术修改:修改了特征提取函数,使其在内部流转检测结果的同时,也将关键信息(坐标、置信度)返回给调用者。
- 丰富输出:在主逻辑中收集这些新信息,并以清晰、结构化(甚至机器可读的JSON)格式输出。
5.1 应用场景拓展
修改后的脚本,其应用场景得到了显著扩展:
- 考勤打卡+区域统计:不仅知道谁打卡了,还能知道他在监控画面的哪个区域出现最频繁,用于优化摄像头布局或分析热点区域。
- 身份核验质量控制:通过检测置信度,可以过滤掉质量太差的图片(如置信度低于0.9),要求用户重拍,从而提高核验的整体通过率和安全性。
- 人脸追踪初始化:在视频流中,第一帧检测到的人脸框和置信度可以作为人脸追踪算法的初始状态,实现检测与追踪的平滑衔接。
- 数据标注辅助:可以批量处理图片,输出人脸框坐标,为人脸数据集标注提供初步的、可供人工校验的参考线。
5.2 后续优化方向
如果你还想更进一步,这里有几个方向:
- 返回多张人脸信息:修改逻辑,返回图片中检测到的所有人脸(而不仅仅是置信度最高的那个)的特征、坐标和置信度,实现一张图片内的多人物识别。
- 集成可视化:使用OpenCV在原始图片上绘制出检测到的人脸框,并标注上置信度,将结果保存为新图片,直观又实用。
- 封装成API服务:使用Flask或FastAPI将修改后的推理代码包装成一个HTTP API服务,方便通过网络调用。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。