Éditer des images existantes (inpainting, outpainting)
Mis à jour le 29 juillet 2026
Éditer des images existantes
La méthode client.images.edit() modifie une image existante en s’appuyant sur un masque qui délimite la zone à régénérer. Deux usages en découlent, et il vaut la peine de les nommer précisément parce qu’ils supposent des masques différents. L’inpainting remplit ou remplace une zone intérieure — vous changez le ciel d’un paysage, l’écran d’un ordinateur, la couleur d’un mur — tandis que l’outpainting étend l’image au-delà de ses bordures d’origine, par exemple pour transformer un portrait carré en bannière panoramique.
Principe du masque
Le masque est une image PNG de la même taille que l’image source. Les zones transparentes (alpha = 0) indiquent où le modèle doit générer du nouveau contenu ; les zones opaques restent intactes. Cette convention est contre-intuitive au premier abord : on a spontanément envie de « peindre » ce qu’on veut modifier, alors qu’il faut au contraire creuser un trou dans le masque. L’appel lui-même reste simple, puisqu’il suffit de passer les deux fichiers et la description de ce qui doit apparaître.
from openai import OpenAI
client = OpenAI()
response = client.images.edit(
model="gpt-image-1",
image=open("photo_bureau.png", "rb"),
mask=open("masque_bureau.png", "rb"),
prompt="Un écran d'ordinateur affichant un tableau de bord analytics moderne"
)
print(response.data[0].url)
Rien ne vous oblige à ouvrir un éditeur graphique pour produire ce masque. Pillow le fabrique en quelques lignes : on part d’un calque entièrement opaque aux dimensions de la source, puis on y dessine la forme à rendre transparente. Le script ci-dessous ouvre ainsi une fenêtre rectangulaire au centre de l’image.
from PIL import Image, ImageDraw
# Charger l'image source pour obtenir ses dimensions
source = Image.open("photo_bureau.png")
width, height = source.size
# Créer un masque entièrement opaque (noir)
mask = Image.new("RGBA", (width, height), (0, 0, 0, 255))
draw = ImageDraw.Draw(mask)
# Dessiner une zone transparente (la zone à éditer)
# Rectangle au centre de l'image
x1, y1 = width // 4, height // 4
x2, y2 = 3 * width // 4, 3 * height // 4
draw.rectangle([x1, y1, x2, y2], fill=(0, 0, 0, 0))
mask.save("masque_bureau.png")
print(f"Masque créé : {width}x{height}, zone éditable au centre")
Inpainting : remplacer un élément
Dès que vous répétez l’opération, il devient rentable d’encapsuler la fabrication du masque et l’appel API dans une seule fonction, paramétrée par la région à modifier. Le prompt ne décrit alors que le contenu attendu dans la zone masquée, jamais le reste de l’image. L’exemple applique la fonction à une photographie de paysage dont on ne reprend que les 400 premiers pixels — le ciel — pour lui donner une ambiance d’orage.
from openai import OpenAI
from PIL import Image, ImageDraw
import base64
client = OpenAI()
def inpaint_region(image_path, region, prompt, output_path):
"""
Remplace une région rectangulaire dans une image.
region : tuple (x1, y1, x2, y2) en pixels
"""
# Créer le masque
source = Image.open(image_path)
mask = Image.new("RGBA", source.size, (0, 0, 0, 255))
draw = ImageDraw.Draw(mask)
draw.rectangle(region, fill=(0, 0, 0, 0))
mask.save("temp_mask.png")
# Appeler l'API
response = client.images.edit(
model="gpt-image-1",
image=open(image_path, "rb"),
mask=open("temp_mask.png", "rb"),
prompt=prompt
)
# Sauvegarder le résultat
import urllib.request
urllib.request.urlretrieve(response.data[0].url, output_path)
print(f"Image éditée sauvegardée : {output_path}")
# Exemple : remplacer le ciel dans une photo de paysage
inpaint_region(
image_path="paysage.png",
region=(0, 0, 1024, 400), # partie supérieure = le ciel
prompt="Ciel dramatique avec des nuages d'orage violets au coucher du soleil",
output_path="paysage_ciel_modifie.png"
)
Outpainting : étendre l’image
Agrandir une image demande une manipulation supplémentaire, car l’API ne sait pas produire du contenu « à côté » d’un fichier : il faut d’abord créer un canvas plus grand, y coller l’original, et fournir un masque qui protège exactement la zone occupée par cet original. Toute la logique tient dans le calcul des nouvelles dimensions et de la position de collage selon la direction demandée.
from openai import OpenAI
from PIL import Image
import base64
from io import BytesIO
client = OpenAI()
def outpaint(image_path, direction, extra_pixels, prompt, output_path):
"""
Étend une image dans une direction donnée.
direction : 'right', 'bottom', 'left', 'top'
extra_pixels : nombre de pixels à ajouter
"""
source = Image.open(image_path).convert("RGBA")
w, h = source.size
# Calculer les nouvelles dimensions
if direction == "right":
new_size = (w + extra_pixels, h)
paste_pos = (0, 0)
elif direction == "bottom":
new_size = (w, h + extra_pixels)
paste_pos = (0, 0)
elif direction == "left":
new_size = (w + extra_pixels, h)
paste_pos = (extra_pixels, 0)
elif direction == "top":
new_size = (w, h + extra_pixels)
paste_pos = (0, extra_pixels)
# Créer le canvas étendu (transparent = zone à générer)
extended = Image.new("RGBA", new_size, (0, 0, 0, 0))
extended.paste(source, paste_pos)
# Le masque : transparent partout sauf où l'image originale est placée
mask = Image.new("RGBA", new_size, (0, 0, 0, 0))
opaque = Image.new("RGBA", (w, h), (0, 0, 0, 255))
mask.paste(opaque, paste_pos)
# Sauvegarder temporairement
extended.save("temp_extended.png")
mask.save("temp_mask_outpaint.png")
response = client.images.edit(
model="gpt-image-1",
image=open("temp_extended.png", "rb"),
mask=open("temp_mask_outpaint.png", "rb"),
prompt=prompt
)
import urllib.request
urllib.request.urlretrieve(response.data[0].url, output_path)
print(f"Image étendue sauvegardée : {output_path}")
# Étendre un portrait vers la droite
outpaint(
image_path="portrait.png",
direction="right",
extra_pixels=512,
prompt="Continuation naturelle de la scène, même éclairage et arrière-plan",
output_path="portrait_etendu.png"
)
Notez la formulation du prompt d’extension : « continuation naturelle de la scène, même éclairage et arrière-plan ». En outpainting, vous ne décrivez pas un nouveau sujet, vous demandez au modèle de prolonger ce qu’il voit déjà. Un prompt trop inventif produirait une jointure visible entre l’original et la zone ajoutée.
Édition itérative
Rien n’interdit d’enchaîner plusieurs éditions sur la même image, chaque sortie devenant l’entrée de l’étape suivante. C’est la façon la plus fiable de construire une composition complexe : on traite l’arrière-plan, puis le premier plan, avec un masque dédié à chaque passe, plutôt que d’espérer qu’un prompt unique règle tout d’un coup.
from openai import OpenAI
import urllib.request
client = OpenAI()
def edit_step(image_path, mask_path, prompt, output_path):
response = client.images.edit(
model="gpt-image-1",
image=open(image_path, "rb"),
mask=open(mask_path, "rb"),
prompt=prompt
)
urllib.request.urlretrieve(response.data[0].url, output_path)
return output_path
# Étape 1 : modifier l'arrière-plan
step1 = edit_step(
"scene.png", "mask_background.png",
"Arrière-plan de forêt tropicale luxuriante",
"scene_step1.png"
)
# Étape 2 : ajouter un élément au premier plan
step2 = edit_step(
step1, "mask_foreground.png",
"Un perroquet ara coloré posé sur une branche",
"scene_step2.png"
)
print(f"Résultat final : {step2}")
Contraintes à connaître
Quatre règles techniques conditionnent le bon fonctionnement de l’appel, et leur non-respect se traduit par une erreur immédiate plutôt que par un résultat dégradé.
- L’image source et le masque doivent avoir les mêmes dimensions
- Les deux fichiers doivent être au format PNG
- La taille maximale par fichier est de 4 Mo
- Le masque utilise le canal alpha pour définir les zones éditables
À ces contraintes s’ajoute une observation empirique : les résultats sont plus cohérents quand la zone masquée représente moins de 50 % de l’image. Au-delà, le modèle dispose de trop peu de contexte pour raccorder correctement, et vous obtiendrez souvent de meilleurs résultats en découpant votre modification en deux éditions successives.
Mettez-le en pratique sur une photo de votre bureau, ou sur n’importe quelle image libre de droits : écrivez un script qui génère automatiquement un masque sur le quart supérieur de l’image, remplace cette zone par un ciel étoilé et sauvegarde le résultat en local.