1+ #!/usr/bin/env python3
2+ """
3+ Script d'amélioration du notebook Forge SD XL Turbo
4+ Phase 21 - Itérations notebooks
5+
6+ Ajoute 3 cellules pédagogiques à des positions spécifiques:
7+ 1. Introduction Visuelle (index 2)
8+ 2. Exemples Avancés (index 10 après modification)
9+ 3. Tips & Troubleshooting (index 12 après modification)
10+ """
11+
12+ import json
13+ import sys
14+ from pathlib import Path
15+ from typing import Dict , Any , List
16+
17+ # Chemin notebook relatif au script
18+ NOTEBOOK_PATH = Path (__file__ ).parent .parent / "MyIA.AI.Notebooks" / "GenAI" / "01-Images-Foundation" / "01-4-Forge-SD-XL-Turbo.ipynb"
19+
20+ def create_code_cell (source : List [str ]) -> Dict [str , Any ]:
21+ """Crée une cellule code avec métadonnées standards"""
22+ return {
23+ "cell_type" : "code" ,
24+ "execution_count" : None ,
25+ "metadata" : {},
26+ "outputs" : [],
27+ "source" : source
28+ }
29+
30+ def create_markdown_cell (source : List [str ]) -> Dict [str , Any ]:
31+ """Crée une cellule markdown avec métadonnées standards"""
32+ return {
33+ "cell_type" : "markdown" ,
34+ "metadata" : {},
35+ "source" : source
36+ }
37+
38+ # --- CELLULE 1: Introduction Visuelle (index 2) ---
39+ CELL_INTRO_VISUELLE = create_code_cell ([
40+ '"""\n ' ,
41+ 'Vérification du statut de l\' API et affichage d\' une bannière visuelle\n ' ,
42+ '"""\n ' ,
43+ '\n ' ,
44+ '# Test de connectivité API\n ' ,
45+ 'try:\n ' ,
46+ ' response = requests.get(f"{API_BASE_URL}/sdapi/v1/options", timeout=10)\n ' ,
47+ ' response.raise_for_status()\n ' ,
48+ ' \n ' ,
49+ ' # Bannière de succès\n ' ,
50+ ' from IPython.display import display, HTML\n ' ,
51+ ' \n ' ,
52+ ' banner_html = """\n ' ,
53+ ' <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n ' ,
54+ ' padding: 20px;\n ' ,
55+ ' border-radius: 10px;\n ' ,
56+ ' text-align: center;\n ' ,
57+ ' color: white;\n ' ,
58+ ' font-family: Arial, sans-serif;\n ' ,
59+ ' box-shadow: 0 4px 6px rgba(0,0,0,0.1);">\n ' ,
60+ ' <h1 style="margin: 0; font-size: 28px;">🎨 Stable Diffusion Forge</h1>\n ' ,
61+ ' <h2 style="margin: 10px 0 0 0; font-weight: normal; font-size: 18px;">SD XL Turbo API</h2>\n ' ,
62+ ' <p style="margin: 15px 0 0 0; font-size: 14px; opacity: 0.9;">✅ API Connectée et Opérationnelle</p>\n ' ,
63+ ' <p style="margin: 5px 0 0 0; font-size: 12px; opacity: 0.7;">Génération rapide 4-steps • Performance ~18s</p>\n ' ,
64+ ' </div>\n ' ,
65+ ' """\n ' ,
66+ ' \n ' ,
67+ ' display(HTML(banner_html))\n ' ,
68+ ' print("\\ n✅ API Forge accessible et prête à l\' utilisation")\n ' ,
69+ ' \n ' ,
70+ 'except requests.exceptions.RequestException as e:\n ' ,
71+ ' print(f"⚠️ Avertissement: Impossible de contacter l\' API Forge")\n ' ,
72+ ' print(f" Erreur: {e}")\n ' ,
73+ ' print(f" Vérifiez que l\' API est accessible à {API_BASE_URL}")\n '
74+ ])
75+
76+ # --- CELLULE 2: Exemples Avancés (index 10 après ajout cellule 1) ---
77+ CELL_EXEMPLES_AVANCES = create_code_cell ([
78+ '"""\n ' ,
79+ 'Techniques Avancées de Génération\n ' ,
80+ '\n ' ,
81+ 'Ce bloc explore des techniques avancées pour améliorer vos générations:\n ' ,
82+ '1. Reproductibilité avec seed fixe\n ' ,
83+ '2. Exploration créative avec seeds aléatoires\n ' ,
84+ '3. Génération batch optimisée\n ' ,
85+ '"""\n ' ,
86+ '\n ' ,
87+ 'import time\n ' ,
88+ 'import random\n ' ,
89+ '\n ' ,
90+ '# --- Technique 1: Reproductibilité avec Seed Fixe ---\n ' ,
91+ 'print("🎯 Technique 1: Génération Reproductible (seed fixe)\\ n")\n ' ,
92+ '\n ' ,
93+ 'def generate_with_seed(prompt: str, seed: int):\n ' ,
94+ ' """Génère une image avec seed fixe pour reproductibilité"""\n ' ,
95+ ' payload = {\n ' ,
96+ ' "prompt": prompt,\n ' ,
97+ ' "steps": 4,\n ' ,
98+ ' "cfg_scale": 2.0,\n ' ,
99+ ' "width": 512,\n ' ,
100+ ' "height": 512,\n ' ,
101+ ' "seed": seed,\n ' ,
102+ ' "sampler_name": "DPM++ 2M"\n ' ,
103+ ' }\n ' ,
104+ ' \n ' ,
105+ ' try:\n ' ,
106+ ' response = requests.post(\n ' ,
107+ ' f"{API_BASE_URL}/sdapi/v1/txt2img",\n ' ,
108+ ' json=payload,\n ' ,
109+ ' timeout=TIMEOUT\n ' ,
110+ ' )\n ' ,
111+ ' response.raise_for_status()\n ' ,
112+ ' result = response.json()\n ' ,
113+ ' \n ' ,
114+ ' if "images" in result and len(result["images"]) > 0:\n ' ,
115+ ' image_data = base64.b64decode(result["images"][0])\n ' ,
116+ ' return Image.open(BytesIO(image_data))\n ' ,
117+ ' except Exception as e:\n ' ,
118+ ' print(f"❌ Erreur: {e}")\n ' ,
119+ ' return None\n ' ,
120+ '\n ' ,
121+ '# Test avec seed fixe (42)\n ' ,
122+ 'prompt_seed = "a mystical forest with glowing mushrooms"\n ' ,
123+ 'print(f"Prompt: \\ "{prompt_seed}\\ "\\ nSeed: 42\\ n")\n ' ,
124+ '\n ' ,
125+ 'img_seed = generate_with_seed(prompt_seed, seed=42)\n ' ,
126+ 'if img_seed:\n ' ,
127+ ' plt.figure(figsize=(6, 6))\n ' ,
128+ ' plt.imshow(img_seed)\n ' ,
129+ ' plt.axis(\' off\' )\n ' ,
130+ ' plt.title("Génération Reproductible (seed=42)", fontsize=11)\n ' ,
131+ ' plt.tight_layout()\n ' ,
132+ ' plt.show()\n ' ,
133+ ' print("💡 Astuce: Réexécutez cette cellule → même résultat garanti\\ n")\n ' ,
134+ '\n ' ,
135+ '# --- Technique 2: Exploration Créative (seeds aléatoires) ---\n ' ,
136+ 'print("\\ n🎲 Technique 2: Exploration Créative (seeds aléatoires)\\ n")\n ' ,
137+ '\n ' ,
138+ 'prompt_creative = "a steampunk airship in the clouds"\n ' ,
139+ 'seeds_random = [random.randint(1, 999999) for _ in range(3)]\n ' ,
140+ '\n ' ,
141+ 'print(f"Génération de 3 variations avec seeds aléatoires...\\ n")\n ' ,
142+ 'images_seeds = []\n ' ,
143+ '\n ' ,
144+ 'for i, seed in enumerate(seeds_random, 1):\n ' ,
145+ ' print(f"{i}. Seed: {seed}")\n ' ,
146+ ' img = generate_with_seed(prompt_creative, seed)\n ' ,
147+ ' if img:\n ' ,
148+ ' images_seeds.append((img, seed))\n ' ,
149+ '\n ' ,
150+ '# Affichage grille\n ' ,
151+ 'if len(images_seeds) == 3:\n ' ,
152+ ' fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n ' ,
153+ ' for ax, (img, seed) in zip(axes, images_seeds):\n ' ,
154+ ' ax.imshow(img)\n ' ,
155+ ' ax.axis(\' off\' )\n ' ,
156+ ' ax.set_title(f"Seed: {seed}", fontsize=9)\n ' ,
157+ ' plt.suptitle("Exploration Créative: Même Prompt, Seeds Différents", fontsize=12)\n ' ,
158+ ' plt.tight_layout()\n ' ,
159+ ' plt.show()\n ' ,
160+ ' print("\\ n💡 Observation: Même prompt → résultats visuels différents\\ n")\n ' ,
161+ '\n ' ,
162+ '# --- Technique 3: Batch Generation Optimisé ---\n ' ,
163+ 'print("\\ n⚡ Technique 3: Génération Batch Optimisée\\ n")\n ' ,
164+ '\n ' ,
165+ 'def generer_batch_optimise(prompts_list: List[str]):\n ' ,
166+ ' """Génère plusieurs images en batch avec gestion erreurs"""\n ' ,
167+ ' results = []\n ' ,
168+ ' start_time = time.time()\n ' ,
169+ ' \n ' ,
170+ ' for i, prompt in enumerate(prompts_list, 1):\n ' ,
171+ ' print(f"[{i}/{len(prompts_list)}] {prompt[:50]}...")\n ' ,
172+ ' img = generate_image_forge(\n ' ,
173+ ' prompt=prompt,\n ' ,
174+ ' steps=4,\n ' ,
175+ ' cfg_scale=2.0,\n ' ,
176+ ' width=512,\n ' ,
177+ ' height=512\n ' ,
178+ ' )\n ' ,
179+ ' if img:\n ' ,
180+ ' results.append((prompt, img))\n ' ,
181+ ' \n ' ,
182+ ' elapsed = time.time() - start_time\n ' ,
183+ ' print(f"\\ n✅ Batch terminé: {len(results)}/{len(prompts_list)} succès en {elapsed:.1f}s")\n ' ,
184+ ' return results\n ' ,
185+ '\n ' ,
186+ '# Batch de 3 prompts thématiques\n ' ,
187+ 'batch_prompts = [\n ' ,
188+ ' "a futuristic city at sunset, cyberpunk style",\n ' ,
189+ ' "a peaceful zen garden with cherry blossoms",\n ' ,
190+ ' "an underwater scene with colorful coral reefs"\n ' ,
191+ ']\n ' ,
192+ '\n ' ,
193+ 'resultats_batch = generer_batch_optimise(batch_prompts)\n ' ,
194+ '\n ' ,
195+ '# Affichage résultats batch\n ' ,
196+ 'if len(resultats_batch) >= 2:\n ' ,
197+ ' n_cols = len(resultats_batch)\n ' ,
198+ ' fig, axes = plt.subplots(1, n_cols, figsize=(5*n_cols, 5))\n ' ,
199+ ' if n_cols == 1:\n ' ,
200+ ' axes = [axes]\n ' ,
201+ ' \n ' ,
202+ ' for ax, (prompt, img) in zip(axes, resultats_batch):\n ' ,
203+ ' ax.imshow(img)\n ' ,
204+ ' ax.axis(\' off\' )\n ' ,
205+ ' title = prompt if len(prompt) <= 35 else prompt[:32] + "..."\n ' ,
206+ ' ax.set_title(title, fontsize=9)\n ' ,
207+ ' \n ' ,
208+ ' plt.suptitle("Génération Batch Thématique", fontsize=12, y=1.02)\n ' ,
209+ ' plt.tight_layout()\n ' ,
210+ ' plt.show()\n ' ,
211+ ' \n ' ,
212+ ' print("\\ n💡 Usage: Idéal pour prototypage rapide de concepts multiples")\n '
213+ ])
214+
215+ # --- CELLULE 3: Tips & Troubleshooting (index 12 après ajout cellules 1+2) ---
216+ CELL_TIPS_TROUBLESHOOTING = create_markdown_cell ([
217+ '## 💡 Tips & Troubleshooting\n ' ,
218+ '\n ' ,
219+ '### Erreurs Courantes et Solutions\n ' ,
220+ '\n ' ,
221+ '#### 1. ⏱️ Timeout Error (`requests.exceptions.Timeout`)\n ' ,
222+ '\n ' ,
223+ '**Symptôme**: La requête échoue après 60 secondes\n ' ,
224+ '\n ' ,
225+ '**Causes possibles**:\n ' ,
226+ '- Charge serveur élevée\n ' ,
227+ '- Paramètres non-optimaux (steps > 4)\n ' ,
228+ '- Résolution trop élevée (>768×768)\n ' ,
229+ '\n ' ,
230+ '**Solutions**:\n ' ,
231+ '```python\n ' ,
232+ '# Augmenter timeout si nécessaire\n ' ,
233+ 'TIMEOUT = 90 # au lieu de 60\n ' ,
234+ '\n ' ,
235+ '# Vérifier paramètres Turbo\n ' ,
236+ 'steps = 4 # ✅ Optimal\n ' ,
237+ 'cfg_scale = 2.0 # ✅ Optimal\n ' ,
238+ 'width = 512 # ✅ Optimal\n ' ,
239+ '```\n ' ,
240+ '\n ' ,
241+ '#### 2. ❌ Bad Request Error (HTTP 400)\n ' ,
242+ '\n ' ,
243+ '**Symptôme**: `response.status_code == 400`\n ' ,
244+ '\n ' ,
245+ '**Causes possibles**:\n ' ,
246+ '- Payload JSON mal formé\n ' ,
247+ '- Paramètres invalides (sampler inconnu, résolution impaire)\n ' ,
248+ '- Prompt trop long (>77 tokens)\n ' ,
249+ '\n ' ,
250+ '**Solutions**:\n ' ,
251+ '```python\n ' ,
252+ '# Valider payload avant envoi\n ' ,
253+ 'import json\n ' ,
254+ 'try:\n ' ,
255+ ' json.dumps(payload) # Test sérialisation\n ' ,
256+ 'except TypeError as e:\n ' ,
257+ ' print(f"Payload invalide: {e}")\n ' ,
258+ '\n ' ,
259+ '# Vérifier résolution (multiple de 64)\n ' ,
260+ 'width = (width // 64) * 64\n ' ,
261+ 'height = (height // 64) * 64\n ' ,
262+ '```\n ' ,
263+ '\n ' ,
264+ '#### 3. 🖼️ Image Non Générée (payload vide)\n ' ,
265+ '\n ' ,
266+ '**Symptôme**: `result["images"]` est vide ou absent\n ' ,
267+ '\n ' ,
268+ '**Causes possibles**:\n ' ,
269+ '- Prompt contient mots-clés bloqués (NSFW filter)\n ' ,
270+ '- Modèle non chargé côté serveur\n ' ,
271+ '\n ' ,
272+ '**Solutions**:\n ' ,
273+ '```python\n ' ,
274+ '# Vérifier réponse complète\n ' ,
275+ 'if "images" not in result:\n ' ,
276+ ' print(f"Réponse API: {result}")\n ' ,
277+ ' \n ' ,
278+ '# Tester avec prompt simple\n ' ,
279+ 'prompt_test = "a landscape" # Minimal, neutre\n ' ,
280+ '```\n ' ,
281+ '\n ' ,
282+ '### 🚀 Tips Performance\n ' ,
283+ '\n ' ,
284+ '#### Optimisation Vitesse\n ' ,
285+ '\n ' ,
286+ '| Action | Gain | Trade-off |\n ' ,
287+ '|--------|------|----------|\n ' ,
288+ '| `steps=4` (vs 20) | **~4× plus rapide** | Légère réduction qualité |\n ' ,
289+ '| `512×512` (vs 768×768) | **~2× plus rapide** | Résolution inférieure |\n ' ,
290+ '| `cfg_scale=2.0` (vs 7.0) | **~1.5× plus rapide** | Moins de guidage |\n ' ,
291+ '\n ' ,
292+ '#### Optimisation Qualité\n ' ,
293+ '\n ' ,
294+ '```python\n ' ,
295+ '# Pour génération finale haute qualité\n ' ,
296+ 'image_hq = generate_image_forge(\n ' ,
297+ ' prompt=prompt,\n ' ,
298+ ' steps=6, # ⚠️ Compromis: +50% temps\n ' ,
299+ ' cfg_scale=2.5, # Guidage légèrement renforcé\n ' ,
300+ ' width=768,\n ' ,
301+ ' height=768\n ' ,
302+ ')\n ' ,
303+ '```\n ' ,
304+ '\n ' ,
305+ '### 📚 Ressources Complémentaires\n ' ,
306+ '\n ' ,
307+ '- **Guide Prompts Efficaces**: [Lexica.art](https://lexica.art) (exemples visuels)\n ' ,
308+ '- **Samplers Comparés**: [Stable Diffusion Wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#sampling-method-selection)\n ' ,
309+ '- **Negative Prompts Templates**: [PromptHero](https://prompthero.com/stable-diffusion-negative-prompts)\n ' ,
310+ '\n ' ,
311+ '### 🔗 Liens Utiles\n ' ,
312+ '\n ' ,
313+ '- **Documentation API Complète**: [`GUIDE-APIS-ETUDIANTS.md`](../../../docs/suivis/genai-image/GUIDE-APIS-ETUDIANTS.md)\n ' ,
314+ '- **Notebook Qwen (Édition Images)**: [`01-5-Qwen-Image-Edit.ipynb`](01-5-Qwen-Image-Edit.ipynb)\n ' ,
315+ '- **Status API Temps Réel**: `https://turbo.stable-diffusion-webui-forge.myia.io/sdapi/v1/progress`\n '
316+ ])
317+
318+ def main ():
319+ """Fonction principale d'amélioration du notebook"""
320+
321+ print ("🔧 Amélioration Notebook Forge SD XL Turbo - Phase 21" )
322+ print ("=" * 60 )
323+
324+ # Lecture notebook
325+ print (f"\n 📖 Lecture: { NOTEBOOK_PATH } " )
326+ try :
327+ with open (NOTEBOOK_PATH , 'r' , encoding = 'utf-8' ) as f :
328+ notebook = json .load (f )
329+ except FileNotFoundError :
330+ print (f"❌ Erreur: Fichier introuvable: { NOTEBOOK_PATH } " )
331+ return 1
332+ except json .JSONDecodeError as e :
333+ print (f"❌ Erreur parsing JSON: { e } " )
334+ return 1
335+
336+ cells = notebook .get ('cells' , [])
337+ initial_count = len (cells )
338+ print (f" Cellules initiales: { initial_count } " )
339+
340+ # Insertion cellules aux positions spécifiées
341+ insertions = [
342+ (2 , CELL_INTRO_VISUELLE , "Introduction Visuelle" ),
343+ (10 + 1 , CELL_EXEMPLES_AVANCES , "Exemples Avancés" ), # +1 car cellule 1 déjà insérée
344+ (12 + 2 , CELL_TIPS_TROUBLESHOOTING , "Tips & Troubleshooting" ) # +2 car cellules 1+2 déjà insérées
345+ ]
346+
347+ print ("\n 🔨 Insertion cellules:" )
348+ for idx , cell , name in insertions :
349+ cells .insert (idx , cell )
350+ print (f" ✅ [{ idx :2d} ] { name } ({ cell ['cell_type' ]} )" )
351+
352+ notebook ['cells' ] = cells
353+ final_count = len (cells )
354+ print (f"\n 📊 Résultat: { initial_count } → { final_count } cellules (+{ final_count - initial_count } )" )
355+
356+ # Sauvegarde
357+ print (f"\n 💾 Sauvegarde: { NOTEBOOK_PATH } " )
358+ try :
359+ with open (NOTEBOOK_PATH , 'w' , encoding = 'utf-8' ) as f :
360+ json .dump (notebook , f , ensure_ascii = False , indent = 1 )
361+ print (" ✅ Notebook sauvegardé avec succès" )
362+ except Exception as e :
363+ print (f" ❌ Erreur sauvegarde: { e } " )
364+ return 1
365+
366+ print ("\n ✅ Amélioration terminée avec succès!" )
367+ print ("\n 📝 Modifications appliquées:" )
368+ print (" • [Index 2] Introduction Visuelle + Vérification API" )
369+ print (" • [Index 11] Techniques Avancées (seed, batch, exploration)" )
370+ print (" • [Index 14] Tips & Troubleshooting complets" )
371+
372+ return 0
373+
374+ if __name__ == "__main__" :
375+ sys .exit (main ())
0 commit comments