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

MediaPipe多人物姿态关键点提取异常:仅导出单人数据求助

问题:MediaPipe PoseLandmarker多人关键点导出失败

我有一段包含4人的视频,使用MediaPipe的PoseLandmarker时能在画面中标注出所有人物的姿态关键点,但将关键点导出到文本文件时,仅能提取到单个人物的数据。以下是我的实现代码:

import cv2
import numpy as np
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
from mediapipe.framework.formats import landmark_pb2

model_path = 'path'
video_path = 'path'

num_poses = 4
min_pose_detection_confidence = 0.5
min_pose_presence_confidence = 0.5
min_tracking_confidence = 0.5

landmark_names = {
    0: "nose",
    1: "left_eye_inner",
    2: "left_eye",
    3: "left_eye_outer",
    4: "right_eye_inner",
    5: "right_eye",
    6: "right_eye_outer",
    7: "left_ear",
    8: "right_ear",
    9: "mouth_left",
    10: "mouth_right",
    11: "left_shoulder",
    12: "right_shoulder",
    13: "left_elbow",
    14: "right_elbow",
    15: "left_wrist",
    16: "right_wrist",
    17: "left_pinky",
    18: "right_pinky",
    19: "left_index",
    20: "right_index",
    21: "left_thumb",
    22: "right_thumb",
    23: "left_hip",
    24: "right_hip",
    25: "left_knee",
    26: "right_knee",
    27: "left_ankle",
    28: "right_ankle",
    29: "left_heel",
    30: "right_heel",
    31: "left_foot_index",
    32: "right_foot_index"
}

def draw_landmarks_on_image(rgb_image, detection_result):
    pose_landmarks_list = detection_result.pose_landmarks
    annotated_image = np.copy(rgb_image)

    # Loop through the detected poses to visualize.
    for idx, pose_landmarks in enumerate(pose_landmarks_list):
        pose_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
        pose_landmarks_proto.landmark.extend([
            landmark_pb2.NormalizedLandmark(
                x=landmark.x,
                y=landmark.y,
                z=landmark.z) for landmark in pose_landmarks
        ])

        mp.solutions.drawing_utils.draw_landmarks(
            annotated_image,
            pose_landmarks_proto,
            mp.solutions.pose.POSE_CONNECTIONS,
            mp.solutions.drawing_styles.get_default_pose_landmarks_style(),
            landmark_tracking_data=None,
            person_id=idx)
    return annotated_image

base_options = python.BaseOptions(model_asset_path=model_path)
options = vision.PoseLandmarkerOptions(
    base_options=base_options,
    running_mode=vision.RunningMode.IMAGE,  # Change to IMAGE mode
    num_poses=num_poses,
    min_pose_detection_confidence=min_pose_detection_confidence,
    min_pose_presence_confidence=min_pose_presence_confidence,
    min_tracking_confidence=min_tracking_confidence,
    output_segmentation_masks=False
)
with vision.PoseLandmarker.create_from_options(options) as landmarker:
    # Open the video file
    cap = cv2.VideoCapture(video_path)
    # Get video properties
    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    # Extract frames at regular intervals (every second)
    frame_interval = int(fps)  # Extract frame every second
    for frame_num in range(0, total_frames, frame_interval):
        # Set frame position
        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)
        # Read frame
        success, image = cap.read()
        if not success:
            print(f"Failed to read frame at {frame_num}th second.")
            continue
        # Convert the frame received from OpenCV to a MediaPipe's Image object.
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
        timestamp_ms = int(frame_num / fps * 1000)  # Calculate timestamp in milliseconds
        minutes = int(timestamp_ms / (1000 * 60))
        seconds = int((timestamp_ms / 1000) % 60)
        detection_result = landmarker.detect(mp_image)  # Changed method to 'detect'
        with open('green_keypoint_data.txt', 'a') as f:
            pose_landmarks_list = detection_result.pose_landmarks
            for pose_landmarks in pose_landmarks_list:
                for landmark_id, landmark in enumerate(pose_landmarks):
                    landmark_name = landmark_names.get(landmark_id, f"Unknown Landmark:{landmark_id}")f.write(f'Landmark ID: {landmark_id}, Landmark Name: {landmark_name}, Timestamp:{minutes:02d}:{seconds:02d}, Frame Number: {frame_num}, Keypoint: ({landmark.x}, {landmark.y}, {landmark.z})\n')
        # Wait for a key press to proceed to the next frame
        if cv2.waitKey(0) & 0xFF == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()
问题排查与修复

核心错误点

代码中文件写入逻辑存在语法错误:

landmark_name = landmark_names.get(landmark_id, f"Unknown Landmark:{landmark_id}")f.write(...)

landmark_name的赋值语句末尾没有换行或分号,导致f.write()被错误地拼接在同一行,Python解释器无法正确解析后续循环逻辑,最终只执行了第一个人的第一个关键点写入,后续数据全部被跳过。

修复后的完整代码

