如何提取FaceMesh鼻尖顶点坐标并计算与食指指尖距离?
问题描述
已在实时单帧中集成FaceMesh与HandMesh,当前可通过print(results.multi_face_landmarks[0])打印所有FaceMesh顶点坐标,但无法单独提取指定顶点(如第4号鼻尖顶点)的坐标。后续需计算该鼻尖顶点与食指指尖的距离,以此提醒用户纠正摸鼻习惯,现有代码已导入math库用于距离计算,请求指导如何获取指定顶点的坐标。
现有代码如下:
import cv2 import numpy as np import mediapipe as mp ################################################################# import math def calculate_distance(point1, point2): x1, y1, z1 = point1 x2, y2, z2 = point2 distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) return distance ################################################################## cap = cv2.VideoCapture(0) mpDraw = mp.solutions.drawing_utils mpFaceMesh = mp.solutions.face_mesh mpHands = mp.solutions.hands faceMesh = mpFaceMesh.FaceMesh(max_num_faces=2) handsMesh = mpHands.Hands(max_num_hands=2) drawSpecFace = mpDraw.DrawingSpec(thickness=1, circle_radius=1) drawSpecHands = mpDraw.DrawingSpec(thickness=1, circle_radius=1) both_detected = False # Initialize detection flag while True: success, img = cap.read() imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) results = faceMesh.process(imgRGB) if results.multi_face_landmarks: for faceLms in results.multi_face_landmarks: mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACEMESH_CONTOURS, drawSpecFace, drawSpecFace) face = results.multi_face_landmarks[0]; results2 = handsMesh.process(imgRGB) if results2.multi_hand_landmarks: for handLms in results2.multi_hand_landmarks: mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, drawSpecHands, drawSpecHands) # Check if both face and hands are detected if results.multi_face_landmarks and results2.multi_hand_landmarks: both_detected = True cv2.putText(img, "Why Are Your Hands Near Face?", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (34,139,34), 2, cv2.LINE_AA) else: both_detected = False print (results.multi_face_landmarks[0]) cv2.imshow("Image", img) cv2.waitKey(1)
运行结果如图:
解决方案
1. 提取指定FaceMesh顶点(鼻尖#4)
FaceMesh的multi_face_landmarks返回的每个face_landmarks对象包含468个顶点,按索引顺序排列。直接通过索引访问目标顶点,提取其x、y、z属性即可:
# 提取第4号鼻尖顶点坐标(归一化值) nose_tip = results.multi_face_landmarks[0].landmark[4] nose_point = (nose_tip.x, nose_tip.y, nose_tip.z)
2. 提取HandMesh食指指尖(#8)
HandMesh中食指指尖对应的索引是8,同样通过索引提取坐标:
# 提取食指指尖坐标(归一化值) index_finger_tip = results2.multi_hand_landmarks[0].landmark[8] finger_point = (index_finger_tip.x, index_finger_tip.y, index_finger_tip.z)
3. 距离计算与提醒逻辑优化
结合已有的calculate_distance函数,设置距离阈值,当指尖与鼻尖距离小于阈值时触发提醒。
修改后的完整代码
import cv2 import numpy as np import mediapipe as mp import math def calculate_distance(point1, point2): x1, y1, z1 = point1 x2, y2, z2 = point2 distance = math.sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z2 - z1)**2) return distance cap = cv2.VideoCapture(0) mpDraw = mp.solutions.drawing_utils mpFaceMesh = mp.solutions.face_mesh mpHands = mp.solutions.hands faceMesh = mpFaceMesh.FaceMesh(max_num_faces=2) handsMesh = mpHands.Hands(max_num_hands=2) drawSpecFace = mpDraw.DrawingSpec(thickness=1, circle_radius=1) drawSpecHands = mpDraw.DrawingSpec(thickness=1, circle_radius=1) # 设置距离阈值(归一化坐标下的阈值,可根据实际情况调整) DISTANCE_THRESHOLD = 0.1 while True: success, img = cap.read() if not success: break imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_h, img_w, _ = img.shape results = faceMesh.process(imgRGB) results2 = handsMesh.process(imgRGB) # 绘制FaceMesh和HandMesh if results.multi_face_landmarks: for faceLms in results.multi_face_landmarks: mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACEMESH_CONTOURS, drawSpecFace, drawSpecFace) if results2.multi_hand_landmarks: for handLms in results2.multi_hand_landmarks: mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, drawSpecHands, drawSpecHands) # 当人脸和手都被检测到,计算距离并判断 if results.multi_face_landmarks and results2.multi_hand_landmarks: # 提取鼻尖(索引4)坐标 nose_tip = results.multi_face_landmarks[0].landmark[4] nose_point = (nose_tip.x, nose_tip.y, nose_tip.z) # 提取食指指尖(索引8)坐标 index_finger_tip = results2.multi_hand_landmarks[0].landmark[8] finger_point = (index_finger_tip.x, index_finger_tip.y, index_finger_tip.z) # 计算距离 dist = calculate_distance(nose_point, finger_point) # 根据距离显示提醒 if dist < DISTANCE_THRESHOLD: cv2.putText(img, "别摸鼻子!", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2, cv2.LINE_AA) else: cv2.putText(img, "姿势正确", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (34,139,34), 2, cv2.LINE_AA) else: cv2.putText(img, "未检测到人脸或手", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (128,128,128), 2, cv2.LINE_AA) cv2.imshow("Image", img) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
关键说明
- 所有顶点的
x、y是归一化到[0,1]的值,对应图像的宽度和高度;z值代表深度,以相机焦距为基准。 - 阈值
DISTANCE_THRESHOLD可根据实际测试调整,确保提醒逻辑准确。 - 代码中添加了退出逻辑(按q键退出),避免程序无法正常关闭。
内容的提问来源于stack exchange,提问作者CodeKimchi CK
相关产品推荐
相关产品推荐

