Bonus Slideshow Vidéo - Générateur de Slideshow (Optionnel)

Navigation : << Précédent (01-1) | Index | Suivant >> (01-2)

Module : 01-Video-Foundation
Niveau : Debutant
Type : Accrétion bonus (optionnelle)
Duree estimee : 15 minutes
VRAM : 0 (CPU uniquement)

Ce notebook est une accrétion optionnelle, extraite de la conclusion de 01-1-Video-Operations-Basics pour rendre la lane principale plus claire. Il reprend intégralement le challenge qui figurait en fin du 01-1, sans modification du fond ni de la forme. Les étudiants qui veulent transformer les manipulations moviepy/imageio du 01-1 en produit livrable trouvent ici le terrain de jeu ; les autres passent directement au 01-2.

Contexte pédagogique

Le 01-1 a posé les fondamentaux :

  • Section 1 : création d’une vidéo de test à partir de frames générées avec PIL (Image.new, ImageDraw) et assemblage avec imageio.get_writer().append_data().
  • Sections 2-4 : manipulations moviepy (trim, concaténation, overlay texte), sonde ffmpeg-python, extraction rapide de frames avec decord.

Le challenge ci-dessous remet ces primitives au service d’un livrable fini : un slideshow 5 secondes @ 1 FPS avec cinq messages sur cinq fonds colorés différents. C’est la transition entre « savoir manipuler une vidéo » et « savoir produire un objet vidéo autonome ».

Prérequis

  • Avoir complété (ou parcouru) le notebook 01-1.
  • Packages : Pillow, imageio, imageio-ffmpeg (déjà listés dans requirements-video.txt à la racine de GenAI/).
  • FFmpeg installé sur le système (winget install FFmpeg ou apt install ffmpeg).

CHALLENGE BONUS - Générateur de Slideshow Vidéo

Points : 0.5 pts

Objectif

Créer une vidéo slideshow à partir de frames générées programmatiquement.

Ce que vous avez appris

La Section 1 montre: - Génération de frames avec PIL (Image.new, ImageDraw) - Assemblage avec imageio (get_writer, append_data)

Critères de succes

    • Frame 1: “Bienvenue”
    • Frame 2: “Voici le sujet”
    • Frame 3: “Point important”
    • Frame 4: “Conclusion”
    • Frame 5: “Merci”
    • Une couleur de fond différente (gradient ou palette cohérente)
    • Le texte centré

Contraintes techniques

  • Résolution: 640x480
  • Utiliser PIL pour les frames, imageio pour l’assemblage
  • FPS = 1 signifie chaque frame dure 1 seconde

Soumission : PR avec titre “Challenge #7 - [Votre Nom]”, aperçu des frames et vidéo générée

Espace de travail du challenge

Le challenge ci-dessus était resté sans endroit où répondre : ce notebook ajoute l’espace de travail. Deux exemples guidés rappellent les primitives posées dans 01-1 (une frame PIL centrée, un mini assemblage imageio) ; trois exercices portent le livrable complet. Tout tourne sur CPU (VRAM 0, conformément à l’en-tête) : Pillow pour les frames, imageio + imageio-ffmpeg (binaire ffmpeg embarqué) pour l’assemblage. Les artefacts sont écrits dans outputs/video_01_1b_slideshow/ (non versionné).

Exemple guidé 1 — Une frame PIL : fond coloré, message centré

La brique de base du slideshow. La fonction frame_demo crée une image RGB 640x480, remplit le fond, puis centre le texte en calculant sa boîte englobante :

from pathlib import Path
from PIL import Image, ImageDraw

OUTPUT_DIR = Path("outputs") / "video_01_1b_slideshow"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)


def frame_demo(message, couleur, taille=(640, 480), chemin=None):
    """Genere une frame PIL : fond colore uniforme, message centre, texte blanc."""
    img = Image.new("RGB", taille, couleur)
    dessin = ImageDraw.Draw(img)
    bbox = dessin.textbbox((0, 0), message)
    x = (taille[0] - (bbox[2] - bbox[0])) // 2
    y = (taille[1] - (bbox[3] - bbox[1])) // 2
    dessin.text((x, y), message, fill="white")
    if chemin is not None:
        img.save(chemin)
    return img


