Equityone commited on
Commit
76ec96d
·
verified ·
1 Parent(s): 353e035

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -466
app.py CHANGED
@@ -1,436 +1,121 @@
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
- # Définition des styles
9
- # Styles optimisés et stabilisés
10
- ART_STYLES = {
11
- "Art Moderne": {
12
- "prompt_prefix": "modern art style poster, professional design",
13
- "text_style": "modern clean typography, geometric style",
14
- "guidance": 9.0, # Réduit pour plus de stabilité
15
- "steps": 55,
16
- "negative_prompt": "traditional, photorealistic, cluttered, busy design"
17
- },
18
- "Neo Vintage": {
19
- "prompt_prefix": "vintage style advertising poster, retro design",
20
- "text_style": "retro typography, vintage lettering",
21
- "guidance": 8.5,
22
- "steps": 50,
23
- "negative_prompt": "modern, digital, contemporary style"
24
- },
25
- "Pop Art": {
26
- "prompt_prefix": "pop art style poster, bold design",
27
- "text_style": "bold typography, comic book style text",
28
- "guidance": 8.0,
29
- "steps": 45,
30
- "negative_prompt": "subtle, realistic, traditional art"
31
- },
32
- "Minimaliste": {
33
- "prompt_prefix": "minimalist design poster, clean composition",
34
- "text_style": "clean minimal typography, simple text layout",
35
- "guidance": 7.5,
36
- "steps": 40,
37
- "negative_prompt": "complex, detailed, ornate, busy"
38
- }
39
- }
40
- ART_STYLES = {
41
- # Styles Photoréalistes
42
- "Photo HDR": {
43
- "prompt_prefix": "ultra realistic photograph, professional HDR photography, extremely detailed, 8k uhd",
44
- "text_style": "photographic text overlay",
45
- "guidance": 9.0,
46
- "steps": 60,
47
- "negative_prompt": "illustration, painting, drawing, cartoon, blurry, low quality, artistic"
48
- },
49
- "Portrait Studio": {
50
- "prompt_prefix": "professional studio photography, high end photoshoot, perfect lighting, sharp focus",
51
- "text_style": "elegant text overlay, magazine style typography",
52
- "guidance": 8.5,
53
- "steps": 55,
54
- "negative_prompt": "illustration, drawing, cartoon, painting, low quality"
55
- },
56
- "Nature Pro": {
57
- "prompt_prefix": "professional nature photography, national geographic style, perfect natural lighting",
58
- "text_style": "outdoor photography text style",
59
- "guidance": 8.0,
60
- "steps": 50,
61
- "negative_prompt": "illustration, artificial, cartoon, painting"
62
- },
63
- "Urban Photo": {
64
- "prompt_prefix": "professional urban photography, architectural photo, perfect city shot",
65
- "text_style": "modern photographic typography",
66
- "guidance": 8.5,
67
- "steps": 55,
68
- "negative_prompt": "illustration, drawing, cartoon, unrealistic"
69
- },
70
-
71
- # Styles Artistiques existants
72
- "Art Moderne": {
73
- "prompt_prefix": "modern art style poster, professional design",
74
- "text_style": "modern clean typography, geometric style",
75
- "guidance": 9.0,
76
- "steps": 55,
77
- "negative_prompt": "photorealistic, traditional, cluttered"
78
- },
79
- "Neo Vintage": {
80
- "prompt_prefix": "vintage style advertising poster, retro design",
81
- "text_style": "retro typography, vintage lettering",
82
- "guidance": 8.5,
83
- "steps": 50,
84
- "negative_prompt": "modern, photographic, contemporary"
85
- },
86
- # ... autres styles existants ...
87
- }
88
 
