Update app.py
Browse files
app.py
CHANGED
|
@@ -5,9 +5,89 @@ import requests
|
|
| 5 |
import io
|
| 6 |
import json
|
| 7 |
|
| 8 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
CUSTOM_CSS = """
|
| 12 |
.container { max-width: 1200px; margin: auto; }
|
| 13 |
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
|
|
@@ -23,27 +103,19 @@ def enhance_prompt(subject, style, text_effect, collection=None, additional_deta
|
|
| 23 |
style_config = ART_STYLES[style]
|
| 24 |
text_config = TEXT_EFFECTS[text_effect]
|
| 25 |
|
| 26 |
-
# Construction du prompt de base
|
| 27 |
base_prompt = f"{style_config['prompt_prefix']}, {subject}"
|
| 28 |
|
| 29 |
-
# Ajout des effets de texte
|
| 30 |
if text_effect != "Standard":
|
| 31 |
base_prompt += f", {text_config['prompt_suffix']}"
|
| 32 |
|
| 33 |
-
# Ajout des éléments de collection
|
| 34 |
if collection and collection in THEME_COLLECTIONS:
|
| 35 |
collection_data = THEME_COLLECTIONS[collection]
|
| 36 |
-
collection_elements =
|
| 37 |
-
collection_data["prompts"] +
|
| 38 |
-
collection_data["styles"]
|
| 39 |
-
)
|
| 40 |
base_prompt += f", {', '.join(collection_elements)}"
|
| 41 |
|
| 42 |
-
# Ajout des détails supplémentaires
|
| 43 |
if additional_details:
|
| 44 |
base_prompt += f", {additional_details}"
|
| 45 |
|
| 46 |
-
# Construction du prompt négatif
|
| 47 |
negative_prompt = style_config['negative_prompt']
|
| 48 |
if collection:
|
| 49 |
negative_prompt += f", {THEME_COLLECTIONS[collection]['negative']}"
|
|
@@ -52,36 +124,33 @@ def enhance_prompt(subject, style, text_effect, collection=None, additional_deta
|
|
| 52 |
|
| 53 |
def generate_image(format_size, orientation, subject, style, text_effect, collection,
|
| 54 |
title, subtitle, quality, creativity, additional_details=""):
|
| 55 |
-
"""Fonction de génération d'image
|
| 56 |
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
|
| 57 |
headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
|
| 58 |
|
| 59 |
try:
|
| 60 |
-
# Optimisation des dimensions
|
| 61 |
base_width = 1024
|
| 62 |
base_height = 1024
|
| 63 |
-
if
|
| 64 |
-
if
|
| 65 |
base_width = 768
|
| 66 |
base_height = 1024
|
| 67 |
-
|
|
|
|
| 68 |
base_width = 1024
|
| 69 |
base_height = 768
|
| 70 |
-
|
| 71 |
-
# Génération du prompt optimisé
|
| 72 |
enhanced_prompt, negative_prompt = enhance_prompt(
|
| 73 |
subject, style, text_effect, collection, additional_details
|
| 74 |
)
|
| 75 |
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
print(f"Prompt final: {enhanced_prompt}") # Debug
|
| 83 |
|
| 84 |
-
# Configuration de la requête
|
| 85 |
payload = {
|
| 86 |
"inputs": enhanced_prompt,
|
| 87 |
"parameters": {
|
|
@@ -89,8 +158,7 @@ def generate_image(format_size, orientation, subject, style, text_effect, collec
|
|
| 89 |
"num_inference_steps": int(ART_STYLES[style]['steps'] * (quality/100)),
|
| 90 |
"guidance_scale": ART_STYLES[style]['guidance'] * (creativity/10),
|
| 91 |
"width": base_width,
|
| 92 |
-
"height": base_height
|
| 93 |
-
"seed": int(creativity * 1000) # Pour la reproductibilité
|
| 94 |
}
|
| 95 |
}
|
| 96 |
|
|
@@ -100,16 +168,15 @@ def generate_image(format_size, orientation, subject, style, text_effect, collec
|
|
| 100 |
image = Image.open(io.BytesIO(response.content))
|
| 101 |
return image, f"✨ Création {style} avec effet {text_effect} réussie!"
|
| 102 |
else:
|
| 103 |
-
print(f"Erreur API: {response.text}")
|
| 104 |
return None, f"⚠️ Erreur {response.status_code}: Ajustez les paramètres"
|
| 105 |
|
| 106 |
except Exception as e:
|
| 107 |
-
print(f"Exception: {str(e)}")
|
| 108 |
return None, f"⚠️ Erreur: {str(e)}"
|
| 109 |
|
| 110 |
def create_interface():
|
| 111 |
with gr.Blocks(css=CUSTOM_CSS) as app:
|
| 112 |
-
# En-tête
|
| 113 |
gr.HTML("""
|
| 114 |
<div class="welcome">
|
| 115 |
<h1>🤖 Equity Artisan 3.0</h1>
|
|
@@ -118,7 +185,7 @@ def create_interface():
|
|
| 118 |
""")
|
| 119 |
|
| 120 |
with gr.Column(elem_classes="container"):
|
| 121 |
-
#
|
| 122 |
with gr.Group(elem_classes="style-group"):
|
| 123 |
gr.Markdown("### 📏 Format et Orientation")
|
| 124 |
with gr.Row():
|
|
@@ -133,29 +200,26 @@ def create_interface():
|
|
| 133 |
label="Orientation"
|
| 134 |
)
|
| 135 |
|
| 136 |
-
#
|
| 137 |
with gr.Group(elem_classes="style-group"):
|
| 138 |
-
gr.Markdown("### 🎨 Style et
|
| 139 |
with gr.Row():
|
| 140 |
with gr.Column(scale=1):
|
| 141 |
style = gr.Dropdown(
|
| 142 |
choices=list(ART_STYLES.keys()),
|
| 143 |
value="Neo Vintage",
|
| 144 |
-
label="Style artistique"
|
| 145 |
-
info="Choisissez le style visuel"
|
| 146 |
)
|
| 147 |
text_effect = gr.Dropdown(
|
| 148 |
choices=list(TEXT_EFFECTS.keys()),
|
| 149 |
value="Standard",
|
| 150 |
-
label="Effet de texte"
|
| 151 |
-
info="Style du texte"
|
| 152 |
)
|
| 153 |
collection = gr.Dropdown(
|
| 154 |
choices=list(THEME_COLLECTIONS.keys()),
|
| 155 |
-
label="Collection
|
| 156 |
-
info="Thème global"
|
| 157 |
)
|
| 158 |
-
|
| 159 |
with gr.Column(scale=2):
|
| 160 |
subject = gr.Textbox(
|
| 161 |
label="Sujet principal",
|
|
@@ -163,61 +227,49 @@ def create_interface():
|
|
| 163 |
)
|
| 164 |
additional_details = gr.Textbox(
|
| 165 |
lines=2,
|
| 166 |
-
label="Détails additionnels
|
| 167 |
-
placeholder="Ajoutez des détails
|
| 168 |
)
|
| 169 |
|
| 170 |
-
#
|
| 171 |
with gr.Group(elem_classes="text-effects"):
|
| 172 |
-
gr.Markdown("### ✍️ Texte
|
| 173 |
with gr.Row():
|
| 174 |
title = gr.Textbox(
|
| 175 |
label="Titre principal",
|
| 176 |
-
placeholder="Texte principal
|
| 177 |
)
|
| 178 |
subtitle = gr.Textbox(
|
| 179 |
-
label="Sous-titre
|
| 180 |
-
placeholder="Texte secondaire"
|
| 181 |
)
|
| 182 |
|
| 183 |
-
#
|
| 184 |
-
with gr.Group(elem_classes="
|
| 185 |
-
gr.Markdown("### ⚙️ Paramètres")
|
| 186 |
with gr.Row():
|
| 187 |
quality = gr.Slider(
|
| 188 |
minimum=30,
|
| 189 |
maximum=50,
|
| 190 |
value=35,
|
| 191 |
step=5,
|
| 192 |
-
label="Qualité"
|
| 193 |
-
info="Niveau de détail"
|
| 194 |
)
|
| 195 |
creativity = gr.Slider(
|
| 196 |
minimum=5,
|
| 197 |
maximum=15,
|
| 198 |
value=7.5,
|
| 199 |
step=0.5,
|
| 200 |
-
label="Créativité"
|
| 201 |
-
info="Balance entre fidélité et originalité"
|
| 202 |
)
|
| 203 |
|
| 204 |
-
# Boutons de génération
|
| 205 |
with gr.Row():
|
| 206 |
generate_btn = gr.Button("✨ Générer", variant="primary")
|
| 207 |
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
| 208 |
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
type="pil"
|
| 214 |
-
)
|
| 215 |
-
status = gr.Textbox(
|
| 216 |
-
label="Statut",
|
| 217 |
-
interactive=False
|
| 218 |
-
)
|
| 219 |
-
|
| 220 |
-
# Événements
|
| 221 |
generate_btn.click(
|
| 222 |
generate_image,
|
| 223 |
inputs=[
|
|
|
|
| 5 |
import io
|
| 6 |
import json
|
| 7 |
|
| 8 |
+
# Définition des styles
|
| 9 |
+
ART_STYLES = {
|
| 10 |
+
"Renaissance": {
|
| 11 |
+
"prompt_prefix": "renaissance style masterpiece, classical composition, chiaroscuro lighting",
|
| 12 |
+
"text_style": "elegant classical typography, golden ratio composition",
|
| 13 |
+
"guidance": 15.0,
|
| 14 |
+
"steps": 80,
|
| 15 |
+
"negative_prompt": "modern, digital, low quality, blurry"
|
| 16 |
+
},
|
| 17 |
+
"Impressionnisme": {
|
| 18 |
+
"prompt_prefix": "impressionist painting style, vibrant brushstrokes, atmospheric lighting",
|
| 19 |
+
"text_style": "artistic text integration, painterly typography",
|
| 20 |
+
"guidance": 12.0,
|
| 21 |
+
"steps": 65,
|
| 22 |
+
"negative_prompt": "sharp details, digital art, photorealistic"
|
| 23 |
+
},
|
| 24 |
+
"Art Moderne": {
|
| 25 |
+
"prompt_prefix": "modern art style, geometric shapes, bold composition",
|
| 26 |
+
"text_style": "modern typography, geometric letter design",
|
| 27 |
+
"guidance": 11.0,
|
| 28 |
+
"steps": 60,
|
| 29 |
+
"negative_prompt": "traditional, realistic, detailed texture"
|
| 30 |
+
},
|
| 31 |
+
"Neo Vintage": {
|
| 32 |
+
"prompt_prefix": "vintage style poster, retro advertisement design, classic illustration",
|
| 33 |
+
"text_style": "vintage typography, retro lettering style",
|
| 34 |
+
"guidance": 12.5,
|
| 35 |
+
"steps": 65,
|
| 36 |
+
"negative_prompt": "modern, digital, contemporary"
|
| 37 |
+
},
|
| 38 |
+
"Pop Art": {
|
| 39 |
+
"prompt_prefix": "pop art style, comic book aesthetic, bold colors, halftone dots",
|
| 40 |
+
"text_style": "bold comic book typography, retro lettering",
|
| 41 |
+
"guidance": 11.5,
|
| 42 |
+
"steps": 60,
|
| 43 |
+
"negative_prompt": "subtle, realistic, painterly"
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
|
| 47 |
+
# Effets de texte
|
| 48 |
+
TEXT_EFFECTS = {
|
| 49 |
+
"Standard": {
|
| 50 |
+
"prompt_suffix": "with clear readable text",
|
| 51 |
+
"text_params": {"weight": 1.0}
|
| 52 |
+
},
|
| 53 |
+
"Néon": {
|
| 54 |
+
"prompt_suffix": "with glowing neon text effect",
|
| 55 |
+
"text_params": {"weight": 1.2}
|
| 56 |
+
},
|
| 57 |
+
"3D": {
|
| 58 |
+
"prompt_suffix": "with 3D text effect, depth and shadows",
|
| 59 |
+
"text_params": {"weight": 1.3}
|
| 60 |
+
},
|
| 61 |
+
"Métallique": {
|
| 62 |
+
"prompt_suffix": "with metallic text effect, reflective surface",
|
| 63 |
+
"text_params": {"weight": 1.1}
|
| 64 |
+
},
|
| 65 |
+
"Graffiti": {
|
| 66 |
+
"prompt_suffix": "with graffiti style text, urban art typography",
|
| 67 |
+
"text_params": {"weight": 1.2}
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
# Collections thématiques
|
| 72 |
+
THEME_COLLECTIONS = {
|
| 73 |
+
"Nature": {
|
| 74 |
+
"prompts": ["natural elements", "organic composition"],
|
| 75 |
+
"styles": ["flowing lines", "natural textures"],
|
| 76 |
+
"negative": "artificial, synthetic"
|
| 77 |
+
},
|
| 78 |
+
"Urbain": {
|
| 79 |
+
"prompts": ["urban landscape", "city elements"],
|
| 80 |
+
"styles": ["street art", "architectural elements"],
|
| 81 |
+
"negative": "rural, natural"
|
| 82 |
+
},
|
| 83 |
+
"Tech": {
|
| 84 |
+
"prompts": ["technological elements", "digital aesthetic"],
|
| 85 |
+
"styles": ["circuit patterns", "tech elements"],
|
| 86 |
+
"negative": "organic, traditional"
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
# CSS personnalisé
|
| 91 |
CUSTOM_CSS = """
|
| 92 |
.container { max-width: 1200px; margin: auto; }
|
| 93 |
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
|
|
|
|
| 103 |
style_config = ART_STYLES[style]
|
| 104 |
text_config = TEXT_EFFECTS[text_effect]
|
| 105 |
|
|
|
|
| 106 |
base_prompt = f"{style_config['prompt_prefix']}, {subject}"
|
| 107 |
|
|
|
|
| 108 |
if text_effect != "Standard":
|
| 109 |
base_prompt += f", {text_config['prompt_suffix']}"
|
| 110 |
|
|
|
|
| 111 |
if collection and collection in THEME_COLLECTIONS:
|
| 112 |
collection_data = THEME_COLLECTIONS[collection]
|
| 113 |
+
collection_elements = collection_data["prompts"] + collection_data["styles"]
|
|
|
|
|
|
|
|
|
|
| 114 |
base_prompt += f", {', '.join(collection_elements)}"
|
| 115 |
|
|
|
|
| 116 |
if additional_details:
|
| 117 |
base_prompt += f", {additional_details}"
|
| 118 |
|
|
|
|
| 119 |
negative_prompt = style_config['negative_prompt']
|
| 120 |
if collection:
|
| 121 |
negative_prompt += f", {THEME_COLLECTIONS[collection]['negative']}"
|
|
|
|
| 124 |
|
| 125 |
def generate_image(format_size, orientation, subject, style, text_effect, collection,
|
| 126 |
title, subtitle, quality, creativity, additional_details=""):
|
| 127 |
+
"""Fonction de génération d'image"""
|
| 128 |
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
|
| 129 |
headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
|
| 130 |
|
| 131 |
try:
|
|
|
|
| 132 |
base_width = 1024
|
| 133 |
base_height = 1024
|
| 134 |
+
if orientation == "Portrait":
|
| 135 |
+
if format_size in ["A4", "A3"]:
|
| 136 |
base_width = 768
|
| 137 |
base_height = 1024
|
| 138 |
+
else:
|
| 139 |
+
if format_size in ["A4", "A3"]:
|
| 140 |
base_width = 1024
|
| 141 |
base_height = 768
|
| 142 |
+
|
|
|
|
| 143 |
enhanced_prompt, negative_prompt = enhance_prompt(
|
| 144 |
subject, style, text_effect, collection, additional_details
|
| 145 |
)
|
| 146 |
|
| 147 |
+
if title:
|
| 148 |
+
enhanced_prompt += f", with text: '{title}'"
|
| 149 |
+
if subtitle:
|
| 150 |
+
enhanced_prompt += f", subtitle: '{subtitle}'"
|
| 151 |
+
|
| 152 |
+
print(f"Prompt: {enhanced_prompt}") # Debug
|
|
|
|
| 153 |
|
|
|
|
| 154 |
payload = {
|
| 155 |
"inputs": enhanced_prompt,
|
| 156 |
"parameters": {
|
|
|
|
| 158 |
"num_inference_steps": int(ART_STYLES[style]['steps'] * (quality/100)),
|
| 159 |
"guidance_scale": ART_STYLES[style]['guidance'] * (creativity/10),
|
| 160 |
"width": base_width,
|
| 161 |
+
"height": base_height
|
|
|
|
| 162 |
}
|
| 163 |
}
|
| 164 |
|
|
|
|
| 168 |
image = Image.open(io.BytesIO(response.content))
|
| 169 |
return image, f"✨ Création {style} avec effet {text_effect} réussie!"
|
| 170 |
else:
|
| 171 |
+
print(f"Erreur API: {response.text}")
|
| 172 |
return None, f"⚠️ Erreur {response.status_code}: Ajustez les paramètres"
|
| 173 |
|
| 174 |
except Exception as e:
|
| 175 |
+
print(f"Exception: {str(e)}")
|
| 176 |
return None, f"⚠️ Erreur: {str(e)}"
|
| 177 |
|
| 178 |
def create_interface():
|
| 179 |
with gr.Blocks(css=CUSTOM_CSS) as app:
|
|
|
|
| 180 |
gr.HTML("""
|
| 181 |
<div class="welcome">
|
| 182 |
<h1>🤖 Equity Artisan 3.0</h1>
|
|
|
|
| 185 |
""")
|
| 186 |
|
| 187 |
with gr.Column(elem_classes="container"):
|
| 188 |
+
# Format
|
| 189 |
with gr.Group(elem_classes="style-group"):
|
| 190 |
gr.Markdown("### 📏 Format et Orientation")
|
| 191 |
with gr.Row():
|
|
|
|
| 200 |
label="Orientation"
|
| 201 |
)
|
| 202 |
|
| 203 |
+
# Style et Contenu
|
| 204 |
with gr.Group(elem_classes="style-group"):
|
| 205 |
+
gr.Markdown("### 🎨 Style et Création")
|
| 206 |
with gr.Row():
|
| 207 |
with gr.Column(scale=1):
|
| 208 |
style = gr.Dropdown(
|
| 209 |
choices=list(ART_STYLES.keys()),
|
| 210 |
value="Neo Vintage",
|
| 211 |
+
label="Style artistique"
|
|
|
|
| 212 |
)
|
| 213 |
text_effect = gr.Dropdown(
|
| 214 |
choices=list(TEXT_EFFECTS.keys()),
|
| 215 |
value="Standard",
|
| 216 |
+
label="Effet de texte"
|
|
|
|
| 217 |
)
|
| 218 |
collection = gr.Dropdown(
|
| 219 |
choices=list(THEME_COLLECTIONS.keys()),
|
| 220 |
+
label="Collection (optionnel)"
|
|
|
|
| 221 |
)
|
| 222 |
+
|
| 223 |
with gr.Column(scale=2):
|
| 224 |
subject = gr.Textbox(
|
| 225 |
label="Sujet principal",
|
|
|
|
| 227 |
)
|
| 228 |
additional_details = gr.Textbox(
|
| 229 |
lines=2,
|
| 230 |
+
label="Détails additionnels",
|
| 231 |
+
placeholder="Ajoutez des détails..."
|
| 232 |
)
|
| 233 |
|
| 234 |
+
# Texte
|
| 235 |
with gr.Group(elem_classes="text-effects"):
|
| 236 |
+
gr.Markdown("### ✍️ Texte")
|
| 237 |
with gr.Row():
|
| 238 |
title = gr.Textbox(
|
| 239 |
label="Titre principal",
|
| 240 |
+
placeholder="Texte principal..."
|
| 241 |
)
|
| 242 |
subtitle = gr.Textbox(
|
| 243 |
+
label="Sous-titre",
|
| 244 |
+
placeholder="Texte secondaire..."
|
| 245 |
)
|
| 246 |
|
| 247 |
+
# Paramètres
|
| 248 |
+
with gr.Group(elem_classes="quality-controls"):
|
|
|
|
| 249 |
with gr.Row():
|
| 250 |
quality = gr.Slider(
|
| 251 |
minimum=30,
|
| 252 |
maximum=50,
|
| 253 |
value=35,
|
| 254 |
step=5,
|
| 255 |
+
label="Qualité"
|
|
|
|
| 256 |
)
|
| 257 |
creativity = gr.Slider(
|
| 258 |
minimum=5,
|
| 259 |
maximum=15,
|
| 260 |
value=7.5,
|
| 261 |
step=0.5,
|
| 262 |
+
label="Créativité"
|
|
|
|
| 263 |
)
|
| 264 |
|
|
|
|
| 265 |
with gr.Row():
|
| 266 |
generate_btn = gr.Button("✨ Générer", variant="primary")
|
| 267 |
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
| 268 |
|
| 269 |
+
image_output = gr.Image(label="Aperçu", type="pil")
|
| 270 |
+
status = gr.Textbox(label="Statut", interactive=False)
|
| 271 |
+
|
| 272 |
+
# Events
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
generate_btn.click(
|
| 274 |
generate_image,
|
| 275 |
inputs=[
|