Update app.py
Browse files
app.py
CHANGED
@@ -19,119 +19,61 @@ logger = logging.getLogger(__name__)
|
|
19 |
# Chargement des variables d'environnement
|
20 |
load_dotenv()
|
21 |
|
22 |
-
# Styles artistiques
|
23 |
ART_STYLES = {
|
24 |
# Styles Réalistes
|
25 |
"Ultra Réaliste": {
|
26 |
-
"prompt_prefix": "
|
27 |
-
|
28 |
-
masterpiece, perfect composition, award winning photography, professional lighting""",
|
29 |
-
"negative_prompt": """artificial, digital art, illustration, painting, drawing, artistic, cartoon,
|
30 |
-
anime, unreal, fake, low quality, blurry, soft, deformed, noisy, unclear, imperfect, amateur""",
|
31 |
-
"quality_multiplier": 1.2
|
32 |
},
|
33 |
"Photoréaliste": {
|
34 |
-
"prompt_prefix": "
|
35 |
-
|
36 |
-
"negative_prompt": """artistic, painterly, abstract, cartoon, illustration, low quality,
|
37 |
-
amateur, imperfect, blurry, noise""",
|
38 |
-
"quality_multiplier": 1.1
|
39 |
},
|
40 |
-
|
41 |
-
# Styles Artistiques
|
42 |
"Expressionniste": {
|
43 |
-
"prompt_prefix": "
|
44 |
-
|
45 |
-
"negative_prompt": """realistic, subtle, photographic, clean lines, digital art, flat, unemotional,
|
46 |
-
generic, weak composition""",
|
47 |
-
"quality_multiplier": 1.0
|
48 |
},
|
49 |
"Impressionniste": {
|
50 |
-
"prompt_prefix": "
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
"
|
|
|
55 |
},
|
56 |
-
|
57 |
-
# Styles Modernes
|
58 |
"Art Moderne": {
|
59 |
-
"prompt_prefix": "
|
60 |
-
|
61 |
-
"negative_prompt": """traditional, cluttered, busy design, vintage, amateur, low quality,
|
62 |
-
unprofessional""",
|
63 |
-
"quality_multiplier": 1.1
|
64 |
},
|
65 |
"Minimaliste": {
|
66 |
-
"prompt_prefix": "
|
67 |
-
|
68 |
-
"negative_prompt": """complex, detailed, ornate, busy, cluttered, chaotic, unbalanced,
|
69 |
-
amateur""",
|
70 |
-
"quality_multiplier": 1.0
|
71 |
-
},
|
72 |
-
|
73 |
-
# Styles Spéciaux
|
74 |
-
"Cyberpunk": {
|
75 |
-
"prompt_prefix": """cyberpunk style, neon lights, futuristic design, high-tech aesthetic,
|
76 |
-
detailed machinery, holographic elements, cinematic lighting""",
|
77 |
-
"negative_prompt": """vintage, natural, rustic, traditional, simple, flat, dull,
|
78 |
-
low-tech""",
|
79 |
-
"quality_multiplier": 1.1
|
80 |
-
},
|
81 |
-
"Art Déco": {
|
82 |
-
"prompt_prefix": """art deco style, geometric patterns, luxury design, 1920s aesthetic,
|
83 |
-
golden age glamour, sophisticated composition""",
|
84 |
-
"negative_prompt": """modern, minimalist, casual, contemporary, simple, rustic,
|
85 |
-
unrefined""",
|
86 |
-
"quality_multiplier": 1.0
|
87 |
}
|
88 |
}
|
89 |
|
90 |
-
# Paramètres de composition
|
91 |
COMPOSITION_PARAMS = {
|
92 |
"Layouts": {
|
93 |
-
"Centré":
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
"
|
98 |
-
"description": "dynamic asymmetrical composition, creative balance, artistic flow",
|
99 |
-
"weight": 1.1
|
100 |
-
},
|
101 |
-
"Grille": {
|
102 |
-
"description": "grid-based layout, structured composition, organized design, perfect alignment",
|
103 |
-
"weight": 1.0
|
104 |
-
},
|
105 |
-
"Diagonal": {
|
106 |
-
"description": "diagonal dynamic composition, energetic flow, dramatic arrangement",
|
107 |
-
"weight": 1.1
|
108 |
-
},
|
109 |
-
"Minimaliste": {
|
110 |
-
"description": "minimal composition, lots of whitespace, elegant spacing, perfect balance",
|
111 |
-
"weight": 1.0
|
112 |
-
}
|
113 |
},
|
114 |
"Ambiances": {
|
115 |
-
"Dramatique":
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
"
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
"
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
"Mystérieux": {
|
128 |
-
"description": "mysterious atmosphere, moody lighting, enigmatic feel, intriguing shadows",
|
129 |
-
"weight": 1.1
|
130 |
-
},
|
131 |
-
"Serein": {
|
132 |
-
"description": "peaceful atmosphere, calm mood, tranquil setting, harmonious lighting",
|
133 |
-
"weight": 1.0
|
134 |
-
}
|
135 |
}
|
136 |
}
|
137 |
|
@@ -144,87 +86,54 @@ class ImageGenerator:
|
|
144 |
self.headers = {"Authorization": f"Bearer {token}"}
|
145 |
logger.info("ImageGenerator initialisé")
|
146 |
|
147 |
-
def
|
148 |
-
"""Optimisation avancée des prompts avec gestion contextuelle"""
|
149 |
-
style_info = ART_STYLES.get(params["style"], ART_STYLES["Art Moderne"])
|
150 |
-
layout_info = COMPOSITION_PARAMS["Layouts"].get(params["layout"])
|
151 |
-
ambiance_info = COMPOSITION_PARAMS["Ambiances"].get(params["ambiance"])
|
152 |
-
|
153 |
-
# Construction du prompt principal
|
154 |
-
base_prompt = f"{params['subject']}"
|
155 |
-
if params.get('title'):
|
156 |
-
base_prompt += f", with text '{params['title']}'"
|
157 |
-
|
158 |
-
# Ajout des éléments de composition
|
159 |
-
composition_elements = [
|
160 |
-
style_info["prompt_prefix"],
|
161 |
-
layout_info["description"],
|
162 |
-
ambiance_info["description"]
|
163 |
-
]
|
164 |
-
|
165 |
-
# Calcul du multiplicateur de qualité
|
166 |
-
quality_multiplier = (
|
167 |
-
style_info.get("quality_multiplier", 1.0) *
|
168 |
-
layout_info.get("weight", 1.0) *
|
169 |
-
ambiance_info.get("weight", 1.0)
|
170 |
-
)
|
171 |
-
|
172 |
-
# Construction du prompt final optimisé
|
173 |
-
enhanced_prompt = f"{base_prompt}, {', '.join(composition_elements)}"
|
174 |
-
|
175 |
-
# Construction du negative prompt optimisé
|
176 |
-
negative_prompt = f"{style_info['negative_prompt']}, low quality, bad anatomy, worst quality, low resolution"
|
177 |
-
|
178 |
-
return enhanced_prompt, negative_prompt, quality_multiplier
|
179 |
-
|
180 |
-
def _enhance_image(self, image: Image.Image, params: Dict[str, Any]) -> Image.Image:
|
181 |
-
"""Post-traitement avancé des images"""
|
182 |
try:
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
|
193 |
-
return image
|
194 |
except Exception as e:
|
195 |
-
logger.
|
196 |
-
return
|
197 |
|
198 |
def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
|
199 |
-
"""Génération d'image avec optimisations avancées"""
|
200 |
try:
|
201 |
logger.info(f"Début de génération avec paramètres: {json.dumps(params, indent=2)}")
|
202 |
|
203 |
if 'Bearer None' in self.headers['Authorization']:
|
204 |
return None, "⚠️ Erreur: Token Hugging Face non configuré"
|
205 |
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
# Configuration des paramètres de génération
|
210 |
-
generation_params = {
|
211 |
-
"num_inference_steps": min(int(50 * quality_multiplier), 60),
|
212 |
-
"guidance_scale": min(8.5 * (params["creativity"]/10), 12.0),
|
213 |
-
"width": 1024 if params.get("quality", 35) > 40 else 768,
|
214 |
-
"height": 1024 if params["orientation"] == "Portrait" else 768
|
215 |
-
}
|
216 |
|
217 |
payload = {
|
218 |
-
"inputs":
|
219 |
"parameters": {
|
220 |
-
|
221 |
-
"
|
|
|
|
|
|
|
222 |
}
|
223 |
}
|
224 |
|
225 |
-
logger.debug(f"Payload
|
226 |
|
227 |
-
# Génération de l'image
|
228 |
response = requests.post(
|
229 |
self.API_URL,
|
230 |
headers=self.headers,
|
@@ -234,7 +143,7 @@ class ImageGenerator:
|
|
234 |
|
235 |
if response.status_code == 200:
|
236 |
image = Image.open(io.BytesIO(response.content))
|
237 |
-
# Post-traitement
|
238 |
image = self._enhance_image(image, params)
|
239 |
return image, "✨ Création réussie!"
|
240 |
else:
|
@@ -249,50 +158,43 @@ class ImageGenerator:
|
|
249 |
finally:
|
250 |
gc.collect()
|
251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
def create_interface():
|
253 |
-
"""Création de l'interface utilisateur enrichie"""
|
254 |
logger.info("Création de l'interface Gradio")
|
255 |
|
256 |
-
# Styles CSS personnalisés
|
257 |
css = """
|
258 |
.container { max-width: 1200px; margin: auto; }
|
259 |
-
.welcome {
|
260 |
-
|
261 |
-
|
262 |
-
padding: 20px;
|
263 |
-
background: linear-gradient(135deg, #1e293b, #334155);
|
264 |
-
border-radius: 10px;
|
265 |
-
color: white;
|
266 |
-
}
|
267 |
-
.controls-group {
|
268 |
-
background: #2d3748;
|
269 |
-
padding: 15px;
|
270 |
-
border-radius: 5px;
|
271 |
-
margin: 10px 0;
|
272 |
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
273 |
-
}
|
274 |
-
.advanced-controls {
|
275 |
-
background: #374151;
|
276 |
-
padding: 12px;
|
277 |
-
border-radius: 5px;
|
278 |
-
margin: 8px 0;
|
279 |
-
}
|
280 |
-
.info-tooltip {
|
281 |
-
color: #94a3b8;
|
282 |
-
font-size: 0.9em;
|
283 |
-
margin-top: 4px;
|
284 |
-
}
|
285 |
"""
|
286 |
|
287 |
generator = ImageGenerator()
|
288 |
|
289 |
with gr.Blocks(css=css) as app:
|
290 |
-
# En-tête et présentation
|
291 |
gr.HTML("""
|
292 |
<div class="welcome">
|
293 |
<h1>🎨 Equity Artisan 3.0</h1>
|
294 |
<p>Assistant de création d'affiches professionnelles</p>
|
295 |
-
<p style="font-size: 0.9em; opacity: 0.8;">Powered by Stable Diffusion XL</p>
|
296 |
</div>
|
297 |
""")
|
298 |
|
@@ -305,19 +207,16 @@ def create_interface():
|
|
305 |
3. Décrivez votre vision dans "Description"
|
306 |
4. Ajustez les paramètres fins selon vos besoins
|
307 |
5. Cliquez sur "Générer" !
|
308 |
-
|
309 |
-
*💡 Pro Tip: Pour de meilleurs résultats, soyez précis dans votre description et expérimentez avec différents styles.*
|
310 |
""")
|
311 |
|
312 |
-
#
|
313 |
with gr.Group(elem_classes="controls-group"):
|
314 |
gr.Markdown("### 📐 Format et Orientation")
|
315 |
with gr.Row():
|
316 |
format_size = gr.Dropdown(
|
317 |
choices=["A4", "A3", "A2", "A1", "A0"],
|
318 |
value="A4",
|
319 |
-
label="Format"
|
320 |
-
info="Choisissez la taille de votre affiche"
|
321 |
)
|
322 |
orientation = gr.Radio(
|
323 |
choices=["Portrait", "Paysage"],
|
@@ -325,7 +224,7 @@ def create_interface():
|
|
325 |
label="Orientation"
|
326 |
)
|
327 |
|
328 |
-
# Style et
|
329 |
with gr.Group(elem_classes="controls-group"):
|
330 |
gr.Markdown("### 🎨 Style et Composition")
|
331 |
with gr.Row():
|
@@ -347,17 +246,17 @@ def create_interface():
|
|
347 |
label="Ambiance"
|
348 |
)
|
349 |
palette = gr.Dropdown(
|
350 |
-
choices=["
|
351 |
value="Contrasté",
|
352 |
label="Palette"
|
353 |
)
|
354 |
|
355 |
-
#
|
356 |
with gr.Group(elem_classes="controls-group"):
|
357 |
gr.Markdown("### 📝 Contenu")
|
358 |
subject = gr.Textbox(
|
359 |
label="Description",
|
360 |
-
placeholder="Ex: Une affiche moderne pour un festival de musique
|
361 |
lines=3
|
362 |
)
|
363 |
title = gr.Textbox(
|
@@ -366,8 +265,6 @@ def create_interface():
|
|
366 |
)
|
367 |
|
368 |
# Contrôles avancés
|
369 |
-
with gr.Group(elem_classes="advanced-controls"):
|
370 |
-
gr.Markdown("### 🎯 Paramètres Av# Contrôles avancés
|
371 |
with gr.Group(elem_classes="advanced-controls"):
|
372 |
gr.Markdown("### 🎯 Paramètres Avancés")
|
373 |
with gr.Row():
|
@@ -376,24 +273,21 @@ def create_interface():
|
|
376 |
maximum=10,
|
377 |
value=7,
|
378 |
step=1,
|
379 |
-
label="Niveau de Détail"
|
380 |
-
info="Plus la valeur est élevée, plus l'image sera détaillée"
|
381 |
)
|
382 |
contrast = gr.Slider(
|
383 |
minimum=1,
|
384 |
maximum=10,
|
385 |
value=5,
|
386 |
step=1,
|
387 |
-
label="Contraste"
|
388 |
-
info="Influence l'intensité des couleurs et la différence entre les zones claires et sombres"
|
389 |
)
|
390 |
saturation = gr.Slider(
|
391 |
minimum=1,
|
392 |
maximum=10,
|
393 |
value=5,
|
394 |
step=1,
|
395 |
-
label="Saturation"
|
396 |
-
info="Contrôle la vivacité des couleurs"
|
397 |
)
|
398 |
|
399 |
# Paramètres de génération
|
@@ -404,32 +298,22 @@ def create_interface():
|
|
404 |
minimum=30,
|
405 |
maximum=50,
|
406 |
value=35,
|
407 |
-
label="Qualité"
|
408 |
-
info="Influence la qualité finale de l'image. Une valeur plus élevée prend plus de temps"
|
409 |
)
|
410 |
creativity = gr.Slider(
|
411 |
minimum=5,
|
412 |
maximum=15,
|
413 |
value=7.5,
|
414 |
-
label="Créativité"
|
415 |
-
info="Plus la valeur est élevée, plus l'IA prendra de libertés créatives"
|
416 |
)
|
417 |
|
418 |
-
# Boutons de contrôle
|
419 |
with gr.Row():
|
420 |
generate_btn = gr.Button("✨ Générer", variant="primary")
|
421 |
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
422 |
|
423 |
# Zone de résultat
|
424 |
-
|
425 |
-
|
426 |
-
image_output = gr.Image(label="Aperçu", height=512)
|
427 |
-
status = gr.Textbox(label="Statut", interactive=False)
|
428 |
-
|
429 |
-
# Zone d'historique
|
430 |
-
with gr.Group(elem_classes="controls-group"):
|
431 |
-
gr.Markdown("### 📋 Historique des Générations")
|
432 |
-
history = gr.Gallery(label="Générations précédentes", show_label=True, columns=4, height=200)
|
433 |
|
434 |
# Fonction de génération
|
435 |
def generate(*args):
|
@@ -484,5 +368,4 @@ def create_interface():
|
|
484 |
|
485 |
if __name__ == "__main__":
|
486 |
app = create_interface()
|
487 |
-
logger.info("Démarrage de l'application")
|
488 |
app.launch()
|
|
|
19 |
# Chargement des variables d'environnement
|
20 |
load_dotenv()
|
21 |
|
22 |
+
# Styles artistiques complets
|
23 |
ART_STYLES = {
|
24 |
# Styles Réalistes
|
25 |
"Ultra Réaliste": {
|
26 |
+
"prompt_prefix": "ultra realistic photograph, stunning photorealistic quality, unreal engine 5 quality, octane render, ray tracing, volumetric lighting, subsurface scattering, 8k UHD, cinema quality, masterpiece, perfect composition, award winning photography",
|
27 |
+
"negative_prompt": "artificial, digital art, illustration, painting, drawing, artistic, cartoon, anime, unreal, fake, low quality, blurry, soft, deformed"
|
|
|
|
|
|
|
|
|
28 |
},
|
29 |
"Photoréaliste": {
|
30 |
+
"prompt_prefix": "hyperrealistic photograph, extremely detailed, studio quality, professional photography, 8k uhd",
|
31 |
+
"negative_prompt": "artistic, painterly, abstract, cartoon, illustration, low quality"
|
|
|
|
|
|
|
32 |
},
|
|
|
|
|
33 |
"Expressionniste": {
|
34 |
+
"prompt_prefix": "expressive painting style, intense emotional art, bold brushstrokes, vibrant colors, van gogh inspired",
|
35 |
+
"negative_prompt": "realistic, subtle, photographic, clean lines, digital art"
|
|
|
|
|
|
|
36 |
},
|
37 |
"Impressionniste": {
|
38 |
+
"prompt_prefix": "impressionist painting style, soft light, visible brushstrokes, outdoor scene, monet inspired",
|
39 |
+
"negative_prompt": "sharp details, high contrast, digital, modern"
|
40 |
+
},
|
41 |
+
"Art Abstrait": {
|
42 |
+
"prompt_prefix": "abstract art, geometric shapes, non-representational, kandinsky style, pure artistic expression",
|
43 |
+
"negative_prompt": "realistic, figurative, photographic, literal"
|
44 |
},
|
|
|
|
|
45 |
"Art Moderne": {
|
46 |
+
"prompt_prefix": "modern art style poster, professional design, contemporary aesthetic",
|
47 |
+
"negative_prompt": "traditional, cluttered, busy design, vintage"
|
|
|
|
|
|
|
48 |
},
|
49 |
"Minimaliste": {
|
50 |
+
"prompt_prefix": "minimalist design poster, clean composition, elegant simplicity",
|
51 |
+
"negative_prompt": "complex, detailed, ornate, busy, cluttered"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
}
|
53 |
}
|
54 |
|
55 |
+
# Paramètres de composition
|
56 |
COMPOSITION_PARAMS = {
|
57 |
"Layouts": {
|
58 |
+
"Centré": "centered composition, balanced layout, harmonious arrangement",
|
59 |
+
"Asymétrique": "dynamic asymmetrical composition, creative balance",
|
60 |
+
"Grille": "grid-based layout, structured composition, organized design",
|
61 |
+
"Diagonal": "diagonal dynamic composition, energetic flow",
|
62 |
+
"Minimaliste": "minimal composition, lots of whitespace, elegant spacing"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
},
|
64 |
"Ambiances": {
|
65 |
+
"Dramatique": "dramatic lighting, high contrast, intense mood",
|
66 |
+
"Doux": "soft lighting, gentle atmosphere, subtle mood",
|
67 |
+
"Vibrant": "vibrant colors, energetic mood, dynamic atmosphere",
|
68 |
+
"Mystérieux": "mysterious atmosphere, moody lighting, enigmatic feel",
|
69 |
+
"Serein": "peaceful atmosphere, calm mood, tranquil setting"
|
70 |
+
},
|
71 |
+
"Palette": {
|
72 |
+
"Monochrome": "monochromatic color scheme, sophisticated tones",
|
73 |
+
"Contrasté": "high contrast color palette, bold color combinations",
|
74 |
+
"Pastel": "soft pastel color palette, gentle colors",
|
75 |
+
"Terre": "earthy color palette, natural tones",
|
76 |
+
"Néon": "neon color palette, vibrant glowing colors"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
}
|
78 |
}
|
79 |
|
|
|
86 |
self.headers = {"Authorization": f"Bearer {token}"}
|
87 |
logger.info("ImageGenerator initialisé")
|
88 |
|
89 |
+
def _build_prompt(self, params: Dict[str, Any]) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
try:
|
91 |
+
style_info = ART_STYLES.get(params["style"], ART_STYLES["Art Moderne"])
|
92 |
+
|
93 |
+
# Construction du prompt de base
|
94 |
+
base_prompt = f"{params['subject']}"
|
95 |
+
if params.get('title'):
|
96 |
+
base_prompt += f", with text '{params['title']}'"
|
97 |
+
|
98 |
+
# Ajout des éléments de composition
|
99 |
+
composition_elements = [
|
100 |
+
style_info['prompt_prefix'],
|
101 |
+
COMPOSITION_PARAMS['Layouts'][params['layout']],
|
102 |
+
COMPOSITION_PARAMS['Ambiances'][params['ambiance']],
|
103 |
+
COMPOSITION_PARAMS['Palette'][params['palette']]
|
104 |
+
]
|
105 |
+
|
106 |
+
enhanced_prompt = f"{base_prompt}, {', '.join(composition_elements)}"
|
107 |
+
|
108 |
+
return enhanced_prompt
|
109 |
|
|
|
110 |
except Exception as e:
|
111 |
+
logger.error(f"Erreur dans la construction du prompt: {str(e)}")
|
112 |
+
return f"{style_info['prompt_prefix']}, {params['subject']}"
|
113 |
|
114 |
def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
|
|
|
115 |
try:
|
116 |
logger.info(f"Début de génération avec paramètres: {json.dumps(params, indent=2)}")
|
117 |
|
118 |
if 'Bearer None' in self.headers['Authorization']:
|
119 |
return None, "⚠️ Erreur: Token Hugging Face non configuré"
|
120 |
|
121 |
+
prompt = self._build_prompt(params)
|
122 |
+
style_info = ART_STYLES[params["style"]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
|
124 |
payload = {
|
125 |
+
"inputs": prompt,
|
126 |
"parameters": {
|
127 |
+
"negative_prompt": style_info["negative_prompt"],
|
128 |
+
"num_inference_steps": min(int(35 * (params["quality"]/100)), 40),
|
129 |
+
"guidance_scale": min(7.5 * (params["creativity"]/10), 10.0),
|
130 |
+
"width": 1024 if params.get("quality", 35) > 40 else 768,
|
131 |
+
"height": 1024 if params["orientation"] == "Portrait" else 768
|
132 |
}
|
133 |
}
|
134 |
|
135 |
+
logger.debug(f"Payload: {json.dumps(payload, indent=2)}")
|
136 |
|
|
|
137 |
response = requests.post(
|
138 |
self.API_URL,
|
139 |
headers=self.headers,
|
|
|
143 |
|
144 |
if response.status_code == 200:
|
145 |
image = Image.open(io.BytesIO(response.content))
|
146 |
+
# Post-traitement basé sur les paramètres
|
147 |
image = self._enhance_image(image, params)
|
148 |
return image, "✨ Création réussie!"
|
149 |
else:
|
|
|
158 |
finally:
|
159 |
gc.collect()
|
160 |
|
161 |
+
def _enhance_image(self, image: Image.Image, params: Dict[str, Any]) -> Image.Image:
|
162 |
+
"""Applique des améliorations post-génération à l'image"""
|
163 |
+
try:
|
164 |
+
# Ajustement du contraste
|
165 |
+
if params.get("contrast", 5) != 5:
|
166 |
+
enhancer = ImageEnhance.Contrast(image)
|
167 |
+
factor = params["contrast"] / 5
|
168 |
+
image = enhancer.enhance(factor)
|
169 |
+
|
170 |
+
# Ajustement de la saturation
|
171 |
+
if params.get("saturation", 5) != 5:
|
172 |
+
enhancer = ImageEnhance.Color(image)
|
173 |
+
factor = params["saturation"] / 5
|
174 |
+
image = enhancer.enhance(factor)
|
175 |
+
|
176 |
+
return image
|
177 |
+
except Exception as e:
|
178 |
+
logger.warning(f"Erreur lors de l'amélioration de l'image: {e}")
|
179 |
+
return image
|
180 |
+
|
181 |
def create_interface():
|
|
|
182 |
logger.info("Création de l'interface Gradio")
|
183 |
|
|
|
184 |
css = """
|
185 |
.container { max-width: 1200px; margin: auto; }
|
186 |
+
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
|
187 |
+
.controls-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
|
188 |
+
.advanced-controls { background: #374151; padding: 12px; border-radius: 5px; margin: 8px 0; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
"""
|
190 |
|
191 |
generator = ImageGenerator()
|
192 |
|
193 |
with gr.Blocks(css=css) as app:
|
|
|
194 |
gr.HTML("""
|
195 |
<div class="welcome">
|
196 |
<h1>🎨 Equity Artisan 3.0</h1>
|
197 |
<p>Assistant de création d'affiches professionnelles</p>
|
|
|
198 |
</div>
|
199 |
""")
|
200 |
|
|
|
207 |
3. Décrivez votre vision dans "Description"
|
208 |
4. Ajustez les paramètres fins selon vos besoins
|
209 |
5. Cliquez sur "Générer" !
|
|
|
|
|
210 |
""")
|
211 |
|
212 |
+
# Format et Orientation
|
213 |
with gr.Group(elem_classes="controls-group"):
|
214 |
gr.Markdown("### 📐 Format et Orientation")
|
215 |
with gr.Row():
|
216 |
format_size = gr.Dropdown(
|
217 |
choices=["A4", "A3", "A2", "A1", "A0"],
|
218 |
value="A4",
|
219 |
+
label="Format"
|
|
|
220 |
)
|
221 |
orientation = gr.Radio(
|
222 |
choices=["Portrait", "Paysage"],
|
|
|
224 |
label="Orientation"
|
225 |
)
|
226 |
|
227 |
+
# Style et Composition
|
228 |
with gr.Group(elem_classes="controls-group"):
|
229 |
gr.Markdown("### 🎨 Style et Composition")
|
230 |
with gr.Row():
|
|
|
246 |
label="Ambiance"
|
247 |
)
|
248 |
palette = gr.Dropdown(
|
249 |
+
choices=list(COMPOSITION_PARAMS["Palette"].keys()),
|
250 |
value="Contrasté",
|
251 |
label="Palette"
|
252 |
)
|
253 |
|
254 |
+
# Contenu
|
255 |
with gr.Group(elem_classes="controls-group"):
|
256 |
gr.Markdown("### 📝 Contenu")
|
257 |
subject = gr.Textbox(
|
258 |
label="Description",
|
259 |
+
placeholder="Ex: Une affiche moderne pour un festival de musique...",
|
260 |
lines=3
|
261 |
)
|
262 |
title = gr.Textbox(
|
|
|
265 |
)
|
266 |
|
267 |
# Contrôles avancés
|
|
|
|
|
268 |
with gr.Group(elem_classes="advanced-controls"):
|
269 |
gr.Markdown("### 🎯 Paramètres Avancés")
|
270 |
with gr.Row():
|
|
|
273 |
maximum=10,
|
274 |
value=7,
|
275 |
step=1,
|
276 |
+
label="Niveau de Détail"
|
|
|
277 |
)
|
278 |
contrast = gr.Slider(
|
279 |
minimum=1,
|
280 |
maximum=10,
|
281 |
value=5,
|
282 |
step=1,
|
283 |
+
label="Contraste"
|
|
|
284 |
)
|
285 |
saturation = gr.Slider(
|
286 |
minimum=1,
|
287 |
maximum=10,
|
288 |
value=5,
|
289 |
step=1,
|
290 |
+
label="Saturation"
|
|
|
291 |
)
|
292 |
|
293 |
# Paramètres de génération
|
|
|
298 |
minimum=30,
|
299 |
maximum=50,
|
300 |
value=35,
|
301 |
+
label="Qualité"
|
|
|
302 |
)
|
303 |
creativity = gr.Slider(
|
304 |
minimum=5,
|
305 |
maximum=15,
|
306 |
value=7.5,
|
307 |
+
label="Créativité"
|
|
|
308 |
)
|
309 |
|
|
|
310 |
with gr.Row():
|
311 |
generate_btn = gr.Button("✨ Générer", variant="primary")
|
312 |
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
313 |
|
314 |
# Zone de résultat
|
315 |
+
image_output = gr.Image(label="Aperçu")
|
316 |
+
status = gr.Textbox(label="Statut", interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
|
318 |
# Fonction de génération
|
319 |
def generate(*args):
|
|
|
368 |
|
369 |
if __name__ == "__main__":
|
370 |
app = create_interface()
|
|
|
371 |
app.launch()
|