89
- # Paramètres photo optimisés
90
- PHOTO_PARAMS = {
91
- "HDR": {
92
- "exposure_strength": 1.2,
93
- "contrast": 1.1,
94
- "saturation": 1.05
95
- },
96
- "Portrait": {
97
- "skin_softening": 0.8,
98
- "bokeh_strength": 0.7,
99
- "lighting": "studio"
100
- },
101
- "Nature": {
102
- "sharpness": 1.2,
103
- "vibrance": 1.1,
104
- "detail_enhancement": 1.15
105
- }
106
- }
107
 
108
- def enhance_photo_prompt(subject, style, photo_type="HDR"):
109
- """Améliore le prompt pour un rendu plus photoréaliste"""
110
- base_prompt = f"highly detailed photograph of {subject}, {ART_STYLES[style]['prompt_prefix']}"
111
-
112
- photo_enhancements = {
113
- "HDR": ", ultra high resolution photograph, perfect exposure, dramatic lighting, professional color grading",
114
- "Portrait": ", professional portrait photography, perfect skin texture, studio lighting setup, shallow depth of field",
115
- "Nature": ", sharp nature photography, perfect natural lighting, high detail macro shot, professional outdoor photography",
116
- "Urban": ", professional architectural photography, perfect perspective, golden hour lighting, urban landscape"
117
- }
118
 
119
- return base_prompt + photo_enhancements.get(photo_type, photo_enhancements["HDR"])
120
-
121
- def generate_image(format_size, orientation, subject, style, text_effect, collection, title, subtitle, quality, creativity):
122
- API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
123
- headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
124
-
125
- try:
126
- width, height = (768, 1024) if format_size == "A4" else (1024, 1024)
127
- if orientation == "Paysage":
128
- width, height = height, width
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
- # Détection du style photo et optimisation du prompt
131
- is_photo_style = any(photo_style in style for photo_style in ["Photo", "Portrait", "Nature Pro", "Urban Photo"])
132
-
133
- if is_photo_style:
134
- prompt = enhance_photo_prompt(subject, style)
135
- guidance_scale = 8.0 # Optimisé pour le photoréalisme
136
- steps = 55
137
- else:
138
- prompt = f"{ART_STYLES[style]['prompt_prefix']}, {subject}"
139
- guidance_scale = ART_STYLES[style]['guidance']
140
- steps = ART_STYLES[style]['steps']
141
-
142
- if text_effect != "Standard":
143
- prompt += f", {TEXT_EFFECTS[text_effect]['prompt_suffix']}"
144
 
145
- if title:
146
- prompt += f", with text '{title}'"
147
 
148
- payload = {
149
- "inputs": prompt,
150
- "parameters": {
151
- "negative_prompt": ART_STYLES[style]['negative_prompt'],
152
- "num_inference_steps": min(int(steps * (quality/100)), 60),
153
- "guidance_scale": min(guidance_scale * (creativity/10), 10.0),
154
- "width": width,
155
- "height": height
 
 
156
  }
157
- }
158
-
159
- print(f"Prompt: {prompt}") # Debug
160
- print(f"Paramètres: {payload}") # Debug
161
-
162
- response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
163
-
164
- if response.status_code == 200:
165
- image = Image.open(io.BytesIO(response.content))
166
 
167
- # Post-traitement pour styles photo
168
- if is_photo_style:
169
- image = enhance_photo(image, style)
170
-
171
- return image, "✨ Photo créée avec succès!"
172
- else:
173
- return None, f"⚠️ Erreur {response.status_code}: Essayez de modifier les paramètres"
174
 
