Equityone commited on
Commit
87dd51f
·
verified ·
1 Parent(s): 60afe6d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -245
app.py CHANGED
@@ -56,7 +56,7 @@ ART_STYLES = {
56
  },
57
  "Bande Dessinée": {
58
  "prompt_prefix": "comic book style poster, vibrant colors, bold outlines",
59
- "negative_prompt": "realistic, photographic, subtle colors"
60
  }
61
  }
62
 
@@ -67,278 +67,196 @@ COMPOSITION_PARAMS = {
67
  "Asymétrique": "dynamic asymmetrical composition",
68
  "Grille": "grid-based layout, structured composition",
69
  "Diagonal": "diagonal dynamic composition",
70
- "Minimaliste": "'minimal composition with lots of whitespace'",
71
- # Nouvelles options ajoutées
72
- 'Radial': "'radial' composition with circular arrangement",
73
- 'En Z': "'Z-shaped' composition guiding the viewer's eye",
74
- 'Règle des tiers': "'rule of thirds' composition for balance",
75
- 'Symétrie': "'symmetrical' composition for mirror-like balance",
76
- 'Cadre dans le cadre': "'frame within a frame' composition"
77
  },
78
- 'Ambiances': {
79
- # Options existantes
80
- 'Dramatique': "'dramatic' lighting with high contrast",
81
- 'Doux': "'soft' lighting creating a gentle atmosphere",
82
- # Nouvelles options ajoutées
83
- 'Vibrant': "'vibrant' colors creating an energetic mood",
84
- 'Mystérieux': "'mysterious' atmosphere with moody lighting",
85
- # Nouvelles ambiances ajoutées
86
- 'Serein': "'peaceful' atmosphere for calmness",
87
- 'Rétro': "'retro' atmosphere evoking nostalgia",
88
- 'Futuriste': "'futuristic' atmosphere with a high-tech feel",
89
- 'Onirique': "'dreamy' atmosphere for surreal effects",
90
- 'Industriel': "'industrial' atmosphere with raw urban vibes",
91
- 'Naturel': "'natural' atmosphere with organic elements"
92
  },
93
- 'Palette': {
94
- # Options existantes
95
- 'Monochrome': 'monochromatic color scheme',
96
- 'Contrasté': 'high contrast color palette',
97
- # Nouvelles options ajoutées
98
- 'Pastel': 'soft pastel color palette',
99
- 'Terre': 'earthy color palette',
100
- # Nouvelles palettes ajoutées
101
- 'Néon': 'neon color palette',
102
- 'Complémentaire': 'complementary color scheme',
103
- 'Analogique': 'analogous color scheme',
104
- 'Triadique': 'triadic color scheme',
105
- 'Tétradique': 'tetradic color scheme',
106
- 'Tons rompus': 'muted tones color palette'
107
- }
108
  }
109
 
110
  class ImageGenerator:
111
  def __init__(self):
112
- self.API_URL = (
113
- f"https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
114
- )
115
-
116
- # Chargement du token Hugging Face depuis les variables d'environnement
117
- token = os.getenv('HUGGINGFACE_TOKEN')
118
-
119
- if not token:
120
- logger.error("HUGGINGFACE_TOKEN non trouvé!")
121
-
122
- self.headers = {"Authorization": f"Bearer {token}"}
123
-
124
- logger.info("ImageGenerator initialisé")
125
 
126
  def _build_prompt(self, params: Dict[str, Any]) -> str:
127
- """Construction de prompt améliorée"""
128
-
129
- style_info = ART_STYLES.get(params["style"], ART_STYLES["Neo Vintage"])
130
-
131
- prompt = f"{style_info['prompt_prefix']}, {params['subject']}"
132
-
133
- # Ajout des paramètres de composition
134
- if params.get("layout"):
135
- prompt += f", {COMPOSITION_PARAMS['Layouts'][params['layout']]}"
136
-
137
- if params.get("ambiance"):
138
- prompt += f", {COMPOSITION_PARAMS['Ambiances'][params['ambiance']]}"
139
-
140
- if params.get("palette"):
141
- prompt += f", {COMPOSITION_PARAMS['Palette'][params['palette']]}"
142
-
143
- # Ajout des ajustements fins
144
- if params.get("detail_level"):
145
- detail_strength = params["detail_level"]
146
- prompt += f", {'highly detailed' if detail_strength > 7 else 'moderately detailed'}"
147
-
148
- if params.get("contrast"):
149
- contrast_strength = params["contrast"]
150
- prompt += f", {'high contrast' if contrast_strength > 7 else 'balanced contrast'}"
151
-
152
- if params.get("saturation"):
153
- saturation_strength = params["saturation"]
154
- prompt += f", {'vibrant colors' if saturation_strength > 7 else 'subtle colors'}"
155
-
156
- if params.get("title"):
157
- prompt += f", with text saying '{params['title']}'"
158
-
159
- logger.debug(f"Prompt final: {prompt}")
160
-
161
- return prompt
162
-
163
- def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
164
 
