Lección 3 — La matière : données, textes, visuels

Visuels générés et détourage

9 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

Visuels générés et détourage

Premier essai de la journée, prompt explicite : « isolated on a perfectly uniform pure #00FF00 green background, no text, no letters ». Le modèle a rendu un entrepôt réussi, sur un sol gris qui touche les bords, avec « LOGISTICS » sur la façade et « EV » sur le camion. Les éléments visuels générés sont la partie la plus séduisante du projet et la moins prévisible : il faut des prompts qui ne demandent aucune information, un détourage qui mesure sa propre qualité et un cache qui évite de payer deux fois.

Premier essai : texte parasite (« LOGISTICS », « EV ») malgré la consigne « no text », sol gris jusqu'aux bords

Le mécanisme

flowchart LR
  P["Demande : nom, prompt, ratio, détourer ?"] --> K{"Dans le cache ? (hash modèle + ratio + prompt)"}
  K -->|"oui"| R["Relire le PNG : 0 $"]
  K -->|"non"| G["Gemini image"] --> W["Écrire dans sorties/cache/"]
  W --> R2["Image RVB"]
  R --> R2
  R2 --> D{"Détourer ?"}
  D -->|"non : fond"| OK["Visuel prêt"]
  D -->|"oui"| C["Clé verte : alpha progressif + suppression du halo"]
  C --> Q["Rapport : bord uni, résidu vert, couverture"]
  Q --> OK

Étape 1 : des prompts sans information

Six demandes, déclarées une fois dans visuels.py :

NomRatioDétouréeRôle
fond_portrait, fond_paysage9:16, 16:9nonambiance, très faible contraste
illustration1:1ouile sujet (entrepôt solaire)
icone_eclair, icone_baisse, icone_soleil1:1ouirepères des KPI
files/src/infographie/visuels.py
SANS_TEXTE = "No text, no letters, no numbers, no logo, no watermark."

Demande(
    "illustration",
    "Flat vector illustration, isometric, of a modern logistics warehouse with solar panels on the "
    "roof and one small electric delivery van. Palette: dark teal, slate blue, warm sand, white. "
    "Isolated on a perfectly uniform pure #00FF00 chroma green background, no ground shadow, "
    "no green in the subject. " + SANS_TEXTE,
    "1:1",
    True,
)

Trois consignes font le travail : « no green in the subject » (sinon le détourage troue le sujet), « no ground shadow » (une ombre portée sur le vert devient une tache semi-transparente), et une palette nommée qui reprend les couleurs de la charte. Les fonds sont demandés « very low contrast, large empty calm areas » : ils seront sous du texte.

Les deux fonds ont leur propre ratio. Recadrer un fond 9:16 en 16:9 ne garde que 32 % de sa hauteur, agrandie ensuite : le flou se voit sur un écran large.

Étape 2 : générer une fois, relire ensuite

files/src/infographie/visuels.py
def obtenir(gen: Generateur, d: Demande, cache: Path) -> Image.Image:
    """Génère une fois, relit ensuite : même prompt + même modèle = même fichier, zéro coût."""
    cle = hashlib.sha256(f"{gen.nom}|{d.aspect}|{d.prompt}".encode()).hexdigest()[:16]
    chemin = cache / f"{d.nom}-{cle}.png"
    if chemin.exists():
        return Image.open(chemin).convert("RGB")
    img = gen.generer(d)
    ...

La clé contient le nom du modèle : passer de Flash Image à Pro Image régénère, modifier une virgule du prompt aussi. Pendant la mise au point de cette formation, onze runs réels ont coûté 12 images (6 par modèle) au lieu de 66.

Le client Gemini est créé une seule fois et gardé pendant tout le run, avec 4 tentatives sur les erreurs 429 et 5xx. Un genai.Client() créé dans une expression puis abandonné est fermé par le ramasse-miettes : l'appel suivant échoue avec « Cannot send a request, as the client has been closed ». Nous l'avons rencontré en écrivant le script de la leçon 1.

Étape 3 : détourer par clé de couleur

Le détourage par clé de couleur (chroma key) rend transparent tout pixel proche d'une couleur choisie. Le vert pur est retenu parce qu'il n'apparaît presque jamais dans un sujet industriel ; pour un sujet végétal, prenez le magenta #FF00FF.

