news 2026/3/24 19:48:22

Retinaface+CurricularFace实战教程:批量图片人脸比对脚本二次开发指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Retinaface+CurricularFace实战教程:批量图片人脸比对脚本二次开发指南

Retinaface+CurricularFace实战教程:批量图片人脸比对脚本二次开发指南

你是不是也遇到过这样的需求:手头有几百张员工照片,需要快速找出哪些人和入职登记照最相似?或者在安防系统里,要从监控截图中批量匹配已知人员?又或者想给自己的相册自动打上“家人”“朋友”“同事”的标签?这些场景背后,其实都指向同一个技术动作——人脸比对。

但市面上大多数教程只教你怎么比对两张图,真要处理几十上百张图片时,就卡在了“怎么批量跑”这一步。今天这篇实战指南,不讲原理、不堆参数,直接带你把官方单图比对脚本改造成真正能干活的批量处理工具。整个过程不需要重写模型,不用调参,只要懂一点Python基础,就能让脚本自己读图、配对、打分、输出结果——而且每一步都有可运行代码、真实报错提示和避坑建议。

我们用的底座是CSDN星图镜像广场上预装好的Retinaface+CurricularFace 人脸识别模型镜像。它已经帮你把环境、依赖、模型权重全配好了,连CUDA版本都对齐了,你唯一要做的,就是把“一次比两张”变成“一次比一百对”。


1. 镜像环境与能力再确认:别急着写代码,先看清手里的工具

在动手改脚本前,得先搞清楚这个镜像到底“能做什么”“不能做什么”。很多人改着改着发现结果不准,最后发现不是代码问题,而是对模型能力边界理解有偏差。

这个镜像不是万能的人脸识别黑盒,它由两个明确分工的模块组成:

  • RetinaFace:负责“找脸”。它能在一张图里精准定位多张人脸,并自动选出面积最大、最完整的一张作为后续处理对象。注意,它不抠图、不裁剪,只是框出位置并做仿射对齐。
  • CurricularFace:负责“认人”。它把对齐后的人脸转成512维特征向量,再用余弦相似度计算两张脸的匹配程度。得分越接近1,说明越可能是同一个人。

两者组合起来,就实现了“输入任意尺寸原图→自动检测最大人脸→提取特征→比对打分”的端到端流程。这也是为什么你不用提前用PS抠图,也不用担心图片旋转或缩放——模型自己会处理。

镜像里所有东西都放在/root/Retinaface_CurricularFace目录下,环境已预装好,无需额外配置。你可以用下面这条命令快速验证是否一切正常:

cd /root/Retinaface_CurricularFace && conda activate torch25 && python inference_face.py

如果终端输出类似Similarity score: 0.872 → Same person,并且没有报ModuleNotFoundErrorCUDA out of memory,说明环境完全就绪,可以进入下一步。

关键提醒:这个镜像默认只支持单张图对单张图比对,且每次只取每张图中的最大人脸。如果你的图片里有多张清晰正面脸(比如合影),它只会比对其中最大的那张——这点在批量处理前必须心里有数。


2. 从单次比对到批量处理:三步改造核心脚本

官方脚本inference_face.py的逻辑非常干净:读两张图 → 检测人脸 → 提取特征 → 计算相似度 → 打印结果。我们要做的,就是把它“复制粘贴”式地扩展成能循环处理文件夹的能力。

2.1 第一步:理解原始脚本结构,找到可复用的核心函数

打开/root/Retinaface_CurricularFace/inference_face.py,你会发现它主要由三部分构成:

  • get_face_embedding(image_path):输入图片路径,返回512维特征向量
  • calculate_similarity(embed1, embed2):输入两个向量,返回余弦相似度
  • main():解析命令行参数、调用上面两个函数、输出结果

真正需要我们动的,只有main()函数。其他两个函数已经封装好,稳定可靠,不要重写,不要魔改,直接复用

2.2 第二步:新增批量比对逻辑——支持三种常用模式

我们在原脚本末尾新增一个batch_compare()函数,支持以下三种实用场景:

  • 模式A:单图 vs 文件夹内所有图(例如:用一张标准照,去比对整个员工库)
  • 模式B:两个文件夹逐一对比(例如:昨天的考勤截图 vs 今天的,看谁没来)
  • 模式C:按CSV列表配对(例如:Excel里列了100组“待验人-标准照”路径,一行一组)

下面这段代码可以直接复制进inference_face.py文件底部(放在if __name__ == "__main__":之前):