165
- try:
166
- logger.info(f"Début de génération avec paramètres: {json.dumps(params)}")
167
-
168
- if 'Bearer None' in self.headers['Authorization']:
169
- return None,"⚠️ Erreur: Token Hugging Face non configuré"
 
 
 
 
 
 
 
 
170
 
171
- prompt = self._build_prompt(params)
172
-
173
- payload = {
174
- # Construction de la requête pour l'API Hugging Face
175
- "inputs": prompt,
176
- "parameters": {
177
- "negative_prompt": ART_STYLES[params["style"]]["negative_prompt"],
178
- "num_inference_steps": min(int(35 * (params["quality"]/100)), 40),
179
- "guidance_scale": min(7.5 * (params["creativity"]/10), 10.0),
180
- "width": 768,
181
- "height": 768 if params["orientation"] == “Portrait” else 512
182
- }
183
- }
184
 
185
- logger.debug(f"Payload: {json.dumps(payload)}")
186
-
187
- response = requests.post(self.API_URL,
188
- headers=self.headers,
189
- json=payload,
190
- timeout=30)
 
 
 
 
 
191
 
192
- if response.status_code == 200:
193
- image = Image.open(io.BytesIO(response.content))
194
- return image,"✨ Création réussie!"
195
- else:
196
- error_msg = f"⚠️ Erreur API {response.status_code}: {response.text}"
197
- logger.error(error_msg)
198
- return None,error_msg
199
 
200
- except Exception as e:
201
- error_msg = f"⚠️ Erreur: {str(e)}"
202
- logger.exception("Erreur pendant la génération:")
203
- return None,error_msg
 
 
 
204
 
205
- finally:
206
- gc.collect()
 
 
 
 
207
 
208
  def create_interface():
209
-
210
- logger.info("Création de l'interface Gradio")
211
- css = """
212
- .container { max-width: 1200px; margin: auto; }
213
- .welcome { text-align: center; margin: 20px 0; padding: 20px; background: #3498db; border-radius: 10px; color: white; }
214
- .controls-group { background: #ecf0f1; padding: 15px; border-radius: 5px; margin: 10px 0; color: #2c3e50; }
215
- .advanced-controls { background: #bdc3c7; padding: 12px; border-radius: 5px; margin: 8px 0; }
216
- .gradio-slider input[type="range"] { accent-color: #3498db; }
217
- .gradio-button { transition: all 0.3s ease; }
218
- .gradio-button:hover { transform: translateY(-2px); box-shadow: 0 4px 6px rgba(52, 152, 219, 0.11),
219
- 0 1px 3px rgba(0 ,0 ,0 ,0.08); }
220
- """
221
 
222
- generator = ImageGenerator()
223
 
224
- with gr.Blocks(css=css) as app:
225
- gr.HTML("""
226
- <div class="welcome">
227
- <h1>🎨 Equity Artisan 4.0</h1>
228
- <p>Assistant de création d'affiches professionnelles avancé</p>
229
- </div>
230
- """)
231
 
232
- with gr.Column(elem_classes="container"):
233
- with gr.Group(elem_classes="controls-group"):
234
- gr.Markdown("### 📐 Format et Orientation")
235
- with gr.Row():
236
- format_size = gr.Dropdown(choices=["A4",
237
- A3,
238
- A2,
239
- A1,
240
- A0],
241
- value="A4", label="Format")
242
- orientation = gr.Radio(choices=["Portrait",
243
- Paysage],
244
- value="Portrait", label="Orientation")
245
 
246
- with gr.Group(elem_classes="controls-group"):
247
- gr.Markdown("### 🎨 Style et Composition")
248
- with gr.Row():
249
- style = gr.Dropdown(choices=list(ART_STYLES.keys()), value="Neo Vintage", label="Style artistique")
250
- layout = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Layouts"].keys()), value="Centré", label="Composition")
251
- with gr.Row():
252
- ambiance = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Ambiances"].keys()), value="Dramatique", label="Ambiance")
253
- palette = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Palette"].keys()), value="Contrasté", label="Palette")
254
 
