aiqtech commited on
Commit
9c1def9
·
verified ·
1 Parent(s): 0aae5f3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +387 -138
app.py CHANGED
@@ -2,7 +2,9 @@ import gradio as gr
2
  import json
3
  import requests
4
  import os
5
- from PIL import Image, ImageDraw
 
 
6
 
7
  # Fireworks AI configuration
8
  FIREWORKS_API_KEY = os.getenv("FIREWORKS_API_KEY", "YOUR_API_KEY_HERE")
@@ -16,102 +18,254 @@ BODY_PARTS = {
16
  "LEye": 15, "REar": 16, "LEar": 17
17
  }
18
 
19
- # Skeleton connections
20
  POSE_CONNECTIONS = [
21
- ("Neck", "RShoulder"), ("RShoulder", "RElbow"), ("RElbow", "RWrist"),
22
- ("Neck", "LShoulder"), ("LShoulder", "LElbow"), ("LElbow", "LWrist"),
23
- ("Neck", "Nose"), ("Nose", "REye"), ("Nose", "LEye"),
24
- ("REye", "REar"), ("LEye", "LEar"),
25
- ("Neck", "RHip"), ("RHip", "RKnee"), ("RKnee", "RAnkle"),
26
- ("Neck", "LHip"), ("LHip", "LKnee"), ("LKnee", "LAnkle"),
27
- ("RHip", "LHip")
28
  ]
29
 
30
- # Pose templates
31
- POSE_TEMPLATES = {
32
- "Standing": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  "Nose": [256, 80], "Neck": [256, 120],
34
- "RShoulder": [220, 140], "RElbow": [200, 220], "RWrist": [190, 300],
35
- "LShoulder": [292, 140], "LElbow": [312, 220], "LWrist": [322, 300],
36
  "RHip": [240, 280], "RKnee": [235, 380], "RAnkle": [230, 480],
37
- "LHip": [272, 280], "LKnee": [277, 380], "LAnkle": [282, 480],
38
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
39
  },
40
- "Sitting": {
41
- "Nose": [256, 120], "Neck": [256, 160],
42
- "RShoulder": [220, 180], "RElbow": [200, 240], "RWrist": [190, 300],
43
- "LShoulder": [292, 180], "LElbow": [312, 240], "LWrist": [322, 300],
44
- "RHip": [240, 320], "RKnee": [280, 380], "RAnkle": [320, 400],
45
- "LHip": [272, 320], "LKnee": [232, 380], "LAnkle": [192, 400],
46
- "REye": [246, 110], "LEye": [266, 110], "REar": [236, 115], "LEar": [276, 115]
47
  },
48
- "Running": {
49
- "Nose": [256, 80], "Neck": [256, 120],
50
- "RShoulder": [220, 140], "RElbow": [180, 180], "RWrist": [150, 140],
51
- "LShoulder": [292, 140], "LElbow": [332, 200], "LWrist": [362, 260],
52
- "RHip": [240, 280], "RKnee": [260, 380], "RAnkle": [290, 470],
53
- "LHip": [272, 280], "LKnee": [252, 360], "LAnkle": [222, 440],
54
- "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
55
  },
56
- "Yoga": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  "Nose": [256, 100], "Neck": [256, 140],
58
- "RShoulder": [200, 160], "RElbow": [150, 120], "RWrist": [100, 100],
59
- "LShoulder": [312, 160], "LElbow": [362, 120], "LWrist": [412, 100],
60
- "RHip": [240, 300], "RKnee": [220, 400], "RAnkle": [200, 480],
61
- "LHip": [272, 300], "LKnee": [292, 400], "LAnkle": [312, 480],
62
  "REye": [246, 90], "LEye": [266, 90], "REar": [236, 95], "LEar": [276, 95]
63
  },
64
- "Dancing": {
65
- "Nose": [256, 80], "Neck": [256, 120],
66
- "RShoulder": [220, 140], "RElbow": [180, 120], "RWrist": [140, 100],
67
- "LShoulder": [292, 140], "LElbow": [332, 160], "LWrist": [372, 140],
68
- "RHip": [240, 280], "RKnee": [260, 380], "RAnkle": [250, 480],
69
- "LHip": [272, 280], "LKnee": [252, 380], "LAnkle": [262, 480],
70
- "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
71
  },
72
- "Waving": {
73
  "Nose": [256, 80], "Neck": [256, 120],
74
  "RShoulder": [220, 140], "RElbow": [200, 100], "RWrist": [180, 60],
75
- "LShoulder": [292, 140], "LElbow": [312, 220], "LWrist": [322, 300],
76
  "RHip": [240, 280], "RKnee": [235, 380], "RAnkle": [230, 480],
77
  "LHip": [272, 280], "LKnee": [277, 380], "LAnkle": [282, 480],
78
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
80
  }
81
 