import os import glob import csv from pathlib import Path def batch_compare(mode="folder_vs_folder", input1=None, input2=None, threshold=0.4, output_csv="batch_result.csv"): """ 批量人脸比对主函数 mode: "single_vs_folder", "folder_vs_folder", "csv_pairs" input1: 单图路径 / 文件夹路径 / CSV路径 input2: 文件夹路径(仅mode=="single_vs_folder"时使用) """ results = [] if mode == "single_vs_folder": # 单图 vs 文件夹:input1是单图,input2是文件夹 assert os.path.isfile(input1), f"input1 must be a file: {input1}" assert os.path.isdir(input2), f"input2 must be a folder: {input2}" ref_emb = get_face_embedding(input1) img_list = sorted(glob.glob(os.path.join(input2, "*.jpg")) + glob.glob(os.path.join(input2, "*.png"))) print(f"[Batch] Comparing {os.path.basename(input1)} against {len(img_list)} images...") for i, img_path in enumerate(img_list): try: emb = get_face_embedding(img_path) score = calculate_similarity(ref_emb, emb) is_same = "Same person" if score >= threshold else "Different person" results.append([os.path.basename(input1), os.path.basename(img_path), f"{score:.3f}", is_same]) if i % 20 == 0: print(f" Processed {i}/{len(img_list)}...") except Exception as e: results.append([os.path.basename(input1), os.path.basename(img_path), "ERROR", str(e)]) elif mode == "folder_vs_folder": # 两个文件夹:按文件名顺序一一配对(要求文件名一致,如 a.jpg vs a.jpg) assert os.path.isdir(input1) and os.path.isdir(input2) list1 = sorted(glob.glob(os.path.join(input1, "*.jpg")) + glob.glob(os.path.join(input1, "*.png"))) list2 = sorted(glob.glob(os.path.join(input2, "*.jpg")) + glob.glob(os.path.join(input2, "*.png"))) assert len(list1) == len(list2), f"Folder sizes differ: {len(list1)} vs {len(list2)}" print(f"[Batch] Comparing {len(list1)} image pairs...") for p1, p2 in zip(list1, list2): try: emb1 = get_face_embedding(p1) emb2 = get_face_embedding(p2) score = calculate_similarity(emb1, emb2) is_same = "Same person" if score >= threshold else "Different person" results.append([os.path.basename(p1), os.path.basename(p2), f"{score:.3f}", is_same]) except Exception as e: results.append([os.path.basename(p1), os.path.basename(p2), "ERROR", str(e)]) elif mode == "csv_pairs": # CSV格式:每行两列,第一列是图1路径,第二列是图2路径 with open(input1, 'r', encoding='utf-8') as f: reader = csv.reader(f) for row in reader: if len(row) < 2: continue p1, p2 = row[0].strip(), row[1].strip() if not (os.path.isfile(p1) and os.path.isfile(p2)): results.append([p1, p2, "MISSING", "File not found"]) continue try: emb1 = get_face_embedding(p1) emb2 = get_face_embedding(p2) score = calculate_similarity(emb1, emb2) is_same = "Same person" if score >= threshold else "Different person" results.append([os.path.basename(p1), os.path.basename(p2), f"{score:.3f}", is_same]) except Exception as e: results.append([os.path.basename(p1), os.path.basename(p2), "ERROR", str(e)]) # 写入CSV结果 with open(output_csv, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow(["Image1", "Image2", "Score", "Result"]) writer.writerows(results) print(f"[Done] Results saved to {output_csv} ({len(results)} rows)") return results

这段代码做了几件关键的事:

  • 自动识别图片路径(支持.jpg.png
  • 对每张图加了异常捕获,避免一张图出错导致整个批次中断
  • 每处理20张图就打印进度,防止你盯着屏幕干等
  • 结果统一导出为CSV,方便Excel打开筛选

2.3 第三步:添加命令行入口,让批量功能像原脚本一样好用

继续在文件末尾追加以下代码,让它能通过命令行直接调用:

if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="RetinaFace+CurricularFace Batch Face Comparison") parser.add_argument("--mode", type=str, default="folder_vs_folder", choices=["single_vs_folder", "folder_vs_folder", "csv_pairs"], help="Batch mode: single_vs_folder, folder_vs_folder, or csv_pairs") parser.add_argument("--input1", type=str, required=True, help="Path to image/file/folder/CSV") parser.add_argument("--input2", type=str, help="Second path (required for single_vs_folder and folder_vs_folder)") parser.add_argument("--threshold", type=float, default=0.4, help="Similarity threshold (default: 0.4)") parser.add_argument("--output", type=str, default="batch_result.csv", help="Output CSV filename") args = parser.parse_args() if args.mode == "single_vs_folder" and not args.input2: raise ValueError("--input2 is required for single_vs_folder mode") if args.mode == "folder_vs_folder" and not args.input2: raise ValueError("--input2 is required for folder_vs_folder mode") if args.mode == "csv_pairs" and args.input2: print("Warning: --input2 ignored in csv_pairs mode") batch_compare( mode=args.mode, input1=args.input1, input2=args.input2, threshold=args.threshold, output_csv=args.output )