255
- with gr.Group(elem_classes="controls-group"):
256
- gr.Markdown("### 📝 Contenu")
257
- subject = gr.Textbox(label="Description", placeholder="Décrivez votre vision...")
258
- title = gr.Textbox(label="Titre", placeholder="Titre de l'affiche...")
259
 
260
- with gr.Group(elem_classes="advanced-controls"):
261
- gr.Markdown("### 🎯 Ajustements Fins")
262
- with gr.Row():
263
- detail_level = gr.Slider(minimum=1,
264
- maximum=10,
265
- value=7,
266
- step=1,
267
- label="Niveau de Détail")
268
- contrast = gr.Slider(minimum=1,
269
- maximum=10,
270
- value=5,
271
- step=1,
272
- label="Contraste")
273
- saturation = gr.Slider(minimum=1,
274
- maximum=10,
275
- value=5,
276
- step=1,
277
- label="Saturation")
278
 
279
- with gr.Group(elem_classes="controls-group"):
280
- with gr.Row():
281
- quality = gr.Slider(minimum=30,
282
- maximum=50,
283
- value=35,
284
- label="Qualité")
285
- creativity = gr.Slider(minimum=5,
286
- maximum=15,
287
- value=7.5,
288
- label="Créativité")
289
 
290
- with gr.Row():
291
- generate_btn = gr.Button("✨ Générer", variant="primary")
292
- clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
293
 
294
- image_output = gr.Image(label="Aperçu")
295
- status = gr.Textbox(label="Statut", interactive=False)
296
 
297
- def generate(*args):
298
- logger.info("Démarrage d'une nouvelle génération")
299
- params = {
300
- "format_size": args[0],
301
- "orientation": args[1],
302
- "style": args[2],
303
- "layout": args[3],
304
- "ambiance": args[4],
305
- "palette": args[5],
306
- "subject": args[6],
307
- "title": args[7],
308
- "detail_level": args[8],
309
- "contrast": args[9],
310
- "saturation": args[10],
311
- "quality": args[11],
312
- "creativity": args[12]
313
- }
314
- result = generator.generate(params)
315
- logger.info(f"Génération terminée avec statut: {result[1]}")
316
- return result
317
 
318
- generate_btn.click(
319
- generate,
320
- inputs=[format_size,
321
- orientation,
322
- style,
323
- layout,
324
- ambiance,
325
- palette,
326
- subject,
327
- title,
328
- detail_level,
329
- contrast,
330
- saturation,
331
- quality,
332
- creativity],
333
- outputs=[image_output,status]
334
- )
335
 
336
- clear_btn.click(lambda: (None,"🗑️ Image effacée"), outputs=[image_output,status])
337
 
338
- logger.info("Interface créée avec succès")
339
- return app
340
 
341
  if __name__ == "__main__":
342
- app=create_interface()
343
- logger.info("Démarrage de l'application")
344
- app.launch()
 
56
  },
57
  "Bande Dessinée": {
58
  "prompt_prefix": "comic book style poster, vibrant colors, bold outlines",
59
+ "negative_prompt": "realistic, photographic, subtle, muted colors"
60
  }
61
  }
62
 
 
67
  "Asymétrique": "dynamic asymmetrical composition",
68
  "Grille": "grid-based layout, structured composition",
69
  "Diagonal": "diagonal dynamic composition",
70
+ "Minimaliste": "minimal composition, lots of whitespace",
71
+ "Radial": "radial composition, circular arrangement",
72
+ "En Z": "Z-shaped composition, guiding viewer's eye",
73
+ "Règle des tiers": "rule of thirds composition",
74
+ "Symétrie": "symmetrical composition, mirror-like balance",
75
+ "Cadre dans le cadre": "frame within a frame composition"
 
76
  },
77
+ "Ambiances": {
78
+ "Dramatique": "dramatic lighting, high contrast",
79
+ "Doux": "soft lighting, gentle atmosphere",
80
+ "Vibrant": "vibrant colors, energetic mood",
81
+ "Mystérieux": "mysterious atmosphere, moody lighting",
82
+ "Serein": "peaceful atmosphere, calm mood",
83
+ "Rétro": "retro atmosphere, nostalgic mood",
84
+ "Futuriste": "futuristic atmosphere, high-tech mood",
85
+ "Onirique": "dreamy atmosphere, surreal mood",
86
+ "Industriel": "industrial atmosphere, raw and urban mood",
87
+ "Naturel": "natural atmosphere, organic and earthy mood"
 
 
 
88
  },
