Phase 08 - Lesson 09

Inpainting, Outpainting & Edición de Imágenes

Text-to-image hace cosas nuevas. Inpainting repara las antiguas. En producción, el 70% del trabajo facturable de imágenes es edición: cambiar un fondo, eliminar un logotipo, extender el lienzo, regenerar una mano. El inpainting es donde la difusión demuestra su valor.

Tipo: Build Lenguajes: Python Prerrequisitos: Phase 8 · 07 (Latent Diffusion), Phase 8 · 08 (ControlNet & LoRA) Tiempo: ~75 minutos

El Problema

Un cliente envía una foto perfecta de un producto con un letrero que distrae en el fondo. Quieres borrar el letrero y dejar todo lo demás idéntico pixel por pixel. No puedes ejecutar text-to-image desde cero: el resultado tendrá un color diferente, una iluminación diferente y un ángulo del producto diferente. Quieres regenerar solo la región enmascarada y quieres que la regeneración respectete el contexto circundante.

Eso es el inpainting. Variantes:

  • Inpainting. Regenerar dentro de una máscara, manteniendo los píxeles externos.
  • Outpainting. Regenerar fuera de una máscara (o más allá del lienzo), manteniendo el interior.
  • Edición de imágenes. Regenerar toda la imagen pero manteniendo la fidelidad semántica o estructural con respecto al original (SDEdit, InstructPix2Pix).

Todos los pipelines de difusión en 2026 incluyen un modo de inpainting. Flux.1-Fill, Stable Diffusion Inpaint, SDXL-Inpaint, DALL-E 3 Edit. Funcionan bajo el mismo principio.

El Concepto

Inpainting: eliminación de ruido consciente de la máscara con reinyección para preservar el contexto