保存文件后,你就可以用下面这些命令直接跑批量任务了:

# 模式A:用一张标准照,比对整个员工照片文件夹 python inference_face.py --mode single_vs_folder --input1 ./standard/employee_a.jpg --input2 ./employees/ # 模式B:两个考勤截图文件夹,按文件名一一比对(a_001.jpg vs b_001.jpg) python inference_face.py --mode folder_vs_folder --input1 ./day1/ --input2 ./day2/ # 模式C:按CSV里指定的100组路径配对(每行:/path/a.jpg,/path/b.jpg) python inference_face.py --mode csv_pairs --input1 ./pairs.csv

3. 实战调试与常见问题:别让小错误毁掉一整天

即使代码写对了,实际运行时也常遇到几个“意料之中”的坑。我把它们整理成对照表,帮你省下至少两小时排查时间。

问题现象根本原因快速解决方法
RuntimeError: CUDA out of memory一次性加载太多图片,显存爆了batch_compare()函数里,把glob.glob(...)改成list(glob.glob(...))[:50]先试前50张;或在循环里加torch.cuda.empty_cache()
ValueError: No face detected图片里没人脸,或人脸太小/太暗/侧脸严重先用原脚本python inference_face.py --input1 xxx.jpg单独测试这张图;确认可用后再加入批量任务
OSError: [Errno 2] No such file or directory路径含中文、空格或符号,Linux下容易出错把所有图片移到纯英文路径下(如/root/images/),或用os.path.abspath()处理路径
ImportError: cannot import name 'xxx'新增代码引用了未导入的模块在文件开头补上import glob,import csv,from pathlib import Path
CSV结果里全是ERROR某张图损坏或格式不支持except块里加一句print(f"Failed on {img_path}: {e}"),立刻定位哪张图有问题

还有一个隐藏但高频的问题:阈值设太高,导致大量“不同人”误判。官方默认0.4是在LFW数据集上平衡准确率和召回率的结果,但你的业务场景可能完全不同。比如:

  • 身份核验(银行开户):建议调到0.65以上,宁可拒真,不可认假
  • 家庭相册归类:0.35就够用,亲人之间特征相似度天然更高
  • 考勤打卡(固定摄像头):0.5~0.55最稳妥,兼顾光照变化和姿态差异

你可以用下面这个小脚本,快速画出当前数据集的分数分布,辅助选阈值:

# save as threshold_test.py import matplotlib.pyplot as plt from inference_face import get_face_embedding, calculate_similarity import glob # 取10张同一个人的不同照片 imgs = glob.glob("./same_person/*.jpg")[:10] embs = [get_face_embedding(p) for p in imgs] scores = [] for i in range(len(embs)): for j in range(i+1, len(embs)): scores.append(calculate_similarity(embs[i], embs[j])) plt.hist(scores, bins=20, alpha=0.7, label="Same person") plt.xlabel("Similarity Score") plt.ylabel("Count") plt.title("Score Distribution (Same Person)") plt.legend() plt.grid(True) plt.show()

运行后你会看到一个集中在0.7~0.9区间的峰——这就是你业务场景下的合理阈值区间。


4. 进阶技巧:让批量脚本更聪明、更省心

上面的脚本已经能干活了,但如果想让它真正融入你的工作流,还需要加点“小心机”。

4.1 自动过滤低质量图:跳过模糊、过暗、过曝的图片

get_face_embedding()调用前,加一段轻量级质量检查:

import cv2 import numpy as np def is_image_quality_ok(image_path, min_blur=100, min_brightness=30, max_brightness=220): """简单质量过滤:模糊度 + 亮度范围""" img = cv2.imread(image_path) if img is None: return False # 模糊度(拉普拉斯方差) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur_score = cv2.Laplacian(gray, cv2.CV_64F).var() # 亮度(灰度均值) brightness = np.mean(gray) return blur_score > min_blur and min_brightness < brightness < max_brightness # 在 batch_compare() 循环里调用: if not is_image_quality_ok(img_path): results.append([..., "SKIPPED", "Low quality"]) continue

这样,模糊的监控截图、手机随手拍的过暗照片,会直接被标记为SKIPPED,不参与比对,避免拖慢速度还拉低准确率。

4.2 输出带高亮的HTML报告:一眼看出哪些对得准、哪些要复查

把CSV结果转成带颜色的HTML页面,比对着Excel划线高效十倍:

def save_html_report(results, html_file="report.html"): html = """<html><head><style> table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ccc; padding: 8px; text-align: left; } .good { background-color: #d4edda; } .warn { background-color: #fff3cd; } .error { background-color: #f8d7da; } </style></head><body><h2>Face Comparison Report</h2> <table><tr><th>Image1</th><th>Image2</th><th>Score</th><th>Result</th></tr>""" for r in results: cls = "good" if r[2].startswith("0.") and float(r[2]) >= 0.6 else \ "warn" if r[2].startswith("0.") and 0.4 <= float(r[2]) < 0.6 else \ "error" html += f'<tr class="{cls}"><td>{r[0]}</td><td>{r[1]}</td><td>{r[2]}</td><td>{r[3]}</td></tr>' html += "</table></body></html>" with open(html_file, "w", encoding="utf-8") as f: f.write(html) print(f"HTML report saved to {html_file}") # 在 batch_compare() 结尾调用: save_html_report(results)

生成的网页里,绿色行是高置信度匹配(≥0.6),黄色是需人工确认(0.4~0.6),红色是明显不匹配或报错——打开浏览器就能快速决策。


5. 总结:你现在已经拥有了一个可落地的人脸比对流水线

回看一下,我们到底完成了什么:

  • 把官方单图比对脚本,扩展成支持三种批量模式的生产级工具
  • 每种模式都有完整命令行接口,无需改代码就能切换场景
  • 内置异常处理、进度提示、结果导出,拒绝“跑着跑着就没了”
  • 提供阈值调优方法、质量过滤、HTML可视化等进阶能力
  • 所有代码都在原镜像环境下直接运行,零环境配置成本

更重要的是,这个脚本不是一次性的Demo。它的结构清晰、职责分明,后续你可以轻松加上:

  • 自动上传结果到数据库(加几行sqlite3代码)
  • 邮件通知匹配成功(加smtplib
  • Web界面(用gradio包裹一下batch_compare函数)

人脸识别从来不是目的,解决具体问题才是。当你不再纠结“模型能不能识别人脸”,而是专注“怎么让识别结果真正用起来”,技术才算真正落地。

现在,就打开终端,cd 到/root/Retinaface_CurricularFace,激活环境,跑起你的第一批批量任务吧。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/23 20:45:43

零代码实现智能连招:GSE宏编译器从入门到精通

零代码实现智能连招&#xff1a;GSE宏编译器从入门到精通 【免费下载链接】GSE-Advanced-Macro-Compiler GSE is an alternative advanced macro editor and engine for World of Warcraft. It uses Travis for UnitTests, Coveralls to report on test coverage and the Curse…

作者头像 李华
网站建设 2026/3/24 14:17:45

WuliArt Qwen-Image Turbo商业实战:小红书/抖音/B站封面图风格统一化生成

WuliArt Qwen-Image Turbo商业实战&#xff1a;小红书/抖音/B站封面图风格统一化生成 1. 为什么封面图统一化是内容运营的隐形胜负手 你有没有遇到过这样的情况&#xff1a; 刚为小红书设计了一套清新胶片风的封面&#xff0c;转头给抖音做同主题视频时&#xff0c;却生成了赛…

作者头像 李华
网站建设 2026/3/23 12:57:35

Cosmos-Reason1-7B在Linux系统管理中的智能辅助

Cosmos-Reason1-7B在Linux系统管理中的智能辅助 如果你是一位Linux系统管理员&#xff0c;每天面对海量的日志、突发的故障和复杂的安全配置&#xff0c;是不是常常感觉分身乏术&#xff1f;排查一个服务异常&#xff0c;可能需要在几十个日志文件里大海捞针&#xff1b;分析一…

作者头像 李华
网站建设 2026/3/21 7:27:24

3大技术壁垒与5种突破路径:非凸碰撞检测全攻略

3大技术壁垒与5种突破路径&#xff1a;非凸碰撞检测全攻略 【免费下载链接】mujoco Multi-Joint dynamics with Contact. A general purpose physics simulator. 项目地址: https://gitcode.com/GitHub_Trending/mu/mujoco 非凸碰撞检测是物理引擎优化的核心挑战&#x…

作者头像 李华
网站建设 2026/3/15 13:33:32

BGE-Large-Zh场景应用:从论文查重到智能推荐

BGE-Large-Zh场景应用&#xff1a;从论文查重到智能推荐 你是否遇到过这样的问题&#xff1a;学生提交的课程论文&#xff0c;如何快速判断是否存在大段重复内容&#xff1f;客服团队每天收到上千条用户咨询&#xff0c;怎样在不读完全部文本的前提下&#xff0c;精准匹配知识…

作者头像 李华
网站建设 2026/3/24 5:18:51

3D Face HRN模型在Win11系统上的性能优化

3D Face HRN模型在Win11系统上的性能优化 如果你在Windows 11上跑过3D人脸重建模型&#xff0c;尤其是像HRN&#xff08;Hierarchical Representation Network&#xff09;这种追求高精度的模型&#xff0c;大概率会遇到过这样的场景&#xff1a;看着代码开始运行&#xff0c;…

作者头像 李华