import os import io import json import base64 import time import numpy as np import logging import gradio as gr from PIL import Image from gradio_client import Client # Setup logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # ───────── Backend connection with health monitoring ───────── HF_TOKEN = os.getenv("HF_TOKEN") if not HF_TOKEN: raise ValueError("HF_TOKEN environment variable is required") # Backend connection state backend_status = { "client": None, "connected": False, "last_check": None, "error_message": "" } def check_backend_connection(): """Check backend connection and update status""" global backend_status try: test_client = Client("SnapwearAI/Image_to_flatlay", hf_token=HF_TOKEN) backend_status["client"] = test_client backend_status["connected"] = True backend_status["error_message"] = "" backend_status["last_check"] = time.time() logger.info("✅ Backend connection established") return True, "🟢 Backend is ready for Image To Flatlay" except Exception as e: backend_status["client"] = None backend_status["connected"] = False backend_status["last_check"] = time.time() error_str = str(e).lower() if "timeout" in error_str or "read operation timed out" in error_str: backend_status["error_message"] = "Backend is starting up (5-6 minutes on first load)" return False, "🟡 Backend is starting up. Please wait 5-6 minutes and try again." else: backend_status["error_message"] = f"Connection error: {str(e)}" return False, f"🔴 Backend error: {str(e)}" # Initial connection attempt try: success, status_msg = check_backend_connection() if success: logger.info("Backend client established") else: logger.warning(f"Initial backend connection failed: {status_msg}") except Exception as e: logger.error(f"Failed to connect to backend: {e}") backend_status["connected"] = False backend_status["error_message"] = str(e) def update_backend_status(): """Check and update backend status""" success, status_msg = check_backend_connection() if success: css_class = "status-ready" elif "starting up" in status_msg: css_class = "status-starting" else: css_class = "status-error" status_html = f'
' return status_html # ───────── Styling ───────── css = """ body, .gradio-container { font-family: 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, sans-serif; } #col-left, #col-mid, #col-right { margin: 0 auto; max-width: 430px; } #col-showcase { margin: 0 auto; max-width: 1100px; } #button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #ffffff; font-weight: 600; font-size: 18px; border: none; border-radius: 12px; padding: 12px 24px; transition: all 0.3s ease; } #button:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(102,126,234,0.3); } #button:disabled { background: #ccc !important; cursor: not-allowed; transform: none; box-shadow: none; } .hero-section { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px 20px; border-radius: 20px; margin: 20px 0; text-align: center; } .feature-box { background: #f8fafc; border: 1px solid #e2e8f0; padding: 20px; border-radius: 12px; margin: 10px 0; border-left: 4px solid #667eea; } .showcase-section { background: #ffffff; border: 1px solid #e2e8f0; padding: 30px; border-radius: 16px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); margin: 20px 0; } .step-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px; border-radius: 12px; text-align: center; font-weight: 600; margin: 10px 0; } .social-links { text-align: center; margin: 20px 0; } .social-links a { margin: 0 10px; padding: 8px 16px; background: #667eea; color: white; text-decoration: none; border-radius: 8px; transition: all 0.3s ease; } .social-links a:hover { background: #764ba2; transform: translateY(-2px); } .error-message { color: #dc3545; font-weight: 500; } .success-message { color: #28a745; font-weight: 500; } .status-banner { padding: 15px; border-radius: 12px; margin: 10px 0; text-align: center; font-weight: 600; } .status-ready { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; } .status-starting { background: #fff3cd; border: 1px solid #ffeaa7; color: #856404; } .status-error { background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; } .queue-info { background: #e8f4fd; border: 1px solid #bee5eb; padding: 12px; border-radius: 8px; margin: 10px 0; text-align: center; font-size: 14px; color: #0c5460; } """ def image_to_base64(image: Image.Image) -> str: """ Convert a PIL Image to a base64‐encoded PNG string. """ if image is None: return "" if image.mode not in ("RGB", "RGBA"): image = image.convert("RGB") buffer = io.BytesIO() image.save(buffer, format="PNG", optimize=True) buffer.seek(0) return base64.b64encode(buffer.getvalue()).decode("utf-8") def base64_to_image(b64_str: str) -> Image.Image: """ Decode a base64 string (with or without data URL prefix) into a PIL Image. """ if not b64_str: return None try: if b64_str.startswith("data:"): b64_str = b64_str.split(",", 1)[1] data = base64.b64decode(b64_str) return Image.open(io.BytesIO(data)).convert("RGBA") except Exception as e: logger.error(f"Failed to decode base64 image: {e}") return None # ───────── Section 2: Flatley Image Generation ───────── def generate_flatlay(image, prompt): """ 1. Convert ImageEditor data to JSON payload. 2. Use `mask_b64` directly. 3. Call backend `/predict` endpoint. 4. Decode returned base64 and return as PIL Image. """ # Check backend connection first if not backend_status["connected"] or not backend_status["client"]: success, status_msg = check_backend_connection() if not success: return None, 0, status_msg current_client = backend_status["client"] # Validate inputs if not image: return None if not prompt: return None # 1) Prepare JSON payload payload_str = image_to_base64(image) # 2) Invoke backend from gradio_client import Client HF_TOKEN = os.getenv("HF_TOKEN") client = Client("SnapwearAI/Image_to_flatlay", hf_token=HF_TOKEN) try: result_b64 = current_client.predict( payload_str, prompt, api_name="/predict" ) except Exception as e: logger.error(f"Image generation call failed: {e}") return None # 3) Decode and return result_img = base64_to_image(result_b64) if result_b64 else None return result_img # ───────── Gradio App (Single Canvas) ───────── # ───────── Main UI ───────── with gr.Blocks(css=css, title="Snapwear Image to Flatlay") as demo: # ──────── Hero Section ──────── gr.HTML("""Disclaimer: This demo is free for trials only. Any solicitation for payment based on the free features we provide on this HuggingFace Space is a fraudulent act.
Automatically transform a model-worn garment into a professional flatlay presentation
Isolate clothing from the model image with pixel-perfect precision for crisp layouts
Generate flatlay images in under 60 seconds with one click
See how Snapwear Image to Flatlay compares against leading Models
Generate consistent flatlay product shots for catalogs and listings
Showcase apparel collections in clean, styled layouts
Create professional flatlays to boost click-through and sales
Design eye-catching flatlay visuals for Instagram and Pinterest
Experience the future of virtual Photoshoot.
© 2024 Snapwear AI. Professional AI tools for fashion and design.