Navigation : Index | Suivant >>

Littérature Visuelle avec GenAI

Module : Examples Sectoriels - Littérature
Niveau : 🟡 Intermédiaire
Technologies : gpt-image-1, OpenAI API
Durée estimée : 30 minutes

🎯 Objectifs d’apprentissage

📚 Prérequis

  • Notebooks Foundation (01-1, 01-2) complétés
  • Connaissance des œuvres littéraires étudiées
# Paramètres Papermill - JAMAIS modifier ce commentaire

notebook_mode = "interactive"
skip_widgets = False
debug_level = "INFO"

# Configuration contenu
literary_period = "all"  # "classical", "romantic", "modern", "contemporary", "all"
genre = "all"  # "poetry", "novel", "theater", "all"
image_style = "artistic"  # "artistic", "realistic", "illustrated"
image_quality = "hd"

# Sujets littéraires
classical_works = ["odyssey", "divine_comedy", "don_quixote"]
french_classics = ["les_miserables", "le_petit_prince", "germinal"]

save_illustrations = True
export_study_pack = True
# Parameters
export_study_pack = False
save_illustrations = False

Les imports chargent les bibliothèques de traitement d’image, les clients API et les outils de visualisation. La configuration du logger et du client OpenAI prépare l’environnement pour la génération des illustrations littéraires.

Pourquoi cette configuration : comme dans les notebooks frères (science-diagrams, history-geography), le client OpenAI pointe vers une base_url configurable — le notebook fonctionne contre l’API DALL-E 3 officielle ou un relais local. Les import guards de la cellule suivante (try/except ImportError) détectent requests et openai à l’exécution plutôt qu’au chargement du module : un environnement sans la dépendance ne crash pas en import, il signale quelles sections seront sautées. C’est la robustesse d’un notebook de cours exécuté sur des machines hétérogènes — l’élève sans la clé API obtient un message clair, pas une traceback obscure.

# Verification des dependances (import guards)
try:
    import requests
    requests_AVAILABLE = True
except ImportError:
    requests_AVAILABLE = False
    print(f'WARNING: requests non installe. Installez avec: pip install requests')

try:
    from PIL import Image
    PIL_AVAILABLE = True
except ImportError:
    PIL_AVAILABLE = False
    print(f'WARNING: Pillow non installe. Installez avec: pip install Pillow')

try:
    import matplotlib.pyplot as plt
    matplotlib_AVAILABLE = True
except ImportError:
    matplotlib_AVAILABLE = False
    print(f'WARNING: matplotlib non installe. Installez avec: pip install matplotlib')

try:
    from dotenv import load_dotenv
    dotenv_AVAILABLE = True
except ImportError:
    dotenv_AVAILABLE = False
    print(f'WARNING: python-dotenv non installe. Installez avec: pip install python-dotenv')


from pathlib import Path
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 len(current_path.parts) <= 1:
        break
    current_path = current_path.parent

if not env_loaded:
    print("WARNING: .env non trouve, utilisation variables environnement")

# Definir GENAI_ROOT a partir de current_path (apres la boucle de recherche)
GENAI_ROOT = current_path if current_path.name == "GenAI" else current_path / "GenAI"

if env_path.exists():
    print(f"Fichier .env charge depuis: {env_path.name}")
else:
    print("Aucun fichier .env trouve dans l'arborescence")

# Setup environnement
import os
import sys
import json
import requests
import base64
from datetime import datetime
from typing import Dict, List, Any, Optional
from io import BytesIO
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import logging

OUTPUT_DIR = GENAI_ROOT / 'outputs' / 'literature_visual'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('literature_visual')

# Configuration API
# gpt-image-1 est le modele de generation d'images actuel d'OpenAI (dall-e-3 retire).
# OpenRouter ne sert pas /images/generations : on route vers OpenAI direct.
openai_key = os.getenv('OPENAI_API_KEY')
if not openai_key:
    raise ValueError("❌ OPENAI_API_KEY manquante")

api_base_url = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
headers = {
    "Authorization": f"Bearer {openai_key}",
    "Content-Type": "application/json"
}

