MediaPipe Face Mesh提取坐标异常:所有图片输出结果一致
MediaPipe Face Mesh提取坐标重复问题的解决方案
核心问题分析
- 全局变量复用导致坐标固定:初始代码仅对
johnny-depp-sunglasses-hat-smile-wallpaper.jpg执行了一次face_mesh.process(img),后续测试其他图片时,未重新调用检测逻辑,始终复用第一次生成的全局landmarks变量,导致坐标完全一致。 - 函数逻辑错误,未提取实际坐标:
extract_landmarks函数直接复用MediaPipe预定义的轮廓点对ID(比如FACEMESH_LIPS是固定的点ID组合),没有从landmarks中提取对应ID的实际坐标值,输出的是固定ID对而非图片的面部坐标。 - 列表推导式逻辑混乱:生成
my_ans的推导式错误嵌套循环,将轮廓名称与点ID对强行绑定,未关联实际坐标数据。 - 提前返回导致功能不完整:函数在处理完第一个轮廓后就执行
return,后续面部特征未被处理。 - 全局列表累积数据:
xs, ys, zs为全局列表,每次调用函数都会追加数据,造成数据污染。
修正后的完整代码
1. 图片处理与检测封装
import mediapipe as mp import cv2 import matplotlib.pyplot as plt import json import os def process_image(img_path): """处理单张图片,返回Face Mesh检测结果""" mp_face_mesh = mp.solutions.face_mesh face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True) img = cv2.imread(img_path) if img is None: raise ValueError(f"无法读取图片: {img_path}") # MediaPipe要求输入RGB格式,转换cv2默认的BGR格式 rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) results = face_mesh.process(rgb_img) if not results.multi_face_landmarks: raise ValueError("图片中未检测到人脸") return img, results.multi_face_landmarks[0]
2. 坐标提取与可视化函数
def extract_landmarks(img, landmarks, contour_list, output_json_path): """提取指定面部特征的坐标,保存为JSON并可视化""" img_copy = img.copy() height, width = img.shape[:2] # 存储所有特征的坐标数据 all_features = {} # 遍历每个面部特征 for contour_name, contour_points in zip(contour_list.keys(), contour_list.values()): feature_coords = [] for src_id, tar_id in contour_points: # 获取点的相对坐标(0-1范围)和绝对像素坐标 src_landmark = landmarks.landmark[src_id] tar_landmark = landmarks.landmark[tar_id] src_x_abs = int(src_landmark.x * width) src_y_abs = int(src_landmark.y * height) tar_x_abs = int(tar_landmark.x * width) tar_y_abs = int(tar_landmark.y * height) # 保存坐标数据 feature_coords.append({ "source_relative": {"x": src_landmark.x, "y": src_landmark.y, "z": src_landmark.z}, "target_relative": {"x": tar_landmark.x, "y": tar_landmark.y, "z": tar_landmark.z}, "source_absolute": {"x": src_x_abs, "y": src_y_abs}, "target_absolute": {"x": tar_x_abs, "y": tar_y_abs} }) # 绘制轮廓线 cv2.line(img_copy, (src_x_abs, src_y_abs), (tar_x_abs, tar_y_abs), (255, 255, 255), 2) all_features[contour_name] = feature_coords # 可视化结果 plt.figure(figsize=(15, 15)) plt.imshow(cv2.cvtColor(img_copy, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.show() # 保存为JSON文件 with open(output_json_path, 'w', encoding='utf-8') as f: json.dump(all_features, f, indent=4, ensure_ascii=False) print(f"坐标已保存至: {output_json_path}") return all_features
3. 主调用逻辑
if __name__ == "__main__": # 定义需要提取的面部特征 mp_face_mesh = mp.solutions.face_mesh CONTOUR_LIST = { "FACEMESH_LIPS": mp_face_mesh.FACEMESH_LIPS, "FACEMESH_FACE_OVAL": mp_face_mesh.FACEMESH_FACE_OVAL, "FACEMESH_LEFT_IRIS": mp_face_mesh.FACEMESH_LEFT_IRIS, "FACEMESH_LEFT_EYEBROW": mp_face_mesh.FACEMESH_LEFT_EYEBROW, "FACEMESH_LEFT_EYE": mp_face_mesh.FACEMESH_LEFT_EYE, "FACEMESH_RIGHT_IRIS": mp_face_mesh.FACEMESH_RIGHT_IRIS, "FACEMESH_RIGHT_EYEBROW": mp_face_mesh.FACEMESH_RIGHT_EYEBROW, "FACEMESH_RIGHT_EYE": mp_face_mesh.FACEMESH_RIGHT_EYE } # 测试不同图片,替换为你的测试图片路径 test_images = [ "./johnny-depp-sunglasses-hat-smile-wallpaper.jpg", "./test_face_2.jpg" ] for idx, img_path in enumerate(test_images): try: img, landmarks = process_image(img_path) output_path = f"./face_landmarks_{idx}.json" extract_landmarks(img, landmarks, CONTOUR_LIST, output_path) except Exception as e: print(f"处理图片 {img_path} 失败: {str(e)}")
关键修正说明
- 解耦图片检测逻辑:每次处理新图片都会重新执行Face Mesh检测,彻底避免复用旧的检测结果。
- 正确提取坐标:从
landmarks.landmark中根据点ID获取实际的相对坐标(0-1范围)和绝对像素坐标,确保输出的是图片对应的面部数据。 - 清晰的数据结构:用字典存储每个面部特征的坐标,便于后续解析和使用。
- 移除全局变量:所有变量通过函数参数传递或内部定义,避免数据污染。
- 完整处理所有特征:移除提前返回语句,确保所有指定的面部特征都被处理。
- 补充格式转换:MediaPipe要求输入RGB格式图片,补充了cv2 BGR到RGB的转换逻辑。
内容的提问来源于stack exchange,提问作者Raghav Kavimandan
相关产品推荐
相关产品推荐