frame_exemple = frame_demo("Demo A", (40, 60, 120), chemin=OUTPUT_DIR / "frame_demo.png")
print(f"Frame exemple sauvegardee : {OUTPUT_DIR / 'frame_demo.png'} ({frame_exemple.size})")
Frame exemple sauvegardee : outputs\video_01_1b_slideshow\frame_demo.png ((640, 480))

Exemple guidé 2 — Mini assemblage : 2 frames @ 1 FPS

Le montage minimal : à 1 FPS, chaque frame dure une seconde. Deux frames donnent donc une vidéo de 2 secondes — le livrable du challenge est la même mécanique répétée 5 fois :

import numpy as np
import imageio.v2 as imageio

frames_exemple = [
    frame_demo("Demo A", (40, 60, 120)),
    frame_demo("Demo B", (120, 60, 40)),
]
video_exemple = OUTPUT_DIR / "mini_slideshow_exemple.mp4"
with imageio.get_writer(video_exemple, fps=1) as writer:
    for f in frames_exemple:
        writer.append_data(np.asarray(f))
print(f"Video exemple : {video_exemple.name} ({video_exemple.stat().st_size} octets, "
      f"{len(frames_exemple)} frames @ 1 FPS = {len(frames_exemple)} s)")
Video exemple : mini_slideshow_exemple.mp4 (2340 octets, 2 frames @ 1 FPS = 2 s)

Exercice 1 — Les 5 frames du challenge

Produire la liste des 5 frames imposées par les critères de succès : une par message, chacune sur un fond distinct (palette cohérente de votre choix), texte centré, résolution 640x480. Réutiliser frame_demo en variant la couleur, ou généraliser si vous préférez un dégradé.

# Exercice 1 (a completer) : les 5 frames du challenge
MESSAGES = ["Bienvenue", "Voici le sujet", "Point important", "Conclusion", "Merci"]
# TODO Etudiant : construire frames_challenge = liste de 5 PIL Images (640x480),
# une par message de MESSAGES, chaque frame sur une couleur de fond distincte
# (palette coherente), texte centre.
frames_challenge = None  # TODO etudiant : liste de 5 PIL.Image
print("Exercice 1 a completer :",
      None if frames_challenge is None else f"{len(frames_challenge)} frames generees")
Exercice 1 a completer : None

Exercice 2 — L’assemblage : 5 secondes @ 1 FPS

Assembler frames_challenge en vidéo slideshow 5 s @ 1 FPS (5 frames, une seconde chacune), exactement comme la vidéo exemple, et sauvegarder dans OUTPUT_DIR sous le nom slideshow_5s.mp4.

# Exercice 2 (a completer) : assembler le livrable video
# TODO Etudiant : ecrire frames_challenge dans OUTPUT_DIR / 'slideshow_5s.mp4'
# via imageio.get_writer(..., fps=1), comme la video exemple.
video_challenge = None  # TODO etudiant : Path du fichier slideshow_5s.mp4 produit
print("Exercice 2 a completer :", video_challenge)
Exercice 2 a completer : None

Exercice 3 — Vérifier le livrable

Un livrable se vérifie avant de se soumettre. Contrôler que slideshow_5s.mp4 existe, que sa taille est non nulle, et que ses métadonnées coïrent avec l’attendu : 5 frames, durée 5 secondes. Indices : imageio.get_reader(...) expose count_frames() et get_meta_data() (clés fps, duration).

# Exercice 3 (a completer) : verification du livrable
# TODO Etudiant : construire verdict_livrable = {"existe": bool, "nframes": int|None,
# "duree_s": float|None, "ok": bool} pour slideshow_5s.mp4, ou ok = existe ET
# nframes == 5 ET duree_s == 5.0 (a tolerance pres).
verdict_livrable = None  # TODO etudiant
print("Exercice 3 a completer :", verdict_livrable)
Exercice 3 a completer : None
Retour au sommet