Combiner génération d'images avec d'autres outils API
Mis à jour le 29 juillet 2026
Combiner génération d’images avec d’autres outils API
Prise isolément, la génération d’images reste un outil d’artisan : on écrit un prompt, on récupère un fichier. Sa vraie puissance apparaît quand elle devient une étape parmi d’autres dans un enchaînement automatisé — un modèle de langage qui rédige le prompt, une analyse visuelle qui décrit une image de référence, un stockage qui publie le résultat. Cette leçon vous montre comment câbler ces pièces ensemble.
Faire écrire le prompt par un modèle de langage
Vos utilisateurs, ou vos collègues, ne formulent pas des prompts visuels : ils formulent des briefs. « J’ai besoin d’une image pour un article sur l’IA dans la santé, ton futuriste mais humain » ne contient ni composition, ni éclairage, ni palette. Plutôt que de leur demander d’apprendre le vocabulaire de la leçon précédente, intercalez un modèle de langage chargé de faire la traduction. Le message système lui assigne un rôle de directeur artistique et lui impose la liste des couches à produire, ainsi que la consigne de ne rien répondre d’autre que le prompt — sans quoi vous récupéreriez une explication polie que vous enverriez ensuite à GPT Image.
from openai import OpenAI
import base64
client = OpenAI()
def texte_vers_image(brief: str, output: str):
"""
Pipeline en deux étapes :
1. Un LLM génère le prompt visuel optimal
2. GPT Image crée l'image
"""
# Étape 1 : générer le prompt visuel avec un LLM
completion = client.chat.completions.create(
model="gpt-5.6-luna",
messages=[
{
"role": "system",
"content": (
"Tu es un expert en direction artistique. "
"À partir d'un brief, génère un prompt détaillé pour un modèle "
"de génération d'images. Inclus : sujet, composition, éclairage, "
"style, perspective, palette de couleurs. "
"Réponds uniquement avec le prompt, sans explication."
)
},
{"role": "user", "content": brief}
]
)
prompt_visuel = completion.choices[0].message.content
print(f"Prompt généré : {prompt_visuel[:100]}...")
# Étape 2 : générer l'image
response = client.images.generate(
model="gpt-image-1",
prompt=prompt_visuel,
size="1536x1024",
quality="high",
response_format="b64_json"
)
data = base64.b64decode(response.data[0].b64_json)
with open(output, "wb") as f:
f.write(data)
print(f"Image générée : {output}")
return prompt_visuel
prompt = texte_vers_image(
"J'ai besoin d'une image pour un article sur l'intelligence artificielle "
"dans la santé. Ton futuriste mais humain.",
"ia_sante.png"
)
Illustrer un article de bout en bout
Un article de fond a besoin d’une couverture et d’une illustration par section, et ces images doivent se ressembler sans être identiques. La fonction illustrer_article applique cette logique en réinjectant le style dans chaque prompt et en rappelant à chaque fois le titre de l’article, ce qui donne au jeu complet son unité thématique. Deux détails d’économie méritent l’attention : la couverture est générée en high parce qu’elle sera vue par tous et souvent partagée, tandis que les illustrations de section restent en medium puisqu’elles s’affichent plus petit et plus bas dans la page. Sur les cinq sections de l’exemple consacré à l’IA dans le recrutement, cet arbitrage divise la facture sans que le lecteur perçoive quoi que ce soit.
from openai import OpenAI
import base64
import json
client = OpenAI()
def illustrer_article(titre: str, sections: list[str], style: str, output_dir: str = "."):
"""
Génère une illustration de header + une par section.
"""
resultats = []
# Image de couverture
header_prompt = (
f"Image de couverture pour un article intitulé \"{titre}\", "
f"style {style}, composition panoramique, "
"pas de texte dans l'image, professionnel"
)
response = client.images.generate(
model="gpt-image-1",
prompt=header_prompt,
size="1536x1024",
quality="high",
response_format="b64_json"
)
path = f"{output_dir}/header.png"
data = base64.b64decode(response.data[0].b64_json)
with open(path, "wb") as f:
f.write(data)
resultats.append({"type": "header", "path": path})
print(f"Header : {path}")
# Illustrations par section
for i, section in enumerate(sections):
section_prompt = (
f"Illustration pour la section \"{section}\" "
f"d'un article sur \"{titre}\", "
f"style {style}, format rectangulaire, "
"illustration conceptuelle sans texte"
)
response = client.images.generate(
model="gpt-image-1",
prompt=section_prompt,
size="1536x1024",
quality="medium",
response_format="b64_json"
)
path = f"{output_dir}/section_{i+1}.png"
data = base64.b64decode(response.data[0].b64_json)
with open(path, "wb") as f:
f.write(data)
resultats.append({"type": f"section_{i+1}", "section": section, "path": path})
print(f"Section {i+1} ({section[:30]}...) : {path}")
return resultats
illustrer_article(
titre="5 manières dont l'IA transforme le recrutement",
sections=[
"Tri automatique des CV",
"Entretiens vidéo analysés par IA",
"Matching candidat-poste intelligent",
"Réduction des biais de recrutement",
"Onboarding personnalisé",
],
style="illustration isométrique 3D, tons bleus et violets",
output_dir="."
)
Faire décrire une image pour la regénérer autrement
Comment obtenir la version hivernale d’un paysage estival dont vous n’avez que le fichier, sans masque ni retouche ? En passant par le langage. La vision du modèle analyse l’image et en produit une description assez précise pour servir de prompt de reconstruction — sujet, composition, éclairage, couleurs, style, perspective, ambiance — puis vous concaténez à cette description l’instruction de modification. Le modèle de génération reçoit alors un prompt textuel complet où seule la saison a changé, et vous récupérez une image cohérente avec l’originale plutôt qu’une image sans rapport. C’est aussi le moyen le plus fiable de reproduire la charte visuelle d’une image de référence que vous ne pouvez pas éditer directement.
from openai import OpenAI
import base64
client = OpenAI()
def analyser_et_varier(image_path: str, instruction: str, output: str):
"""
1. Analyse une image avec GPT vision
2. Génère une variante basée sur l'analyse
"""
# Étape 1 : analyser l'image
with open(image_path, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode()
analysis = client.chat.completions.create(
model="gpt-5.6-luna",
messages=[
{
"role": "user",
"content": [
{
"type": "text",
"text": (
"Décris cette image en détail pour qu'un modèle de "
"génération d'images puisse la reproduire fidèlement. "
"Inclus : sujet, composition, éclairage, couleurs, style, "
"perspective, ambiance. Sois très précis."
)
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/png;base64,{image_b64}"
}
}
]
}
]
)
description = analysis.choices[0].message.content
print(f"Analyse : {description[:150]}...")
# Étape 2 : générer une variante modifiée
prompt_variante = f"{description}. Modification demandée : {instruction}"
response = client.images.generate(
model="gpt-image-1",
prompt=prompt_variante,
size="1024x1024",
quality="high",
response_format="b64_json"
)
data = base64.b64decode(response.data[0].b64_json)
with open(output, "wb") as f:
f.write(data)
print(f"Variante générée : {output}")
analyser_et_varier(
"paysage_original.png",
"Changer la saison de l'été à l'hiver, avec de la neige sur les toits et les arbres",
"paysage_hiver.png"
)
Traiter les gros volumes par lots
Générer cinq visuels bijoux en série prend quelques minutes ; en générer cinq cents en séquentiel prend une nuit et se casse au premier incident réseau. Le dispositif ci-dessous répond aux deux problèmes. Chaque tâche est un dictionnaire autonome portant son identifiant, son prompt, son fichier de sortie et ses réglages, et la fonction unitaire enveloppe l’appel dans un try qui renvoie un statut au lieu de faire exploser le lot : une génération refusée n’emporte plus les quatre cent quatre-vingt-dix-neuf autres. Le ThreadPoolExecutor parallélise ensuite l’exécution, mais avec un max_workers délibérément bas — trois dans l’exemple — parce que le facteur limitant n’est pas votre machine, ce sont les quotas de l’API. Le rapport final compte les succès et les échecs pour que vous sachiez quoi relancer.
from openai import OpenAI
import base64
import json
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
client = OpenAI()
def generer_une_image(tache: dict) -> dict:
"""Génère une seule image à partir d'une tâche."""
try:
response = client.images.generate(
model="gpt-image-1",
prompt=tache["prompt"],
size=tache.get("size", "1024x1024"),
quality=tache.get("quality", "medium"),
response_format="b64_json"
)
data = base64.b64decode(response.data[0].b64_json)
with open(tache["output"], "wb") as f:
f.write(data)
return {"id": tache["id"], "status": "ok", "path": tache["output"]}
except Exception as e:
return {"id": tache["id"], "status": "erreur", "erreur": str(e)}
def generation_par_lots(taches: list[dict], max_parallele: int = 3):
"""Exécute un lot de générations avec parallélisme contrôlé."""
resultats = []
with ThreadPoolExecutor(max_workers=max_parallele) as executor:
futures = {
executor.submit(generer_une_image, tache): tache
for tache in taches
}
for future in as_completed(futures):
resultat = future.result()
resultats.append(resultat)
status = resultat["status"]
print(f" [{status}] {resultat['id']}")
# Rapport
ok = sum(1 for r in resultats if r["status"] == "ok")
erreurs = sum(1 for r in resultats if r["status"] == "erreur")
print(f"\nTerminé : {ok} ok, {erreurs} erreurs sur {len(taches)} tâches")
return resultats
# Définir un lot de tâches
taches = [
{
"id": f"produit_{i}",
"prompt": f"Photo produit e-commerce : {produit}, fond blanc, studio",
"output": f"batch_produit_{i}.png",
"quality": "high"
}
for i, produit in enumerate([
"montre en or rose",
"lunettes de soleil aviateur",
"bracelet en cuir tressé",
"bague solitaire diamant",
"collier perles de culture",
])
]
resultats = generation_par_lots(taches, max_parallele=3)
Publier directement vers le stockage
Écrire le PNG sur le disque de la machine qui exécute le script n’a de sens qu’en développement. En production, l’image doit atterrir là où votre site ira la chercher, et le détour par le système de fichiers local devient un point de fragilité inutile. Le format b64_json se prête bien à cette suppression : les octets décodés sont enveloppés dans un BytesIO et poussés tels quels vers le bucket, avec un ContentType explicite sans lequel les navigateurs proposeraient un téléchargement au lieu d’afficher l’image. La fonction retourne l’URL publique, qui est ce dont votre CMS a réellement besoin.
from openai import OpenAI
import base64
import boto3
from io import BytesIO
client = OpenAI()
def generer_et_stocker_s3(prompt: str, bucket: str, key: str):
"""Génère une image et l'envoie directement sur S3."""
response = client.images.generate(
model="gpt-image-1",
prompt=prompt,
size="1024x1024",
quality="high",
response_format="b64_json"
)
image_data = base64.b64decode(response.data[0].b64_json)
s3 = boto3.client("s3")
s3.upload_fileobj(
BytesIO(image_data),
bucket,
key,
ExtraArgs={"ContentType": "image/png"}
)
url = f"https://{bucket}.s3.amazonaws.com/{key}"
print(f"Image stockée sur S3 : {url}")
return url
# Exemple
url = generer_et_stocker_s3(
"Icône d'application mobile : livre ouvert avec des étoiles, flat design",
"mon-bucket-images",
"generated/app_icon_v1.png"
)
Ce qui tient un pipeline debout
Les cinq pipelines de cette leçon partagent la même hygiène de construction. Chaque étape ne fait qu’une chose, ce qui vous permet de remplacer la rédaction du prompt ou le stockage sans toucher au reste. Chaque étape gère ses propres erreurs, faute de quoi un refus de modération au milieu d’un lot fait tomber toute la chaîne — c’est exactement ce que prévient le try de generer_une_image. Le parallélisme reste bridé pour respecter les limites de débit, et sur ce point la prudence coûte moins cher que la reprise après un bannissement temporaire.
Restent deux réflexes moins visibles mais tout aussi décisifs. Loguez les prompts effectivement envoyés, en particulier lorsqu’ils ont été rédigés par un modèle de langage : sans ce journal, une image que vous adorez devient irreproductible dès la seconde exécution. Et mettez en cache les résultats dès qu’un même prompt peut revenir, ce que la leçon suivante formalise en dispositif complet.
Assemblez maintenant votre propre chaîne complète : elle prend un titre d’article en entrée, demande à un modèle de langage trois descriptions de visuels pertinents, génère les trois images en parallèle, et produit un JSON récapitulatif reliant chaque prompt à son fichier. Vous aurez alors tout ce qu’il faut pour illustrer un blog entier sans intervention manuelle.