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()
额外优化说明
- 语法修正:将
landmark_name赋值与f.write()拆分为两行,确保循环逻辑正常执行。 - 人物ID标识:在写入数据时增加
Person ID,方便后续区分不同人物的关键点。 - 文件初始化:循环前先用
'w'模式清空文件,避免多次运行导致数据重复追加。 - IO优化:如果视频帧数较多,可以将文件打开放在循环外,减少频繁打开/关闭文件的开销。
内容的提问来源于stack exchange,提问作者Wihar
相关产品推荐
相关产品推荐