175
- except Exception as e:
176
- print(f"Exception: {str(e)}")
177
- return None, f"⚠️ Erreur: {str(e)}"
178
-
179
- def enhance_photo(image, style):
180
- """Améliore la photo selon le style"""
181
- try:
182
- enhancer = ImageEnhance.Contrast(image)
183
- image = enhancer.enhance(1.1)
184
-
185
- enhancer = ImageEnhance.Color(image)
186
- image = enhancer.enhance(1.05)
187
-
188
- enhancer = ImageEnhance.Sharpness(image)
189
- image = enhancer.enhance(1.15)
190
-
191
- return image
192
- except:
193
- return image
194
- # Effets de texte stabilisés
195
- TEXT_EFFECTS = {
196
- "Standard": {
197
- "prompt_suffix": "with clear readable text",
198
- "text_params": {"weight": 1.0}
199
- },
200
- "Graffiti": {
201
- "prompt_suffix": "with urban graffiti style text",
202
- "text_params": {"weight": 0.8}
203
- },
204
- "Néon": {
205
- "prompt_suffix": "with glowing neon text",
206
- "text_params": {"weight": 0.9}
207
- },
208
- "3D": {
209
- "prompt_suffix": "with 3D style text",
210
- "text_params": {"weight": 0.85}
211
- }
212
- }
213
-
214
- # Collections optimisées
215
- THEME_COLLECTIONS = {
216
- "Tech": {
217
- "prompts": ["technology inspired", "digital aesthetic"],
218
- "styles": ["modern tech elements", "futuristic design"],
219
- "negative": "organic, rustic, natural"
220
- },
221
- "Nature": {
222
- "prompts": ["natural elements", "organic design"],
223
- "styles": ["flowing shapes", "natural textures"],
224
- "negative": "artificial, geometric"
225
- },
226
- "Urbain": {
227
- "prompts": ["urban style", "city aesthetic"],
228
- "styles": ["street art influence", "urban textures"],
229
- "negative": "rural, natural"
230
- }
231
- }
232
-
233
- def generate_image(format_size, orientation, subject, style, text_effect, collection, title, subtitle, quality, creativity):
234
- API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
235
- headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
236
-
237
- try:
238
- # Dimensions optimisées
239
- width, height = (768, 1024) if format_size == "A4" else (1024, 1024)
240
- if orientation == "Paysage":
241
- width, height = height, width
242
 
243
- # Construction du prompt optimisée
244
- style_config = ART_STYLES[style]
245
- text_config = TEXT_EFFECTS[text_effect]
246
-
247
- prompt = f"{style_config['prompt_prefix']}, {subject}"
248
-
249
- # Ajout des éléments de style et texte de manière plus stable
250
- if text_effect != "Standard":
251
- prompt += f", {text_config['prompt_suffix']}"
252
-
253
- if collection and collection in THEME_COLLECTIONS:
254
- coll = THEME_COLLECTIONS[collection]
255
- prompt += f", {', '.join(coll['prompts'][:1])}" # Limite à un prompt pour stabilité
256
-
257
- if title:
258
- prompt += f", with text '{title}'"
259
-
260
- # Configuration optimisée
261
- payload = {
262
- "inputs": prompt,
263
- "parameters": {
264
- "negative_prompt": style_config['negative_prompt'],
265
- "num_inference_steps": min(style_config['steps'], 50), # Limite pour stabilité
266
- "guidance_scale": min(style_config['guidance'] * (creativity/10), 10.0), # Limite max
267
- "width": width,
268
- "height": height
269
- }
270
- }
271
-
272
- print(f"Prompt: {prompt}") # Debug
273
- print(f"Paramètres: {payload}") # Debug
274
-
275
- response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
276
-
277
- if response.status_code == 200:
278
- image = Image.open(io.BytesIO(response.content))
279
- return image, "✨ Création réussie!"
280
- else:
281
- print(f"Erreur API: {response.text}") # Debug
282
- return None, f"⚠️ Erreur {response.status_code}: Essayez de modifier les paramètres"
283
-
284
- except Exception as e:
285
- print(f"Exception: {str(e)}") # Debug
286
- return None, f"⚠️ Erreur: {str(e)}"
287
-
288
- # Effets de texte
289
- TEXT_EFFECTS = {
290
- "Standard": {
291
- "prompt_suffix": "with clear readable text",
292
- "text_params": {"weight": 1.0}
293
- },
294
- "Néon": {
295
- "prompt_suffix": "with glowing neon text effect",
296
- "text_params": {"weight": 1.2}
297
- },
298
- "3D": {
299
- "prompt_suffix": "with 3D text effect, depth and shadows",
300
- "text_params": {"weight": 1.3}
301
- },
302
- "Métallique": {
303
- "prompt_suffix": "with metallic text effect, reflective surface",
304
- "text_params": {"weight": 1.1}
305
- },
306
- "Graffiti": {
307
- "prompt_suffix": "with graffiti style text, urban art typography",
308
- "text_params": {"weight": 1.2}
309
- }
310
- }
311
-
312
- # Collections thématiques
313
- THEME_COLLECTIONS = {
314
- "Nature": {
315
- "prompts": ["natural elements", "organic composition"],
316
- "styles": ["flowing lines", "natural textures"],
317
- "negative": "artificial, synthetic"
318
- },
319
- "Urbain": {
320
- "prompts": ["urban landscape", "city elements"],
321
- "styles": ["street art", "architectural elements"],
322
- "negative": "rural, natural"
323
- },
324
- "Tech": {
325
- "prompts": ["technological elements", "digital aesthetic"],
326
- "styles": ["circuit patterns", "tech elements"],
327
- "negative": "organic, traditional"
328
- }
329
- }
330
-
331
- # CSS personnalisé
332
- CUSTOM_CSS = """
333
- .container { max-width: 1200px; margin: auto; }
334
- .welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
335
- .quality-controls { margin: 10px 0; padding: 10px; background: #2d3748; border-radius: 5px; }
336
- .style-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
337
- .text-effects { background: #374151; padding: 12px; border-radius: 5px; margin: 8px 0; }
338
- .preview-panel { position: relative; }
339
- .parameters { display: flex; gap: 10px; }
340
- """
341
 
