Chuyển đến nội dung chính

Lesson 8: Inpainting, Outpainting & Image Editing with AI

Inpainting: edit specific areas in the image. Outpainting: expand the image outward. Instruction-based editing: InstructPix2Pix. Background removal and replacement. Hands-on with Stable Diffusion inpainting pipeline.

🧠 AI & ML — Lesson 7 Lesson 8: Inpainting, Outpainting & Image Editing with AI

Generative AI: Create Images & Videos with AI

Part 3: Practicing Advanced Image Generation

xdev.asia

Introduction

Inpainting allows you to edit specific areas in photos using AI — change objects, remove watermarks, or add new details. Outpainting expands the image beyond the original frame. This is the foundation for AI-powered image editing.


1. Inpainting Pipeline

from diffusers import StableDiffusionXLInpaintPipeline
from PIL import Image
import torch

pipe = StableDiffusionXLInpaintPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    torch_dtype=torch.float16,
)
pipe.to("cuda")

# Original image + mask (white = area to edit)
image = Image.open("photo.png").resize((1024, 1024))
mask = Image.open("mask.png").resize((1024, 1024))

result = pipe(
    prompt="a golden retriever puppy",
    image=image,
    mask_image=mask,
    num_inference_steps=30,
    guidance_scale=7.5,
    strength=0.99,  # how much to change masked area
).images[0]

Create Mask programmatically

from PIL import Image, ImageDraw
import numpy as np

# Mask thủ công: rectangle vùng cần edit
mask = Image.new("L", (1024, 1024), 0)  # black = keep
draw = ImageDraw.Draw(mask)
draw.rectangle([300, 200, 700, 600], fill=255)  # white = edit

# Mask từ segmentation (SAM)
from segment_anything import SamPredictor, sam_model_registry

sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h.pth")
predictor = SamPredictor(sam)
predictor.set_image(np.array(image))
masks, _, _ = predictor.predict(point_coords=np.array([[500, 400]]),
                                  point_labels=np.array([1]))
mask = Image.fromarray(masks[0].astype(np.uint8) * 255)

2. Outpainting — Expanding the image

def outpaint(pipe, image, direction="right", extend_px=512):
    """Mở rộng ảnh bằng inpainting"""
    w, h = image.size

    if direction == "right":
        canvas = Image.new("RGB", (w + extend_px, h), (0, 0, 0))
        canvas.paste(image, (0, 0))
        mask = Image.new("L", (w + extend_px, h), 0)
        draw = ImageDraw.Draw(mask)
        draw.rectangle([w - 50, 0, w + extend_px, h], fill=255)  # overlap 50px
    elif direction == "down":
        canvas = Image.new("RGB", (w, h + extend_px), (0, 0, 0))
        canvas.paste(image, (0, 0))
        mask = Image.new("L", (w, h + extend_px), 0)
        draw = ImageDraw.Draw(mask)
        draw.rectangle([0, h - 50, w, h + extend_px], fill=255)

    result = pipe(
        prompt="continue the scene naturally",
        image=canvas,
        mask_image=mask,
        num_inference_steps=30,
    ).images[0]

    return result

3. InstructPix2Pix — Editing with Commands

from diffusers import StableDiffusionInstructPix2PixPipeline

pipe = StableDiffusionInstructPix2PixPipeline.from_pretrained(
    "timbrooks/instruct-pix2pix",
    torch_dtype=torch.float16,
)
pipe.to("cuda")

image = Image.open("photo.jpg")

# Chỉnh sửa bằng text instruction
edited = pipe(
    prompt="make it a winter scene with snow",
    image=image,
    image_guidance_scale=1.5,  # how close to original
    guidance_scale=7.0,
    num_inference_steps=20,
).images[0]

# Ví dụ khác
# "turn the cat into a tiger"
# "make it sunset lighting"
# "add flowers to the garden"
# "change the shirt color to blue"

4. Background Removal & Replacement

from rembg import remove
from PIL import Image

# Remove background
input_img = Image.open("person.jpg")
output = remove(input_img)  # transparent background
output.save("person_nobg.png")

# Replace background with AI
def replace_background(person_img, bg_prompt, pipe):
    # 1. Remove background
    fg = remove(person_img)

    # 2. Generate new background
    bg = pipe(
        prompt=bg_prompt,
        num_inference_steps=25,
        width=person_img.width,
        height=person_img.height,
    ).images[0]

    # 3. Composite
    bg.paste(fg, (0, 0), fg)  # paste with alpha mask
    return bg

result = replace_background(
    Image.open("person.jpg"),
    "a tropical beach at sunset, palm trees",
    pipe
)

5. Object Removal

from diffusers import StableDiffusionXLInpaintPipeline
from segment_anything import SamPredictor

# 1. Select object to remove (click point)
predictor.set_image(np.array(image))
masks, _, _ = predictor.predict(
    point_coords=np.array([[x, y]]),  # click on object
    point_labels=np.array([1])
)

# 2. Dilate mask slightly
import cv2
kernel = np.ones((15, 15), np.uint8)
dilated = cv2.dilate(masks[0].astype(np.uint8), kernel) * 255
mask = Image.fromarray(dilated)

# 3. Inpaint to remove
result = pipe(
    prompt="empty background, natural scenery",
    image=image,
    mask_image=mask,
    num_inference_steps=30,
).images[0]

6. Batch Image Editing

import os
from pathlib import Path

def batch_edit(input_dir, output_dir, pipe, edit_prompt):
    """Batch edit images with InstructPix2Pix"""
    Path(output_dir).mkdir(exist_ok=True)

    for img_file in Path(input_dir).glob("*.{jpg,png}"):
        image = Image.open(img_file).resize((512, 512))
        edited = pipe(
            prompt=edit_prompt,
            image=image,
            num_inference_steps=20,
        ).images[0]
        edited.save(Path(output_dir) / img_file.name)
        print(f"✓ {img_file.name}")

batch_edit("photos/", "edited/", pipe, "make it a pencil sketch")

Summary

EngineeringDescriptionUse cases
InpaintingEdit mask areaObject replacement
OutpaintingExpand photoExtend composition
InstructPix2PixEdit with textStyle changes
Background removalSplit foregroundProduct photos
Object removalDelete objectClean up images

📌 Next article: LoRA & Custom Model Training — create your own style.