82
- def draw_pose(keypoints, width=512, height=512):
83
- """Draw pose skeleton on image"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  img = Image.new('RGB', (width, height), color='white')
85
  draw = ImageDraw.Draw(img)
86
 
87
- # Draw skeleton connections
88
- for start, end in POSE_CONNECTIONS:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  if start in keypoints and end in keypoints:
90
  start_point = keypoints[start]
91
  end_point = keypoints[end]
92
  if start_point and end_point:
93
- draw.line([tuple(start_point), tuple(end_point)], fill='blue', width=3)
 
 
 
 
 
94
 
95
- # Draw keypoints
96
  for part, point in keypoints.items():
97
  if point:
98
  x, y = point
99
- radius = 5
100
- draw.ellipse([x-radius, y-radius, x+radius, y+radius], fill='red', outline='darkred')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  return img
103
 
104
  def generate_pose_from_llm(prompt):
105
- """Generate pose using LLM"""
106
- system_prompt = """You are an expert in generating human pose keypoints.
107
- Given a description, generate 18 keypoint coordinates for OpenPose.
 
108
 
109
- Canvas size: 512x512 pixels
110
- Keypoints: Nose, Neck, RShoulder, RElbow, RWrist, LShoulder, LElbow, LWrist,
111
- RHip, RKnee, RAnkle, LHip, LKnee, LAnkle, REye, LEye, REar, LEar
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  Return ONLY a JSON object with keypoint names and [x, y] coordinates.
114
- Example: {"Nose": [256, 80], "Neck": [256, 120], ...}"""
115
 
116
  headers = {
117
  "Accept": "application/json",
@@ -122,10 +276,10 @@ def generate_pose_from_llm(prompt):
122
  payload = {
123
  "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
124
  "max_tokens": 1024,
125
- "temperature": 0.3,
126
  "messages": [
127
- {"role": "system", "content": system_prompt},
128
- {"role": "user", "content": f"Generate pose keypoints for: {prompt}"}
129
  ]
130
  }
131
 
@@ -143,60 +297,129 @@ def generate_pose_from_llm(prompt):
143
  except Exception as e:
144
  print(f"LLM Error: {e}")
145
 
146
- return get_template_from_prompt(prompt)
147
 
148
- def get_template_from_prompt(prompt):
149
- """Select template based on prompt"""
150
  if not prompt:
151
- return POSE_TEMPLATES["Standing"]
152
 
153
  prompt_lower = prompt.lower()
154
 
155
- if any(word in prompt_lower for word in ["sit", "chair", "seated"]):
156
- return POSE_TEMPLATES["Sitting"]
157
- elif any(word in prompt_lower for word in ["run", "jog", "sprint"]):
158
- return POSE_TEMPLATES["Running"]
159
- elif any(word in prompt_lower for word in ["yoga", "meditation", "stretch"]):
160
- return POSE_TEMPLATES["Yoga"]
161
- elif any(word in prompt_lower for word in ["dance", "dancing"]):
162
- return POSE_TEMPLATES["Dancing"]
163
- elif any(word in prompt_lower for word in ["wave", "waving", "hello"]):
164
- return POSE_TEMPLATES["Waving"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  else:
166
- return POSE_TEMPLATES["Standing"]
 
 
167
 
168
- def refine_pose(current_keypoints, instruction):
169
- """Refine existing pose"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  if not current_keypoints or not instruction:
171
  return current_keypoints
172
 
173
  keypoints = current_keypoints.copy()
174
  instruction_lower = instruction.lower()
175
 
176
- # Simple refinements
177
- if "raise" in instruction_lower or "lift" in instruction_lower:
178
- if "arm" in instruction_lower:
179
- if "left" in instruction_lower:
180
- if "LWrist" in keypoints:
181
- keypoints["LWrist"][1] -= 50
182
- elif "right" in instruction_lower:
183
- if "RWrist" in keypoints:
184
- keypoints["RWrist"][1] -= 50
185
- else:
186
- for part in ["LWrist", "RWrist"]:
187
- if part in keypoints:
188
- keypoints[part][1] -= 50
 
 
 
 
 
 
 
 
 
 
 
189
 
190
- elif "lower" in instruction_lower:
 
 
 
 
 
 
191
  if "arm" in instruction_lower:
192
- for part in ["LWrist", "RWrist"]:
193
- if part in keypoints:
194
- keypoints[part][1] += 50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
  return keypoints
197
 
198
  def keypoints_to_openpose_format(keypoints):
199
- """Convert to OpenPose JSON format"""
200
  if not keypoints:
201
  return "{}"
202
 
@@ -220,107 +443,133 @@ def keypoints_to_openpose_format(keypoints):
220
  return json.dumps({"candidate": candidate, "subset": subset}, indent=2)
221
 
222
  # Main generation function
223
- def generate_pose(prompt, use_llm, template):
224
- """Main function to generate pose"""
225
  if template and template != "None":
226
- keypoints = POSE_TEMPLATES[template]
227
  elif use_llm and FIREWORKS_API_KEY != "YOUR_API_KEY_HERE" and prompt:
228
  keypoints = generate_pose_from_llm(prompt)
229
  elif prompt:
230
- keypoints = get_template_from_prompt(prompt)
231
  else:
232
- keypoints = POSE_TEMPLATES["Standing"]
 
 
 
 
 
233
 
234
- pose_img = draw_pose(keypoints)
235
  json_str = keypoints_to_openpose_format(keypoints)