342
- def enhance_prompt(subject, style, text_effect, collection=None, additional_details=""):
343
- """Génération de prompt optimisée"""
344
- style_config = ART_STYLES[style]
345
- text_config = TEXT_EFFECTS[text_effect]
346
-
347
- base_prompt = f"{style_config['prompt_prefix']}, {subject}"
348
-
349
- if text_effect != "Standard":
350
- base_prompt += f", {text_config['prompt_suffix']}"
351
-
352
- if collection and collection in THEME_COLLECTIONS:
353
- collection_data = THEME_COLLECTIONS[collection]
354
- collection_elements = collection_data["prompts"] + collection_data["styles"]
355
- base_prompt += f", {', '.join(collection_elements)}"
356
-
357
- if additional_details:
358
- base_prompt += f", {additional_details}"
359
 
360
- negative_prompt = style_config['negative_prompt']
361
- if collection:
362
- negative_prompt += f", {THEME_COLLECTIONS[collection]['negative']}"
 
 
 
363
 
364
- return base_prompt, negative_prompt
365
-
366
- def generate_image(format_size, orientation, subject, style, text_effect, collection,
367
- title, subtitle, quality, creativity, additional_details=""):
368
- """Fonction de génération d'image"""
369
- API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
370
- headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
371
 
372
- try:
373
- base_width = 1024
374
- base_height = 1024
375
- if orientation == "Portrait":
376
- if format_size in ["A4", "A3"]:
377
- base_width = 768
378
- base_height = 1024
379
- else:
380
- if format_size in ["A4", "A3"]:
381
- base_width = 1024
382
- base_height = 768
383
-
384
- enhanced_prompt, negative_prompt = enhance_prompt(
385
- subject, style, text_effect, collection, additional_details
386
- )
387
-
388
- if title:
389
- enhanced_prompt += f", with text: '{title}'"
390
- if subtitle:
391
- enhanced_prompt += f", subtitle: '{subtitle}'"
392
-
393
- print(f"Prompt: {enhanced_prompt}") # Debug
394
-
395
- payload = {
396
- "inputs": enhanced_prompt,
397
- "parameters": {
398
- "negative_prompt": negative_prompt,
399
- "num_inference_steps": int(ART_STYLES[style]['steps'] * (quality/100)),
400
- "guidance_scale": ART_STYLES[style]['guidance'] * (creativity/10),
401
- "width": base_width,
402
- "height": base_height
403
- }
404
- }
405
-
406
- response = requests.post(API_URL, headers=headers, json=payload, timeout=60)
407
-
408
- if response.status_code == 200:
409
- image = Image.open(io.BytesIO(response.content))
410
- return image, f"✨ Création {style} avec effet {text_effect} réussie!"
411
- else:
412
- print(f"Erreur API: {response.text}")
413
- return None, f"⚠️ Erreur {response.status_code}: Ajustez les paramètres"
414
-
415
- except Exception as e:
416
- print(f"Exception: {str(e)}")
417
- return None, f"⚠️ Erreur: {str(e)}"
418
-
419
- def create_interface():
420
- with gr.Blocks(css=CUSTOM_CSS) as app:
421
  gr.HTML("""
422
  <div class="welcome">
423
- <h1>🤖 Equity Artisan 3.0</h1>
424
- <p>Créez des affiches artistiques professionnelles avec notre assistant créatif augmenté.</p>
425
  </div>
426
  """)
