|
|
import gradio as gr |
|
|
import json |
|
|
import requests |
|
|
import os |
|
|
import math |
|
|
from PIL import Image, ImageDraw, ImageFont |
|
|
import numpy as np |
|
|
|
|
|
|
|
|
FIREWORKS_API_KEY = os.getenv("FIREWORKS_API_KEY", "YOUR_API_KEY_HERE") |
|
|
FIREWORKS_API_URL = "https://api.fireworks.ai/inference/v1/chat/completions" |
|
|
|
|
|
|
|
|
BODY_PARTS = { |
|
|
"Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4, |
|
|
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9, |
|
|
"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14, |
|
|
"LEye": 15, "REar": 16, "LEar": 17 |
|
|
} |
|
|
|
|
|
|
|
|
POSE_CONNECTIONS = [ |
|
|
("Neck", "Nose", 2), ("Nose", "REye", 1), ("REye", "REar", 1), |
|
|
("Nose", "LEye", 1), ("LEye", "LEar", 1), |
|
|
("Neck", "RShoulder", 3), ("RShoulder", "RElbow", 3), ("RElbow", "RWrist", 2), |
|
|
("Neck", "LShoulder", 3), ("LShoulder", "LElbow", 3), ("LElbow", "LWrist", 2), |
|
|
("Neck", "RHip", 4), ("RHip", "RKnee", 4), ("RKnee", "RAnkle", 3), |
|
|
("Neck", "LHip", 4), ("LHip", "LKnee", 4), ("LKnee", "LAnkle", 3), |
|
|
("RHip", "LHip", 4) |
|
|
] |
|
|
|
|
|
|
|
|
DYNAMIC_POSES = { |
|
|
"🏃 Sprint": { |
|
|
"Nose": [256, 60], "Neck": [256, 100], |
|
|
"RShoulder": [230, 120], "RElbow": [260, 140], "RWrist": [290, 120], |
|
|
"LShoulder": [282, 120], "LElbow": [252, 160], "LWrist": [222, 200], |
|
|
"RHip": [245, 260], "RKnee": [220, 360], "RAnkle": [195, 460], |
|
|
"LHip": [267, 260], "LKnee": [300, 340], "LAnkle": [330, 420], |
|
|
"REye": [246, 50], "LEye": [266, 50], "REar": [236, 55], "LEar": [276, 55] |
|
|
}, |
|
|
"🤸 Backflip": { |
|
|
"Nose": [256, 250], "Neck": [256, 280], |
|
|
"RShoulder": [220, 300], "RElbow": [180, 280], "RWrist": [140, 260], |
|
|
"LShoulder": [292, 300], "LElbow": [332, 280], "LWrist": [372, 260], |
|
|
"RHip": [240, 180], "RKnee": [220, 120], "RAnkle": [200, 60], |
|
|
"LHip": [272, 180], "LKnee": [292, 120], "LAnkle": [312, 60], |
|
|
"REye": [246, 260], "LEye": [266, 260], "REar": [236, 255], "LEar": [276, 255] |
|
|
}, |
|
|
"🥋 Martial Arts Kick": { |
|
|
"Nose": [256, 80], "Neck": [256, 120], |
|
|
"RShoulder": [220, 140], "RElbow": [180, 120], "RWrist": [140, 100], |
|
|
"LShoulder": [292, 140], "LElbow": [332, 160], "LWrist": [372, 180], |
|
|
"RHip": [240, 280], "RKnee": [235, 380], "RAnkle": [230, 480], |
|
|
"LHip": [272, 280], "LKnee": [350, 250], "LAnkle": [420, 220], |
|
|
"REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75] |
|
|
}, |
|
|
"🩰 Ballet Leap": { |
|
|
"Nose": [256, 100], "Neck": [256, 140], |
|
|
"RShoulder": [200, 130], "RElbow": [150, 110], "RWrist": [100, 90], |
|
|
"LShoulder": [312, 130], "LElbow": [362, 110], "LWrist": [412, 90], |
|
|
"RHip": [240, 300], "RKnee": [180, 380], "RAnkle": [120, 460], |
|
|
"LHip": [272, 300], "LKnee": [332, 380], "LAnkle": [392, 460], |
|
|
"REye": [246, 90], "LEye": [266, 90], "REar": [236, 95], "LEar": [276, 95] |
|
|
}, |
|
|
"🏀 Basketball Dunk": { |
|
|
"Nose": [256, 60], "Neck": [256, 100], |
|
|
"RShoulder": [220, 120], "RElbow": [200, 80], "RWrist": [180, 40], |
|
|
"LShoulder": [292, 120], "LElbow": [312, 80], "LWrist": [332, 40], |
|
|
"RHip": [240, 260], "RKnee": [230, 360], "RAnkle": [225, 460], |
|
|
"LHip": [272, 260], "LKnee": [282, 340], "LAnkle": [287, 420], |
|
|
"REye": [246, 50], "LEye": [266, 50], "REar": [236, 55], "LEar": [276, 55] |
|
|
}, |
|
|
"🧘 Advanced Yoga": { |
|
|
"Nose": [256, 380], "Neck": [256, 420], |
|
|
"RShoulder": [220, 440], "RElbow": [200, 400], "RWrist": [180, 360], |
|
|
"LShoulder": [292, 440], "LElbow": [312, 400], "LWrist": [332, 360], |
|
|
"RHip": [240, 280], "RKnee": [220, 180], "RAnkle": [200, 80], |
|
|
"LHip": [272, 280], "LKnee": [292, 180], "LAnkle": [312, 80], |
|
|
"REye": [246, 390], "LEye": [266, 390], "REar": [236, 385], "LEar": [276, 385] |
|
|
}, |
|
|
"🕺 Breakdance Freeze": { |
|
|
"Nose": [150, 400], "Neck": [180, 420], |
|
|
"RShoulder": [200, 400], "RElbow": [220, 350], "RWrist": [240, 300], |
|
|
"LShoulder": [160, 440], "LElbow": [140, 480], "LWrist": [120, 500], |
|
|
"RHip": [300, 350], "RKnee": [350, 300], "RAnkle": [400, 250], |
|
|
"LHip": [280, 380], "LKnee": [330, 420], "LAnkle": [380, 460], |
|
|
"REye": [140, 390], "LEye": [160, 390], "REar": [130, 395], "LEar": [170, 395] |
|
|
}, |
|
|
"🏋️ Weightlifting": { |
|
|
"Nose": [256, 100], "Neck": [256, 140], |
|
|
"RShoulder": [210, 160], "RElbow": [180, 120], "RWrist": [150, 80], |
|
|
"LShoulder": [302, 160], "LElbow": [332, 120], "LWrist": [362, 80], |
|
|
"RHip": [235, 300], "RKnee": [220, 400], "RAnkle": [210, 480], |
|
|
"LHip": [277, 300], "LKnee": [292, 400], "LAnkle": [302, 480], |
|
|
"REye": [246, 90], "LEye": [266, 90], "REar": [236, 95], "LEar": [276, 95] |
|
|
}, |
|
|
"🏊 Swimming Dive": { |
|
|
"Nose": [380, 200], "Neck": [360, 220], |
|
|
"RShoulder": [340, 240], "RElbow": [380, 230], "RWrist": [420, 220], |
|
|
"LShoulder": [340, 200], "LElbow": [380, 190], "LWrist": [420, 180], |
|
|
"RHip": [280, 260], "RKnee": [220, 280], "RAnkle": [160, 300], |
|
|
"LHip": [280, 240], "LKnee": [220, 250], "LAnkle": [160, 260], |
|
|
"REye": [390, 195], "LEye": [390, 205], "REar": [385, 190], "LEar": [385, 210] |
|
|
}, |
|
|
"🎾 Tennis Serve": { |
|
|
"Nose": [256, 80], "Neck": [256, 120], |
|
|
"RShoulder": [220, 140], "RElbow": [200, 100], "RWrist": [180, 60], |
|
|
"LShoulder": [292, 140], "LElbow": [312, 180], "LWrist": [332, 220], |
|
|
"RHip": [240, 280], "RKnee": [235, 380], "RAnkle": [230, 480], |
|
|
"LHip": [272, 280], "LKnee": [277, 380], "LAnkle": [282, 480], |
|
|
"REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75] |
|
|
}, |
|
|
"🪂 Skydiving": { |
|
|
"Nose": [256, 200], "Neck": [256, 240], |
|
|
"RShoulder": [200, 260], "RElbow": [150, 240], "RWrist": [100, 220], |
|
|
"LShoulder": [312, 260], "LElbow": [362, 240], "LWrist": [412, 220], |
|
|
"RHip": [240, 340], "RKnee": [200, 400], "RAnkle": [160, 460], |
|
|
"LHip": [272, 340], "LKnee": [312, 400], "LAnkle": [352, 460], |
|
|
"REye": [246, 190], "LEye": [266, 190], "REar": [236, 195], "LEar": [276, 195] |
|
|
}, |
|
|
"🤾 Handball Jump": { |
|
|
"Nose": [256, 120], "Neck": [256, 160], |
|
|
"RShoulder": [220, 180], "RElbow": [200, 140], "RWrist": [180, 100], |
|
|
"LShoulder": [292, 180], "LElbow": [312, 220], "LWrist": [332, 260], |
|
|
"RHip": [240, 320], "RKnee": [220, 400], "RAnkle": [200, 480], |
|
|
"LHip": [272, 320], "LKnee": [292, 380], "LAnkle": [312, 440], |
|
|
"REye": [246, 110], "LEye": [266, 110], "REar": [236, 115], "LEar": [276, 115] |
|
|
} |
|
|
} |
|
|
|
|
|
def rotate_point(point, center, angle): |
|
|
"""Rotate a point around a center by angle (in radians)""" |
|
|
x, y = point |
|
|
cx, cy = center |
|
|
cos_a = math.cos(angle) |
|
|
sin_a = math.sin(angle) |
|
|
nx = cos_a * (x - cx) - sin_a * (y - cy) + cx |
|
|
ny = sin_a * (x - cx) + cos_a * (y - cy) + cy |
|
|
return [nx, ny] |
|
|
|
|
|
def scale_pose(keypoints, scale_factor): |
|
|
"""Scale pose around center""" |
|
|
if not keypoints: |
|
|
return keypoints |
|
|
|
|
|
|
|
|
valid_points = [p for p in keypoints.values() if p] |
|
|
if not valid_points: |
|
|
return keypoints |
|
|
|
|
|
cx = sum(p[0] for p in valid_points) / len(valid_points) |
|
|
cy = sum(p[1] for p in valid_points) / len(valid_points) |
|
|
|
|
|
|
|
|
scaled = {} |
|
|
for part, point in keypoints.items(): |
|
|
if point: |
|
|
x, y = point |
|
|
nx = cx + (x - cx) * scale_factor |
|
|
ny = cy + (y - cy) * scale_factor |
|
|
scaled[part] = [nx, ny] |
|
|
else: |
|
|
scaled[part] = point |
|
|
|
|
|
return scaled |
|
|
|
|
|
def add_motion_blur(keypoints, direction="horizontal", intensity=0.3): |
|
|
"""Add motion effect to pose""" |
|
|
blurred = keypoints.copy() |
|
|
|
|
|
for part in ["RWrist", "LWrist", "RAnkle", "LAnkle"]: |
|
|
if part in blurred and blurred[part]: |
|
|
x, y = blurred[part] |
|
|
if direction == "horizontal": |
|
|
blurred[part] = [x + intensity * 30, y] |
|
|
elif direction == "vertical": |
|
|
blurred[part] = [x, y + intensity * 30] |
|
|
|
|
|
return blurred |
|
|
|
|
|
def draw_pose(keypoints, width=512, height=512, style="dynamic"): |
|
|
"""Draw pose with enhanced visualization""" |
|
|
img = Image.new('RGB', (width, height), color='white') |
|
|
draw = ImageDraw.Draw(img) |
|
|
|
|
|
|
|
|
if style == "dynamic": |
|
|
for i in range(height): |
|
|
color_val = 255 - int((i / height) * 50) |
|
|
draw.rectangle([(0, i), (width, i+1)], fill=(color_val, color_val, 255)) |
|
|
|
|
|
|
|
|
if style == "dynamic": |
|
|
for start, end, _ in POSE_CONNECTIONS: |
|
|
if start in keypoints and end in keypoints: |
|
|
start_point = keypoints[start] |
|
|
end_point = keypoints[end] |
|
|
if start_point and end_point: |
|
|
|
|
|
shadow_start = [start_point[0] + 5, start_point[1] + 5] |
|
|
shadow_end = [end_point[0] + 5, end_point[1] + 5] |
|
|
draw.line([tuple(shadow_start), tuple(shadow_end)], |
|
|
fill=(200, 200, 200), width=2) |
|
|
|
|
|
|
|
|
for connection in POSE_CONNECTIONS: |
|
|
start, end = connection[0], connection[1] |
|
|
thickness = connection[2] if len(connection) > 2 else 3 |
|
|
|
|
|
if start in keypoints and end in keypoints: |
|
|
start_point = keypoints[start] |
|
|
end_point = keypoints[end] |
|
|
if start_point and end_point: |
|
|
|
|
|
draw.line([tuple(start_point), tuple(end_point)], |
|
|
fill='darkblue', width=thickness) |
|
|
|
|
|
draw.line([tuple(start_point), tuple(end_point)], |
|
|
fill='blue', width=thickness-1) |
|
|
|
|
|
|
|
|
for part, point in keypoints.items(): |
|
|
if point: |
|
|
x, y = point |
|
|
|
|
|
if part in ["Neck", "RHip", "LHip"]: |
|
|
radius = 7 |
|
|
elif part in ["RWrist", "LWrist", "RAnkle", "LAnkle"]: |
|
|
radius = 4 |
|
|
else: |
|
|
radius = 5 |
|
|
|
|
|
|
|
|
draw.ellipse([x-radius-1, y-radius-1, x+radius+1, y+radius+1], |
|
|
fill='darkred', outline='black') |
|
|
|
|
|
draw.ellipse([x-radius, y-radius, x+radius, y+radius], |
|
|
fill='red', outline='darkred') |
|
|
|
|
|
draw.ellipse([x-radius+2, y-radius+2, x+radius-2, y+radius-2], |
|
|
fill='pink', outline=None) |
|
|
|
|
|
return img |
|
|
|
|
|
def generate_pose_from_llm(prompt): |
|
|
"""Generate dynamic pose using LLM with enhanced prompt""" |
|
|
enhanced_prompt = f"""You are an expert in generating DYNAMIC and PRECISE human pose keypoints. |
|
|
|
|
|
Task: Generate highly dynamic, action-oriented pose for: {prompt} |
|
|
|
|
|
Requirements: |
|
|
1. Canvas: 512x512 pixels |
|
|
2. Create EXTREME and DYNAMIC poses with: |
|
|
- Large range of motion |
|
|
- Athletic and acrobatic positions |
|
|
- Dramatic angles and perspectives |
|
|
- Action-oriented body positions |
|
|
3. Use all 18 keypoints: Nose, Neck, RShoulder, RElbow, RWrist, LShoulder, LElbow, LWrist, |
|
|
RHip, RKnee, RAnkle, LHip, LKnee, LAnkle, REye, LEye, REar, LEar |
|
|
4. Make poses that show: |
|
|
- Movement and energy |
|
|
- Athletic performance |
|
|
- Extreme flexibility |
|
|
- Dramatic action |
|
|
|
|
|
Return ONLY a JSON object with keypoint names and [x, y] coordinates. |
|
|
Make it VERY dynamic and exciting!""" |
|
|
|
|
|
headers = { |
|
|
"Accept": "application/json", |
|
|
"Content-Type": "application/json", |
|
|
"Authorization": f"Bearer {FIREWORKS_API_KEY}" |
|
|
} |
|
|
|
|
|
payload = { |
|
|
"model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507", |
|
|
"max_tokens": 1024, |
|
|
"temperature": 0.7, |
|
|
"messages": [ |
|
|
{"role": "system", "content": enhanced_prompt}, |
|
|
{"role": "user", "content": f"Create an extremely dynamic pose for: {prompt}"} |
|
|
] |
|
|
} |
|
|
|
|
|
try: |
|
|
response = requests.post(FIREWORKS_API_URL, headers=headers, json=payload, timeout=30) |
|
|
if response.status_code == 200: |
|
|
data = response.json() |
|
|
content = data['choices'][0]['message']['content'] |
|
|
|
|
|
import re |
|
|
json_match = re.search(r'\{.*\}', content, re.DOTALL) |
|
|
if json_match: |
|
|
keypoints = json.loads(json_match.group()) |
|
|
return keypoints |
|
|
except Exception as e: |
|
|
print(f"LLM Error: {e}") |
|
|
|
|
|
return get_dynamic_template(prompt) |
|
|
|
|
|
def get_dynamic_template(prompt): |
|
|
"""Select dynamic template based on prompt""" |
|
|
if not prompt: |
|
|
return DYNAMIC_POSES["🏃 Sprint"] |
|
|
|
|
|
prompt_lower = prompt.lower() |
|
|
|
|
|
|
|
|
if any(word in prompt_lower for word in ["run", "sprint", "dash"]): |
|
|
return DYNAMIC_POSES["🏃 Sprint"] |
|
|
elif any(word in prompt_lower for word in ["flip", "backflip", "acrobat"]): |
|
|
return DYNAMIC_POSES["🤸 Backflip"] |
|
|
elif any(word in prompt_lower for word in ["kick", "martial", "karate", "taekwondo"]): |
|
|
return DYNAMIC_POSES["🥋 Martial Arts Kick"] |
|
|
elif any(word in prompt_lower for word in ["ballet", "dance", "leap", "jump"]): |
|
|
return DYNAMIC_POSES["🩰 Ballet Leap"] |
|
|
elif any(word in prompt_lower for word in ["basketball", "dunk", "shoot"]): |
|
|
return DYNAMIC_POSES["🏀 Basketball Dunk"] |
|
|
elif any(word in prompt_lower for word in ["yoga", "stretch", "flexible"]): |
|
|
return DYNAMIC_POSES["🧘 Advanced Yoga"] |
|
|
elif any(word in prompt_lower for word in ["breakdance", "freeze", "bboy"]): |
|
|
return DYNAMIC_POSES["🕺 Breakdance Freeze"] |
|
|
elif any(word in prompt_lower for word in ["weight", "lift", "gym"]): |
|
|
return DYNAMIC_POSES["🏋️ Weightlifting"] |
|
|
elif any(word in prompt_lower for word in ["swim", "dive", "water"]): |
|
|
return DYNAMIC_POSES["🏊 Swimming Dive"] |
|
|
elif any(word in prompt_lower for word in ["tennis", "serve", "racket"]): |
|
|
return DYNAMIC_POSES["🎾 Tennis Serve"] |
|
|
elif any(word in prompt_lower for word in ["sky", "fall", "fly"]): |
|
|
return DYNAMIC_POSES["🪂 Skydiving"] |
|
|
elif any(word in prompt_lower for word in ["handball", "throw", "ball"]): |
|
|
return DYNAMIC_POSES["🤾 Handball Jump"] |
|
|
else: |
|
|
|
|
|
import random |
|
|
return random.choice(list(DYNAMIC_POSES.values())) |
|
|
|
|
|
def apply_physics(keypoints, effect="gravity"): |
|
|
"""Apply physics effects to make poses more realistic""" |
|
|
modified = keypoints.copy() |
|
|
|
|
|
if effect == "gravity": |
|
|
|
|
|
for part in ["RWrist", "LWrist"]: |
|
|
if part in modified and modified[part]: |
|
|
modified[part][1] += 10 |
|
|
elif effect == "momentum": |
|
|
|
|
|
if "RWrist" in modified and modified["RWrist"]: |
|
|
modified["RWrist"][0] += 15 |
|
|
if "LAnkle" in modified and modified["LAnkle"]: |
|
|
modified["LAnkle"][0] -= 10 |
|
|
|
|
|
return modified |
|
|
|
|
|
def refine_pose_advanced(current_keypoints, instruction): |
|
|
"""Advanced pose refinement with multiple options""" |
|
|
if not current_keypoints or not instruction: |
|
|
return current_keypoints |
|
|
|
|
|
keypoints = current_keypoints.copy() |
|
|
instruction_lower = instruction.lower() |
|
|
|
|
|
|
|
|
if "rotate" in instruction_lower: |
|
|
angle = math.pi / 6 |
|
|
center = [256, 256] |
|
|
for part, point in keypoints.items(): |
|
|
if point: |
|
|
keypoints[part] = rotate_point(point, center, angle) |
|
|
|
|
|
elif "scale" in instruction_lower: |
|
|
if "up" in instruction_lower or "large" in instruction_lower: |
|
|
keypoints = scale_pose(keypoints, 1.2) |
|
|
elif "down" in instruction_lower or "small" in instruction_lower: |
|
|
keypoints = scale_pose(keypoints, 0.8) |
|
|
|
|
|
elif "jump" in instruction_lower or "leap" in instruction_lower: |
|
|
|
|
|
for part, point in keypoints.items(): |
|
|
if point: |
|
|
keypoints[part][1] -= 50 |
|
|
|
|
|
if "RAnkle" in keypoints: |
|
|
keypoints["RAnkle"][0] -= 30 |
|
|
if "LAnkle" in keypoints: |
|
|
keypoints["LAnkle"][0] += 30 |
|
|
|
|
|
elif "crouch" in instruction_lower or "squat" in instruction_lower: |
|
|
|
|
|
for part in ["RKnee", "LKnee", "RAnkle", "LAnkle"]: |
|
|
if part in keypoints and keypoints[part]: |
|
|
keypoints[part][1] += 40 |
|
|
|
|
|
elif "spread" in instruction_lower: |
|
|
if "arm" in instruction_lower: |
|
|
if "RWrist" in keypoints: |
|
|
keypoints["RWrist"][0] -= 40 |
|
|
if "LWrist" in keypoints: |
|
|
keypoints["LWrist"][0] += 40 |
|
|
elif "leg" in instruction_lower: |
|
|
if "RAnkle" in keypoints: |
|
|
keypoints["RAnkle"][0] -= 40 |
|
|
if "LAnkle" in keypoints: |
|
|
keypoints["LAnkle"][0] += 40 |
|
|
|
|
|
elif "twist" in instruction_lower: |
|
|
|
|
|
if "RShoulder" in keypoints: |
|
|
keypoints["RShoulder"][0] += 20 |
|
|
if "LShoulder" in keypoints: |
|
|
keypoints["LShoulder"][0] -= 20 |
|
|
if "RHip" in keypoints: |
|
|
keypoints["RHip"][0] -= 15 |
|
|
if "LHip" in keypoints: |
|
|
keypoints["LHip"][0] += 15 |
|
|
|
|
|
return keypoints |
|
|
|
|
|
def keypoints_to_openpose_format(keypoints): |
|
|
"""Convert to OpenPose JSON format with confidence scores""" |
|
|
if not keypoints: |
|
|
return "{}" |
|
|
|
|
|
candidate = [] |
|
|
for i in range(18): |
|
|
part_name = None |
|
|
for name, idx in BODY_PARTS.items(): |
|
|
if idx == i: |
|
|
part_name = name |
|
|
break |
|
|
|
|
|
if part_name and part_name in keypoints: |
|
|
x, y = keypoints[part_name] |
|
|
candidate.append([float(x), float(y), 1.0]) |
|
|
else: |
|
|
candidate.append([0.0, 0.0, 0.0]) |
|
|
|
|
|
subset = [[i for i in range(18) if candidate[i][2] > 0]] |
|
|
subset[0].extend([float(len(subset[0])), len(subset[0])]) |
|
|
|
|
|
return json.dumps({"candidate": candidate, "subset": subset}, indent=2) |
|
|
|
|
|
|
|
|
def generate_pose(prompt, use_llm, template, physics_effect): |
|
|
"""Generate dynamic pose with physics""" |
|
|
if template and template != "None": |
|
|
keypoints = DYNAMIC_POSES[template] |
|
|
elif use_llm and FIREWORKS_API_KEY != "YOUR_API_KEY_HERE" and prompt: |
|
|
keypoints = generate_pose_from_llm(prompt) |
|
|
elif prompt: |
|
|
keypoints = get_dynamic_template(prompt) |
|
|
else: |
|
|
import random |
|
|
keypoints = random.choice(list(DYNAMIC_POSES.values())) |
|
|
|
|
|
|
|
|
if physics_effect != "None": |
|
|
keypoints = apply_physics(keypoints, physics_effect.lower()) |
|
|
|
|
|
pose_img = draw_pose(keypoints, style="dynamic") |
|
|
json_str = keypoints_to_openpose_format(keypoints) |
|
|
|
|
|
return pose_img, json_str, keypoints |
|
|
|
|
|
def refine_existing_pose(instruction, keypoints_json): |
|
|
"""Refine pose with advanced options""" |
|
|
if not keypoints_json: |
|
|
return None, "{}", {} |
|
|
|
|
|
refined_keypoints = refine_pose_advanced(keypoints_json, instruction) |
|
|
pose_img = draw_pose(refined_keypoints, style="dynamic") |
|
|
json_str = keypoints_to_openpose_format(refined_keypoints) |
|
|
|
|
|
return pose_img, json_str, refined_keypoints |
|
|
|
|
|
def check_api_status(): |
|
|
"""Check API status""" |
|
|
if FIREWORKS_API_KEY != "YOUR_API_KEY_HERE": |
|
|
return "✅ API key configured - Advanced AI ready" |
|
|
return "⚠️ API key not set - Using dynamic templates" |
|
|
|
|
|
|
|
|
app = gr.Blocks(title="Dynamic AI Pose Generator", theme=gr.themes.Soft()) |
|
|
|
|
|
with app: |
|
|
keypoints_state = gr.State({}) |
|
|
|
|
|
gr.Markdown(""" |
|
|
# 🎯 Dynamic AI Pose Generator |
|
|
### Generate extremely dynamic and precise action poses! |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
api_status = gr.Markdown(check_api_status()) |
|
|
|
|
|
use_llm = gr.Checkbox( |
|
|
label="🚀 Use Advanced AI (Fireworks)", |
|
|
value=False |
|
|
) |
|
|
|
|
|
prompt = gr.Textbox( |
|
|
label="Describe your action pose", |
|
|
placeholder="e.g., Ninja doing a flying kick, Athlete jumping over hurdles", |
|
|
lines=2 |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
template = gr.Dropdown( |
|
|
choices=["None"] + list(DYNAMIC_POSES.keys()), |
|
|
label="🎬 Dynamic Templates", |
|
|
value="None" |
|
|
) |
|
|
|
|
|
physics_effect = gr.Dropdown( |
|
|
choices=["None", "Gravity", "Momentum"], |
|
|
label="⚡ Physics Effect", |
|
|
value="None" |
|
|
) |
|
|
|
|
|
generate_btn = gr.Button("💥 Generate Dynamic Pose", variant="primary", size="lg") |
|
|
|
|
|
gr.Markdown("### 🔧 Advanced Refinement") |
|
|
refinement = gr.Textbox( |
|
|
label="Refinement command", |
|
|
placeholder="e.g., rotate, scale up, jump higher, spread arms, twist body", |
|
|
lines=1 |
|
|
) |
|
|
|
|
|
refine_btn = gr.Button("✨ Apply Refinement", variant="secondary") |
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
"Ninja performing a flying kick", |
|
|
"Basketball player doing a 360 dunk", |
|
|
"Breakdancer doing a freeze", |
|
|
"Gymnast doing a backflip", |
|
|
"Martial artist in combat stance", |
|
|
"Dancer leaping through the air", |
|
|
"Rock climber reaching for a hold", |
|
|
"Skateboarder doing a trick" |
|
|
], |
|
|
inputs=prompt |
|
|
) |
|
|
|
|
|
gr.Markdown(""" |
|
|
### 💡 Pro Tips: |
|
|
- Use action verbs: jump, kick, flip, spin |
|
|
- Add intensity: extreme, dynamic, explosive |
|
|
- Specify sports/activities for better results |
|
|
- Combine with physics effects for realism |
|
|
""") |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
pose_image = gr.Image( |
|
|
label="🎨 Generated Dynamic Pose", |
|
|
type="pil", |
|
|
height=512 |
|
|
) |
|
|
|
|
|
with gr.Accordion("📊 OpenPose JSON Data", open=False): |
|
|
json_output = gr.Code( |
|
|
language="json", |
|
|
lines=15 |
|
|
) |
|
|
|
|
|
|
|
|
generate_btn.click( |
|
|
fn=generate_pose, |
|
|
inputs=[prompt, use_llm, template, physics_effect], |
|
|
outputs=[pose_image, json_output, keypoints_state] |
|
|
) |
|
|
|
|
|
refine_btn.click( |
|
|
fn=refine_existing_pose, |
|
|
inputs=[refinement, keypoints_state], |
|
|
outputs=[pose_image, json_output, keypoints_state] |
|
|
) |
|
|
|
|
|
|
|
|
app.queue() |
|
|
app.launch() |