236
 
237
  return pose_img, json_str, keypoints
238
 
239
  def refine_existing_pose(instruction, keypoints_json):
240
- """Refine pose with instruction"""
241
  if not keypoints_json:
242
  return None, "{}", {}
243
 
244
- refined_keypoints = refine_pose(keypoints_json, instruction)
245
- pose_img = draw_pose(refined_keypoints)
246
  json_str = keypoints_to_openpose_format(refined_keypoints)
247
 
248
  return pose_img, json_str, refined_keypoints
249
 
250
  def check_api_status():
251
- """Check if API key is configured"""
252
  if FIREWORKS_API_KEY != "YOUR_API_KEY_HERE":
253
  return "✅ API key configured - Advanced AI ready"
254
- return "⚠️ API key not set - Template mode active"
255
 
256
- # Create Gradio interface - IMPORTANT: Must be named 'app' for Hugging Face Spaces
257
- app = gr.Blocks(title="AI Pose Generator")
258
 
259
  with app:
260
  keypoints_state = gr.State({})
261
 
262
  gr.Markdown("""
263
- # 🎨 AI Line Art Pose Generator
264
- ### Generate precise poses from text descriptions
265
  """)
266
 
267
  with gr.Row():
268
  with gr.Column(scale=1):
269
- # Input controls
270
  api_status = gr.Markdown(check_api_status())
271
 
272
  use_llm = gr.Checkbox(
273
- label="Use Advanced AI (Fireworks API)",
274
  value=False
275
  )
276
 
277
  prompt = gr.Textbox(
278
- label="Describe the pose",
279
- placeholder="e.g., A person sitting and reading a book",
280
  lines=2
281
  )
282
 
283
- template = gr.Dropdown(
284
- choices=["None"] + list(POSE_TEMPLATES.keys()),
285
- label="Or select a template",
286
- value="None"
287
- )
 
 
 
 
 
 
 
288
 
289
- generate_btn = gr.Button("🎯 Generate Pose", variant="primary")
290
 
291
- # Refinement
292
- gr.Markdown("### Refine Pose")
293
  refinement = gr.Textbox(
294
- label="Refinement instruction",
295
- placeholder="e.g., Raise the left arm",
296
  lines=1
297
  )
298
 
299
- refine_btn = gr.Button("✨ Refine", variant="secondary")
300
 
301
- # Examples
302
  gr.Examples(
303
  examples=[
304
- "A person standing with arms raised",
305
- "Someone sitting at a desk",
306
- "A person doing yoga",
307
- "Someone waving hello",
308
- "A person running"
 
 
 
309
  ],
310
  inputs=prompt
311
  )
 
 
 
 
 
 
 
 
312
 
313
  with gr.Column(scale=1):
314
- # Output
315
- pose_image = gr.Image(label="Generated Pose", type="pil")
 
 
 
316
 
317
- with gr.Accordion("OpenPose JSON", open=False):
318
- json_output = gr.Code(language="json", lines=10)
 
 
 
319
 
320
  # Event handlers
321
  generate_btn.click(
322
  fn=generate_pose,
323
- inputs=[prompt, use_llm, template],
324
  outputs=[pose_image, json_output, keypoints_state]
325
  )
326
 
@@ -330,6 +579,6 @@ with app:
330
  outputs=[pose_image, json_output, keypoints_state]
331
  )
332
 
333
- # For Hugging Face Spaces
334
  app.queue()
335
  app.launch()
 
2
  import json
3
  import requests
4
  import os
5
+ import math
6
+ from PIL import Image, ImageDraw, ImageFont
7
+ import numpy as np
8
 
9
  # Fireworks AI configuration
10
  FIREWORKS_API_KEY = os.getenv("FIREWORKS_API_KEY", "YOUR_API_KEY_HERE")
 
18
  "LEye": 15, "REar": 16, "LEar": 17
19
  }
20
 
21
+ # Enhanced skeleton connections with thickness
22
  POSE_CONNECTIONS = [
23
+ ("Neck", "Nose", 2), ("Nose", "REye", 1), ("REye", "REar", 1),
24
+ ("Nose", "LEye", 1), ("LEye", "LEar", 1),
25
+ ("Neck", "RShoulder", 3), ("RShoulder", "RElbow", 3), ("RElbow", "RWrist", 2),
26
+ ("Neck", "LShoulder", 3), ("LShoulder", "LElbow", 3), ("LElbow", "LWrist", 2),
27
+ ("Neck", "RHip", 4), ("RHip", "RKnee", 4), ("RKnee", "RAnkle", 3),
28
+ ("Neck", "LHip", 4), ("LHip", "LKnee", 4), ("LKnee", "LAnkle", 3),
29
+ ("RHip", "LHip", 4)
30
  ]
31
 
