使用OpenCV内置深度学习人脸模块,几行代码轻松完成人脸检测和识别
作者:冯远滔(OpenCV China),王成瑞(北京邮电大学),钟瑶瑶(北京邮电大学)
最新发布的OpenCV 4.5.4版本收录了一个基于深度学习神经网络的人脸模块(以下称“OpenCV DNN Face”),包括人脸检测(使用模型YuNet,由OpenCV China团队贡献)和人脸识别(使用模型SFace,由北京邮电大学邓伟洪教授课题组贡献)。
使用OpenCV DNN Face的API,只需几行代码便可以完成整个人脸检测和人脸识别处理,极大的方便了开发。
# 人脸检测
img = cv.imread('path/to/image')
faceDetector = cv.FaceDetectorYN.create('/path/to/model', '', img.shape[:2])
faces = faceDetector.detect(image)
# 人脸识别
recognizer = cv.FaceRecognizerSF.create(recog_model_path, '' )
aligned_face = recognizer.alignCrop(img, faces[1][0])
feature = recognizer.feature(aligned_face)
cosine_score = recognizer.match(feature1, feature2, 0)
在相应的数据集上对OpenCV DNN Face的两个模型分别进行测试:
人脸检测模型YuNet在WIDER Face数据集的验证集中达到了0.856(AP_easy),0.842(AP_medium),0.727(AP_hard);
人脸识别模型SFace在LFW数据集上达到了99.60%的准确率 (https://docs.opencv.org/master/d0/dd4/tutorial_dnn_face.html中有更详细的测试结果,以及此人脸模块的一些使用说明)。
根据OpenCV最新公开的OpenCV Zoo(https://github.com/opencv/opencv_zoo)的基准测试结果:
当输入图像尺寸为160x120时,YuNet在i7-5930K@3.50GHz上达到689FPS,在树莓派4B上可达160FPS;
SFace在输入图像尺寸为112x112,同样的平台下分别为115FPS和10FPS。
YuNet仅为337K,SFace为37MB,二者均为轻量模型,非常适合移动端应用。
OpenCV DNN Face的实现代码:
https://github.com/opencv/opencv/tree/master/modules/objdetect
C++和Python的例子face_detect和face_match:
https://github.com/opencv/opencv/tree/master/samples/dnn
下面笔者分别介绍一下OpenCV DNN Face中的人脸检测和人脸识别。
OpenCV基于深度学习的人脸检测FaceDetectorYN
人脸检测是返回一张图像中人脸的位置(x1, y1)和大小(w, h),如下图所示(原图片来自于WIDER Face数据集)。
YuNet是广受好评的人脸检测库libfacedetection所使用的模型。它的网络结构设计参考了经典的SSD[1]以及MobileNet[2],其结构示意图可以在
https://netron.app/?url=https://raw.githubusercontent.com/ShiqiYu/libfacedetection.train/master/tasks/task1/onnx/yunet.onnx查看。
YuNet是基于anchor的人脸检测器,分别在4种尺度的特征图上生成正方形anchor,可检测的最小人脸是10x10,最大人脸是256x256。参考MobileNet,我们使用了Depth-wise卷积代替部分卷积,从而减少模型参数和模型体积。我们使用人脸检测数据集WIDER Face[3]作为训练和测试数据集,使用EIoU损失函数[4]作为训练YuNet的损失函数,从而提高对人脸的定位能力。除了人脸的定位之外,我们还额外标注了人脸的5个关键点(左右眼,鼻尖,左右嘴角),为模型增加了关键点检测的多任务能力。
上图展示了FaceDetectorYN的使用流程。如果输入图像的尺寸是一致的,例如视频输入流,那么实例化FaceDetectorYN时指定尺寸(width, height),即可使用detect()方法检测人脸。如果输入图像的尺寸不一致,例如网上搜集的图片,那么实例化FaceDetectorYN时可任意指定尺寸,在使用detect()方法前需要调用setInputSize()方法先指定正确的输入图像尺寸。
C++下人脸检测API(FaceDetectorYN类)的使用方式:
1// 第一步:导入相关头文件 2#include <opencv2/imgproc.hpp> // cv::imread 3#include <opencv2/objdetect.hpp> // cv::FaceDetectorYN & cv::FaceRecognizerSF 4using namespace cv; 5 6int main() 7{ 8// 第二步:读取图像 9Mat img = imread('path/to/image');10// 第三步:初始化FaceDetectorYN11Ptr<FaceDetectorYN> faceDetector = FaceDetectorYN::create(modelPath, '', img.size());12// 第四步:检测人脸并将结果保存到一个Mat中13Mat faces;14faceDetector->detect(image, faces);15// faces是一个nx15的二维Mat,每一行分别是:16// [x1, y1, w, h, x_re, y_re, x_le, y_le, x_nt, y_nt, x_rcm, y_rcm, x_lcm, y_lcm, score]17// 其中,x1, y1是人脸框左上角坐标,w和h分别是人脸框的宽和高;{x, y}_{re, le, nt, rcm, lcm}分别是人脸右眼瞳孔、左眼瞳孔、鼻尖、右嘴角和左嘴角的坐标;score是该人脸的得分。18}
Python下人脸检测API的使用方式:
1import numpy as np # 请在import cv2前先import numpy以避免一些问题
2import cv2 as cv
3
4img = cv.imread('path/to/image')
5faceDetector = cv.FaceDetectorYN.create('/path/to/model', '', img.shape[:2])
6
7faces = faceDetector.detect(image)
8# 如果没有检测出人脸,则faces为NoneType而非一个空的numpy数组。这是由于OpenCV的Python bindings导致的。
9# 如果检测出人脸,则faces为一个len=2的Tuple,其中faces[1]为nx15的二维numpy数组(排列方式同上)。
OpenCV基于深度学习的人脸识别FaceRecognizerSF
人脸识别是利用人的脸部特征信息进行身份鉴别的技术。广义的人脸识别包括人脸图像采集与检测、人脸图像预处理、人脸特征提取、特征比对与分类等一系列技术。
人脸预处理
将检测到的人脸输入人脸识别模型前,通常需要先进行人脸对齐。人脸对齐利用检测部分提取到的关键点,与给定关键点之间计算变换矩阵,使用仿射变换对人脸进行变换,以减轻人脸尺度、姿态等对人脸特征提取的性能的影响。
C++下人脸对齐部分API(FaceRecognizerSF类)的使用方式:
// 初始化FaceRecognizerSFPtr<FaceRecognizerSF> faceRecognizer = FaceRecognizerSF::create(recog_model_path, '');
// 在人脸检测部分的基础上, 对齐检测到的首个人脸(faces.row(0)), 保存至aligned_face。Mat aligned_face;faceRecognizer->alignCrop(image, faces.row(0), aligned_face);
Python下人脸对齐部分API的使用方式:
# 初始化FaceRecognizerSF
recognizer = cv.FaceRecognizerSF.create(recog_model_path, '' )
# 在人脸检测部分的基础上, 对齐检测到的首个人脸(faces[1][0]), 保存至aligned_face。
aligned_face = recognizer.alignCrop(img, faces[1][0])
人脸特征提取
狭义的人脸识别模型仅作用于人脸特征提取过程。近年来,深度学习促进了人脸识别的发展,深度人脸识别模型也因其优越的性能得到了广泛应用[5]。深度人脸识别模型的训练离不开大规模训练集、高效的神经网络结构和训练损失函数。深度人脸识别模型在经过良好训练后,可以将预处理过的人脸图像转换为深度人脸特征用于后续的特征匹配。
OpenCV DNN Face所集成的深度人脸识别模型是由超球面损失函数监督、在百万名人数据集上训练的MobileNet轻量模型。将百万名人数据集[6],在使用基于元学习的自适应标签噪声清洗算法[7]进行数据清洗后,用于训练人脸识别模型,提高了人脸识别模型的性能。
模型结构使用了MobileNet[8],其利用深度可分离卷积减小了模型体积与计算量,是一种适用于移动设备的轻量级深度卷积神经网络。训练损失函数使用了SFace[9], SFace在超球面上施加由S型曲线控制的类内和类间约束以有裕度地减小类内距离而增大类间距离,从而训练出对训练噪声鲁棒的深度人脸模型,提高了人脸识别模型的性能。
人脸识别模型以尺寸为3*112*112的人脸图像对齐作为输入,输出维度为128维的人脸特征。
C++下特征提取部分API(FaceRecognizerSF类)的使用方式:
// 在上文的基础上, 获取对齐人脸的特征feature。Mat feature;faceRecognizer->feature(aligned_face, feature);
Python下特征提取部分API的使用方式:
# 在上文的基础上, 获取对齐人脸的特征feature。
feature = recognizer.feature(aligned_face)
人脸特征比对
对于不同人脸图像的人脸特征,经过人脸特征比对求出特征之间的距离,以确定不同人脸图像是否属于同一身份。当使用consine距离时,值越大,则人脸越相似身份越接近;当使用normL2距离时,值越小,则人脸越相似身份越接近。
C++下特征比对部分API(FaceRecognizerSF类)的使用方式:
// 在上文的基础上, 比对两张人脸的特征feature1,feature2以确认身份。// 使用consine距离作为指标double cos_score = faceRecognizer->match(feature1, feature2, FaceRecognizerSF::DisType::FR_COSINE);if(cos_score >= cosine_similar_thresh) { // the same identity } else { // different identities}
// 使用normL2距离作为指标double L2_score = faceRecognizer->match(feature1, feature2, FaceRecognizerSF::DisType::FR_NORM_L2);if(L2_score <= l2norm_similar_thresh) { // the same identity } else { // different identities}
Python下特征比对部分API的使用方式:
# 在上文的基础上, 比对两张人脸的特征feature1,feature2以确认身份。
# 使用consine距离作为指标
cosine_similarity_threshold = 0.363
cosine_score = recognizer.match(feature1, feature2, 0)
if cosine_score >= cosine_similarity_threshold:
# 'the same identity'
else:
# 'different identities'
# 使用normL2距离作为指标
l2_similarity_threshold = 1.128
l2_score = recognizer.match(feature1, feature2, 1)
if l2_score <= l2_similarity_threshold:
# 'the same identity'
else:
# 'different identities'
参考文献
[1] Liu, Wei, Dragomir, Anguelov, Dumitru, Erhan, Christian, Szegedy, Scott, Reed, Cheng-Yang, Fu, and Alexander C, Berg. 'Ssd: Single shot multibox detector.' . In ECCV, 2016.
[2] Howard, Andrew G, Menglong, Zhu, Bo, Chen, Dmitry, Kalenichenko, Weijun, Wang, Tobias, Weyand, Marco, Andreetto, and Hartwig, Adam. 'Mobilenets: Efficient convolutional neural networks for mobile vision applications'. In arXiv:1704.04861, 2017.
[3] Yang, Shuo, Ping, Luo, Chen Change, Loy, and Xiaoou, Tang. 'WIDER FACE: A Face Detection Benchmark.' . In CVPR, 2016.
[4] Peng, Hanyang, and Shiqi, Yu. 'A Systematic IoU-Related Method: Beyond Simplified Regression for Better Localization'. In TIP, 2021.
[5] Mei Wang and Weihong Deng. Deep face recognition: A survey. In Neurocomputing, 2021.
[6] Yandong Guo, Lei Zhang, Yuxiao Hu, Xiaodong He, and Jianfeng Gao. Ms-celeb-1m: A dataset and benchmark for large-scale face recognition. In ECCV, 2016.
[7] Yaobin Zhang, Weihong Deng, Yaoyao Zhong, Jiani Hu, Xian Li, Dongyue Zhao, and Dongchao Wen. Adaptive Label Noise Cleaning with Meta-Supervision for Deep Face Recognition. In ICCV, 2021.
[8] Andrew G. Howard, Menglong Zhu, Bo Chen, Dmitry Kalenichenko, Weijun Wang, Tobias Weyand, Marco Andreetto and Hartwig Adam. MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications. In arXiv:1704.04861, 2017.
[9] Yaoyao Zhong, Weihong Deng, Jiani Hu, Dongyue Zhao, Xian Li, and Dongchao Wen. SFace: sigmoid-constrained hypersphere loss for robust face recognition. In TIP, 2021.