Bachstelze commited on
Commit
8ea0417
·
1 Parent(s): d11b9f9

example csv and json pose data saving

Browse files
app.py CHANGED
@@ -5,28 +5,70 @@ import json
5
  import csv
6
  import os
7
  from datetime import datetime
8
- from typing import Dict, List, Any
 
9
 
10
  # Load OpenPose detector
11
  openpose = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
12
 
13
- def extract_joint_positions(openpose_result) -> Dict[str, Any]:
14
- """Extract joint positions from OpenPose result."""
15
- # OpenPose returns a PIL Image with encoded pose data
16
- # We need to access the pose data from the result
17
- if hasattr(openpose_result, 'pose_keypoints_2d'):
18
- # If OpenPose returns structured data
19
- return {
20
- "keypoints": openpose_result.pose_keypoints_2d,
21
- "timestamp": datetime.now().isoformat()
22
- }
23
- else:
24
- # Fallback: extract from image if possible
25
- return {
26
- "keypoints": [],
27
- "timestamp": datetime.now().isoformat(),
28
- "note": "No structured pose data available"
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  def save_to_csv(joint_data: Dict[str, Any], filename: str = None) -> str:
32
  """Save joint positions to CSV file."""
@@ -39,30 +81,29 @@ def save_to_csv(joint_data: Dict[str, Any], filename: str = None) -> str:
39
 
40
  with open(filepath, 'w', newline='') as csvfile:
41
  writer = csv.writer(csvfile)
42
- writer.writerow(["Joint", "X", "Y", "Confidence"])
43
-
44
- keypoints = joint_data.get("keypoints", [])
45
- if keypoints and isinstance(keypoints, list):
46
- # OpenPose format: [x1, y1, c1, x2, y2, c2, ...]
47
- joint_names = [
48
- "Nose", "Neck", "RShoulder", "RElbow", "RWrist",
49
- "LShoulder", "LElbow", "LWrist", "RHip", "RKnee",
50
- "RAnkle", "LHip", "LKnee", "LAnkle", "REye",
51
- "LEye", "REar", "LEar", "LBigToe", "LSmallToe",
52
- "LHeel", "RBigToe", "RSmallToe", "RHeel"
53
- ]
54
-
55
- for i in range(0, len(keypoints), 3):
56
- if i + 2 < len(keypoints):
57
- joint_idx = i // 3
58
- joint_name = joint_names[joint_idx] if joint_idx < len(joint_names) else f"Joint_{joint_idx}"
59
- writer.writerow([
60
- joint_name,
61
- keypoints[i], # X
62
- keypoints[i + 1], # Y
63
- keypoints[i + 2] # Confidence
64
- ])
65
-
66
  writer.writerow(["Timestamp", joint_data.get("timestamp", "")])
67
 
68
  return filepath
@@ -81,82 +122,110 @@ def save_to_json(joint_data: Dict[str, Any], filename: str = None) -> str:
81
 
82
  return filepath
83
 
84
- def generate_pose(image, use_openpose=True, save_outputs=True):
 
85
  img = image.convert("RGB")
 
86
  if use_openpose:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  result = openpose(img)
 
 
 
 
 
 
 
88
  else:
89
  result = img
 
 
 
 
 
 
90
  if not isinstance(result, Image.Image):
91
  result = Image.fromarray(result)
92
 
93
- # Extract and save pose data if OpenPose was used
94
- joint_data = {}
95
- if use_openpose and save_outputs:
96
- joint_data = extract_joint_positions(result)
97
- csv_path = save_to_csv(joint_data)
98
- json_path = save_to_json(joint_data)
99
- joint_data["csv_path"] = csv_path
100
- joint_data["json_path"] = json_path
101
-
102
  return result, joint_data
103
 
104
- # Gradio UI with pose data outputs
105
  def format_pose_output(joint_data: Dict[str, Any]) -> str:
106
- """Format pose data for display."""
107
- if not joint_data or not joint_data.get("keypoints"):
108
- return "No pose data available."
109
-
110
- output = "### Joint Positions\n\n"
 
 
 
111
  output += f"**Timestamp:** {joint_data.get('timestamp', 'N/A')}\n\n"
112
 
113
- keypoints = joint_data.get("keypoints", [])
114
- if keypoints and isinstance(keypoints, list):
115
- joint_names = [
116
- "Nose", "Neck", "RShoulder", "RElbow", "RWrist",
117
- "LShoulder", "LElbow", "LWrist", "RHip", "RKnee",
118
- "RAnkle", "LHip", "LKnee", "LAnkle", "REye",
119
- "LEye", "REar", "LEar", "LBigToe", "LSmallToe",
120
- "LHeel", "RBigToe", "RSmallToe", "RHeel"
121
- ]
122
-
123
- output += "| Joint | X | Y | Confidence |\n"
124
- output += "|-------|---|---|------------|\n"
125
-
126
- for i in range(0, min(len(keypoints), 72), 3):
127
- if i + 2 < len(keypoints):
128
- joint_idx = i // 3
129
- joint_name = joint_names[joint_idx] if joint_idx < len(joint_names) else f"Joint_{joint_idx}"
130
- x = keypoints[i]
131
- y = keypoints[i + 1]
132
- confidence = keypoints[i + 2]
133
- output += f"| {joint_name} | {x:.1f} | {y:.1f} | {confidence:.3f} |\n"
134
-
135
- output += f"\n**CSV File:** `{joint_data.get('csv_path', 'N/A')}`\n"
 
136
  output += f"**JSON File:** `{joint_data.get('json_path', 'N/A')}`\n"
137
 
138
  return output
139
 
140
- def process_and_display(image, use_openpose=True):
141
  """Process image and return pose output with data files."""
142
- result, joint_data = generate_pose(image, use_openpose, save_outputs=True)
143
-
144
- if use_openpose and joint_data:
145
- pose_info = format_pose_output(joint_data)
146
- return result, pose_info
147
- else:
148
- return result, "Pose data extraction skipped."
149
-
150
- # Gradio UI with pose data outputs
 
 
 
151
  demo = gr.Interface(
152
  fn=process_and_display,
153
  inputs=[
154
  gr.Image(type="pil", label="Upload Image"),
155
  gr.Checkbox(value=True, label="Use OpenPose (default: true)"),
 
 
156
  ],
157
  outputs=[
158
  gr.Image(type="pil", label="Pose Output"),
159
- gr.Textbox(label="Pose Data", lines=10)
160
  ],
161
  title="OpenPose Pose Generator",
162
  description="Generate full body pose including face and hands. Extracts and stores joint positions in CSV and JSON formats."
 
5
  import csv
6
  import os
7
  from datetime import datetime
8
+ from typing import Dict, List, Any, Optional
9
+ import numpy as np
10
 
11
  # Load OpenPose detector
12
  openpose = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
13
 
14
+ # OpenPose joint mapping (COCO format - 18 joints)
15
+ JOINT_NAMES = [
16
+ "Nose", # 0
17
+ "Neck", # 1
18
+ "RShoulder", # 2
19
+ "RElbow", # 3
20
+ "RWrist", # 4
21
+ "LShoulder", # 5
22
+ "LElbow", # 6
23
+ "LWrist", # 7
24
+ "RHip", # 8
25
+ "RKnee", # 9
26
+ "RAnkle", # 10
27
+ "LHip", # 11
28
+ "LKnee", # 12
29
+ "LAnkle", # 13
30
+ "REye", # 14
31
+ "LEye", # 15
32
+ "REar", # 16
33
+ "LEar" # 17
34
+ ]
35
+
36
+ def extract_joint_positions_from_detect_poses(pose_results: List[Any]) -> Dict[str, Any]:
37
+ """Extract joint positions from OpenPose detect_poses result."""
38
+ all_poses = []
39
+
40
+ for idx, pose in enumerate(pose_results):
41
+ body = pose.body
42
+ keypoints = []
43
+
44
+ for joint_idx, keypoint in enumerate(body.keypoints):
45
+ if keypoint is not None:
46
+ keypoints.append({
47
+ "x": keypoint.x,
48
+ "y": keypoint.y,
49
+ "score": getattr(keypoint, 'score', 0.0),
50
+ "name": JOINT_NAMES[joint_idx] if joint_idx < len(JOINT_NAMES) else f"Joint_{joint_idx}"
51
+ })
52
+ else:
53
+ keypoints.append({
54
+ "x": None,
55
+ "y": None,
56
+ "score": None,
57
+ "name": JOINT_NAMES[joint_idx] if joint_idx < len(JOINT_NAMES) else f"Joint_{joint_idx}"
58
+ })
59
+
60
+ all_poses.append({
61
+ "pose_id": idx,
62
+ "total_score": body.total_score,
63
+ "total_parts": body.total_parts,
64
+ "keypoints": keypoints
65
+ })
66
+
67
+ return {
68
+ "poses": all_poses,
69
+ "timestamp": datetime.now().isoformat(),
70
+ "joint_names": JOINT_NAMES
71
+ }
72
 
73
  def save_to_csv(joint_data: Dict[str, Any], filename: str = None) -> str:
74
  """Save joint positions to CSV file."""
 
81
 
82
  with open(filepath, 'w', newline='') as csvfile:
83
  writer = csv.writer(csvfile)
84
+ writer.writerow(["Pose_ID", "Joint", "X", "Y", "Confidence", "Visible"])
85
+
86
+ poses = joint_data.get("poses", [])
87
+ for pose in poses:
88
+ pose_id = pose.get("pose_id", 0)
89
+ for kp in pose.get("keypoints", []):
90
+ x = kp.get("x")
91
+ y = kp.get("y")
92
+ score = kp.get("score")
93
+ name = kp.get("name", "Unknown")
94
+
95
+ visible = "Yes" if x is not None and y is not None else "No"
96
+
97
+ writer.writerow([
98
+ pose_id,
99
+ name,
100
+ f"{x:.2f}" if x is not None else "N/A",
101
+ f"{y:.2f}" if y is not None else "N/A",
102
+ f"{score:.3f}" if score is not None else "N/A",
103
+ visible
104
+ ])
105
+
106
+ writer.writerow([])
 
107
  writer.writerow(["Timestamp", joint_data.get("timestamp", "")])
108
 
109
  return filepath
 
122
 
123
  return filepath
124
 
125
+ def generate_pose(image, use_openpose=True, save_outputs=True, include_hands=False, include_face=False):
126
+ """Generate pose estimation and extract joint positions."""
127
  img = image.convert("RGB")
128
+
129
  if use_openpose:
130
+ # Convert PIL Image to numpy array for detect_poses
131
+ img_array = np.array(img)
132
+
133
+ # Use detect_poses to get structured data
134
+ pose_results = openpose.detect_poses(
135
+ img_array,
136
+ include_hand=include_hands,
137
+ include_face=include_face
138
+ )
139
+
140
+ # Extract joint positions from pose results
141
+ joint_data = extract_joint_positions_from_detect_poses(pose_results)
142
+
143
+ # Generate the annotated image
144
  result = openpose(img)
145
+
146
+ # Save pose data if requested
147
+ if save_outputs:
148
+ csv_path = save_to_csv(joint_data)
149
+ json_path = save_to_json(joint_data)
150
+ joint_data["csv_path"] = csv_path
151
+ joint_data["json_path"] = json_path
152
  else:
153
  result = img
154
+ joint_data = {
155
+ "poses": [],
156
+ "timestamp": datetime.now().isoformat(),
157
+ "note": "OpenPose disabled - no pose data extracted"
158
+ }
159
+
160
  if not isinstance(result, Image.Image):
161
  result = Image.fromarray(result)
162
 
 
 
 
 
 
 
 
 
 
163
  return result, joint_data
164
 
 
165
  def format_pose_output(joint_data: Dict[str, Any]) -> str:
166
+ """Format pose data for display in Gradio."""
167
+ if not joint_data.get("poses"):
168
+ return "No pose data available.\n\n" + \
169
+ f"**Timestamp:** {joint_data.get('timestamp', 'N/A')}\n" + \
170
+ f"**CSV File:** `{joint_data.get('csv_path', 'N/A')}`\n" + \
171
+ f"**JSON File:** `{joint_data.get('json_path', 'N/A')}`"
172
+
173
+ output = "### Detected Poses\n\n"
174
  output += f"**Timestamp:** {joint_data.get('timestamp', 'N/A')}\n\n"
175
 
176
+ for pose in joint_data.get("poses", []):
177
+ output += f"#### Pose #{pose.get('pose_id', 0)}\n"
178
+ output += f"- **Total Score:** {pose.get('total_score', 0):.3f}\n"
179
+ output += f"- **Total Parts:** {pose.get('total_parts', 0)}\n\n"
180
+
181
+ output += "| Joint | X | Y | Confidence | Visible |\n"
182
+ output += "|-------|---|---|------------|---------|\n"
183
+
184
+ for kp in pose.get("keypoints", []):
185
+ name = kp.get("name", "Unknown")
186
+ x = kp.get("x")
187
+ y = kp.get("y")
188
+ score = kp.get("score")
189
+
190
+ x_str = f"{x:.1f}" if x is not None else "N/A"
191
+ y_str = f"{y:.1f}" if y is not None else "N/A"
192
+ score_str = f"{score:.3f}" if score is not None else "N/A"
193
+ visible = "Yes" if x is not None and y is not None else "No"
194
+
195
+ output += f"| {name} | {x_str} | {y_str} | {score_str} | {visible} |\n"
196
+
197
+ output += "\n"
198
+
199
+ output += f"**CSV File:** `{joint_data.get('csv_path', 'N/A')}`\n"
200
  output += f"**JSON File:** `{joint_data.get('json_path', 'N/A')}`\n"
201
 
202
  return output
203
 
204
+ def process_and_display(image, use_openpose=True, include_hands=False, include_face=False):
205
  """Process image and return pose output with data files."""
206
+ result, joint_data = generate_pose(
207
+ image,
208
+ use_openpose=use_openpose,
209
+ save_outputs=True,
210
+ include_hands=include_hands,
211
+ include_face=include_face
212
+ )
213
+
214
+ pose_info = format_pose_output(joint_data)
215
+ return result, pose_info
216
+
217
+ # Gradio UI
218
  demo = gr.Interface(
219
  fn=process_and_display,
220
  inputs=[
221
  gr.Image(type="pil", label="Upload Image"),
222
  gr.Checkbox(value=True, label="Use OpenPose (default: true)"),
223
+ gr.Checkbox(value=False, label="Include Hands"),
224
+ gr.Checkbox(value=False, label="Include Face"),
225
  ],
226
  outputs=[
227
  gr.Image(type="pil", label="Pose Output"),
228
+ gr.Textbox(label="Pose Data", lines=15)
229
  ],
230
  title="OpenPose Pose Generator",
231
  description="Generate full body pose including face and hands. Extracts and stores joint positions in CSV and JSON formats."
pose_outputs/pose_data_20260404_105941.csv ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Pose_ID,Joint,X,Y,Confidence,Visible
2
+ 0,Nose,0.56,0.24,1.000,Yes
3
+ 0,Neck,0.62,0.33,1.000,Yes
4
+ 0,RShoulder,0.52,0.32,1.000,Yes
5
+ 0,RElbow,0.39,0.30,1.000,Yes
6
+ 0,RWrist,0.33,0.22,1.000,Yes
7
+ 0,LShoulder,0.71,0.34,1.000,Yes
8
+ 0,LElbow,0.76,0.45,1.000,Yes
9
+ 0,LWrist,0.67,0.48,1.000,Yes
10
+ 0,RHip,0.48,0.51,1.000,Yes
11
+ 0,RKnee,0.50,0.69,1.000,Yes
12
+ 0,RAnkle,0.51,0.86,1.000,Yes
13
+ 0,LHip,0.58,0.53,1.000,Yes
14
+ 0,LKnee,0.66,0.69,1.000,Yes
15
+ 0,LAnkle,0.77,0.86,1.000,Yes
16
+ 0,REye,0.55,0.24,1.000,Yes
17
+ 0,LEye,0.59,0.23,1.000,Yes
18
+ 0,REar,N/A,N/A,N/A,No
19
+ 0,LEar,0.66,0.24,1.000,Yes
20
+
21
+ Timestamp,2026-04-04T10:59:40.316720
pose_outputs/pose_data_20260404_105941.json ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "poses": [
3
+ {
4
+ "pose_id": 0,
5
+ "total_score": 31.495056734426026,
6
+ "total_parts": 17.0,
7
+ "keypoints": [
8
+ {
9
+ "x": 0.5646766169154229,
10
+ "y": 0.24378109452736318,
11
+ "score": 1.0,
12
+ "name": "Nose"
13
+ },
14
+ {
15
+ "x": 0.6169154228855721,
16
+ "y": 0.3283582089552239,
17
+ "score": 1.0,
18
+ "name": "Neck"
19
+ },
20
+ {
21
+ "x": 0.5199004975124378,
22
+ "y": 0.3200663349917081,
23
+ "score": 1.0,
24
+ "name": "RShoulder"
25
+ },
26
+ {
27
+ "x": 0.3880597014925373,
28
+ "y": 0.29850746268656714,
29
+ "score": 1.0,
30
+ "name": "RElbow"
31
+ },
32
+ {
33
+ "x": 0.3308457711442786,
34
+ "y": 0.2155887230514096,
35
+ "score": 1.0,
36
+ "name": "RWrist"
37
+ },
38
+ {
39
+ "x": 0.7114427860696517,
40
+ "y": 0.34328358208955223,
41
+ "score": 1.0,
42
+ "name": "LShoulder"
43
+ },
44
+ {
45
+ "x": 0.7611940298507462,
46
+ "y": 0.44776119402985076,
47
+ "score": 1.0,
48
+ "name": "LElbow"
49
+ },
50
+ {
51
+ "x": 0.6716417910447762,
52
+ "y": 0.48092868988391374,
53
+ "score": 1.0,
54
+ "name": "LWrist"
55
+ },
56
+ {
57
+ "x": 0.47761194029850745,
58
+ "y": 0.5140961857379768,
59
+ "score": 1.0,
60
+ "name": "RHip"
61
+ },
62
+ {
63
+ "x": 0.49502487562189057,
64
+ "y": 0.6865671641791045,
65
+ "score": 1.0,
66
+ "name": "RKnee"
67
+ },
68
+ {
69
+ "x": 0.5124378109452736,
70
+ "y": 0.8557213930348259,
71
+ "score": 1.0,
72
+ "name": "RAnkle"
73
+ },
74
+ {
75
+ "x": 0.5845771144278606,
76
+ "y": 0.527363184079602,
77
+ "score": 1.0,
78
+ "name": "LHip"
79
+ },
80
+ {
81
+ "x": 0.6616915422885572,
82
+ "y": 0.6898839137645107,
83
+ "score": 1.0,
84
+ "name": "LKnee"
85
+ },
86
+ {
87
+ "x": 0.7686567164179104,
88
+ "y": 0.8590381426202321,
89
+ "score": 1.0,
90
+ "name": "LAnkle"
91
+ },
92
+ {
93
+ "x": 0.554726368159204,
94
+ "y": 0.23548922056384744,
95
+ "score": 1.0,
96
+ "name": "REye"
97
+ },
98
+ {
99
+ "x": 0.5895522388059702,
100
+ "y": 0.23217247097844113,
101
+ "score": 1.0,
102
+ "name": "LEye"
103
+ },
104
+ {
105
+ "x": null,
106
+ "y": null,
107
+ "score": null,
108
+ "name": "REar"
109
+ },
110
+ {
111
+ "x": 0.6567164179104478,
112
+ "y": 0.24046434494195687,
113
+ "score": 1.0,
114
+ "name": "LEar"
115
+ }
116
+ ]
117
+ }
118
+ ],
119
+ "timestamp": "2026-04-04T10:59:40.316720",
120
+ "joint_names": [
121
+ "Nose",
122
+ "Neck",
123
+ "RShoulder",
124
+ "RElbow",
125
+ "RWrist",
126
+ "LShoulder",
127
+ "LElbow",
128
+ "LWrist",
129
+ "RHip",
130
+ "RKnee",
131
+ "RAnkle",
132
+ "LHip",
133
+ "LKnee",
134
+ "LAnkle",
135
+ "REye",
136
+ "LEye",
137
+ "REar",
138
+ "LEar"
139
+ ]
140
+ }