使用 MediaPipe 檢測面部五官
面部識別和檢測已成為許多現(xiàn)代應(yīng)用中不可或缺的組成部分,包括用于設(shè)備解鎖和社交媒體應(yīng)用中實(shí)時(shí)效果的添加。然而,準(zhǔn)確高效地檢測面部特征,包括鼻子、嘴巴、眼睛甚至虹膜,可能是一個(gè)挑戰(zhàn)性的過程。幸運(yùn)的是,由Google開發(fā)的開源框架MediaPipe提供了一個(gè)解決方案,它提供了強(qiáng)大的預(yù)訓(xùn)練機(jī)器學(xué)習(xí)模型,允許開發(fā)者以高精度跟蹤和分析面部標(biāo)志點(diǎn)。
MediaPipe為計(jì)算機(jī)視覺任務(wù)提供了一套全面的預(yù)構(gòu)建解決方案,包括手部跟蹤、姿態(tài)估計(jì)和面部標(biāo)志點(diǎn)檢測。輕量級設(shè)計(jì)確保了實(shí)時(shí)性能,使其成為集成到移動和基于網(wǎng)絡(luò)的應(yīng)用中的最佳選擇。
本文將重點(diǎn)介紹如何使用MediaPipe檢測和跟蹤特定的面部特征,包括鼻子、嘴巴、眼睛和虹膜。通過本指南的結(jié)束,讀者不僅將全面了解MediaPipe的功能,而且還能夠在自己的項(xiàng)目中實(shí)現(xiàn)面部特征檢測。這將有助于探索如何輕松利用MediaPipe的強(qiáng)大功能檢測這些關(guān)鍵的面部特征。
開始使用
步驟1:安裝必要的庫
pip install opencv-python mediapip
# 避坑:記得先使用 pip install msvc-runtime 命令安裝msvc-runtime, 不然會報(bào)錯(cuò)
# ImportError: DLL load failed while importing _framework_bindings: 動態(tài)鏈接庫(DLL)初始化例程失敗。
步驟2:導(dǎo)入庫
# coding: utf-8
import mediapipe as mp
import cv2
import os
步驟3:初始化 FaceMesh 模型并定義面部特征標(biāo)志點(diǎn)
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):
# 初始化面部網(wǎng)格檢測的參數(shù)
self.static_image_mode = static_image_mode # 是否處理圖像(True)或視頻流(False)
self.max_num_faces = max_num_faces # 要檢測的最大面孔數(shù)
self.refine_landmarks = refine_landmarks # 是否為了更好的精度細(xì)化虹膜標(biāo)志點(diǎn)
self.min_detection_con = min_detection_con # 面部檢測的最小置信度
self.min_tracking_con = min_tracking_con # 跟蹤的最小置信度
# 初始化Mediapipe面部網(wǎng)格解決方案
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)
# 存儲特定面部特征的標(biāo)志點(diǎn)索引
# 這些是Mediapipe為左右眼、虹膜、鼻子和嘴巴預(yù)定義的索引
self.LEFT_EYE_LANDMARKS = [463, 398, 384, 385, 386, 387, 388, 466, 263, 249, 390, 373, 374,
380, 381, 382, 362] # 左眼標(biāo)志點(diǎn)
self.RIGHT_EYE_LANDMARKS = [33, 246, 161, 160, 159, 158, 157, 173, 133, 155, 154, 153, 145,
144, 163, 7] # 右眼標(biāo)志點(diǎn)
self.LEFT_IRIS_LANDMARKS = [474, 475, 477, 476] # 左虹膜標(biāo)志點(diǎn)
self.RIGHT_IRIS_LANDMARKS = [469, 470, 471, 472] # 右虹膜標(biāo)志點(diǎn)
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] # 鼻子標(biāo)志點(diǎn)
self.MOUTH_LANDMARKS = [0, 267, 269, 270, 409, 306, 375, 321, 405, 314, 17, 84, 181, 91, 146, 61, 185, 40, 39,
37] # 嘴巴標(biāo)志點(diǎn)
代碼定義了一個(gè)類,指定為FaceMeshDetector,它使用MediaPipe開發(fā)的FaceMesh解決方案來檢測面部標(biāo)志點(diǎn)。這次檢測的重點(diǎn)是特定區(qū)域,即眼睛、虹膜、鼻子和嘴巴。識別出的標(biāo)志點(diǎn)從圖像中提取出來,并以像素坐標(biāo)的形式返回。
__init__方法
__init__方法的目的是初始化檢測器,這是通過配置MediaPipe的FaceMesh解決方案并存儲眼睛、虹膜、鼻子和嘴巴的特定標(biāo)志點(diǎn)索引來實(shí)現(xiàn)的。
步驟4:處理圖像以檢測面部標(biāo)志點(diǎn),提取我們想要的面部特征的坐標(biāo)
def findMeshInFace(self, img): # 初始化一個(gè)字典來存儲面部特征的標(biāo)志點(diǎn) landmarks = {}
def findMeshInFace(self, img):
# 初始化一個(gè)字典來存儲面部特征的標(biāo)志點(diǎn)
landmarks = {}
# 將輸入圖像轉(zhuǎn)換為RGB,因?yàn)镸ediapipe需要RGB圖像
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 處理圖像以使用FaceMesh模型找到面部標(biāo)志點(diǎn)
results = self.faceMesh.process(imgRGB)
# 檢查是否檢測到任何面孔
if results.multi_face_landmarks:
# 遍歷檢測到的面孔(這里,max_num_faces = 1,所以通常只有一個(gè)面孔)
for faceLms in results.multi_face_landmarks:
# 在標(biāo)志點(diǎn)字典中初始化列表以存儲每個(gè)面部特征的坐標(biāo)
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"] = [] # 存儲所有面部標(biāo)志點(diǎn)以完成面部網(wǎng)格
# 遍歷所有面部標(biāo)志點(diǎn)
for i, lm in enumerate(faceLms.landmark):
h, w, ic = img.shape # 獲取圖像高度、寬度和通道數(shù)
x, y = int(lm.x * w), int(lm.y * h) # 將歸一化坐標(biāo)轉(zhuǎn)換為像素值
# 存儲所有標(biāo)志點(diǎn)的坐標(biāo)
landmarks["all_landmarks"].append((x, y))
# 根據(jù)預(yù)定義的索引存儲特定特征的標(biāo)志點(diǎn)
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)) # 嘴巴
# 返回處理后的圖像和特征標(biāo)志點(diǎn)的字典
return img, landmarks
findMeshInFace方法
這個(gè)方法處理輸入圖像,檢測面部標(biāo)志點(diǎn),并返回帶有面部特征坐標(biāo)的圖像。
步驟5:為圖像定義主函數(shù)
使用細(xì)化虹膜標(biāo)志點(diǎn)的FaceMeshDetector以獲得更好的精度
# 使用細(xì)化虹膜標(biāo)志點(diǎn)的FaceMeshDetector以獲得更好的精度
detector = FaceMeshDetector(refine_landmarks=True)
# 定義我們感興趣的面部特征(眼睛、鼻子、嘴巴、虹膜和所有標(biāo)志點(diǎn))
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>替換為實(shí)際的圖像路徑
save_path = r'D:'
# 使用FaceMeshDetector在當(dāng)前幀中找到面部標(biāo)志點(diǎn)
image, landmarks = detector.findMeshInFace(image)
# 嘗試?yán)L制指定面部部分(本例中為鼻子)的標(biāo)志點(diǎn)
try:
for landmark in landmarks[face_parts[item]]:
# 在每個(gè)標(biāo)志點(diǎn)坐標(biāo)處繪制一個(gè)小綠圈
cv2.circle(image, (landmark[0], landmark[1]), 3, (0, 255, 0), -1) # 圓圈參數(shù):中心,半徑,顏色,厚度
except KeyError:
# 如果未找到指定部分的標(biāo)志點(diǎn),則跳過繪制
pass
# 在幀上顯示正在檢測的面部特征的名稱(例如,“nose_landmarks”)
cv2.putText(image, f"{face_parts[item]}", (20, 70), cv2.FONT_HERSHEY_PLAIN, 5, (0, 255, 0), 5)
# cv2.putText參數(shù):圖像,文本,位置,字體,字體大小,顏色,厚度
# 在標(biāo)題為“Image”的窗口中顯示帶有檢測到的標(biāo)志點(diǎn)的修改后的幀
# cv2.imshow("Image", image)
# 保存圖像
cv2.imwrite(os.path.join(save_path, face_parts[item] + '.jpg'), image)
# 等待按鍵以關(guān)閉顯示的圖像窗口
cv2.waitKey(0)
以下表示上述圖像的每個(gè)面部特征的結(jié)果。
步驟6:為視頻定義主函數(shù)
# 使用細(xì)化的虹膜標(biāo)志點(diǎn)初始化FaceMeshDetector以提高精度
detector = FaceMeshDetector(refine_landmarks=True)
# 定義我們感興趣的面部特征(眼睛、鼻子、嘴巴、虹膜和所有標(biāo)志點(diǎn))
face_parts = ["left_eye_landmarks", "right_eye_landmarks", "nose_landmarks",
"mouth_landmarks", "all_landmarks", "left_iris_landmarks",
"right_iris_landmarks"]
# 指定要檢測的面部特征(索引2在這里指的是鼻子標(biāo)志點(diǎn))
face_part = 2
# 從文件"woman_face.mp4"捕獲視頻
cap = cv2.VideoCapture("<YourVideoPath>") # 使用0表示網(wǎng)絡(luò)攝像頭
# 開始一個(gè)循環(huán),逐幀處理視頻
while True:
# 從視頻捕獲中讀取下一幀
success, image = cap.read()
# 使用FaceMeshDetector在當(dāng)前幀中找到面部標(biāo)志點(diǎn)
image, landmarks = detector.findMeshInFace(image)
# 如果讀取幀不成功(例如,視頻結(jié)束),則跳出循環(huán)
if not success:
break
# 嘗試?yán)L制指定面部部分(本例中為鼻子)的標(biāo)志點(diǎn)
try:
for landmark in landmarks[face_parts[face_part]]:
# 在每個(gè)標(biāo)志點(diǎn)坐標(biāo)處繪制一個(gè)小綠圈
cv2.circle(image, (landmark[0], landmark[1]), 3, (0, 255, 0), -1)
except KeyError:
# 如果未找到指定部分的標(biāo)志點(diǎn),則跳過繪制
pass
# 在幀上顯示正在檢測的面部特征的名稱(例如,“nose_landmarks”)
cv2.putText(image, f"{face_parts[face_part]}", (20, 70), cv2.FONT_HERSHEY_PLAIN, 5, (0, 255, 0), 5)
# 在標(biāo)題為“Image”的窗口中顯示帶有檢測到的標(biāo)志點(diǎn)的修改后的幀
cv2.imshow("Image", image)
# 等待按鍵1毫秒,并檢查用戶是否按下了'q'鍵以退出
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break