|
|
|
|
|
|
|
import gradio as gr |
|
import logging |
|
import sys |
|
import random |
|
import time |
|
from huggingface_hub import InferenceClient |
|
from PIL import Image |
|
import io |
|
import base64 |
|
|
|
|
|
logging.basicConfig( |
|
level=logging.INFO, |
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', |
|
handlers=[logging.StreamHandler(sys.stdout)] |
|
) |
|
logger = logging.getLogger("ai_image_creator") |
|
|
|
|
|
def setup_client(api_key, provider=None): |
|
"""Initialize and return API client""" |
|
try: |
|
if provider: |
|
client = InferenceClient(provider=provider, api_key=api_key) |
|
logger.info(f"{provider} client initialized successfully") |
|
else: |
|
client = InferenceClient(api_key=api_key) |
|
logger.info("Hugging Face client initialized successfully") |
|
return client |
|
except Exception as e: |
|
logger.error(f"Error initializing client: {str(e)}") |
|
return None |
|
|
|
|
|
try: |
|
|
|
hf_api_key = os.getenv("HF_API_KEY") |
|
|
|
hf_client = setup_client(hf_api_key) |
|
logger.info("Hugging Face client created successfully") |
|
|
|
|
|
llama_api_key = os.getenv("HF_API_KEY") |
|
try: |
|
llama_client = setup_client(llama_api_key, "sambanova") |
|
use_llama = True |
|
logger.info("Llama client created successfully") |
|
except Exception as e: |
|
logger.warning(f"Llama client not available: {str(e)}. Will use fallback enhancement.") |
|
llama_client = None |
|
use_llama = False |
|
except Exception as e: |
|
logger.error(f"Failed to create Hugging Face client: {str(e)}") |
|
hf_client = None |
|
llama_client = None |
|
use_llama = False |
|
|
|
|
|
|
|
|
|
IMAGE_MODELS = { |
|
"stabilityai/stable-diffusion-xl-base-1.0": { |
|
"display_name": "SDXL 1.0", |
|
"description": "Best overall quality, slower generation", |
|
"icon": "⭐", |
|
"speed": "slow", |
|
"quality": "excellent" |
|
}, |
|
"runwayml/stable-diffusion-v1-5": { |
|
"display_name": "SD 1.5", |
|
"description": "Good for general purpose, faster generation", |
|
"icon": "🚀", |
|
"speed": "fast", |
|
"quality": "good" |
|
}, |
|
"stabilityai/stable-diffusion-2-1": { |
|
"display_name": "SD 2.1", |
|
"description": "Improved details, balanced speed and quality", |
|
"icon": "✨", |
|
"speed": "medium", |
|
"quality": "very good" |
|
}, |
|
"prompthero/openjourney": { |
|
"display_name": "OpenJourney", |
|
"description": "Midjourney-like stylized results", |
|
"icon": "🎨", |
|
"speed": "medium", |
|
"quality": "stylized" |
|
}, |
|
"dreamlike-art/dreamlike-diffusion-1.0": { |
|
"display_name": "Dreamlike", |
|
"description": "Artistic style with dreamy aesthetics", |
|
"icon": "💫", |
|
"speed": "medium", |
|
"quality": "artistic" |
|
} |
|
} |
|
|
|
|
|
CREATION_TYPES = { |
|
"Realistic Photo": { |
|
"description": "Create a photorealistic image with natural details and lighting", |
|
"icon": "📷", |
|
"prompt_hint": "Try to include details about lighting, time of day, and environment" |
|
}, |
|
"Digital Art": { |
|
"description": "Create colorful digital artwork with clean lines and vibrant colors", |
|
"icon": "🖌️", |
|
"prompt_hint": "Consider specifying color palette and mood for better results" |
|
}, |
|
"Fantasy Illustration": { |
|
"description": "Create magical and fantastical scenes with otherworldly elements", |
|
"icon": "🧙", |
|
"prompt_hint": "Describe magical elements, creatures, and environments in detail" |
|
}, |
|
"Concept Art": { |
|
"description": "Create professional concept art for characters, environments or objects", |
|
"icon": "🎮", |
|
"prompt_hint": "Include details about perspective, purpose, and design influences" |
|
}, |
|
"Anime/Manga": { |
|
"description": "Create Japanese anime or manga style illustration", |
|
"icon": "🍙", |
|
"prompt_hint": "Specify anime aesthetics like shading style and character features" |
|
}, |
|
"Oil Painting": { |
|
"description": "Create an image with oil painting textures and artistic brushstrokes", |
|
"icon": "🖼️", |
|
"prompt_hint": "Consider describing texture, brushwork style, and canvas feel" |
|
}, |
|
"Watercolor": { |
|
"description": "Create a soft watercolor illustration with subtle color blending", |
|
"icon": "💧", |
|
"prompt_hint": "Mention color blending, paper texture, and watercolor-specific effects" |
|
}, |
|
"Sketch": { |
|
"description": "Create a detailed sketch or drawing with line art focus", |
|
"icon": "✏️", |
|
"prompt_hint": "Describe line weight, hatching style, and sketch medium (pencil, charcoal, etc.)" |
|
}, |
|
"3D Rendering": { |
|
"description": "Create an image that looks like a 3D rendered scene with realistic lighting", |
|
"icon": "💻", |
|
"prompt_hint": "Include details about lighting setup, materials, and camera perspective" |
|
}, |
|
"Pixel Art": { |
|
"description": "Create retro-style pixel art with limited color palette", |
|
"icon": "👾", |
|
"prompt_hint": "Specify resolution, color limitations, and pixel art style (e.g., 16-bit, 8-bit)" |
|
} |
|
} |
|
|
|
|
|
ART_STYLES = { |
|
"Photorealistic": { |
|
"description": "Detailed realistic style that resembles a photograph with accurate lighting and textures", |
|
"icon": "📸", |
|
"examples": "Works by Chuck Close, Richard Estes, or modern 3D renderings" |
|
}, |
|
"Impressionist": { |
|
"description": "Soft brushstrokes that capture light and atmosphere over precise details, like Monet", |
|
"icon": "🌈", |
|
"examples": "Claude Monet, Pierre-Auguste Renoir, Camille Pissarro" |
|
}, |
|
"Surrealist": { |
|
"description": "Dreamlike quality with impossible or irrational scenes, like Salvador Dali", |
|
"icon": "🌀", |
|
"examples": "Salvador Dali, René Magritte, Frida Kahlo" |
|
}, |
|
"Pop Art": { |
|
"description": "Bold colors, sharp lines and popular culture references, like Andy Warhol", |
|
"icon": "🎭", |
|
"examples": "Andy Warhol, Roy Lichtenstein, Keith Haring" |
|
}, |
|
"Minimalist": { |
|
"description": "Simplified forms, limited color palette, and clean composition with minimal elements", |
|
"icon": "⬜", |
|
"examples": "Piet Mondrian, Kazimir Malevich, Agnes Martin" |
|
}, |
|
"Abstract": { |
|
"description": "Non-representational style using shapes, colors, and forms to express ideas", |
|
"icon": "🔶", |
|
"examples": "Wassily Kandinsky, Jackson Pollock, Mark Rothko" |
|
}, |
|
"Cubist": { |
|
"description": "Geometric shapes and multiple perspectives shown simultaneously, like Picasso", |
|
"icon": "📐", |
|
"examples": "Pablo Picasso, Georges Braque, Juan Gris" |
|
}, |
|
"Art Nouveau": { |
|
"description": "Ornate, flowing lines inspired by natural forms with decorative elegance", |
|
"icon": "🌿", |
|
"examples": "Alphonse Mucha, Gustav Klimt, Antoni Gaudí" |
|
}, |
|
"Gothic": { |
|
"description": "Dark, medieval-inspired aesthetic with dramatic lighting and architectural elements", |
|
"icon": "🏰", |
|
"examples": "Zdzisław Beksiński, H.R. Giger, medieval architecture" |
|
}, |
|
"Cyberpunk": { |
|
"description": "Futuristic dystopian style with neon colors, technology, and urban decay", |
|
"icon": "🤖", |
|
"examples": "Blade Runner, Ghost in the Shell, Akira" |
|
}, |
|
"Steampunk": { |
|
"description": "Victorian-era aesthetic combined with steam-powered technology and brass elements", |
|
"icon": "⚙️", |
|
"examples": "Works by James Ng, Keith Thompson, retrofuturistic Jules Verne adaptations" |
|
}, |
|
"Retro/Vintage": { |
|
"description": "Nostalgic style reminiscent of past decades with period-appropriate elements", |
|
"icon": "📺", |
|
"examples": "1950s advertisements, vintage travel posters, pulp magazine covers" |
|
}, |
|
"Art Deco": { |
|
"description": "Geometric patterns, bold colors, and luxurious materials in a symmetrical style", |
|
"icon": "🏢", |
|
"examples": "Works from the 1920s-30s, Chrysler Building, Tamara de Lempicka paintings" |
|
}, |
|
"Baroque": { |
|
"description": "Dramatic, ornate style with rich details, contrast, and dynamic composition", |
|
"icon": "👑", |
|
"examples": "Caravaggio, Rembrandt, Peter Paul Rubens" |
|
}, |
|
"Ukiyo-e": { |
|
"description": "Traditional Japanese woodblock print style with flat areas of color and strong outlines", |
|
"icon": "🌊", |
|
"examples": "Hokusai's Great Wave, Hiroshige's landscapes, traditional Japanese prints" |
|
}, |
|
"Comic Book": { |
|
"description": "Bold outlines, bright colors, and action-oriented composition like classic comics", |
|
"icon": "💥", |
|
"examples": "Jack Kirby, Steve Ditko, modern Marvel/DC art styles" |
|
}, |
|
"Psychedelic": { |
|
"description": "Vibrant, swirling colors with abstract patterns inspired by 1960s art", |
|
"icon": "🌈", |
|
"examples": "1960s concert posters, Peter Max, Alex Grey" |
|
}, |
|
"Vaporwave": { |
|
"description": "Glitch aesthetics with pastel colors, 80s/90s nostalgia and digital elements", |
|
"icon": "📼", |
|
"examples": "Retrowave aesthetics, 80s digital graphics, glitch art" |
|
}, |
|
"Studio Ghibli": { |
|
"description": "Whimsical, detailed animation style inspired by Japanese animated films", |
|
"icon": "🐉", |
|
"examples": "Spirited Away, My Neighbor Totoro, Howl's Moving Castle" |
|
}, |
|
"Hyperrealism": { |
|
"description": "Extremely detailed realism that exceeds photograph-like precision", |
|
"icon": "🔍", |
|
"examples": "Works by Roberto Bernardi, Denis Peterson, Gottfried Helnwein" |
|
} |
|
} |
|
|
|
|
|
MOODS = { |
|
"Happy": { |
|
"description": "Bright, cheerful atmosphere with warm colors", |
|
"icon": "😊", |
|
"color_palette": "Warm and vibrant colors: yellows, bright oranges, light blues" |
|
}, |
|
"Sad": { |
|
"description": "Melancholic atmosphere with muted colors", |
|
"icon": "😢", |
|
"color_palette": "Muted blues, grays, desaturated colors, cool tones" |
|
}, |
|
"Mysterious": { |
|
"description": "Enigmatic atmosphere with shadows and hidden elements", |
|
"icon": "🔮", |
|
"color_palette": "Deep purples, dark blues, hints of teal, selective lighting" |
|
}, |
|
"Peaceful": { |
|
"description": "Serene, calm atmosphere with gentle lighting", |
|
"icon": "🕊️", |
|
"color_palette": "Soft blues, gentle greens, pastel colors, balanced light" |
|
}, |
|
"Tense": { |
|
"description": "Suspenseful atmosphere with dramatic lighting", |
|
"icon": "😰", |
|
"color_palette": "High contrast, dark shadows, selective reds, strong highlights" |
|
}, |
|
"Whimsical": { |
|
"description": "Playful, imaginative atmosphere with fanciful elements", |
|
"icon": "🦄", |
|
"color_palette": "Pastels, candy colors, unexpected color combinations" |
|
}, |
|
"Dark": { |
|
"description": "Gloomy atmosphere with deep shadows and low lighting", |
|
"icon": "🌑", |
|
"color_palette": "Dark blues, blacks, deep greens, minimal highlights" |
|
}, |
|
"Energetic": { |
|
"description": "Dynamic, vibrant atmosphere with strong colors and movement", |
|
"icon": "⚡", |
|
"color_palette": "Saturated primary colors, bold contrasts, vibrant hues" |
|
}, |
|
"Romantic": { |
|
"description": "Soft, dreamy atmosphere with warm, gentle lighting", |
|
"icon": "❤️", |
|
"color_palette": "Soft pinks, gentle reds, golden highlights, warm tones" |
|
}, |
|
"Epic": { |
|
"description": "Grand, impressive atmosphere with dramatic scale and lighting", |
|
"icon": "🏔️", |
|
"color_palette": "Bold colors, dramatic contrast, atmospheric lighting, expansive scale" |
|
} |
|
} |
|
|
|
|
|
EXAMPLE_PROMPTS = [ |
|
{ |
|
"text": "A serene lake at sunset with mountains in the background and a small wooden boat floating nearby", |
|
"thumbnail_desc": "Peaceful lake scene at sunset", |
|
"creation_type": "Realistic Photo", |
|
"art_style": "Photorealistic", |
|
"mood": "Peaceful", |
|
"tags": ["nature", "landscape", "water", "sunset"] |
|
}, |
|
{ |
|
"text": "A futuristic cityscape with flying cars, neon lights, and tall skyscrapers under a night sky with two moons", |
|
"thumbnail_desc": "Futuristic city with flying cars", |
|
"creation_type": "Concept Art", |
|
"art_style": "Cyberpunk", |
|
"mood": "Mysterious", |
|
"tags": ["scifi", "future", "urban", "night"] |
|
}, |
|
{ |
|
"text": "A close-up portrait of an elderly craftsman with weathered hands working on an intricate wooden carving in his workshop", |
|
"thumbnail_desc": "Elderly craftsman working with wood", |
|
"creation_type": "Oil Painting", |
|
"art_style": "Hyperrealism", |
|
"mood": "Peaceful", |
|
"tags": ["portrait", "craftsmanship", "elderly", "detail"] |
|
}, |
|
{ |
|
"text": "A magical forest with glowing mushrooms, fairy lights, and a small cottage with smoke coming from the chimney", |
|
"thumbnail_desc": "Magical forest with glowing elements", |
|
"creation_type": "Fantasy Illustration", |
|
"art_style": "Studio Ghibli", |
|
"mood": "Whimsical", |
|
"tags": ["fantasy", "forest", "magic", "cottage"] |
|
}, |
|
{ |
|
"text": "A cybernetic samurai with glowing blue circuits standing in a rainy Tokyo street at night", |
|
"thumbnail_desc": "Cybernetic samurai in rainy Tokyo", |
|
"creation_type": "Digital Art", |
|
"art_style": "Cyberpunk", |
|
"mood": "Dark", |
|
"tags": ["character", "cyberpunk", "samurai", "rain"] |
|
}, |
|
{ |
|
"text": "A cute cat with dragon wings and tiny horns sleeping on a pile of gold coins", |
|
"thumbnail_desc": "Cat with dragon features on gold", |
|
"creation_type": "Fantasy Illustration", |
|
"art_style": "Comic Book", |
|
"mood": "Whimsical", |
|
"tags": ["creature", "fantasy", "cute", "treasure"] |
|
}, |
|
{ |
|
"text": "An ancient temple covered in vines and moss, partially sunken into a crystal-clear cenote in the jungle", |
|
"thumbnail_desc": "Ancient temple in jungle cenote", |
|
"creation_type": "Concept Art", |
|
"art_style": "Photorealistic", |
|
"mood": "Mysterious", |
|
"tags": ["architecture", "ruins", "jungle", "water"] |
|
}, |
|
{ |
|
"text": "A cozy coffee shop interior with rain falling outside the windows, soft lighting, and a few people reading books", |
|
"thumbnail_desc": "Cozy rainy day in coffee shop", |
|
"creation_type": "Digital Art", |
|
"art_style": "Impressionist", |
|
"mood": "Peaceful", |
|
"tags": ["interior", "rainy", "cozy", "urban"] |
|
} |
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
def format_dropdown_choices(options_dict): |
|
return [f"{info.get('icon', '•')} {key}" for key in options_dict.keys()] |
|
|
|
|
|
def extract_key(formatted_choice): |
|
|
|
parts = formatted_choice.split(' ', 1) |
|
if len(parts) > 1: |
|
return parts[1] |
|
return formatted_choice |
|
|
|
|
|
def load_example(example_index): |
|
if example_index < 0 or example_index >= len(EXAMPLE_PROMPTS): |
|
return "", "", "", "" |
|
|
|
example = EXAMPLE_PROMPTS[example_index] |
|
creation = f"{CREATION_TYPES[example['creation_type']]['icon']} {example['creation_type']}" |
|
art = f"{ART_STYLES[example['art_style']]['icon']} {example['art_style']}" |
|
mood = f"{MOODS[example['mood']]['icon']} {example['mood']}" |
|
|
|
return example["text"], creation, art, mood |
|
|
|
|
|
def get_model_key(formatted_name): |
|
|
|
if ' ' in formatted_name: |
|
display_name = formatted_name.split(' ', 1)[1] |
|
|
|
for key, info in IMAGE_MODELS.items(): |
|
if info['display_name'] == display_name: |
|
return key |
|
return list(IMAGE_MODELS.keys())[0] |
|
|
|
|
|
|
|
|
|
|
|
css = """ |
|
/* Main theme colors with CSS variables for better customization */ |
|
:root { |
|
--primary-color: #4F46E5; |
|
--primary-hover: #4338CA; |
|
--secondary-color: #7C3AED; |
|
--secondary-hover: #6D28D9; |
|
--background-color: #F8FAFC; |
|
--card-color: #FFFFFF; |
|
--text-color: #1E293B; |
|
--text-muted: #64748B; |
|
--accent-color: #3B82F6; |
|
--success-color: #10B981; |
|
--success-hover: #059669; |
|
--warning-color: #F59E0B; |
|
--error-color: #EF4444; |
|
--error-hover: #DC2626; |
|
--border-color: #E2E8F0; |
|
--border-hover: #CBD5E1; |
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); |
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); |
|
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); |
|
--radius-sm: 0.375rem; |
|
--radius: 0.5rem; |
|
--radius-lg: 0.75rem; |
|
--radius-xl: 1rem; |
|
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
|
} |
|
|
|
/* Global styles and resets */ |
|
body, html { |
|
font-family: var(--font-sans); |
|
color: var(--text-color); |
|
background-color: var(--background-color); |
|
line-height: 1.5; |
|
margin: 0; |
|
padding: 0; |
|
} |
|
|
|
/* Container with responsive padding */ |
|
.container { |
|
max-width: 1400px; |
|
margin: 0 auto; |
|
padding: 1rem; |
|
} |
|
|
|
@media (max-width: 640px) { |
|
.container { |
|
padding: 0.5rem; |
|
} |
|
} |
|
|
|
/* Card styling with elevation and hover effects */ |
|
.gr-panel { |
|
border-radius: var(--radius) !important; |
|
border: 1px solid var(--border-color) !important; |
|
box-shadow: var(--shadow) !important; |
|
overflow: hidden; |
|
transition: transform 0.2s, box-shadow 0.2s; |
|
background-color: var(--card-color) !important; |
|
} |
|
|
|
.gr-panel:hover { |
|
transform: translateY(-2px); |
|
box-shadow: var(--shadow-lg) !important; |
|
} |
|
|
|
/* Button styling with gradient and hover states */ |
|
button.primary { |
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important; |
|
color: white !important; |
|
border: none !important; |
|
border-radius: var(--radius) !important; |
|
font-weight: 600 !important; |
|
letter-spacing: 0.025em !important; |
|
padding: 0.75rem 1.5rem !important; |
|
transition: all 0.3s ease !important; |
|
box-shadow: var(--shadow-sm) !important; |
|
outline: none !important; |
|
text-transform: none !important; |
|
} |
|
|
|
button.primary:hover { |
|
transform: translateY(-1px); |
|
box-shadow: var(--shadow) !important; |
|
opacity: 0.9; |
|
} |
|
|
|
button.primary:active { |
|
transform: translateY(0); |
|
opacity: 0.8; |
|
} |
|
|
|
button.primary[disabled], button.primary[disabled]:hover { |
|
opacity: 0.5; |
|
cursor: not-allowed; |
|
transform: none; |
|
} |
|
|
|
/* Secondary button styling */ |
|
button.secondary { |
|
background-color: transparent !important; |
|
color: var(--primary-color) !important; |
|
border: 1px solid var(--primary-color) !important; |
|
border-radius: var(--radius) !important; |
|
font-weight: 500 !important; |
|
padding: 0.625rem 1.25rem !important; |
|
transition: all 0.2s ease !important; |
|
text-transform: none !important; |
|
} |
|
|
|
button.secondary:hover { |
|
background-color: rgba(79, 70, 229, 0.05) !important; |
|
border-color: var(--primary-hover) !important; |
|
} |
|
|
|
/* Style for the example buttons */ |
|
.example-button { |
|
font-size: 0.875rem !important; |
|
padding: 0.5rem 0.75rem !important; |
|
background-color: transparent !important; |
|
border: 1px solid var(--border-color) !important; |
|
border-radius: var(--radius) !important; |
|
transition: all 0.2s !important; |
|
text-align: left !important; |
|
justify-content: flex-start !important; |
|
height: auto !important; |
|
text-overflow: ellipsis; |
|
overflow: hidden; |
|
white-space: nowrap; |
|
width: 100%; |
|
color: var(--text-color) !important; |
|
} |
|
|
|
.example-button:hover { |
|
background-color: rgba(79, 70, 229, 0.05) !important; |
|
border-color: var(--primary-color) !important; |
|
transform: translateY(-1px); |
|
} |
|
|
|
/* Form controls styling */ |
|
.gr-input, .gr-textarea, .gr-dropdown { |
|
border-radius: var(--radius) !important; |
|
border: 1px solid var(--border-color) !important; |
|
transition: border-color 0.2s, box-shadow 0.2s !important; |
|
font-family: var(--font-sans) !important; |
|
color: var(--text-color) !important; |
|
} |
|
|
|
.gr-input:focus, .gr-textarea:focus, .gr-dropdown:focus-within { |
|
border-color: var(--primary-color) !important; |
|
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2) !important; |
|
outline: none !important; |
|
} |
|
|
|
.gr-form { |
|
gap: 1rem !important; |
|
} |
|
|
|
.gr-input-label, .gr-dropdown-label, .gr-textarea-label, .gr-checkbox-label, .gr-radio-label { |
|
font-size: 0.875rem !important; |
|
font-weight: 500 !important; |
|
color: var(--text-color) !important; |
|
margin-bottom: 0.25rem !important; |
|
} |
|
|
|
/* Input placeholder styling */ |
|
.gr-input::placeholder, .gr-textarea::placeholder { |
|
color: var(--text-muted) !important; |
|
opacity: 0.7; |
|
} |
|
|
|
/* Input and textarea styling */ |
|
textarea, input[type="text"] { |
|
border-radius: var(--radius) !important; |
|
border: 1px solid var(--border-color) !important; |
|
padding: 0.75rem 1rem !important; |
|
transition: border-color 0.2s, box-shadow 0.2s !important; |
|
font-family: var(--font-sans) !important; |
|
} |
|
|
|
textarea:focus, input[type="text"]:focus { |
|
border-color: var(--primary-color) !important; |
|
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2) !important; |
|
outline: none !important; |
|
} |
|
|
|
/* Dropdown styling */ |
|
.gr-dropdown { |
|
border-radius: var(--radius) !important; |
|
border: 1px solid var(--border-color) !important; |
|
background-color: var(--card-color) !important; |
|
} |
|
|
|
.gr-dropdown > div { |
|
border-radius: var(--radius) !important; |
|
min-height: 38px !important; |
|
} |
|
|
|
.gr-dropdown > div > span { |
|
font-size: 0.9375rem !important; |
|
} |
|
|
|
/* Dropdown menu styling */ |
|
.gr-dropdown ul { |
|
background-color: var(--card-color) !important; |
|
border: 1px solid var(--border-color) !important; |
|
border-radius: var(--radius) !important; |
|
box-shadow: var(--shadow) !important; |
|
} |
|
|
|
.gr-dropdown ul li { |
|
padding: 0.5rem 0.75rem !important; |
|
} |
|
|
|
.gr-dropdown ul li:hover { |
|
background-color: rgba(79, 70, 229, 0.05) !important; |
|
} |
|
|
|
/* Custom header with gradient background */ |
|
.app-header { |
|
text-align: center; |
|
padding: 1.75rem 1rem; |
|
margin-bottom: 2rem; |
|
background: linear-gradient(135deg, rgba(79, 70, 229, 0.08), rgba(124, 58, 237, 0.08)); |
|
border-radius: var(--radius-lg); |
|
border-bottom: 3px solid var(--primary-color); |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
|
|
.app-header::before { |
|
content: ''; |
|
position: absolute; |
|
top: -50px; |
|
left: -50px; |
|
right: -50px; |
|
height: 100px; |
|
background: linear-gradient(135deg, rgba(79, 70, 229, 0.2), rgba(124, 58, 237, 0.2)); |
|
transform: rotate(-5deg); |
|
z-index: 0; |
|
} |
|
|
|
.app-header h1 { |
|
font-size: 2.5rem !important; |
|
font-weight: 800 !important; |
|
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); |
|
-webkit-background-clip: text; |
|
-webkit-text-fill-color: transparent; |
|
margin-bottom: 0.5rem !important; |
|
position: relative; |
|
z-index: 1; |
|
} |
|
|
|
.app-header p { |
|
font-size: 1.25rem !important; |
|
color: var(--text-color); |
|
opacity: 0.8; |
|
max-width: 40rem; |
|
margin: 0 auto; |
|
position: relative; |
|
z-index: 1; |
|
} |
|
|
|
/* Responsive header */ |
|
@media (max-width: 640px) { |
|
.app-header h1 { |
|
font-size: 2rem !important; |
|
} |
|
|
|
.app-header p { |
|
font-size: 1rem !important; |
|
} |
|
} |
|
|
|
/* Examples gallery with grid layout */ |
|
.example-gallery { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); |
|
gap: 1rem; |
|
margin: 1rem 0; |
|
} |
|
|
|
.example-item { |
|
border-radius: var(--radius); |
|
overflow: hidden; |
|
cursor: pointer; |
|
border: 2px solid transparent; |
|
transition: all 0.2s; |
|
display: flex; |
|
flex-direction: column; |
|
background-color: var(--card-color); |
|
} |
|
|
|
.example-item:hover { |
|
transform: translateY(-2px); |
|
border-color: var(--accent-color); |
|
box-shadow: var(--shadow); |
|
} |
|
|
|
.example-item:active { |
|
transform: translateY(0); |
|
} |
|
|
|
.example-item-image { |
|
width: 100%; |
|
aspect-ratio: 1; |
|
background-color: #f0f0f0; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
color: var(--text-muted); |
|
font-size: 1.5rem; |
|
border-bottom: 1px solid var(--border-color); |
|
} |
|
|
|
.example-item-caption { |
|
padding: 0.5rem; |
|
font-size: 0.75rem; |
|
line-height: 1.25; |
|
color: var(--text-color); |
|
overflow: hidden; |
|
display: -webkit-box; |
|
-webkit-line-clamp: 2; |
|
-webkit-box-orient: vertical; |
|
} |
|
|
|
/* Loading indicator with animation */ |
|
.loading-indicator { |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
height: 100%; |
|
width: 100%; |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
background-color: rgba(255, 255, 255, 0.8); |
|
z-index: 1000; |
|
backdrop-filter: blur(2px); |
|
border-radius: var(--radius); |
|
} |
|
|
|
.spinner { |
|
width: 40px; |
|
height: 40px; |
|
border: 4px solid rgba(79, 70, 229, 0.2); |
|
border-radius: 50%; |
|
border-top-color: var(--primary-color); |
|
animation: spin 1s linear infinite; |
|
} |
|
|
|
@keyframes spin { |
|
to { |
|
transform: rotate(360deg); |
|
} |
|
} |
|
|
|
/* Info cards with subtle styling */ |
|
.info-card { |
|
background-color: var(--card-color); |
|
border-radius: var(--radius); |
|
padding: 1rem; |
|
border: 1px solid var(--border-color); |
|
margin-bottom: 1rem; |
|
transition: all 0.2s; |
|
} |
|
|
|
.info-card:hover { |
|
border-color: var(--border-hover); |
|
box-shadow: var(--shadow-sm); |
|
} |
|
|
|
.info-card h3 { |
|
margin-top: 0; |
|
margin-bottom: 0.5rem; |
|
font-size: 1rem; |
|
font-weight: 600; |
|
color: var(--primary-color); |
|
display: flex; |
|
align-items: center; |
|
gap: 0.5rem; |
|
} |
|
|
|
.info-card p { |
|
margin: 0; |
|
font-size: 0.875rem; |
|
color: var(--text-muted); |
|
line-height: 1.5; |
|
} |
|
|
|
/* Model info panel with branded styling */ |
|
.model-info { |
|
background-color: rgba(79, 70, 229, 0.05); |
|
border-left: 3px solid var(--primary-color); |
|
padding: 0.75rem 1rem; |
|
border-radius: 0 var(--radius) var(--radius) 0; |
|
margin: 1rem 0; |
|
} |
|
|
|
.model-info h3 { |
|
display: flex; |
|
align-items: center; |
|
gap: 0.5rem; |
|
margin-top: 0; |
|
margin-bottom: 0.5rem; |
|
font-size: 1rem; |
|
font-weight: 600; |
|
color: var(--primary-color); |
|
} |
|
|
|
.model-info p { |
|
margin: 0 0 0.5rem 0; |
|
font-size: 0.875rem; |
|
color: var(--text-color); |
|
} |
|
|
|
.model-info .model-id { |
|
font-size: 0.75rem; |
|
color: var(--text-muted); |
|
font-family: monospace; |
|
background-color: rgba(0, 0, 0, 0.03); |
|
padding: 0.25rem 0.5rem; |
|
border-radius: 4px; |
|
margin-top: 0.5rem; |
|
word-break: break-all; |
|
} |
|
|
|
/* Parameter pills for displaying selections */ |
|
.parameter-pill { |
|
display: inline-flex; |
|
align-items: center; |
|
background-color: rgba(79, 70, 229, 0.1); |
|
color: var(--primary-color); |
|
border-radius: 16px; |
|
padding: 0.25rem 0.75rem; |
|
margin-right: 0.5rem; |
|
margin-bottom: 0.5rem; |
|
font-size: 0.75rem; |
|
font-weight: 500; |
|
user-select: none; |
|
} |
|
|
|
.parameter-pill .icon { |
|
margin-right: 0.25rem; |
|
} |
|
|
|
/* Badge for showing model speed/quality */ |
|
.badge { |
|
display: inline-flex; |
|
align-items: center; |
|
justify-content: center; |
|
border-radius: 9999px; |
|
padding: 0.125rem 0.5rem; |
|
font-size: 0.75rem; |
|
font-weight: 500; |
|
margin-left: 0.5rem; |
|
} |
|
|
|
.badge-success { |
|
background-color: rgba(16, 185, 129, 0.1); |
|
color: var(--success-color); |
|
} |
|
|
|
.badge-warning { |
|
background-color: rgba(245, 158, 11, 0.1); |
|
color: var(--warning-color); |
|
} |
|
|
|
.badge-error { |
|
background-color: rgba(239, 68, 68, 0.1); |
|
color: var(--error-color); |
|
} |
|
|
|
.badge-info { |
|
background-color: rgba(59, 130, 246, 0.1); |
|
color: var(--accent-color); |
|
} |
|
|
|
/* Enhanced accordion styling */ |
|
.gr-accordion { |
|
border: 1px solid var(--border-color) !important; |
|
border-radius: var(--radius) !important; |
|
margin-bottom: 1rem !important; |
|
overflow: hidden; |
|
} |
|
|
|
.gr-accordion > div:first-child { |
|
padding: 0.75rem 1rem !important; |
|
background-color: rgba(0, 0, 0, 0.02) !important; |
|
font-weight: 600 !important; |
|
color: var(--text-color) !important; |
|
font-size: 0.9375rem !important; |
|
} |
|
|
|
.gr-accordion > div:last-child { |
|
padding: 1rem !important; |
|
} |
|
|
|
/* Tooltip styling */ |
|
.tooltip { |
|
position: relative; |
|
display: inline-block; |
|
cursor: help; |
|
} |
|
|
|
.tooltip .tooltiptext { |
|
visibility: hidden; |
|
width: 200px; |
|
background-color: var(--text-color); |
|
color: white; |
|
text-align: center; |
|
border-radius: var(--radius-sm); |
|
padding: 0.5rem 0.75rem; |
|
position: absolute; |
|
z-index: 1000; |
|
bottom: 125%; |
|
left: 50%; |
|
transform: translateX(-50%); |
|
opacity: 0; |
|
transition: opacity 0.3s; |
|
font-size: 0.75rem; |
|
box-shadow: var(--shadow); |
|
pointer-events: none; |
|
} |
|
|
|
.tooltip .tooltiptext::after { |
|
content: ""; |
|
position: absolute; |
|
top: 100%; |
|
left: 50%; |
|
margin-left: -5px; |
|
border-width: 5px; |
|
border-style: solid; |
|
border-color: var(--text-color) transparent transparent transparent; |
|
} |
|
|
|
.tooltip:hover .tooltiptext { |
|
visibility: visible; |
|
opacity: 1; |
|
} |
|
|
|
/* History item styling */ |
|
.history-item { |
|
display: flex; |
|
align-items: center; |
|
padding: 0.75rem; |
|
border-radius: var(--radius); |
|
margin-bottom: 0.75rem; |
|
background-color: var(--card-color); |
|
border: 1px solid var(--border-color); |
|
cursor: pointer; |
|
transition: all 0.2s; |
|
} |
|
|
|
.history-item:hover { |
|
background-color: rgba(79, 70, 229, 0.05); |
|
transform: translateY(-1px); |
|
} |
|
|
|
.history-item-image { |
|
width: 48px; |
|
height: 48px; |
|
border-radius: var(--radius-sm); |
|
object-fit: cover; |
|
margin-right: 0.75rem; |
|
background-color: #f0f0f0; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
color: var(--text-muted); |
|
font-size: 1.25rem; |
|
} |
|
|
|
.history-item-content { |
|
flex: 1; |
|
overflow: hidden; |
|
} |
|
|
|
.history-item-title { |
|
margin: 0; |
|
font-size: 0.875rem; |
|
font-weight: 500; |
|
white-space: nowrap; |
|
overflow: hidden; |
|
text-overflow: ellipsis; |
|
color: var(--text-color); |
|
} |
|
|
|
.history-item-subtitle { |
|
margin: 0; |
|
font-size: 0.75rem; |
|
color: var(--text-muted); |
|
margin-top: 0.25rem; |
|
} |
|
|
|
/* Tabs styling */ |
|
.tabs { |
|
display: flex; |
|
border-bottom: 1px solid var(--border-color); |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.tab { |
|
padding: 0.75rem 1rem; |
|
cursor: pointer; |
|
border-bottom: 2px solid transparent; |
|
font-weight: 500; |
|
font-size: 0.9375rem; |
|
color: var(--text-muted); |
|
transition: all 0.2s; |
|
} |
|
|
|
.tab:hover { |
|
color: var(--primary-color); |
|
} |
|
|
|
.tab.active { |
|
color: var(--primary-color); |
|
border-bottom-color: var(--primary-color); |
|
} |
|
|
|
/* Progress bar */ |
|
.progress-container { |
|
width: 100%; |
|
height: 8px; |
|
background-color: rgba(79, 70, 229, 0.1); |
|
border-radius: 4px; |
|
overflow: hidden; |
|
margin: 0.5rem 0; |
|
} |
|
|
|
.progress-bar { |
|
height: 100%; |
|
background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); |
|
width: 0%; |
|
transition: width 0.3s ease; |
|
border-radius: 4px; |
|
} |
|
|
|
/* Image output container */ |
|
.image-output-container { |
|
position: relative; |
|
border-radius: var(--radius); |
|
overflow: hidden; |
|
transition: all 0.2s; |
|
background-color: #f5f5f5; |
|
min-height: 300px; |
|
} |
|
|
|
.image-output-container img { |
|
width: 100%; |
|
display: block; |
|
border-radius: var(--radius); |
|
} |
|
|
|
.image-placeholder { |
|
position: absolute; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
justify-content: center; |
|
color: var(--text-muted); |
|
font-size: 1rem; |
|
text-align: center; |
|
padding: 1rem; |
|
} |
|
|
|
.image-placeholder .icon { |
|
font-size: 3rem; |
|
margin-bottom: 1rem; |
|
opacity: 0.6; |
|
} |
|
|
|
/* Image controls overlay */ |
|
.image-controls { |
|
position: absolute; |
|
bottom: 0; |
|
left: 0; |
|
right: 0; |
|
padding: 0.75rem; |
|
background: linear-gradient(to top, rgba(0,0,0,0.7), rgba(0,0,0,0)); |
|
display: flex; |
|
justify-content: flex-end; |
|
opacity: 0; |
|
transition: opacity 0.2s; |
|
} |
|
|
|
.image-output-container:hover .image-controls { |
|
opacity: 1; |
|
} |
|
|
|
.image-control-button { |
|
background-color: rgba(255, 255, 255, 0.9); |
|
border: none; |
|
border-radius: 50%; |
|
width: 36px; |
|
height: 36px; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
cursor: pointer; |
|
margin-left: 0.5rem; |
|
color: var(--text-color); |
|
transition: all 0.2s; |
|
} |
|
|
|
.image-control-button:hover { |
|
background-color: white; |
|
transform: translateY(-2px); |
|
box-shadow: var(--shadow); |
|
} |
|
|
|
/* Character counter */ |
|
.character-counter { |
|
text-align: right; |
|
font-size: 0.75rem; |
|
color: var(--text-muted); |
|
margin-top: 0.25rem; |
|
transition: color 0.2s; |
|
} |
|
|
|
.character-counter.warning { |
|
color: var(--warning-color); |
|
} |
|
|
|
.character-counter.error { |
|
color: var(--error-color); |
|
} |
|
|
|
/* Status message */ |
|
.status-message { |
|
padding: 0.75rem 1rem; |
|
border-radius: var(--radius); |
|
margin: 1rem 0; |
|
font-size: 0.875rem; |
|
display: flex; |
|
align-items: center; |
|
} |
|
|
|
.status-message .icon { |
|
margin-right: 0.75rem; |
|
font-size: 1.25rem; |
|
} |
|
|
|
.status-success { |
|
background-color: rgba(16, 185, 129, 0.1); |
|
color: var(--success-color); |
|
border-left: 3px solid var(--success-color); |
|
} |
|
|
|
.status-error { |
|
background-color: rgba(239, 68, 68, 0.1); |
|
color: var(--error-color); |
|
border-left: 3px solid var(--error-color); |
|
} |
|
|
|
.status-warning { |
|
background-color: rgba(245, 158, 11, 0.1); |
|
color: var(--warning-color); |
|
border-left: 3px solid var(--warning-color); |
|
} |
|
|
|
.status-info { |
|
background-color: rgba(59, 130, 246, 0.1); |
|
color: var(--accent-color); |
|
border-left: 3px solid var(--accent-color); |
|
} |
|
|
|
/* Responsive adjustments */ |
|
@media (max-width: 768px) { |
|
.example-gallery { |
|
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); |
|
} |
|
|
|
.gr-panel { |
|
padding: 0.75rem !important; |
|
} |
|
|
|
.gr-accordion > div:first-child { |
|
padding: 0.625rem 0.75rem !important; |
|
} |
|
|
|
.gr-accordion > div:last-child { |
|
padding: 0.75rem !important; |
|
} |
|
|
|
button.primary { |
|
padding: 0.625rem 1.25rem !important; |
|
} |
|
} |
|
""" |
|
|
|
|
|
|
|
|
|
def create_ui(): |
|
with gr.Blocks(title="AI Image Creator", css=css) as interface: |
|
|
|
with gr.Row(elem_classes="app-header"): |
|
with gr.Column(): |
|
gr.HTML(""" |
|
<h1>🎨 AI Image Creator</h1> |
|
<p>Transform your ideas into stunning images with AI-powered text-to-image generation</p> |
|
""") |
|
|
|
|
|
with gr.Row(equal_height=False): |
|
|
|
with gr.Column(scale=1, min_width=380): |
|
|
|
with gr.Group(elem_classes="input-group"): |
|
description_input = gr.Textbox( |
|
label="Describe what you want to see", |
|
placeholder="Be detailed and specific about colors, composition, lighting, and subject...", |
|
lines=4, |
|
max_lines=8, |
|
elem_id="description-input" |
|
) |
|
|
|
|
|
char_counter = gr.HTML( |
|
value="<div class='character-counter'>0 characters</div>", |
|
elem_classes="char-counter-container" |
|
) |
|
|
|
|
|
with gr.Group(elem_classes="examples-group"): |
|
gr.HTML("<h3 style='margin-top: 0; font-size: 1rem; margin-bottom: 0.75rem;'>🌟 Try an example:</h3>") |
|
|
|
|
|
with gr.Row(elem_classes="example-gallery"): |
|
for i, example in enumerate(EXAMPLE_PROMPTS): |
|
with gr.Column(elem_classes="example-item"): |
|
|
|
example_card = gr.Button( |
|
example["thumbnail_desc"], |
|
elem_classes="example-button" |
|
) |
|
|
|
|
|
example_card.click( |
|
fn=lambda idx=i: load_example(idx), |
|
outputs=[description_input, creation_type, art_style, mood_dropdown] |
|
) |
|
|
|
|
|
with gr.Group(elem_classes="settings-group"): |
|
gr.HTML("<h3 style='margin-top: 0; font-size: 1rem; margin-bottom: 0.75rem;'>🛠️ Image Settings</h3>") |
|
|
|
|
|
with gr.Row(): |
|
|
|
creation_type = gr.Dropdown( |
|
choices=format_dropdown_choices(CREATION_TYPES), |
|
value=f"{CREATION_TYPES['Digital Art']['icon']} Digital Art", |
|
label="Creation Type", |
|
elem_classes="enhanced-dropdown" |
|
) |
|
|
|
|
|
art_style = gr.Dropdown( |
|
choices=format_dropdown_choices(ART_STYLES), |
|
value=f"{ART_STYLES['Photorealistic']['icon']} Photorealistic", |
|
label="Art Style", |
|
elem_classes="enhanced-dropdown" |
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
|
mood_dropdown = gr.Dropdown( |
|
choices=format_dropdown_choices(MOODS), |
|
value=f"{MOODS['Peaceful']['icon']} Peaceful", |
|
label="Mood", |
|
elem_classes="enhanced-dropdown" |
|
) |
|
|
|
|
|
formatted_models = [f"{info['icon']} {info['display_name']}" for model_key, info in IMAGE_MODELS.items()] |
|
model_selector = gr.Dropdown( |
|
choices=formatted_models, |
|
value=f"{IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['icon']} {IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['display_name']}", |
|
label="Model", |
|
elem_classes="enhanced-dropdown" |
|
) |
|
|
|
|
|
with gr.Group(elem_classes="info-panels"): |
|
|
|
creation_info = gr.HTML(value="", elem_classes="option-info") |
|
|
|
|
|
art_info = gr.HTML(value="", elem_classes="option-info") |
|
|
|
|
|
model_info = gr.HTML(value="", elem_classes="option-info") |
|
|
|
|
|
with gr.Group(elem_classes="action-group"): |
|
|
|
generate_button = gr.Button( |
|
"✨ Generate Image", |
|
variant="primary", |
|
elem_classes="primary", |
|
elem_id="generate-button" |
|
) |
|
|
|
|
|
generation_status = gr.HTML(value="", elem_classes="generation-status") |
|
|
|
|
|
with gr.Accordion("📝 Tips for better results", open=True): |
|
gr.HTML(""" |
|
<div class="tips-container"> |
|
<h3>Tips for better results:</h3> |
|
<ul> |
|
<li><strong>Be specific and detailed</strong> - Include information about subjects, environment, lighting, colors, perspective, time of day, etc.</li> |
|
<li><strong>Use descriptive adjectives</strong> - Words like "vibrant", "moody", "ethereal", "weathered" help set the tone.</li> |
|
<li><strong>Experiment with art styles</strong> - Different styles can dramatically change the look and feel.</li> |
|
<li><strong>Combine with the right model</strong> - SDXL provides the highest quality but takes longer.</li> |
|
<li><strong>Think about composition</strong> - Mention foreground/background elements and their relationships.</li> |
|
</ul> |
|
</div> |
|
""") |
|
|
|
|
|
with gr.Column(scale=1, min_width=480): |
|
|
|
with gr.Group(elem_classes="output-container"): |
|
|
|
image_output = gr.Image( |
|
label="Generated Image", |
|
elem_id="image-output", |
|
type="pil", |
|
height=512 |
|
) |
|
|
|
|
|
with gr.Accordion("Image Details", open=True): |
|
parameters_display = gr.HTML(value="") |
|
|
|
|
|
with gr.Accordion("Enhanced Prompt", open=False): |
|
prompt_output = gr.Textbox( |
|
label="AI-Enhanced Prompt Used", |
|
lines=5, |
|
elem_id="prompt-output", |
|
elem_classes="prompt-display" |
|
) |
|
|
|
|
|
with gr.Accordion("Technical Details", open=False): |
|
technical_info = gr.HTML(""" |
|
<div class="technical-info"> |
|
<h4>How Image Generation Works</h4> |
|
<p>Images are generated using Stable Diffusion, a latent text-to-image diffusion model. Your text prompt is: |
|
<ol> |
|
<li>Enhanced with AI to add descriptive details and quality terms</li> |
|
<li>Processed through a neural network that gradually transforms random noise into an image</li> |
|
<li>Refined based on the parameters you select (model, style, mood)</li> |
|
</ol> |
|
</p> |
|
<p>Different models have different strengths: |
|
<ul> |
|
<li><strong>SDXL 1.0</strong>: Highest quality, best composition and details</li> |
|
<li><strong>SD 1.5</strong>: Faster generation, good for general purpose</li> |
|
<li><strong>SD 2.1</strong>: Better with human faces, improved consistency</li> |
|
<li><strong>OpenJourney</strong>: Midjourney-like stylized artistic results</li> |
|
<li><strong>Dreamlike</strong>: Dreamy, ethereal aesthetic with artistic flair</li> |
|
</ul> |
|
</p> |
|
</div> |
|
""") |
|
|
|
|
|
return interface, description_input, creation_type, art_style, mood_dropdown, model_selector, generate_button, image_output, generation_status, prompt_output, parameters_display, char_counter, creation_info, art_info, model_info |
|
|
|
|
|
def update_char_count(text): |
|
count = len(text) |
|
if count == 0: |
|
color_class = "" |
|
elif count < 20: |
|
color_class = "warning" |
|
elif count > 300: |
|
color_class = "warning" |
|
elif count > 500: |
|
color_class = "error" |
|
else: |
|
color_class = "" |
|
|
|
return f"<div class='character-counter {color_class}'>{count} characters</div>" |
|
|
|
|
|
def update_creation_info(choice): |
|
key = extract_key(choice) |
|
if key in CREATION_TYPES: |
|
info = CREATION_TYPES[key] |
|
return f"""<div class="info-card"> |
|
<h3>{info['icon']} {key}</h3> |
|
<p>{info['description']}</p> |
|
<p style="margin-top: 0.5rem; font-style: italic; opacity: 0.8;">💡 {info['prompt_hint']}</p> |
|
</div>""" |
|
return "" |
|
|
|
|
|
def update_art_style_info(choice): |
|
key = extract_key(choice) |
|
if key in ART_STYLES: |
|
info = ART_STYLES[key] |
|
return f"""<div class="info-card"> |
|
<h3>{info['icon']} {key}</h3> |
|
<p>{info['description']}</p> |
|
<p style="margin-top: 0.5rem; font-style: italic; opacity: 0.8;">🎨 Examples: {info['examples']}</p> |
|
</div>""" |
|
return "" |
|
|
|
|
|
def update_model_info(formatted_choice): |
|
|
|
if ' ' in formatted_choice: |
|
display_name = formatted_choice.split(' ', 1)[1] |
|
|
|
for key, info in IMAGE_MODELS.items(): |
|
if info['display_name'] == display_name: |
|
|
|
speed_badge = "" |
|
if info.get('speed') == 'fast': |
|
speed_badge = '<span class="badge badge-success">Fast</span>' |
|
elif info.get('speed') == 'medium': |
|
speed_badge = '<span class="badge badge-warning">Medium</span>' |
|
elif info.get('speed') == 'slow': |
|
speed_badge = '<span class="badge badge-error">Slower</span>' |
|
|
|
|
|
quality_badge = "" |
|
if info.get('quality') == 'excellent': |
|
quality_badge = '<span class="badge badge-success">Excellent Quality</span>' |
|
elif info.get('quality') == 'very good': |
|
quality_badge = '<span class="badge badge-success">Very Good Quality</span>' |
|
elif info.get('quality') == 'good': |
|
quality_badge = '<span class="badge badge-info">Good Quality</span>' |
|
elif info.get('quality') == 'stylized': |
|
quality_badge = '<span class="badge badge-info">Stylized</span>' |
|
elif info.get('quality') == 'artistic': |
|
quality_badge = '<span class="badge badge-info">Artistic</span>' |
|
|
|
return f"""<div class="model-info"> |
|
<h3>{info['icon']} {info['display_name']} {speed_badge} {quality_badge}</h3> |
|
<p>{info['description']}</p> |
|
<div class="model-id">{key}</div> |
|
</div>""" |
|
return "" |
|
|
|
|
|
def update_status(message, is_error=False, is_warning=False, is_info=False): |
|
if is_error: |
|
status_class = "status-error" |
|
icon = "❌" |
|
elif is_warning: |
|
status_class = "status-warning" |
|
icon = "⚠️" |
|
elif is_info: |
|
status_class = "status-info" |
|
icon = "ℹ️" |
|
else: |
|
status_class = "status-success" |
|
icon = "✅" |
|
|
|
return f"""<div class="status-message {status_class}"> |
|
<span class="icon">{icon}</span> |
|
<span>{message}</span> |
|
</div>""" |
|
|
|
|
|
def format_parameters(creation_type_val, art_style_val, mood_val, model_name): |
|
creation_key = extract_key(creation_type_val) |
|
art_key = extract_key(art_style_val) |
|
mood_key = extract_key(mood_val) |
|
|
|
|
|
model_display_name = "Unknown Model" |
|
model_id = "" |
|
model_icon = "🤖" |
|
for key, info in IMAGE_MODELS.items(): |
|
if f"{info['icon']} {info['display_name']}" == model_name: |
|
model_display_name = info['display_name'] |
|
model_id = key |
|
model_icon = info['icon'] |
|
break |
|
|
|
html = """<div style="margin-bottom: 1rem;"> |
|
<div style="font-weight: 500; margin-bottom: 0.75rem;">Generated with these parameters:</div> |
|
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">""" |
|
|
|
|
|
if creation_key in CREATION_TYPES: |
|
html += f"""<div class="parameter-pill"> |
|
<span class="icon">{CREATION_TYPES[creation_key]['icon']}</span> {creation_key} |
|
</div>""" |
|
|
|
|
|
if art_key in ART_STYLES: |
|
html += f"""<div class="parameter-pill"> |
|
<span class="icon">{ART_STYLES[art_key]['icon']}</span> {art_key} |
|
</div>""" |
|
|
|
|
|
if mood_key in MOODS: |
|
html += f"""<div class="parameter-pill"> |
|
<span class="icon">{MOODS[mood_key]['icon']}</span> {mood_key} |
|
</div>""" |
|
|
|
|
|
html += f"""<div class="parameter-pill"> |
|
<span class="icon">{model_icon}</span> {model_display_name} |
|
</div>""" |
|
|
|
|
|
html += """</div> |
|
<div style="margin-top: 1rem; font-size: 0.75rem; color: var(--text-muted);"> |
|
Image generated on {timestamp} |
|
</div> |
|
</div>""".replace("{timestamp}", time.strftime("%Y-%m-%d at %H:%M:%S")) |
|
|
|
return html |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def enhance_prompt_with_llama(user_input, creation_type, art_style, mood): |
|
""" |
|
Enhance user input with Llama 4 model to create detailed image generation prompts |
|
|
|
Args: |
|
user_input (str): User's original description |
|
creation_type (str): Selected creation type (e.g., "Digital Art") |
|
art_style (str): Selected art style (e.g., "Photorealistic") |
|
mood (str): Selected mood (e.g., "Peaceful") |
|
|
|
Returns: |
|
str: Enhanced prompt optimized for image generation |
|
""" |
|
try: |
|
if not use_llama or llama_client is None: |
|
logger.warning("Llama enhancement not available, using fallback") |
|
return enhance_prompt_fallback(user_input, creation_type, art_style, mood) |
|
|
|
logger.info(f"Enhancing prompt with Llama 4 for creation type: {creation_type}, art style: {art_style}") |
|
|
|
|
|
system_prompt = """You are a world-class prompt engineer who specializes in creating detailed, effective prompts for text-to-image AI models. |
|
|
|
Your task is to transform a user's simple description into a comprehensive, detailed image generation prompt that will create stunning visuals. Consider all the provided elements (description, creation type, art style, mood) and combine them into a cohesive, detailed prompt. |
|
|
|
MOST IMPORTANTLY - ADD LOGICAL DETAILS: |
|
- Analyze what the user wants and add logical details that would make the scene realistic or coherent |
|
- If describing something fantastical (e.g., "flying cat"), add logical details about how this could work (e.g., "a cat with majestic feathered wings spread wide") |
|
- Think about environment, lighting, perspective, time of day, weather, and other contextual elements |
|
- Create a vivid, imaginable scene with spatial relationships clearly defined |
|
|
|
PROMPT STRUCTURE GUIDELINES: |
|
1. Start with the core subject and its primary characteristics |
|
2. Add environment and setting details |
|
3. Describe lighting, atmosphere, and mood |
|
4. Include specific visual style and artistic technique references |
|
5. Add technical quality terms (8K, detailed, masterful, etc.) |
|
|
|
FORMAT YOUR RESPONSE AS A SINGLE PARAGRAPH with no additional comments, explanations, or bullet points. Use natural language without awkward comma separations. Aim for 75-150 words. |
|
|
|
AVOID: |
|
- Do not include quotation marks in your response |
|
- Do not preface with "here's a prompt" or similar text |
|
- Do not use placeholders |
|
- Do not add negative prompts |
|
- Do not write in list format or use bullet points |
|
|
|
Respond only with the enhanced prompt and nothing else.""" |
|
|
|
|
|
creation_info = CREATION_TYPES.get(creation_type, {"description": "Create a detailed image", "icon": "🎨"}) |
|
creation_description = creation_info["description"] |
|
|
|
|
|
style_info = ART_STYLES.get(art_style, {"description": "with detailed and professional quality", "icon": "🖌️"}) |
|
style_description = style_info["description"] |
|
|
|
|
|
mood_info = MOODS.get(mood, {"description": "atmospheric", "icon": "✨"}) |
|
mood_description = mood_info["description"] |
|
|
|
|
|
user_prompt = f"""Description: {user_input} |
|
Creation Type: {creation_type} - {creation_description} |
|
Art Style: {art_style} - {style_description} |
|
Mood: {mood} - {mood_description} |
|
|
|
Please create a comprehensive, detailed image generation prompt that combines all these elements.""" |
|
|
|
try: |
|
|
|
completion = llama_client.chat.completions.create( |
|
model="meta-llama/Llama-4-Scout-17B-16E-Instruct", |
|
messages=[ |
|
{"role": "system", "content": system_prompt}, |
|
{"role": "user", "content": user_prompt} |
|
], |
|
max_tokens=500, |
|
temperature=0.7, |
|
) |
|
enhanced = completion.choices[0].message.content |
|
logger.info(f"Llama 4 enhanced prompt: {enhanced[:100]}...") |
|
return enhanced if enhanced else user_input |
|
except Exception as e: |
|
logger.error(f"Error during Llama enhancement: {str(e)}") |
|
return enhance_prompt_fallback(user_input, creation_type, art_style, mood) |
|
except Exception as e: |
|
logger.error(f"Error in Llama enhancement: {str(e)}") |
|
return enhance_prompt_fallback(user_input, creation_type, art_style, mood) |
|
|
|
|
|
def enhance_prompt_fallback(user_input, creation_type, art_style, mood): |
|
""" |
|
Enhance user input without requiring Llama API using rule-based enhancement |
|
|
|
Args: |
|
user_input (str): User's original description |
|
creation_type (str): Selected creation type (e.g., "Digital Art") |
|
art_style (str): Selected art style (e.g., "Photorealistic") |
|
mood (str): Selected mood (e.g., "Peaceful") |
|
|
|
Returns: |
|
str: Enhanced prompt using predefined rules and templates |
|
""" |
|
logger.info(f"Using fallback enhancement for: {user_input[:50]}...") |
|
|
|
|
|
quality_terms = { |
|
"Realistic Photo": [ |
|
"photorealistic", "high resolution", "detailed", |
|
"natural lighting", "sharp focus", "professional photography", |
|
"crisp details", "realistic textures", "DSLR photo" |
|
], |
|
"Digital Art": [ |
|
"vibrant colors", "clean lines", "digital illustration", |
|
"polished", "professional digital art", "detailed rendering", |
|
"digital painting", "colorful", "vector-like precision" |
|
], |
|
"Fantasy Illustration": [ |
|
"magical atmosphere", "fantasy art", "detailed illustration", |
|
"epic", "otherworldly", "imaginative scene", |
|
"fantasy environment", "magical lighting", "mythical qualities" |
|
], |
|
"Concept Art": [ |
|
"professional concept art", "detailed design", "conceptual illustration", |
|
"industry standard", "visual development", "production artwork", |
|
"concept design", "detailed environment", "character design" |
|
], |
|
"Anime/Manga": [ |
|
"anime style", "manga illustration", "cel shaded", |
|
"Japanese animation", "2D character art", "anime aesthetic", |
|
"clean linework", "anime proportions", "stylized features" |
|
], |
|
"Oil Painting": [ |
|
"oil on canvas", "textured brushwork", "rich colors", |
|
"traditional painting", "artistic brushstrokes", "gallery quality", |
|
"glazed layers", "impasto technique", "classical painting style" |
|
], |
|
"Watercolor": [ |
|
"watercolor painting", "soft color bleeding", "delicate washes", |
|
"transparent layers", "loose brushwork", "gentle transitions", |
|
"watercolor paper texture", "wet-on-wet technique", "fluid color blending" |
|
], |
|
"Sketch": [ |
|
"detailed sketch", "pencil drawing", "line art", |
|
"hand-drawn", "fine details", "shading techniques", |
|
"graphite", "charcoal texture", "gestural lines" |
|
], |
|
"3D Rendering": [ |
|
"3D render", "volumetric lighting", "ray tracing", |
|
"3D modeling", "realistic textures", "computer graphics", |
|
"physically based rendering", "global illumination", "ambient occlusion" |
|
], |
|
"Pixel Art": [ |
|
"pixel art", "8-bit style", "retro game aesthetic", |
|
"limited color palette", "pixelated", "nostalgic game art", |
|
"16-bit look", "pixel perfect", "dithering effects" |
|
] |
|
} |
|
|
|
|
|
style_modifiers = { |
|
"Photorealistic": "highly detailed photorealistic style with perfect lighting, natural shadows, and lifelike textures", |
|
"Impressionist": "impressionist style with visible brushstrokes capturing light and atmosphere over precise details, reminiscent of Claude Monet", |
|
"Surrealist": "surrealist style with dreamlike and impossible elements, juxtaposed reality, inspired by Salvador Dali", |
|
"Pop Art": "pop art style with bold colors, sharp lines, halftone patterns and cultural references, like Andy Warhol", |
|
"Minimalist": "minimalist style with simplified forms, limited color palette, clean composition, and essential elements only", |
|
"Abstract": "abstract style using non-representational shapes, colors, and forms to express emotion rather than reality", |
|
"Cubist": "cubist style with geometric forms, multiple perspectives shown simultaneously, fractured surfaces, like Pablo Picasso", |
|
"Art Nouveau": "art nouveau style with ornate flowing lines inspired by natural forms, decorative elegance, and organic shapes", |
|
"Gothic": "gothic style with dark atmosphere, dramatic elements, pointed arches, and medieval-inspired architecture", |
|
"Cyberpunk": "cyberpunk style with neon colors, high tech low life aesthetic, futuristic technology, and urban decay", |
|
"Steampunk": "steampunk style with Victorian aesthetics, brass machinery, steam-powered technology, and retrofuturistic design", |
|
"Retro/Vintage": "retro style with nostalgic elements from past decades, aged texture, and period-appropriate colors and design", |
|
"Art Deco": "art deco style with geometric patterns, bold colors, symmetry, luxurious materials, and streamlined forms", |
|
"Baroque": "baroque style with dramatic lighting, rich details, contrast, dynamic composition, and ornate decorations", |
|
"Ukiyo-e": "ukiyo-e style Japanese woodblock print aesthetic with flat areas of color, strong outlines, and traditional subjects", |
|
"Comic Book": "comic book style with bold outlines, vibrant colors, dynamic action poses, and expressive characters", |
|
"Psychedelic": "psychedelic style with vibrant swirling colors, abstract patterns, distorted perspective, and 1960s-inspired visuals", |
|
"Vaporwave": "vaporwave aesthetic with glitch art, pastel colors, 80s/90s nostalgia, ancient statues, and digital elements", |
|
"Studio Ghibli": "Studio Ghibli anime style with whimsical detailed environments, soft colors, and charming character design", |
|
"Hyperrealism": "hyperrealistic style with extreme detail beyond photography, perfect textures, and meticulous precision" |
|
} |
|
|
|
|
|
mood_modifiers = { |
|
"Happy": "bright cheerful atmosphere with warm golden lighting, vibrant colors, and uplifting elements", |
|
"Sad": "melancholic atmosphere with muted colors, soft shadows, and somber emotional tone", |
|
"Mysterious": "enigmatic atmosphere with shadows, fog, hidden elements, and dramatic lighting contrasts", |
|
"Peaceful": "serene calm atmosphere with gentle lighting, soft colors, and tranquil composition", |
|
"Tense": "suspenseful atmosphere with dramatic lighting, stark contrasts, and unsettling composition", |
|
"Whimsical": "playful whimsical atmosphere with imaginative elements, saturated colors, and fantastical details", |
|
"Dark": "dark gloomy atmosphere with deep shadows, limited lighting, and ominous elements", |
|
"Energetic": "dynamic vibrant atmosphere with strong colors, motion effects, and active composition", |
|
"Romantic": "soft romantic atmosphere with dreamy lighting, gentle colors, and intimate ambiance", |
|
"Epic": "grand epic atmosphere with dramatic scale, sweeping vistas, and majestic lighting" |
|
} |
|
|
|
|
|
type_terms = quality_terms.get(creation_type, [ |
|
"high quality", "detailed", "professional", "masterful", "high resolution", "sharp details" |
|
]) |
|
|
|
|
|
common_terms = [ |
|
"8K resolution", "highly detailed", "professional", "masterpiece", |
|
"trending on artstation", "award winning", "stunning", "intricate details", |
|
"perfect composition", "cinematic lighting" |
|
] |
|
|
|
|
|
style_modifier = style_modifiers.get(art_style, "detailed professional style") |
|
|
|
|
|
mood_modifier = mood_modifiers.get(mood, "atmospheric") |
|
|
|
|
|
prompt_parts = [ |
|
user_input, |
|
style_modifier, |
|
mood_modifier |
|
] |
|
|
|
|
|
selected_type_terms = random.sample(type_terms, min(3, len(type_terms))) |
|
selected_common_terms = random.sample(common_terms, min(3, len(common_terms))) |
|
|
|
|
|
quality_description = ", ".join(selected_type_terms + selected_common_terms) |
|
|
|
|
|
enhanced_prompt = f"{', '.join(prompt_parts)}, {quality_description}" |
|
|
|
logger.info(f"Fallback enhanced prompt: {enhanced_prompt[:100]}...") |
|
return enhanced_prompt |
|
|
|
|
|
|
|
|
|
def generate_image(description, creation_type, art_style, mood, model_name, retries=1): |
|
""" |
|
Generate image based on user inputs by enhancing prompt and calling image model API |
|
|
|
Args: |
|
description (str): User's original description |
|
creation_type (str): Selected creation type |
|
art_style (str): Selected art style |
|
mood (str): Selected mood |
|
model_name (str): Model identifier |
|
retries (int): Number of retries if generation fails |
|
|
|
Returns: |
|
tuple: (image, status_message, enhanced_prompt) |
|
""" |
|
try: |
|
|
|
if not description.strip(): |
|
return None, "Please enter a description for your image", "" |
|
|
|
logger.info(f"Generating image with model: {model_name}") |
|
|
|
|
|
enhanced_prompt = enhance_prompt_with_llama(description, creation_type, art_style, mood) |
|
|
|
|
|
if hf_client is None: |
|
logger.error("Hugging Face client not available") |
|
return None, "Error: Unable to connect to image generation service. Please try again later.", enhanced_prompt |
|
|
|
|
|
negative_prompt = "low quality, blurry, distorted, deformed, disfigured, bad anatomy, watermark, signature, text, poorly drawn, amateur, ugly" |
|
|
|
try: |
|
|
|
logger.info(f"Sending request to model {model_name} with prompt: {enhanced_prompt[:100]}...") |
|
|
|
|
|
start_time = time.time() |
|
|
|
|
|
image = hf_client.text_to_image( |
|
prompt=enhanced_prompt, |
|
model=model_name, |
|
negative_prompt=negative_prompt |
|
) |
|
|
|
|
|
generation_time = time.time() - start_time |
|
logger.info(f"Image generated successfully in {generation_time:.2f} seconds") |
|
|
|
|
|
if use_llama: |
|
enhancement_method = "Llama 4 AI" |
|
else: |
|
enhancement_method = "rule-based enhancement" |
|
|
|
success_message = f"Image created successfully in {generation_time:.1f}s using {model_name.split('/')[-1]} model and {enhancement_method}" |
|
|
|
return image, success_message, enhanced_prompt |
|
|
|
except Exception as e: |
|
error_message = str(e) |
|
logger.error(f"Error during image generation: {error_message}") |
|
|
|
|
|
if retries > 0: |
|
logger.info(f"Retrying image generation, {retries} attempts remaining") |
|
time.sleep(1) |
|
return generate_image(description, creation_type, art_style, mood, model_name, retries - 1) |
|
|
|
|
|
if "429" in error_message: |
|
friendly_error = "Server is currently busy. Please try again in a few moments." |
|
elif "401" in error_message or "403" in error_message: |
|
friendly_error = "Authentication error with the image service. Please check API settings." |
|
elif "timeout" in error_message.lower(): |
|
friendly_error = "Request timed out. The server might be under heavy load." |
|
else: |
|
friendly_error = f"Error generating image: {error_message}" |
|
|
|
return None, friendly_error, enhanced_prompt |
|
except Exception as e: |
|
logger.error(f"Unexpected error in generate_image: {str(e)}") |
|
return None, f"Unexpected error: {str(e)}", "" |
|
|
|
|
|
def generate_with_status(description, creation_type_val, art_style_val, mood_val, model_name): |
|
""" |
|
Wrapper for generate_image that handles UI status updates and parameter formatting |
|
|
|
Args: |
|
description (str): User's original description |
|
creation_type_val (str): Formatted creation type with icon |
|
art_style_val (str): Formatted art style with icon |
|
mood_val (str): Formatted mood with icon |
|
model_name (str): Formatted model name with icon |
|
|
|
Returns: |
|
tuple: (image, status_html, enhanced_prompt, parameters_html) |
|
""" |
|
|
|
if not description.strip(): |
|
return None, update_status("Please enter a description", is_error=True), "", "" |
|
|
|
|
|
creation_key = extract_key(creation_type_val) |
|
art_key = extract_key(art_style_val) |
|
mood_key = extract_key(mood_val) |
|
|
|
|
|
model_key = None |
|
for key, info in IMAGE_MODELS.items(): |
|
if f"{info['icon']} {info['display_name']}" == model_name: |
|
model_key = key |
|
break |
|
|
|
if not model_key: |
|
return None, update_status("Invalid model selection", is_error=True), "", "" |
|
|
|
try: |
|
|
|
image, message, enhanced_prompt = generate_image( |
|
description, creation_key, art_key, mood_key, model_key |
|
) |
|
|
|
if image is None: |
|
return None, update_status(message, is_error=True), "", "" |
|
|
|
|
|
params_html = format_parameters(creation_type_val, art_style_val, mood_val, model_name) |
|
|
|
|
|
success_message = update_status(message) |
|
return image, success_message, enhanced_prompt, params_html |
|
|
|
except Exception as e: |
|
error_message = str(e) |
|
logger.error(f"Error in generate_with_status: {error_message}") |
|
return None, update_status(f"Error: {error_message}", is_error=True), "", "" |
|
|
|
|
|
|
|
def main(): |
|
""" |
|
Main application entry point - creates UI and sets up event handlers |
|
""" |
|
|
|
interface, description_input, creation_type, art_style, mood_dropdown, model_selector, generate_button, image_output, generation_status, prompt_output, parameters_display, char_counter, creation_info, art_info, model_info = create_ui() |
|
|
|
|
|
|
|
|
|
description_input.change( |
|
fn=update_char_count, |
|
inputs=description_input, |
|
outputs=char_counter |
|
) |
|
|
|
|
|
creation_type.change( |
|
fn=update_creation_info, |
|
inputs=creation_type, |
|
outputs=creation_info |
|
) |
|
|
|
|
|
art_style.change( |
|
fn=update_art_style_info, |
|
inputs=art_style, |
|
outputs=art_info |
|
) |
|
|
|
|
|
model_selector.change( |
|
fn=update_model_info, |
|
inputs=model_selector, |
|
outputs=model_info |
|
) |
|
|
|
|
|
generate_button.click( |
|
fn=generate_with_status, |
|
inputs=[ |
|
description_input, |
|
creation_type, |
|
art_style, |
|
mood_dropdown, |
|
model_selector |
|
], |
|
outputs=[ |
|
image_output, |
|
generation_status, |
|
prompt_output, |
|
parameters_display |
|
] |
|
) |
|
|
|
|
|
def load_defaults(): |
|
creation_val = f"{CREATION_TYPES['Digital Art']['icon']} Digital Art" |
|
art_val = f"{ART_STYLES['Photorealistic']['icon']} Photorealistic" |
|
model_val = f"{IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['icon']} {IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['display_name']}" |
|
|
|
creation_info = update_creation_info(creation_val) |
|
art_info = update_art_style_info(art_val) |
|
model_info = update_model_info(model_val) |
|
|
|
return creation_info, art_info, model_info |
|
|
|
|
|
interface.load( |
|
fn=load_defaults, |
|
outputs=[creation_info, art_info, model_info] |
|
) |
|
|
|
|
|
interface.launch( |
|
share=False, |
|
debug=False, |
|
enable_queue=True, |
|
) |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
try: |
|
import pkg_resources |
|
required_packages = ['huggingface_hub', 'gradio', 'pillow'] |
|
for package in required_packages: |
|
try: |
|
pkg_resources.get_distribution(package) |
|
except pkg_resources.DistributionNotFound: |
|
logger.info(f"Installing required package: {package}") |
|
import subprocess |
|
subprocess.check_call(['pip', 'install', package]) |
|
logger.info(f"Successfully installed {package}") |
|
except Exception as e: |
|
logger.warning(f"Error checking or installing dependencies: {str(e)}") |
|
|
|
|
|
main() |