
人脸识别原理
要从检测到的行人中判断是否有特定目标人物存在,其技术流程和关键技术可以分为以下几个核心环节:
整个流程可以概括为:人脸检测 → 人脸对齐 → 特征提取 → 特征比对与识别。

下面来详细解析每个环节的关键技术:
第一阶段:人脸检测
这是第一步,目标是从整张图片或视频帧中找出所有人脸的位置。
- 关键技术:
- 基于深度学习的目标检测算法: 这是当前绝对的主流技术。
- 单阶段检测器: 如 SSD、YOLO 系列(如YOLOv5, v7, v8)。它们速度非常快,适合实时性要求高的场景。
- 两阶段检测器: 如 Faster R-CNN。通常精度更高,但速度稍慢。
- 基于深度学习的目标检测算法: 这是当前绝对的主流技术。
目标输出: 一个或多个标注了位置(边界框,Bounding Box)的人脸图像。
第二阶段:人脸对齐
人脸对齐的目的是将检测到的人脸“摆正”,消除姿态(如抬头、转头)、表情等造成的几何形变,为后续特征提取提供标准化的输入。
- 关键技术:
- 人脸关键点检测: 定位人脸上的基准点,如眼角、鼻尖、嘴角等(通常是5点、68点或106点)。
- 算法示例: 基于CNN的回归网络,如 MTCNN(它同时完成了检测和对齐),以及后续更先进的 HRNet、MobileNet 等轻量化网络。
- 几何变换: 根据检测到的关键点,通过仿射变换或透视变换,将人脸旋转、缩放至“标准脸”的姿态。
- 人脸关键点检测: 定位人脸上的基准点,如眼角、鼻尖、嘴角等(通常是5点、68点或106点)。
目标输出: 姿态端正、大小统一(例如112x112像素)的标准化人脸图像。
第三阶段:特征提取
这是人脸识别技术的核心。目标是从对齐后的人脸图像中,提取出一个能够代表该人脸独一无二身份的、高 discriminative 的数值向量(也称为“特征向量”或“嵌入”)。
- 关键技术:
- 深度卷积神经网络: 这是现代人脸识别的基石。
- 主干网络: 用于提取特征的CNN架构,如 ResNet、Inception、MobileNet、EfficientNet 等。它们负责从像素中抽象出高层次的特征。
- 损失函数: 这是驱动网络学习“区分性特征”的关键。好的损失函数能让同类(同一人)的特征在向量空间中尽可能聚集,不同类(不同人)的特征尽可能远离。
- Softmax Loss 及其变种: 最基础的分类损失。
- Contrastive Loss、Triplet Loss: 通过直接比较样本对或三元组来学习特征距离。
- ArcFace、CosFace、SphereFace: 当前最主流的损失函数。它们在Softmax的基础上引入了角度/余弦间隔,使得学习到的特征在角度空间内具有更大的类间距离和更小的类内距离,识别效果极佳。
- 深度卷积神经网络: 这是现代人脸识别的基石。
目标输出: 一个固定长度的特征向量(例如512维的浮点数向量)。这个向量可以看作是这张人脸的“数字身份证”。
第四阶段:特征比对与识别
这是最后一步,将提取到的特征与数据库中预存的目标人物特征进行比对,做出判断。
- 关键技术:
- 相似度度量:
- 余弦相似度: 最常用且有效的方法,计算两个特征向量在方向上的差异。值越接近1,说明越相似。
- 欧氏距离: 计算两个向量在空间中的直线距离。距离越接近0,说明越相似。
- 向量搜索: 当目标库(Gallery)非常大时(如万级以上),需要高效的搜索技术。
- 近似最近邻搜索: 如 FAISS(Facebook开源的库)、HNSW 等,可以在海量向量中快速找到最相似的几个候选。
- 阈值判断:
- 设定一个相似度阈值。如果待识别人脸与库中某目标人脸的相似度高于该阈值,则判定为“存在”;否则判定为“不存在”。阈值的设定直接影响识别的准确率(Recall)和误识率(FAR),需要根据应用场景的严格程度进行权衡。
- 相似度度量:
总结与补充要点
人脸识别中还需要注意:
- 人脸质量评估: 在实际应用中,并非所有检测到的人脸都适合识别。模糊、过暗、过大遮挡、侧脸过度的人脸应被过滤掉,以免产生错误的识别结果。
- 活体检测: 为防止照片、视频等攻击,需要集成活体检测技术,判断摄像头前的是真人还是伪造品。技术包括眨眼、张嘴、摇头等动作指令,以及红外、3D结构光等物理手段。
参考:
https://blog.csdn.net/weixin_44714085/article/details/147304225
https://www.cnblogs.com/ai-ldj/p/18300343
人脸检测算法:

人脸对齐:
即将人脸截取出来并将倾斜的人脸处理成正常的姿态。这样可以使每一个截取的人脸中的眼睛等位置处于同一位置,会对后面的识别算法起到一定的优化作用。
人脸对齐主要包括以下几个步骤:
1,提取出每张图片里眼睛的坐标,进行读取数据
2,找两眼间的直线距离并计算该直线与水平线之间的夹角,如倾斜角度3,根据找到的倾斜角度旋转图片
4,在旋转后的图片中找到眼睛的位置
5,根据眼睛坐标找到包含面部的框的宽度和高度,调整图片的尺寸

特征提取:
| 模型名称 | 核心特征与原理 | 准确率表现 (参考) | 性能与速度 | 主要特点与应用 |
|---|---|---|---|---|
| FaceNet | 使用三元组损失函数,直接将人脸图像映射到欧几里得空间,通过距离判断相似度 | 在LFW数据集上准确率可达99.65%;在真实场景中表现出较低的错误接受率 | 模型相对复杂,速度不是其最大优势 | Google出品,是许多后续模型的基础。综合准确率高,适合对精度要求高的场景。 |
| InsightFace | 集成了ArcFace损失函数,在特征层面通过角度间隔增强类间判别力 | 在LFW数据集上准确率表现优异,可达99.86% | 提供多种速度与精度平衡的模型,部分优化版本适合实时应用 | 项目生态完整,包含检测、识别、对齐等全套工具。当前SOTA(最先进)模型之一,社区活跃。 |
| ArcFace | Additive Angular Margin Loss,在角度空间最大化决策边界,使得学到的特征更具判别性 | 在多个测试中显示出高真正例率和真负例率 | 性能与InsightFace类似,依赖于具体的实现和配置 | 理论创新显著,尤其擅长处理大型类别(如上百万ID)的人脸识别任务。常作为InsightFace框架的核心算法。 |
| DeepFace | 较早使用大型卷积神经网络进行端到端人脸识别,进行了初步的人脸对齐 | 准确率表现尚可,有研究报道其准确率约为91.90% | 作为较早的模型,其结构可能并非为现代硬件做最优效率设计 | Facebook出品,具有开创性意义。它是一个框架,可以集成VGG-Face、FaceNet、OpenFace等多种模型作为后端。 |
| OpenFace | 基于Google的FaceNet模型,但更注重效率和开源,使用三元组损失训练 | 准确率尚可,在LFW上为99.38%,但通常略低于FaceNet和InsightFace | 速度非常快,是表中所有模型里最轻量、高效的选择之一 | 由CMU团队开发,非常适合实时应用、移动端或计算资源有限且对精度要求不极致的场景 |
InsightFace 使用
InsightFace 是一个开源的 2D&3D 深度人脸分析工具箱 ,它高效地实现了人脸识别、人脸检测和人脸对齐等丰富多样且前沿的算法,并针对训练和部署进行了优化。自从2018年开源以来,其在GitHub上保持着强劲的增长态势,基于 PyTorch 和 MXNet 深度学习框架,提供了从数据预处理到模型部署的全栈能力。
🔬 核心技术与优势
InsightFace 的核心优势主要体现在以下几个方面:
- 领先的损失函数设计:其核心创新 ArcFace (Additive Angular Margin Loss) ,通过在角度空间添加固定的边际,使得同类人脸的特征更加紧凑,不同类的特征更加分散,有效解决了传统Softmax损失函数决策边界模糊的问题。在MegaFace数据集上,ArcFace相比传统Triplet Loss的识别准确率提升显著。
- 丰富的模型与算法支持:提供了多种主干网络选择,从高性能的 ResNet 到轻量级的 MobileFaceNet ,后者通过深度可分离卷积等技术,在保持高准确率的同时大幅压缩模型体积。同时,也集成了 RetinaFace、SCRFD 等多种高效的人脸检测方法。
- 强大的工程化与部署能力:采用模块化设计,将人脸检测、对齐、特征提取等步骤解耦。支持 ONNX Runtime、TensorRT、MNN 等多种推理后端,并提供针对云端、边缘设备和移动端的优化方案,例如通过量化技术可在精度损失极小的情况下提升推理速度。
🛠️ 主要功能与应用场景
InsightFace 实现的功能覆盖了深度面部分析的完整 pipeline,并广泛应用于各类实际场景中。
🔍** 主要功能**
- 人脸检测与对齐:精准定位图片或视频中的人脸位置,并进行关键点检测与对齐。
- 人脸识别与验证:完成1:1的人脸验证(如手机解锁)或1:N的人脸识别(在大量人脸中查找特定身份)。
- 人脸属性分析:预测人脸的性别、年龄、种族等属性。
- 3D人脸重建:结合深度信息提升防伪能力,是未来的技术方向之一。
💼** 应用场景**
- 金融身份核验:用于远程开户、刷脸支付等场景,通过活体检测与人脸比对保障安全。
- 智慧安防与考勤:实现实时人脸布控、黑名单预警及智能考勤管理。
- 移动端应用:优化后在手机解锁、社交媒体处理等场景中提供便捷体验。
📦 如何使用与部署
入门与基础使用
对于初学者,通常推荐从 Python 接口开始,利用官方提供的预训练模型快速体验核心功能:
import insightface
# 创建人脸分析模型,并准备模型
model = insightface.app.FaceAnalysis(name='buffalo_l', allowed_modules=['detection', 'recognition'])
model.prepare(ctx_id=0, det_size=(640, 640))
# 进行人脸分析
faces = model.get(img)
# 获取人脸特征向量
embedding = face.embedding
训练自己的模型
如果需要在自己的数据集上进行训练,可以参考以下关键步骤:
- 数据准备:使用 MS1M-RetinaFace 等大型公开数据集,并利用 RetinaFace 等工具进行人脸检测和对齐。
- 模型配置:选择合适的骨干网络(如 ResNet100 或 MobileFaceNet),并配置损失函数(如 ArcFace)及训练参数(如批量大小、学习率策略等)。
部署与优化
针对不同部署环境的优化策略:
- 云端服务:可使用 TensorRT 加速的 FP16 精度模型。
- 边缘设备:可采用 TVM 编译的轻量级模型。
- 移动端:可借助 MNN 等推理框架,并进行模型量化。
此外,InsightFace 团队也推出了 InspireFace SDK,这是一个由 C/C++ 开发、支持多平台和多种推理后端(CPU/GPU/NPU)的商用级人脸识别 SDK,在性能上做了深度优化。
💎 总结
InsightFace 凭借其领先的算法(特别是ArcFace)、丰富的模型库、强大的工程化能力和活跃的开源社区,已成为人脸识别领域极具影响力的工具箱之一。无论是用于学术研究还是商业产品开发,InsightFace 都提供了一个坚实可靠的基座。
希望以上介绍能帮助你全面了解 InsightFace。如果你对某个具体功能或者在特定场景下的应用有进一步的疑问,可以随时提出。
全流程pipeline使用
使用insightface可以完成人脸检测、人脸对齐、向量提取等功能。insightface封装好了一系列流程,以较少的代码轻松完成所有的功能。
import cv2
import insightface
from insightface.app import FaceAnalysis
from insightface.data import get_image as ins_get_image# 初始化人脸分析模型
app = FaceAnalysis()
# 准备模型,指定运行上下文(ctx_id=0 表示使用第一个可用的设备)
# 你可以通过 providers 参数优先选择GPU
app.prepare(ctx_id=0, det_size=(640, 640))# 加载一张图片
# 使用库自带的示例图片,或者用 cv2.imread("你的图片路径.jpg") 读取自己的图片
img = ins_get_image('t1')
cv2.imshow("face", img)
cv2.waitKey(0)
# 进行人脸检测和分析
faces = app.get(img)
# 将检测到的人脸框和关键点绘制在图片上
result_img = app.draw_on(img, faces)
cv2.destroyAllWindows()
# 保存结果图片
cv2.imwrite("./output_with_faces.jpg", result_img)
输出结果:

以上使用的人脸检测算法是自研的算法,而不是RetinaFace,如果想使用InsightFace模型,可以只使用InsightFace的人脸对齐和特征提取能力,人脸检测使用InsightFace模型。
默认需要从Github下载模型文件,包含的模型文件如下:
| 模型名称 | 功能 |
|---|---|
| 1k3d68.onnx | 人脸特征landmark |
| 2d106det.onnx | 人脸关键点检测 |
| det_10g.onnx | 人脸检测 |
| genderage.onnx | 年龄,性别检测 |
| w600k_r50.onnx | 人脸体征提取 |
配合RetainaFace
retainaface是目前人脸检测中效果最好模型之一,而insightface默认使用的并不是该模型。如果解耦人脸检测和特征提取两个环节,使用retainaface作为人脸检测的实现,那么可以retinaface先识别出人脸,再将人脸抠图送给insightface完成特征提取
import cv2
from retinaface import RetinaFace
from insightface.app import FaceAnalysisframe = cv2.imread("demo.jpeg")app = FaceAnalysis(name='buffalo_l')
app.prepare(ctx_id=-1)resized_frame = cv2.resize(frame, (640, 480))
faces = RetinaFace.detect_faces(frame)for face in faces.values():x1, y1, x2, y2 = face["facial_area"]print(f"{x1=} {y1=} {x2=} {y2=}")landmarks = face["landmarks"] # 关键点(眼睛、鼻子等)face_img = frame[y1:y2, x1:x2]cv2.imshow("face", face_img)if cv2.waitKey(25) & 0xFF == ord('q'):break# 只使用特征提取的能力embedding = app.models['recognition'].get_feat(face_img)if embedding is not None:print(f"特征向量形状: {embedding.shape}")embedding = embedding.reshape(1, -1)print(embedding)else:print("特征提取返回None")
输出特征向量:
Applied providers: ['CPUExecutionProvider'], with options: {'CPUExecutionProvider': {}}
find model: /Users/ljk/.insightface/models/buffalo_l/1k3d68.onnx landmark_3d_68 ['None', 3, 192, 192] 0.0 1.0
Applied providers: ['CPUExecutionProvider'], with options: {'CPUExecutionProvider': {}}
find model: /Users/ljk/.insightface/models/buffalo_l/2d106det.onnx landmark_2d_106 ['None', 3, 192, 192] 0.0 1.0
Applied providers: ['CPUExecutionProvider'], with options: {'CPUExecutionProvider': {}}
find model: /Users/ljk/.insightface/models/buffalo_l/det_10g.onnx detection [1, 3, '?', '?'] 127.5 128.0
Applied providers: ['CPUExecutionProvider'], with options: {'CPUExecutionProvider': {}}
find model: /Users/ljk/.insightface/models/buffalo_l/genderage.onnx genderage ['None', 3, 96, 96] 0.0 1.0
Applied providers: ['CPUExecutionProvider'], with options: {'CPUExecutionProvider': {}}
find model: /Users/ljk/.insightface/models/buffalo_l/w600k_r50.onnx recognition ['None', 3, 112, 112] 127.5 127.5
set det-size: (640, 640)
x1=np.int64(1281) y1=np.int64(281) x2=np.int64(1478) y2=np.int64(512)
特征向量形状: (1, 512)
[-0.18836826 -0.6296245 0.41510874 -0.60636526 -0.16569017 -0.53891915-0.9803167 -0.3922735 0.3360191 -1.4537412 ]
x1=np.int64(1099) y1=np.int64(487) x2=np.int64(1268) y2=np.int64(694)
特征向量形状: (1, 512)
[-1.7220321 -0.9097497 1.0802007 0.88352764 -0.16584872 0.06488310.7612704 0.49298155 0.7476485 -0.948328 ]
x1=np.int64(858) y1=np.int64(485) x2=np.int64(1028) y2=np.int64(699)
特征向量形状: (1, 512)
[ 1.4237909 0.5163184 0.01861984 0.39578098 1.4124055 -1.61463240.99330807 0.73720586 0.77999234 -3.1522732 ]
x1=np.int64(565) y1=np.int64(285) x2=np.int64(783) y2=np.int64(562)
特征向量形状: (1, 512)
[-0.0127585 1.3230383 -0.67356586 0.477445 -0.90607387 -0.075302181.5227439 -0.62858045 0.8654297 0.4775056 ]
人脸搜索实现
人脸搜索是在人脸特征提取的前提下,保存特征到向量数据库,再通过比对找出最相似的特征。
使用faiss向量数据库保存提取的特征,再使用其向量相似度搜索的能力,找到最接近的Top k 个向量。
import cv2
import faiss
from retinaface import RetinaFace
from insightface.app import FaceAnalysis
import matplotlib.pyplot as pltapp = FaceAnalysis(name='buffalo_l')
app.prepare(ctx_id=-1)
index = faiss.IndexFlatIP(512) # 内积加速
face_imgs = []def save_embed(img):res = []frame = cv2.imread(img)faces = RetinaFace.detect_faces(frame)for index, face in enumerate(faces.values()):x1, y1, x2, y2 = face["facial_area"]print(f"{x1=} {y1=} {x2=} {y2=}")face_img = frame[y1:y2, x1:x2]face_img_t = face_img.copy()cv2.putText(face_img_t, str(index), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 0, 0), 2)face_imgs.append(face_img_t)cv2.imwrite(f"face_img/{index}.png", face_img_t)embedding = app.models['recognition'].get_feat(face_img)if embedding is not None:print(f"特征向量形状: {embedding.shape}")res.append(embedding)else:print("特征提取返回None")return resdef get_embed(img):res = []frame = cv2.imread(img)faces = RetinaFace.detect_faces(frame)for index, face in enumerate(faces.values()):x1, y1, x2, y2 = face["facial_area"]print(f"{x1=} {y1=} {x2=} {y2=}")face_img = frame[y1:y2, x1:x2]embedding = app.models['recognition'].get_feat(face_img)if embedding is not None:print(f"特征向量形状: {embedding.shape}")res.append(embedding)else:print("特征提取返回None")return resif __name__ == '__main__':embed_list = save_embed("人名的名义.jpg")for item in embed_list:index.add(item)target_list = get_embed("祁同伟.png")if target_list:target_embed = target_list[0]distances, indices = index.search(target_embed, k=2)print('--------distances---------')print(distances)print('--------indices----------')print(indices)img1 = cv2.imread("祁同伟.png")img2 = face_imgs[int(indices[0][0])]img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB) # OpenCV 读出来是 BGR,要转 RGBimg2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)fig, ax = plt.subplots(1, 2, figsize=(10, 5)) # 一行两列ax[0].imshow(img1)ax[0].set_title('Image 1')ax[0].axis('off')ax[1].imshow(img2)ax[1].set_title('Image 2')ax[1].axis('off')plt.tight_layout()plt.show()
原图人脸入库:

人脸识别搜索:

持久化人脸搜索
持久化人脸底库,支持多批次入库,持久化保存,人脸识别。
import os
import random
import cv2
import faiss
import numpy as np
from retinaface import RetinaFace
from insightface.app import FaceAnalysis
import matplotlib.pyplot as pltdef save_embed(app, img):res = []frame = cv2.imread(img)faces = RetinaFace.detect_faces(frame)for face in faces.values():x1, y1, x2, y2 = face["facial_area"]print(f"{x1=} {y1=} {x2=} {y2=}")face_img = frame[y1:y2, x1:x2]face_img_t = face_img.copy()face_img_uuid = random.randint(10**10, 10**11)cv2.imwrite(f"face_record/{face_img_uuid}.png", face_img_t)embedding = app.models['recognition'].get_feat(face_img)if embedding is not None:print(f"特征向量形状: {embedding.shape} {face_img_uuid}")res.append([np.array([face_img_uuid]), embedding])else:print("特征提取返回None")return resdef get_embed(app, img):res = []frame = cv2.imread(img)faces = RetinaFace.detect_faces(frame)for index, face in enumerate(faces.values()):x1, y1, x2, y2 = face["facial_area"]print(f"{x1=} {y1=} {x2=} {y2=}")face_img = frame[y1:y2, x1:x2]embedding = app.models['recognition'].get_feat(face_img)if embedding is not None:print(f"特征向量形状: {embedding.shape}")res.append(embedding)else:print("特征提取返回None")return resdef in_database(index, app, img_path):embed_list = save_embed(app, img_path)for img_uuid, embedding in embed_list:index.add_with_ids(embedding, img_uuid)faiss.write_index(index, "basic_index.faiss")def serach_image(app, img_path):target_list = get_embed(app, img_path)if target_list:target_embed = target_list[0]index = faiss.read_index("basic_index.faiss")distances, indices = index.search(target_embed, k=2)print('--------distances---------')print(distances)print('--------indices----------')print(indices)img1 = cv2.imread(img_path)img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB) # OpenCV 读出来是 BGR,要转 RGBimg_uuid = str(indices[0][0])img_path = f"face_record/{img_uuid}.png"if not os.path.exists(img_path):print("人脸库中不存在该人脸信息")returnimg2 = cv2.imread(img_path)fig, ax = plt.subplots(1, 2, figsize=(10, 5)) # 一行两列ax[0].imshow(img1)ax[0].set_title('search image')ax[0].axis('off')if img2.any():img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)ax[1].imshow(img2)ax[1].set_title('archive images')ax[1].axis('off')plt.tight_layout()plt.show()def init_app():app = FaceAnalysis(name='buffalo_l')app.prepare(ctx_id=-1)base_index = faiss.IndexFlatIP(512) # 内积加速index = faiss.IndexIDMap(base_index)return index, appif __name__ == '__main__':index, app = init_app()in_database(index, app, "庆余年.jpg")serach_image(app, "范闲.jpg")