32
+ # Dynamic and precise pose templates
33
+ DYNAMIC_POSES = {
34
+ "🏃 Sprint": {
35
+ "Nose": [256, 60], "Neck": [256, 100],
36
+ "RShoulder": [230, 120], "RElbow": [260, 140], "RWrist": [290, 120],
37
+ "LShoulder": [282, 120], "LElbow": [252, 160], "LWrist": [222, 200],
38
+ "RHip": [245, 260], "RKnee": [220, 360], "RAnkle": [195, 460],
39
+ "LHip": [267, 260], "LKnee": [300, 340], "LAnkle": [330, 420],
40
+ "REye": [246, 50], "LEye": [266, 50], "REar": [236, 55], "LEar": [276, 55]
41
+ },
42
+ "🤸 Backflip": {
43
+ "Nose": [256, 250], "Neck": [256, 280],
44
+ "RShoulder": [220, 300], "RElbow": [180, 280], "RWrist": [140, 260],
45
+ "LShoulder": [292, 300], "LElbow": [332, 280], "LWrist": [372, 260],
46
+ "RHip": [240, 180], "RKnee": [220, 120], "RAnkle": [200, 60],
47
+ "LHip": [272, 180], "LKnee": [292, 120], "LAnkle": [312, 60],
48
+ "REye": [246, 260], "LEye": [266, 260], "REar": [236, 255], "LEar": [276, 255]
49
+ },
50
+ "🥋 Martial Arts Kick": {
51
  "Nose": [256, 80], "Neck": [256, 120],
52
+ "RShoulder": [220, 140], "RElbow": [180, 120], "RWrist": [140, 100],
53
+ "LShoulder": [292, 140], "LElbow": [332, 160], "LWrist": [372, 180],
54
  "RHip": [240, 280], "RKnee": [235, 380], "RAnkle": [230, 480],
55
+ "LHip": [272, 280], "LKnee": [350, 250], "LAnkle": [420, 220],
56
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
57
  },
58
+ "🩰 Ballet Leap": {
59
+ "Nose": [256, 100], "Neck": [256, 140],
60
+ "RShoulder": [200, 130], "RElbow": [150, 110], "RWrist": [100, 90],
61
+ "LShoulder": [312, 130], "LElbow": [362, 110], "LWrist": [412, 90],
62
+ "RHip": [240, 300], "RKnee": [180, 380], "RAnkle": [120, 460],
63
+ "LHip": [272, 300], "LKnee": [332, 380], "LAnkle": [392, 460],
64
+ "REye": [246, 90], "LEye": [266, 90], "REar": [236, 95], "LEar": [276, 95]
65
  },
66
+ "🏀 Basketball Dunk": {
67
+ "Nose": [256, 60], "Neck": [256, 100],
68
+ "RShoulder": [220, 120], "RElbow": [200, 80], "RWrist": [180, 40],
69
+ "LShoulder": [292, 120], "LElbow": [312, 80], "LWrist": [332, 40],
70
+ "RHip": [240, 260], "RKnee": [230, 360], "RAnkle": [225, 460],
71
+ "LHip": [272, 260], "LKnee": [282, 340], "LAnkle": [287, 420],
72
+ "REye": [246, 50], "LEye": [266, 50], "REar": [236, 55], "LEar": [276, 55]
73
  },
74
+ "🧘 Advanced Yoga": {
75
+ "Nose": [256, 380], "Neck": [256, 420],
76
+ "RShoulder": [220, 440], "RElbow": [200, 400], "RWrist": [180, 360],
77
+ "LShoulder": [292, 440], "LElbow": [312, 400], "LWrist": [332, 360],
78
+ "RHip": [240, 280], "RKnee": [220, 180], "RAnkle": [200, 80],
79
+ "LHip": [272, 280], "LKnee": [292, 180], "LAnkle": [312, 80],
80
+ "REye": [246, 390], "LEye": [266, 390], "REar": [236, 385], "LEar": [276, 385]
81
+ },
82
+ "🕺 Breakdance Freeze": {
83
+ "Nose": [150, 400], "Neck": [180, 420],
84
+ "RShoulder": [200, 400], "RElbow": [220, 350], "RWrist": [240, 300],
85
+ "LShoulder": [160, 440], "LElbow": [140, 480], "LWrist": [120, 500],
86
+ "RHip": [300, 350], "RKnee": [350, 300], "RAnkle": [400, 250],
87
+ "LHip": [280, 380], "LKnee": [330, 420], "LAnkle": [380, 460],
88
+ "REye": [140, 390], "LEye": [160, 390], "REar": [130, 395], "LEar": [170, 395]
89
+ },
90
+ "🏋️ Weightlifting": {
91
  "Nose": [256, 100], "Neck": [256, 140],
92
+ "RShoulder": [210, 160], "RElbow": [180, 120], "RWrist": [150, 80],
93
+ "LShoulder": [302, 160], "LElbow": [332, 120], "LWrist": [362, 80],
94
+ "RHip": [235, 300], "RKnee": [220, 400], "RAnkle": [210, 480],
95
+ "LHip": [277, 300], "LKnee": [292, 400], "LAnkle": [302, 480],
96
  "REye": [246, 90], "LEye": [266, 90], "REar": [236, 95], "LEar": [276, 95]
97
  },
98
+ "🏊 Swimming Dive": {
99
+ "Nose": [380, 200], "Neck": [360, 220],
100
+ "RShoulder": [340, 240], "RElbow": [380, 230], "RWrist": [420, 220],
101
+ "LShoulder": [340, 200], "LElbow": [380, 190], "LWrist": [420, 180],
102
+ "RHip": [280, 260], "RKnee": [220, 280], "RAnkle": [160, 300],
103
+ "LHip": [280, 240], "LKnee": [220, 250], "LAnkle": [160, 260],
104
+ "REye": [390, 195], "LEye": [390, 205], "REar": [385, 190], "LEar": [385, 210]
105
  },
106
+ "🎾 Tennis Serve": {
107
  "Nose": [256, 80], "Neck": [256, 120],
108
  "RShoulder": [220, 140], "RElbow": [200, 100], "RWrist": [180, 60],
109
+ "LShoulder": [292, 140], "LElbow": [312, 180], "LWrist": [332, 220],
110
  "RHip": [240, 280], "RKnee": [235, 380], "RAnkle": [230, 480],
111
  "LHip": [272, 280], "LKnee": [277, 380], "LAnkle": [282, 480],
112
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
113
+ },
114
+ "🪂 Skydiving": {
115
+ "Nose": [256, 200], "Neck": [256, 240],
116
+ "RShoulder": [200, 260], "RElbow": [150, 240], "RWrist": [100, 220],
117
+ "LShoulder": [312, 260], "LElbow": [362, 240], "LWrist": [412, 220],
118
+ "RHip": [240, 340], "RKnee": [200, 400], "RAnkle": [160, 460],
119
+ "LHip": [272, 340], "LKnee": [312, 400], "LAnkle": [352, 460],
120
+ "REye": [246, 190], "LEye": [266, 190], "REar": [236, 195], "LEar": [276, 195]
121
+ },
122
+ "🤾 Handball Jump": {
123
+ "Nose": [256, 120], "Neck": [256, 160],
124
+ "RShoulder": [220, 180], "RElbow": [200, 140], "RWrist": [180, 100],
125
+ "LShoulder": [292, 180], "LElbow": [312, 220], "LWrist": [332, 260],
126
+ "RHip": [240, 320], "RKnee": [220, 400], "RAnkle": [200, 480],
127
+ "LHip": [272, 320], "LKnee": [292, 380], "LAnkle": [312, 440],
128
+ "REye": [246, 110], "LEye": [266, 110], "REar": [236, 115], "LEar": [276, 115]
129
  }
130
  }
