当前位置:首页>python>OpenCV-python小玩意24 深度学习的人脸识别-ArcFace/SFace+YuNet实践

OpenCV-python小玩意24 深度学习的人脸识别-ArcFace/SFace+YuNet实践

  • 2026-10-13 03:20:42
OpenCV-python小玩意24 深度学习的人脸识别-ArcFace/SFace+YuNet实践

1. 深度学习的人脸识别简介

书接上回,哦,是第21回介绍深度学习人脸模型时,提到了FaceNet、ArcFace、VGGFace2等。这是当前主流人脸识别的趋势,也是我们今天的重头戏。接下来我们了解一下这些常用模型。

1.1 FaceNet

FaceNet来自Google团队2015年的论文《FaceNet: A Unified Embedding for Face Recognition and Clustering》,它的核心思想是直接学习一个映射函数, 使得人脸图像被嵌入到一个欧氏空间,用L2距离衡量相似性。简单来讲就是用比距离的方式,看谁跟谁像。

 据说当时做训练使用了上亿张图片,基于 Inception-ResNet-v1网络,输出128维归一化向量。结果就是首次实现 99.63% LFW 准确率(超越人类 97.53%),成为了工业界的事实标准。

当年我们选择合作的旷世,也是使用了FaceNet理论。 不过它的训练成本高,开源模型弱于论文效果。而且,我个人也是找了一圈,并没有那么容易找到一个合适的模型来直接使用,

1.2 ArcFace

ArcFace来自有中科院参与的2019年的论文《ArcFace: Additive Angular Margin Loss for Deep Face Recognition》。 它的核心思想是在softmax分类loss中引入“角度间隔(angular margin)”,让类内更紧凑,类间更分离。训练的数据集使用MS1MV2,是一个公开的百万数据集。基于ResNet-100 / MobileFaceNet网络结构,输出512维向量。这个维度在表达能力和计算效率之间取得了很好的平衡。

 我在onnx模型库中找到了arcface的onnx模型,接下来结合OpenCV的SFace模块和YuNet人脸检测模型做一个人脸识别的实践。

2. FaceRecognizerSF

2.1 SFace简介

OpenCV 4.5.4版本在 opencv_contrib 模块中新增了基于深度学习的人脸分析功能(简称"OpenCV DNN Face"),其核心由两个中国团队主导开发: 

人脸检测模型 YuNet——由OpenCV中国团队研发; 

人脸识别模型 SFace——由北京邮电大学课题组设计。

 其中,cv2.FaceRecognizerSF 类作为SFace模型的接口,专为高效的人脸特征提取与相似度计算而设计,兼顾精度与性能。

SFace自带了onnx格式的模型,高精度的是face_recognition_sface_2021dec.onnx,37MB左右。在opencv_zoo-4.10.0\models\face_recognition_sface可以找到。 我们今天先用该接口调用ArcFace模型。

2.2 FaceRecognizerSF主要方法介绍

(1). 创建实例cv2.FaceRecognizerSF.create函数原型

defcreate(    model: str,    config: str,    backend_id: int = cv2.dnn.DNN_BACKEND_DEFAULT,    target_id: int = cv2.dnn.DNN_TARGET_CPU) -> cv2.FaceRecognizerSF

参数简介:

参数
类型
默认值
说明
modelstr
—
ONNX 模型文件路径,如 "face_recognizer_fast.onnx"
configstr
—
配置文件路径(SFace 模型通常不需要,传空字符串 "")
backend_idintcv2.dnn.DNN_BACKEND_DEFAULT
DNN 推理后端:• cv2.dnn.DNN_BACKEND_OPENCV• cv2.dnn.DNN_BACKEND_CUDA(需编译 CUDA 支持)
target_idintcv2.dnn.DNN_TARGET_CPU
推理目标设备:• cv2.dnn.DNN_TARGET_CPU• cv2.dnn.DNN_TARGET_CUDA / DNN_TARGET_CUDA_FP16(GPU 加速)

默认 CPU 推理: 直接忽略后两个参数即可,如下:

recognizer = cv2.FaceRecognizerSF.create(    model="face_recognizer_fast.onnx",    config="")

也可以使用后两个参数,这样写:

recognizer = cv2.FaceRecognizerSF.create(    model=arcface_model,    config="",    backend_id=cv2.dnn.DNN_BACKEND_DEFAULT,    target_id=cv2.dnn.DNN_TARGET_CPU)

那后两个参数如何配合使用呢?一个是推理后端,一个推理目标设备,起初我是没有搞懂为啥要这样区分,我觉得它们是在干同一件事儿。推理后端决定谁来执行计算:使用哪个底层推理引擎,OpenCV自研还是CUDA,或者事Intel的推理引擎;目标设备是指在哪种硬件上运行,是CPU还是GPU或者是NPU,但必须与所选backend兼容。启用 CUDA 加速:

recognizer = cv2.FaceRecognizerSF.create(    model="face_recognizer_fast.onnx",    config="",    backend_id=cv2.dnn.DNN_BACKEND_CUDA,    target_id=cv2.dnn.DNN_TARGET_CUDA)

好了,其他的要怎么配合呢?看下面的表格你就全明白了。

目标
推荐 Backend
推荐 Target
通用部署
DNN_BACKEND_OPENCVDNN_TARGET_CPU
NVIDIA GPU
DNN_BACKEND_CUDADNN_TARGET_CUDA
 / DNN_TARGET_CUDA_FP16
Intel GPU
DNN_BACKEND_OPENCVDNN_TARGET_OPENCL
Intel NCS2
DNN_BACKEND_INFERENCE_ENGINEDNN_TARGET_MYRIAD
国产 NPU
DNN_BACKEND_TIMVX
 或 CANN
DNN_TARGET_NPU

(2). alignCrop(src_img, face_aligned)人脸仿射变换对齐

该方法根据人脸关键点对原始图像进行仿射变换对齐,裁剪出标准 112×112 的正面人脸。原型:

defalignCrop(    src_img: numpy.ndarray,    face_aligned: numpy.ndarray) -> numpy.ndarray: ...

参数介绍:

  • src_img:原始 BGR 图像(np.ndarray)
  • face_aligned:来自 FaceDetectorYN.detect() 的 15 维人脸信息数组(包含 5 个关键点:右眼、左眼、鼻尖、右嘴角、左嘴角) 举个栗子:
_, faces = detector.detect(image)if faces isnotNone:     single_face = faces[0]  # shape (15,)     aligned_face = recognizer.alignCrop(image, single_face)

对齐是人脸识别的关键预处理步骤,能显著提升特征一致性。

(3). feature(aligned_face)特征提取

该方法从对齐后的人脸图像中提取 128 维特征向量。

feature = recognizer.feature(aligned_face)  # shape: (128,)
  • 返回值为 np.ndarray,可用于后续比对。

(4). match(feature1, feature2, dis_type=0) 比较

该方法计算两个人脸特征之间的相似度。

  • dis_type:
    • 0:余弦相似度(Cosine Similarity),值 ∈ [-1, 1],越接近 1 越相似。常量cv2.FaceRecognizerSF_FR_COSINE。
    • 1:L2 距离(欧氏距离),值 ≥ 0,越小越相似。常量cv2.FaceRecognizerSF_FR_NORM_L2。
score = recognizer.match(feature_A, feature_B, dis_type=0)if score > 0.363:  # 余弦阈值(经验值)    print("是同一个人")

推荐阈值(仅供参考):

  • 余弦相似度:> 0.363
  • L2 距离:< 1.128

3. 完整实践代码

对比两张照片是否为同一个人。通过YuNet的detect → SFace的alignCrop → feature → match得出结论。

