人脸识别技术详解
人脸识别技术详解:从原理到实践
人脸识别技术作为计算机视觉领域最具影响力的应用之一,已经从实验室走向了我们生活的方方面面。从手机解锁到机场安检,从考勤系统到智能监控,人脸识别正在正深刻地改变着我们的生活方式和安全管理模式。本文将深入剖析人脸识别技术的原理、发展历程、核心算法,并通过实战代码展示如何构建一个简单的人脸识别系统。
人脸识别技术概述
人脸识别(Face Recognition)是一种基于人的脸部特征信息进行身份识别的生物识别技术。它利用计算机技术对人脸图像进行分析,提取有效的特征信息,并与已知人脸信息进行比对,从而完成身份验证或识别的过程。
与其他生物识别技术(如指纹识别、虹膜识别、声纹识别等)相比,人脸识别具有非接触性、自然性和不易察觉性等独特优势,使其在公共安全、人机交互、金融支付等领域具有不可替代的应用价值。
人脸识别技术的发展历程
人脸识别技术的发展大致可以分为四个阶段:
-
早期探索阶段(20 世纪 60-90 年代):这一阶段主要依赖于人工设计的特征和简单的分类器,如基于几何特征(眼睛间距、鼻子宽度等)的识别方法。识别准确率低,对环境变化敏感。
-
特征工程阶段(2000-2010 年):研究者开始提出更鲁棒的特征表示方法,如局部二值模式(LBP)、尺度不变特征变换(SIFT)等。同时,支持向量机(SVM)等更强大的分类算法被引入人脸识别领域。
-
深度学习初步应用阶段(2010-2015 年):随着深度学习技术的兴起,卷积神经网络(CNN)开始应用于人脸识别。2014 年,DeepFace 等开创性工作将人脸识别准确率提升到了接近人类水平。
-
深度学习成熟阶段(2015 年至今):一系列先进的深度学习模型(如 FaceNet、ArcFace 等)被提出,在 LFW 等标准数据集上的准确率不断刷新纪录。同时,人脸识别技术开始大规模商业化应用。
人脸识别系统的基本流程
一个完整的人脸识别系统通常包含以下几个核心步骤:
- 人脸检测(Face Detection):从输入图像中定位并提取出人脸区域。
- 人脸对齐(Face Alignment):对检测到的人脸进行标准化处理,将人脸调整到统一的角度和尺度。
- 特征提取(Feature Extraction):从对齐后的人脸图像中提取具有判别性的特征向量。
- 特征比对(Feature Matching):将提取的特征向量与数据库中的已知特征进行比对,计算相似度。
- 决策判断(Decision Making):根据相似度分数和预设阈值,判断是否为同一人。
接下来,我们将详细介绍每个步骤的技术原理,并提供相应的代码实现。
人脸检测技术
人脸检测是人脸识别系统的第一步,其任务是在复杂的背景中准确地定位人脸的位置和范围。一个好的人脸检测器需要能够处理不同姿态、表情、光照条件下的人脸,同时具有较高的检测速度和准确率。
人脸检测的主流算法
-
基于 Haar 特征的级联分类器:Viola-Jones 算法是最早广泛应用的实时人脸检测算法,它利用 Haar 特征和 AdaBoost 算法构建级联分类器,能够在普通硬件上实现实时人脸检测。
-
基于 HOG 特征的检测算法:方向梯度直方图(HOG)特征能够很好地描述物体的边缘和形状信息,结合支持向量机(SVM)可以实现较高准确率的人脸检测。
-
基于深度学习的检测算法:近年来,基于深度学习的人脸检测算法取得了突破性进展,如 SSD、YOLO、Faster R-CNN 等通用目标检测算法都可以用于人脸检测,而 MTCNN、RetinaFace 等专门针对人脸优化的算法则在检测精度和速度上表现更为出色。
基于 OpenCV 的人脸检测实现
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,其中包含了多种人脸检测算法的实现。下面我们将使用 OpenCV 提供的 Haar 级联分类器和基于深度学习的人脸检测器来实现人脸检测功能。
import cv2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import os
# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
class FaceDetector:
"""人脸检测器类,支持Haar级联分类器和基于深度学习的检测器"""
def __init__(self):
# 加载Haar级联分类器
self.haar_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
# 加载基于深度学习的人脸检测器
model_path = "res10_300x300_ssd_iter_140000.caffemodel"
config_path = "deploy.prototxt.txt"
# 如果模型文件不存在,则下载
if not os.path.exists(model_path):
print("正在下载深度学习人脸检测模型...")
os.system(f"wget https://github.com/opencv/opencv_3rdparty/raw/dnn_samples_face_detector_20170830/{model_path}")
if not os.path.exists(config_path):
print("正在下载模型配置文件...")
os.system(f"wget https://github.com/opencv/opencv/raw/master/samples/dnn/face_detector/{config_path}")
self.dnn_net = cv2.dnn.readNetFromCaffe(config_path, model_path)
def detect_with_haar(self, image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)):
"""
使用Haar级联分类器检测人脸
参数:
image: 输入图像(BGR格式)
scaleFactor: 图像缩放比例
minNeighbors: 每个候选矩形应保留的邻居数
minSize: 最小人脸尺寸
返回:
检测到的人脸边界框列表,每个边界框为(x, y, width, height)
"""
# 转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 检测人脸
faces = self.haar_cascade.detectMultiScale(
gray,
scaleFactor=scaleFactor,
minNeighbors=minNeighbors,
minSize=minSize
)
# 转换为列表形式返回
return [ (x, y, w, h) for (x, y, w, h) in faces ]
def detect_with_dnn(self, image, confidence_threshold=0.5):
"""
使用基于深度学习的方法检测人脸
参数:
image: 输入图像(BGR格式)
confidence_threshold: 置信度阈值,高于此值的检测结果才会被保留
返回:
检测到的人脸边界框列表,每个边界框为(x1, y1, x2, y2, confidence)
"""
(h, w) = image.shape[:2]
# 预处理图像:缩放至300x300并进行均值减法
blob = cv2.dnn.blobFromImage(
cv2.resize(image, (300, 300)),
1.0,
(300, 300),
(104.0, 177.0, 123.0)
)
# 输入图像到网络并前向传播
self.dnn_net.setInput(blob)
detections = self.dnn_net.forward()
faces = []
# 解析检测结果
for i in range(0, detections.shape[2]):
confidence = detections[0, 0, i, 2]
# 过滤低置信度的检测结果
if confidence > confidence_threshold:
# 计算边界框坐标
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(x1, y1, x2, y2) = box.astype("int")
faces.append( (x1, y1, x2, y2, confidence) )
return faces
def draw_faces(self, image, faces, is_dnn=False):
"""在图像上绘制检测到的人脸边界框"""
# 复制图像以避免修改原图
result = image.copy()
if is_dnn:
# 绘制DNN检测结果
for (x1, y1, x2, y2, confidence) in faces:
cv2.rectangle(result, (x1, y1), (x2, y2), (0, 255, 0), 2)
text = f"置信度: {confidence:.2f}"
cv2.putText(result, text, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
else:
# 绘制Haar检测结果
for (x, y, w, h) in faces:
cv2.rectangle(result, (x, y), (x + w, y + h), (0, 0, 255), 2)
return result
def compare_detectors(self, image_path):
"""比较两种检测算法的结果"""
# 读取图像
image = cv2.imread(image_path)
if image is None:
raise ValueError(f"无法读取图像: {image_path}")
# 转换为RGB格式用于matplotlib显示
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 使用两种算法检测人脸
haar_faces = self.detect_with_haar(image)
dnn_faces = self.detect_with_dnn(image)
# 绘制结果
haar_result = self.draw_faces(image, haar_faces, is_dnn=False)
haar_result_rgb = cv2.cvtColor(haar_result, cv2.COLOR_BGR2RGB)
dnn_result = self.draw_faces(image, dnn_faces, is_dnn=True)
dnn_result_rgb = cv2.cvtColor(dnn_result, cv2.COLOR_BGR2RGB)
# 显示比较结果
plt.figure(figsize=(15, 10))
plt.subplot(121)
plt.imshow(haar_result_rgb)
plt.title(f"Haar级联分类器检测结果 (检测到{len(haar_faces)}张人脸)")
plt.axis('off')
plt.subplot(122)
plt.imshow(dnn_result_rgb)
plt.title(f"基于深度学习的检测结果 (检测到{len(dnn_faces)}张人脸)")
plt.axis('off')
plt.tight_layout()
plt.show()
return haar_faces, dnn_faces
# 演示代码
if __name__ == "__main__":
# 创建人脸检测器实例
detector = FaceDetector()
# 测试单张图片
test_image_path = "test_face.jpg"
# 如果测试图片不存在,下载一张示例图片
if not os.path.exists(test_image_path):
print("正在下载测试图片...")
os.system(f"wget https://picsum.photos/800/600 -O {test_image_path}")
# 比较两种检测算法
haar_faces, dnn_faces = detector.compare_detectors(test_image_path)
# 实时摄像头检测演示
print("按 'q' 键退出摄像头演示")
cap = cv2.VideoCapture(0) # 0表示默认摄像头
while True:
# 读取一帧图像
ret, frame = cap.read()
if not ret:
print("无法获取摄像头图像")
break
# 使用DNN方法检测人脸(速度和准确率平衡较好)
faces = detector.detect_with_dnn(frame)
# 绘制人脸边界框
result = detector.draw_faces(frame, faces, is_dnn=True)
# 显示结果
cv2.imshow('人脸检测演示', result)
# 按q键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
代码解析
上面的代码实现了一个功能完整的人脸检测器类 FaceDetector,它包含两种检测算法:
-
Haar 级联分类器:这是 OpenCV 内置的传统人脸检测算法,优点是速度快、计算量小,适合在资源受限的设备上运行;缺点是对姿态变化和复杂背景的鲁棒性较差。
-
基于深度学习的检测器:使用 Caffe 框架训练的 SSD 模型,对不同姿态、光照条件下的人脸都有较好的检测效果,准确率高于 Haar 方法,但计算量也更大。
代码主要包含以下几个核心功能:
detect_with_haar():使用 Haar 级联分类器检测人脸detect_with_dnn():使用基于深度学习的方法检测人脸draw_faces():在图像上绘制检测到的人脸边界框compare_detectors():比较两种检测算法的结果- 实时摄像头检测演示:通过电脑摄像头实时检测人脸
运行代码时,程序会自动下载所需的模型文件和测试图片(如果不存在)。你可以通过修改 test_image_path 来测试自己的图片,也可以体验实时摄像头检测功能。
人脸对齐技术
人脸对齐是人脸识别流程中的关键步骤,其目的是对检测到的人脸进行标准化处理,将人脸调整到统一的角度、尺度和位置,以便后续的特征提取。
由于人脸具有高度的结构性(眼睛、鼻子、嘴巴等器官的相对位置是固定的),我们可以通过检测面部特征点(facial landmarks)来实现精确的人脸对齐。
人脸特征点检测算法
-
基于回归树的方法:如 Ensemble of Regression Trees (ERT) 算法,通过训练一系列回归树来预测特征点位置,速度快且准确率高。
-
基于深度学习的方法:近年来,基于深度学习的特征点检测方法取得了显著进展,如 Face Alignment Network (FAN) 等,能够在复杂条件下准确检测出大量特征点。
-
特定数量的特征点模型:在实际应用中,常使用 68 点、194 点等固定数量的特征点模型,这些模型定义了人脸关键部位(如眼角、鼻尖、嘴角等)的标准位置。
基于 dlib 的人脸对齐实现
dlib 是一个强大的 C++ 机器学习库,提供了 Python 接口,其中包含了高质量的人脸特征点检测算法。下面我们将使用 dlib 实现人脸特征点检测和对齐功能。
import cv2
import dlib
import numpy as np
import matplotlib.pyplot as plt
import os
from skimage import transform as tf
# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
class FaceAligner:
"""人脸对齐器类,用于检测人脸特征点并进行对齐"""
def __init__(self, predictor_path=None):
"""
初始化人脸对齐器
参数:
predictor_path: 特征点预测器模型路径
"""
# 初始化dlib的人脸检测器
self.detector = dlib.get_frontal_face_detector()
# 初始化特征点预测器
if predictor_path is None or not os.path.exists(predictor_path):
# 如果没有提供模型路径或模型不存在,则下载
predictor_path = "shape_predictor_68_face_landmarks.dat"
if not os.path.exists(predictor_path):
print("正在下载人脸特征点预测器模型...")
os.system(f"wget http://dlib.net/files/{predictor_path}.bz2")
os.system(f"bzip2 -d {predictor_path}.bz2")
self.predictor = dlib.shape_predictor(predictor_path)
# 定义标准人脸的68个特征点位置(归一化坐标)
self.standard_landmarks = self._get_standard_landmarks()
# 对齐后人脸的大小
self.output_size = (150, 180) # 宽度x高度
def _get_standard_landmarks(self):
"""定义标准人脸的68个特征点位置(归一化坐标)"""
# 这些坐标是基于经验值的标准人脸特征点位置
# 实际应用中可以根据需要调整
landmarks = np.array([
# 下巴
[30.2946, 51.6963], [65.5318, 51.5014], [48.0252, 71.7366],
[33.5493, 92.3655], [62.7299, 92.2041],
# 左眼眉
[29.0192, 33.4469], [42.5347, 30.2827], [53.4558, 30.4269],
[64.5912, 33.5346],
# 右眼眉
[27.5126, 34.2009], [40.4188, 31.0826], [51.0401, 31.2051],
[61.6490, 34.3789],
# 鼻子
[39.9307, 41.5781], [49.9999, 41.5781], [45.0353, 50.1397],
[45.0353, 58.7669],
# 左眼
[35.0836, 38.0409], [40.6665, 37.0621], [45.1720, 37.6431],
[42.8959, 41.5896], [37.3840, 41.5896],
# 右眼
[50.8129, 37.0621], [56.3958, 38.0409], [60.9013, 37.6431],
[58.6251, 41.5896], [53.1133, 41.5896],
# 嘴巴
[36.7684, 71.0074], [42.3616, 67.4808], [47.6233, 67.4808],
[52.2164, 71.0074], [47.6233, 73.1850], [42.3616, 73.1850]
])
# 归一化到0-1范围
landmarks = landmarks / np.array([[96, 112]])
return landmarks
def detect_landmarks(self, image):
"""
检测图像中的人脸特征点
参数:
image: 输入图像(BGR格式)
返回:
检测到的人脸矩形和对应的特征点列表
"""
# 转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 检测人脸
faces = self.detector(gray)
result = []
for face in faces:
# 检测特征点
shape = self.predictor(gray, face)
# 将特征点转换为numpy数组
landmarks = np.zeros((68, 2), dtype=np.int32)
for i in range(68):
landmarks[i] = (shape.part(i).x, shape.part(i).y)
# 保存人脸矩形和特征点
face_rect = (face.left(), face.top(), face.right(), face.bottom())
result.append( (face_rect, landmarks) )
return result
def draw_landmarks(self, image, landmarks_list):
"""在图像上绘制特征点和人脸矩形"""
# 复制图像以避免修改原图
result = image.copy()
for face_rect, landmarks in landmarks_list:
# 绘制人脸矩形
x1, y1, x2, y2 = face_rect
cv2.rectangle(result, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 绘制特征点
for (x, y) in landmarks:
cv2.circle(result, (x, y), 2, (0, 0, 255), -1)
return result
def align_face(self, image, landmarks):
"""
根据特征点对齐人脸
参数:
image: 输入图像(BGR格式)
landmarks: 人脸特征点
返回:
对齐后的人脸图像
"""
# 将特征点归一化到图像尺寸
h, w = image.shape[:2]
landmarks_normalized = landmarks / np.array([w, h])
# 标准人脸特征点转换到输出尺寸
standard = self.standard_landmarks * np.array(self.output_size)
# 计算变换矩阵
tform = tf.estimate_transform('similarity', landmarks, standard)
# 应用变换
aligned = tf.warp(image, inverse_map=tform.inverse,
output_shape=self.output_size)
# 转换回uint8格式
aligned = (aligned * 255).astype(np.uint8)
return aligned
def process_image(self, image_path):
"""处理单张图像,检测特征点并进行对齐"""
# 读取图像
image = cv2.imread(image_path)
if image is None:
raise ValueError(f"无法读取图像: {image_path}")
# 检测特征点
landmarks_list = self.detect_landmarks(image)
# 绘制特征点
image_with_landmarks = self.draw_landmarks(image, landmarks_list)
# 对每个人脸进行对齐
aligned_faces = []
for face_rect, landmarks in landmarks_list:
aligned = self.align_face(image, landmarks)
aligned_faces.append(aligned)
# 显示结果
plt.figure(figsize=(15, 10))
# 原始图像及特征点
plt.subplot(1, len(aligned_faces) + 1, 1)
plt.imshow(cv2.cvtColor(image_with_landmarks, cv2.COLOR_BGR2RGB))
plt.title(f"原始图像及特征点 (检测到{len(landmarks_list)}张人脸)")
plt.axis('off')
# 对齐后的人脸
for i, aligned in enumerate(aligned_faces):
plt.subplot(1, len(aligned_faces) + 1, i + 2)
plt.imshow(cv2.cvtColor(aligned, cv2.COLOR_BGR2RGB))
plt.title(f"对齐后的人脸 #{i+1}")
plt.axis('off')
plt.tight_layout()
plt.show()
return image_with_landmarks, aligned_faces
# 演示代码
if __name__ == "__main__":
# 创建人脸对齐器实例
aligner = FaceAligner()
# 测试单张图片
test_image_path = "test_face.jpg"
# 如果测试图片不存在,下载一张示例图片
if not os.path.exists(test_image_path):
print("正在下载测试图片...")
os.system(f"wget https://picsum.photos/800/600 -O {test_image_path}")
# 处理图像
image_with_landmarks, aligned_faces = aligner.process_image(test_image_path)
# 保存对齐后的人脸(如果需要)
for i, face in enumerate(aligned_faces):
cv2.imwrite(f"aligned_face_{i+1}.jpg", face)
print(f"已保存对齐后的人脸: aligned_face_{i+1}.jpg")
代码解析
上面的代码实现了一个人脸对齐器类 FaceAligner,主要功能包括:
-
特征点检测:使用 dlib 的
shape_predictor模型检测人脸的 68 个特征点,这些点覆盖了人脸的关键部位(下巴、眉毛、眼睛、鼻子、嘴巴等)。 -
人脸对齐:通过计算检测到的特征点与标准人脸特征点之间的变换关系,将人脸归一化到统一的尺度和角度。这里使用了相似变换(similarity transform),它可以保持图像的比例和形状,同时进行平移、旋转和缩放。
-
可视化:在原始图像上绘制检测到的人脸矩形和特征点,并展示对齐前后的人脸图像对比。
代码中的 _get_standard_landmarks() 方法定义了一个标准人脸的 68 个特征点位置,这些坐标是基于经验值的,可以根据实际需求进行调整。对齐后的人脸尺寸设置为 150x180 像素,这个尺寸在保留足够细节的同时也不会占用太多计算资源。
运行代码时,程序会自动下载 dlib 所需的特征点预测器模型(如果不存在)。处理图像后,程序会显示原始图像及特征点,以及对齐后的人脸图像,并将对齐后的人脸保存到本地。
人脸对齐是提高后续人脸识别准确率的关键步骤,通过对齐,可以减少姿态、尺度等因素对特征提取的影响,使不同人脸图像之间具有更好的可比性。
人脸特征提取
人脸特征提取是人脸识别系统的核心环节,其目标是从对齐后的人脸图像中提取出具有判别性的特征向量,使得同一人的不同图像具有相似的特征向量,而不同人的图像具有差异较大的特征向量。
人脸特征表示方法
-
传统特征表示:
- 局部二值模式(LBP):通过比较像素与其邻域像素的灰度值来描述图像局部纹理特征。
- Gabor 小波特征:能够在不同尺度和方向上捕捉图像的纹理信息,对光照和姿态变化具有一定的鲁棒性。
- 主成分分析(PCA):通过降维将高维人脸图像映射到低维特征空间,提取最具代表性的特征。
-
基于深度学习的特征表示:
- 卷积神经网络(CNN):通过多层卷积和池化操作自动学习人脸的层次化特征,从低级的边缘、纹理特征到高级的语义特征。
- 特定设计的网络结构:如 FaceNet、ArcFace、SphereFace 等专门为人脸识别任务设计的网络结构,能够学习到更具判别性的特征。
基于深度学习的特征表示方法在近年来的人脸识别任务中表现出了显著优势,已经成为主流的特征提取方法。
基于 FaceNet 的人脸特征提取实现
FaceNet 是 Google 提出的一种端到端的人脸识别系统,它能够直接将人脸图像映射到一个高维特征空间,使得同一人的不同图像在特征空间中距离较近,而不同人的图像距离较远。
下面我们将使用预训练的 FaceNet 模型实现人脸特征提取功能。
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
import tensorflow as tf
from tensorflow.keras.models import load_model
from mtcnn import MTCNN
import pickle
from sklearn.metrics.pairwise import cosine_similarity
# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
# 确保TensorFlow使用GPU(如果可用)
physical_devices = tf.config.list_physical_devices('GPU')
if physical_devices:
try:
tf.config.experimental.set_memory_growth(physical_devices[0], True)
print("GPU已启用")
except:
print("启用GPU时出错,将使用CPU")
class FaceFeatureExtractor:
"""人脸特征提取器类,使用FaceNet模型提取人脸特征向量"""
def __init__(self, model_path=None):
"""
初始化人脸特征提取器
参数:
model_path: FaceNet模型路径
"""
# 初始化MTCNN人脸检测器(用于检测和对齐人脸)
self.detector = MTCNN()
# 加载FaceNet模型
if model_path is None or not os.path.exists(model_path):
# 如果没有提供模型路径或模型不存在,则下载
model_path = "facenet_keras.h5"
if not os.path.exists(model_path):
print("正在下载FaceNet模型...")
# 注意:实际使用时可能需要从可靠来源获取模型
# 这里仅为示例,实际应用中请使用合法渠道获取的模型
os.system(f"wget https://github.com/nyoki-mtl/keras-facenet/releases/download/v0.0.1/{model_path}")
self.model = load_model(model_path)
print("FaceNet模型加载完成")
# 人脸特征数据库
self.face_database = {}
# 特征向量维度
self.embedding_dim = 128
def preprocess_face(self, image, required_size=(160, 160)):
"""
预处理人脸图像,用于输入FaceNet模型
参数:
image: 输入图像(BGR格式)
required_size: 模型要求的输入尺寸
返回:
预处理后的人脸图像列表,如果未检测到人脸则返回空列表
"""
# 转换为RGB格式(MTCNN需要RGB输入)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 检测人脸
results = self.detector.detect_faces(image_rgb)
faces = []
for result in results:
# 提取人脸边界框
x1, y1, width, height = result['box']
x1, y1 = abs(x1), abs(y1)
x2, y2 = x1 + width, y1 + height
# 提取人脸区域
face = image_rgb[y1:y2, x1:x2]
# 调整大小
face = cv2.resize(face, required_size)
# 转换为数组
face_array = np.asarray(face)
# 预处理(FaceNet要求)
face_array = face_array.astype('float32')
mean, std = face_array.mean(), face_array.std()
face_array = (face_array - mean) / std
faces.append( (face_array, (x1, y1, x2, y2)) )
return faces
def extract_embeddings(self, face_pixels):
"""
从人脸图像中提取特征向量(嵌入)
参数:
face_pixels: 预处理后的人脸图像数组
返回:
特征向量
"""
# 增加批次维度
samples = np.expand_dims(face_pixels, axis=0)
# 预测特征向量
yhat = self.model.predict(samples)
return yhat[0]
def add_to_database(self, name, embedding):
"""
将人脸特征添加到数据库
参数:
name: 人脸对应的姓名/标识
embedding: 人脸特征向量
"""
if name not in self.face_database:
self.face_database[name] = []
self.face_database[name].append(embedding)
def save_database(self, path="face_database.pkl"):
"""保存人脸特征数据库到文件"""
with open(path, 'wb') as f:
pickle.dump(self.face_database, f)
print(f"人脸特征数据库已保存到 {path}")
def load_database(self, path="face_database.pkl"):
"""从文件加载人脸特征数据库"""
if os.path.exists(path):
with open(path, 'rb') as f:
self.face_database = pickle.load(f)
print(f"已从 {path} 加载人脸特征数据库,包含 {len(self.face_database)} 个人的特征")
else:
print(f"未找到人脸特征数据库文件 {path},将使用空数据库")
def recognize_face(self, embedding, threshold=0.6):
"""
识别人脸,将输入特征与数据库中的特征进行比对
参数:
embedding: 输入人脸的特征向量
threshold: 相似度阈值,高于此值则认为匹配
返回:
识别结果,包含最匹配的姓名和相似度分数
"""
if not self.face_database:
return ("未知", 0.0)
best_name = "未知"
best_similarity = 0.0
# 与数据库中的每个人脸特征进行比对
for name, embeddings in self.face_database.items():
# 计算与该人所有特征的平均相似度
similarities = [cosine_similarity([embedding], [e])[0][0] for e in embeddings]
avg_similarity = np.mean(similarities)
# 更新最佳匹配
if avg_similarity > best_similarity:
best_similarity = avg_similarity
best_name = name
# 如果最佳相似度低于阈值,则认为是未知人脸
if best_similarity < threshold:
best_name = "未知"
return (best_name, best_similarity)
def process_image(self, image_path, add_to_db=False, name=None):
"""
处理单张图像,提取人脸特征并可选地添加到数据库
参数:
image_path: 图像路径
add_to_db: 是否将提取的特征添加到数据库
name: 当add_to_db为True时,指定人脸对应的姓名/标识
返回:
处理结果,包含带标注的图像和提取的特征
"""
# 读取图像
image = cv2.imread(image_path)
if image is None:
raise ValueError(f"无法读取图像: {image_path}")
# 预处理人脸
faces = self.preprocess_face(image)
if not faces:
print("未检测到人脸")
return (image, [])
# 创建结果图像的副本
result_image = image.copy()
# 存储提取的特征
embeddings = []
# 处理每个人脸
for i, (face_pixels, (x1, y1, x2, y2)) in enumerate(faces):
# 提取特征
embedding = self.extract_embeddings(face_pixels)
embeddings.append(embedding)
# 如果需要,添加到数据库
if add_to_db and name is not None:
self.add_to_database(name, embedding)
label = f"{name} (已添加到数据库)"
else:
# 尝试识别人脸
name_recog, similarity = self.recognize_face(embedding)
label = f"{name_recog} (相似度: {similarity:.2f})"
# 在图像上绘制边界框和标签
cv2.rectangle(result_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(result_image, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# 显示结果
plt.figure(figsize=(10, 8))
plt.imshow(cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB))
plt.title(f"人脸特征提取结果 (检测到{len(faces)}张人脸)")
plt.axis('off')
plt.show()
return (result_image, embeddings)
# 演示代码
if __name__ == "__main__":
# 创建人脸特征提取器实例
extractor = FaceFeatureExtractor()
# 尝试加载已有的特征数据库
extractor.load_database()
# 示例:添加一些人脸到数据库
print("=== 添加人脸到数据库 ===")
# 注意:实际使用时请替换为你自己的人脸图像
test_images = [
("person1.jpg", "张三"),
("person2.jpg", "李四"),
("person3.jpg", "王五")
]
# 下载示例图片(如果不存在)
for img_path, name in test_images:
if not os.path.exists(img_path):
print(f"正在下载 {name} 的示例图片...")
os.system(f"wget https://picsum.photos/seed/{name}/300/400 -O {img_path}")
# 处理图像并添加到数据库
print(f"处理 {name} 的图像...")
extractor.process_image(img_path, add_to_db=True, name=name)
# 保存数据库
extractor.save_database()
# 示例:识别人脸
print("\n=== 识别人脸 ===")
test_recog_image = "test_recognition.jpg"
# 下载测试识别的图片(如果不存在)
if not os.path.exists(test_recog_image):
print("正在下载测试识别的图片...")
os.system(f"wget https://picsum.photos/seed/recog/600/400 -O {test_recog_image}")
# 识别人脸
result_image, embeddings = extractor.process_image(test_recog_image)
# 保存识别结果
cv2.imwrite("recognition_result.jpg", result_image)
print("识别结果已保存到 recognition_result.jpg")
代码解析
上面的代码实现了一个基于 FaceNet 模型的人脸特征提取器类 FaceFeatureExtractor,主要功能包括:
-
人脸检测与预处理:使用 MTCNN(Multi-task Cascaded Convolutional Networks)检测人脸并进行对齐,然后对人脸图像进行标准化处理,使其符合 FaceNet 模型的输入要求。
-
特征提取:使用预训练的 FaceNet 模型将人脸图像映射到 128 维的特征向量(embedding)。这个特征向量具有很好的判别性,同一人的不同图像会映射到特征空间中距离较近的点,而不同人的图像则会映射到距离较远的点。
-
人脸数据库管理:提供了添加人脸特征到数据库、保存和加载数据库的功能,方便构建和维护已知人脸的特征库。
-
人脸识别:通过计算输入人脸特征与数据库中特征的余弦相似度来识别人脸,余弦相似度越接近 1,表示两个人脸越相似。
代码中的 preprocess_face() 方法负责对人脸图像进行预处理,包括人脸检测、裁剪、 resize 和标准化,这是保证特征提取质量的关键步骤。extract_embeddings() 方法使用 FaceNet 模型实际提取特征向量。
在演示代码中,我们首先创建了一个特征提取器实例,然后添加了几个人脸到数据库中,最后使用一张测试图片进行人脸识别。运行代码时,程序会自动下载所需的 FaceNet 模型和示例图片(如果不存在)。
需要注意的是,实际应用中,为了提高识别准确率,通常会为每个人添加多张不同姿态、光照条件下的人脸图像到数据库中,以覆盖更多的变化情况。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)