Equityone commited on
Commit
8bac5ab
·
verified ·
1 Parent(s): 4aea8e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +230 -163
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, muted colors"
60
  }
61
  }
62
 
@@ -67,196 +67,263 @@ COMPOSITION_PARAMS = {
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()
 
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
  "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
+ if not token:
119
+ logger.error("HUGGINGFACE_TOKEN non trouvé!")
120
+ self.headers = {"Authorization": f"Bearer {token}"}
121
+ logger.info("ImageGenerator initialisé")
122
+
123
+ def _build_prompt(self, params: Dict[str, Any]) -> str:
124
+ """Construction de prompt améliorée"""
125
+ style_info = ART_STYLES.get(params["style"], ART_STYLES["Neo Vintage"])
126
+ prompt = f"{style_info['prompt_prefix']}, {params['subject']}"
127
+
128
+ # Ajout des paramètres de composition
129
+ if params.get("layout"):
130
+ prompt += f", {COMPOSITION_PARAMS['Layouts'][params['layout']]}"
131
+ if params.get("ambiance"):
132
+ prompt += f", {COMPOSITION_PARAMS['Ambiances'][params['ambiance']]}"
133
+ if params.get("palette"):
134
+ prompt += f", {COMPOSITION_PARAMS['Palette'][params['palette']]}"
135
+
136
+ # Ajout des ajustements fins
137
+ if params.get("detail_level"):
138
+ detail_strength = params["detail_level"]
139
+ prompt += f", {'highly detailed' if detail_strength > 7 else 'moderately detailed'}"
140
+ if params.get("contrast"):
141
+ contrast_strength = params["contrast"]
142
+ prompt += f", {'high contrast' if contrast_strength > 7 else 'balanced contrast'}"
143
+ if params.get("saturation"):
144
+ saturation_strength = params["saturation"]
145
+ prompt += f", {'vibrant colors' if saturation_strength > 7 else 'subtle colors'}"
146
+
147
+ if params.get("title"):
148
+ prompt += f", with text saying '{params['title']}'"
149
+
150
+ logger.debug(f"Prompt final: {prompt}")
151
+ return prompt
152
+
153
+ def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]:
154
+ try:
155
+ logger.info(f"Début de génération avec paramètres: {json.dumps(params)}")
156
+ if 'Bearer None' in self.headers['Authorization']:
157
+ return None, ("⚠️ Erreur: Token Hugging Face non configuré")
158
 
159
+ prompt = self._build_prompt(params)
160
+ payload = {
161
+ # Construction de la requête pour l'API Hugging Face
162
+ “inputs”: prompt,
163
+ “parameters”: {
164
+ “negative_prompt”: ART_STYLES[params["style"]]["negative_prompt"],
165
+ “num_inference_steps”: min(int(35 * (params["quality"]/100)), 40),
166
+ “guidance_scale”: min(7.5 * (params["creativity"]/10), 10.0),
167
+ “width”: 768,
168
+ “height”: 768 if params["orientation"] == “Portrait” else 512
169
+ }
170
+ }
171
 
172
+ logger.debug(f"Payload: {json.dumps(payload)}")
173
+ response = requests.post(self.API_URL,
174
+ headers=self.headers,
175
+ json=payload,
176
+ timeout=30)
 
 
 
 
 
 
177
 
178
+ if response.status_code == 200:
179
+ image = Image.open(io.BytesIO(response.content))
180
+ return image,"✨ Création réussie!"
181
+ else:
182
+ error_msg = f"⚠️ Erreur API {response.status_code}: {response.text}"
183
+ logger.error(error_msg)
184
+ return None,error_msg
185
 
186
+ except Exception as e:
187
+ error_msg = f"⚠️ Erreur: {str(e)}"
188
+ logger.exception("Erreur pendant la génération:")
189
+ return None,error_msg
 
 
 
190
 
191
+ finally:
192
+ gc.collect()
 
 
 
 
193
 
194
  def create_interface():
195
+ logger.info("Création de l'interface Gradio")
196
+ css = """
197
+ .container { max-width: 1200px; margin: auto; }
198
+ .welcome { text-align: center; margin: 20px 0; padding: 20px; background: #3498db; border-radius: 10px; color: white; }
199
+ .controls-group { background: #ecf0f1; padding: 15px; border-radius: 5px; margin: 10px 0; color: #2c3e50; }
200
+ .advanced-controls { background: #bdc3c7; padding: 12px; border-radius: 5px; margin: 8px 0; }
201
+ .gradio-slider input[type="range"] { accent-color: #3498db; }
202
+ .gradio-button { transition: all 0.3s ease; }
203
+ .gradio-button:hover { transform: translateY(-2px); box-shadow: 0 4px 6px rgba(52, 152, 219, 0.11),
204
+ 0 1px 3px rgba(0, 0 ,0 ,0.08); }
205
+ """
206
 