import cv2import numpy as npimport osdefmain():# === 检查模型是否存在 ===    yunet_model = "face_detection_yunet_2023mar.onnx"    arcface_model = "arcfaceresnet100-8.onnx"ifnot os.path.exists(yunet_model):        print(f"❌ Error: {yunet_model} not found.")returnifnot os.path.exists(arcface_model):        print(f"❌ Error: {arcface_model} not found.")return# === 加载模型 ===try:        detector = cv2.FaceDetectorYN.create(            model=yunet_model,            config="",            input_size=[320, 320],          # 初始输入尺寸(后续会动态调整)            score_threshold=0.6,           # 置信度阈值            nms_threshold=0.3,             # NMS 阈值            top_k=5000# 最多检测人脸数        )        recognizer = cv2.FaceRecognizerSF.create(            model=arcface_model,            config="",            backend_id=cv2.dnn.DNN_BACKEND_DEFAULT,            target_id=cv2.dnn.DNN_TARGET_CPU        )except Exception as e:        print("❌ Failed to load models:", e)return# === 读取两张图像 ===    img1_path = "../../face/o/8.png"    img2_path = "../../face/o/2.png"    img1 = cv2.imread(img1_path)    img2 = cv2.imread(img2_path)if img1 isNoneor img2 isNone:        print("❌ Failed to load images.")return# === 设置检测器输入尺寸===    h1, w1, _ = img1.shape    h2, w2, _ = img2.shape# 处理第一张图    detector.setInputSize([w1, h1])    _, faces1 = detector.detect(img1)if faces1 isNoneor len(faces1) == 0:        print("❌ No face detected in", img1_path)return# 取最大人脸(按面积),这里的逻辑根据实际情况调整    areas1 = [f[2] * f[3] for f in faces1]  # w * h    idx1 = int(np.argmax(areas1))    face1 = faces1[idx1]    aligned_face1 = recognizer.alignCrop(img1, face1)# 处理第二张图    detector.setInputSize([w2, h2])    _, faces2 = detector.detect(img2)if faces2 isNoneor len(faces2) == 0:        print("❌ No face detected in", img2_path)return    areas2 = [f[2] * f[3] for f in faces2]    idx2 = int(np.argmax(areas2))    face2 = faces2[idx2]    aligned_face2 = recognizer.alignCrop(img2, face2)# === 提取特征===    feat1 = recognizer.feature(aligned_face1)    feat2 = recognizer.feature(aligned_face2)# === 计算相似度 ===    cosine_score = recognizer.match(feat1, feat2, cv2.FaceRecognizerSF_FR_COSINE)    l2_distance = recognizer.match(feat1, feat2, cv2.FaceRecognizerSF_FR_NORM_L2)    print(f"\n✅ Recognition Result:")    print(f"  Cosine Similarity: {cosine_score:.4f}")    print(f"  L2 Distance       : {l2_distance:.4f}")# 判断是否为同一人,可根据场景调整if cosine_score > 0.5:        print("  👤 Verdict: Same person!")else:        print("  👤 Verdict: Different persons.")# === 可视化===defdraw_face(img, face, color=(0, 255, 0)):        x, y, w, h = map(int, face[:4])        cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)# 绘制5个关键点(YuNet 输出:[x,y,w,h,5*landmarks,conf])        landmarks = face[4:14].reshape((5, 2)).astype(np.int32)for pt in landmarks:            cv2.circle(img, tuple(pt), 2, (0, 0, 255), -1)return img    vis1 = draw_face(img1.copy(), face1)    vis2 = draw_face(img2.copy(), face2)# 显示结果    cv2.imshow("Face 1", vis1)    cv2.imshow("Face 2", vis2)    cv2.waitKey(0)    cv2.destroyAllWindows()if __name__ == "__main__":    main()

结果如下:

识别结果

4. 最后

如果使用OpenCV提供的SFace模型,直接替换模型即可。比如我后来使用face_recognition_sface_2021dec_int8.onnx。速度更快,后续准备在安卓设备上验证一下。 

所有源码和模型等资料请公众号菜单中获取。 

至此,我们的人脸识别之旅就先到这里。有问题评论区讨论。 我们下期再见!

参考

1.FaceNet: A Unified Embedding for Face Recognition and Clustering

2.ArcFace: Additive Angular Margin Loss for Deep Face Recognition

3.facenet github

4.ArcFace Model Download

5.YuNet OpenCV Zoo

#facenet #arcface #YuNet #FaceRecognizerSF

最新文章

随机文章