OpenCV计算机视觉实战(31)——人脸识别详解

OpenCV计算机视觉实战(31)------人脸识别详解

    • [0. 前言](#0. 前言)
    • [1. 人脸检测与关键点定位](#1. 人脸检测与关键点定位)
      • [1.1 实现过程](#1.1 实现过程)
      • [1.2 优化思路](#1.2 优化思路)
    • [2. 特征编码与比对](#2. 特征编码与比对)
      • [2.1 实现过程](#2.1 实现过程)
      • [2.2 优化思路](#2.2 优化思路)
    • [3. 活体检测技术](#3. 活体检测技术)
      • [3.1 实现过程](#3.1 实现过程)
      • [3.2 优化思路](#3.2 优化思路)
    • 小结
    • 系列链接

0. 前言

在人脸识别领域,从检测、对齐到特征编码和比对,再到抗假脸的活体检测,每一步都关乎系统的准确性与安全性。本文以 OpenCV 为依托,深入展示如何构建一个端到端的人脸识别系统:首先结合 DNN 与跟踪器,实现多尺度、非极大值抑制与 KCF 跟踪融合的人脸检测与精确关键点定位;接着利用 Torch 模型批量提取归一化特征向量,并通过欧氏距离与余弦相似度在大规模库中快速比对;最后,将眨眼、头部微动与轻量级 CNN 活体检测模型融合投票,构筑多模态活体防护,显著提升对照片与视频攻击的抵御能力。

1. 人脸检测与关键点定位

使用 OpenCV DNN 模型对人脸进行检测,并借助 LBF 模型提取 68 个面部关键点,为后续对齐、特征提取和活体检测提供坐标依据。

1.1 实现过程

  • DNN 人脸检测:readNetFromCaffe 加载 ResNet SSD--Caffe 模型,输入 blob,输出多个人脸框
  • Facemark LBFOpenCV 提供 cv2.face.createFacemarkLBF,需先下载并加载 LBF 模型文件
  • 批量关键点拟合:facemark.fit 接受原图与人脸矩形列表,返回对应的 68 点坐标
  • 可视化:人脸框用绿框,高精度关键点用红点标记
python 复制代码
import cv2
import numpy as np

# 功能:在视频流中检测人脸并绘制 68 点关键点
# 1. 加载 DNN 人脸检测器(ResNet SSD)
detector = cv2.dnn.readNetFromCaffe(
    'deploy.prototxt', 
    'res10_300x300_ssd_iter_140000_fp16.caffemodel'
)
# 2. 加载 Facemark LBF 关键点检测器
facemark = cv2.face.createFacemarkLBF()
facemark.loadModel('lbfmodel.yaml')

cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    if not ret: break
    h, w = frame.shape[:2]
    # 检测人脸
    blob = cv2.dnn.blobFromImage(frame, 1.0, (300,300),
                                 (104,117,123), swapRB=False)
    detector.setInput(blob)
    dets = detector.forward()[0,0,:,:]
    faces = []
    for i in range(dets.shape[0]):
        conf = float(dets[i,2])
        if conf > 0.6:
            x1 = int(dets[i,3]*w); y1 = int(dets[i,4]*h)
            x2 = int(dets[i,5]*w); y2 = int(dets[i,6]*h)
            faces.append((x1,y1,x2-x1,y2-y1))
            cv2.rectangle(frame,(x1,y1),(x2,y2),(0,255,0),2)
    # 关键点检测
    if faces:
        _, landmarks = facemark.fit(frame, np.array(faces))
        for lm in landmarks:
            for (x,y) in lm[0]:
                cv2.circle(frame, (int(x),int(y)), 2, (0,0,255), -1)
    cv2.imshow('Face+Landmarks', frame)
    if cv2.waitKey(1)==27: break

cap.release()
cv2.destroyAllWindows()

关键函数解析:

  • cv2.dnn.blobFromImage(img, scalefactor, size, mean, swapRB):构建网络输入
  • detector.forward():返回形状为 [1,1,N,7] 的检测结果
  • cv2.face.createFacemarkLBF():创建 LBF 面部关键点检测器
  • facemark.fit(img, faces):对每个 ROI 拟合关键点

1.2 优化思路

  • NMS + 多尺度检测:结合 cv2.dnn.NMSBoxes 消除重叠检测框,并在不同输入尺度上检测小脸
  • 人脸对齐:利用 68 点关键点对人脸做仿射变换,对齐到统一姿态,后续编码更鲁棒
  • 跟踪融合:用 cv2.TrackerKCF_create() 对检测到的人脸进行短期跟踪,减少 DNN 推理次数,提升帧率
python 复制代码
import cv2
import numpy as np

# 功能:多尺度 DNN 检测 + NMS + 68 点对齐 + KCF 跟踪融合
detector = cv2.dnn.readNetFromCaffe('deploy.prototxt','res10_300x300_ssd_iter_140000_fp16.caffemodel')
facemark = cv2.face.createFacemarkLBF(); facemark.loadModel('lbfmodel_68.yaml')
trackers = []  # 存储 (tracker, bbox)

def align_face(img, pts):
    # 用眼角和嘴角计算仿射矩阵,对齐至标准位置``
    src = np.float32([pts[36], pts[45], pts[33]])  # 左眼外、右眼外、鼻尖
    dst = np.float32([[30,30],[70,30],[50,60]])
    M = cv2.getAffineTransform(src, dst)
    return cv2.warpAffine(img, M, (100,100))

cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    if not ret: break
    h, w = frame.shape[:2]
    # 1. 跟踪优先
    new_trackers = []
    for tr, bbox in trackers:
        ok, b = tr.update(frame)
        if ok:
            x,y,ww,hh = map(int,b)
            new_trackers.append((tr,(x,y,ww,hh)))
            cv2.rectangle(frame,(x,y),(x+ww,y+hh),(255,0,0),2)
    trackers = new_trackers
    # 2. 检测补充
    blob = cv2.dnn.blobFromImage(frame,1.0,(300,300),(104,117,123))
    detector.setInput(blob); dets = detector.forward()[0,0]
    boxes,confs = [],[]
    for d in dets:
        if d[2]>0.6:
            x1,y1,x2,y2 = (d[3:7]*[w,h,w,h]).astype(int)
            boxes.append([x1,y1,x2-x1,y2-y1]); confs.append(float(d[2]))
    idxs = cv2.dnn.NMSBoxes(boxes,confs,0.6,0.4)
    if len(idxs):
        faces = []
        for i in idxs.flatten():
            faces.append(tuple(boxes[i]))
            x,y,ww,hh = boxes[i]
            tr = cv2.TrackerKCF_create()
            tr.init(frame,(x,y,ww,hh))
            trackers.append((tr,(x,y,ww,hh)))
    # 3. 关键点与对齐
    if faces:
        _, lms = facemark.fit(frame, np.array(faces))
        for lm,face in zip(lms,faces):
            pts = lm[0].astype(int)
            aligned = align_face(frame, pts)
            cv2.imshow('Aligned Face', aligned)
            for (xk,yk) in pts:
                cv2.circle(frame,(xk,yk),2,(0,0,255),-1)
    cv2.imshow('Detection+Tracking', frame)
    if cv2.waitKey(1)==27: break

cap.release(); cv2.destroyAllWindows()

关键函数解析:

  • cv2.dnn.NMSBoxes():剔除重叠度过高的检测框
  • cv2.TrackerKCF_create():对检测结果进行轻量级跟踪,减少 DNN 推理
  • cv2.getAffineTransform() + warpAffine():根据眼、鼻关键点对齐人脸到统一参考模板

2. 特征编码与比对

利用 OpenCV DNN 加载预训练的 Torch FaceNet 模型,将对齐后的面部裁剪为 128 维特征向量,后续通过欧氏距离比对不同人脸相似度。

2.1 实现过程

  • 人脸对齐:输入裁剪并对齐至 96 × 96 大小
  • Torch 模型加载:readNetFromTorch 支持 OpenFace 提供的小型 FaceNet
  • 特征提取:网络输出 128 维归一化向量
  • 相似度度量:欧氏距离小于阈值(如 0.6) 时判定为同一人
python 复制代码
import cv2
import numpy as np

# 功能:提取人脸特征编码并比对欧氏距离
# 加载 FaceNet Torch 模型
embedder = cv2.dnn.readNetFromTorch('openface.nn4.small2.v1.t7')

def get_embedding(face_img):
    blob = cv2.dnn.blobFromImage(face_img, 1.0/255, (96,96),
                                 (0,0,0), swapRB=True, crop=False)
    embedder.setInput(blob)
    vec = embedder.forward()
    return vec.flatten()

# 假设有两张对齐好的人脸 faceA, faceB
faceA = cv2.imread('personA.jpg')
faceB = cv2.imread('personB.jpg')
embA = get_embedding(faceA)
embB = get_embedding(faceB)

# 计算欧氏距离
dist = np.linalg.norm(embA - embB)
print(f'Euler distance between faces: {dist:.2f}')

关键函数解析:

  • cv2.dnn.readNetFromTorch(model):加载 .t7 网络权重
  • blobFromImage:缩放到网络要求尺寸,并归一化
  • net.forward():前向提取特征向量
  • np.linalg.norm:计算两个向量间的欧氏距离

2.2 优化思路

  • 批量人脸编码:将多张人脸同时送入网络,加速批处理
  • 比对库管理:维护多名用户的特征库,支持距离阈值自动学习(基于验证集)
  • 余弦相似度:除欧氏距离外,还可用余弦相似度衡量编码向量夹角
python 复制代码
import cv2, numpy as np

# 功能:批量提取编码,构建人脸特征库并支持余弦比对
embedder = cv2.dnn.readNetFromTorch('openface.nn4.small2.v1.t7')

def get_embeddings(faces):
    blobs = cv2.dnn.blobFromImages(
        faces, 1.0/255, (96,96), (0,0,0), swapRB=True, crop=False)
    embedder.setInput(blobs)
    vecs = embedder.forward()
    # L2 归一化
    norms = np.linalg.norm(vecs, axis=1, keepdims=True)
    return vecs / norms

# 假设 facesA, facesB 为裁剪且对齐好的人脸列表
embA = get_embeddings(facesA)  # shape (N,128)
embB = get_embeddings(facesB)

# 欧氏距离与余弦相似度
dists = np.linalg.norm(embA[:,None]-embB[None,:], axis=2)
cosine = (embA @ embB.T)  # 两向量已归一化,直接点积

print('Euclid min:', dists.min())
print('Cosine max:', cosine.max())

关键函数解析:

  • blobFromImages():一次性批量构建输入 Blob,加速多脸推理
  • 向量归一化 vec / ||vec||:确保欧氏距离与余弦相似度可比
  • 余弦相似度 u·v:对于归一化特征,值越接近 1 越相似

3. 活体检测技术

通过监测眨眼动作实现简易活体检测:基于面部关键点计算眼睛长宽比 (EAR),检测到连续几帧内长宽比突变则认为是真人眨眼,抵御照片欺骗。

3.1 实现过程

  • EAR 公式:根据六个关键点计算眼睛长宽比,健康范围内波动表示眨眼
  • 阈值与计数:当 EAR 低于阈值连续多帧后计为一次眨眼
  • 活体判定:检测到一定次数眨眼则判定真人,反之可怀疑照片攻击

关键函数解析:

  • scipy.spatial.distance.euclidean(p, q):计算两点间欧氏距离
  • eye_aspect_ratio(eye_pts):实现 EAR 公式 (‖p2−p6‖ + ‖p3−p5‖)/(2*‖p1−p4‖)
  • 帧计数与阈值:确保短时抖动不误判

3.2 优化思路

  • 头部微动:结合面部关键点估计头部欧拉角,检测自然微动
  • 深度学习活体检测:引入轻量级 CNN (如 MobileNet +二分类) 对 ROI 做帧级真伪判定
  • 融合策略:将眨眼、头动和 CNN 判断结果通过加权投票,提升活体检测准确率
python 复制代码
# 功能:眨眼 + 头动 + CNN 活体判定融合
# 1. CNN 模型加载(TensorFlow Lite 示例)
import tflite_runtime.interpreter as tflite
interpreter = tflite.Interpreter('liveness.tflite'); interpreter.allocate_tensors()

def cnn_liveness(face_roi):
    # 预处理并推理返回真(1)/假(0)
    inp = cv2.resize(face_roi,(64,64))/255.0
    inp = np.expand_dims(inp.astype(np.float32),0)
    interpreter.set_tensor(input_idx, inp)
    interpreter.invoke()
    return interpreter.get_tensor(output_idx)[0,1]  # 活体概率

while True:
    # 检测&关键点
    # ... 获得 aligned_face 和 EAR, head_angle ...
    eye_score = (ear < EAR_THRESH)
    head_score = abs(head_angle) > ANGLE_THRESH
    cnn_score = cnn_liveness(aligned_face) > 0.5

    # 加权投票
    votes = eye_score + head_score + cnn_score
    is_live = votes >= 2
    cv2.putText(frame, f'Live: {is_live}', (10,60), ...)

关键函数解析:

  • 头动估计:用 OpenCV SolvePnP 根据 2D-3D 关键点计算头部欧拉角
  • TFLite Interpreter:在 CPU 上快速执行轻量级活体检测模型
  • 投票融合:融合多信号减少单一算法误判,提升鲁棒性

小结

本节系统介绍了基于 OpenCV 构建端到端人脸识别系统的完整流程。首先通过 DNN 模型与 KCF 跟踪器融合,实现高效稳定的人脸检测与关键点定位;接着利用 Torch 加载轻量级 FaceNet 网络,对齐人脸并提取归一化特征编码,实现欧氏距离与余弦相似度的人脸精确比对;最后引入眨眼检测、头部姿态估计和轻量级 CNN 模型,构建多模态融合的活体检测机制,有效防御照片与视频伪造攻击。

系列链接

OpenCV计算机视觉实战(1)------计算机视觉简介
OpenCV计算机视觉实战(2)------环境搭建与OpenCV简介
OpenCV计算机视觉实战(3)------计算机图像处理基础
OpenCV计算机视觉实战(4)------计算机视觉核心技术全解析
OpenCV计算机视觉实战(5)------图像基础操作全解析
OpenCV计算机视觉实战(6)------经典计算机视觉算法
OpenCV计算机视觉实战(7)------色彩空间详解
OpenCV计算机视觉实战(8)------图像滤波详解
OpenCV计算机视觉实战(9)------阈值化技术详解
OpenCV计算机视觉实战(10)------形态学操作详解
OpenCV计算机视觉实战(11)------边缘检测详解
OpenCV计算机视觉实战(12)------图像金字塔与特征缩放
OpenCV计算机视觉实战(13)------轮廓检测详解
OpenCV计算机视觉实战(14)------直方图均衡化
OpenCV计算机视觉实战(15)------霍夫变换详解
OpenCV计算机视觉实战(16)------图像分割技术
OpenCV计算机视觉实战(17)------特征点检测详解
OpenCV计算机视觉实战(18)------视频处理详解
OpenCV计算机视觉实战(19)------特征描述符详解
OpenCV计算机视觉实战(20)------光流法运动分析
OpenCV计算机视觉实战(21)------模板匹配详解
OpenCV计算机视觉实战(22)------图像拼接详解
OpenCV计算机视觉实战(23)------目标检测详解
OpenCV计算机视觉实战(24)------目标追踪算法
OpenCV计算机视觉实战(25)------立体视觉详解
OpenCV计算机视觉实战(26)------OpenCV与机器学习
OpenCV计算机视觉实战(27)------深度学习与卷积神经网络
OpenCV计算机视觉实战(28)------深度学习初体验
OpenCV计算机视觉实战(29)------OpenCV DNN模块
OpenCV计算机视觉实战(30)------图像分类模型

相关推荐
九河云1 小时前
汽车轻量化部件智造:碳纤维成型 AI 调控与强度性能数字孪生验证实践
人工智能·汽车·数字化转型
3DVisionary1 小时前
DIC技术如何重新定义汽车板料成形测试
人工智能·汽车·材料力学性能·dic技术·汽车板料·成形极限图·非接触式测量
5***o5001 小时前
深度学习代码库
人工智能·深度学习
2501_941664961 小时前
AI在创意产业的应用:从艺术到娱乐的数字变革
人工智能
没有梦想的咸鱼185-1037-16631 小时前
最新“科研创新与智能化转型“暨AI 智能体(Agent)开发、大语言模型(LLM)本地化部署与RAG/微调优化技术
人工智能·语言模型·自然语言处理·chatgpt·数据分析
沛沛老爹1 小时前
Text2SQL:让自助式数据报表开发从“技术门槛”走向“人人可用”
人工智能·text2sql·rag +·ai入门知识
Predestination王瀞潞2 小时前
Cuda的安装
linux·人工智能·深度学习
二川bro2 小时前
2025深度学习框架对决:TensorFlow与PyPyTorch深度测评
人工智能·深度学习·tensorflow
大雷神2 小时前
MateChat+ DevUI 电商后台管理系统中集成 AI 聊天助手功能
人工智能·ui