| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- #!/usr/bin/env python3
- """
- 提取人体姿态骨骼图 v2 - 使用MediaPipe Pose
- 针对不同图片使用不同参数
- """
- import mediapipe as mp
- import cv2
- import numpy as np
- import json
- import os
- mp_pose = mp.solutions.pose
- mp_drawing = mp.solutions.drawing_utils
- def extract_pose(image_path, output_dir, img_id, complexity=2, conf=0.5):
- """提取单张图片的姿态骨骼"""
- img = cv2.imread(image_path)
- if img is None:
- return None
-
- h, w = img.shape[:2]
- img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
-
- with mp_pose.Pose(
- static_image_mode=True,
- model_complexity=complexity,
- enable_segmentation=False,
- min_detection_confidence=conf
- ) as pose:
- results = pose.process(img_rgb)
-
- if not results.pose_landmarks:
- return None
-
- # 创建黑色背景的骨骼图
- skeleton_img = np.zeros((h, w, 3), dtype=np.uint8)
-
- # 绘制骨骼
- mp_drawing.draw_landmarks(
- skeleton_img,
- results.pose_landmarks,
- mp_pose.POSE_CONNECTIONS,
- landmark_drawing_spec=mp_drawing.DrawingSpec(
- color=(255, 255, 255), thickness=4, circle_radius=6
- ),
- connection_drawing_spec=mp_drawing.DrawingSpec(
- color=(180, 180, 180), thickness=3
- )
- )
-
- # 保存骨骼图
- skeleton_path = os.path.join(output_dir, f"{img_id}_pose_skeleton.png")
- cv2.imwrite(skeleton_path, skeleton_img)
-
- # 提取关键点坐标
- landmark_names = [lm.name for lm in mp_pose.PoseLandmark]
- landmarks_data = {}
-
- for i, landmark in enumerate(results.pose_landmarks.landmark):
- name = landmark_names[i] if i < len(landmark_names) else f"landmark_{i}"
- landmarks_data[name] = {
- "x": round(landmark.x, 4),
- "y": round(landmark.y, 4),
- "z": round(landmark.z, 4),
- "visibility": round(landmark.visibility, 4),
- "pixel_x": int(landmark.x * w),
- "pixel_y": int(landmark.y * h)
- }
-
- # 保存关键点JSON
- json_path = os.path.join(output_dir, f"{img_id}_pose_keypoints.json")
- with open(json_path, 'w', encoding='utf-8') as f:
- json.dump({
- "image_id": img_id,
- "image_size": {"width": w, "height": h},
- "model_complexity": complexity,
- "detection_confidence": conf,
- "landmarks": landmarks_data,
- "skeleton_image": f"{img_id}_pose_skeleton.png"
- }, f, ensure_ascii=False, indent=2)
-
- return landmarks_data
- def main():
- input_dir = "input"
- output_dir = "output/features/pose_skeleton"
-
- # 针对不同图片的参数配置
- # img_5是手臂特写,无法检测全身姿态,记录为局部
- configs = {
- "img_1": {"complexity": 2, "conf": 0.5},
- "img_2": {"complexity": 0, "conf": 0.1},
- "img_3": {"complexity": 2, "conf": 0.5},
- "img_4": {"complexity": 2, "conf": 0.5},
- "img_5": None, # 手臂特写,无法检测全身
- "img_6": {"complexity": 0, "conf": 0.1},
- "img_7": {"complexity": 0, "conf": 0.1},
- "img_8": {"complexity": 2, "conf": 0.5},
- "img_9": {"complexity": 0, "conf": 0.1},
- }
-
- results_summary = []
-
- for i in range(1, 10):
- img_id = f"img_{i}"
- image_path = os.path.join(input_dir, f"{img_id}.jpg")
-
- if not os.path.exists(image_path):
- continue
-
- config = configs.get(img_id)
- print(f"处理 {img_id}...", end=" ")
-
- if config is None:
- print("跳过(局部特写,无法检测全身姿态)")
- results_summary.append({"image_id": img_id, "detected": False, "reason": "partial_view"})
- continue
-
- landmarks = extract_pose(image_path, output_dir, img_id,
- config["complexity"], config["conf"])
-
- if landmarks:
- print(f"✓ 骨骼图已保存")
- results_summary.append({
- "image_id": img_id,
- "detected": True,
- "keypoints_file": f"{img_id}_pose_keypoints.json",
- "skeleton_file": f"{img_id}_pose_skeleton.png"
- })
- else:
- print("✗ 未检测到姿态")
- results_summary.append({"image_id": img_id, "detected": False, "reason": "not_detected"})
-
- # 建立mapping.json
- pose_segment_map = {
- "img_1": {"segment": "段落1.1", "feature": "女性人物姿态(站立侧身作画)", "element": "元素1"},
- "img_2": {"segment": "段落2.1", "feature": "女性人物姿态(背对镜头作画)", "element": "元素1"},
- "img_3": {"segment": "段落3.1", "feature": "女性人物姿态(跪姿作画)", "element": "元素1"},
- "img_4": {"segment": "段落4.1", "feature": "女性人物姿态(侧身面对画架)", "element": "元素1"},
- "img_6": {"segment": "段落6.1", "feature": "女性人物姿态(背部特写作画)", "element": "元素1"},
- "img_7": {"segment": "段落7.1", "feature": "女性人物姿态(侧颜嗅花)", "element": "元素1"},
- "img_8": {"segment": "段落8.1", "feature": "女性人物姿态(侧身面对画架)", "element": "元素1"},
- "img_9": {"segment": "段落9.1", "feature": "女性人物姿态(背影远景)", "element": "元素1"},
- }
-
- mapping = {
- "dimension": "pose_skeleton",
- "description": "人体姿态骨骼关键点图,使用MediaPipe Pose提取33个关键点",
- "tool": "MediaPipe Pose v0.10.9",
- "format": {
- "skeleton_image": "PNG,黑色背景,白色骨骼连线,尺寸与原图相同",
- "keypoints_json": "JSON,包含33个关键点的归一化坐标(x,y,z)和像素坐标"
- },
- "mappings": []
- }
-
- for result in results_summary:
- img_id = result["image_id"]
- if result["detected"]:
- seg_info = pose_segment_map.get(img_id, {})
- mapping["mappings"].append({
- "file": result["skeleton_file"],
- "keypoints_file": result["keypoints_file"],
- "source_image": f"input/{img_id}.jpg",
- "segment": seg_info.get("segment", ""),
- "category": "实质",
- "feature": seg_info.get("feature", ""),
- "element_id": seg_info.get("element", "元素1"),
- "highlight_cluster": "cluster_1"
- })
- else:
- mapping["mappings"].append({
- "file": None,
- "source_image": f"input/{img_id}.jpg",
- "segment": pose_segment_map.get(img_id, {}).get("segment", ""),
- "category": "实质",
- "feature": "局部特写,无法提取全身姿态",
- "element_id": "元素1",
- "note": result.get("reason", "")
- })
-
- mapping_path = os.path.join(output_dir, "mapping.json")
- with open(mapping_path, 'w', encoding='utf-8') as f:
- json.dump(mapping, f, ensure_ascii=False, indent=2)
-
- detected = len([r for r in results_summary if r['detected']])
- print(f"\n✓ 完成: {detected}/{len(results_summary)} 张图片检测到姿态")
- print(f"✓ mapping.json 已保存")
- if __name__ == "__main__":
- main()
|