427
 
 
428
  with gr.Column(elem_classes="container"):
429
- # Format
430
- with gr.Group(elem_classes="style-group"):
431
- gr.Markdown("### 📏 Format et Orientation")
432
  with gr.Row():
433
- format_choice = gr.Dropdown(
434
  choices=["A4", "A3", "A2", "A1", "A0"],
435
  value="A4",
436
  label="Format"
@@ -440,100 +125,91 @@ def create_interface():
440
  value="Portrait",
441
  label="Orientation"
442
  )
443
-
444
- # Style et Contenu
445
- with gr.Group(elem_classes="style-group"):
446
- gr.Markdown("### 🎨 Style et Création")
447
- with gr.Row():
448
- with gr.Column(scale=1):
449
- style = gr.Dropdown(
450
- choices=list(ART_STYLES.keys()),
451
- value="Neo Vintage",
452
- label="Style artistique"
453
- )
454
- text_effect = gr.Dropdown(
455
- choices=list(TEXT_EFFECTS.keys()),
456
- value="Standard",
457
- label="Effet de texte"
458
- )
459
- collection = gr.Dropdown(
460
- choices=list(THEME_COLLECTIONS.keys()),
461
- label="Collection (optionnel)"
462
- )
463
-
464
- with gr.Column(scale=2):
465
- subject = gr.Textbox(
466
- label="Sujet principal",
467
- placeholder="Ex: loup, paysage urbain..."
468
- )
469
- additional_details = gr.Textbox(
470
- lines=2,
471
- label="Détails additionnels",
472
- placeholder="Ajoutez des détails..."
473
- )
474
-
475
- # Texte
476
- with gr.Group(elem_classes="text-effects"):
477
- gr.Markdown("### ✍️ Texte")
478
  with gr.Row():
479
- title = gr.Textbox(
480
- label="Titre principal",
481
- placeholder="Texte principal..."
 
482
  )
483
- subtitle = gr.Textbox(
484
- label="Sous-titre",
485
- placeholder="Texte secondaire..."
 
486
  )
487
-
488
- # Paramètres
489
- with gr.Group(elem_classes="quality-controls"):
 
 
 
 
 
 
 
 
 
 
 
490
  with gr.Row():
491
  quality = gr.Slider(
492
  minimum=30,
493
  maximum=50,
494
  value=35,
495
- step=5,
496
  label="Qualité"
497
  )
498
  creativity = gr.Slider(
499
  minimum=5,
500
  maximum=15,
501
  value=7.5,
502
- step=0.5,
503
  label="Créativité"
504
  )
505
-
 
506
  with gr.Row():
507
  generate_btn = gr.Button("✨ Générer", variant="primary")
508
  clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
509
-
510
- image_output = gr.Image(label="Aperçu", type="pil")
 