131
 
132
+ def rotate_point(point, center, angle):
133
+ """Rotate a point around a center by angle (in radians)"""
134
+ x, y = point
135
+ cx, cy = center
136
+ cos_a = math.cos(angle)
137
+ sin_a = math.sin(angle)
138
+ nx = cos_a * (x - cx) - sin_a * (y - cy) + cx
139
+ ny = sin_a * (x - cx) + cos_a * (y - cy) + cy
140
+ return [nx, ny]
141
+
142
+ def scale_pose(keypoints, scale_factor):
143
+ """Scale pose around center"""
144
+ if not keypoints:
145
+ return keypoints
146
+
147
+ # Find center
148
+ valid_points = [p for p in keypoints.values() if p]
149
+ if not valid_points:
150
+ return keypoints
151
+
152
+ cx = sum(p[0] for p in valid_points) / len(valid_points)
153
+ cy = sum(p[1] for p in valid_points) / len(valid_points)
154
+
155
+ # Scale around center
156
+ scaled = {}
157
+ for part, point in keypoints.items():
158
+ if point:
159
+ x, y = point
160
+ nx = cx + (x - cx) * scale_factor
161
+ ny = cy + (y - cy) * scale_factor
162
+ scaled[part] = [nx, ny]
163
+ else:
164
+ scaled[part] = point
165
+
166
+ return scaled
167
+
168
+ def add_motion_blur(keypoints, direction="horizontal", intensity=0.3):
169
+ """Add motion effect to pose"""
170
+ blurred = keypoints.copy()
171
+
172
+ for part in ["RWrist", "LWrist", "RAnkle", "LAnkle"]:
173
+ if part in blurred and blurred[part]:
174
+ x, y = blurred[part]
175
+ if direction == "horizontal":
176
+ blurred[part] = [x + intensity * 30, y]
177
+ elif direction == "vertical":
178
+ blurred[part] = [x, y + intensity * 30]
179
+
180
+ return blurred
181
+
182
+ def draw_pose(keypoints, width=512, height=512, style="dynamic"):
183
+ """Draw pose with enhanced visualization"""
184
  img = Image.new('RGB', (width, height), color='white')
185
  draw = ImageDraw.Draw(img)
186
 