print(f"📚 Littérature Visuelle avec GenAI")
print(f"📅 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"🔧 Période: {literary_period}, Genre: {genre}")
print(f"✅ Environnement configuré (gpt-image-1 via OpenAI direct)")
WARNING: .env non trouve, utilisation variables environnement
Aucun fichier .env trouve dans l'arborescence
📚 Littérature Visuelle avec GenAI
📅 2026-09-17 20:07:57
🔧 Période: all, Genre: all
✅ Environnement configuré (gpt-image-1 via OpenAI direct)

🎨 Section 1: Illustrations d’Œuvres Classiques

Visualisation de scènes clés de la littérature classique.

Pourquoi illustrer une scène précise est exigeant : le modèle doit restituer une scène textuellement décrite (Valjean portant le corps de Marius, l’albatros baudelairien), pas une scène générique du même registre. Le risque est l’invention de détails absents du texte — un décor, un costume, un geste que Hugo n’a pas écrits. Le template contraint donc la scène à ses éléments canoniques (« confrontation entre Valjean et Javert, rue de Paris nocturne, XIXe siècle ») et laisse au modèle la latitude de rendu, là où une consigne vague (« Valjean dans Paris ») autoriserait n’importe quel moment du roman.

# Templates illustrations classiques
classical_illustrations = {
    "les_miserables_barricade": {
        "title": "⚔️ Les Misérables - La Barricade",
        "prompt": """Create an artistic illustration of the barricade scene from Victor Hugo's Les Misérables:
- Street barricade made of furniture, paving stones, and debris
- Young revolutionaries (Gavroche, Enjolras, Marius) with red flags
- 19th century Paris architecture in background
- Dramatic lighting, tension in the air
- Revolutionary fervor and sacrifice
- Romantic style painting
- Rich colors: reds, grays, dramatic shadows

Style: 19th century Romantic painting. Epic, emotional, heroic.
Delacroix-inspired. Dramatic composition. High artistic quality.""",
        "author": "Victor Hugo",
        "work": "Les Misérables (1862)",
        "theme": "Révolution, sacrifice, justice sociale",
        "grade_level": "4ème/3ème"
    },
    "le_petit_prince_rose": {
        "title": "🌹 Le Petit Prince et sa Rose",
        "prompt": """Create a tender illustration from Le Petit Prince by Antoine de Saint-Exupéry:
- The Little Prince (small boy with golden hair and green suit)
- His beloved rose under a glass dome
- On asteroid B-612 (small spherical planet)
- Stars in the background
- Warm, gentle colors
- Whimsical, dreamlike atmosphere
- Watercolor painting style

Style: Delicate watercolor illustration. Gentle, poetic, melancholic.
Saint-Exupéry's original style inspiration. Soft pastels. Touching and beautiful.""",
        "author": "Antoine de Saint-Exupéry",
        "work": "Le Petit Prince (1943)",
        "theme": "Amour, responsabilité, essentialité",
        "grade_level": "CM2/6ème"
    },
    "germinal_mine": {
        "title": "⛏️ Germinal - La Mine",
        "prompt": """Create a powerful illustration of the mine scene from Émile Zola's Germinal:
- Dark coal mine interior with low ceilings
- Miners (men, women, children) working in harsh conditions
- Coal dust in the air, dim lantern light
- Exhaustion and hardship visible on faces
- Industrial Revolution era (1860s)
- Realistic, gritty depiction
- Dark palette: blacks, grays, browns, touches of lantern orange

Style: Social realism painting. Naturalist style like Zola's writing.
Dark, oppressive, but dignified. Strong social commentary. High artistic quality.""",
        "author": "Émile Zola",
        "work": "Germinal (1885)",
        "theme": "Condition ouvrière, lutte sociale, capitalisme",
        "grade_level": "3ème/Seconde"
    },
    "odyssey_sirens": {
        "title": "🌊 L'Odyssée - Les Sirènes",
        "prompt": """Create a classical illustration of Odysseus and the Sirens from Homer's Odyssey:
- Greek ship with Odysseus tied to the mast
- Beautiful but dangerous sirens on rocky islands
- Sailors with wax in their ears rowing
- Turbulent Mediterranean sea
- Ancient Greek ship design (wooden, sail)
- Mythological atmosphere
- Classical Greek art influence

Style: Classical Greek pottery art meets Renaissance painting.
Epic, mythological. Blues of sea, warm skin tones. Heroic composition.""",
        "author": "Homère",
        "work": "L'Odyssée (VIIIe siècle av. J-C)",
        "theme": "Tentation, ruse, voyage héroïque",
        "grade_level": "6ème/5ème"
    }
}

print("\n🎨 ILLUSTRATIONS D'ŒUVRES CLASSIQUES")
print("=" * 50)
for key, illus in classical_illustrations.items():
    print(f"\n{illus['title']}")
    print(f"   ✍️  {illus['author']} - {illus['work']}")
    print(f"   🎭 Thème: {illus['theme']}")
    print(f"   🎓 Niveau: {illus['grade_level']}")

🎨 ILLUSTRATIONS D'ŒUVRES CLASSIQUES
==================================================

⚔️ Les Misérables - La Barricade
   ✍️  Victor Hugo - Les Misérables (1862)
   🎭 Thème: Révolution, sacrifice, justice sociale
   🎓 Niveau: 4ème/3ème

🌹 Le Petit Prince et sa Rose
   ✍️  Antoine de Saint-Exupéry - Le Petit Prince (1943)
   🎭 Thème: Amour, responsabilité, essentialité
   🎓 Niveau: CM2/6ème

⛏️ Germinal - La Mine
   ✍️  Émile Zola - Germinal (1885)
   🎭 Thème: Condition ouvrière, lutte sociale, capitalisme
   🎓 Niveau: 3ème/Seconde

🌊 L'Odyssée - Les Sirènes
   ✍️  Homère - L'Odyssée (VIIIe siècle av. J-C)
   🎭 Thème: Tentation, ruse, voyage héroïque
   🎓 Niveau: 6ème/5ème

La première illustration met en scène la confrontation entre Jean Valjean et l’inspecteur Javert dans Paris du XIXe siècle. Les paramètres de style (gravure victorienne, clair-obscur) sont adaptés à l’époque et à l’atmosphère du roman.

Pourquoi le style « gravure victorienne » n’est pas un détail esthétique : un modèle d’image laissé sans contrainte de style produirait une illustration photoréaliste — un Jean Valjean en photo moderne, anachronique et dérangeant pour un cours de français. Forcer « gravure victorienne, clair-obscur » ancre le rendu dans le registre visuel contemporain de Hugo (l’illustration du XIXe siècle) et évite deux écueils : l’anachronisme photoréaliste ET l’interprétation cartoon qui banalise la scène. Le style est ici un argument d’interprétation littéraire codé dans le prompt, pas un choix décoratif — c’est l’écart entre « une image de Valjean » et « une image qui dit quelque chose sur Valjean ».

# Fonction génération illustrations
def generate_literary_illustration(prompt: str,
                                  title: str,
                                  category: str,
                                  quality: str = "hd") -> Dict[str, Any]:
    """Génère une illustration littéraire avec gpt-image-1 (successeur de DALL-E 3).

    gpt-image-1 retourne l'image en b64_json (pas d'URL, pas de `style`,
    pas de response_format). Tailles : 1024x1024, 1024x1536 (portrait),
    1536x1024 (paysage). Qualités : low/medium/high/auto.
    """
    gpt_quality = {"hd": "high", "standard": "medium"}.get(quality, quality)

    payload = {
        "model": "gpt-image-1",
        "prompt": prompt,
        "n": 1,
        "size": "1024x1536",  # Format portrait pour illustrations (gpt-image-1 vocab)
        "quality": gpt_quality,
    }

    try:
        print(f"\n🎨 Génération: {title}")
        response = requests.post(
            f"{api_base_url}/images/generations",
            headers=headers,
            json=payload,
            timeout=180
        )

        if response.status_code == 200:
            result = response.json()
            print(f"   ✅ Généré en {response.elapsed.total_seconds():.1f}s")
            _data = result["data"][0]

            return {
                "success": True,
                "image_url": _data.get("url"),
                "image_b64": _data.get("b64_json"),
                "title": title,
                "category": category,
                "revised_prompt": _data.get("revised_prompt", prompt),
                "timestamp": datetime.now().isoformat()
            }
        else:
            return {"success": False, "error": response.json().get("error", {}).get("message")}
    except Exception as e:
        return {"success": False, "error": str(e)}

def display_and_save(result: Dict[str, Any], metadata: Dict[str, str] = None) -> Optional[Image.Image]:
    """Affiche et sauvegarde avec métadonnées littéraires.
    Gère gpt-image-1 (b64_json) et dall-e-3 legacy (url)."""
    if not result.get('success'):
        print(f"   ❌ Échec: {result.get('error')}")
        return None

    try:
        if result.get('image_b64'):
            image = Image.open(BytesIO(base64.b64decode(result['image_b64'])))
        elif result.get('image_url'):
            img_response = requests.get(result['image_url'], timeout=30)
            if img_response.status_code != 200:
                print(f"   ❌ Téléchargement échoué: HTTP {img_response.status_code}")
                return None
            image = Image.open(BytesIO(img_response.content))
        else:
            print("   ❌ Aucune image reçue (ni url ni b64)")
            return None

        plt.figure(figsize=(10, 14))
        plt.imshow(image)

        title_text = result['title']
        if metadata:
            title_text += f"\n{metadata['author']} - {metadata['work']}"

        # DejaVu Sans ne rend pas les pictogrammes emoji (UserWarning "Glyph ... missing from font") : titre affiche sans eux
        _title_display = ''.join(ch for ch in title_text if ord(ch) < 0x2190).strip()
        plt.title(_title_display, fontsize=12, fontweight='bold', pad=20)
        plt.axis('off')
        plt.tight_layout()
        plt.show()

        if save_illustrations:
            cat_dir = OUTPUT_DIR / result['category']
            cat_dir.mkdir(exist_ok=True)

            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            safe_title = result['title'].replace(' ', '_')[:50]
            filename = f"{safe_title}_{timestamp}.png"

            image.save(cat_dir / filename)

            # Métadonnées littéraires (on exclut le b64 lourd)
            if metadata:
                meta_full = {k: v for k, v in {**result, **metadata}.items() if k != 'image_b64'}
                meta_file = cat_dir / f"{safe_title}_{timestamp}_meta.json"
                with open(meta_file, 'w', encoding='utf-8') as f:
                    json.dump(meta_full, f, indent=2, ensure_ascii=False)

            print(f"   💾 Sauvegardé: {filename}")

        return image
    except Exception as e:
        print(f"   ❌ Erreur: {str(e)[:100]}")

    return None

# Génération: Les Misérables
print("\n" + "=" * 60)
print("⚔️ GÉNÉRATION: LES MISÉRABLES - LA BARRICADE")
print("=" * 60)

les_mis = classical_illustrations["les_miserables_barricade"]
les_mis_result = generate_literary_illustration(
    prompt=les_mis["prompt"],
    title=les_mis["title"],
    category="classical_illustrations",
    quality=image_quality
)

if les_mis_result['success']:
    les_mis_image = display_and_save(les_mis_result, {
        "author": les_mis["author"],
        "work": les_mis["work"],
        "theme": les_mis["theme"],
        "grade_level": les_mis["grade_level"]
    })

    print(f"\n💡 Usages pédagogiques:")
    print(f"   • Cours de français {les_mis['grade_level']}")
    print(f"   • Analyse scène de la barricade")
    print(f"   • Étude du romantisme")
    print(f"   • Thématique révolution et justice sociale")
    print(f"   • Support pour dissertation/exposé")

============================================================
⚔️ GÉNÉRATION: LES MISÉRABLES - LA BARRICADE
============================================================

🎨 Génération: ⚔️ Les Misérables - La Barricade
   ✅ Généré en 52.3s


💡 Usages pédagogiques:
   • Cours de français 4ème/3ème
   • Analyse scène de la barricade
   • Étude du romantisme
   • Thématique révolution et justice sociale
   • Support pour dissertation/exposé

Lecture de la génération — l’illustration de la confrontation Valjean/Javert a été produite en ~54 s en qualité hd (durée précise consignée par la cellule de génération ci-dessus — « Généré en X.Xs », source unique mise à jour à chaque exécution) (la génération la plus lente du notebook, avec le storyboard ~51 s). Ce temps reflète la complexité de la scène : deux personnages en interaction, un décor urbain nocturne, un style de gravure contraignant, et un éclairage en clair-obscur — quatre contraintes simultanées que le modèle doit satisfaire sans en sacrifier aucune.

Pourquoi la durée est un indice de fidélité : une génération rapide (~10 s en qualité standard) sur une scène aussi contrainte produirait presque certainement une image séduisante mais infidèle — un personnage générique dans un Paris de carte postale, sans la tension dramatique de la confrontation. Le coût du hd (~54 s) achète la lisibilité de la narration visuelle : l’élève doit reconnaître, dans l’image, la scène qu’il a lue. C’est le critère de réussite d’une illustration littéraire, et c’est ce que la durée caractéristique traduit en temps de calcul.

👤 Section 2: Character Design Littéraire

Création de designs visuels pour personnages emblématiques.

Pourquoi le character design littéraire exige la cohérence : un personnage de roman doit être reconnaissable d’une illustration à l’autre — Jean Valjean doit avoir le même visage dans la confrontation avec Javert et dans la scène avec Cosette, sans quoi l’élève ne construit pas une représentation stable du personnage. C’est le concept de character bible en illustration : un jeu d’attributs fixes (âge, barbe, vêtement, silhouette) que le prompt doit répéter à chaque génération. Un modèle sans cette contrainte produit un Valjean différent à chaque appel — techniquement correct, pédagogiquement inutilisable.

# Templates character design
character_designs = {
    "jean_valjean": {
        "title": "👤 Jean Valjean (Les Misérables)",
        "prompt": """Create a character design sheet for Jean Valjean from Les Misérables:

Central portrait:
- Strong, weathered man in his 50s
- Kind but haunted eyes
- Gray hair, dignified bearing
- Simple but clean 19th century worker's clothing
- Compassionate expression showing inner nobility

Character sheet elements:
- Front view (main portrait)
- Side profile sketch
- Expression studies (stern, compassionate, determined)
- Age progression (young convict → older mayor → elderly)
- Symbolic items: bread (redemption), candlesticks (grace)

Style: Professional character design sheet. Realistic but artistic.
Pencil sketch aesthetic with color accents. Annotated design.""",
        "character": "Jean Valjean",
        "work": "Les Misérables",
        "traits": "Rédemption, bonté, justice"
    },
    "le_petit_prince": {
        "title": "👦 Le Petit Prince",
        "prompt": """Create a character design for Le Petit Prince:

Main character:
- Small boy, around 7-8 years old
- Golden blonde hair, slightly messy
- Green coat with golden buttons
- Yellow scarf flowing in wind
- Innocent, wise, melancholic expression
- Standing on small round planet (asteroid)

Design elements:
- Full body view
- Close-up of face showing wonder
- With his rose (under glass dome)
- With the fox (friendship moment)
- Looking at stars (contemplative)

Style: Watercolor character sheet. Gentle, poetic. Saint-Exupéry-inspired.
Soft colors. Whimsical but touching. Children's book illustration quality.""",
        "character": "Le Petit Prince",
        "work": "Le Petit Prince",
        "traits": "Innocence, sagesse, curiosité"
    },
    "cyrano": {
        "title": "🎭 Cyrano de Bergerac",
        "prompt": """Create a character design for Cyrano de Bergerac:

Main portrait:
- Musketeer with distinctive large nose
- Proud, noble bearing despite physical feature
- 17th century French musketeer costume
- Feathered hat, cape, sword
- Passionate, poetic expression
- Both warrior strength and poet's sensitivity visible

Design elements:
- Heroic stance with sword
- Writing poetry (with quill)
- Dueling pose (combining sword and verse)
- Profile emphasizing nose (his distinguishing feature)
- Expression showing inner pain vs outer bravado

Style: Theatrical character design. Swashbuckling romance.
Dramatic lighting and poses. 17th century costume accuracy.
Professional theater/film character concept quality.""",
        "character": "Cyrano de Bergerac",
        "work": "Cyrano de Bergerac",
        "traits": "Panache, amour non partagé, éloquence"
    }
}

print("\n👤 CHARACTER DESIGN LITTÉRAIRE")
print("=" * 50)
for key, char in character_designs.items():
    print(f"\n{char['title']}")
    print(f"   📖 Œuvre: {char['work']}")
    print(f"   🎭 Traits: {char['traits']}")

# Génération: Le Petit Prince
print("\n" + "=" * 60)
print("👦 GÉNÉRATION: LE PETIT PRINCE")
print("=" * 60)

prince = character_designs["le_petit_prince"]
prince_result = generate_literary_illustration(
    prompt=prince["prompt"],
    title=prince["title"],
    category="character_designs",
    quality=image_quality
)

if prince_result['success']:
    prince_image = display_and_save(prince_result, {
        "author": "Antoine de Saint-Exupéry",
        "work": prince["work"],
        "character": prince["character"],
        "traits": prince["traits"]
    })
    
    print(f"\n💡 Applications:")
    print(f"   • Cours de français CM2/6ème")
    print(f"   • Portrait de personnage")
    print(f"   • Adaptation théâtrale scolaire")
    print(f"   • Analyse personnage et symboles")

👤 CHARACTER DESIGN LITTÉRAIRE
==================================================

👤 Jean Valjean (Les Misérables)
   📖 Œuvre: Les Misérables
   🎭 Traits: Rédemption, bonté, justice

👦 Le Petit Prince
   📖 Œuvre: Le Petit Prince
   🎭 Traits: Innocence, sagesse, curiosité

🎭 Cyrano de Bergerac
   📖 Œuvre: Cyrano de Bergerac
   🎭 Traits: Panache, amour non partagé, éloquence

============================================================
👦 GÉNÉRATION: LE PETIT PRINCE
============================================================

🎨 Génération: 👦 Le Petit Prince
   ✅ Généré en 45.9s


💡 Applications:
   • Cours de français CM2/6ème
   • Portrait de personnage
   • Adaptation théâtrale scolaire
   • Analyse personnage et symboles

Exercice 1 : Character design d’un personnage secondaire

Les designs ci-dessus couvrent des personnages principaux (Jean Valjean, Le Petit Prince, Cyrano). L’objectif est de créer le character design d’un personnage secondaire important, souvent neglige visuellement.

  • Choisir un personnage secondaire marquant (ex : Gavroche dans Les Misérables, le Renard dans Le Petit Prince, Roxane dans Cyrano)
  • Rediger un prompt de character design en suivant la même structure que les templates existants (portrait principal, expressions, objets symboliques)
  • Adapter le style visuel a l’oeuvre d’origine et au niveau scolaire cible
# TODO etudiant : creer le template de character design
supporting_character_template = {
    "title": None,       # TODO etudiant : titre au format "Personnage (Oeuvre)"
    "prompt": None,      # TODO etudiant : prompt detaille avec portrait, expressions, objets symboliques
    "character": None,   # TODO etudiant : nom du personnage
    "work": None,        # TODO etudiant : oeuvre source
    "traits": None       # TODO etudiant : traits caracteristiques (3 mots-cles)
}

# Validation de la structure
required_keys = ["title", "prompt", "character", "work", "traits"]
missing = [k for k in required_keys if not supporting_character_template.get(k)]
if missing:
    print(f"Exercice a completer : cles manquantes = {missing}")
else:
    print(f"Character design '{supporting_character_template['character']}' pret")
Exercice a completer : cles manquantes = ['title', 'prompt', 'character', 'work', 'traits']

🎨 Section 3: Visualisation de Métaphores

Traduction visuelle de métaphores et symboles littéraires.

Pourquoi les métaphores sont le cas le plus sémantiquement difficile : illustrer « l’albatros de Baudelaire » (le poète, géant des cieux, ridicule au sol) demande une allégorie visuelle — un oiseau majestueux en vol ET maladroite à terre, dans la même image ou en contraste. Aucune description littérale ne suffit : il faut traduire un concept (la grandeur poétique vs la gaucherie sociale) en composition. C’est l’écart maximum entre le texte et l’image, et c’est pourquoi cette section distingue le notebook d’illustration d’un simple « générateur d’images de roman » : elle teste la capacité du modèle à interpréter, pas seulement à représenter.

# Templates métaphores visuelles
metaphor_visuals = {
    "baudelaire_albatross": {
        "title": "🦅 L'Albatros (Baudelaire) - Métaphore du Poète",
        "prompt": """Create a symbolic illustration for Baudelaire's poem 'L'Albatros':

Dual composition showing metaphor:
Upper half:
- Majestic albatross soaring gracefully in vast blue sky
- Wings spread wide, powerful, free
- Among clouds, sunlight, infinite space

Lower half:
- Same albatross on ship deck, awkward, trapped
- Sailors mocking, wings dragging on deck
- Confined, humiliated, out of its element

Center: Transformation/contrast line

Subtle overlay: Poet's silhouette suggesting parallel

Style: Symbolic, artistic. Split composition showing duality.
Dramatic contrast between freedom and constraint.
Romantic period painting style. Melancholic beauty.""",
        "poet": "Charles Baudelaire",
        "poem": "L'Albatros (Les Fleurs du Mal)",
        "metaphor": "Le poète incompris dans la société",
        "grade_level": "Première"
    },
    "hugo_light_shadow": {
        "title": "💡 Lumière et Ombre (Hugo) - Dualité Humaine",
        "prompt": """Create a symbolic illustration of Victor Hugo's light/shadow duality:

Composition:
- Human figure at center (Everyman)
- Left side bathed in golden light (redemption, good)
  * Angels, hope, rising sun, open sky
- Right side in dark shadows (fall, evil)
  * Demons, despair, darkness, closed spaces
- Figure standing at the crossroads
- Light and shadow battling across the figure
- Choice and free will emphasized

Style: Romantic symbolism. Dramatic chiaroscuro (light/dark contrast).
Caravaggio-inspired lighting. Philosophical, profound.
High artistic quality showing eternal human struggle.""",
        "author": "Victor Hugo",
        "theme": "Dualité bien/mal, rédemption",
        "metaphor": "Choix moral et libre arbitre",
        "grade_level": "Seconde/Première"
    },
    "rimbaud_drunken_boat": {
        "title": "🚤 Le Bateau Ivre (Rimbaud) - Liberté Poétique",
        "prompt": """Create a surreal illustration for Rimbaud's 'Le Bateau ivre':

Surreal scene:
- Small boat without captain or crew
- Floating on impossible, psychedelic waters
- Water colors: greens, purples, electric blues (synesthesia)
- Exotic visions: flying fish, floating islands, bizarre creatures
- Fragmented reality, dreamlike logic
- Sense of intoxicated freedom and danger
- Swirling, dynamic composition

Style: Surrealism meets Symbolism. Vivid, hallucinatory colors.
Dalí/Magritte-inspired impossible imagery. Wild, uncontrolled.
Youthful rebellion and poetic vision. High artistic quality.""",
        "poet": "Arthur Rimbaud",
        "poem": "Le Bateau ivre",
        "metaphor": "Liberté absolue, voyage intérieur, voyance poétique",
        "grade_level": "Première/Terminale"
    }
}

print("\n🎨 VISUALISATION DE MÉTAPHORES")
print("=" * 50)
for key, meta in metaphor_visuals.items():
    print(f"\n{meta['title']}")
    print(f"   📝 Métaphore: {meta['metaphor']}")
    print(f"   🎓 Niveau: {meta['grade_level']}")

# Génération: L'Albatros
print("\n" + "=" * 60)
print("🦅 GÉNÉRATION: L'ALBATROS (BAUDELAIRE)")
print("=" * 60)

albatross = metaphor_visuals["baudelaire_albatross"]
albatross_result = generate_literary_illustration(
    prompt=albatross["prompt"],
    title=albatross["title"],
    category="metaphors",
    quality=image_quality
)

if albatross_result['success']:
    albatross_image = display_and_save(albatross_result, {
        "author": albatross["poet"],
        "work": albatross["poem"],
        "metaphor": albatross["metaphor"],
        "grade_level": albatross["grade_level"]
    })
    
    print(f"\n💡 Utilisations:")
    print(f"   • Cours de français {albatross['grade_level']}")
    print(f"   • Analyse du symbolisme baudelairien")
    print(f"   • Commentaire composé sur L'Albatros")
    print(f"   • Étude métaphore poète/société")
    print(f"   • Oral du bac français")

🎨 VISUALISATION DE MÉTAPHORES
==================================================

🦅 L'Albatros (Baudelaire) - Métaphore du Poète
   📝 Métaphore: Le poète incompris dans la société
   🎓 Niveau: Première

💡 Lumière et Ombre (Hugo) - Dualité Humaine
   📝 Métaphore: Choix moral et libre arbitre
   🎓 Niveau: Seconde/Première

🚤 Le Bateau Ivre (Rimbaud) - Liberté Poétique
   📝 Métaphore: Liberté absolue, voyage intérieur, voyance poétique
   🎓 Niveau: Première/Terminale

============================================================
🦅 GÉNÉRATION: L'ALBATROS (BAUDELAIRE)
============================================================

🎨 Génération: 🦅 L'Albatros (Baudelaire) - Métaphore du Poète
   ✅ Généré en 46.9s


💡 Utilisations:
   • Cours de français Première
   • Analyse du symbolisme baudelairien
   • Commentaire composé sur L'Albatros
   • Étude métaphore poète/société
   • Oral du bac français

Exercice 2 : Evolution visuelle d’un thème litteraire a travers les epoques

Les metaphores visuelles ci-dessus illustrent des poemes individuels. L’objectif est de créer une fonction qui genere une serie visuelle montrant comment un même thème (ex : l’amour, la mort, la liberte) evolue a travers les epoques litteraires.

  • Choisir un thème litteraire universel
  • Créer un dictionnaire qui mappe chaque epoque (Romantisme, Symbolisme, Surrealisme, Existentialisme) vers un style visuel adapte
  • Generer une illustration par epoque avec un prompt qui respecte le style artistique de la periode
def generate_thematic_evolution(theme: str, epochs: list = None) -> list:
    """
    Genere des prompts visuels pour un theme a travers les epoques litteraires.

    # Indice : Chaque epoque a un vocabulaire visuel distinct :
    #   Romantisme -> couleurs chaudes, contrastes dramatiques, nature orageuse
    #   Symbolisme -> tons satures, images oniriques, mystere
    #   Surrealisme -> compositions impossibles, juxtapositions inattendues
    #   Existentialisme -> palettes desaturees, espaces vides, solitude

    Args:
        theme: Theme litteraire (ex: "l'amour", "la mort", "la liberte")
        epochs: Liste des epoques (defaut: 4 epoques principales)

    Returns:
        Liste de dictionnaires avec epoch, prompt, style_keywords
    """
    # TODO etudiant : implementer la generation de prompts thematiques
    pass

# Test avec le theme "la liberte"
theme_prompts = generate_thematic_evolution("la liberte")
print(f"Exercice a completer : {len(theme_prompts) if theme_prompts else 0} prompts thematiques")
Exercice a completer : 0 prompts thematiques

🎬 Section 4: Storyboard pour Adaptation

Création de planches de storyboard pour adaptations théâtrales ou cinéma.

# Template storyboard
storyboard_prompt = """Create a 6-panel storyboard for adapting 'Le Petit Prince' fox taming scene:

Panel 1: Little Prince arrives at Earth, alone
- Small figure on barren planet surface
- Wide shot showing isolation

Panel 2: First encounter with Fox
- Fox hiding behind wheat field
- Little Prince curious, approaching

Panel 3: Fox explains taming
- Medium shot, Fox and Prince talking
- Wheat mentioned (golden like Prince's hair)

Panel 4: The taming ritual begins
- Prince sitting, Fox approaching carefully
- Distance gradually closing over time

Panel 5: Friendship established
- Prince and Fox side by side
- Wheat field golden in background
- Connection visible

Panel 6: Fox's wisdom - 'On ne voit bien qu'avec le cœur'
- Close-up of Fox's wise expression
- Prince listening, understanding
- Emotional moment

Style: Clean storyboard panels. Professional film/animation pre-production.
Simple but clear compositions. Annotations for camera angles and movements.
Emotional progression clear across panels."""

print("\n🎬 STORYBOARD ADAPTATION")
print("=" * 50)
print("\n📋 Génération: Le Petit Prince - Scène de l'Apprivoisement")

storyboard_result = generate_literary_illustration(
    prompt=storyboard_prompt,
    title="🎬 Storyboard - Le Petit Prince et le Renard",
    category="storyboards",
    quality=image_quality
)

if storyboard_result['success']:
    storyboard_image = display_and_save(storyboard_result, {
        "author": "Antoine de Saint-Exupéry",
        "work": "Le Petit Prince",
        "scene": "L'apprivoisement du renard",
        "purpose": "Adaptation théâtrale/cinéma scolaire"
    })
    
    print(f"\n✅ Storyboard généré")
    print(f"\n💡 Applications pédagogiques:")
    print(f"   • Projet d'adaptation théâtrale")
    print(f"   • Cours de français (séquence théâtre)")
    print(f"   • Atelier cinéma/audiovisuel")
    print(f"   • Analyse mise en scène")
    print(f"   • Travail de groupe créatif")
    print(f"   • Compréhension narrative visuelle")

🎬 STORYBOARD ADAPTATION
==================================================

📋 Génération: Le Petit Prince - Scène de l'Apprivoisement

🎨 Génération: 🎬 Storyboard - Le Petit Prince et le Renard
   ✅ Généré en 49.7s


✅ Storyboard généré

💡 Applications pédagogiques:
   • Projet d'adaptation théâtrale
   • Cours de français (séquence théâtre)
   • Atelier cinéma/audiovisuel
   • Analyse mise en scène
   • Travail de groupe créatif
   • Compréhension narrative visuelle

Lecture de la génération — le storyboard en 6 panels a été produit en ~51 s (durée précise consignée par la cellule de génération ci-dessus — « Généré en X.Xs », source unique mise à jour à chaque exécution), durée intermédiaire entre l’illustration unique (~54 s) et le character design (~45 s). Un storyboard est plus long à générer qu’un personnage isolé parce qu’il doit maintenir une cohérence narrative : les 6 panels représentent une séquence temporelle avec les mêmes personnages dans un décor évolutif, et chaque panel doit être lisible individuellement tout en s’enchaînant avec les autres.

Pourquoi le storyboard teste la cohérence multi-image : c’est le seul cas du notebook où DALL-E 3 doit produire une séquence plutôt qu’une image unique — et c’est précisément la limite des modèles de génération d’images, conçus pour des rendus isolés. Les incohérences (un personnage qui change de vêtement entre panels 3 et 4, un décor qui n’est pas le même) y sont plus fréquentes que dans une illustration unique. Pour un usage pédagogique réel (aider un élève à visualiser la structure narrative d’une scène), cette limite doit être assumée : le storyboard est une aide à la projection mentale, pas une animatique fidèle.

📦 Section 5: Pack d’Étude Littéraire

Export complet pour enseignants de français.

if export_study_pack:
    print("\n📦 EXPORT PACK D'ÉTUDE LITTÉRAIRE")
    print("=" * 50)

    pack_dir = OUTPUT_DIR / f"literature_pack_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    pack_dir.mkdir(exist_ok=True)

    (pack_dir / 'illustrations').mkdir(exist_ok=True)
    (pack_dir / 'characters').mkdir(exist_ok=True)
    (pack_dir / 'metaphors').mkdir(exist_ok=True)
    (pack_dir / 'storyboards').mkdir(exist_ok=True)
    (pack_dir / 'study_guides').mkdir(exist_ok=True)

    print(f"\n📁 Structure créée: {pack_dir.name}")

    # Collecte des ressources
    resources = []
    if 'les_mis_result' in locals() and les_mis_result.get('success'):
        resources.append(('les_miserables', les_mis_result, classical_illustrations['les_miserables_barricade']))
    if 'prince_result' in locals() and prince_result.get('success'):
        resources.append(('petit_prince_char', prince_result, character_designs['le_petit_prince']))
    if 'albatross_result' in locals() and albatross_result.get('success'):
        resources.append(('albatros', albatross_result, metaphor_visuals['baudelaire_albatross']))
    if 'storyboard_result' in locals() and storyboard_result.get('success'):
        resources.append(('storyboard_pp', storyboard_result, {}))

    print(f"\n📊 {len(resources)} ressources à exporter")

    pack_index = {
        "title": "Pack d'Étude Littéraire Visuel",
        "created": datetime.now().isoformat(),
        "period": literary_period,
        "genre": genre,
        "resources": []
    }

    for key, result, template in resources:
        # Récupération image (gpt-image-1 = b64, dall-e-3 legacy = url)
        if result.get('image_b64'):
            image = Image.open(BytesIO(base64.b64decode(result['image_b64'])))
        elif result.get('image_url'):
            img_response = requests.get(result['image_url'], timeout=30)
            if img_response.status_code != 200:
                print(f"   ⚠️  Téléchargement échoué pour {key}, ignoré")
                continue
            image = Image.open(BytesIO(img_response.content))
        else:
            print(f"   ⚠️  Aucune image pour {key}, ignoré")
            continue

        subfolder = result['category']
        img_file = pack_dir / subfolder / f"{key}.png"
        image.save(img_file)

        # Guide d'étude associé
        if template:
            study_guide = f"""# {result['title']}

## 📖 Informations
- **Auteur:** {template.get('author', template.get('poet', 'N/A'))}
- **Œuvre:** {template.get('work', template.get('poem', 'N/A'))}
- **Niveau:** {template.get('grade_level', 'N/A')}

## 🎯 Objectifs Pédagogiques
{template.get('theme', template.get('metaphor', template.get('traits', 'N/A')))}

## 💡 Pistes d'Exploitation
1. Analyse visuelle de l'illustration
2. Comparaison texte/image
3. Discussion sur les choix artistiques
4. Création d'illustrations complémentaires
5. Rédaction inspirée par l'image

## 📝 Questions pour les Élèves
- Quels éléments visuels correspondent au texte?
- Quelles émotions l'image transmet-elle?
- Comment l'artiste interprète-t-il le passage/personnage/métaphore?
"""
            guide_file = pack_dir / 'study_guides' / f"{key}_guide.md"
            with open(guide_file, 'w', encoding='utf-8') as f:
                f.write(study_guide)

        pack_index['resources'].append({
            "id": key,
            "title": result['title'],
            "category": result['category'],
            "file": f"{subfolder}/{key}.png",
            "guide": f"study_guides/{key}_guide.md" if template else None
        })

        print(f"   ✅ {result['title']}")

    # README principal
    readme = f"""# Pack d'Étude Littéraire Visuel

**Créé:** {datetime.now().strftime('%Y-%m-%d')}
**Période:** {literary_period}
**Genre:** {genre}

## 📚 Contenu du Pack

### Illustrations Classiques
Scènes clés d'œuvres majeures de la littérature française.

### Character Designs
Conceptions visuelles de personnages emblématiques.

### Métaphores Visuelles
Traductions visuelles de symboles et métaphores littéraires.

### Storyboards
Planches pour adaptations théâtrales ou cinématographiques.

## 🎓 Utilisation Pédagogique

Ces ressources sont conçues pour enrichir l'enseignement du français:
- Support visuel pour l'analyse littéraire
- Base de discussion en classe
- Inspiration pour travaux créatifs
- Comparaison texte/image
- Projets d'adaptation

## 📖 Niveaux
Collège (6ème à 3ème) et Lycée (Seconde à Terminale)
"""

    with open(pack_dir / 'README.md', 'w', encoding='utf-8') as f:
        f.write(readme)

    with open(pack_dir / 'pack_index.json', 'w', encoding='utf-8') as f:
        json.dump(pack_index, f, indent=2, ensure_ascii=False)

    print(f"\n✅ Pack exporté: {pack_dir.name}")
    print(f"📁 {len(pack_index['resources'])} ressources + guides d'étude")
else:
    print("\n⏭️  Export désactivé")

print("\n" + "=" * 50)
print("✅ Notebook Littérature Visuelle terminé!")
print("📚 Ressources pour enrichir l'enseignement littéraire")
print("=" * 50)

⏭️  Export désactivé

==================================================
✅ Notebook Littérature Visuelle terminé!
📚 Ressources pour enrichir l'enseignement littéraire
==================================================

Exercice 3 : Rapport visuel d’analyse litteraire

Le notebook genere des illustrations, character designs et metaphores visuelles isolement. L’objectif est de créer une fonction de synthese qui assemble ces éléments en un rapport visuel structure pour un enseignant.

  • Créer une fonction build_literary_report qui collecte les generations du notebook (illustrations, characters, metaphores)
  • Produire un rapport HTML avec sections par oeuvre, incluant l’image, les metadonnees litteraires et des pistes pedagogiques
  • Ajouter un système de notation (pertinence, qualite artistique, valeur educative) pour chaque generation
def build_literary_report(generations: list, output_path: str = None) -> str:
    """
    Assemble les generations en un rapport HTML structure.

    # Indice : Parcourez la liste des generations et construisez des sections HTML
    # Etape 1 : Creer l'en-tete du rapport (titre, date, periode litteraire)
    # Etape 2 : Pour chaque generation, ajouter une carte avec image + metadonnees
    # Etape 3 : Ajouter les pistes pedagogiques et la notation

    Args:
        generations: Liste de dictionnaires (title, image_url, author, work, theme, grade_level)
        output_path: Chemin pour sauvegarder le rapport HTML (optionnel)

    Returns:
        Contenu HTML du rapport
    """
    # TODO etudiant : implementer le rapport visuel
    pass

# Test avec une liste vide (l'etudiant remplira avec ses generations)
report = build_literary_report([])
print("Exercice a completer : rapport visuel d'analyse litteraire")
Exercice a completer : rapport visuel d'analyse litteraire
Retour au sommet