511
  status = gr.Textbox(label="Statut", interactive=False)
512
-
513
- # Events
 
 
 
 
 
 
 
 
 
 
 
 
 
514
  generate_btn.click(
515
- generate_image,
516
  inputs=[
517
- format_choice,
518
  orientation,
519
  subject,
520
  style,
521
  text_effect,
522
- collection,
523
  title,
524
- subtitle,
525
  quality,
526
- creativity,
527
- additional_details
528
  ],
529
  outputs=[image_output, status]
530
  )
531
 
532
  clear_btn.click(
533
- lambda: (None, "Image effacée"),
534
  outputs=[image_output, status]
535
  )
536
-
537
  return app
538
 
539
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  import os
3
+ from PIL import Image
4
  import requests
5
  import io
6
+ import gc
7
+ from typing import Tuple, Optional, Dict, Any
8
+ import logging
9
+ from dotenv import load_dotenv
10
 
11
+ # Configuration du logging
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ # Chargement des variables d'environnement
16
+ load_dotenv()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ class ImageGenerator:
19
+ """Gestionnaire de génération d'images optimisé pour les ressources limitées"""
 
 
 
 
 
 
 
 
20
 
21
+ def __init__(self):
22
+ self.API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
23
+ self.headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
24
+
25
+ # Configurations optimisées pour les ressources limitées
26
+ self.MAX_IMAGE_SIZE = 768 # Limite la taille maximale pour la mémoire
27
+ self.MAX_STEPS = 40 # Limite les steps pour optimiser le temps
28
+ self.DEFAULT_TIMEOUT = 30
29
+
30
+ @staticmethod
31
+ def get_image_dimensions(format_size: str, orientation: str) -> Tuple[int, int]:
32
+ """Calcule les dimensions optimisées selon le format"""
33
+ base_dimensions = {
34
+ "A4": (768, 1024),
35
+ "A3": (768, 1024),
36
+ "A2": (768, 1024),
37
+ "A1": (768, 1024),
38
+ "A0": (768, 1024)
39
+ }
40
+ width, height = base_dimensions.get(format_size, (768, 768))
41
+ return (height, width) if orientation == "Paysage" else (width, height)
42
+
43
+ def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
44
+ """Génère une image avec gestion optimisée des ressources"""
45
+ try:
46
+ # Nettoyage mémoire préventif
47
+ gc.collect()
48
 
49
+ # Préparation des dimensions
50
+ width, height = self.get_image_dimensions(params["format_size"], params["orientation"])
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ # Construction du prompt
53
+ prompt = self._build_prompt(params)
54
 
55
+ # Configuration de la requête
56
+ payload = {
57
+ "inputs": prompt,
58
+ "parameters": {
59
+ "negative_prompt": self._get_negative_prompt(params["style"]),
60
+ "num_inference_steps": min(int(35 * (params["quality"]/100)), self.MAX_STEPS),
61
+ "guidance_scale": min(7.5 * (params["creativity"]/10), 10.0),
62
+ "width": width,
63
+ "height": height
64
+ }
65
  }
 
 
 
 
 
 
 
 
 
66
 
67
+ logger.info(f"Génération avec prompt: {prompt}")
 
 
 
 
 
 
68
 