187
+ # Dynamic gradient background for action poses
188
+ if style == "dynamic":
189
+ for i in range(height):
190
+ color_val = 255 - int((i / height) * 50)
191
+ draw.rectangle([(0, i), (width, i+1)], fill=(color_val, color_val, 255))
192
+
193
+ # Draw shadow/trail effect for dynamic poses
194
+ if style == "dynamic":
195
+ for start, end, _ in POSE_CONNECTIONS:
196
+ if start in keypoints and end in keypoints:
197
+ start_point = keypoints[start]
198
+ end_point = keypoints[end]
199
+ if start_point and end_point:
200
+ # Shadow effect
201
+ shadow_start = [start_point[0] + 5, start_point[1] + 5]
202
+ shadow_end = [end_point[0] + 5, end_point[1] + 5]
203
+ draw.line([tuple(shadow_start), tuple(shadow_end)],
204
+ fill=(200, 200, 200), width=2)
205
+
206
+ # Draw skeleton connections with variable thickness
207
+ for connection in POSE_CONNECTIONS:
208
+ start, end = connection[0], connection[1]
209
+ thickness = connection[2] if len(connection) > 2 else 3
210
+
211
  if start in keypoints and end in keypoints:
212
  start_point = keypoints[start]
213
  end_point = keypoints[end]
214
  if start_point and end_point:
215
+ # Main line
216
+ draw.line([tuple(start_point), tuple(end_point)],
217
+ fill='darkblue', width=thickness)
218
+ # Highlight
219
+ draw.line([tuple(start_point), tuple(end_point)],
220
+ fill='blue', width=thickness-1)
221
 
222
+ # Draw keypoints with gradient effect
223
  for part, point in keypoints.items():
224
  if point:
225
  x, y = point
226
+ # Determine size based on body part
227
+ if part in ["Neck", "RHip", "LHip"]:
228
+ radius = 7
229
+ elif part in ["RWrist", "LWrist", "RAnkle", "LAnkle"]:
230
+ radius = 4
231
+ else:
232
+ radius = 5
233
+
234
+ # Outer circle
235
+ draw.ellipse([x-radius-1, y-radius-1, x+radius+1, y+radius+1],
236
+ fill='darkred', outline='black')
237
+ # Inner circle
238
+ draw.ellipse([x-radius, y-radius, x+radius, y+radius],
239
+ fill='red', outline='darkred')
240
+ # Highlight
241
+ draw.ellipse([x-radius+2, y-radius+2, x+radius-2, y+radius-2],
242
+ fill='pink', outline=None)
243
 
244
  return img
245
 
246
  def generate_pose_from_llm(prompt):
247
+ """Generate dynamic pose using LLM with enhanced prompt"""
248
+ enhanced_prompt = f"""You are an expert in generating DYNAMIC and PRECISE human pose keypoints.
249
+
250
+ Task: Generate highly dynamic, action-oriented pose for: {prompt}
251
 
252
+ Requirements:
253
+ 1. Canvas: 512x512 pixels
254
+ 2. Create EXTREME and DYNAMIC poses with:
255
+ - Large range of motion
256
+ - Athletic and acrobatic positions
257
+ - Dramatic angles and perspectives
258
+ - Action-oriented body positions
259
+ 3. Use all 18 keypoints: Nose, Neck, RShoulder, RElbow, RWrist, LShoulder, LElbow, LWrist,
260
+ RHip, RKnee, RAnkle, LHip, LKnee, LAnkle, REye, LEye, REar, LEar
261
+ 4. Make poses that show:
262
+ - Movement and energy
263
+ - Athletic performance
264
+ - Extreme flexibility
265
+ - Dramatic action
266
 
267
  Return ONLY a JSON object with keypoint names and [x, y] coordinates.
268
+ Make it VERY dynamic and exciting!"""
269
 
