# Paramètres Papermill - JAMAIS modifier ce commentaire
# Configuration workflow créatif
workflow_type = "concept_to_image" # "concept_to_image", "style_variations", "storyboard", "brand_identity"
creative_style = "modern" # "modern", "vintage", "minimalist", "artistic", "professional"
target_audience = "students" # "students", "professionals", "educators", "general"
# Paramètres génération
concept_description = "innovative learning platform" # Description du concept initial
style_variations_count = 4 # Nombre de variations à générer
image_quality = "high" # "low", "medium", "high", "auto"
image_size = "1024x1024" # "1024x1024", "1024x1536", "1536x1024"
# Pipeline multi-étapes
enable_sketch_phase = True # Inclure phase esquisse/wireframe
enable_refinement = True # Inclure phase de raffinement
enable_variations = True # Générer des variations
enable_ab_comparison = True # Activer comparaisons A/B
# Story-boarding
storyboard_scenes = 5 # Nombre de scènes pour story-board
narrative_style = "educational" # "educational", "commercial", "artistic", "documentary"
# Export et sauvegarde
save_intermediate_steps = True # Sauvegarder toutes les étapes
create_gallery = True # Créer une galerie HTML
export_templates = True # Exporter comme templates réutilisables
# Paramètres avancés
batch_processing = False # Mode batch pour plusieurs projets
auto_optimization = True # Optimisation automatique des prompts
quality_assessment = True # Évaluation qualité automatiqueNavigation : Index | << Précédent | Suivant >>
Creative Workflows - GenAI
Auteur : Équipe CoursIA Date : 2025-01-08 Version : 1.0.0 Module : 04-Images-Applications Niveau : Expert Technologies : DALL-E 3, GPT-5, OpenRouter, Workflows Multi-Étapes Duree estimee : 60 minutes
Objectifs d’Apprentissage
Prerequis
- Environment Setup (notebook 00-1) complete
- API Configuration (notebook 00-3) complete
- Foundation DALL-E 3 (notebook 01-1) complete
- Educational Content (notebook 04-1) complete
Note sur le modele d’image reellement utilise
Ce notebook presente les workflows creatifs en reference a DALL-E 3 (le concept pedagogique historique de generation d’images OpenAI). Toutefois, l’endpoint OpenAI Images standard ne fournit plus DALL-E 3 : le modele reellement invoque par generate_creative_image() est gpt-image-1 (successeur de DALL-E 3 dans l’API Images).
Trois elements du code le confirment : (1) la configuration model_image = "gpt-image-1" ; (2) la docstring et l’appel client.images.generate(model="gpt-image-1", ...) ; (3) la sortie de la cellule de demonstration qui affiche Image generee (gpt-image-1).
Les mentions ulterieures de “DALL-E 3” (ligne Technologies, description des appels API, table des composants) designent donc le concept pedagogique, l’execution reelle utilisant gpt-image-1.
# Parameters
BATCH_MODE = "true"Setup et configuration
Chargement des dependances Python (OpenAI, PIL, ipywidgets) et resolution du fichier .env contenant les cles API. Le notebook recherche le fichier de configuration en remontant l’arborescence de repertoires pour fonctionner a la fois en interactif et via Papermill.
# Verification des dependances externes
import importlib
_DEPS_STATUS = {}
try:
importlib.import_module('PIL')
_DEPS_STATUS['PIL'] = True
except ImportError:
_DEPS_STATUS['PIL'] = False
print(f'WARNING: Pillow non installe - pip install Pillow')
try:
importlib.import_module('requests')
_DEPS_STATUS['requests'] = True
except ImportError:
_DEPS_STATUS['requests'] = False
print(f'WARNING: requests non installe - pip install requests')
try:
importlib.import_module('matplotlib')
_DEPS_STATUS['matplotlib'] = True
except ImportError:
_DEPS_STATUS['matplotlib'] = False
print(f'WARNING: matplotlib non installe - pip install matplotlib')
try:
importlib.import_module('numpy')
_DEPS_STATUS['numpy'] = True
except ImportError:
_DEPS_STATUS['numpy'] = False
print(f'WARNING: numpy non installe - pip install numpy')
try:
importlib.import_module('dotenv')
_DEPS_STATUS['dotenv'] = True
except ImportError:
_DEPS_STATUS['dotenv'] = False
print(f'WARNING: python-dotenv non installe - pip install python-dotenv')
try:
importlib.import_module('ipywidgets')
_DEPS_STATUS['ipywidgets'] = True
except ImportError:
_DEPS_STATUS['ipywidgets'] = False
print(f'WARNING: ipywidgets non installe - pip install ipywidgets')
try:
importlib.import_module('IPython')
_DEPS_STATUS['IPython'] = True
except ImportError:
_DEPS_STATUS['IPython'] = False
print(f'WARNING: ipython non installe - pip install ipython')
_all_deps_ok = all(_DEPS_STATUS.values())
if not _all_deps_ok:
missing = [k for k, v in _DEPS_STATUS.items() if not v]
print(f'Dependances manquantes: {missing}')
else:
print('Toutes les dependances sont disponibles')
# Setup environnement et imports
import os
import sys
import json
import asyncio
import base64
import requests
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional, Tuple
import logging
import uuid
# Imports pour génération et traitement
from openai import AsyncOpenAI, OpenAI
import ipywidgets as widgets
from IPython.display import display, HTML, Image, clear_output, Markdown
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image as PILImage, ImageDraw, ImageFont, ImageEnhance
import io
import numpy as np
# Import helpers GenAI
# Chargement robuste de la configuration .env
from dotenv import load_dotenv
import os
# Recherche du .env dans tous les parents (pour Papermill qui change le cwd)
current_path = Path.cwd()
env_loaded = False
for _ in range(10):
env_path = current_path / ".env"
if env_path.exists():
load_dotenv(env_path)
print(f".env charge depuis: {env_path.name}")
env_loaded = True
break
if current_path.name == "GenAI" or len(current_path.parts) <= 1:
break
current_path = current_path.parent
if not env_loaded:
print("WARNING: .env non trouve, utilisation variables environnement")
# GENAI_ROOT pointe vers le dossier GenAI (current_path du while loop)
HELPERS_PATH = current_path / 'shared' / 'helpers'
if HELPERS_PATH.exists():
sys.path.insert(0, str(HELPERS_PATH.parent))
try:
from helpers.genai_helpers import setup_genai_logging, load_genai_config
print("Helpers GenAI importés")
except ImportError:
print("Helpers GenAI non disponibles")
# Configuration logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('creative_workflows')
print(f"Creative Workflows - GenAI")
print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Workflow: {workflow_type} | Style: {creative_style} | Audience: {target_audience}")Toutes les dependances sont disponibles
.env charge depuis: .env
Helpers GenAI importés
Creative Workflows - GenAI
2026-08-31 02:37:47
Workflow: concept_to_image | Style: modern | Audience: students
# Configuration APIs et validation
from dotenv import load_dotenv
# Charger variables d'environnement
env_path = current_path / '.env'
if env_path.exists():
load_dotenv(env_path)
print(f"Configuration chargée: {env_path.name}")
else:
print(f"Fichier .env non trouvé: {env_path.name}")
# Configuration APIs
api_configs = {
'openai': {
'api_key': os.getenv('OPENAI_API_KEY'),
'base_url': os.getenv('OPENAI_BASE_URL', 'https://api.openai.com/v1'),
'model_chat': os.getenv('OPENAI_CHAT_MODEL_ID', 'gpt-5-mini'),
'model_image': 'gpt-image-1'
},
'openrouter': {
'api_key': os.getenv('OPENROUTER_API_KEY'),
'base_url': os.getenv('OPENROUTER_BASE_URL', 'https://openrouter.ai/api/v1'),
'app_name': os.getenv('OPENROUTER_APP_NAME', 'CoursIA-GenAI'),
'model_chat': 'anthropic/claude-3.5-sonnet'
}
}
# Validation configuration
config_status = {}
for api_name, config in api_configs.items():
if config['api_key']:
config_status[api_name] = 'Configuré'
print(f"{api_name.upper()}: API configurée")
else:
config_status[api_name] = 'Manquant'
print(f"{api_name.upper()}: Clé API manquante")
print(f"\nConfiguration APIs: {config_status}")Configuration chargée: .env
OPENAI: API configurée
OPENROUTER: API configurée
Configuration APIs: {'openai': 'Configuré', 'openrouter': 'Configuré'}
Validation de la configuration API
L’output ci-dessus confirme que les cles API sont detectees. Deux providers sont configures :
- OpenAI : Utilise pour la generation d’images (DALL-E 3) et le chat (GPT-5-mini). La cle est stockee dans
OPENAI_API_KEY. - OpenRouter : Route optionnelle vers d’autres modèles (Claude 3.5 Sonnet). Permet de comparer les résultats entre providers.
Si une cle est marquee “Manquante”, le notebook fonctionnera partiellement. La generation d’images via DALL-E 3 necessite imperativement OPENAI_API_KEY.
Moteur de workflows creatifs
Le Creative Workflow Engine orchestre des pipelines multi-étapes : concept art, style transfer, variations iteratives, et composition assistee par IA.
# Classe Creative Workflow Engine
class CreativeWorkflowEngine:
"""Moteur de workflows créatifs multi-étapes"""
def __init__(self, api_configs: Dict):
self.api_configs = api_configs
self.openai_client = None
self.openrouter_client = None
# Templates de styles créatifs
self.style_templates = {
'modern': {
'description': 'clean, contemporary design with bold colors and geometric shapes',
'colors': ['#2196F3', '#4CAF50', '#FF9800', '#E91E63'],
'keywords': ['minimalist', 'sleek', 'contemporary', 'geometric', 'bold']
},
'vintage': {
'description': 'retro aesthetic with warm tones and classic typography',
'colors': ['#8B4513', '#D2691E', '#F4A460', '#DEB887'],
'keywords': ['retro', 'classic', 'nostalgic', 'warm', 'textured']
},
'minimalist': {
'description': 'clean, simple design with lots of white space',
'colors': ['#FFFFFF', '#F5F5F5', '#E0E0E0', '#9E9E9E'],
'keywords': ['simple', 'clean', 'spacious', 'elegant', 'subtle']
},
'artistic': {
'description': 'creative, expressive design with vibrant colors and organic shapes',
'colors': ['#9C27B0', '#E91E63', '#FF5722', '#FFC107'],
'keywords': ['creative', 'expressive', 'vibrant', 'organic', 'imaginative']
},
'professional': {
'description': 'corporate, trustworthy design with blue and gray tones',
'colors': ['#1976D2', '#424242', '#757575', '#ECEFF1'],
'keywords': ['professional', 'corporate', 'trustworthy', 'reliable', 'structured']
}
}
self._init_clients()
def _init_clients(self):
"""Initialise les clients API"""
try:
if self.api_configs['openai']['api_key']:
self.openai_client = OpenAI(
api_key=self.api_configs['openai']['api_key'],
base_url=self.api_configs['openai']['base_url']
)
print("Client OpenAI initialisé")
if self.api_configs['openrouter']['api_key']:
self.openrouter_client = OpenAI(
api_key=self.api_configs['openrouter']['api_key'],
base_url=self.api_configs['openrouter']['base_url']
)
print("Client OpenRouter initialisé")
except Exception as e:
logger.error(f"Erreur initialisation clients: {str(e)}")
def generate_creative_prompt(self, concept: str, stage: str, style: str,
additional_context: str = "") -> str:
"""Génère un prompt créatif optimisé selon le stage du workflow"""
style_template = self.style_templates.get(style, self.style_templates['modern'])
stage_prompts = {
'concept_analysis': f"Conceptual visualization of {concept}",
'sketch_generation': f"Detailed sketch or wireframe showing {concept}, hand-drawn style, architectural blueprint aesthetic",
'refinement': f"Polished, refined version of {concept} with attention to detail and professional finish",
'final_render': f"High-quality, professional rendering of {concept} ready for presentation",
'style_variation': f"Artistic interpretation of {concept} in {style} style",
'storyboard_scene': f"Storyboard panel showing {concept}, clean illustration style for presentation"
}
base_prompt = stage_prompts.get(stage, f"Creative visualization of {concept}")
enhanced_prompt = f"""
Create a {base_prompt}.
Style requirements:
- {style_template['description']}
- Keywords: {', '.join(style_template['keywords'])}
- Professional quality suitable for creative portfolios
- High contrast and visual clarity
Technical specifications:
- Clean, modern aesthetic
- Suitable for digital presentation
- Professional creative industry standard
{additional_context}
Subject: {concept}
Style: {style.capitalize()}
Stage: {stage.replace('_', ' ').title()}
"""
return enhanced_prompt.strip()
async def generate_creative_image(self, prompt: str, size: str = "1024x1024",
quality: str = "high") -> Optional[Dict]:
"""Génère une image créative avec gpt-image-1"""
if not self.openai_client:
raise RuntimeError("Client OpenAI non initialisé")
try:
print(f"Génération image créative...")
response = self.openai_client.images.generate(
model="gpt-image-1",
prompt=prompt,
size=size,
quality=quality,
n=1
)
if response.data and len(response.data) > 0:
# gpt-image-1 retourne b64_json (pas d'URL) : on construit un data-URI
# pour que les <img src="{result['url']}"> en aval restent valides.
_img = response.data[0]
_b64 = getattr(_img, 'b64_json', None)
_url = getattr(_img, 'url', None)
_src = _url if _url else (f"data:image/png;base64,{_b64}" if _b64 else "")
result = {
'url': _src,
'revised_prompt': getattr(_img, 'revised_prompt', prompt),
'timestamp': datetime.now().isoformat(),
'size': size,
'quality': quality,
'model': 'gpt-image-1',
'id': str(uuid.uuid4())
}
print(f"Image générée: {result['id']}")
return result
except Exception as e:
logger.error(f"Erreur génération image: {str(e)}")
return None
# Initialisation du moteur créatif
creative_engine = CreativeWorkflowEngine(api_configs)
print("Moteur de workflows créatifs initialisé")Client OpenAI initialisé
Client OpenRouter initialisé
Moteur de workflows créatifs initialisé
Exercice 1 : Inventaire des templates de styles
Objectif : Ecrire une fonction qui extrait un resume structurel de tous les styles disponibles dans le CreativeWorkflowEngine (noms, palettes, mots-cles).
Contexte : Le moteur creatif contient 5 styles preconfigures (modern, vintage, minimalist, artistic, professional). Avant de lancer un workflow, il est important de connaitre les options disponibles pour choisir le style adapte a l’audience cible.
Indices : - Accedez aux templates via creative_engine.style_templates - Chaque style contient : description, colors (liste hex), keywords (liste de strings) - Retournez une liste de dictionnaires avec les informations cles
# Exercice 1 : Inventaire des templates de styles
# Objectif : Extraire un resume de tous les styles du CreativeWorkflowEngine
# TODO etudiant : implementer inventory_styles
def inventory_styles(engine) -> list:
"""
Parcourt les style_templates et retourne un inventaire structure.
# Etape 1 : Iterer sur engine.style_templates avec .items()
# Etape 2 : Pour chaque style, extraire : nombre de couleurs, nombre de keywords
# Etape 3 : Construire une liste de dictionnaires avec le nom et les metriques
Retourne: [{"name": str, "num_colors": int, "num_keywords": int,
"description_preview": str}]
"""
# Indice : for name, template in engine.style_templates.items():
# Indice : template["description"][:50] pour un apercu
result = None # TODO etudiant : implementer l'inventaire
return result
# Tester avec le moteur creatif initialise
styles = inventory_styles(creative_engine)
if styles:
for s in styles:
print(f"Style: {s.get('name', 'N/A')} | "
f"Couleurs: {s.get('num_colors', 0)} | "
f"Mots-cles: {s.get('num_keywords', 0)} | "
f"Apercu: {s.get('description_preview', 'N/A')}")
print("Exercice a completer")Exercice a completer
Interface interactive pour l’exploration creative
La cellule suivante construit une interface graphique avec ipywidgets qui expose les paramètres du moteur creatif. Cette approche offre deux avantages :
- Exploration rapide : La demonstration integree genere une image avec un concept predefini pour valider que l’API fonctionne sans avoir a configurer chaque paramètre.
- Parametrisation progressive : Plutot que de modifier le code source, l’utilisateur ajuste les paramètres (workflow, style, audience, qualite) via des menus deroulants et des cases a cocher.
L’interface est construite avec un pattern VBox/HBox qui organise les contrôles verticalement et horizontalement. Les boutons declenchent des fonctions asynchrones (asyncio.create_task) car les appels DALL-E 3 sont non-bloquants.
# Interface utilisateur interactive
print("\n" + "=" * 60)
print("INTERFACE WORKFLOWS CRÉATIFS")
print("=" * 60)
# Widgets de configuration
workflow_widget = widgets.Dropdown(
options=[
('Concept → Image', 'concept_to_image'),
('Variations Stylistiques', 'style_variations'),
('Story-board', 'storyboard'),
('Identité de Marque', 'brand_identity')
],
value=workflow_type,
description='Workflow:'
)
concept_widget = widgets.Textarea(
value=concept_description,
description='Concept:',
placeholder='Décrivez votre concept créatif en détail...',
rows=3
)
style_widget = widgets.Dropdown(
options=['modern', 'vintage', 'minimalist', 'artistic', 'professional'],
value=creative_style,
description='Style:'
)
audience_widget = widgets.Dropdown(
options=['students', 'professionals', 'educators', 'general'],
value=target_audience,
description='Audience:'
)
quality_widget = widgets.Dropdown(
options=[('Standard', 'medium'), ('Haute qualité', 'high')],
value=image_quality,
description='Qualité:'
)
size_widget = widgets.Dropdown(
options=[
('Carré 1024x1024', '1024x1024'),
('Portrait 1024x1536', '1024x1536'),
('Paysage 1536x1024', '1536x1024')
],
value=image_size,
description='Format:'
)
# Options avancées
sketch_checkbox = widgets.Checkbox(
value=enable_sketch_phase,
description='Inclure phase esquisse'
)
refinement_checkbox = widgets.Checkbox(
value=enable_refinement,
description='Inclure raffinement'
)
variations_checkbox = widgets.Checkbox(
value=enable_variations,
description='Générer variations'
)
gallery_checkbox = widgets.Checkbox(
value=create_gallery,
description='Créer galerie'
)
# Boutons d'action
execute_button = widgets.Button(
description='Exécuter Workflow',
button_style='primary',
layout=widgets.Layout(width='200px')
)
demo_button = widgets.Button(
description='Demo Rapide',
button_style='success',
layout=widgets.Layout(width='200px')
)
output_area = widgets.Output()
# Layout de l'interface
ui_layout = widgets.VBox([
widgets.HTML("<h3>Interface de Workflows Créatifs</h3>"),
widgets.HBox([workflow_widget, style_widget]),
concept_widget,
widgets.HBox([audience_widget, quality_widget, size_widget]),
widgets.HTML("<h4>Options Avancées:</h4>"),
widgets.HBox([sketch_checkbox, refinement_checkbox]),
widgets.HBox([variations_checkbox, gallery_checkbox]),
widgets.HTML("<h4>Actions:</h4>"),
widgets.HBox([demo_button, execute_button]),
output_area
])
# Fonction de démonstration rapide
async def execute_demo_workflow():
"""Démonstration rapide avec un exemple prédéfini"""
with output_area:
clear_output(wait=True)
print("DÉMONSTRATION WORKFLOW CRÉATIF")
print("=" * 50)
# Paramètres de démonstration
demo_concept = "futuristic learning environment with holographic displays"
demo_style = "modern"
demo_audience = "students"
print(f"Concept demo: {demo_concept}")
print(f"Style: {demo_style}")
print(f"Audience: {demo_audience}")
print("-" * 50)
try:
# Génération simple pour demo
prompt = creative_engine.generate_creative_prompt(
concept=demo_concept,
stage='final_render',
style=demo_style,
additional_context="Demo version for creative workflow showcase"
)
print("Génération image de démonstration...")
demo_result = await creative_engine.generate_creative_image(
prompt=prompt,
size="1024x1024",
quality="medium"
)
if demo_result:
print("\nRÉSULTAT DÉMONSTRATION")
print("=" * 30)
# Afficher l'image de demo
display(HTML(f'''
<div style="text-align: center; padding: 20px; background: #f0f8ff; border-radius: 10px; margin: 15px 0;">
<h3 style="color: #333; margin-bottom: 15px;">Démonstration Workflow Créatif</h3>
<img src="{demo_result['url']}" alt="Demo creative workflow" style="max-width: 100%; height: auto; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);">
<p style="margin-top: 15px; color: #666;"><strong>Concept:</strong> {demo_concept}</p>
<p style="color: #666;"><strong>Style:</strong> {demo_style.capitalize()} | <strong>ID:</strong> {demo_result['id']}</p>
</div>
'''))
print(f"Démonstration réussie!")
print(f"Image ID: {demo_result['id']}")
print(f"Format: {demo_result['size']}")
print(f"\nMaintenant, personnalisez vos paramètres et utilisez 'Exécuter Workflow' pour vos propres créations!")
else:
print("Échec de la démonstration")
except Exception as e:
print(f"Erreur démonstration: {str(e)}")
# Fonction principale de workflow
async def execute_creative_workflow():
"""Exécute le workflow créatif sélectionné"""
with output_area:
clear_output(wait=True)
workflow_type = workflow_widget.value
concept = concept_widget.value
style = style_widget.value
audience = audience_widget.value
if not concept.strip():
print("Veuillez entrer une description du concept")
return
print(f"Démarrage workflow: {workflow_type}")
print(f"Concept: {concept[:100]}{'...' if len(concept) > 100 else ''}")
print(f"Style: {style} | Audience: {audience}")
print(f"Format: {size_widget.value} | Qualité: {quality_widget.value}")
print("-" * 60)
try:
if workflow_type == 'concept_to_image':
await execute_concept_to_image_workflow(concept, style, audience)
elif workflow_type == 'style_variations':
await execute_style_variations_workflow(concept, style, audience)
elif workflow_type == 'storyboard':
await execute_storyboard_workflow(concept, style, audience)
elif workflow_type == 'brand_identity':
await execute_brand_identity_workflow(concept, style, audience)
except Exception as e:
print(f"Erreur workflow: {str(e)}")
logger.error(f"Erreur exécution workflow: {str(e)}")
# Handlers pour les boutons
def on_execute_clicked(b):
asyncio.create_task(execute_creative_workflow())
def on_demo_clicked(b):
asyncio.create_task(execute_demo_workflow())
execute_button.on_click(on_execute_clicked)
demo_button.on_click(on_demo_clicked)
# Affichage de l'interface
display(ui_layout)
print("\nInterface prête! Essayez d'abord la 'Demo Rapide' puis configurez vos paramètres.")
============================================================
INTERFACE WORKFLOWS CRÉATIFS
============================================================
Interface prête! Essayez d'abord la 'Demo Rapide' puis configurez vos paramètres.
Workflows specialises : concept, styles, story-board, marque
La cellule suivante implemente les quatre workflows specialises du moteur creatif. Chaque fonction execute_*_workflow orchestre differemment les primitives du moteur (generate_creative_prompt + generate_creative_image) :
execute_concept_to_image_workflow: Pipeline séquentiel en 3 étapes (base, esquisse optionnelle, raffinement optionnel). Chaque étape enrichit le prompt avec du contexte supplementaire.execute_style_variations_workflow: Boucle sur N styles différents avec le même concept. Compare les résultats dans une galerie HTML avec indication visuelle du style de base.execute_storyboard_workflowetexecute_brand_identity_workflow: Versions simplifiees generant une seule image chacune.
Notez l’utilisation de display(HTML(...)) pour generer des galleries visuelles avec du CSS inline (gradients, grid layout, border-radius).
# Implémentation des workflows spécialisés
print("\n" + "=" * 60)
print("IMPLÉMENTATION DES WORKFLOWS SPÉCIALISÉS")
print("=" * 60)
async def execute_concept_to_image_workflow(concept: str, style: str, audience: str):
"""Workflow Concept → Image finale"""
print("WORKFLOW: Concept → Image")
stages = []
# Étape 1: Version de base
print("\nÉTAPE 1: Génération de base")
base_prompt = creative_engine.generate_creative_prompt(
concept=concept,
stage='final_render',
style=style,
additional_context=f"Primary version for {audience}"
)
base_result = await creative_engine.generate_creative_image(
prompt=base_prompt,
size=size_widget.value,
quality=quality_widget.value
)
if base_result:
stages.append(('Base', base_result, base_prompt))
print(f"Version de base générée")
# Étape 2: Esquisse (si activée)
if sketch_checkbox.value and base_result:
print("\nÉTAPE 2: Génération esquisse")
sketch_prompt = creative_engine.generate_creative_prompt(
concept=concept,
stage='sketch_generation',
style=style,
additional_context="Wireframe and structural elements"
)
sketch_result = await creative_engine.generate_creative_image(
prompt=sketch_prompt,
size=size_widget.value,
quality="medium"
)
if sketch_result:
stages.append(('Esquisse', sketch_result, sketch_prompt))
print(f"Esquisse générée")
# Étape 3: Raffinement (si activé)
if refinement_checkbox.value and base_result:
print("\nÉTAPE 3: Raffinement")
refined_prompt = creative_engine.generate_creative_prompt(
concept=concept,
stage='refinement',
style=style,
additional_context="Enhanced version with professional polish"
)
refined_result = await creative_engine.generate_creative_image(
prompt=refined_prompt,
size=size_widget.value,
quality=quality_widget.value
)
if refined_result:
stages.append(('Raffiné', refined_result, refined_prompt))
print(f"Version raffinée générée")
# Affichage des résultats
if stages:
print("\n" + "=" * 50)
print("RÉSULTATS DU WORKFLOW CONCEPT → IMAGE")
print("=" * 50)
gallery_html = f"""
<div style="padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; margin: 20px 0; color: white;">
<h2 style="text-align: center; margin-bottom: 10px;">Workflow Concept → Image</h2>
<p style="text-align: center; opacity: 0.9; margin-bottom: 30px;"><strong>Concept:</strong> {concept}</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px;">
"""
for stage_name, result, prompt in stages:
gallery_html += f"""
<div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.2);">
<h3 style="color: #fff; margin-top: 0; text-align: center;">{stage_name}</h3>
<img src="{result['url']}" alt="{stage_name}" style="width: 100%; height: 250px; object-fit: cover; border-radius: 8px; margin-bottom: 15px;">
<p style="font-size: 0.9em; opacity: 0.8; margin: 5px 0; text-align: center;">ID: {result['id']}</p>
<p style="font-size: 0.8em; opacity: 0.7; margin: 0; text-align: center;">{result['size']} | {result['quality']}</p>
</div>
"""
gallery_html += """
</div>
</div>
"""
display(HTML(gallery_html))
print(f"Workflow terminé: {len(stages)} étapes générées")
else:
print("Aucune image générée avec succès")
async def execute_style_variations_workflow(concept: str, base_style: str, audience: str):
"""Workflow variations stylistiques"""
print("WORKFLOW: Variations stylistiques")
# Styles pour variations
all_styles = ['modern', 'vintage', 'minimalist', 'artistic', 'professional']
if base_style in all_styles:
all_styles.remove(base_style)
# Ajouter le style de base en premier
variation_styles = [base_style] + all_styles[:style_variations_count-1]
print(f" Styles à générer: {', '.join(variation_styles)}")
variations = []
for i, style in enumerate(variation_styles, 1):
print(f"\n[{i}/{len(variation_styles)}] Génération style: {style}")
variation_prompt = creative_engine.generate_creative_prompt(
concept=concept,
stage='style_variation',
style=style,
additional_context=f"Variation in {style} style for {audience}"
)
variation_result = await creative_engine.generate_creative_image(
prompt=variation_prompt,
size=size_widget.value,
quality=quality_widget.value
)
if variation_result:
variations.append((style, variation_result, variation_prompt, i == 1))
print(f"Style {style} généré")
else:
print(f"Échec style {style}")
# Affichage galerie
if variations:
print("\n" + "=" * 50)
print("GALERIE DE VARIATIONS STYLISTIQUES")
print("=" * 50)
gallery_html = f"""
<div style="padding: 25px; background: linear-gradient(45deg, #ff9a9e 0%, #fecfef 50%, #fecfef 100%); border-radius: 20px; margin: 20px 0;">
<h2 style="text-align: center; color: #333; margin-bottom: 15px;">Galerie de Variations Stylistiques</h2>
<p style="text-align: center; color: #555; margin-bottom: 30px; font-style: italic;">Concept: {concept}</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px;">
"""
for style, result, prompt, is_base in variations:
base_indicator = "BASE" if is_base else "VARIATION"
border_color = "#ff4757" if is_base else "#5352ed"
gallery_html += f"""
<div style="background: white; padding: 20px; border-radius: 15px; box-shadow: 0 8px 25px rgba(0,0,0,0.1); text-align: center; border: 3px solid {border_color};">
<h3 style="color: #333; margin-top: 0; margin-bottom: 10px;">{base_indicator}</h3>
<h4 style="color: {border_color}; margin: 0 0 15px 0;">{style.upper()}</h4>
<img src="{result['url']}" alt="{style} variation" style="width: 100%; height: 220px; object-fit: cover; border-radius: 10px; margin-bottom: 15px;">
<p style="font-size: 0.85em; color: #666; margin: 5px 0;">{result['id'][:8]}...</p>
<p style="font-size: 0.8em; color: #999; margin: 0;">{result['size']}</p>
</div>
"""
gallery_html += """
</div>
<p style="text-align: center; margin-top: 20px; color: #666; font-size: 0.9em;">Cliquez sur une image pour l'agrandir</p>
</div>
"""
display(HTML(gallery_html))
print(f"Galerie générée: {len(variations)} variations")
else:
print("Aucune variation générée avec succès")
async def execute_storyboard_workflow(concept: str, style: str, audience: str):
"""Workflow story-board (version simplifiée)"""
print("WORKFLOW: Story-board")
print("Version simplifiée - Génération d'une scène représentative")
storyboard_prompt = creative_engine.generate_creative_prompt(
concept=f"storyboard scene representing {concept}",
stage='storyboard_scene',
style=style,
additional_context=f"Visual narrative panel for {audience}, {narrative_style} style presentation"
)
storyboard_result = await creative_engine.generate_creative_image(
prompt=storyboard_prompt,
size=size_widget.value,
quality=quality_widget.value
)
if storyboard_result:
print("\nSTORY-BOARD GÉNÉRÉ")
print("=" * 30)
display(HTML(f'''
<div style="padding: 25px; background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%); border-radius: 15px; margin: 20px 0; color: white;">
<h2 style="text-align: center; margin-bottom: 15px;">Story-board Scene</h2>
<p style="text-align: center; opacity: 0.9; margin-bottom: 25px;"><strong>Concept:</strong> {concept}</p>
<div style="text-align: center;">
<img src="{storyboard_result['url']}" alt="Storyboard scene" style="width: 100%; max-width: 600px; height: auto; border-radius: 10px; border: 3px solid rgba(255,255,255,0.3);">
<p style="margin-top: 15px; opacity: 0.8;">Style: {narrative_style.capitalize()} | Audience: {audience.capitalize()}</p>
<p style="opacity: 0.7; font-size: 0.9em;">ID: {storyboard_result['id']}</p>
</div>
</div>
'''))
print(f"Story-board généré: {storyboard_result['id']}")
else:
print("Échec génération story-board")
async def execute_brand_identity_workflow(concept: str, style: str, audience: str):
"""Workflow identité de marque (version simplifiée)"""
print("WORKFLOW: Identité de marque")
print("Version simplifiée - Génération logo/identité visuelle")
brand_prompt = creative_engine.generate_creative_prompt(
concept=f"brand identity and logo for {concept}",
stage='final_render',
style=style,
additional_context=f"Professional brand identity design for {audience}, clean logo design, corporate identity"
)
brand_result = await creative_engine.generate_creative_image(
prompt=brand_prompt,
size=size_widget.value,
quality=quality_widget.value
)
if brand_result:
print("\nIDENTITÉ DE MARQUE GÉNÉRÉE")
print("=" * 35)
display(HTML(f'''
<div style="padding: 25px; background: linear-gradient(45deg, #a29bfe 0%, #6c5ce7 100%); border-radius: 15px; margin: 20px 0; color: white;">
<h2 style="text-align: center; margin-bottom: 15px;">Identité de Marque</h2>
<p style="text-align: center; opacity: 0.9; margin-bottom: 25px;"><strong>Concept:</strong> {concept}</p>
<div style="text-align: center;">
<img src="{brand_result['url']}" alt="Brand identity" style="width: 100%; max-width: 600px; height: auto; border-radius: 10px; border: 3px solid rgba(255,255,255,0.3);">
<p style="margin-top: 15px; opacity: 0.8;">Style: {style.capitalize()} | Audience: {audience.capitalize()}</p>
<p style="opacity: 0.7; font-size: 0.9em;">ID: {brand_result['id']}</p>
</div>
</div>
'''))
print(f"Identité de marque générée: {brand_result['id']}")
else:
print("Échec génération identité de marque")
print("Workflows spécialisés implémentés")
============================================================
IMPLÉMENTATION DES WORKFLOWS SPÉCIALISÉS
============================================================
Workflows spécialisés implémentés
# Demonstration non-interactive : execute un workflow creatif SANS l'interface widgets.
# Utile pour l'execution en batch (Papermill/CI) et garantit que le moteur gpt-image-1
# produit une vraie image dans les sorties du notebook (regle C.2 - outputs reels).
import base64
from io import BytesIO
async def _run_non_interactive_demo():
"""Execute le workflow concept -> image de facon non-interactive."""
concept = "futuristic learning environment with holographic displays"
style = "modern"
prompt = creative_engine.generate_creative_prompt(
concept=concept,
stage="final_render",
style=style,
additional_context="Non-interactive demo for batch execution"
)
print(f"Concept: {concept}")
print(f"Style: {style}")
print("Generation gpt-image-1 en cours...")
result = await creative_engine.generate_creative_image(
prompt=prompt,
size="1024x1024",
quality="low" # qualite reduite pour minimiser cout/temps (image reelle gpt-image-1)
)
return result
# Jupyter/papermill kernel : await de haut niveau (le kernel a deja une event loop active,
# asyncio.run() echouerait avec "cannot be called from a running event loop").
try:
_demo_result = await _run_non_interactive_demo()
if _demo_result and _demo_result.get("url"):
_src = _demo_result["url"]
_b64 = _src.split(",", 1)[1] if _src.startswith("data:") else _src
_demo_img = PILImage.open(BytesIO(base64.b64decode(_b64)))
print(f"Image generee (gpt-image-1): {_demo_img.size[0]}x{_demo_img.size[1]} | ID: {_demo_result['id']}")
display(_demo_img)
else:
print("Aucune image retournee par le moteur creatif.")
except Exception as e:
print(f"Demonstration non-interactive: {type(e).__name__}: {e}")Concept: futuristic learning environment with holographic displays
Style: modern
Generation gpt-image-1 en cours...
Génération image créative...
Image générée: c3df9a40-29c6-4b61-81b9-7bb30bbb54ed
Image generee (gpt-image-1): 1024x1024 | ID: c3df9a40-29c6-4b61-81b9-7bb30bbb54ed

