Update app.py
Browse files
app.py
CHANGED
@@ -124,120 +124,97 @@ class ImageGenerator:
|
|
124 |
logger.info("ImageGenerator initialisé")
|
125 |
|
126 |
def _build_prompt(self, params: Dict[str, Any]) -> str:
|
|
|
127 |
try:
|
|
|
128 |
enhancer = PromptEnhancer()
|
|
|
|
|
129 |
style_info = ART_STYLES.get(params["style"], ART_STYLES["Art Moderne"])
|
130 |
-
|
131 |
-
# Détection du type de contenu
|
132 |
-
content_type = self._detect_content_type(params["subject"])
|
133 |
-
|
134 |
-
# Adaptation du style selon le contenu
|
135 |
-
style_adjustments = {
|
136 |
-
"anime_manga": {
|
137 |
-
"prompt_prefix": "high quality anime artwork, detailed anime illustration, studio anime quality, professional anime art",
|
138 |
-
"negative_prompt": "low quality, simple, poorly drawn, blurry"
|
139 |
-
},
|
140 |
-
"art_digital": {
|
141 |
-
"prompt_prefix": "professional digital artwork, detailed digital illustration, modern digital art",
|
142 |
-
"negative_prompt": "traditional art, poor quality, amateur"
|
143 |
-
},
|
144 |
-
"art_traditionnel": {
|
145 |
-
"prompt_prefix": "traditional art masterpiece, fine art painting, artistic technique, professional artwork",
|
146 |
-
"negative_prompt": "digital art, 3d render, poor quality"
|
147 |
-
},
|
148 |
-
"photo_realiste": {
|
149 |
-
"prompt_prefix": "ultra realistic photograph, professional photography, high end photo, studio quality",
|
150 |
-
"negative_prompt": "drawing, painting, artificial, digital art"
|
151 |
-
},
|
152 |
-
"graphisme": {
|
153 |
-
"prompt_prefix": "professional graphic design, clean modern design, commercial quality artwork",
|
154 |
-
"negative_prompt": "amateur, messy, unrefined"
|
155 |
-
},
|
156 |
-
"fantastique": {
|
157 |
-
"prompt_prefix": "fantasy art masterpiece, mythical artwork, magical atmosphere, epic fantasy illustration",
|
158 |
-
"negative_prompt": "realistic, mundane, ordinary"
|
159 |
-
},
|
160 |
-
"sci_fi": {
|
161 |
-
"prompt_prefix": "futuristic sci-fi artwork, high tech aesthetic, advanced technology, science fiction art",
|
162 |
-
"negative_prompt": "vintage, retro, traditional"
|
163 |
-
},
|
164 |
-
"art_abstrait": {
|
165 |
-
"prompt_prefix": "abstract art composition, non-representational artwork, artistic expression",
|
166 |
-
"negative_prompt": "realistic, figurative, literal"
|
167 |
-
},
|
168 |
-
"art_pop": {
|
169 |
-
"prompt_prefix": "pop art style, bold colors, graphic art, contemporary pop culture",
|
170 |
-
"negative_prompt": "classical, traditional, subtle"
|
171 |
-
},
|
172 |
-
"art_conceptuel": {
|
173 |
-
"prompt_prefix": "concept art, professional design, detailed visualization, production quality",
|
174 |
-
"negative_prompt": "amateur, unrefined, sketch"
|
175 |
-
}
|
176 |
-
}
|
177 |
|
178 |
-
#
|
179 |
-
if content_type in style_adjustments:
|
180 |
-
style_info = style_adjustments[content_type]
|
181 |
-
|
182 |
-
# Construction du contexte
|
183 |
style_context = {
|
184 |
"prompt_prefix": style_info['prompt_prefix'],
|
185 |
"layout": COMPOSITION_PARAMS['Layouts'][params['layout']],
|
186 |
"ambiance": COMPOSITION_PARAMS['Ambiances'][params['ambiance']],
|
187 |
"palette": COMPOSITION_PARAMS['Palette'][params['palette']]
|
188 |
}
|
189 |
-
|
|
|
190 |
base_prompt = f"{params['subject']}"
|
191 |
if params.get('title'):
|
192 |
base_prompt += f", with text '{params['title']}'"
|
193 |
|
|
|
194 |
enhanced_prompt = enhancer.enhance_prompt(base_prompt, style_context)
|
|
|
|
|
|
|
|
|
|
|
195 |
return enhanced_prompt
|
196 |
|
197 |
except Exception as e:
|
198 |
logger.error(f"Erreur dans la construction du prompt: {str(e)}")
|
|
|
199 |
return f"{style_info['prompt_prefix']}, {params['subject']}"
|
200 |
|
201 |
-
def
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
|
229 |
def create_interface():
|
230 |
logger.info("Création de l'interface Gradio")
|
231 |
-
|
232 |
css = """
|
233 |
.container { max-width: 1200px; margin: auto; }
|
234 |
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
|
235 |
.controls-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
|
236 |
.advanced-controls { background: #374151; padding: 12px; border-radius: 5px; margin: 8px 0; }
|
237 |
"""
|
238 |
-
|
239 |
generator = ImageGenerator()
|
240 |
-
|
241 |
with gr.Blocks(css=css) as app:
|
242 |
gr.HTML("""
|
243 |
<div class="welcome">
|
@@ -245,7 +222,7 @@ def create_interface():
|
|
245 |
<p>Assistant de création d'affiches professionnelles</p>
|
246 |
</div>
|
247 |
""")
|
248 |
-
|
249 |
with gr.Column(elem_classes="container"):
|
250 |
# Zone d'introduction et guide
|
251 |
gr.Markdown("""
|
@@ -272,7 +249,7 @@ def create_interface():
|
|
272 |
label="Orientation",
|
273 |
info="Portrait (vertical) ou Paysage (horizontal)"
|
274 |
)
|
275 |
-
|
276 |
with gr.Group(elem_classes="controls-group"):
|
277 |
gr.Markdown("### 🎨 Style et Composition")
|
278 |
with gr.Row():
|
@@ -302,7 +279,7 @@ def create_interface():
|
|
302 |
label="Palette",
|
303 |
info="Les types de couleurs utilisées"
|
304 |
)
|
305 |
-
|
306 |
with gr.Group(elem_classes="controls-group"):
|
307 |
gr.Markdown("""### 📝 Contenu
|
308 |
*Conseils pour la description : soyez précis sur ce que vous souhaitez voir dans l'affiche*""")
|
@@ -316,7 +293,7 @@ def create_interface():
|
|
316 |
placeholder="Le titre qui apparaîtra sur l'affiche...",
|
317 |
info="Laissez vide si vous ne voulez pas de titre sur l'affiche"
|
318 |
)
|
319 |
-
|
320 |
with gr.Group(elem_classes="advanced-controls"):
|
321 |
gr.Markdown("""### 🎯 Ajustements Fins
|
322 |
*Ces paramètres permettent d'affiner le résultat final*""")
|
@@ -345,7 +322,7 @@ def create_interface():
|
|
345 |
label="Saturation",
|
346 |
info="Contrôle la vivacité des couleurs"
|
347 |
)
|
348 |
-
|
349 |
with gr.Group(elem_classes="controls-group"):
|
350 |
gr.Markdown("### ⚙️ Paramètres de Génération")
|
351 |
with gr.Row():
|
@@ -363,77 +340,35 @@ def create_interface():
|
|
363 |
label="Créativité",
|
364 |
info="Plus la valeur est élevée, plus l'IA prendra de libertés créatives"
|
365 |
)
|
366 |
-
|
367 |
with gr.Row():
|
368 |
generate_btn = gr.Button("✨ Générer", variant="primary")
|
369 |
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
370 |
-
|
371 |
image_output = gr.Image(label="Aperçu")
|
372 |
status = gr.Textbox(label="Statut", interactive=False)
|
373 |
-
|
374 |
-
def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
|
375 |
-
try:
|
376 |
-
logger.info(f"Début de génération avec paramètres: {json.dumps(params, indent=2)}")
|
377 |
-
|
378 |
-
if 'Bearer None' in self.headers['Authorization']:
|
379 |
-
logger.error("Token Hugging Face manquant ou invalide")
|
380 |
-
return None, "⚠️ Erreur: Token Hugging Face non configuré"
|
381 |
|
382 |
-
|
383 |
-
logger.info(
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
"
|
390 |
-
"
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
|
|
397 |
}
|
398 |
-
|
399 |
-
logger.
|
400 |
-
|
401 |
-
try:
|
402 |
-
response = requests.post(
|
403 |
-
self.API_URL,
|
404 |
-
headers=self.headers,
|
405 |
-
json=payload,
|
406 |
-
timeout=30
|
407 |
-
)
|
408 |
-
|
409 |
-
logger.info(f"Statut de la réponse API: {response.status_code}")
|
410 |
-
if response.status_code != 200:
|
411 |
-
logger.error(f"Contenu de la réponse en erreur: {response.text}")
|
412 |
-
|
413 |
-
if response.status_code == 200:
|
414 |
-
image = Image.open(io.BytesIO(response.content))
|
415 |
-
return image, "✨ Création réussie!"
|
416 |
-
else:
|
417 |
-
error_msg = f"⚠️ Erreur API {response.status_code}: {response.text}"
|
418 |
-
logger.error(error_msg)
|
419 |
-
return None, error_msg
|
420 |
-
|
421 |
-
except requests.exceptions.Timeout:
|
422 |
-
error_msg = "⚠️ Erreur: Le serveur met trop de temps à répondre"
|
423 |
-
logger.error(error_msg)
|
424 |
-
return None, error_msg
|
425 |
-
except requests.exceptions.RequestException as e:
|
426 |
-
error_msg = f"⚠️ Erreur de connexion: {str(e)}"
|
427 |
-
logger.error(error_msg)
|
428 |
-
return None, error_msg
|
429 |
|
430 |
-
except Exception as e:
|
431 |
-
error_msg = f"⚠️ Erreur inattendue: {str(e)}"
|
432 |
-
logger.exception("Erreur détaillée pendant la génération:")
|
433 |
-
return None, error_msg
|
434 |
-
finally:
|
435 |
-
gc.collect()
|
436 |
-
|
437 |
generate_btn.click(
|
438 |
generate,
|
439 |
inputs=[
|
@@ -453,16 +388,16 @@ def create_interface():
|
|
453 |
],
|
454 |
outputs=[image_output, status]
|
455 |
)
|
456 |
-
|
457 |
clear_btn.click(
|
458 |
lambda: (None, "🗑️ Image effacée"),
|
459 |
outputs=[image_output, status]
|
460 |
)
|
461 |
-
|
462 |
logger.info("Interface créée avec succès")
|
463 |
return app
|
464 |
|
465 |
if __name__ == "__main__":
|
466 |
app = create_interface()
|
467 |
logger.info("Démarrage de l'application")
|
468 |
-
app.launch()
|
|
|
124 |
logger.info("ImageGenerator initialisé")
|
125 |
|
126 |
def _build_prompt(self, params: Dict[str, Any]) -> str:
|
127 |
+
"""Construction de prompt améliorée avec le PromptEnhancer"""
|
128 |
try:
|
129 |
+
# Initialisation du PromptEnhancer
|
130 |
enhancer = PromptEnhancer()
|
131 |
+
|
132 |
+
# Récupération du style
|
133 |
style_info = ART_STYLES.get(params["style"], ART_STYLES["Art Moderne"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
|
135 |
+
# Construction du contexte de style
|
|
|
|
|
|
|
|
|
136 |
style_context = {
|
137 |
"prompt_prefix": style_info['prompt_prefix'],
|
138 |
"layout": COMPOSITION_PARAMS['Layouts'][params['layout']],
|
139 |
"ambiance": COMPOSITION_PARAMS['Ambiances'][params['ambiance']],
|
140 |
"palette": COMPOSITION_PARAMS['Palette'][params['palette']]
|
141 |
}
|
142 |
+
|
143 |
+
# Préparation du prompt initial
|
144 |
base_prompt = f"{params['subject']}"
|
145 |
if params.get('title'):
|
146 |
base_prompt += f", with text '{params['title']}'"
|
147 |
|
148 |
+
# Amélioration du prompt
|
149 |
enhanced_prompt = enhancer.enhance_prompt(base_prompt, style_context)
|
150 |
+
|
151 |
+
# Analyse de l'efficacité du prompt
|
152 |
+
prompt_analysis = enhancer.analyze_prompt_effectiveness(enhanced_prompt)
|
153 |
+
logger.debug(f"Analyse du prompt: {json.dumps(prompt_analysis, indent=2)}")
|
154 |
+
|
155 |
return enhanced_prompt
|
156 |
|
157 |
except Exception as e:
|
158 |
logger.error(f"Erreur dans la construction du prompt: {str(e)}")
|
159 |
+
# Fallback sur le prompt basique en cas d'erreur
|
160 |
return f"{style_info['prompt_prefix']}, {params['subject']}"
|
161 |
|
162 |
+
def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
|
163 |
+
try:
|
164 |
+
logger.info(f"Début de génération avec paramètres: {json.dumps(params, indent=2)}")
|
165 |
+
|
166 |
+
if 'Bearer None' in self.headers['Authorization']:
|
167 |
+
return None, "⚠️ Erreur: Token Hugging Face non configuré"
|
168 |
+
|
169 |
+
prompt = self._build_prompt(params)
|
170 |
+
|
171 |
+
payload = {
|
172 |
+
"inputs": prompt,
|
173 |
+
"parameters": {
|
174 |
+
"negative_prompt": ART_STYLES[params["style"]]["negative_prompt"],
|
175 |
+
"num_inference_steps": min(int(35 * (params["quality"]/100)), 40),
|
176 |
+
"guidance_scale": min(7.5 * (params["creativity"]/10), 10.0),
|
177 |
+
"width": 768,
|
178 |
+
"height": 768 if params["orientation"] == "Portrait" else 512
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
logger.debug(f"Payload: {json.dumps(payload, indent=2)}")
|
183 |
+
|
184 |
+
response = requests.post(
|
185 |
+
self.API_URL,
|
186 |
+
headers=self.headers,
|
187 |
+
json=payload,
|
188 |
+
timeout=30
|
189 |
+
)
|
190 |
+
|
191 |
+
if response.status_code == 200:
|
192 |
+
image = Image.open(io.BytesIO(response.content))
|
193 |
+
return image, "✨ Création réussie!"
|
194 |
+
else:
|
195 |
+
error_msg = f"⚠️ Erreur API {response.status_code}: {response.text}"
|
196 |
+
logger.error(error_msg)
|
197 |
+
return None, error_msg
|
198 |
+
|
199 |
+
except Exception as e:
|
200 |
+
error_msg = f"⚠️ Erreur: {str(e)}"
|
201 |
+
logger.exception("Erreur pendant la génération:")
|
202 |
+
return None, error_msg
|
203 |
+
finally:
|
204 |
+
gc.collect()
|
205 |
|
206 |
def create_interface():
|
207 |
logger.info("Création de l'interface Gradio")
|
208 |
+
|
209 |
css = """
|
210 |
.container { max-width: 1200px; margin: auto; }
|
211 |
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
|
212 |
.controls-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
|
213 |
.advanced-controls { background: #374151; padding: 12px; border-radius: 5px; margin: 8px 0; }
|
214 |
"""
|
215 |
+
|
216 |
generator = ImageGenerator()
|
217 |
+
|
218 |
with gr.Blocks(css=css) as app:
|
219 |
gr.HTML("""
|
220 |
<div class="welcome">
|
|
|
222 |
<p>Assistant de création d'affiches professionnelles</p>
|
223 |
</div>
|
224 |
""")
|
225 |
+
|
226 |
with gr.Column(elem_classes="container"):
|
227 |
# Zone d'introduction et guide
|
228 |
gr.Markdown("""
|
|
|
249 |
label="Orientation",
|
250 |
info="Portrait (vertical) ou Paysage (horizontal)"
|
251 |
)
|
252 |
+
|
253 |
with gr.Group(elem_classes="controls-group"):
|
254 |
gr.Markdown("### 🎨 Style et Composition")
|
255 |
with gr.Row():
|
|
|
279 |
label="Palette",
|
280 |
info="Les types de couleurs utilisées"
|
281 |
)
|
282 |
+
|
283 |
with gr.Group(elem_classes="controls-group"):
|
284 |
gr.Markdown("""### 📝 Contenu
|
285 |
*Conseils pour la description : soyez précis sur ce que vous souhaitez voir dans l'affiche*""")
|
|
|
293 |
placeholder="Le titre qui apparaîtra sur l'affiche...",
|
294 |
info="Laissez vide si vous ne voulez pas de titre sur l'affiche"
|
295 |
)
|
296 |
+
|
297 |
with gr.Group(elem_classes="advanced-controls"):
|
298 |
gr.Markdown("""### 🎯 Ajustements Fins
|
299 |
*Ces paramètres permettent d'affiner le résultat final*""")
|
|
|
322 |
label="Saturation",
|
323 |
info="Contrôle la vivacité des couleurs"
|
324 |
)
|
325 |
+
|
326 |
with gr.Group(elem_classes="controls-group"):
|
327 |
gr.Markdown("### ⚙️ Paramètres de Génération")
|
328 |
with gr.Row():
|
|
|
340 |
label="Créativité",
|
341 |
info="Plus la valeur est élevée, plus l'IA prendra de libertés créatives"
|
342 |
)
|
343 |
+
|
344 |
with gr.Row():
|
345 |
generate_btn = gr.Button("✨ Générer", variant="primary")
|
346 |
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
347 |
+
|
348 |
image_output = gr.Image(label="Aperçu")
|
349 |
status = gr.Textbox(label="Statut", interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
|
351 |
+
def generate(*args):
|
352 |
+
logger.info("Démarrage d'une nouvelle génération")
|
353 |
+
params = {
|
354 |
+
"format_size": args[0],
|
355 |
+
"orientation": args[1],
|
356 |
+
"style": args[2],
|
357 |
+
"layout": args[3],
|
358 |
+
"ambiance": args[4],
|
359 |
+
"palette": args[5],
|
360 |
+
"subject": args[6],
|
361 |
+
"title": args[7],
|
362 |
+
"detail_level": args[8],
|
363 |
+
"contrast": args[9],
|
364 |
+
"saturation": args[10],
|
365 |
+
"quality": args[11],
|
366 |
+
"creativity": args[12]
|
367 |
}
|
368 |
+
result = generator.generate(params)
|
369 |
+
logger.info(f"Génération terminée avec statut: {result[1]}")
|
370 |
+
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
371 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
372 |
generate_btn.click(
|
373 |
generate,
|
374 |
inputs=[
|
|
|
388 |
],
|
389 |
outputs=[image_output, status]
|
390 |
)
|
391 |
+
|
392 |
clear_btn.click(
|
393 |
lambda: (None, "🗑️ Image effacée"),
|
394 |
outputs=[image_output, status]
|
395 |
)
|
396 |
+
|
397 |
logger.info("Interface créée avec succès")
|
398 |
return app
|
399 |
|
400 |
if __name__ == "__main__":
|
401 |
app = create_interface()
|
402 |
logger.info("Démarrage de l'application")
|
403 |
+
app.launch()
|