Update app.py
Browse files
app.py
CHANGED
@@ -1,89 +1,85 @@
|
|
1 |
import gradio as gr
|
2 |
import os
|
3 |
-
from PIL import Image, ImageDraw, ImageFont
|
4 |
import requests
|
5 |
import io
|
|
|
6 |
|
7 |
-
#
|
8 |
-
ART_STYLES = {
|
9 |
-
"Neo Vintage": {
|
10 |
-
"prompt_prefix": "vintage propaganda poster style, retro advertisement design",
|
11 |
-
"text_style": "clear vintage typography, screen printing style lettering, clean text layout",
|
12 |
-
"guidance": 12.5,
|
13 |
-
"steps": 70,
|
14 |
-
"negative_prompt": "blurry text, illegible writing, damaged letters, distorted fonts, gibberish"
|
15 |
-
},
|
16 |
-
"Art Déco": {
|
17 |
-
"prompt_prefix": "art deco poster design, 1920s luxury advertisement style",
|
18 |
-
"text_style": "elegant art deco typography, decorative lettering, geometric text design",
|
19 |
-
"guidance": 13.0,
|
20 |
-
"steps": 75,
|
21 |
-
"negative_prompt": "modern fonts, messy text, unclear letters, random characters"
|
22 |
-
},
|
23 |
-
"Moderne": {
|
24 |
-
"prompt_prefix": "modern minimalist poster design, contemporary professional layout",
|
25 |
-
"text_style": "clean sans-serif typography, precise text placement, minimal design",
|
26 |
-
"guidance": 11.5,
|
27 |
-
"steps": 65,
|
28 |
-
"negative_prompt": "cluttered text, unreadable fonts, messy layout, poor typography"
|
29 |
-
},
|
30 |
-
"Pop Art": {
|
31 |
-
"prompt_prefix": "pop art style poster, comic book inspired design",
|
32 |
-
"text_style": "bold comic book typography, dynamic text placement, retro lettering",
|
33 |
-
"guidance": 11.0,
|
34 |
-
"steps": 60,
|
35 |
-
"negative_prompt": "subtle text, unclear typography, messy letters"
|
36 |
-
}
|
37 |
-
}
|
38 |
|
39 |
-
# CSS personnalisé
|
40 |
CUSTOM_CSS = """
|
41 |
.container { max-width: 1200px; margin: auto; }
|
42 |
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
|
43 |
.quality-controls { margin: 10px 0; padding: 10px; background: #2d3748; border-radius: 5px; }
|
44 |
-
.
|
|
|
|
|
|
|
45 |
"""
|
46 |
|
47 |
-
def
|
48 |
-
"""
|
49 |
style_config = ART_STYLES[style]
|
|
|
50 |
|
51 |
# Construction du prompt de base
|
52 |
base_prompt = f"{style_config['prompt_prefix']}, {subject}"
|
53 |
|
54 |
-
# Ajout des
|
55 |
-
|
56 |
-
|
57 |
-
text_elements.append(f"with main title text '{title}'")
|
58 |
-
if subtitle:
|
59 |
-
text_elements.append(f"subtitle text '{subtitle}'")
|
60 |
-
|
61 |
-
text_prompt = ", ".join(text_elements)
|
62 |
-
if text_prompt:
|
63 |
-
base_prompt += f", {text_prompt}, {style_config['text_style']}"
|
64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
if additional_details:
|
66 |
base_prompt += f", {additional_details}"
|
67 |
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
-
def generate_image(format_size, orientation, subject, style,
|
|
|
|
|
71 |
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
|
72 |
headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
|
73 |
|
74 |
try:
|
75 |
-
#
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
# Génération du prompt optimisé
|
81 |
-
enhanced_prompt, negative_prompt =
|
82 |
-
subject, style,
|
83 |
)
|
84 |
|
85 |
-
|
86 |
-
|
|
|
|
|
|
|
|
|
|
|
87 |
|
88 |
# Configuration de la requête
|
89 |
payload = {
|
@@ -92,8 +88,9 @@ def generate_image(format_size, orientation, subject, style, title, subtitle, qu
|
|
92 |
"negative_prompt": negative_prompt,
|
93 |
"num_inference_steps": int(ART_STYLES[style]['steps'] * (quality/100)),
|
94 |
"guidance_scale": ART_STYLES[style]['guidance'] * (creativity/10),
|
95 |
-
"width":
|
96 |
-
"height":
|
|
|
97 |
}
|
98 |
}
|
99 |
|
@@ -101,7 +98,7 @@ def generate_image(format_size, orientation, subject, style, title, subtitle, qu
|
|
101 |
|
102 |
if response.status_code == 200:
|
103 |
image = Image.open(io.BytesIO(response.content))
|
104 |
-
return image, f"✨ Création {style}
|
105 |
else:
|
106 |
print(f"Erreur API: {response.text}") # Debug
|
107 |
return None, f"⚠️ Erreur {response.status_code}: Ajustez les paramètres"
|
@@ -112,17 +109,18 @@ def generate_image(format_size, orientation, subject, style, title, subtitle, qu
|
|
112 |
|
113 |
def create_interface():
|
114 |
with gr.Blocks(css=CUSTOM_CSS) as app:
|
|
|
115 |
gr.HTML("""
|
116 |
<div class="welcome">
|
117 |
<h1>🤖 Equity Artisan 3.0</h1>
|
118 |
-
<p>Créez des affiches professionnelles
|
119 |
</div>
|
120 |
""")
|
121 |
|
122 |
with gr.Column(elem_classes="container"):
|
123 |
# Étape 1: Format
|
124 |
-
with gr.Group():
|
125 |
-
gr.Markdown("### 📏
|
126 |
with gr.Row():
|
127 |
format_choice = gr.Dropdown(
|
128 |
choices=["A4", "A3", "A2", "A1", "A0"],
|
@@ -135,78 +133,89 @@ def create_interface():
|
|
135 |
label="Orientation"
|
136 |
)
|
137 |
|
138 |
-
# Étape 2:
|
139 |
-
with gr.Group():
|
140 |
-
gr.Markdown("### 🎨
|
141 |
with gr.Row():
|
142 |
-
with gr.Column():
|
143 |
style = gr.Dropdown(
|
144 |
choices=list(ART_STYLES.keys()),
|
145 |
value="Neo Vintage",
|
146 |
label="Style artistique",
|
147 |
-
info="Choisissez le style
|
148 |
)
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
subject = gr.Textbox(
|
151 |
label="Sujet principal",
|
152 |
-
placeholder="Ex: loup, paysage urbain..."
|
153 |
-
info="Décrivez l'élément principal"
|
154 |
)
|
155 |
-
|
156 |
-
# Nouvelle section texte
|
157 |
-
with gr.Group(elem_classes="text-input"):
|
158 |
-
gr.Markdown("#### 📝 Texte de l'affiche")
|
159 |
-
title = gr.Textbox(
|
160 |
-
label="Titre principal",
|
161 |
-
placeholder="Texte principal qui apparaîtra sur l'affiche",
|
162 |
-
info="Ce texte sera intégré de manière lisible"
|
163 |
-
)
|
164 |
-
subtitle = gr.Textbox(
|
165 |
-
label="Sous-titre (optionnel)",
|
166 |
-
placeholder="Texte secondaire plus petit",
|
167 |
-
info="Texte complémentaire"
|
168 |
-
)
|
169 |
-
|
170 |
additional_details = gr.Textbox(
|
171 |
lines=2,
|
172 |
label="Détails additionnels (optionnel)",
|
173 |
-
placeholder="Ajoutez des détails
|
174 |
-
info="Plus de détails = meilleur résultat"
|
175 |
)
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
info="Influence le niveau de détail"
|
185 |
-
)
|
186 |
-
|
187 |
-
creativity = gr.Slider(
|
188 |
-
minimum=5,
|
189 |
-
maximum=15,
|
190 |
-
value=7.5,
|
191 |
-
step=0.5,
|
192 |
-
label="Créativité",
|
193 |
-
info="Balance entre fidélité et créativité"
|
194 |
-
)
|
195 |
-
|
196 |
-
generate_btn = gr.Button("✨ Générer", variant="primary")
|
197 |
-
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
198 |
-
|
199 |
-
image_output = gr.Image(
|
200 |
-
label="Aperçu",
|
201 |
-
type="pil"
|
202 |
)
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
|
211 |
# Événements
|
212 |
generate_btn.click(
|
@@ -216,6 +225,8 @@ def create_interface():
|
|
216 |
orientation,
|
217 |
subject,
|
218 |
style,
|
|
|
|
|
219 |
title,
|
220 |
subtitle,
|
221 |
quality,
|
|
|
1 |
import gradio as gr
|
2 |
import os
|
3 |
+
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
|
4 |
import requests
|
5 |
import io
|
6 |
+
import json
|
7 |
|
8 |
+
# [Insérer ici le code des ART_STYLES, TEXT_EFFECTS, et THEME_COLLECTIONS précédents]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
+
# Interface utilisateur améliorée avec CSS personnalisé
|
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; }
|
14 |
.quality-controls { margin: 10px 0; padding: 10px; background: #2d3748; border-radius: 5px; }
|
15 |
+
.style-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
|
16 |
+
.text-effects { background: #374151; padding: 12px; border-radius: 5px; margin: 8px 0; }
|
17 |
+
.preview-panel { position: relative; }
|
18 |
+
.parameters { display: flex; gap: 10px; }
|
19 |
"""
|
20 |
|
21 |
+
def enhance_prompt(subject, style, text_effect, collection=None, additional_details=""):
|
22 |
+
"""Génération de prompt optimisée"""
|
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']}"
|
50 |
+
|
51 |
+
return base_prompt, negative_prompt
|
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 améliorée"""
|
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 format_size in ["A4", "A3"]:
|
64 |
+
if orientation == "Portrait":
|
65 |
+
base_width = 768
|
66 |
+
base_height = 1024
|
67 |
+
else:
|
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 |
+
# Ajout des textes si présents
|
77 |
+
if title or subtitle:
|
78 |
+
enhanced_prompt += f", with text elements: '{title}'"
|
79 |
+
if subtitle:
|
80 |
+
enhanced_prompt += f" and subtitle: '{subtitle}'"
|
81 |
+
|
82 |
+
print(f"Prompt final: {enhanced_prompt}") # Debug
|
83 |
|
84 |
# Configuration de la requête
|
85 |
payload = {
|
|
|
88 |
"negative_prompt": negative_prompt,
|
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 |
|
|
|
98 |
|
99 |
if response.status_code == 200:
|
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}") # Debug
|
104 |
return None, f"⚠️ Erreur {response.status_code}: Ajustez les paramètres"
|
|
|
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>
|
116 |
+
<p>Créez des affiches artistiques professionnelles avec notre assistant créatif augmenté.</p>
|
117 |
</div>
|
118 |
""")
|
119 |
|
120 |
with gr.Column(elem_classes="container"):
|
121 |
# Étape 1: Format
|
122 |
+
with gr.Group(elem_classes="style-group"):
|
123 |
+
gr.Markdown("### 📏 Format et Orientation")
|
124 |
with gr.Row():
|
125 |
format_choice = gr.Dropdown(
|
126 |
choices=["A4", "A3", "A2", "A1", "A0"],
|
|
|
133 |
label="Orientation"
|
134 |
)
|
135 |
|
136 |
+
# Étape 2: Style et Contenu
|
137 |
+
with gr.Group(elem_classes="style-group"):
|
138 |
+
gr.Markdown("### 🎨 Style et Contenu")
|
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 thématique",
|
156 |
+
info="Thème global"
|
157 |
+
)
|
158 |
+
|
159 |
+
with gr.Column(scale=2):
|
160 |
subject = gr.Textbox(
|
161 |
label="Sujet principal",
|
162 |
+
placeholder="Ex: loup, paysage urbain..."
|
|
|
163 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
additional_details = gr.Textbox(
|
165 |
lines=2,
|
166 |
label="Détails additionnels (optionnel)",
|
167 |
+
placeholder="Ajoutez des détails spécifiques..."
|
|
|
168 |
)
|
169 |
+
|
170 |
+
# Étape 3: Texte
|
171 |
+
with gr.Group(elem_classes="text-effects"):
|
172 |
+
gr.Markdown("### ✍️ Texte de l'affiche")
|
173 |
+
with gr.Row():
|
174 |
+
title = gr.Textbox(
|
175 |
+
label="Titre principal",
|
176 |
+
placeholder="Texte principal de l'affiche"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
)
|
178 |
+
subtitle = gr.Textbox(
|
179 |
+
label="Sous-titre (optionnel)",
|
180 |
+
placeholder="Texte secondaire"
|
181 |
+
)
|
182 |
+
|
183 |
+
# Étape 4: Paramètres de génération
|
184 |
+
with gr.Group(elem_classes="style-group"):
|
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 |
+
# Zone de prévisualisation
|
210 |
+
with gr.Group(elem_classes="preview-panel"):
|
211 |
+
image_output = gr.Image(
|
212 |
+
label="Aperçu",
|
213 |
+
type="pil"
|
214 |
+
)
|
215 |
+
status = gr.Textbox(
|
216 |
+
label="Statut",
|
217 |
+
interactive=False
|
218 |
+
)
|
219 |
|
220 |
# Événements
|
221 |
generate_btn.click(
|
|
|
225 |
orientation,
|
226 |
subject,
|
227 |
style,
|
228 |
+
text_effect,
|
229 |
+
collection,
|
230 |
title,
|
231 |
subtitle,
|
232 |
quality,
|