207
+ generator = ImageGenerator()
208
 
209
+ with gr.Blocks(css=css) as app:
210
+ gr.HTML("""
211
+ <div class="welcome">
212
+ <h1>🎨 Equity Artisan 4.0</h1>
213
+ <p>Assistant de création d'affiches professionnelles avancé</p>
214
+ </div>
215
+ """)
216
 
217
+ with gr.Column(elem_classes="container"):
218
+ with gr.Group(elem_classes="controls-group"):
219
+ gr.Markdown("### 📐 Format et Orientation")
220
+ with gr.Row():
221
+ format_size = gr.Dropdown(choices=["A4",
222
+ A3",
223
+ A2",
224
+ A1",
225
+ A0"],
226
+ value="A4", label="Format")
227
+ orientation = gr.Radio(choices=["Portrait",
228
+ Paysage"],
229
+ value="Portrait", label="Orientation")
230
 
231
+ with gr.Group(elem_classes="controls-group"):
232
+ gr.Markdown("### 🎨 Style et Composition")
233
+ with gr.Row():
234
+ style = gr.Dropdown(choices=list(ART_STYLES.keys()), value="Neo Vintage", label="Style artistique")
235
+ layout = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Layouts"].keys()), value="Centré", label="Composition")
236
+ with gr.Row():
237
+ ambiance = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Ambiances"].keys()), value="Dramatique", label="Ambiance")
238
+ palette = gr.Dropdown(choices=list(COMPOSITION_PARAMS["Palette"].keys()), value="Contrasté", label="Palette")
239
 
240
+ with gr.Group(elem_classes="controls-group"):
241
+ gr.Markdown("### 📝 Contenu")
242
+ subject = gr.Textbox(label="Description", placeholder="Décrivez votre vision...")
243
+ title = gr.Textbox(label="Titre", placeholder="Titre de l'affiche...")
244
 
245
+ with gr.Group(elem_classes="advanced-controls"):
246
+ gr.Markdown("### 🎯 Ajustements Fins")
247
+ with gr.Row():
248
+ detail_level = gr.Slider(minimum=1,
249
+ maximum=10,
250
+ value=7,
251
+ step=1,
252
+ label="Niveau de Détail")
253
+ contrast = gr.Slider(minimum=1,
254
+ maximum=10,
255
+ value=5,
256
+ step=1,
257
+ label="Contraste")
258
+ saturation = gr.Slider(minimum=1,
259
+ maximum=10,
260
+ value=5,
261
+ step=1,
262
+ label="Saturation")
263
 
264
+ with gr.Group(elem_classes="controls-group"):
265
+ with gr.Row():
266
+ quality = gr.Slider(minimum=30,
267
+ maximum=50,
268
+ value=35,
269
+ label="Qualité")
270
+ creativity = gr.Slider(minimum=5,
271
+ maximum=15,
272
+ value=7.5,
273
+ label="Créativité")
274
 
275
+ with gr.Row():
276
+ generate_btn = gr.Button("✨ Générer", variant="primary")
277
+ clear_btn = gr.Button("🗑️ Effacer", variant="secondary")
278
 
279
+ image_output = gr.Image(label="Aperçu")
280
+ status = gr.Textbox(label="Statut", interactive=False)
281
 
282
+ def generate(*args):
283
+ logger.info("Démarrage d'une nouvelle génération")
284
+ params = {
285
+ format_size”: args[0],
286
+ “orientation”: args[1],
287
+ “style”: args[2],
288
+ “layout”: args[3],
289
+ “ambiance”: args[4],
290
+ “palette”: args[5],
291
+ “subject”: args[6],
292
+ “title”: args[7],
293
+ “detail_level”: args[8],
294
+ “contrast”: args[9],
295
+ “saturation”: args[10],
296
+ “quality”: args[11],
297
+ “creativity”: args[12]
298
+ }
299
+ result = generator.generate(params)
300
+ logger.info(f"Génération terminée avec statut: {result[1]}")
301
+ return result
302
 
303
+ generate_btn.click(
304
+ generate,
305
+ inputs=[format_size,
306
+ orientation,
307
+ style,
308
+ layout,
309
+ ambiance,
310
+ palette,
311
+ subject,
312
+ title,
313
+ detail_level,
314
+ contrast,
315
+ saturation,
316
+ quality,
317
+ creativity],
318
+ outputs=[image_output,status]
319
+ )
320
 
321
+ clear_btn.click(lambda: (None,"🗑️ Image effacée"), outputs=[image_output,status])
322
 
323
+ logger.info("Interface créée avec succès")
324
+ return app
325
 
326
  if __name__ == "__main__":
327
+ app=create_interface()
328
+ logger.info("Démarrage de l'application")
329
+ app.launch()