修正语法错误,并增加人物ID标识(方便区分不同人的关键点),修改后的代码如下:

import cv2
import numpy as np
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
from mediapipe.framework.formats import landmark_pb2

model_path = 'path'
video_path = 'path'

num_poses = 4
min_pose_detection_confidence = 0.5
min_pose_presence_confidence = 0.5
min_tracking_confidence = 0.5

landmark_names = {
    0: "nose",
    1: "left_eye_inner",
    2: "left_eye",
    3: "left_eye_outer",
    4: "right_eye_inner",
    5: "right_eye",
    6: "right_eye_outer",
    7: "left_ear",
    8: "right_ear",
    9: "mouth_left",
    10: "mouth_right",
    11: "left_shoulder",
    12: "right_shoulder",
    13: "left_elbow",
    14: "right_elbow",
    15: "left_wrist",
    16: "right_wrist",
    17: "left_pinky",
    18: "right_pinky",
    19: "left_index",
    20: "right_index",
    21: "left_thumb",
    22: "right_thumb",
    23: "left_hip",
    24: "right_hip",
    25: "left_knee",
    26: "right_knee",
    27: "left_ankle",
    28: "right_ankle",
    29: "left_heel",
    30: "right_heel",
    31: "left_foot_index",
    32: "right_foot_index"
}

def draw_landmarks_on_image(rgb_image, detection_result):
    pose_landmarks_list = detection_result.pose_landmarks
    annotated_image = np.copy(rgb_image)

    # Loop through the detected poses to visualize.
    for idx, pose_landmarks in enumerate(pose_landmarks_list):
        pose_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
        pose_landmarks_proto.landmark.extend([
            landmark_pb2.NormalizedLandmark(
                x=landmark.x,
                y=landmark.y,
                z=landmark.z) for landmark in pose_landmarks
        ])

        mp.solutions.drawing_utils.draw_landmarks(
            annotated_image,
            pose_landmarks_proto,
            mp.solutions.pose.POSE_CONNECTIONS,
            mp.solutions.drawing_styles.get_default_pose_landmarks_style(),
            landmark_tracking_data=None,
            person_id=idx)
    return annotated_image

base_options = python.BaseOptions(model_asset_path=model_path)
options = vision.PoseLandmarkerOptions(
    base_options=base_options,
    running_mode=vision.RunningMode.IMAGE,  # Change to IMAGE mode
    num_poses=num_poses,
    min_pose_detection_confidence=min_pose_detection_confidence,
    min_pose_presence_confidence=min_pose_presence_confidence,
    min_tracking_confidence=min_tracking_confidence,
    output_segmentation_masks=False
)
with vision.PoseLandmarker.create_from_options(options) as landmarker:
    # Open the video file
    cap = cv2.VideoCapture(video_path)
    # Get video properties
    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    # Extract frames at regular intervals (every second)
    frame_interval = int(fps)  # Extract frame every second
    
    # 提前清空文件,避免多次运行数据重复
    with open('green_keypoint_data.txt', 'w') as f:
        pass
    
    for frame_num in range(0, total_frames, frame_interval):
        # Set frame position
        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)
        # Read frame
        success, image = cap.read()
        if not success:
            print(f"Failed to read frame at {frame_num}th second.")
            continue
        # Convert the frame received from OpenCV to a MediaPipe's Image object.
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
        timestamp_ms = int(frame_num / fps * 1000)  # Calculate timestamp in milliseconds
        minutes = int(timestamp_ms / (1000 * 60))
        seconds = int((timestamp_ms / 1000) % 60)
        detection_result = landmarker.detect(mp_image)  # Changed method to 'detect'
        
        with open('green_keypoint_data.txt', 'a') as f:
            pose_landmarks_list = detection_result.pose_landmarks
            # 增加人物索引,区分不同个体
            for person_idx, pose_landmarks in enumerate(pose_landmarks_list):
                for landmark_id, landmark in enumerate(pose_landmarks):
                    landmark_name = landmark_names.get(landmark_id, f"Unknown Landmark:{landmark_id}")
                    # 写入时添加人物ID
                    f.write(f'Person ID: {person_idx}, Landmark ID: {landmark_id}, Landmark Name: {landmark_name}, Timestamp:{minutes:02d}:{seconds:02d}, Frame Number: {frame_num}, Keypoint: ({landmark.x}, {landmark.y}, {landmark.z})\n')
        
        # Wait for a key press to proceed to the next frame
        if cv2.waitKey(0) & 0xFF == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()

额外优化说明

  1. 语法修正:将landmark_name赋值与f.write()拆分为两行,确保循环逻辑正常执行。
  2. 人物ID标识:在写入数据时增加Person ID,方便后续区分不同人物的关键点。
  3. 文件初始化:循环前先用'w'模式清空文件,避免多次运行导致数据重复追加。
  4. IO优化:如果视频帧数较多,可以将文件打开放在循环外,减少频繁打开/关闭文件的开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 10:44:51