SCRFD人脸检测器选型全解:0.5GF起步、3.6ms出框,一篇讲透从原理到部署
【免费下载链接】insightfaceState-of-the-art 2D and 3D Face Analysis Project项目地址: https://gitcode.com/GitHub_Trending/in/insightface
支付摄像头前,近处的人脸占满半个画面,远处的人脸只剩十几像素。实时人脸检测要在几十毫秒内把每张脸都框出来,还要给出 5 个关键点,否则下游对齐、识别全部跑偏。InsightFace 的 SCRFD(Sample and Computation Redistribution,样本与计算再分配,ICLR 2022 收录)就是为此设计的:从 0.5GF 到 34GF(GF 即 GigaFLOPs,计算量档位)的五个规格,覆盖手机到服务器的全部部署场景。本文带你完成理解原理、选对规格、跑通推理三件事。
🎯 到底难在哪
核心矛盾是一条尺度跨度:大脸区域脸多、位置稳定,每个特征点都算一遍是浪费;小脸区域脸少而密,特征稀疏时稍有遗漏就漏检。传统检测器在所有位置花同样的算力,两头都不讨好。
SCRFD 的解法是像排班一样重新分配资源:市中心店铺密集,不必每个路口都站岗;郊区人口稀疏,反而要加密巡逻。具体落到三个设计要点:
- 计算再分配:骨干浅层通道多、算得密,深层通道少、算得省,算力压在定位贡献最大的浅层;
- 样本再分配:训练时不靠人工 IoU 阈值挑正样本,由分配器按邻域统计量自动判定;
- 头部共享:分类与回归头共用卷积,砍掉一半头部的 FLOPs。
🔬 三个关键机制
ATSS:让正负样本自己长出来
这个机制解决"小脸正样本被固定阈值误杀"的问题。配置里只声明分配器类型和 topk 数:
# detection/scrfd/configs/scrfd/scrfd_2.5g.py train_cfg=dict( assigner=dict(type='ATSSAssigner', topk=9)) # 邻域内按中心距离取前9个候选ATSSAssigner 在 detection/scrfd/mmdet/core/bbox/assigners/atss_assigner.py 中的实现:对每张真值脸,在每个金字塔层取中心距离最近的 topk 个 anchor 做候选,计算这些候选的 IoU 均值和标准差,用均值加标准差当动态阈值,高于它的才算正样本。阈值随图像局部尺度自适应,不再写死一个 0.5。
不这样做,密集小脸场景里大量本该算正样本的 anchor 会因 IoU 略低于阈值被判成负样本,模型被反复教"这里没有脸",Hard 子集精度直接受损。
共享头加双尺度 anchor:小脸在浅层就能被接住
这个机制解决"小脸需要密集特征、但算力预算有限"的问题。关键配置:
# detection/scrfd/configs/scrfd/scrfd_2.5g.py bbox_head=dict( type='SCRFDHead', cls_reg_share=True, # 分类/回归共用卷积,头部FLOPs减半 anchor_generator=dict( ratios=[1.0], scales=[1, 2], # 每个位置两个尺度候选 base_sizes=[16, 64, 256], strides=[8, 16, 32])) # 三层特征金字塔三个数各管一摊:base_sizes=[16, 64, 256]对应三个 stride,覆盖小、中、大脸;scales=[1, 2]让每个特征位置有两个尺度候选,十几像素的小脸在 stride=8 的浅层就有合适的 anchor 接住;cls_reg_share=True则把分类和回归从两组卷积压成一组,小模型的推理延迟对头部 FLOPs 极其敏感,这一步省得实在。
不共享的话,同等主干下头部算力翻倍,0.5GF 档位根本塞不进手机实时预算;没有双尺度 anchor,小脸只能靠更深、更稀疏的特征层,漏检率会明显上升。
推理端 anchor 缓存:同一尺寸只算一次
这个机制解决"每帧重复计算锚点中心坐标"的隐性开销。ONNX 导出默认走动态输入,也可固定形状后交给 onnx-simplifier 优化:
# 固定640x640导出,可再经onnx-simplifier优化 python tools/scrfd2onnx.py configs/scrfd/scrfd_2.5g.py model.pth --shape 640 640tools/scrfd2onnx.py 不传--shape时按 640x640 建图并导出动态 shape 的 ONNX;传了则输出固定形状文件。Python 推理端 tools/scrfd.py 用center_cache按(高, 宽, stride)做键缓存锚点中心坐标表,同一输入尺寸从第二次推理起直接命中,不再重建。
不缓存的话,每帧都要为三层特征图各算一遍 mgrid 坐标数组,高频调用时这部分纯浪费的开销会被放大。
📊 规格怎么选:五档对号入座
下表来自 detection/scrfd/README.md 的 Pretrained-Models 表:mAP 为 WIDERFace 单尺度、VGA 分辨率评测,Infer 为 640x640 输入的单帧延迟。
| 规格 | 参数量(M) | Infer(ms) | Easy | Medium | Hard |
|---|---|---|---|---|---|
| SCRFD_500M | 0.57 | 3.6 | 90.57 | 88.12 | 68.51 |
| SCRFD_1G | 0.64 | 4.1 | 92.38 | 90.57 | 74.80 |
| SCRFD_2.5G | 0.67 | 4.2 | 93.78 | 92.16 | 77.87 |
| SCRFD_10G | 3.86 | 4.9 | 95.16 | 93.87 | 83.05 |
| SCRFD_34G | 9.80 | 11.7 | 96.06 | 94.92 | 85.29 |
如果你的场景是手机 App 或嵌入式端侧,选 SCRFD_500M,因为 0.57M 参数、3.6ms 档位延迟是端侧唯一能同时满足实时和占用的选项。如果你的场景是服务器单路实时、精度预算中等,选 SCRFD_2.5G,因为 Hard 77.87 比 500M 高近 10 个点,延迟只多 0.6ms,性价比最高。如果你的场景是安防或支付这类高精度离线服务,选 SCRFD_10G,因为 3.86M 参数换 Hard 83.05,4.9ms 的延迟在离线链路里完全不构成瓶颈。
两个可核对的实测数据点。其一,端侧 CPU 单线程(AMD Ryzen 9 3950X、OMP_NUM_THREADS=1):500M 规格在 640x480 输入下 28.3ms,降到 320x240 只要 11.4ms——速度不够时先降输入尺寸,再考虑换更小模型。其二,同条件下 RetinaFace MobileNet-0.25 版本 Hard 精度 47.32、延迟 7.9ms,可见 SCRFD 在小档位的精度优势来自算法本身而非参数堆料。
🚀 跑通最小路径:四步出框
# 1. 克隆仓库 git clone https://gitcode.com/GitHub_Trending/in/insightface # 2. 安装推理依赖 pip install onnxruntime opencv-python numpy # 3. 进入推理目录,按README的Pretrained-Models表下载 scrfd_2p5gkps.onnx 放到当前目录 cd insightface/detection/scrfd # 4. 运行(需准备一张测试图,默认读 tests/data/t3.jpg) python tools/scrfd.py权重下载位置在 detection/scrfd/README.md 的 Pretrained-Models 表里,_KPS后缀的权重带 5 个关键点输出。tools/scrfd.py的__main__里已内置加载、prepare(-1)、detect(img, 0.5)和可视化保存的完整流程,照着改图片路径即可。
如果卡住了,对照三条排查项:
detect 直接抛 AssertionError→ ONNX 模型导出时固定了 shape,detect()却没传input_size,内部断言input_size is not None or self.input_size is not None失败 → 显式传入detect(img, 0.5, input_size=(640, 640))。
关键点全为空→ 你拿的是不带关键点的权重,use_kps由输出头数量自动推断,6/10 个输出头无关键点,9/15 个才有 → 换_KPS后缀的权重重新下载。
框的位置整体偏移或缩放不对→ 在返回结果上又手动除了一次缩放系数 →detect()返回的框已映射回原图坐标系,直接使用即可。
⚙️ 上生产前:三端各一条关键配置
CPU:ONNX Runtime 多核跑法,关键配置是在set_providers之外设置intra_op_num_threads等于物理核数。官方单线程数字(上表 28.3ms)只作基线,多线程下同一张 500M 模型能压到一半以下。
GPU:把 python-package/insightface/model_zoo/scrfd.py 里prepare(ctx_id)走的CPUExecutionProvider换成CUDAExecutionProvider,ctx_id传 GPU 设备号。注意 SCRFD 本身 FLOPs 不高,GPU 上的收益主要在批量并发,单帧收益有限。
端侧:仓库 README 的 Demo 一节列了 ncnn、MNN、TNN 等 C++ 推理实现参考,500M 规格是端侧首选,FP16 或 INT8 量化可再压一档延迟。改模型精度前先回归 Hard 子集,量化对小脸分数影响比框坐标更敏感。
🔗 它在整条链路里的位置
SCRFD 是 InsightFace 整条链路的"前端":输出人脸框和 5 个关键点,下游 ArcFace 识别器用这 5 点做仿射对齐(把脸摆正到统一姿态再提特征),3D 重建模块则直接消费检测框作为输入。框不准,后面全歪。
两条可直接跑通的路径:examples/demo_analysis.py 演示"检测 → 对齐 → 识别/属性"的完整组装;python-package/insightface/app/face_analysis.py 是这套组装方式的库实现。
这张多人照片来自 python-package 自带测试数据,恰好覆盖近处大脸和侧脸,适合当 SCRFD 的首张测试图。
⚠️ 边界与延伸
SCRFD 适合静态图和逐帧视频,对密集小脸和中等遮挡的鲁棒性在同类轻量检测器里算强,但对极端侧脸、严重遮挡没有额外保障,这类场景需要配合姿态估计或活体检测模块,到这儿就该换工具了。
想定制自己的 FLOPs 档位,从 detection/scrfd/search_tools/ 入手,里面是论文中两步网络结构搜索的完整脚本;导出细节看 detection/scrfd/tools/scrfd2onnx.py。多档位权重方面,_KPS版本与同档非关键点版相比:2.5G 规格 Easy 93.80 对 93.78,Hard 77.13 对 77.87,延迟 4.3ms 对 4.2ms——需要关键点时这个取舍基本无成本,精度差不到 1 个点。
【免费下载链接】insightfaceState-of-the-art 2D and 3D Face Analysis Project项目地址: https://gitcode.com/GitHub_Trending/in/insightface
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考