files/src/infographie/visuels.py
def detourer_chroma(img, cle=CLE, bas: float = 70, haut: float = 160):
    rgb = np.asarray(img.convert("RGB"), dtype=np.float32)
    d = np.linalg.norm(rgb - np.array(cle, dtype=np.float32), axis=2)
    alpha = np.clip((d - bas) / (haut - bas), 0, 1)
    # dé-contamination du vert (« despill ») sur les bords semi-transparents
    r, g, b = rgb[..., 0], rgb[..., 1], rgb[..., 2]
    bord = alpha < 0.999
    g_corrige = np.where(bord, np.minimum(g, np.maximum(r, b)), g)
  • Alpha progressif : en dessous d'une distance de 70 au vert, transparent ; au-dessus de 160, opaque ; entre les deux, un dégradé. Un seuil unique donne un bord en escalier.
  • Despill : un pixel de bord mélange le sujet et le vert. On plafonne sa composante verte au maximum du rouge et du bleu, ce qui retire le liseré vert sans toucher l'intérieur du sujet.
  • Les images arrivent en JPEG : la compression bave autour des contours, d'où des seuils larges plutôt qu'une égalité stricte.
Illustration de gemini-3-pro-image avant et après détourage, sur damier de transparence

Le détourage mesure sa propre qualité :

MesureDéfinitionSeuil
bord_unipart des pixels du cadre proches du vert≥ 0,6, sinon le fond n'était pas uni
residu_clepart des pixels opaques encore verdâtres≤ 2 %
couverturepart de l'image occupée par le sujetentre 2 % et 95 %

Quand le fond n'est pas uni (photo réelle, modèle qui a ignoré la consigne), detourer_masque() applique un masque fourni par un outil de segmentation ou dessiné, avec un léger flou du bord.

Le rapport donne bord_uni = 0,31 pour l'illustration. Le rendu final montre un rectangle beige autour de l'entrepôt. Quelle est la première action ?

Étape 4 : lancer avec la clé

cp .env.example .env    # puis renseignez GOOGLE_API_KEY
make run

Sans clé, make run utilise FauxGenerateur : des formes procédurales déterministes, qui servent aussi aux tests. Le reste du pipeline est identique.

Ce qui casse en production

SymptômeCauseParade
Lettres parasites sur l'illustrationle modèle « décore » malgré la consigneregarder chaque visuel une fois avant de figer le cache ; régénérer avec un autre prompt
Trous dans le sujetle sujet contient du vert (feuillage, logo vert)clé magenta, ou masque
Liseré vert sur fond sombredespill absent ou seuil haut trop basgarder le despill, élargir l'intervalle bas/haut
Facture qui grimpe en CImake clean entre chaque run vide le cacheconserver sorties/cache/ comme artefact ou ne pas générer en CI (--hors-ligne)
aucune image renvoyéerefus du filtre de sécurité, souvent sur un mot ambigul'erreur est explicite ; reformuler le prompt

Règle de décision

  • Demandez au modèle d'image des formes et une ambiance, jamais un mot ni un chiffre.
  • Fond uni + clé pour tout sujet que vous contrôlez par le prompt ; masque pour une photo réelle.
  • Relisez les visuels une fois quand ils entrent dans le cache ; le cache garantit ensuite qu'ils ne changent plus.

Un graphiste propose de générer les icônes en une seule image (une planche de trois icônes) pour économiser deux appels. Votre avis ?

Exercice

Ajoutez une quatrième icône icone_camion et faites-la apparaître à côté du KPI meilleur_site_variation. Que se passe-t-il au premier make run et au second ?

Voir la solution

Dans DEMANDES :

Demande(
    "icone_camion",
    "Single flat icon of a delivery van seen from the side, solid dark teal #0F766E, thick simple "
    "geometric shape, centered with generous margin, on a perfectly uniform pure #00FF00 green background. "
    + SANS_TEXTE,
    "1:1",
    True,
),

Dans mise_en_page.py, ICONES["meilleur_site_variation"] = "icone_camion".

Premier make run : une seule génération (génération icone_camion avec …), les cinq autres visuels sortent du cache. Second run : aucune génération. Vérifiez detourage.icone_camion.acceptable dans manifeste.json.

Résultat obtenu

Premier run réel du 2026-10-15 (20:47, gemini-3.1-flash-image), puis run avec gemini-3-pro-image (20:51) : six générations chacun, 1 min 44 s et 2 min 26 s au total. Rapport de détourage du run Pro, extrait de manifeste.json :

"detourage": {
  "illustration": {"bord_uni": 1.0, "residu_cle": 0.00066, "couverture": 0.40, "acceptable": true},
  "icone_eclair": {"bord_uni": 1.0, "residu_cle": 0.0,     "couverture": 0.09, "acceptable": true},
  "icone_baisse": {"bord_uni": 1.0, "residu_cle": 0.0,     "couverture": 0.11, "acceptable": true},
  "icone_soleil": {"bord_uni": 1.0, "residu_cle": 0.0,     "couverture": 0.17, "acceptable": true}
}

Les quatre visuels détourés du run Pro ne contiennent aucun texte parasite.

Je sais faire

  • Écrire un prompt d'image qui ne porte aucune information
  • Mettre en cache les générations par empreinte du modèle et du prompt
  • Détourer par clé avec alpha progressif et despill, et lire le rapport de qualité
  • Choisir entre clé de couleur et masque
Siguiente