The naive approach (and why it's wrong)

Ejecutar text-to-image estándar con una máscara. En cada paso de muestreo, reemplazar la región no enmascarada del latente ruidoso con la imagen limpia con difusión directa (forward-diffused). Funciona... mal. Los artefactos de los bordes se filtran porque el modelo no tiene información sobre lo que hay en la región enmascarada.

El modelo de inpainting adecuado

Entrenar una U-Net modificada que reciba 9 canales de entrada en lugar de 4:

input = concat([ noisy_latent (4ch), encoded_image (4ch), mask (1ch) ], dim=channel)

Los canales adicionales son una copia de la imagen original codificada por VAE más una máscara de canal único. En el momento del entrenamiento, enmascaras aleatoriamente regiones de la imagen y entrenas al modelo para eliminar el ruido solo de la región enmascarada, mientras que la región no enmascarada se proporciona como una señal de condicionamiento limpia. En la inferencia, el modelo puede "ver" lo que rodea a la región enmascarada y produce compleciones coherentes.

SD-Inpaint, SDXL-Inpaint, Flux-Fill utilizan esta entrada de 9 canales (o similar). Diffusers StableDiffusionInpaintPipeline, FluxFillPipeline.

SDEdit (Meng et al., 2022) — edición libre

Agregar ruido a la imagen de origen hasta un t intermedio, luego ejecutar la cadena inversa desde t hasta 0 con un nuevo prompt. Sin reentrenamiento. La elección del t inicial equilibra la fidelidad y la libertad creativa:

  • t/T = 0.3 → casi idéntico a la fuente original, pequeños cambios de estilo
  • t/T = 0.6 → ediciones moderadas, conserva la estructura general
  • t/T = 0.9 → generado a partir de casi puro ruido, preservación mínima de la fuente original

InstructPix2Pix (Brooks et al., 2023)

Ajustar un modelo de difusión en tríos de (input_image, instruction, output_image). En la inferencia, condicionar tanto en la imagen de entrada como en una instrucción de texto ("hacer que sea el atardecer", "agregar un dragón"). Dos escalas CFG: escala de imagen y escala de texto.

RePaint (Lugmayr et al., 2022)

Mantener un modelo de difusión incondicional estándar. En cada paso inverso, realizar un nuevo muestreo: volver ocasionalmente a un estado más ruidoso y regenerar. Evita los artefactos en los bordes. Se utiliza cuando no se dispone de un modelo de inpainting entrenado.

Build It

El archivo code/main.py implementa un esquema de inpainting didáctico 1D en datos de 5 dimensiones. Entrenamos un DDPM en datos mixtos 5D donde cada muestra consiste en 5 números de punto flotante de uno de los dos clústeres. En la inferencia, "enmascaramos" 2 de las 5 dimensiones, inyectamos la versión ruidosa directa (forward) de las tres dimensiones no enmascaradas en cada paso y regeneramos solo las dimensiones enmascaradas.

Paso 1: Datos del DDPM 5D

def sample_data(rng):
    cluster = rng.choice([0, 1])
    center = [-1.0] * 5 if cluster == 0 else [1.0] * 5
    return [c + rng.gauss(0, 0.2) for c in center], cluster

Paso 2: Entrenar el denoiser en las 5 dimensiones

DDPM estándar. La red genera una predicción de ruido 5D para una entrada ruidosa 5D.

Paso 3: En la inferencia, proceso inverso consciente de la máscara

def inpaint_step(x_t, mask, clean_image, alpha_bars, t, rng):
    # replace unmasked dims with a freshly noised version of the clean source
    a_bar = alpha_bars[t]
    for i in range(len(x_t)):
        if not mask[i]:
            x_t[i] = math.sqrt(a_bar) * clean_image[i] + math.sqrt(1 - a_bar) * rng.gauss(0, 1)
    # ...then run the normal reverse step on x_t

Este es el enfoque ingenuo y funciona en datos didácticos 1D. El inpainting de imágenes reales utiliza la entrada de 9 canales porque la coherencia de la textura es más importante.

Paso 4: Outpainting

El outpainting es inpainting con la máscara invertida: se enmascara el lienzo nuevo (que antes no existía) y se llena el resto con el original. El objetivo de entrenamiento es idéntico.

Errores Comunes

  • Bordes visibles (seams). El enfoque ingenuo deja límites visibles porque la información del gradiente no fluye a través de la máscara. Solución: dilatar la máscara en 8-16 píxeles o usar un modelo de inpainting adecuado.
  • Fuga de máscara (mask leakage). Si la región no enmascarada de la imagen de condicionamiento es de baja calidad o ruidosa, contamina la generación dentro de la máscara. Elimina el ruido o desenfoca ligeramente.
  • El CFG interactúa con el tamaño de la máscara. Un CFG alto en una máscara pequeña = parche saturado. Reduce el CFG para ediciones pequeñas.
  • Caída de fidelidad en SDEdit (fidelity cliff). Pasar de t/T = 0.5 a t/T = 0.6 puede hacer que se pierda la identidad del sujeto. Realiza barridos (sweeps) y guarda checkpoints.
  • Incompatibilidad de prompt (prompt mismatch). El prompt debe describir toda la imagen, no solo el nuevo contenido. "A cat sitting on a chair" en el lugar de "a cat".

Use It

Tarea Pipeline
Eliminar objeto, máscara pequeña SD-Inpaint o Flux-Fill, prompt estándar
Reemplazar cielo SD-Inpaint + "blue sky at sunset"
Extender lienzo Modo outpaint de SDXL (difuminado/feather de 8px) o Flux-Fill con máscara de outpaint
Regenerar mano / rostro SD-Inpaint con prompt que redescribe al sujeto + ControlNet-Openpose
Cambiar el estilo de una región SDEdit en t/T=0.5 en la región enmascarada
"Make it sunset" InstructPix2Pix o Flux-Kontext
Reemplazo de fondo Máscara SAM → SD-Inpaint
Fidelidad ultraalta Flux-Fill o GPT-Image (alojado) para los casos más difíciles

SAM (Segment Anything de Meta, 2023) + inpaint por difusión es el pipeline de eliminación de fondo para 2026. SAM 2 (2024) funciona en video.

Ship It

Guarda outputs/skill-editing-pipeline.md. La Skill toma una imagen original + descripción de la edición + máscara opcional (o prompt de SAM) y genera: enfoque de generación de máscara, modelo base, escalas CFG (imagen + texto), SDEdit-t o modo de inpainting, y una lista de verificación de control de calidad (QA checklist).

Ejercicios

  1. Fácil. En code/main.py, varía la fracción de dimensiones enmascaradas de 0.2 a 0.8. ¿A qué fracción la calidad del inpaint (residuo en las dimensiones enmascaradas) se iguala a la de la generación incondicional?
  2. Medio. Implementa RePaint: en cada décimo paso inverso, regresa 5 pasos (agrega ruido) y vuelve a eliminar el ruido. Mide si esto reduce el residuo de los bordes en el límite de la máscara.
  3. Difícil. Usa diffusers de Hugging Face para comparar: SD 1.5 Inpaint + ControlNet-Openpose vs Flux.1-Fill en 20 tareas de regeneración de rostros. Califica la adherencia de la pose y la preservación de la identidad por separado.

Términos Clave

Término Lo que la gente dice Lo que realmente significa
Inpainting "Llenar el hueco" Regenerar dentro de una máscara; mantener los píxeles externos.
Outpainting "Extender el lienzo" Regenerar fuera del lienzo; mantener el interior.
U-Net de 9 canales "Modelo de inpainting adecuado" U-Net con noisy | encoded-source | mask como entrada.
SDEdit "Img2img con nivel de ruido" Ruido hasta el tiempo t, eliminación de ruido con un nuevo prompt.
InstructPix2Pix "Ediciones solo de texto" Difusión ajustada (fine-tuned) en tríos de (imagen, instrucción, salida).
RePaint "Sin reentrenamiento" Volver a aplicar ruido periódicamente durante el proceso inverso para reducir los bordes visibles.
SAM "Segment Anything" Generador de máscaras mediante clics o cajas de selección; se complementa con inpaint.
Flux-Kontext "Editar con contexto" Variante de Flux que acepta una imagen de referencia + instrucción para realizar ediciones.

Nota de producción: los pipelines de edición son sensibles a la latencia

Los usuarios que editan una imagen esperan tiempos de respuesta (round trips) inferiores a 5 segundos. Un SDXL-Inpaint de 30 pasos a 1024² toma entre 3 y 4 segundos en una L4, más la generación de máscara con SAM (200 ms) y codificación/decodificación VAE (500 ms en total). En el contexto de producción, esto está limitado por el TTFT (Time to First Token) en lugar del rendimiento (throughput): lote (batch) de tamaño 1, baja concurrencia, minimizar cada etapa:

  • SAM-H es el lento. SAM-H a 1024² toma ~200 ms; SAM-ViT-B toma ~40 ms con una pérdida menor de calidad. SAM 2 (video) agrega sobrecarga temporal; no lo uses para ediciones de imágenes individuales.
  • Evita la codificación cuando sea posible. pipe.image_processor.preprocess(img) codifica en latentes. Si tienes los latentes de la generación anterior (común en UIs de edición iterativa), pásalos directamente a través de latents=... para omitir una codificación VAE.
  • La dilatación de la máscara también importa para el rendimiento. Una máscara pequeña significa que la mayor parte de la pasada directa (forward pass) de la U-Net se desperdicia (los píxeles no enmascarados se limitan de todos modos). La StableDiffusionInpaintPipeline de diffusers ejecuta la U-Net completa de todos modos; solo las variantes adecuadas de inpainting de 9 canales aprovechan el cómputo enmascarado.
  • Flux-Kontext es la respuesta para 2025. Una sola pasada directa sobre (source_image, instruction): sin máscara separada, sin barrido de ruido SDEdit. En una H100, entrega una edición en ~1.5 s. La lección arquitectónica: unificar las etapas.

Lectura Adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).