3、人脸检测基础(下):基于深度学习的人脸检测方法
上一章我们聊了传统方法,说白了就是靠手工特征去猜人脸在哪。这一章咱们进入正题——深度学习时代的人脸检测。我个人习惯把这一章看作是「从规则驱动到数据驱动」的分水岭。你想想看,传统方法遇到大角度、强遮挡、暗光环境基本就歇菜了,但深度学习模型却能扛住大部分极端场景。
3.1 MTCNN:多任务级联卷积网络
MTCNN 是我最早在生产环境中大规模使用的深度学习人脸检测器。2016 年提出,到现在依然有大量嵌入式项目在用。为什么?因为它轻量、快速、且检测+对齐一步到位。
3.1.1 网络结构:三段式级联
MTCNN 的核心思路是「由粗到精」。它把检测拆成三个网络,串行执行:
- P-Net(Proposal Network):快速生成候选框。输入 12×12 的图像块,输出人脸置信度和边界框回归值。这一步会扫一遍图像金字塔,产生大量候选框。
- R-Net(Refine Network):精筛候选框。输入 24×24,比 P-Net 多了一层全连接,能更准确地区分人脸和非人脸。
- O-Net(Output Network):输出最终结果。输入 48×48,除了边界框回归,还会输出 5 个关键点坐标(左眼、右眼、鼻子、左嘴角、右嘴角)。
我在项目中遇到过一个问题:P-Net 的阈值设得太低,候选框数量爆炸,导致 R-Net 和 O-Net 处理不过来。后来我把 P-Net 的阈值从 0.6 提到 0.7,候选框数量直接砍掉一半,速度提升明显,召回率只掉了不到 1%。
关键参数速查表
| 网络层 | 输入尺寸 | 主要作用 | 输出 |
|---|---|---|---|
| P-Net | 12×12 | 快速生成候选框 | 置信度 + 4 个边界框偏移 |
| R-Net | 24×24 | 精筛候选框 | 置信度 + 4 个边界框偏移 |
| O-Net | 48×48 | 输出最终结果 + 关键点 | 置信度 + 4 个边界框偏移 + 10 个关键点坐标 |
3.1.2 损失函数与训练策略
MTCNN 的损失函数由三部分组成:
- 人脸分类损失:交叉熵损失,判断是不是人脸。
- 边界框回归损失:欧氏距离损失,修正候选框位置。
- 关键点定位损失:欧氏距离损失,只对包含关键点标注的样本计算。
训练时有个小技巧:不同任务使用不同权重的样本。比如 O-Net 训练时,关键点定位的样本只占一小部分,但损失权重可以设高一些,不然关键点学不好。
避坑指南
我曾经在训练 MTCNN 时发现关键点定位一直不准,后来排查发现是数据增强做得不够。人脸检测的数据增强不能只做随机裁剪和翻转,还要加入光照变化和模糊模拟。尤其是跨摄像头场景,不同摄像头的曝光和焦距差异很大,不做增强的话模型泛化能力会很差。
3.2 RetinaFace:单阶段高精度检测器
如果说 MTCNN 是「轻量级选手」,那 RetinaFace 就是「精度怪兽」。它基于特征金字塔和单阶段检测框架,在 WIDER Face 数据集上长期霸榜。我个人在安防项目中用过 RetinaFace,效果确实比 MTCNN 好一截,但代价是计算量翻了好几倍。
3.2.1 核心设计思路
RetinaFace 有几个关键创新点:
- 特征金字塔(FPN):从不同尺度的特征图上检测人脸,解决多尺度问题。小脸用浅层特征,大脸用深层特征。
- 上下文模块(SSH):在特征图上增加感受野,让模型能利用人脸周围的上下文信息。比如肩膀、头发这些区域,对人脸检测有辅助作用。
- 密集回归分支:除了边界框和关键点,还额外回归了 3D 人脸参数(姿态角、形状等)。不过这个分支在工程中我一般会关掉,因为计算量太大,收益有限。
3.2.2 与 MTCNN 的对比
我整理了一张对比表,方便你快速决策:
| 对比维度 | MTCNN | RetinaFace |
|---|---|---|
| 网络结构 | 三段级联,串行处理 | 单阶段,端到端 |
| 输入尺寸 | 图像金字塔 + 12/24/48 | 固定输入(如 640×640) |
| 关键点输出 | 5 个关键点 | 5 个关键点 + 可选 3D 参数 |
| 小脸检测 | 一般,依赖图像金字塔 | 优秀,依赖 FPN |
| 推理速度(GPU) | 快(~10ms/帧) | 中等(~30ms/帧) |
| 推理速度(CPU) | 较快(~50ms/帧) | 慢(~200ms/帧) |
| 模型大小 | ~2MB | ~50MB |
| 适用场景 | 嵌入式、移动端、实时 | 服务器端、高精度要求 |
注意
RetinaFace 的官方实现默认使用 ResNet-50 作为 backbone,显存占用较高。如果你在边缘设备上部署,可以考虑使用 MobileNet 版本的 RetinaFace,精度会下降一些,但速度能提升 3-5 倍。
3.3 工程选型建议
选型这件事,没有银弹。我根据实际项目经验,给出几条建议:
3.3.1 场景一:嵌入式/移动端实时检测
- 推荐方案:MTCNN(轻量版)或 MobileNet-SSD
- 理由:模型小、推理快、不需要 GPU。我在树莓派 4B 上跑过 MTCNN,720P 视频能到 15fps,基本够用。
- 注意:小脸检测能力弱,摄像头安装距离不宜过远。
3.3.2 场景二:服务器端高精度检测
- 推荐方案:RetinaFace(ResNet-50)或 DSFD
- 理由:精度高,能处理大角度、遮挡、暗光等复杂场景。我在安防项目中用 RetinaFace 做离线批量检测,召回率能到 98% 以上。
- 注意:需要 GPU 加速,显存建议 8GB 以上。
3.3.3 场景三:跨摄像头追踪中的检测
- 推荐方案:RetinaFace + 轻量级跟踪器(如 SORT)
- 理由:跨摄像头场景下,人脸尺度变化大、光照不一致。RetinaFace 的 FPN 结构能更好地处理多尺度问题。检测 + 跟踪的组合可以降低检测频率,提升整体效率。
- 注意:不同摄像头的白平衡和曝光参数不同,建议在检测前做简单的图像预处理(直方图均衡化或自适应亮度调整)。
我的个人经验
如果你刚开始做人脸检测项目,我建议先从 MTCNN 入手。它简单、稳定、文档多,踩坑成本低。等你对检测流程熟悉了,再切换到 RetinaFace 追求更高精度。别一上来就上最复杂的模型,否则调试起来会很痛苦。
3.4 本章知识结构图
下面这张图梳理了 MTCNN 和 RetinaFace 的核心流程与选型逻辑:
3.5 代码示例:MTCNN 快速上手
这里给一个 MTCNN 的推理代码示例,用的是我常用的 mtcnn 库(基于 TensorFlow 后端):
from mtcnn import MTCNN
import cv2
# 初始化检测器
detector = MTCNN()
# 读取图像
img = cv2.imread('group_photo.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 执行检测
results = detector.detect_faces(img_rgb)
# 遍历结果
for result in results:
# 边界框
x, y, w, h = result['box']
confidence = result['confidence']
# 关键点
keypoints = result['keypoints']
left_eye = keypoints['left_eye']
right_eye = keypoints['right_eye']
nose = keypoints['nose']
mouth_left = keypoints['mouth_left']
mouth_right = keypoints['mouth_right']
# 绘制
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.circle(img, left_eye, 2, (0, 0, 255), -1)
cv2.circle(img, right_eye, 2, (0, 0, 255), -1)
cv2.circle(img, nose, 2, (0, 0, 255), -1)
cv2.circle(img, mouth_left, 2, (0, 0, 255), -1)
cv2.circle(img, mouth_right, 2, (0, 0, 255), -1)
cv2.imshow('Detection', img)
cv2.waitKey(0)
小提示
如果你用的是 PyTorch,推荐 facenet-pytorch 库,它包含了 MTCNN 的实现,而且支持 GPU 加速。安装命令:pip install facenet-pytorch。用法和上面类似,但返回的是张量,方便后续接入人脸识别流程。
3.6 本章小结
MTCNN 和 RetinaFace 代表了两种不同的设计哲学:一个追求轻量和实时,一个追求精度和鲁棒性。选哪个,取决于你的硬件条件、场景要求和性能指标。我个人建议:先跑通 MTCNN 做 baseline,再根据实际效果决定是否升级到 RetinaFace。别一上来就追求最先进的模型,工程落地讲究的是「够用就好」。