270
  headers = {
271
  "Accept": "application/json",
 
276
  payload = {
277
  "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
278
  "max_tokens": 1024,
279
+ "temperature": 0.7, # Higher temperature for more creative poses
280
  "messages": [
281
+ {"role": "system", "content": enhanced_prompt},
282
+ {"role": "user", "content": f"Create an extremely dynamic pose for: {prompt}"}
283
  ]
284
  }
285
 
 
297
  except Exception as e:
298
  print(f"LLM Error: {e}")
299
 
300
+ return get_dynamic_template(prompt)
301
 
302
+ def get_dynamic_template(prompt):
303
+ """Select dynamic template based on prompt"""
304
  if not prompt:
305
+ return DYNAMIC_POSES["🏃 Sprint"]
306
 
307
  prompt_lower = prompt.lower()
308
 
309
+ # Check for specific action keywords
310
+ if any(word in prompt_lower for word in ["run", "sprint", "dash"]):
311
+ return DYNAMIC_POSES["🏃 Sprint"]
312
+ elif any(word in prompt_lower for word in ["flip", "backflip", "acrobat"]):
313
+ return DYNAMIC_POSES["🤸 Backflip"]
314
+ elif any(word in prompt_lower for word in ["kick", "martial", "karate", "taekwondo"]):
315
+ return DYNAMIC_POSES["🥋 Martial Arts Kick"]
316
+ elif any(word in prompt_lower for word in ["ballet", "dance", "leap", "jump"]):
317
+ return DYNAMIC_POSES["🩰 Ballet Leap"]
318
+ elif any(word in prompt_lower for word in ["basketball", "dunk", "shoot"]):
319
+ return DYNAMIC_POSES["🏀 Basketball Dunk"]
320
+ elif any(word in prompt_lower for word in ["yoga", "stretch", "flexible"]):
321
+ return DYNAMIC_POSES["🧘 Advanced Yoga"]
322
+ elif any(word in prompt_lower for word in ["breakdance", "freeze", "bboy"]):
323
+ return DYNAMIC_POSES["🕺 Breakdance Freeze"]
324
+ elif any(word in prompt_lower for word in ["weight", "lift", "gym"]):
325
+ return DYNAMIC_POSES["🏋️ Weightlifting"]
326
+ elif any(word in prompt_lower for word in ["swim", "dive", "water"]):
327
+ return DYNAMIC_POSES["🏊 Swimming Dive"]
328
+ elif any(word in prompt_lower for word in ["tennis", "serve", "racket"]):
329
+ return DYNAMIC_POSES["🎾 Tennis Serve"]
330
+ elif any(word in prompt_lower for word in ["sky", "fall", "fly"]):
331
+ return DYNAMIC_POSES["🪂 Skydiving"]
332
+ elif any(word in prompt_lower for word in ["handball", "throw", "ball"]):
333
+ return DYNAMIC_POSES["🤾 Handball Jump"]
334
  else:
335
+ # Return random dynamic pose
336
+ import random
337
+ return random.choice(list(DYNAMIC_POSES.values()))
338
 
339
+ def apply_physics(keypoints, effect="gravity"):
340
+ """Apply physics effects to make poses more realistic"""
341
+ modified = keypoints.copy()
342
+
343
+ if effect == "gravity":
344
+ # Apply downward pull to limbs
345
+ for part in ["RWrist", "LWrist"]:
346
+ if part in modified and modified[part]:
347
+ modified[part][1] += 10
348
+ elif effect == "momentum":
349
+ # Add motion direction
350
+ if "RWrist" in modified and modified["RWrist"]:
351
+ modified["RWrist"][0] += 15
352
+ if "LAnkle" in modified and modified["LAnkle"]:
353
+ modified["LAnkle"][0] -= 10
354
+
355
+ return modified
356
+
357
+ def refine_pose_advanced(current_keypoints, instruction):
358
+ """Advanced pose refinement with multiple options"""
359
  if not current_keypoints or not instruction:
360
  return current_keypoints
361
 
362
  keypoints = current_keypoints.copy()
363
  instruction_lower = instruction.lower()
364
 
365
+ # Complex refinements
366
+ if "rotate" in instruction_lower:
367
+ angle = math.pi / 6 # 30 degrees
368
+ center = [256, 256]
369
+ for part, point in keypoints.items():
370
+ if point:
371
+ keypoints[part] = rotate_point(point, center, angle)
372
+
373
+ elif "scale" in instruction_lower:
374
+ if "up" in instruction_lower or "large" in instruction_lower:
375
+ keypoints = scale_pose(keypoints, 1.2)
376
+ elif "down" in instruction_lower or "small" in instruction_lower:
377
+ keypoints = scale_pose(keypoints, 0.8)
378
+
379
+ elif "jump" in instruction_lower or "leap" in instruction_lower:
380
+ # Move everything up
381
+ for part, point in keypoints.items():
382
+ if point:
383
+ keypoints[part][1] -= 50
384
+ # Spread legs
385
+ if "RAnkle" in keypoints:
386
+ keypoints["RAnkle"][0] -= 30
387
+ if "LAnkle" in keypoints:
388
+ keypoints["LAnkle"][0] += 30
389
 
390
+ elif "crouch" in instruction_lower or "squat" in instruction_lower:
391
+ # Lower body parts
392
+ for part in ["RKnee", "LKnee", "RAnkle", "LAnkle"]:
393
+ if part in keypoints and keypoints[part]:
394
+ keypoints[part][1] += 40
395
+
396
+ elif "spread" in instruction_lower:
397
  if "arm" in instruction_lower:
398
+ if "RWrist" in keypoints:
399
+ keypoints["RWrist"][0] -= 40
400
+ if "LWrist" in keypoints:
401
+ keypoints["LWrist"][0] += 40
402
+ elif "leg" in instruction_lower:
403
+ if "RAnkle" in keypoints:
404
+ keypoints["RAnkle"][0] -= 40
405
+ if "LAnkle" in keypoints:
406
+ keypoints["LAnkle"][0] += 40
407
+
408
+ elif "twist" in instruction_lower:
409
+ # Create twisting effect
410
+ if "RShoulder" in keypoints:
411
+ keypoints["RShoulder"][0] += 20
412
+ if "LShoulder" in keypoints:
413
+ keypoints["LShoulder"][0] -= 20
414
+ if "RHip" in keypoints:
415
+ keypoints["RHip"][0] -= 15
416
+ if "LHip" in keypoints:
417
+ keypoints["LHip"][0] += 15
418
 
419
  return keypoints
420
 
421
  def keypoints_to_openpose_format(keypoints):
422
+ """Convert to OpenPose JSON format with confidence scores"""
423
  if not keypoints:
424
  return "{}"
425
 
 
443
  return json.dumps({"candidate": candidate, "subset": subset}, indent=2)
444
 
445
  # Main generation function
446
+ def generate_pose(prompt, use_llm, template, physics_effect):
447
+ """Generate dynamic pose with physics"""
448
  if template and template != "None":
449
+ keypoints = DYNAMIC_POSES[template]
450
  elif use_llm and FIREWORKS_API_KEY != "YOUR_API_KEY_HERE" and prompt:
451
  keypoints = generate_pose_from_llm(prompt)
452
  elif prompt:
453
+ keypoints = get_dynamic_template(prompt)
454
  else:
455
+ import random
456
+ keypoints = random.choice(list(DYNAMIC_POSES.values()))
457
+
458
+ # Apply physics effect
459
+ if physics_effect != "None":
460
+ keypoints = apply_physics(keypoints, physics_effect.lower())
461
 
462
+ pose_img = draw_pose(keypoints, style="dynamic")
463
  json_str = keypoints_to_openpose_format(keypoints)
464
 
465
  return pose_img, json_str, keypoints
466
 
467
  def refine_existing_pose(instruction, keypoints_json):
468
+ """Refine pose with advanced options"""
469
  if not keypoints_json:
470
  return None, "{}", {}
471
 
472
+ refined_keypoints = refine_pose_advanced(keypoints_json, instruction)
473
+ pose_img = draw_pose(refined_keypoints, style="dynamic")
474
  json_str = keypoints_to_openpose_format(refined_keypoints)
475
 
476
  return pose_img, json_str, refined_keypoints
477
 
478
  def check_api_status():
479
+ """Check API status"""
480
  if FIREWORKS_API_KEY != "YOUR_API_KEY_HERE":
481
  return "✅ API key configured - Advanced AI ready"
482
+ return "⚠️ API key not set - Using dynamic templates"
483
 
484
+ # Create Gradio interface
485
+ app = gr.Blocks(title="Dynamic AI Pose Generator", theme=gr.themes.Soft())
486
 
487
  with app:
488
  keypoints_state = gr.State({})
489
 
490
  gr.Markdown("""
491
+ # 🎯 Dynamic AI Pose Generator
492
+ ### Generate extremely dynamic and precise action poses!
493
  """)
494
 
495
  with gr.Row():
496
  with gr.Column(scale=1):
 
497
  api_status = gr.Markdown(check_api_status())
498
 
499
  use_llm = gr.Checkbox(
500
+ label="🚀 Use Advanced AI (Fireworks)",
501
  value=False
502
  )
503
 
504
  prompt = gr.Textbox(
505
+ label="Describe your action pose",
506
+ placeholder="e.g., Ninja doing a flying kick, Athlete jumping over hurdles",
507
  lines=2
508
  )
509
 
510
+ with gr.Row():
511
+ template = gr.Dropdown(
512
+ choices=["None"] + list(DYNAMIC_POSES.keys()),
513
+ label="🎬 Dynamic Templates",
514
+ value="None"
515
+ )
516
+
517
+ physics_effect = gr.Dropdown(
518
+ choices=["None", "Gravity", "Momentum"],
519
+ label="⚡ Physics Effect",
520
+ value="None"
521
+ )
522
 
523
+ generate_btn = gr.Button("💥 Generate Dynamic Pose", variant="primary", size="lg")
524
 
525
+ gr.Markdown("### 🔧 Advanced Refinement")
 
526
  refinement = gr.Textbox(
527
+ label="Refinement command",
528
+ placeholder="e.g., rotate, scale up, jump higher, spread arms, twist body",
529
  lines=1
530
  )
531
 
532
+ refine_btn = gr.Button("✨ Apply Refinement", variant="secondary")
533
 
 
534
  gr.Examples(
535
  examples=[
536
+ "Ninja performing a flying kick",
537
+ "Basketball player doing a 360 dunk",
538
+ "Breakdancer doing a freeze",
539
+ "Gymnast doing a backflip",
540
+ "Martial artist in combat stance",
541
+ "Dancer leaping through the air",
542
+ "Rock climber reaching for a hold",
543
+ "Skateboarder doing a trick"
544
  ],
545
  inputs=prompt
546
  )
547
+
548
+ gr.Markdown("""
549
+ ### 💡 Pro Tips:
550
+ - Use action verbs: jump, kick, flip, spin
551
+ - Add intensity: extreme, dynamic, explosive
552
+ - Specify sports/activities for better results
553
+ - Combine with physics effects for realism
554
+ """)
555
 
556
  with gr.Column(scale=1):
557
+ pose_image = gr.Image(
558
+ label="🎨 Generated Dynamic Pose",
559
+ type="pil",
560
+ height=512
561
+ )
562
 
563
+ with gr.Accordion("📊 OpenPose JSON Data", open=False):
564
+ json_output = gr.Code(
565
+ language="json",
566
+ lines=15
567
+ )
568
 
569
  # Event handlers
570
  generate_btn.click(
571
  fn=generate_pose,
572
+ inputs=[prompt, use_llm, template, physics_effect],
573
  outputs=[pose_image, json_output, keypoints_state]
574
  )
575
 
 
579
  outputs=[pose_image, json_output, keypoints_state]
580
  )
581
 
582
+ # Launch
583
  app.queue()
584
  app.launch()