generart / app.py
Equityone's picture
Update app.py
38edf4a verified
raw
history blame
20.2 kB
import gradio as gr
import os
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
import requests
import io
import json
# Définition des styles
# Styles optimisés et stabilisés
ART_STYLES = {
"Art Moderne": {
"prompt_prefix": "modern art style poster, professional design",
"text_style": "modern clean typography, geometric style",
"guidance": 9.0, # Réduit pour plus de stabilité
"steps": 55,
"negative_prompt": "traditional, photorealistic, cluttered, busy design"
},
"Neo Vintage": {
"prompt_prefix": "vintage style advertising poster, retro design",
"text_style": "retro typography, vintage lettering",
"guidance": 8.5,
"steps": 50,
"negative_prompt": "modern, digital, contemporary style"
},
"Pop Art": {
"prompt_prefix": "pop art style poster, bold design",
"text_style": "bold typography, comic book style text",
"guidance": 8.0,
"steps": 45,
"negative_prompt": "subtle, realistic, traditional art"
},
"Minimaliste": {
"prompt_prefix": "minimalist design poster, clean composition",
"text_style": "clean minimal typography, simple text layout",
"guidance": 7.5,
"steps": 40,
"negative_prompt": "complex, detailed, ornate, busy"
}
}
ART_STYLES = {
# Styles Photoréalistes
"Photo HDR": {
"prompt_prefix": "ultra realistic photograph, professional HDR photography, extremely detailed, 8k uhd",
"text_style": "photographic text overlay",
"guidance": 9.0,
"steps": 60,
"negative_prompt": "illustration, painting, drawing, cartoon, blurry, low quality, artistic"
},
"Portrait Studio": {
"prompt_prefix": "professional studio photography, high end photoshoot, perfect lighting, sharp focus",
"text_style": "elegant text overlay, magazine style typography",
"guidance": 8.5,
"steps": 55,
"negative_prompt": "illustration, drawing, cartoon, painting, low quality"
},
"Nature Pro": {
"prompt_prefix": "professional nature photography, national geographic style, perfect natural lighting",
"text_style": "outdoor photography text style",
"guidance": 8.0,
"steps": 50,
"negative_prompt": "illustration, artificial, cartoon, painting"
},
"Urban Photo": {
"prompt_prefix": "professional urban photography, architectural photo, perfect city shot",
"text_style": "modern photographic typography",
"guidance": 8.5,
"steps": 55,
"negative_prompt": "illustration, drawing, cartoon, unrealistic"
},
# Styles Artistiques existants
"Art Moderne": {
"prompt_prefix": "modern art style poster, professional design",
"text_style": "modern clean typography, geometric style",
"guidance": 9.0,
"steps": 55,
"negative_prompt": "photorealistic, traditional, cluttered"
},
"Neo Vintage": {
"prompt_prefix": "vintage style advertising poster, retro design",
"text_style": "retro typography, vintage lettering",
"guidance": 8.5,
"steps": 50,
"negative_prompt": "modern, photographic, contemporary"
},
# ... autres styles existants ...
}
# Paramètres photo optimisés
PHOTO_PARAMS = {
"HDR": {
"exposure_strength": 1.2,
"contrast": 1.1,
"saturation": 1.05
},
"Portrait": {
"skin_softening": 0.8,
"bokeh_strength": 0.7,
"lighting": "studio"
},
"Nature": {
"sharpness": 1.2,
"vibrance": 1.1,
"detail_enhancement": 1.15
}
}
def enhance_photo_prompt(subject, style, photo_type="HDR"):
"""Améliore le prompt pour un rendu plus photoréaliste"""
base_prompt = f"highly detailed photograph of {subject}, {ART_STYLES[style]['prompt_prefix']}"
photo_enhancements = {
"HDR": ", ultra high resolution photograph, perfect exposure, dramatic lighting, professional color grading",
"Portrait": ", professional portrait photography, perfect skin texture, studio lighting setup, shallow depth of field",
"Nature": ", sharp nature photography, perfect natural lighting, high detail macro shot, professional outdoor photography",
"Urban": ", professional architectural photography, perfect perspective, golden hour lighting, urban landscape"
}
return base_prompt + photo_enhancements.get(photo_type, photo_enhancements["HDR"])
def generate_image(format_size, orientation, subject, style, text_effect, collection, title, subtitle, quality, creativity):
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
try:
width, height = (768, 1024) if format_size == "A4" else (1024, 1024)
if orientation == "Paysage":
width, height = height, width
# Détection du style photo et optimisation du prompt
is_photo_style = any(photo_style in style for photo_style in ["Photo", "Portrait", "Nature Pro", "Urban Photo"])
if is_photo_style:
prompt = enhance_photo_prompt(subject, style)
guidance_scale = 8.0 # Optimisé pour le photoréalisme
steps = 55
else:
prompt = f"{ART_STYLES[style]['prompt_prefix']}, {subject}"
guidance_scale = ART_STYLES[style]['guidance']
steps = ART_STYLES[style]['steps']
if text_effect != "Standard":
prompt += f", {TEXT_EFFECTS[text_effect]['prompt_suffix']}"
if title:
prompt += f", with text '{title}'"
payload = {
"inputs": prompt,
"parameters": {
"negative_prompt": ART_STYLES[style]['negative_prompt'],
"num_inference_steps": min(int(steps * (quality/100)), 60),
"guidance_scale": min(guidance_scale * (creativity/10), 10.0),
"width": width,
"height": height
}
}
print(f"Prompt: {prompt}") # Debug
print(f"Paramètres: {payload}") # Debug
response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
if response.status_code == 200:
image = Image.open(io.BytesIO(response.content))
# Post-traitement pour styles photo
if is_photo_style:
image = enhance_photo(image, style)
return image, "✨ Photo créée avec succès!"
else:
return None, f"⚠️ Erreur {response.status_code}: Essayez de modifier les paramètres"
except Exception as e:
print(f"Exception: {str(e)}")
return None, f"⚠️ Erreur: {str(e)}"
def enhance_photo(image, style):
"""Améliore la photo selon le style"""
try:
enhancer = ImageEnhance.Contrast(image)
image = enhancer.enhance(1.1)
enhancer = ImageEnhance.Color(image)
image = enhancer.enhance(1.05)
enhancer = ImageEnhance.Sharpness(image)
image = enhancer.enhance(1.15)
return image
except:
return image
# Effets de texte stabilisés
TEXT_EFFECTS = {
"Standard": {
"prompt_suffix": "with clear readable text",
"text_params": {"weight": 1.0}
},
"Graffiti": {
"prompt_suffix": "with urban graffiti style text",
"text_params": {"weight": 0.8}
},
"Néon": {
"prompt_suffix": "with glowing neon text",
"text_params": {"weight": 0.9}
},
"3D": {
"prompt_suffix": "with 3D style text",
"text_params": {"weight": 0.85}
}
}
# Collections optimisées
THEME_COLLECTIONS = {
"Tech": {
"prompts": ["technology inspired", "digital aesthetic"],
"styles": ["modern tech elements", "futuristic design"],
"negative": "organic, rustic, natural"
},
"Nature": {
"prompts": ["natural elements", "organic design"],
"styles": ["flowing shapes", "natural textures"],
"negative": "artificial, geometric"
},
"Urbain": {
"prompts": ["urban style", "city aesthetic"],
"styles": ["street art influence", "urban textures"],
"negative": "rural, natural"
}
}
def generate_image(format_size, orientation, subject, style, text_effect, collection, title, subtitle, quality, creativity):
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
try:
# Dimensions optimisées
width, height = (768, 1024) if format_size == "A4" else (1024, 1024)
if orientation == "Paysage":
width, height = height, width
# Construction du prompt optimisée
style_config = ART_STYLES[style]
text_config = TEXT_EFFECTS[text_effect]
prompt = f"{style_config['prompt_prefix']}, {subject}"
# Ajout des éléments de style et texte de manière plus stable
if text_effect != "Standard":
prompt += f", {text_config['prompt_suffix']}"
if collection and collection in THEME_COLLECTIONS:
coll = THEME_COLLECTIONS[collection]
prompt += f", {', '.join(coll['prompts'][:1])}" # Limite à un prompt pour stabilité
if title:
prompt += f", with text '{title}'"
# Configuration optimisée
payload = {
"inputs": prompt,
"parameters": {
"negative_prompt": style_config['negative_prompt'],
"num_inference_steps": min(style_config['steps'], 50), # Limite pour stabilité
"guidance_scale": min(style_config['guidance'] * (creativity/10), 10.0), # Limite max
"width": width,
"height": height
}
}
print(f"Prompt: {prompt}") # Debug
print(f"Paramètres: {payload}") # Debug
response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
if response.status_code == 200:
image = Image.open(io.BytesIO(response.content))
return image, "✨ Création réussie!"
else:
print(f"Erreur API: {response.text}") # Debug
return None, f"⚠️ Erreur {response.status_code}: Essayez de modifier les paramètres"
except Exception as e:
print(f"Exception: {str(e)}") # Debug
return None, f"⚠️ Erreur: {str(e)}"
# Effets de texte
TEXT_EFFECTS = {
"Standard": {
"prompt_suffix": "with clear readable text",
"text_params": {"weight": 1.0}
},
"Néon": {
"prompt_suffix": "with glowing neon text effect",
"text_params": {"weight": 1.2}
},
"3D": {
"prompt_suffix": "with 3D text effect, depth and shadows",
"text_params": {"weight": 1.3}
},
"Métallique": {
"prompt_suffix": "with metallic text effect, reflective surface",
"text_params": {"weight": 1.1}
},
"Graffiti": {
"prompt_suffix": "with graffiti style text, urban art typography",
"text_params": {"weight": 1.2}
}
}
# Collections thématiques
THEME_COLLECTIONS = {
"Nature": {
"prompts": ["natural elements", "organic composition"],
"styles": ["flowing lines", "natural textures"],
"negative": "artificial, synthetic"
},
"Urbain": {
"prompts": ["urban landscape", "city elements"],
"styles": ["street art", "architectural elements"],
"negative": "rural, natural"
},
"Tech": {
"prompts": ["technological elements", "digital aesthetic"],
"styles": ["circuit patterns", "tech elements"],
"negative": "organic, traditional"
}
}
# CSS personnalisé
CUSTOM_CSS = """
.container { max-width: 1200px; margin: auto; }
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
.quality-controls { margin: 10px 0; padding: 10px; background: #2d3748; border-radius: 5px; }
.style-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
.text-effects { background: #374151; padding: 12px; border-radius: 5px; margin: 8px 0; }
.preview-panel { position: relative; }
.parameters { display: flex; gap: 10px; }
"""
def enhance_prompt(subject, style, text_effect, collection=None, additional_details=""):
"""Génération de prompt optimisée"""
style_config = ART_STYLES[style]
text_config = TEXT_EFFECTS[text_effect]
base_prompt = f"{style_config['prompt_prefix']}, {subject}"
if text_effect != "Standard":
base_prompt += f", {text_config['prompt_suffix']}"
if collection and collection in THEME_COLLECTIONS:
collection_data = THEME_COLLECTIONS[collection]
collection_elements = collection_data["prompts"] + collection_data["styles"]
base_prompt += f", {', '.join(collection_elements)}"
if additional_details:
base_prompt += f", {additional_details}"
negative_prompt = style_config['negative_prompt']
if collection:
negative_prompt += f", {THEME_COLLECTIONS[collection]['negative']}"
return base_prompt, negative_prompt
def generate_image(format_size, orientation, subject, style, text_effect, collection,
title, subtitle, quality, creativity, additional_details=""):
"""Fonction de génération d'image"""
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
try:
base_width = 1024
base_height = 1024
if orientation == "Portrait":
if format_size in ["A4", "A3"]:
base_width = 768
base_height = 1024
else:
if format_size in ["A4", "A3"]:
base_width = 1024
base_height = 768
enhanced_prompt, negative_prompt = enhance_prompt(
subject, style, text_effect, collection, additional_details
)
if title:
enhanced_prompt += f", with text: '{title}'"
if subtitle:
enhanced_prompt += f", subtitle: '{subtitle}'"
print(f"Prompt: {enhanced_prompt}") # Debug
payload = {
"inputs": enhanced_prompt,
"parameters": {
"negative_prompt": negative_prompt,
"num_inference_steps": int(ART_STYLES[style]['steps'] * (quality/100)),
"guidance_scale": ART_STYLES[style]['guidance'] * (creativity/10),
"width": base_width,
"height": base_height
}
}
response = requests.post(API_URL, headers=headers, json=payload, timeout=60)
if response.status_code == 200:
image = Image.open(io.BytesIO(response.content))
return image, f"✨ Création {style} avec effet {text_effect} réussie!"
else:
print(f"Erreur API: {response.text}")
return None, f"⚠️ Erreur {response.status_code}: Ajustez les paramètres"
except Exception as e:
print(f"Exception: {str(e)}")
return None, f"⚠️ Erreur: {str(e)}"
def create_interface():
with gr.Blocks(css=CUSTOM_CSS) as app:
gr.HTML("""
<div class="welcome">
<h1>🤖 Equity Artisan 3.0</h1>
<p>Créez des affiches artistiques professionnelles avec notre assistant créatif augmenté.</p>
</div>
""")
with gr.Column(elem_classes="container"):
# Format
with gr.Group(elem_classes="style-group"):
gr.Markdown("### 📏 Format et Orientation")
with gr.Row():
format_choice = gr.Dropdown(
choices=["A4", "A3", "A2", "A1", "A0"],
value="A4",
label="Format"
)
orientation = gr.Radio(
choices=["Portrait", "Paysage"],
value="Portrait",
label="Orientation"
)
# Style et Contenu
with gr.Group(elem_classes="style-group"):
gr.Markdown("### 🎨 Style et Création")
with gr.Row():
with gr.Column(scale=1):
style = gr.Dropdown(
choices=list(ART_STYLES.keys()),
value="Neo Vintage",
label="Style artistique"
)
text_effect = gr.Dropdown(
choices=list(TEXT_EFFECTS.keys()),
value="Standard",
label="Effet de texte"
)
collection = gr.Dropdown(
choices=list(THEME_COLLECTIONS.keys()),
label="Collection (optionnel)"
)
with gr.Column(scale=2):
subject = gr.Textbox(
label="Sujet principal",
placeholder="Ex: loup, paysage urbain..."
)
additional_details = gr.Textbox(
lines=2,
label="Détails additionnels",
placeholder="Ajoutez des détails..."
)
# Texte
with gr.Group(elem_classes="text-effects"):
gr.Markdown("### ✍️ Texte")
with gr.Row():
title = gr.Textbox(
label="Titre principal",
placeholder="Texte principal..."
)
subtitle = gr.Textbox(
label="Sous-titre",
placeholder="Texte secondaire..."
)
# Paramètres
with gr.Group(elem_classes="quality-controls"):
with gr.Row():
quality = gr.Slider(
minimum=30,
maximum=50,
value=35,
step=5,
label="Qualité"
)
creativity = gr.Slider(
minimum=5,
maximum=15,
value=7.5,
step=0.5,
label="Créativité"
)
with gr.Row():
generate_btn = gr.Button("✨ Générer", variant="primary")
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
image_output = gr.Image(label="Aperçu", type="pil")
status = gr.Textbox(label="Statut", interactive=False)
# Events
generate_btn.click(
generate_image,
inputs=[
format_choice,
orientation,
subject,
style,
text_effect,
collection,
title,
subtitle,
quality,
creativity,
additional_details
],
outputs=[image_output, status]
)
clear_btn.click(
lambda: (None, "Image effacée"),
outputs=[image_output, status]
)
return app
if __name__ == "__main__":
app = create_interface()
app.launch()