You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)}")

关键修正说明

  1. 解耦图片检测逻辑:每次处理新图片都会重新执行Face Mesh检测,彻底避免复用旧的检测结果。
  2. 正确提取坐标:从landmarks.landmark中根据点ID获取实际的相对坐标(0-1范围)和绝对像素坐标,确保输出的是图片对应的面部数据。
  3. 清晰的数据结构:用字典存储每个面部特征的坐标,便于后续解析和使用。
  4. 移除全局变量:所有变量通过函数参数传递或内部定义,避免数据污染。
  5. 完整处理所有特征:移除提前返回语句,确保所有指定的面部特征都被处理。
  6. 补充格式转换:MediaPipe要求输入RGB格式图片,补充了cv2 BGR到RGB的转换逻辑。

内容的提问来源于stack exchange,提问作者Raghav Kavimandan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 23:50:44