Exercice 2 : Analyseur de structure de prompt selon le stage
Objectif : Implementer une fonction qui genere des prompts pour tous les stages d’un même concept, puis analyse les différences de longueur et de vocabulaire entre chaque stage.
Contexte : La méthode generate_creative_prompt() produit des prompts différents selon le stage (concept_analysis, sketch_generation, refinement, final_render). Comprendre ces différences permet d’optimiser le pipeline de generation.
Indices : - Les stages sont : concept_analysis, sketch_generation, refinement, final_render, style_variation, storyboard_scene - Appelez creative_engine.generate_creative_prompt(concept, stage, style) pour chaque stage - Comparez la longueur en caractères et le nombre de mots uniques
# Exercice 2 : Analyseur de structure de prompt selon le stage
# Objectif : Comparer les prompts generes pour chaque stage d'un workflow
# TODO etudiant : implementer analyze_prompt_stages
def analyze_prompt_stages(engine, concept: str, style: str) -> dict:
"""
Genere et analyse les prompts pour chaque stage du pipeline creatif.
# Etape 1 : Definir la liste des stages valides
# Etape 2 : Generer le prompt pour chaque stage via engine.generate_creative_prompt
# Etape 3 : Pour chaque prompt, calculer : longueur (caracteres), nombre de mots,
# nombre de mots uniques (apres .lower() et .split())
# Etape 4 : Identifier le stage qui produit le prompt le plus long et le plus court
Retourne: {"stages": [{"stage": str, "length": int, "word_count": int,
"unique_words": int}], "longest_stage": str, "shortest_stage": str}
"""
# Indice : stages = ["concept_analysis", "sketch_generation", "refinement",
# "final_render", "style_variation", "storyboard_scene"]
result = None # TODO etudiant : implementer l'analyse
return result
# Tester avec un concept
analysis = analyze_prompt_stages(creative_engine, "AI-powered classroom", "modern")
if analysis:
for s in analysis.get("stages", []):
print(f" {s.get('stage', 'N/A')}: {s.get('length', 0)} chars, "
f"{s.get('unique_words', 0)} mots uniques")
print(f" Plus long: {analysis.get('longest_stage', 'N/A')}")
print("Exercice a completer")Exercice a completer
Interpretation des résultats des workflows
Chaque workflow specialise suit un pattern commun mais avec des objectifs différents :
Concept vers Image : Le pipeline le plus complet. Il decompose la creation en étapes successives (base, esquisse, raffinement), ce qui permet de corriger la direction artistique a chaque étape plutot que de tout rejouer depuis zero.
Variations stylistiques : Ce workflow illustre la puissance de la separation concept/style. Le même concept est envoye a DALL-E 3 avec des directives stylistiques différentes (modern, vintage, minimalist…). Le résultat montre comment le style influence la composition, les couleurs et la mise en page.
Story-board et Identite de marque : Versions simplifiees qui generent une seule image. En production, le story-board produirait N scenes coherentes, et l’identite de marque genererait un kit complet (logo, couleur, typographie).
Le point commun est l’utilisation de generate_creative_prompt() qui construit des prompts différents selon le stage (étape du pipeline) plutot que d’utiliser un prompt unique pour tout le processus.
Conclusion - Workflows Creatifs avec l’IA
Ce que nous avons construit
Ce notebook a mis en place une architecture complete de pipelines creatifs multi-étapes orchestres par IA. L’approche centrale est la decomposition d’un concept abstrait en étapes visuelles progressives : analyse conceptuelle, esquisse structurelle, raffinement et rendu final.
Architecture du moteur creatif
Le CreativeWorkflowEngine illustre un pattern d’architecture fondamental en production GenAI :
| Composant | Rôle | Point cle |
|---|---|---|
style_templates |
Dictionnaire de styles | Chaque style définit palette + mots-cles |
generate_creative_prompt() |
Adaptation prompt selon l’étape | Le même concept produit des prompts différents par phase |
generate_creative_image() |
Appel DALL-E 3 | Gestion d’erreur et metadata |
| Workflows specialises | Orchestration par cas d’usage | Chaque workflow combine les étapes differemment |
Concepts techniques a retenir
Prompt engineering contextuel : Un prompt de sketch n’est pas un prompt de rendu final. Le moteur adapte le vocabulaire, les specifications techniques et le style selon l’étape du pipeline.
Decomposition en étapes : Plutot que de demander une image finale directement, le pipeline genere des versions intermediaires (esquisse, raffinement) qui servent de points de contrôle qualite.
Separation concept/style : Le moteur separe le contenu (le concept decrit par l’utilisateur) du traitement visuel (le style selectionne). Cette separation permet de generer N variations d’un même concept sans reecrire le prompt.
Interface reactive vs pipeline : L’interface ipywidgets permet l’exploration interactive, mais les mêmes fonctions (
execute_concept_to_image_workflow, etc.) peuvent etre appelees programmatiquement pour du traitement en batch.
Comparaison des workflows
| Workflow | Force | Limite | Cas d’usage |
|---|---|---|---|
| Concept vers Image | Pipeline progressif | Consomme 2-3 appels API | Prototypage visuel |
| Variations stylistiques | Exploration creative | Cout eleve (N styles) | Direction artistique |
| Story-board | Narration visuelle | Version simplifiee | Presentations |
| Identite de marque | Cohérence visuelle | Un seul rendu | Branding projets |
Pour aller plus loin
Le notebook 04-3-Production-Integration aborde l’integration de ces workflows dans des pipelines de production avec automatisation, gestion des couts API et optimisation des prompts.
Auto-evaluation
Exercice : Pipeline Créatif Multi-Variations avec Branding
Durée estimée : 30-35 minutes
Objectif
Construire un pipeline créatif complet qui génère des variations d’un concept en plusieurs styles, avec application de branding cohérent (couleurs, logo, typographie).
Instructions
- Créer une fonction
build_branded_creative_campaignqui :- Prend un concept de base et une identité de marque
- Génère 5 variations stylistiques différentes
- Applique une palette de couleurs de marque cohérente
- Crée une séquence logique (esquisse → raffinement → final)
- Implémenter un système de gestion de marque :
- Palette de couleurs personnalisable
- Directives stylistiques (minimaliste, vibrant, corporate)
- Cohérence visuelle entre toutes les variations
- Créer une galerie comparative avec métriques de qualité
Indices
- Étendez
CreativeWorkflowEngineavec des méthodes de branding - Utilisez les styles existants (modern, vintage, minimalist, etc.)
- Pour la cohérence, générez d’abord une “image de référence” de marque
- Créez une classe
BrandIdentitypour gérer les couleurs et directives - Utilisez PIL/Pillow pour l’application de couleurs si nécessaire
Code de départ
# TODO: Créer la classe d'identité de marque
class BrandIdentity:
"""
Gère l'identité visuelle d'une marque pour les générations créatives.
"""
def __init__(self, brand_name, color_palette, style_directives):
"""
Args:
brand_name: Nom de la marque
color_palette: Liste de couleurs hex (ex: ['#2196F3', '#4CAF50'])
style_directives: Dictionnaire avec directives (minimalist, bold, etc.)
"""
pass
def get_brand_prompt_enhancement(self, base_prompt):
"""
Ajoute des directives de marque à un prompt existant.
Returns:
Prompt enrichi avec les directives de marque
"""
pass
# TODO: Créer la fonction de campagne créative brandée
async def build_branded_creative_campaign(
concept,
brand_identity,
num_variations=5,
workflow_engine=None
):
"""
Génère une campagne créative complète avec branding cohérent.
Args:
concept: Concept de base pour la campagne
brand_identity: Objet BrandIdentity avec les directives
num_variations: Nombre de variations à générer
workflow_engine: Instance de CreativeWorkflowEngine
Returns:
Dictionnaire avec: variations, metadata, quality_metrics
"""
pass
# TODO: Créer une fonction de galerie comparative
def create_comparison_gallery(campaign_results, brand_name):
"""
Crée une galerie HTML comparative des variations générées.
Args:
campaign_results: Résultats de build_branded_creative_campaign
brand_name: Nom de la marque pour le titre
Returns:
HTML string pour affichage
"""
pass
# TODO: Tester avec une marque fictive
# Exemple: marque "EcoLearn" avec palette verte/bleue, style éducatif moderne
passCritères de succès
Exercice 3 : Comparateur de styles visuels
Objectif : Implementer une fonction qui compare les palettes de couleurs et les mots-cles de deux styles du CreativeWorkflowEngine pour determiner leur compatibilite visuelle.
Contexte : Quand on créé des variations stylistiques, certaines combinaisons de styles sont plus coherentes que d’autres. Par exemple, “modern” et “minimalist” partagent des mots-cles similaires, tandis que “vintage” et “minimalist” sont plus contrastes.
Indices : - Accedez aux styles via creative_engine.style_templates - Comparez les colors (listes hex) et les keywords de chaque style - Utilisez les opérations sur les set pour trouver les intersections
# Exercice 3 : Comparateur de styles visuels
# Objectif : Comparer les palettes et mots-cles de deux styles
# TODO etudiant : implementer compare_style_compatibility
def compare_style_compatibility(engine, style_a: str, style_b: str) -> dict:
"""
Compare deux styles et retourne leur compatibilite.
# Etape 1 : Extraire les keywords de chaque style depuis engine.style_templates
# Etape 2 : Calculer l'intersection des keywords (mots en commun)
# Etape 3 : Calculer un score de compatibilite (intersection / union)
Retourne: {"style_a": str, "style_b": str,
"shared_keywords": list, "compatibility_score": float}
"""
# Indice : set(keywords_a) & set(keywords_b) pour l'intersection
# Indice : len(intersection) / len(union) pour le score Jaccard
result = None # TODO etudiant : implementer la comparaison
return result
# Tester avec des paires de styles
pairs = [("modern", "minimalist"), ("vintage", "artistic"), ("professional", "modern")]
for a, b in pairs:
comp = compare_style_compatibility(creative_engine, a, b)
if comp:
print(f"{a} vs {b}: score={comp.get('compatibility_score', 0):.2f}")
print("Exercice a completer")Exercice a completer