面部识别和检测已成为许多现代应用中不可或缺的组成部分,包括用于设备解锁和社交媒体应用中实时效果的添加。然而,准确高效地检测面部特征,包括鼻子、嘴巴、眼睛甚至虹膜,可能是一个挑战性的过程。幸运的是,由Google开发的开源框架MediaPipe提供了一个解决方案,它提供了强大的预训练机器学习模型,允许开发者以高精度跟踪和分析面部标志点。
MediaPipe为计算机视觉任务提供了一套全面的预构建解决方案,包括手部跟踪、姿态估计和面部标志点检测。轻量级设计确保了实时性能,使其成为集成到移动和基于网络的应用中的最佳选择。
本文将重点介绍如何使用MediaPipe检测和跟踪特定的面部特征,包括鼻子、嘴巴、眼睛和虹膜。通过本指南的结束,读者不仅将全面了解MediaPipe的功能,而且还能够在自己的项目中实现面部特征检测。这将有助于探索如何轻松利用MediaPipe的强大功能检测这些关键的面部特征。
开始使用
步骤1:安装必要的库
pip install opencv-python mediapip
# 避坑:记得先使用 pip install msvc-runtime 命令安装msvc-runtime, 不然会报错
# ImportError: DLL load failed while importing _framework_bindings: 动态链接库(DLL)初始化例程失败。
步骤2:导入库
# coding: utf-8
import mediapipe as mp
import cv2
import os
步骤3:初始化 FaceMesh 模型并定义面部特征标志点
class FaceMeshDetector:
def __init__(self, static_image_mode=False, max_num_faces=1, refine_landmarks=False, min_detection_con=0.5,
min_tracking_con=0.5):
# 初始化面部网格检测的参数
self.static_image_mode = static_image_mode # 是否处理图像(True)或视频流(False)
self.max_num_faces = max_num_faces # 要检测的最大面孔数
self.refine_landmarks = refine_landmarks # 是否为了更好的精度细化虹膜标志点
self.min_detection_con = min_detection_con # 面部检测的最小置信度
self.min_tracking_con = min_tracking_con # 跟踪的最小置信度
# 初始化Mediapipe面部网格解决方案
self.mpFaceMesh = mp.solutions.face_mesh
self.faceMesh = self.mpFaceMesh.FaceMesh(self.static_image_mode,
self.max_num_faces,
self.refine_landmarks,
self.min_detection_con,
self.min_tracking_con)
# 存储特定面部特征的标志点索引
# 这些是Mediapipe为左右眼、虹膜、鼻子和嘴巴预定义的索引
self.LEFT_EYE_LANDMARKS = [463, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374,
380, 381, 382, 362] # 左眼标志点
self.RIGHT_EYE_LANDMARKS = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145,
144, 163, 7] # 右眼标志点
self.LEFT_IRIS_LANDMARKS = [474, 475, 477, 476] # 左虹膜标志点
self.RIGHT_IRIS_LANDMARKS = [469, 470, 471, 472] # 右虹膜标志点
self.NOSE_LANDMARKS = [193, 168, 417, 122, 351, 196, 419, 3, 248, 236, 456, 198, 420, 131, 360, 49, 279, 48,
278, 219, 439, 59, 289, 218, 438, 237, 457, 44, 19, 274] # 鼻子标志点
self.MOUTH_LANDMARKS = [0, 267, 269, 270, 409, 306, 375, 321, 405, 314, 17, 84, 181, 91, 146, 61, 185, 40, 39,
37] # 嘴巴标志点
代码定义了一个类,指定为FaceMeshDetector,它使用MediaPipe开发的FaceMesh解决方案来检测面部标志点。这次检测的重点是特定区域,即眼睛、虹膜、鼻子和嘴巴。识别出的标志点从图像中提取出来,并以像素坐标的形式返回。
__init__方法
__init__方法的目的是初始化检测器,这是通过配置MediaPipe的FaceMesh解决方案并存储眼睛、虹膜、鼻子和嘴巴的特定标志点索引来实现的。
步骤4:处理图像以检测面部标志点,提取我们想要的面部特征的坐标
def findMeshInFace(self, img): # 初始化一个字典来存储面部特征的标志点 landmarks = {}
def findMeshInFace(self, img):
# 初始化一个字典来存储面部特征的标志点
landmarks = {}
# 将输入图像转换为RGB,因为Mediapipe需要RGB图像
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 处理图像以使用FaceMesh模型找到面部标志点
results = self.faceMesh.process(imgRGB)
# 检查是否检测到任何面孔
if results.multi_face_landmarks:
# 遍历检测到的面孔(这里,max_num_faces = 1,所以通常只有一个面孔)
for faceLms in results.multi_face_landmarks:
# 在标志点字典中初始化列表以存储每个面部特征的坐标
landmarks["left_eye_landmarks"] = []
landmarks["right_eye_landmarks"] = []
landmarks["left_iris_landmarks"] = []
landmarks["right_iris_landmarks"] = []
landmarks["nose_landmarks"] = []
landmarks["mouth_landmarks"] = []
landmarks["all_landmarks"] = [] # 存储所有面部标志点以完成面部网格
# 遍历所有面部标志点
for i, lm in enumerate(faceLms.landmark):
h, w, ic = img.shape # 获取图像高度、宽度和通道数
x, y = int(lm.x * w), int(lm.y * h) # 将归一化坐标转换为像素值
# 存储所有标志点的坐标
landmarks["all_landmarks"].append((x, y))
# 根据预定义的索引存储特定特征的标志点
if i in self.LEFT_EYE_LANDMARKS:
landmarks["left_eye_landmarks"].append((x, y)) # 左眼
if i in self.RIGHT_EYE_LANDMARKS:
landmarks["right_eye_landmarks"].append((x, y)) # 右眼
if i in self.LEFT_IRIS_LANDMARKS:
landmarks["left_iris_landmarks"].append((x, y)) # 左虹膜
if i in self.RIGHT_IRIS_LANDMARKS:
landmarks["right_iris_landmarks"].append((x, y)) # 右虹膜
if i in self.NOSE_LANDMARKS:
landmarks["nose_landmarks"].append((x, y)) # 鼻子
if i in self.MOUTH_LANDMARKS:
landmarks["mouth_landmarks"].append((x, y)) # 嘴巴
# 返回处理后的图像和特征标志点的字典
return img, landmarks
findMeshInFace方法
这个方法处理输入图像,检测面部标志点,并返回带有面部特征坐标的图像。
步骤5:为图像定义主函数
使用细化虹膜标志点的FaceMeshDetector以获得更好的精度
# 使用细化虹膜标志点的FaceMeshDetector以获得更好的精度
detector = FaceMeshDetector(refine_landmarks=True)
# 定义我们感兴趣的面部特征(眼睛、鼻子、嘴巴、虹膜和所有标志点)
face_parts = ["left_eye_landmarks", "right_eye_landmarks", "nose_landmarks",
"mouth_landmarks", "all_landmarks", "left_iris_landmarks",
"right_iris_landmarks"]
for item in range(len(face_parts)):
# 从指定的文件路径读取图像
image = cv2.imread(r"D:\test.jpg") # 将<YourImagePath>替换为实际的图像路径
save_path = r'D:'
# 使用FaceMeshDetector在当前帧中找到面部标志点
image, landmarks = detector.findMeshInFace(image)
# 尝试绘制指定面部部分(本例中为鼻子)的标志点
try:
for landmark in landmarks[face_parts[item]]:
# 在每个标志点坐标处绘制一个小绿圈
cv2.circle(image, (landmark[0], landmark[1]), 3, (0, 255, 0), -1) # 圆圈参数:中心,半径,颜色,厚度
except KeyError:
# 如果未找到指定部分的标志点,则跳过绘制
pass
# 在帧上显示正在检测的面部特征的名称(例如,“nose_landmarks”)
cv2.putText(image, f"{face_parts[item]}", (20, 70), cv2.FONT_HERSHEY_PLAIN, 5, (0, 255, 0), 5)
# cv2.putText参数:图像,文本,位置,字体,字体大小,颜色,厚度
# 在标题为“Image”的窗口中显示带有检测到的标志点的修改后的帧
# cv2.imshow("Image", image)
# 保存图像
cv2.imwrite(os.path.join(save_path, face_parts[item] + '.jpg'), image)
# 等待按键以关闭显示的图像窗口
cv2.waitKey(0)
以下表示上述图像的每个面部特征的结果。
步骤6:为视频定义主函数
# 使用细化的虹膜标志点初始化FaceMeshDetector以提高精度
detector = FaceMeshDetector(refine_landmarks=True)
# 定义我们感兴趣的面部特征(眼睛、鼻子、嘴巴、虹膜和所有标志点)
face_parts = ["left_eye_landmarks", "right_eye_landmarks", "nose_landmarks",
"mouth_landmarks", "all_landmarks", "left_iris_landmarks",
"right_iris_landmarks"]
# 指定要检测的面部特征(索引2在这里指的是鼻子标志点)
face_part = 2
# 从文件"woman_face.mp4"捕获视频
cap = cv2.VideoCapture("<YourVideoPath>") # 使用0表示网络摄像头
# 开始一个循环,逐帧处理视频
while True:
# 从视频捕获中读取下一帧
success, image = cap.read()
# 使用FaceMeshDetector在当前帧中找到面部标志点
image, landmarks = detector.findMeshInFace(image)
# 如果读取帧不成功(例如,视频结束),则跳出循环
if not success:
break
# 尝试绘制指定面部部分(本例中为鼻子)的标志点
try:
for landmark in landmarks[face_parts[face_part]]:
# 在每个标志点坐标处绘制一个小绿圈
cv2.circle(image, (landmark[0], landmark[1]), 3, (0, 255, 0), -1)
except KeyError:
# 如果未找到指定部分的标志点,则跳过绘制
pass
# 在帧上显示正在检测的面部特征的名称(例如,“nose_landmarks”)
cv2.putText(image, f"{face_parts[face_part]}", (20, 70), cv2.FONT_HERSHEY_PLAIN, 5, (0, 255, 0), 5)
# 在标题为“Image”的窗口中显示带有检测到的标志点的修改后的帧
cv2.imshow("Image", image)
# 等待按键1毫秒,并检查用户是否按下了'q'键以退出
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break