Update app.py
Browse files
app.py
CHANGED
@@ -4,105 +4,127 @@ 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(
|
|
|
|
|
|
|
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 |
-
|
24 |
-
|
25 |
-
|
26 |
-
self.
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
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 |
-
#
|
47 |
-
|
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)),
|
61 |
"guidance_scale": min(7.5 * (params["creativity"]/10), 10.0),
|
62 |
-
"width":
|
63 |
-
"height":
|
64 |
}
|
65 |
}
|
66 |
|
67 |
-
logger.
|
68 |
-
|
69 |
-
# Requête API
|
|
|
70 |
response = requests.post(
|
71 |
self.API_URL,
|
72 |
headers=self.headers,
|
73 |
json=payload,
|
74 |
-
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 |
-
|
82 |
-
|
83 |
-
|
|
|
84 |
except Exception as e:
|
85 |
-
|
86 |
-
|
87 |
-
|
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>
|
@@ -110,9 +132,7 @@ def create_interface():
|
|
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(
|
@@ -126,21 +146,13 @@ def create_interface():
|
|
126 |
label="Orientation"
|
127 |
)
|
128 |
|
129 |
-
# Groupe Style
|
130 |
with gr.Group(elem_classes="controls-group"):
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
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",
|
@@ -151,7 +163,6 @@ def create_interface():
|
|
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(
|
@@ -167,37 +178,35 @@ def create_interface():
|
|
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 |
-
"
|
185 |
-
"
|
186 |
-
"
|
187 |
-
"
|
188 |
-
"
|
189 |
-
"creativity": args[7]
|
190 |
}
|
191 |
-
|
|
|
|
|
192 |
|
193 |
generate_btn.click(
|
194 |
generate,
|
195 |
inputs=[
|
196 |
format_size,
|
197 |
orientation,
|
198 |
-
subject,
|
199 |
style,
|
200 |
-
|
201 |
title,
|
202 |
quality,
|
203 |
creativity
|
@@ -210,8 +219,10 @@ def create_interface():
|
|
210 |
outputs=[image_output, status]
|
211 |
)
|
212 |
|
|
|
213 |
return app
|
214 |
|
215 |
if __name__ == "__main__":
|
216 |
app = create_interface()
|
|
|
217 |
app.launch()
|
|
|
4 |
import requests
|
5 |
import io
|
6 |
import gc
|
7 |
+
import json
|
8 |
from typing import Tuple, Optional, Dict, Any
|
9 |
import logging
|
10 |
from dotenv import load_dotenv
|
11 |
|
12 |
+
# Configuration du logging détaillé
|
13 |
+
logging.basicConfig(
|
14 |
+
level=logging.DEBUG,
|
15 |
+
format='%(asctime)s - %(levelname)s - %(message)s'
|
16 |
+
)
|
17 |
logger = logging.getLogger(__name__)
|
18 |
|
19 |
# Chargement des variables d'environnement
|
20 |
load_dotenv()
|
21 |
|
22 |
+
# Styles pour la génération
|
23 |
+
ART_STYLES = {
|
24 |
+
"Art Moderne": {
|
25 |
+
"prompt_prefix": "modern art style poster, professional design",
|
26 |
+
"negative_prompt": "traditional, photorealistic, cluttered, busy design"
|
27 |
+
},
|
28 |
+
"Neo Vintage": {
|
29 |
+
"prompt_prefix": "vintage style advertising poster, retro design",
|
30 |
+
"negative_prompt": "modern, digital, contemporary style"
|
31 |
+
},
|
32 |
+
"Pop Art": {
|
33 |
+
"prompt_prefix": "pop art style poster, bold design",
|
34 |
+
"negative_prompt": "subtle, realistic, traditional art"
|
35 |
+
},
|
36 |
+
"Minimaliste": {
|
37 |
+
"prompt_prefix": "minimalist design poster, clean composition",
|
38 |
+
"negative_prompt": "complex, detailed, ornate, busy"
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
class ImageGenerator:
|
|
|
|
|
43 |
def __init__(self):
|
44 |
self.API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
|
45 |
+
token = os.getenv('HUGGINGFACE_TOKEN')
|
46 |
+
if not token:
|
47 |
+
logger.error("HUGGINGFACE_TOKEN non trouvé dans les variables d'environnement!")
|
48 |
+
self.headers = {"Authorization": f"Bearer {token}"}
|
49 |
+
logger.info("ImageGenerator initialisé")
|
50 |
+
|
51 |
+
def _build_prompt(self, params: Dict[str, Any]) -> str:
|
52 |
+
style_info = ART_STYLES.get(params["style"], ART_STYLES["Neo Vintage"])
|
53 |
+
prompt = f"{style_info['prompt_prefix']}, {params['subject']}"
|
54 |
+
if params.get("title"):
|
55 |
+
prompt += f", with text saying '{params['title']}'"
|
56 |
+
logger.debug(f"Prompt construit: {prompt}")
|
57 |
+
return prompt
|
58 |
+
|
59 |
+
def _get_negative_prompt(self, style: str) -> str:
|
60 |
+
return ART_STYLES.get(style, ART_STYLES["Neo Vintage"])["negative_prompt"]
|
|
|
|
|
|
|
61 |
|
62 |
def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
|
|
|
63 |
try:
|
64 |
+
# Log des paramètres
|
65 |
+
logger.info(f"Début de génération avec paramètres: {json.dumps(params, indent=2)}")
|
|
|
|
|
|
|
66 |
|
67 |
+
# Vérification du token
|
68 |
+
if 'Bearer None' in self.headers['Authorization']:
|
69 |
+
return None, "⚠️ Erreur: Token Hugging Face non configuré"
|
70 |
+
|
71 |
# Construction du prompt
|
72 |
prompt = self._build_prompt(params)
|
73 |
+
logger.debug(f"Prompt final: {prompt}")
|
74 |
+
|
75 |
# Configuration de la requête
|
76 |
payload = {
|
77 |
"inputs": prompt,
|
78 |
"parameters": {
|
79 |
"negative_prompt": self._get_negative_prompt(params["style"]),
|
80 |
+
"num_inference_steps": min(int(35 * (params["quality"]/100)), 40),
|
81 |
"guidance_scale": min(7.5 * (params["creativity"]/10), 10.0),
|
82 |
+
"width": 768,
|
83 |
+
"height": 768 if params["orientation"] == "Portrait" else 512
|
84 |
}
|
85 |
}
|
86 |
|
87 |
+
logger.debug(f"Payload de la requête: {json.dumps(payload, indent=2)}")
|
88 |
+
|
89 |
+
# Requête API
|
90 |
+
logger.info("Envoi de la requête à l'API...")
|
91 |
response = requests.post(
|
92 |
self.API_URL,
|
93 |
headers=self.headers,
|
94 |
json=payload,
|
95 |
+
timeout=30
|
96 |
)
|
97 |
|
98 |
+
logger.debug(f"Statut de la réponse: {response.status_code}")
|
99 |
+
logger.debug(f"Contenu de la réponse: {response.text[:200]}...") # Premiers 200 caractères
|
100 |
+
|
101 |
if response.status_code == 200:
|
102 |
image = Image.open(io.BytesIO(response.content))
|
103 |
+
logger.info("Image générée avec succès")
|
104 |
return image, "✨ Création réussie!"
|
105 |
else:
|
106 |
+
error_msg = f"⚠️ Erreur API {response.status_code}: {response.text}"
|
107 |
+
logger.error(error_msg)
|
108 |
+
return None, error_msg
|
109 |
+
|
110 |
except Exception as e:
|
111 |
+
error_msg = f"⚠️ Erreur: {str(e)}"
|
112 |
+
logger.exception("Erreur pendant la génération:")
|
113 |
+
return None, error_msg
|
|
|
114 |
|
115 |
def create_interface():
|
116 |
+
"""Crée l'interface utilisateur Gradio avec logging"""
|
117 |
+
logger.info("Création de l'interface Gradio")
|
118 |
|
|
|
119 |
css = """
|
120 |
.container { max-width: 1200px; margin: auto; }
|
121 |
.welcome { text-align: center; margin: 20px 0; padding: 20px; background: #1e293b; border-radius: 10px; }
|
122 |
.controls-group { background: #2d3748; padding: 15px; border-radius: 5px; margin: 10px 0; }
|
123 |
"""
|
124 |
|
|
|
125 |
generator = ImageGenerator()
|
126 |
|
|
|
127 |
with gr.Blocks(css=css) as app:
|
|
|
128 |
gr.HTML("""
|
129 |
<div class="welcome">
|
130 |
<h1>🎨 Equity Artisan 3.0</h1>
|
|
|
132 |
</div>
|
133 |
""")
|
134 |
|
|
|
135 |
with gr.Column(elem_classes="container"):
|
|
|
136 |
with gr.Group(elem_classes="controls-group"):
|
137 |
with gr.Row():
|
138 |
format_size = gr.Dropdown(
|
|
|
146 |
label="Orientation"
|
147 |
)
|
148 |
|
|
|
149 |
with gr.Group(elem_classes="controls-group"):
|
150 |
+
style = gr.Dropdown(
|
151 |
+
choices=list(ART_STYLES.keys()),
|
152 |
+
value="Neo Vintage",
|
153 |
+
label="Style artistique"
|
154 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
|
|
|
156 |
with gr.Group(elem_classes="controls-group"):
|
157 |
subject = gr.Textbox(
|
158 |
label="Description",
|
|
|
163 |
placeholder="Titre de l'affiche..."
|
164 |
)
|
165 |
|
|
|
166 |
with gr.Group(elem_classes="controls-group"):
|
167 |
with gr.Row():
|
168 |
quality = gr.Slider(
|
|
|
178 |
label="Créativité"
|
179 |
)
|
180 |
|
|
|
181 |
with gr.Row():
|
182 |
generate_btn = gr.Button("✨ Générer", variant="primary")
|
183 |
clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
|
184 |
|
|
|
185 |
image_output = gr.Image(label="Aperçu")
|
186 |
status = gr.Textbox(label="Statut", interactive=False)
|
187 |
|
|
|
188 |
def generate(*args):
|
189 |
+
logger.info("Démarrage d'une nouvelle génération")
|
190 |
params = {
|
191 |
"format_size": args[0],
|
192 |
"orientation": args[1],
|
193 |
+
"style": args[2],
|
194 |
+
"subject": args[3],
|
195 |
+
"title": args[4],
|
196 |
+
"quality": args[5],
|
197 |
+
"creativity": args[6]
|
|
|
198 |
}
|
199 |
+
result = generator.generate(params)
|
200 |
+
logger.info(f"Génération terminée avec statut: {result[1]}")
|
201 |
+
return result
|
202 |
|
203 |
generate_btn.click(
|
204 |
generate,
|
205 |
inputs=[
|
206 |
format_size,
|
207 |
orientation,
|
|
|
208 |
style,
|
209 |
+
subject,
|
210 |
title,
|
211 |
quality,
|
212 |
creativity
|
|
|
219 |
outputs=[image_output, status]
|
220 |
)
|
221 |
|
222 |
+
logger.info("Interface créée avec succès")
|
223 |
return app
|
224 |
|
225 |
if __name__ == "__main__":
|
226 |
app = create_interface()
|
227 |
+
logger.info("Démarrage de l'application")
|
228 |
app.launch()
|