69
+ # Requête API avec gestion d'erreur
70
+ response = requests.post(
71
+ self.API_URL,
72
+ headers=self.headers,
73
+ json=payload,
74
+ timeout=self.DEFAULT_TIMEOUT
75
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ if response.status_code == 200:
78
+ image = Image.open(io.BytesIO(response.content))
79
+ return image, "✨ Création réussie!"
80
+ else:
81
+ logger.error(f"Erreur API: {response.status_code} - {response.text}")
82
+ return None, f"⚠️ Erreur {response.status_code}: Ajustez les paramètres"
83
+
84
+ except Exception as e:
85
+ logger.error(f"Erreur de génération: {str(e)}")
86
+ return None, f"⚠️ Erreur: {str(e)}"
87
+ finally:
88
+ gc.collect() # Nettoyage final
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ def create_interface():
91
+ """Crée l'interface utilisateur Gradio"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
+ # CSS optimisé
94
+ css = """
95
+ .container { max-width: 1200px; margin: auto; }
96
+ .welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
97
+ .controls-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
98
+ """
99
 
100
+ # Initialisation du générateur
101
+ generator = ImageGenerator()
 
 
 
 
 
102
 
103
+ # Interface Gradio optimisée
104
+ with gr.Blocks(css=css) as app:
105
+ # En-tête
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  gr.HTML("""
107
  <div class="welcome">
108
+ <h1>🎨 Equity Artisan 3.0</h1>
109
+ <p>Assistant de création d'affiches professionnelles</p>
110
  </div>
111
  """)
112
 
113
+ # Conteneur principal
114
  with gr.Column(elem_classes="container"):
115
+ # Groupe Format
116
+ with gr.Group(elem_classes="controls-group"):
 
117
  with gr.Row():
118
+ format_size = gr.Dropdown(
119
  choices=["A4", "A3", "A2", "A1", "A0"],
120
  value="A4",
121
  label="Format"
 
125
  value="Portrait",
126
  label="Orientation"
127
  )
128
+
129
+ # Groupe Style
130
+ with gr.Group(elem_classes="controls-group"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  with gr.Row():
132
+ style = gr.Dropdown(
133
+ choices=["Art Moderne", "Neo Vintage", "Pop Art", "Minimaliste"],
134
+ value="Neo Vintage",
135
+ label="Style artistique"
136
  )
137
+ text_effect = gr.Dropdown(
138
+ choices=["Standard", "Néon", "3D", "Métallique", "Graffiti"],
139
+ value="Standard",
140
+ label="Effet de texte"
141
  )
142
+
143
+ # Groupe Contenu
144
+ with gr.Group(elem_classes="controls-group"):
145
+ subject = gr.Textbox(
146
+ label="Description",
147
+ placeholder="Décrivez votre vision..."
148
+ )
149
+ title = gr.Textbox(
150
+ label="Titre",
151
+ placeholder="Titre de l'affiche..."
152
+ )
153
+
154
+ # Groupe Qualité
155
+ with gr.Group(elem_classes="controls-group"):
156
  with gr.Row():
157
  quality = gr.Slider(
158
  minimum=30,
159
  maximum=50,
160
  value=35,
 
161
  label="Qualité"
162
  )
163
  creativity = gr.Slider(
164
  minimum=5,
165
  maximum=15,
166
  value=7.5,
 
167
  label="Créativité"
168
  )
169
+
170
+ # Boutons
171
  with gr.Row():
172
  generate_btn = gr.Button("✨ Générer", variant="primary")
173
  clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
174
+
175
+ # Sortie
176
+ image_output = gr.Image(label="Aperçu")
177
  status = gr.Textbox(label="Statut", interactive=False)
178
+
179
+ # Événements
180
+ def generate(*args):
181
+ params = {
182
+ "format_size": args[0],
183
+ "orientation": args[1],
184
+ "subject": args[2],
185
+ "style": args[3],
186
+ "text_effect": args[4],
187
+ "title": args[5],
188
+ "quality": args[6],
189
+ "creativity": args[7]
190
+ }
191
+ return generator.generate(params)
192
+
193
  generate_btn.click(
194
+ generate,
195
  inputs=[
196
+ format_size,
197
  orientation,
198
  subject,
199
  style,
200
  text_effect,
 
201
  title,
 
202
  quality,
203
+ creativity
 
204
  ],
205
  outputs=[image_output, status]
206
  )
207
 
208
  clear_btn.click(
209
+ lambda: (None, "🗑️ Image effacée"),
210
  outputs=[image_output, status]
211
  )
212
+
213
  return app
214
 
215
  if __name__ == "__main__":