89
+ "Palette": {
90
+ "Monochrome": "monochromatic color scheme",
91
+ "Contrasté": "high contrast color palette",
92
+ "Pastel": "soft pastel color palette",
93
+ "Terre": "earthy color palette",
94
+ "Néon": "neon color palette",
95
+ "Complémentaire": "complementary color scheme",
96
+ "Analogique": "analogous color scheme",
97
+ "Triadique": "triadic color scheme",
98
+ "Tétradique": "tetradic color scheme",
99
+ "Tons rompus": "muted tones color palette"
100
+ }
 
 
 
101
  }
102
 
103
  class ImageGenerator:
104
  def __init__(self):
105
+ self.API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
106
+ token = os.getenv('HUGGINGFACE_TOKEN')
107
+ if not token:
108
+ logger.error("HUGGINGFACE_TOKEN non trouvé!")
109
+ self.headers = {"Authorization": f"Bearer {token}"}
110
+ logger.info("ImageGenerator initialisé")
 
 
 
 
 
 
 
111
 
112
  def _build_prompt(self, params: Dict[str, Any]) -> str:
113
+ style_info = ART_STYLES.get(params["style"], ART_STYLES["Neo Vintage"])
114
+ prompt = f"{style_info['prompt_prefix']}, {params['subject']}"
115
+
116
+ if params.get("layout"):
117
+ prompt += f", {COMPOSITION_PARAMS['Layouts'][params['layout']]}"
118
+ if params.get("ambiance"):
119
+ prompt += f", {COMPOSITION_PARAMS['Ambiances'][params['ambiance']]}"
120
+ if params.get("palette"):
121
+ prompt += f", {COMPOSITION_PARAMS['Palette'][params['palette']]}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
+ for param, description in [
124
+ ("detail_level", "highly detailed" if params.get("detail_level", 0) > 7 else "moderately detailed"),
125
+ ("contrast", "high contrast" if params.get("contrast", 0) > 7 else "balanced contrast"),
126
+ ("saturation", "vibrant colors" if params.get("saturation", 0) > 7 else "subtle colors")
127
+ ]:
128
+ if params.get(param):
129
+ prompt += f", {description}"
130
+
131
+ if params.get("title"):
132
+ prompt += f", with text saying '{params['title']}'"
133
+
134
+ logger.debug(f"Prompt final: {prompt}")
135
+ return prompt
136
 
137
+ def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
138
+ try:
139
+ logger.info(f"Début de génération avec paramètres: {json.dumps(params, indent=2)}")
140
+ if 'Bearer None' in self.headers['Authorization']:
141
+ return None, "⚠️ Erreur: Token Hugging Face non configuré"
 
 
 
 
 
 
 
 
142
 
143
+ prompt = self._build_prompt(params)
144
+ payload = {
145
+ "inputs": prompt,
146
+ "parameters": {
147
+ "negative_prompt": ART_STYLES[params["style"]]["negative_prompt"],
148
+ "num_inference_steps": min(int(35 * (params["quality"]/100)), 40),
149
+ "guidance_scale": min(7.5 * (params["creativity"]/10), 10.0),
150
+ "width": 768,
151
+ "height": 768 if params["orientation"] == "Portrait" else 512
152
+ }
153
+ }
154
 
155
+ logger.debug(f"Payload: {json.dumps(payload, indent=2)}")
156
+ response = requests.post(self.API_URL, headers=self.headers, json=payload, timeout=30)
 
 
 
 
 
157
 
158
+ if response.status_code == 200:
159
+ image = Image.open(io.BytesIO(response.content))
160
+ return image, " Création réussie!"
161
+ else:
162
+ error_msg = f"⚠️ Erreur API {response.status_code}: {response.text}"
163
+ logger.error(error_msg)
164
+ return None, error_msg
165
 
166
+ except Exception as e:
167
+ error_msg = f"⚠️ Erreur: {str(e)}"
168
+ logger.exception("Erreur pendant la génération:")
169
+ return None, error_msg
170
+ finally:
171
+ gc.collect()
172
 
173
  def create_interface():
174
+ logger.info("Création de l'interface Gradio")
175
+ css = """
176
+ .container { max-width: 1200px; margin: auto; }
177
+ .welcome { text-align: center; margin: 20px 0; padding: 20px; background: #3498db; border-radius: 10px; color: white; }
178
+ .controls-group { background: #ecf0f1; padding: 15px; border-radius: 5px; margin: 10px 0; color: #2c3e50; }
179
+ .advanced-controls { background: #bdc3c7; padding: 12px; border-radius: 5px; margin: 8px 0; }
180
+ .gradio-slider input[type="range"] { accent-color: #3498db; }
181
+ .gradio-button { transition: all 0.3s ease; }
182
+ .gradio-button:hover { transform: translateY(-2px); box-shadow: 0 4px 6px rgba(52, 152, 219, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); }
183
+ """
 
 
184
 
