RetinaFace人脸检测实战:如何导出检测框坐标与关键点坐标的CSV表格
你是不是也遇到过这样的问题:用RetinaFace跑完人脸检测,看到图上画出了漂亮的检测框和五个红点,但真正想拿这些数据做后续分析时——却发现结果只存在图片里,坐标信息根本没保存下来?别急,这篇实战指南就带你从“看得见”走向“拿得到”,手把手教你把每张图里的人脸位置、检测框坐标、五个关键点坐标全部导出成标准CSV表格,方便Excel打开、Python读取、数据库入库,真正实现结构化数据输出。
这不是一个泛泛而谈的模型介绍,而是一份聚焦工程落地的实操手册。我们不讲FPN多层特征融合的数学推导,也不堆砌论文里的指标曲线,只解决你此刻最关心的一个动作:怎么把屏幕上显示的那些点和框,变成一行行可计算、可统计、可复用的数字?全程基于CSDN星图镜像广场提供的「RetinaFace 人脸检测与关键点绘制」预置镜像,开箱即用,无需编译、不用配环境,连conda环境都已为你激活好——你只需要知道哪几行代码改一下,就能让模型不仅“画出来”,还能“吐出来”。
1. 为什么默认不导出坐标?先搞懂脚本在做什么
在动手改代码之前,得先明白当前镜像里那个inference_retinaface.py到底干了什么。它不是黑盒,而是一段清晰、简洁、高度可读的推理逻辑。理解它,是安全修改的第一步。
1.1 原始脚本的核心流程
打开/root/RetinaFace/inference_retinaface.py,你会发现整个流程非常线性:
- 加载模型:从ModelScope自动下载并加载
iic/cv_resnet50_face-detection_retinaface; - 读图预处理:将输入图片转为Tensor,归一化,送入模型;
- 模型前向推理:得到原始输出——一个包含多个人脸的列表,每个元素是一个字典,含
bbox(4维数组:[x1, y1, x2, y2])、kps(5×2数组:[[x1,y1], [x2,y2], ..., [x5,y5]])和score(置信度); - 可视化绘制:调用OpenCV,在原图上画矩形框、画圆点、标分数;
- 保存图片:把画完的图存进
face_results/文件夹。
关键就在这里:第3步拿到的bbox和kps是纯内存里的Python对象,但脚本到第4步就直接丢给绘图函数了,再没做任何格式化保存。它的设计初衷是“快速验证效果”,而不是“交付结构化数据”。
1.2 坐标数据长什么样?举个真实例子
假设你用一张单人正脸图测试,模型返回的第一个检测结果可能是这样:
{ 'bbox': [128.3, 95.7, 312.6, 289.4], # 左上x, 左上y, 右下x, 右下y 'kps': [ [172.1, 143.5], # 左眼中心 [245.8, 142.9], # 右眼中心 [209.3, 198.2], # 鼻尖 [178.6, 231.7], # 左嘴角 [240.2, 232.1] # 右嘴角 ], 'score': 0.987 }这组数字就是你要的全部信息。bbox是检测框的精确像素坐标;kps是五个关键点的二维坐标。它们天然就是结构化数据,只需稍作整理,就能写入CSV。
2. 动手改造:三步添加CSV导出功能
我们不需要重写整个脚本,只需在原有逻辑中插入三个轻量级模块:数据收集 → 表头定义 → 文件写入。所有改动都在同一个文件内完成,不影响原有绘图功能,完全向后兼容。
2.1 第一步:在推理循环中收集所有坐标数据
找到脚本中遍历检测结果的for i, det in enumerate(detections):循环(通常在绘图代码之前)。在循环内部,新增一个列表用于暂存每张图的每张脸数据:
# 在 import 语句下方或 main() 函数开头,添加: import csv import os # 在 detections = model(...) 之后、绘图循环之前,初始化空列表: all_face_data = [] # 用于收集所有图片的所有人脸数据 # 找到绘图循环,例如: for i, det in enumerate(detections): bbox = det['bbox'] kps = det['kps'] score = det['score'] # 新增:将当前这张脸的所有坐标打包成一行数据 face_row = { 'image_name': os.path.basename(args.input) if os.path.isfile(args.input) else 'url_image', 'face_id': i + 1, 'score': f"{score:.4f}", 'bbox_x1': f"{bbox[0]:.2f}", 'bbox_y1': f"{bbox[1]:.2f}", 'bbox_x2': f"{bbox[2]:.2f}", 'bbox_y2': f"{bbox[3]:.2f}", 'left_eye_x': f"{kps[0][0]:.2f}", 'left_eye_y': f"{kps[0][1]:.2f}", 'right_eye_x': f"{kps[1][0]:.2f}", 'right_eye_y': f"{kps[1][1]:.2f}", 'nose_x': f"{kps[2][0]:.2f}", 'nose_y': f"{kps[2][1]:.2f}", 'left_mouth_x': f"{kps[3][0]:.2f}", 'left_mouth_y': f"{kps[3][1]:.2f}", 'right_mouth_x': f"{kps[4][0]:.2f}", 'right_mouth_y': f"{kps[4][1]:.2f}", } all_face_data.append(face_row)这段代码的作用,是把每张检测到的人脸,按字段名(如bbox_x1,left_eye_x)组织成一个字典,并追加到all_face_data列表中。注意:我们用了f"{x:.2f}"统一保留两位小数,既保证精度又提升可读性。
2.2 第二步:定义CSV表头并创建输出文件
在绘图循环结束后、图片保存之前,加入CSV写入逻辑:
# 新增:定义CSV表头(顺序必须与 face_row 字段一致) csv_headers = [ 'image_name', 'face_id', 'score', 'bbox_x1', 'bbox_y1', 'bbox_x2', 'bbox_y2', 'left_eye_x', 'left_eye_y', 'right_eye_x', 'right_eye_y', 'nose_x', 'nose_y', 'left_mouth_x', 'left_mouth_y', 'right_mouth_x', 'right_mouth_y' ] # 新增:构造CSV文件路径(与 face_results 同级,避免混淆) csv_output_path = os.path.join(os.path.dirname(args.output_dir), "detection_results.csv") # 新增:写入CSV with open(csv_output_path, 'w', newline='', encoding='utf-8') as f: writer = csv.DictWriter(f, fieldnames=csv_headers) writer.writeheader() writer.writerows(all_face_data) print(f"[INFO] CSV results saved to: {csv_output_path}") print(f"[INFO] Total faces detected: {len(all_face_data)}")这里的关键点:
csv_output_path被设为与face_results/同级目录,比如你用-d /root/workspace/output_detect,CSV就会生成在/root/workspace/detection_results.csv,逻辑清晰不打架;- 使用
csv.DictWriter直接写入字典列表,无需手动拼接字符串,安全又简洁; - 最后两行打印提示,让你一眼确认是否成功、检测了多少张脸。
2.3 第三步:支持批量图片输入(可选但强烈推荐)
默认脚本只支持单张图(--input),但实际工作中你往往有一整个文件夹的图片。我们只需加几行代码,就能让它批量处理:
# 在参数解析部分(argparse.ArgumentParser),添加新参数: parser.add_argument('--input_dir', '-id', type=str, default=None, help='Directory containing input images (e.g., ./test_images). If provided, --input is ignored.') # 在主逻辑中,替换原来的单图读取逻辑: if args.input_dir and os.path.isdir(args.input_dir): # 批量处理目录下所有 .jpg/.png 图片 image_files = [os.path.join(args.input_dir, f) for f in os.listdir(args.input_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))] print(f"[INFO] Found {len(image_files)} images in {args.input_dir}") else: # 单图模式(保持原有逻辑) image_files = [args.input]然后,把原来处理args.input的整段逻辑(从读图到推理到绘图到写CSV),放进一个for img_path in image_files:循环里即可。这样,一条命令就能处理上百张图,结果统一汇总到一个CSV里。
3. 运行与验证:亲眼看到坐标变成表格
完成上述三步修改后,保存文件。现在,让我们用一次完整的命令来验证效果。
3.1 单图测试:快速确认功能生效
cd /root/RetinaFace conda activate torch25 python inference_retinaface.py --input /root/RetinaFace/test.jpg执行完毕后,除了看到face_results/test.jpg这张带框和点的图,你还会在/root/RetinaFace/目录下发现一个新文件:detection_results.csv。
用head -n 5 detection_results.csv查看前五行:
image_name,face_id,score,bbox_x1,bbox_y1,bbox_x2,bbox_y2,left_eye_x,left_eye_y,right_eye_x,right_eye_y,nose_x,nose_y,left_mouth_x,left_mouth_y,right_mouth_x,right_mouth_y test.jpg,1,0.9870,128.30,95.70,312.60,289.40,172.10,143.50,245.80,142.90,209.30,198.20,178.60,231.70,240.20,232.10成功!所有坐标都以标准CSV格式整齐排列,Excel双击即可打开,Pandas一行pd.read_csv("detection_results.csv")就能加载分析。
3.2 批量处理:处理一个文件夹的实战效果
假设你把100张员工证件照放在/root/workspace/id_photos/下:
python inference_retinaface.py --input_dir /root/workspace/id_photos/ --output_dir /root/workspace/id_results/ --threshold 0.7几秒钟后:
/root/workspace/id_results/里有100张带检测框的图;/root/workspace/detection_results.csv里有100行(或更多,如果有人脸多张)结构化数据;- 每行对应一个人脸,字段完整,可直接用于统计平均脸宽、关键点偏移分析、质量筛选等。
4. 进阶技巧:让CSV更实用、更专业
导出只是第一步。为了让这份CSV真正成为你的生产力工具,这里分享几个工程师日常都在用的小技巧。
4.1 添加“人脸宽高比”和“关键点间距”衍生字段
在face_row构建阶段,可以顺手计算一些业务强相关的衍生指标:
# 在 face_row = {...} 之后,新增: bbox_w = bbox[2] - bbox[0] bbox_h = bbox[3] - bbox[1] eye_dist = ((kps[1][0] - kps[0][0])**2 + (kps[1][1] - kps[0][1])**2)**0.5 face_row.update({ 'bbox_width': f"{bbox_w:.2f}", 'bbox_height': f"{bbox_h:.2f}", 'bbox_aspect_ratio': f"{bbox_w/bbox_h:.3f}", 'inter_eye_distance': f"{eye_dist:.2f}", })这样,CSV里就多了bbox_aspect_ratio(判断是否侧脸)、inter_eye_distance(用于标准化关键点)等字段,省去后续用Excel或Python再算一遍的麻烦。
4.2 按置信度自动过滤,只保留高质量结果
如果你只关心高置信度检测,可以在写入前加一道过滤:
# 替换原来的 writer.writerows(all_face_data) high_conf_faces = [row for row in all_face_data if float(row['score']) >= 0.85] writer.writerows(high_conf_faces) print(f"[INFO] Wrote {len(high_conf_faces)} high-confidence faces (score >= 0.85)")4.3 导出为Excel(.xlsx)而非CSV(可选)
如果团队习惯用Excel且需要多Sheet,可安装openpyxl并替换写入逻辑:
pip install openpyxl然后用from openpyxl import Workbook创建工作簿,把不同图片的结果写入不同Sheet,甚至加上图表——这已超出本文范围,但方向明确:CSV是起点,不是终点。
5. 总结:从“看见”到“拥有”,才是AI落地的真正开始
回顾整个过程,我们没有碰触模型权重,没有重写网络结构,甚至没有安装新库。我们只是读懂了一段已有代码的意图,在它最自然的数据出口处,轻轻接上一根“导出管道”。这恰恰体现了工程化思维的核心:不追求炫技,而专注打通最后一公里。
通过这篇实战,你现在应该已经掌握:
- 定位能力:能快速找到推理脚本中坐标数据的原始来源(
detections列表); - 改造能力:能在不破坏原有功能的前提下,安全地插入数据收集与导出逻辑;
- 扩展能力:能根据业务需求,灵活添加衍生字段、过滤条件、批量处理等实用特性。
更重要的是,你获得了一种可迁移的方法论:无论下次面对的是YOLO的边界框、SAM的分割掩码,还是Stable Diffusion的潜变量,只要数据在内存里,你就拥有了把它结构化、持久化、产品化的主动权。
技术的价值,从来不在模型有多深,而在于你能否把它变成手边可用的工具。今天,你已经迈出了最关键的一步。
--- > **获取更多AI镜像** > > 想探索更多AI镜像和应用场景?访问 [CSDN星图镜像广场](https://ai.csdn.net/?utm_source=mirror_blog_end),提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。