import gradio as gr import json import requests import os import math from PIL import Image, ImageDraw, ImageFont import numpy as np # Fireworks AI configuration FIREWORKS_API_KEY = os.getenv("FIREWORKS_API_KEY", "YOUR_API_KEY_HERE") FIREWORKS_API_URL = "https://api.fireworks.ai/inference/v1/chat/completions" # OpenPose keypoint definitions 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 } # Enhanced skeleton connections with thickness 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 and precise pose templates 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 # Find center 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) # Scale around center 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) # Dynamic gradient background for action poses 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)) # Draw shadow/trail effect for dynamic poses 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 effect 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) # Draw skeleton connections with variable thickness 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: # Main line draw.line([tuple(start_point), tuple(end_point)], fill='darkblue', width=thickness) # Highlight draw.line([tuple(start_point), tuple(end_point)], fill='blue', width=thickness-1) # Draw keypoints with gradient effect for part, point in keypoints.items(): if point: x, y = point # Determine size based on body part if part in ["Neck", "RHip", "LHip"]: radius = 7 elif part in ["RWrist", "LWrist", "RAnkle", "LAnkle"]: radius = 4 else: radius = 5 # Outer circle draw.ellipse([x-radius-1, y-radius-1, x+radius+1, y+radius+1], fill='darkred', outline='black') # Inner circle draw.ellipse([x-radius, y-radius, x+radius, y+radius], fill='red', outline='darkred') # Highlight 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, # Higher temperature for more creative poses "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() # Check for specific action keywords 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: # Return random dynamic pose 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": # Apply downward pull to limbs for part in ["RWrist", "LWrist"]: if part in modified and modified[part]: modified[part][1] += 10 elif effect == "momentum": # Add motion direction 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() # Complex refinements if "rotate" in instruction_lower: angle = math.pi / 6 # 30 degrees 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: # Move everything up for part, point in keypoints.items(): if point: keypoints[part][1] -= 50 # Spread legs 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: # Lower body parts 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: # Create twisting effect 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) # Main generation function 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())) # Apply physics effect 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" # Create Gradio interface 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 ) # Event handlers 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] ) # Launch app.queue() app.launch()