185
+ generator = ImageGenerator()
186
 
187
+ with gr.Blocks(css=css) as app:
188
+ gr.HTML("""
189
+ <div class="welcome">
190
+ <h1>🎨 Equity Artisan 4.0</h1>
191
+ <p>Assistant de création d'affiches professionnelles avancé</p>
192
+ </div>
193
+ """)
194
 
195
+ with gr.Column(elem_classes="container"):
196
+ with gr.Group(elem_classes="controls-group"):
197
+ gr.Markdown("### 📐 Format et Orientation")
198
+ with gr.Row():
199
+ format_size = gr.Dropdown(choices=["A4", "A3", "A2", "A1", "A0"], value="A4", label="Format")
200
+ orientation = gr.Radio(choices=["Portrait", "Paysage"], value="Portrait", label="Orientation")
 
 
 
 
 
 
 
201
 
202
+ with gr.Group(elem_classes="controls-group"):
203
+ gr.Markdown("### 🎨 Style et Composition")
204
+ with gr.Row():
205
+ style = gr.Dropdown(choices=list(ART_STYLES.keys()), value="Neo Vintage", label="Style artistique")
206
+ layout = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Layouts"].keys()), value="Centré", label="Composition")
207
+ with gr.Row():
208
+ ambiance = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Ambiances"].keys()), value="Dramatique", label="Ambiance")
209
+ palette = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Palette"].keys()), value="Contrasté", label="Palette")
210
 
211
+ with gr.Group(elem_classes="controls-group"):
212
+ gr.Markdown("### 📝 Contenu")
213
+ subject = gr.Textbox(label="Description", placeholder="Décrivez votre vision...")
214
+ title = gr.Textbox(label="Titre", placeholder="Titre de l'affiche...")
215
 
216
+ with gr.Group(elem_classes="advanced-controls"):
217
+ gr.Markdown("### 🎯 Ajustements Fins")
218
+ with gr.Row():
219
+ detail_level = gr.Slider(minimum=1, maximum=10, value=7, step=1, label="Niveau de Détail")
220
+ contrast = gr.Slider(minimum=1, maximum=10, value=5, step=1, label="Contraste")
221
+ saturation = gr.Slider(minimum=1, maximum=10, value=5, step=1, label="Saturation")
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
+ with gr.Group(elem_classes="controls-group"):
224
+ with gr.Row():
225
+ quality = gr.Slider(minimum=30, maximum=50, value=35, label="Qualité")
226
+ creativity = gr.Slider(minimum=5, maximum=15, value=7.5, label="Créativité")
 
 
 
 
 
 
227
 
228
+ with gr.Row():
229
+ generate_btn = gr.Button("✨ Générer", variant="primary")
230
+ clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
231
 
232
+ image_output = gr.Image(label="Aperçu")
233
+ status = gr.Textbox(label="Statut", interactive=False)
234
 
235
+ def generate(*args):
236
+ logger.info("Démarrage d'une nouvelle génération")
237
+ params = {
238
+ "format_size": args[0], "orientation": args[1], "style": args[2],
239
+ "layout": args[3], "ambiance": args[4], "palette": args[5],
240
+ "subject": args[6], "title": args[7], "detail_level": args[8],
241
+ "contrast": args[9], "saturation": args[10], "quality": args[11],
242
+ "creativity": args[12]
243
+ }
244
+ result = generator.generate(params)
245
+ logger.info(f"Génération terminée avec statut: {result[1]}")
246
+ return result
 
 
 
 
 
 
 
 
247
 
248
+ generate_btn.click(
249
+ generate,
250
+ inputs=[format_size, orientation, style, layout, ambiance, palette, subject, title, detail_level, contrast, saturation, quality, creativity],
251
+ outputs=[image_output, status]
252
+ )
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
+ clear_btn.click(lambda: (None, "🗑️ Image effacée"), outputs=[image_output, status])
255
 
256
+ logger.info("Interface créée avec succès")
257
+ return app
258
 
259
  if __name__ == "__main__":
260
+ app = create_interface()
261
+ logger.info("Démarrage de l'application")
262
+ app.launch()