인페인팅, 아웃페인팅과 이미지 편집(Inpainting, Outpainting & Image Editing)
텍스트-투-이미지(Text-to-Image)는 새로운 것을 만들고, 인페인팅(Inpainting)은 기존 이미지를 고칩니다. 실제 운영(Production) 현장에서 비용이 청구되는 이미지 작업의 70% 정도는 편집입니다. 배경을 바꾸고, 로고를 지우고, 캔버스를 확장하고, 손을 다시 생성하는 일들이죠. 인페인팅은 디퓨전(Diffusion)이 실제로 값을 만들어내는 지점입니다.
유형: Build
언어: Python
선수 지식: Phase 8 · 07 (잠재 디퓨전, Latent Diffusion), Phase 8 · 08 (ControlNet과 LoRA)
예상 시간: 약 75분
문제
어떤 고객이 완벽한 제품 사진을 보냈는데, 배경에 신경 쓰이는 간판(sign) 하나가 들어가 있다고 해봅시다. 우리는 간판만 지우고 나머지 픽셀(pixel)은 원본과 완전히 똑같이 유지하고 싶습니다. 이때 텍스트-투-이미지를 처음부터 다시 돌리면 색감, 조명, 제품 각도가 모두 달라져 버립니다. 우리가 원하는 것은 마스크(mask)로 지정한 영역만 다시 생성하면서, 그 영역이 주변 맥락(surrounding context)과 자연스럽게 어울리도록 만드는 것입니다.
이것이 바로 인페인팅(Inpainting)입니다. 변형은 다음과 같습니다.
인페인팅(Inpainting). 마스크 안쪽만 다시 생성하고, 바깥쪽 픽셀은 그대로 유지합니다.
아웃페인팅(Outpainting). 마스크 바깥, 또는 기존 캔버스(canvas) 너머의 영역을 새로 생성하고, 안쪽은 그대로 유지합니다.
이미지 편집(Image Editing). 이미지 전체를 다시 생성하되, 원본의 의미적(semantic) 또는 구조적(structural) 충실도(fidelity)는 보존합니다. SDEdit, InstructPix2Pix가 이 범주에 속합니다.
2026년의 거의 모든 디퓨전 파이프라인은 인페인팅 모드를 함께 제공합니다. Flux.1-Fill, Stable Diffusion Inpaint, SDXL-Inpaint, DALL-E 3 Edit 모두 같은 원리 위에서 동작합니다.
사전 테스트
2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요
1.제품 사진에서 거슬리는 간판 하나만 지우고 나머지 픽셀(pixel)은 그대로 유지해야 합니다. 텍스트-투-이미지(Text-to-Image)를 처음부터 다시 돌리는 것이 좋지 않은 이유는 무엇인가요?
2.아웃페인팅(Outpainting)과 인페인팅(Inpainting)을 구분하는 핵심 차이는 무엇인가요?
0/2 답변 완료
개념
순진한 접근(Naive approach)과 그 한계
가장 단순한 방법은 일반적인 텍스트-투-이미지에 마스크를 씌우는 것입니다. 샘플링 단계마다 마스크 바깥 영역(unmasked region)의 노이즈가 섞인 잠재 표현(noisy latent)을 깨끗한 이미지의 정방향 확산(forward-diffused) 결과로 교체합니다. 동작은 하지만 결과가 나쁩니다. 모델은 마스크 안쪽 영역에 무엇이 있었는지 전혀 모른 채로 생성하기 때문에 경계 부분에서 아티팩트(boundary artifact)가 번져 나옵니다.
추가된 채널은 VAE로 인코딩한 원본 이미지(encoded source image)와 단일 채널 마스크(single-channel mask)입니다. 학습 단계에서는 이미지의 일부 영역을 무작위로 가리고(random mask), 가리지 않은 영역(unmasked region)은 깨끗한 조건 신호(clean conditioning signal)로 제공하면서 가린 영역(masked region)만 디노이즈(denoise)하도록 학습시킵니다. 추론 단계에서는 모델이 마스크 주변을 "볼 수 있기" 때문에, 주변과 자연스럽게 이어지는 완성(coherent completion)을 만들어 냅니다.
SD-Inpaint, SDXL-Inpaint, Flux-Fill은 모두 이 9채널(혹은 유사한 형태의) 입력을 사용합니다. Diffusers 라이브러리에서는 StableDiffusionInpaintPipeline, FluxFillPipeline이 여기에 해당합니다.
SDEdit (Meng et al., 2022) — 학습 없는 자유 편집
SDEdit은 원본 이미지에 중간 시점(intermediate t)까지 노이즈를 더한 뒤, 새 프롬프트로 t에서 0까지 역방향 체인(reverse chain)을 실행하는 방법입니다. 재학습(retraining)이 필요 없습니다. 시작 시점 t를 어떻게 잡느냐에 따라 충실도(fidelity)와 창작 자유도(creative freedom) 사이의 균형이 달라집니다.
t/T = 0.3 → 원본과 거의 동일하고, 작은 스타일 변화만 일어납니다.
t/T = 0.6 → 중간 정도의 편집, 거친 구조(coarse structure)는 보존됩니다.
t/T = 0.9 → 노이즈에 가까운 상태에서 생성되므로 원본 보존(source preservation)은 거의 사라집니다.
InstructPix2Pix (Brooks et al., 2023)
디퓨전 모델을 (input_image, instruction, output_image) 삼중쌍(triple)으로 미세 조정(fine-tune)하는 방식입니다. 추론에서는 입력 이미지와 텍스트 지시문("make it sunset", "add a dragon")을 동시에 조건(condition)으로 사용합니다. 이미지 스케일(image scale)과 텍스트 스케일(text scale), 두 개의 CFG 스케일을 사용합니다.
RePaint (Lugmayr et al., 2022)
RePaint는 일반적인 무조건(unconditional) 디퓨전 모델을 그대로 사용합니다. 역방향 진행 중에 가끔 더 노이즈가 많은 상태로 점프 백(jump back)했다가 다시 디노이즈하는 재샘플링(resample) 전략을 씁니다. 이렇게 하면 경계 아티팩트가 줄어듭니다. 별도로 학습된 인페인팅 모델이 없을 때 쓰기 좋은 방법입니다.
직접 만들기
code/main.py는 5차원 데이터 위에서 동작하는 장난감 수준의 1차원 인페인팅 스킴(toy 1-D inpainting scheme)을 구현합니다. 두 클러스터(cluster) 중 하나에서 뽑힌 5개의 실수(float)로 구성된 5차원 혼합 데이터(5-D mixture data)에 DDPM을 학습시킵니다. 추론에서는 5개 차원 중 2개를 "가리고(mask)", 가리지 않은 3개 차원은 각 단계마다 정방향으로 새로 노이즈를 입힌(noised-forward) 값으로 주입하면서, 가린 차원만 다시 생성합니다.
Step 1: 5차원 DDPM 데이터
defsample_data(rng):
cluster = rng.choice([0, 1])
center = [-1.0] * 5if cluster == 0else [1.0] * 5return [c + rng.gauss(0, 0.2) for c in center], cluster
Step 2: 5개 차원 전체에 대해 디노이저 학습
표준 DDPM입니다. 네트워크는 5차원 노이지 입력(noisy input)을 받아 5차원 노이즈 예측(noise prediction)을 출력합니다.
Step 3: 추론 단계의 마스크 인식 역방향(Mask-aware reverse)
definpaint_step(x_t, mask, clean_image, alpha_bars, t, rng):
# 가리지 않은 차원은 깨끗한 원본을 새로 노이즈로 덮어쓴 값으로 교체한다
a_bar = alpha_bars[t]
for i inrange(len(x_t)):
ifnot mask[i]:
x_t[i] = math.sqrt(a_bar) * clean_image[i] + math.sqrt(1 - a_bar) * rng.gauss(0, 1)
# ...그 다음 x_t에 대해 일반적인 reverse step을 수행한다
이것이 순진한 접근(naive approach)이며, 장난감 1차원 데이터에서는 잘 동작합니다. 실제 이미지 인페인팅에서는 텍스처 일관성(texture coherence)이 더 중요하기 때문에 9채널 입력 방식을 사용합니다.
Step 4: 아웃페인팅(Outpainting)
아웃페인팅은 마스크가 뒤집힌 인페인팅입니다. 새로 만들 (이전에는 존재하지 않던) 캔버스 영역을 마스크로 지정하고, 나머지는 원본으로 채웁니다. 학습 목표(training objective)는 인페인팅과 동일합니다.
함정(Pitfalls)
이음매(Seams). 순진한 접근은 마스크 경계에 눈에 보이는 경계선(visible boundaries)을 남깁니다. 그래디언트 정보가 마스크를 가로질러 흐르지 않기 때문입니다. 해결책은 마스크를 8~16픽셀 정도 확장(dilate)하거나, 제대로 된 인페인팅 모델을 사용하는 것입니다.
마스크 누수(Mask leakage). 조건 이미지(conditioning image)의 가리지 않은 영역이 노이즈가 많거나 품질이 낮으면, 마스크 안쪽 생성까지 오염됩니다. 약간 디노이즈하거나 살짝 블러(blur)를 줍니다.
CFG와 마스크 크기의 상호작용. 작은 마스크에 높은 CFG를 사용하면 색이 포화된(saturated) 패치가 만들어집니다. 작은 편집에는 CFG를 낮추세요.
SDEdit 충실도 절벽(Fidelity cliff).t/T = 0.5에서 0.6으로 살짝만 올려도 피사체의 정체성(subject identity)을 잃을 수 있습니다. 값을 훑어가며(sweep) 체크포인트를 남기세요.
프롬프트 불일치(Prompt mismatch). 프롬프트는 새로 생성될 부분만이 아니라 이미지 전체를 묘사해야 합니다. "a cat"이 아니라 "a cat sitting on a chair"입니다.
사용해보기
작업
파이프라인
작은 마스크로 객체 제거
SD-Inpaint 또는 Flux-Fill, 일반 프롬프트
하늘 교체
SD-Inpaint + "blue sky at sunset"
캔버스 확장
SDXL 아웃페인트 모드(8px 페더링) 또는 Flux-Fill의 outpaint 마스크
손/얼굴 재생성
피사체를 다시 묘사하는 프롬프트 + ControlNet-Openpose + SD-Inpaint
한 영역의 스타일 변경
마스크 영역에 t/T=0.5 SDEdit
"Make it sunset" 같은 지시 편집
InstructPix2Pix 또는 Flux-Kontext
배경 교체
SAM 마스크 → SD-Inpaint
초고충실도(Ultra-high-fidelity)
가장 어려운 케이스에는 Flux-Fill 또는 호스티드(hosted) GPT-Image
SAM(Meta의 Segment Anything, 2023)과 디퓨전 인페인트 조합은 2026년 기준 배경 제거(background removal) 파이프라인의 표준입니다. SAM 2(2024)는 비디오에도 동작합니다.
산출물 만들기
outputs/skill-editing-pipeline.md를 작성합니다. 이 스킬은 원본 이미지와 편집 설명, 그리고 선택적으로 마스크나 SAM 프롬프트를 입력으로 받아, 마스크 생성 방식(mask-generation approach), 베이스 모델(base model), CFG 스케일(이미지와 텍스트 양쪽), SDEdit-t 또는 인페인팅 모드, 그리고 품질 보증(QA) 체크리스트를 출력합니다.
연습문제
쉬움.code/main.py에서 마스킹되는 차원의 비율을 0.2부터 0.8까지 바꿔봅니다. 어느 비율에서 인페인트 품질(가린 차원의 잔차)이 무조건 생성(unconditional generation)과 같아지는지 확인합니다.
중간. RePaint를 구현해 봅니다. 매 10번째 역방향 단계마다 5단계 뒤로 점프 백(노이즈를 다시 추가)하고 디노이즈를 반복합니다. 마스크 가장자리의 경계 잔차(boundary residual)가 줄어드는지 측정합니다.
어려움. Hugging Face의 diffusers를 사용해 SD 1.5 Inpaint + ControlNet-Openpose와 Flux.1-Fill을 20개의 얼굴 재생성 과제에서 비교합니다. 자세 일치도(pose adherence)와 정체성 보존(identity preservation)을 별도로 채점합니다.
핵심 용어
용어
흔한 설명
실제 의미
인페인팅(Inpainting)
"구멍 채우기"
마스크 안쪽을 다시 생성하고 바깥 픽셀은 유지한다.
아웃페인팅(Outpainting)
"캔버스 확장"
캔버스 바깥을 새로 생성하고 안쪽은 유지한다.
9채널 U-Net(9-channel U-Net)
"제대로 된 인페인팅 모델"
`noisy
SDEdit
"노이즈 레벨이 있는 img2img"
시점 t까지 노이즈를 더한 뒤 새 프롬프트로 디노이즈한다.
InstructPix2Pix
"텍스트만으로 하는 편집"
(이미지, 지시문, 출력) 삼중쌍으로 미세 조정한 디퓨전 모델이다.
RePaint
"재학습 없음"
역방향 진행 중 주기적으로 다시 노이즈를 더해 이음매를 줄인다.
SAM(Segment Anything)
"Segment Anything"
클릭이나 박스로 마스크를 생성하는 모델이다. 인페인트와 자주 짝지어 쓴다.
Flux-Kontext
"맥락으로 편집"
참조 이미지와 지시문을 입력받아 편집을 수행하는 Flux 변형이다.
운영 노트: 편집 파이프라인은 지연(latency)에 민감하다
이미지를 편집하는 사용자는 보통 5초 미만의 응답 시간을 기대합니다. 1024² 해상도에서 30스텝 SDXL-Inpaint는 L4 GPU 기준 약 3~4초, SAM 마스크 생성은 약 200ms, VAE 인코드/디코드는 합쳐서 약 500ms 정도 걸립니다. 운영 관점에서 이는 처리량(throughput)이 아니라 첫 토큰까지의 시간(TTFT) 쪽에 가까운 문제이며, 배치(batch) 1, 낮은 동시성(low concurrency) 환경에서 모든 단계를 최소화해야 합니다.
SAM-H가 가장 느린 단계입니다. 1024²에서 SAM-H는 약 200ms이고, SAM-ViT-B는 품질 손실은 작으면서 약 40ms입니다. SAM 2(video)는 시간적(temporal) 오버헤드가 추가되므로 단일 이미지 편집에는 사용하지 않습니다.
가능하면 인코드를 건너뛰세요.pipe.image_processor.preprocess(img)는 잠재 표현(latent)으로 인코드하는 단계입니다. 반복 편집(iterative-edit) UI처럼 이전 생성의 잠재 표현이 이미 있는 경우, latents=...로 직접 넘기면 VAE 인코드 하나를 건너뛸 수 있습니다.
마스크 확장(dilation)은 처리량에도 영향을 줍니다. 마스크가 작으면 U-Net 순전파의 대부분이 낭비됩니다(어차피 가리지 않은 픽셀은 잘려나가니까요). diffusers의 StableDiffusionInpaintPipeline은 마스크 크기와 상관없이 U-Net 전체를 실행하며, 9채널 기반의 제대로 된 인페인트 변형(proper-inpaint variants)만이 마스킹된 영역에 한정해 연산을 수행할 수 있습니다.
Flux-Kontext가 2025년식 정답입니다.(source_image, instruction)에 대한 단일 순전파(single forward pass) — 별도 마스크도, SDEdit 노이즈 스윕도 없습니다. H100에서 약 1.5초 만에 한 번의 편집을 내놓습니다. 여기서 얻는 아키텍처적 교훈은, 단계들을 하나로 합쳐서(collapse) 줄이라는 것입니다.