이미지 로딩 중...
AI Generated
2025. 11. 8. · 3 Views
AI 이미지 생성 Inpainting과 Outpainting 완벽 가이드
Stable Diffusion의 Inpainting과 Outpainting 기법을 마스터하여 이미지의 특정 영역을 수정하거나 확장하는 방법을 배웁니다. 실무에서 바로 활용 가능한 Python 코드와 함께 각 기법의 핵심 원리와 활용 사례를 상세히 다룹니다.
목차
- Inpainting 기초
- 마스크 생성과 활용
- Outpainting 기초
- 컨텍스트 보존 Inpainting
- 다중 영역 Inpainting
- Inpainting 파이프라인 최적화
- 프롬프트 가이드 Outpainting
- 이미지 복원과 Inpainting
1. Inpainting 기초
시작하며
여러분이 완벽하게 생성된 AI 이미지에서 단 하나의 요소만 바꾸고 싶을 때가 있나요? 전체 이미지를 다시 생성하면 원하던 다른 부분들까지 모두 바뀌어버리는 상황을 겪어본 적 있으실 겁니다.
이런 문제는 실제 AI 이미지 생성 작업에서 가장 자주 발생하는 고민입니다. 전체를 재생성하면 시간도 오래 걸리고, 원하던 구도나 색감을 다시 얻기 어렵습니다.
특히 클라이언트 작업에서 "이 부분만 살짝 바꿔주세요"라는 요청을 받았을 때 난감해집니다. 바로 이럴 때 필요한 것이 Inpainting입니다.
이미지의 특정 영역만 선택적으로 수정하면서 나머지 부분은 그대로 유지할 수 있습니다.
개요
간단히 말해서, Inpainting은 이미지의 선택된 영역만 AI가 다시 그리도록 하는 기술입니다. 마스크로 지정한 부분만 변경되고 나머지는 완벽하게 보존됩니다.
이 기법이 필요한 이유는 효율성과 정확성 때문입니다. 전체 이미지를 재생성하는 대신 문제가 되는 부분만 수정하면 시간과 컴퓨팅 자원을 절약할 수 있습니다.
예를 들어, 인물 사진에서 배경은 마음에 드는데 옷 색상만 바꾸고 싶을 때 매우 유용합니다. 기존에는 전체 이미지를 여러 번 재생성하면서 원하는 결과를 기다려야 했다면, 이제는 Inpainting으로 특정 부분만 몇 초 만에 수정할 수 있습니다.
Inpainting의 핵심 특징은 컨텍스트 인식입니다. AI가 마스크 주변의 내용을 분석하여 자연스럽게 어울리는 결과를 생성합니다.
또한 마스크 경계에서 부드러운 블렌딩을 통해 이어붙인 티가 나지 않습니다. 이러한 특징들이 전문적인 이미지 편집 작업에서 필수적인 이유입니다.
코드 예제
from diffusers import StableDiffusionInpaintPipeline
from PIL import Image
import torch
# Inpainting 전용 파이프라인 로드
pipe = StableDiffusionInpaintPipeline.from_pretrained(
"runwayml/stable-diffusion-inpainting",
torch_dtype=torch.float16
).to("cuda")
# 원본 이미지와 마스크 로드 (마스크는 흰색 부분이 수정될 영역)
init_image = Image.open("original.png").resize((512, 512))
mask_image = Image.open("mask.png").resize((512, 512))
# 마스크 영역만 새로운 내용으로 채우기
prompt = "a red rose in a vase"
result = pipe(
prompt=prompt,
image=init_image,
mask_image=mask_image,
num_inference_steps=50,
guidance_scale=7.5
).images[0]
result.save("inpainted_result.png")
설명
이것이 하는 일: 이 코드는 이미지의 마스크된 부분만 선택적으로 재생성하여 원하는 요소로 교체합니다. 원본 이미지의 컨텍스트를 유지하면서 자연스러운 결과를 만들어냅니다.
첫 번째로, StableDiffusionInpaintPipeline을 로드합니다. 일반 text-to-image 모델과 달리 이 파이프라인은 inpainting에 특화되어 학습되었습니다.
마스크 영역과 주변 컨텍스트를 함께 이해하도록 훈련되어 더 자연스러운 결과를 생성합니다. float16을 사용하여 메모리 사용량을 절반으로 줄이면서도 품질은 유지합니다.
그 다음으로, 원본 이미지와 마스크 이미지를 준비합니다. 마스크는 흰색(255) 부분이 수정될 영역이고 검은색(0) 부분은 보존될 영역입니다.
두 이미지를 동일한 크기로 리사이즈하는 것이 중요한데, 크기가 다르면 마스크가 올바른 위치에 적용되지 않습니다. 512x512는 Stable Diffusion의 최적 해상도입니다.
마지막으로, pipe 함수에 prompt, 원본 이미지, 마스크를 모두 전달하여 inpainting을 실행합니다. num_inference_steps는 생성 품질을 결정하는데, 50 정도면 대부분의 경우 충분히 자연스러운 결과를 얻을 수 있습니다.
guidance_scale 7.5는 프롬프트를 얼마나 엄격하게 따를지 결정하며, 너무 높으면 부자연스러워지고 너무 낮으면 프롬프트와 다른 결과가 나옵니다. 여러분이 이 코드를 사용하면 이미지 수정 작업 시간을 90% 이상 단축할 수 있습니다.
전체 재생성 없이 정확히 원하는 부분만 바꿀 수 있어 클라이언트 피드백 대응이 빨라지고, 마스크 주변과 자연스럽게 블렌딩되어 전문적인 품질을 유지합니다.
실전 팁
💡 마스크 이미지는 반드시 흑백(grayscale)으로 준비하세요. 흰색(255)이 수정 영역이고, 회색은 부분적 수정, 검은색(0)은 보존 영역입니다. 컬러 이미지를 마스크로 사용하면 예상치 못한 결과가 나올 수 있습니다.
💡 마스크 경계를 약간 블러 처리하면 더 자연스러운 블렌딩이 가능합니다. Photoshop이나 GIMP에서 Gaussian Blur(반경 2-5px)를 적용하면 경계선이 눈에 띄지 않게 됩니다.
💡 프롬프트에 원본 이미지의 전체적인 스타일이나 분위기를 언급하면 더 일관성 있는 결과를 얻습니다. 예: "red rose in a vase, photorealistic, soft lighting"처럼 원본의 조명이나 화풍을 포함하세요.
💡 strength 파라미터(0.01.0)를 조절하여 원본을 얼마나 유지할지 제어할 수 있습니다. 0.70.9 정도가 적당하며, 너무 낮으면 변화가 없고 너무 높으면 원본과 동떨어진 결과가 나옵니다.
💡 복잡한 영역을 수정할 때는 num_inference_steps을 70100으로 높이세요. 단순한 수정은 3040 스텝으로도 충분하지만, 얼굴이나 텍스트 같은 디테일은 더 많은 스텝이 필요합니다.
2. 마스크 생성과 활용
시작하며
여러분이 Inpainting을 시도할 때 가장 먼저 막히는 부분이 무엇인가요? 바로 "어떻게 마스크를 만들지"입니다.
포토샵을 켜서 수작업으로 그리기에는 번거롭고, 정확한 영역을 선택하기도 어렵습니다. 이런 문제는 특히 여러 이미지를 빠르게 처리해야 하는 상황에서 심각해집니다.
한 장씩 이미지 편집 프로그램을 열어서 마스크를 그리고 저장하는 과정은 너무 비효율적입니다. 게다가 복잡한 형태의 객체를 정확하게 선택하려면 상당한 시간과 노력이 필요합니다.
바로 이럴 때 필요한 것이 프로그래매틱 마스크 생성입니다. Python 코드로 자동화하여 일관성 있고 빠르게 마스크를 생성할 수 있습니다.
개요
간단히 말해서, 마스크 생성은 이미지에서 수정할 영역과 보존할 영역을 구분하는 흑백 이미지를 만드는 과정입니다. 코드로 자동화하면 반복 작업을 수백 배 빠르게 처리할 수 있습니다.
마스크가 중요한 이유는 Inpainting의 정확성을 결정하기 때문입니다. 마스크가 너무 크면 불필요한 부분까지 바뀌고, 너무 작으면 원하는 효과를 얻지 못합니다.
예를 들어, 인물의 옷만 바꾸고 싶다면 얼굴이나 배경이 포함되지 않도록 정확한 마스크가 필요합니다. 기존에는 Photoshop에서 레이어 마스크를 그리고 PNG로 내보내는 수작업을 했다면, 이제는 PIL이나 OpenCV로 특정 색상 범위, 객체 검출, 또는 세그멘테이션 모델을 활용해 자동으로 마스크를 생성할 수 있습니다.
마스크 생성의 핵심 기법은 임계값(threshold) 기반, 색상 범위 선택, AI 세그멘테이션 세 가지입니다. 임계값은 간단하지만 효과적이고, 색상 범위는 특정 색상의 객체 선택에 유용하며, AI 세그멘테이션은 복잡한 형태도 정확하게 감지합니다.
상황에 맞는 방법을 선택하는 것이 효율적인 워크플로우의 핵심입니다.
코드 예제
from PIL import Image, ImageDraw
import numpy as np
# 방법 1: 직사각형 영역 마스크 생성
def create_rect_mask(width, height, x, y, w, h):
# 검은색 배경 (보존 영역)
mask = Image.new('L', (width, height), 0)
draw = ImageDraw.Draw(mask)
# 흰색 직사각형 (수정 영역)
draw.rectangle([x, y, x+w, y+h], fill=255)
return mask
# 방법 2: 색상 기반 마스크 생성 (특정 색상 범위 선택)
def create_color_mask(image, lower_color, upper_color):
img_array = np.array(image)
# 색상 범위 내의 픽셀을 흰색으로
mask = np.all((img_array >= lower_color) & (img_array <= upper_color), axis=-1)
mask_image = Image.fromarray((mask * 255).astype(np.uint8))
return mask_image
# 사용 예시
mask1 = create_rect_mask(512, 512, 100, 100, 200, 200)
mask1.save("rect_mask.png")
# 빨간색 계열 객체 선택
image = Image.open("original.png")
red_mask = create_color_mask(image, [180, 0, 0], [255, 100, 100])
red_mask.save("color_mask.png")
설명
이것이 하는 일: 이 코드는 두 가지 방법으로 마스크를 프로그래매틱하게 생성합니다. 직사각형 영역 선택과 색상 기반 선택을 통해 수작업 없이 정확한 마스크를 만듭니다.
첫 번째로, create_rect_mask 함수는 가장 기본적인 직사각형 마스크를 생성합니다. Image.new('L', ...)로 grayscale 이미지를 만들고 초기값 0(검은색)으로 채웁니다.
ImageDraw를 사용하여 지정된 좌표에 흰색(255) 직사각형을 그립니다. 이 방법은 배경 제거나 특정 사각 영역 수정에 유용합니다.
예를 들어 이미지 중앙의 로고를 바꾸거나 모서리의 워터마크를 제거할 때 사용합니다. 두 번째로, create_color_mask 함수는 더 정교한 색상 기반 선택을 수행합니다.
이미지를 numpy 배열로 변환하여 각 픽셀의 RGB 값을 검사합니다. lower_color와 upper_color 사이의 모든 픽셀을 선택하여 마스크로 만듭니다.
이 기법은 특정 색상의 객체만 선택적으로 수정할 때 강력합니다. 예를 들어 빨간 꽃만 파란색으로 바꾸거나, 초록색 배경을 다른 장면으로 교체할 수 있습니다.
numpy의 브로드캐스팅과 논리 연산을 활용하여 모든 픽셀을 한 번에 처리하므로 매우 빠릅니다. 512x512 이미지도 1초 이내에 마스크를 생성할 수 있습니다.
mask * 255는 True/False 값을 0/255로 변환하여 표준 grayscale 이미지 형식으로 만듭니다. 여러분이 이 코드를 사용하면 마스크 생성 작업을 자동화하여 배치 처리가 가능해집니다.
수백 장의 이미지에 동일한 위치의 마스크를 적용하거나, 특정 색상의 모든 객체를 일괄 수정할 수 있습니다. 또한 마스크를 저장하여 재사용할 수 있어 동일한 영역을 다양한 프롬프트로 실험할 때 편리합니다.
실전 팁
💡 복잡한 형태의 마스크가 필요하면 SAM(Segment Anything Model)이나 U-2-Net 같은 세그멘테이션 모델을 활용하세요. 이들은 객체의 정확한 윤곽을 자동으로 감지하여 픽셀 단위로 정교한 마스크를 생성합니다.
💡 마스크 경계를 부드럽게 하려면 ImageFilter.GaussianBlur를 적용하세요. mask.filter(ImageFilter.GaussianBlur(radius=3))로 경계를 블러 처리하면 inpainting 결과가 훨씬 자연스럽습니다.
💡 여러 조건을 조합한 복잡한 마스크는 numpy 배열 연산으로 만들 수 있습니다. 예: (색상 범위 AND 특정 영역) OR (다른 색상 범위) 같은 복합 조건을 논리 연산자로 구현하세요.
💡 마스크를 시각화하여 확인하는 습관을 들이세요. mask.show()로 생성된 마스크를 먼저 확인하고, 원본 이미지와 겹쳐서(alpha blending) 정확한 영역이 선택되었는지 검증하세요.
💡 동적 마스크 생성 시 안전 마진을 두세요. 객체 경계보다 2-3px 더 넓게 마스크를 만들면 경계 아티팩트를 방지할 수 있습니다. ImageOps.expand()로 마스크를 확장할 수 있습니다.
3. Outpainting 기초
시작하며
여러분이 완벽한 이미지를 생성했는데 구도가 약간 답답하게 느껴진 적 있나요? 좌우나 상하로 조금만 더 넓으면 훨씬 좋을 것 같은데, 전체를 다시 생성하면 원하던 중심 부분까지 바뀌어버립니다.
이런 문제는 특히 배경 이미지나 파노라마 제작 시 자주 발생합니다. 기존 이미지를 버리고 더 큰 캔버스로 재생성하면 원래 마음에 들었던 구성 요소들을 잃게 됩니다.
또한 여러 번 시도해도 똑같은 결과를 얻기 어려워 시간과 리소스가 낭비됩니다. 바로 이럴 때 필요한 것이 Outpainting입니다.
기존 이미지를 중심에 고정하고 주변을 확장하여 더 넓은 장면을 만들 수 있습니다.
개요
간단히 말해서, Outpainting은 기존 이미지의 경계를 넘어 새로운 내용을 생성하여 캔버스를 확장하는 기술입니다. 원본은 그대로 유지하면서 사방으로 이미지를 늘릴 수 있습니다.
이 기법이 필요한 이유는 유연성과 창의성 때문입니다. 이미 생성된 좋은 이미지를 활용하면서 더 넓은 컨텍스트를 추가할 수 있습니다.
예를 들어, 인물 초상화를 전신 사진으로 확장하거나, 좁은 풍경을 파노라마로 넓힐 때 매우 효과적입니다. 기존에는 이미지 편집 소프트웨어에서 캔버스를 키우고 Content-Aware Fill로 채워야 했다면, 이제는 AI가 컨텍스트를 이해하여 자연스럽고 창의적인 확장을 자동으로 생성합니다.
Outpainting의 핵심 원리는 Inpainting의 반대입니다. 원본 이미지 주변에 빈 영역을 만들고 그 부분을 마스크로 지정하여 채웁니다.
AI는 원본 이미지의 경계 정보를 분석하여 자연스럽게 이어지는 내용을 생성합니다. 이러한 방식이 단순 확대나 반복과 달리 완전히 새로운 디테일을 만들어내는 이유입니다.
코드 예제
from diffusers import StableDiffusionInpaintPipeline
from PIL import Image
import torch
pipe = StableDiffusionInpaintPipeline.from_pretrained(
"runwayml/stable-diffusion-inpainting",
torch_dtype=torch.float16
).to("cuda")
# 원본 이미지를 더 큰 캔버스 중앙에 배치
original = Image.open("portrait.png")
orig_w, orig_h = original.size
# 좌우로 256px씩 확장 (총 512px 추가)
new_width = orig_w + 512
canvas = Image.new('RGB', (new_width, orig_h), (128, 128, 128))
canvas.paste(original, (256, 0)) # 중앙에 원본 배치
# 마스크: 양쪽 확장 영역만 흰색
mask = Image.new('L', (new_width, orig_h), 0)
from PIL import ImageDraw
draw = ImageDraw.Draw(mask)
draw.rectangle([0, 0, 255, orig_h], fill=255) # 왼쪽
draw.rectangle([new_width-256, 0, new_width, orig_h], fill=255) # 오른쪽
# 확장 영역 생성
prompt = "natural outdoor background, trees and sky, photorealistic"
result = pipe(prompt=prompt, image=canvas, mask_image=mask,
num_inference_steps=50).images[0]
result.save("outpainted.png")
설명
이것이 하는 일: 이 코드는 원본 이미지를 중앙에 유지하면서 좌우로 캔버스를 확장하고, 새로 추가된 영역을 AI가 채워 넣습니다. 마치 카메라를 뒤로 빼서 더 넓은 장면을 촬영한 것처럼 보입니다.
첫 번째로, 원본 이미지보다 큰 캔버스를 생성합니다. Image.new('RGB', ...)로 확장된 크기의 빈 이미지를 만들고 중간 회색(128, 128, 128)으로 채웁니다.
회색을 사용하는 이유는 AI가 이 부분을 중립적인 영역으로 인식하여 더 자유롭게 생성할 수 있기 때문입니다. canvas.paste()로 원본을 정확히 중앙에 배치하여 양쪽으로 균등하게 확장 공간을 만듭니다.
두 번째로, 확장 영역만 선택하는 마스크를 생성합니다. 원본이 위치한 중앙 부분은 검은색(0)으로 보존하고, 좌우 새로 추가된 256px씩은 흰색(255)으로 표시합니다.
이렇게 하면 AI는 오직 확장 영역만 생성하고 원본은 전혀 건드리지 않습니다. draw.rectangle로 정확한 픽셀 좌표를 지정하는 것이 중요합니다.
마지막으로, inpainting 파이프라인을 사용하지만 용도는 outpainting입니다. 프롬프트에 원본 이미지의 스타일과 어울리는 내용을 지정합니다.
"natural outdoor background"처럼 확장 영역에 들어갈 내용을 구체적으로 명시하면 더 일관성 있는 결과를 얻습니다. AI는 원본의 경계 정보를 참고하여 자연스럽게 연결되는 배경을 생성합니다.
여러분이 이 코드를 사용하면 이미지의 활용도를 크게 높일 수 있습니다. 스퀘어 이미지를 와이드스크린으로 확장하거나, 세로 사진을 가로로 넓혀서 다양한 종횡비에 대응할 수 있습니다.
또한 원본의 품질과 구성은 유지하면서 더 풍부한 컨텍스트를 추가하여 스토리텔링을 강화할 수 있습니다.
실전 팁
💡 확장 영역의 배경색을 원본 이미지의 평균 색상으로 설정하면 더 자연스러운 결과를 얻습니다. ImageStat.Stat(original).mean을 사용해 평균 RGB를 계산하여 캔버스 배경으로 사용하세요.
💡 한 번에 너무 많이 확장하지 마세요. 원본 크기의 50%씩 단계적으로 확장하는 것이 품질 면에서 유리합니다. 예: 512px → 768px → 1024px처럼 점진적으로 늘리세요.
💡 상하좌우 네 방향을 동시에 확장할 때는 프롬프트에 전체적인 구도를 명시하세요. "centered subject with wide background on both sides"처럼 원본의 위치를 AI에게 알려주면 더 균형잡힌 결과를 얻습니다.
💡 원본과 확장 영역의 경계가 눈에 띈다면 마스크 경계를 원본 이미지 안쪽으로 10-20px 침범시키세요. 이렇게 하면 경계 부분도 약간 재생성되어 더 매끄럽게 블렌딩됩니다.
💡 여러 번 outpainting을 반복할 때는 각 단계의 결과를 새로운 원본으로 사용하세요. 예: 좌우 확장 → 저장 → 그 결과로 상하 확장하면 더 큰 캔버스를 만들 수 있습니다.
4. 컨텍스트 보존 Inpainting
시작하며
여러분이 Inpainting을 사용할 때 수정한 부분이 주변과 어울리지 않게 나온 경험이 있나요? 색감이나 조명이 달라서 마치 다른 이미지에서 오려 붙인 것처럼 부자연스러운 결과를 받아본 적 있을 겁니다.
이런 문제는 AI가 마스크 영역만 독립적으로 생성할 때 발생합니다. 전체 이미지의 톤, 조명 방향, 화풍을 고려하지 않고 프롬프트만 따라가면 일관성이 깨집니다.
특히 사진처럼 사실적인 이미지일수록 작은 차이도 눈에 크게 띕니다. 바로 이럴 때 필요한 것이 컨텍스트 보존 Inpainting입니다.
원본 이미지의 스타일과 분위기를 분석하여 완벽하게 조화되는 결과를 생성합니다.
개요
간단히 말해서, 컨텍스트 보존 Inpainting은 마스크 주변의 정보를 적극적으로 활용하여 원본과 일관성 있는 수정을 수행하는 기법입니다. 단순히 새로운 내용을 생성하는 것이 아니라 기존 이미지에 자연스럽게 녹아드는 결과를 만듭니다.
이 접근법이 중요한 이유는 전문성과 품질 때문입니다. 아마추어와 프로의 차이는 디테일의 일관성에서 나타납니다.
예를 들어, 야외 인물 사진에서 옷을 바꿀 때 조명 방향과 색온도가 일치해야 자연스럽습니다. 기존의 기본 Inpainting이 프롬프트만 따랐다면, 컨텍스트 보존 방식은 strength 파라미터 조절, negative prompt 활용, 그리고 이미지 임베딩 조건부 생성을 통해 원본의 특성을 유지합니다.
핵심 기법은 세 가지입니다. 첫째, strength를 낮춰 원본의 영향을 강하게 유지합니다.
둘째, negative prompt로 원하지 않는 스타일 변화를 방지합니다. 셋째, guidance_scale을 적절히 조절하여 프롬프트 준수와 컨텍스트 보존의 균형을 맞춥니다.
이러한 조합이 전문가 수준의 결과를 만드는 비결입니다.
코드 예제
from diffusers import StableDiffusionInpaintPipeline
from PIL import Image, ImageFilter
import torch
pipe = StableDiffusionInpaintPipeline.from_pretrained(
"runwayml/stable-diffusion-inpainting",
torch_dtype=torch.float16
).to("cuda")
init_image = Image.open("portrait.png").resize((512, 512))
mask_image = Image.open("clothing_mask.png").resize((512, 512))
# 마스크 경계 부드럽게 처리
mask_image = mask_image.filter(ImageFilter.GaussianBlur(radius=4))
# 컨텍스트를 보존하는 파라미터 설정
prompt = "elegant blue dress, studio lighting, professional photography"
negative_prompt = "different lighting, different color temperature, inconsistent style, low quality"
result = pipe(
prompt=prompt,
negative_prompt=negative_prompt, # 스타일 불일치 방지
image=init_image,
mask_image=mask_image,
num_inference_steps=75, # 더 높은 품질
guidance_scale=7.5, # 균형잡힌 프롬프트 준수
strength=0.85 # 원본의 85% 변경, 15% 보존
).images[0]
result.save("context_preserved.png")
설명
이것이 하는 일: 이 코드는 여러 파라미터를 정교하게 조정하여 마스크 영역이 원본 이미지의 조명, 색감, 화풍과 완벽하게 일치하도록 생성합니다. 수정한 부분을 구별할 수 없을 정도로 자연스러운 결과를 만듭니다.
첫 번째로, 마스크에 GaussianBlur를 적용하여 경계를 부드럽게 만듭니다. radius=4는 약 8px의 블러 영역을 만들어 하드 엣지를 부드러운 그라데이션으로 바꿉니다.
이렇게 하면 AI가 생성하는 내용이 점진적으로 원본과 블렌딩되어 이어붙인 티가 전혀 나지 않습니다. 마스크 전처리는 컨텍스트 보존의 첫 번째 단계입니다.
두 번째로, negative prompt를 전략적으로 활용합니다. "different lighting"과 "different color temperature"는 AI가 원본과 다른 조명 조건을 생성하지 못하게 막습니다.
"inconsistent style"은 화풍이나 렌더링 방식의 불일치를 방지합니다. negative prompt는 프롬프트만큼 중요하며, 하지 말아야 할 것을 명시하여 결과의 일관성을 크게 높입니다.
세 번째로, strength 파라미터를 0.85로 설정합니다. 이 값은 노이즈를 85% 추가한다는 의미로, 완전히 새로 생성하는 1.0과 거의 변경하지 않는 0.5의 중간입니다.
0.8~0.9 범위가 컨텍스트 보존에 이상적이며, 충분한 변화를 주면서도 원본의 분위기를 유지합니다. num_inference_steps를 75로 높여 더 정교한 디테일을 생성합니다.
마지막으로, guidance_scale 7.5는 표준 값으로, 프롬프트를 적절히 따르면서도 이미지 조건(원본과 마스크)을 존중하는 균형점입니다. 너무 높이면(>10) 프롬프트만 따라가서 컨텍스트를 무시하고, 너무 낮으면(<5) 변화가 미미합니다.
여러분이 이 코드를 사용하면 전문가 수준의 이미지 합성이 가능합니다. 클라이언트에게 제출할 수 있는 품질의 결과물을 얻을 수 있으며, 여러 번 재생성할 필요 없이 첫 시도에서 만족스러운 결과를 얻을 확률이 크게 높아집니다.
조명과 색감이 완벽하게 일치하여 후처리 작업도 최소화됩니다.
실전 팁
💡 원본 이미지의 스타일을 프롬프트에 포함하세요. "studio lighting", "natural outdoor light", "sunset golden hour" 등 조명 조건을 명시하면 일관성이 크게 향상됩니다.
💡 strength 값을 찾는 실험을 하세요. 0.7(보수적), 0.85(균형), 0.95(적극적) 세 가지를 시도하여 프로젝트에 맞는 최적값을 찾으세요. 인물 사진은 0.8, 배경은 0.9가 일반적으로 좋습니다.
💡 color reference를 위해 원본 이미지의 평균 색상을 분석하고 프롬프트에 반영하세요. "warm tones", "cool blue palette", "earth colors"처럼 색상 분위기를 지정하면 색감 일치도가 높아집니다.
💡 여러 샘플을 생성하려면 num_images_per_prompt=4로 설정하세요. 한 번의 실행으로 4가지 변형을 받아 가장 컨텍스트가 잘 보존된 것을 선택할 수 있습니다.
💡 CLIP 임베딩을 활용한 이미지 조건부 생성을 시도하세요. IP-Adapter 같은 도구로 원본 이미지를 스타일 레퍼런스로 사용하면 컨텍스트 보존이 극대화됩니다.
5. 다중 영역 Inpainting
시작하며
여러분이 한 이미지에서 여러 부분을 동시에 수정하고 싶을 때 어떻게 하시나요? 한 번에 하나씩 마스크를 만들고 Inpainting을 반복하다 보면 시간도 오래 걸리고, 이전 수정이 다음 수정에 영향을 미쳐 예상치 못한 결과가 나오기도 합니다.
이런 문제는 특히 복잡한 이미지 편집 작업에서 심각해집니다. 예를 들어 실내 인테리어 사진에서 소파, 커튼, 액자를 모두 바꾸고 싶다면 세 번의 별도 작업이 필요하고, 각 단계마다 이전 결과물이 입력이 되어 오류가 누적될 수 있습니다.
바로 이럴 때 필요한 것이 다중 영역 Inpainting입니다. 여러 개의 마스크를 하나로 합쳐 한 번에 모든 영역을 수정하여 일관성과 효율성을 동시에 확보합니다.
개요
간단히 말해서, 다중 영역 Inpainting은 이미지의 여러 위치에 있는 객체들을 단일 작업으로 동시에 수정하는 기법입니다. 복합 마스크를 생성하여 한 번의 파이프라인 실행으로 모든 변경을 처리합니다.
이 방식이 효율적인 이유는 시간 절약과 일관성 때문입니다. 한 번의 AI 추론으로 여러 영역을 처리하면 GPU 시간을 절약하고, 모든 수정이 동일한 컨텍스트에서 이루어져 스타일 일관성이 보장됩니다.
예를 들어, 단체 사진에서 여러 사람의 의상을 통일된 테마로 바꿀 때 매우 유용합니다. 기존에는 각 영역을 순차적으로 처리하면서 중간 결과를 저장하고 다시 로드해야 했다면, 이제는 PIL의 이미지 연산으로 여러 마스크를 합성하고 한 번에 모든 수정을 적용할 수 있습니다.
핵심 기법은 마스크 합성과 프롬프트 전략입니다. 여러 개의 개별 마스크를 논리 OR 연산으로 합치면 모든 영역이 포함된 단일 마스크가 만들어집니다.
프롬프트는 각 영역별로 다른 변경사항을 명시하거나, 전체적으로 일관된 테마를 적용할 수 있습니다. 이러한 접근이 복잡한 편집을 단순화하는 열쇠입니다.
코드 예제
from PIL import Image, ImageDraw, ImageChops
import numpy as np
# 여러 개의 마스크 영역 생성
def create_multi_region_mask(width, height, regions):
"""regions: [(x, y, w, h), ...] 리스트"""
mask = Image.new('L', (width, height), 0)
draw = ImageDraw.Draw(mask)
# 모든 영역을 하나의 마스크에 그리기
for x, y, w, h in regions:
draw.rectangle([x, y, x+w, y+h], fill=255)
return mask
# 또는 여러 마스크 이미지를 합치기
def merge_masks(mask_files):
"""여러 마스크 파일을 하나로 합성"""
merged = None
for mask_file in mask_files:
mask = Image.open(mask_file).convert('L')
if merged is None:
merged = mask
else:
# 논리 OR: 둘 중 하나라도 흰색이면 흰색
merged = ImageChops.lighter(merged, mask)
return merged
# 사용 예시
regions = [(50, 50, 100, 100), (300, 200, 150, 120), (200, 400, 80, 90)]
multi_mask = create_multi_region_mask(512, 512, regions)
# 또는 개별 마스크 파일 합치기
mask_files = ["sofa_mask.png", "curtain_mask.png", "frame_mask.png"]
combined_mask = merge_masks(mask_files)
# 이제 이 마스크로 한 번에 inpainting
from diffusers import StableDiffusionInpaintPipeline
pipe = StableDiffusionInpaintPipeline.from_pretrained("runwayml/stable-diffusion-inpainting").to("cuda")
result = pipe(prompt="modern furniture, blue sofa, white curtains, abstract art frame",
image=init_image, mask_image=combined_mask).images[0]
설명
이것이 하는 일: 이 코드는 두 가지 방법으로 다중 영역 마스크를 생성합니다. 프로그래매틱하게 좌표로 여러 사각형을 그리거나, 별도로 만든 마스크 파일들을 합성하여 모든 수정 영역을 포함하는 단일 마스크를 만듭니다.
첫 번째로, create_multi_region_mask 함수는 좌표 리스트를 받아 한 이미지에 여러 사각형을 그립니다. regions 파라미터는 (x, y, width, height) 튜플의 리스트로, 각 튜플이 하나의 수정 영역을 나타냅니다.
for 루프로 모든 영역을 순회하면서 동일한 ImageDraw 객체에 흰색 사각형을 추가합니다. 결과는 여러 흰색 영역이 있는 하나의 마스크입니다.
두 번째로, merge_masks 함수는 더 복잡한 상황을 처리합니다. 포토샵이나 다른 도구로 각 객체별로 만든 정교한 마스크 파일들을 프로그래매틱하게 합칩니다.
ImageChops.lighter()는 두 이미지의 각 픽셀에서 더 밝은 값을 선택하는 함수로, 논리 OR 연산과 동일합니다. 어느 마스크에서든 흰색인 부분은 최종 마스크에서도 흰색이 됩니다.
이 접근법의 장점은 각 객체의 복잡한 윤곽을 보존하면서도 하나의 마스크로 통합한다는 것입니다. 예를 들어 소파의 곡선 형태, 커튼의 주름, 액자의 직선 등 각각 다른 도구로 정밀하게 만든 마스크를 잃지 않고 합칠 수 있습니다.
마지막으로, 통합된 마스크로 inpainting을 실행할 때 프롬프트에 모든 영역의 변경사항을 포함합니다. "blue sofa, white curtains, abstract art frame"처럼 각 객체의 원하는 상태를 나열하면 AI가 각 마스크 영역에 적절한 내용을 생성합니다.
한 번의 추론으로 모든 수정이 완료되어 시간이 크게 절약됩니다. 여러분이 이 코드를 사용하면 복잡한 이미지 편집 워크플로우를 단순화할 수 있습니다.
10개의 순차적 작업이 1개로 줄어들어 작업 시간이 90% 이상 단축되고, 모든 변경이 동일한 컨텍스트에서 이루어져 조명과 스타일의 일관성이 자동으로 유지됩니다.
실전 팁
💡 마스크 영역들이 서로 겹치지 않도록 주의하세요. 겹치는 부분이 있으면 AI가 혼란스러워하여 예측 불가능한 결과가 나올 수 있습니다. 영역 간 최소 10-20px 간격을 유지하세요.
💡 영역별로 다른 변경을 원한다면 프롬프트를 구체적으로 작성하세요. "left side: red sofa, center: blue curtains, right side: landscape painting"처럼 위치를 명시하면 AI가 각 영역을 올바르게 매칭합니다.
💡 ControlNet의 segmentation 모델을 활용하면 더 정교한 다중 영역 제어가 가능합니다. 각 영역에 서로 다른 시맨틱 레이블을 부여하여 명확하게 구분할 수 있습니다.
💡 마스크가 3개 이상이면 우선순위를 정하세요. 중요한 영역(예: 인물 얼굴)은 별도로 먼저 처리하고, 덜 중요한 배경 요소들을 그룹으로 묶어 처리하는 2단계 접근이 더 안정적입니다.
💡 각 영역의 크기가 비슷하도록 조정하세요. 하나가 너무 크고 다른 것이 너무 작으면 AI가 큰 영역에 집중하여 작은 영역의 품질이 떨어질 수 있습니다.
6. Inpainting 파이프라인 최적화
시작하며
여러분이 Inpainting을 프로덕션 환경에서 사용할 때 가장 큰 고민은 무엇인가요? 바로 속도와 메모리입니다.
한 이미지당 10초 이상 걸리고, GPU 메모리가 부족해서 에러가 나는 상황을 겪어본 적 있을 겁니다. 이런 문제는 실시간 서비스나 대량 배치 처리에서 치명적입니다.
사용자가 기다릴 수 없을 정도로 느리거나, 서버 비용이 감당할 수 없을 만큼 높아집니다. 특히 여러 사용자가 동시에 요청하면 GPU 메모리 부족으로 서비스가 다운될 수도 있습니다.
바로 이럴 때 필요한 것이 파이프라인 최적화입니다. 모델 최적화, 메모리 관리, 배치 처리를 통해 속도를 2-3배 높이고 메모리 사용량을 절반으로 줄일 수 있습니다.
개요
간단히 말해서, 파이프라인 최적화는 동일한 결과를 더 빠르고 적은 리소스로 생성하기 위한 기술들의 조합입니다. float16, attention slicing, model offloading 등 다양한 기법을 적용합니다.
최적화가 중요한 이유는 비용과 사용자 경험 때문입니다. GPU 사용 시간을 줄이면 클라우드 비용이 직접적으로 감소하고, 응답 속도가 빨라지면 사용자 만족도가 크게 향상됩니다.
예를 들어, 배치 처리로 1000장의 이미지를 처리할 때 최적화 여부에 따라 3시간과 1시간의 차이가 날 수 있습니다. 기존에는 기본 설정으로 파이프라인을 사용했다면, 이제는 xformers, torch.compile, VAE tiling 같은 고급 최적화 기법을 활용하여 성능을 극대화할 수 있습니다.
핵심 최적화 기법은 네 가지입니다. 첫째, float16으로 메모리를 절반으로 줄입니다.
둘째, attention slicing으로 메모리 피크를 낮춥니다. 셋째, enable_xformers_memory_efficient_attention으로 attention 연산을 최적화합니다.
넷째, torch.compile로 추론 속도를 20-30% 향상시킵니다. 이러한 조합이 프로덕션급 성능을 만듭니다.
코드 예제
from diffusers import StableDiffusionInpaintPipeline
import torch
# 최적화된 파이프라인 설정
pipe = StableDiffusionInpaintPipeline.from_pretrained(
"runwayml/stable-diffusion-inpainting",
torch_dtype=torch.float16, # 메모리 절반으로 감소
safety_checker=None, # 프로덕션에서는 제거하여 속도 향상
).to("cuda")
# xformers 메모리 효율적 attention (30-40% 메모리 절감)
pipe.enable_xformers_memory_efficient_attention()
# Attention slicing으로 메모리 피크 감소
pipe.enable_attention_slicing(slice_size="auto")
# VAE tiling으로 대용량 이미지 처리 (512 이상)
pipe.enable_vae_tiling()
# torch.compile로 추론 속도 20-30% 향상 (PyTorch 2.0+)
pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)
# 배치 처리로 여러 이미지 동시 생성
prompts = ["red rose", "blue bird", "green tree"]
images = [img1, img2, img3]
masks = [mask1, mask2, mask3]
# 한 번에 처리
results = pipe(
prompt=prompts,
image=images,
mask_image=masks,
num_inference_steps=30, # 속도 우선이면 30, 품질 우선이면 50
).images
# 메모리 정리
torch.cuda.empty_cache()
설명
이것이 하는 일: 이 코드는 다양한 최적화 기법을 체계적으로 적용하여 Inpainting 파이프라인의 성능을 극대화합니다. 품질 손실 없이 속도와 메모리 효율을 크게 향상시킵니다.
첫 번째로, torch_dtype=torch.float16을 설정하여 모든 가중치와 연산을 16비트로 수행합니다. 기본 float32 대비 메모리 사용량이 정확히 절반으로 줄어들고, 최신 GPU(RTX 30xx, 40xx, A100 등)에서는 연산 속도도 빨라집니다.
safety_checker=None은 NSFW 필터를 제거하여 약간의 추가 속도 향상을 얻습니다(프로덕션에서 필요 없다면). 두 번째로, enable_xformers_memory_efficient_attention()은 Facebook의 xformers 라이브러리를 활용한 최적화입니다.
표준 attention 메커니즘보다 30-40% 적은 메모리를 사용하면서 동일한 결과를 생성합니다. 특히 고해상도 이미지에서 효과가 큽니다.
이 기능을 사용하려면 pip install xformers가 필요합니다. 세 번째로, enable_attention_slicing()은 attention 연산을 여러 단계로 나누어 메모리 피크를 낮춥니다.
slice_size="auto"는 GPU 메모리 크기에 따라 자동으로 최적값을 선택합니다. 이 기법은 특히 VRAM이 제한적인 GPU(8GB 이하)에서 필수적입니다.
enable_vae_tiling()은 큰 이미지를 타일로 나누어 VAE가 처리하여 메모리 한계를 극복합니다. 네 번째로, torch.compile()은 PyTorch 2.0의 혁신적인 기능으로, 모델을 컴파일하여 추론 속도를 20-30% 향상시킩니다.
mode="reduce-overhead"는 레이턴시 감소에 최적화되고, fullgraph=True는 전체 모델을 하나의 그래프로 컴파일합니다. 첫 실행 시 컴파일 시간(1-2분)이 걸리지만, 이후 모든 추론이 빨라집니다.
마지막으로, 배치 처리는 여러 이미지를 동시에 GPU에 올려 병렬로 처리합니다. 3개를 순차 처리하면 30초씩 총 90초 걸리지만, 배치로 처리하면 40초에 끝납니다.
GPU 활용률이 높아져 시간당 처리량이 크게 증가합니다. 여러분이 이 코드를 사용하면 프로덕션 환경에서 실용적인 성능을 얻을 수 있습니다.
사용자 대기 시간을 수 초 이내로 줄이고, 동시 사용자 수를 2배 이상 늘릴 수 있으며, GPU 비용을 50% 이상 절감할 수 있습니다. 메모리 에러 없이 안정적인 서비스가 가능합니다.
실전 팁
💡 torch.compile()은 첫 실행 시 느리지만 이후 빨라집니다. 서버 시작 시 워밍업 추론을 한 번 실행하여 컴파일을 미리 완료하세요. 실제 사용자 요청에서는 빠른 속도를 경험합니다.
💡 배치 크기를 GPU 메모리에 맞게 조절하세요. RTX 3090(24GB)은 배치 4-6, RTX 4090(24GB)은 배치 6-8, A100(40GB)은 배치 10-12 정도가 적당합니다. OOM 에러가 나면 줄이세요.
💡 num_inference_steps를 동적으로 조절하세요. 간단한 수정(색상 변경)은 25-30, 복잡한 수정(객체 교체)은 50-75 스텝을 사용하여 품질과 속도의 균형을 맞추세요.
💡 모델을 TensorRT로 변환하면 추가로 50-100% 속도 향상이 가능합니다. 프로덕션에서 동일한 설정을 반복 사용한다면 TensorRT 최적화를 고려하세요.
💡 CPU offloading(enable_model_cpu_offload)은 메모리가 매우 부족할 때만 사용하세요. 메모리는 절약되지만 속도가 3-5배 느려지므로, 가능하면 GPU 업그레이드나 다른 최적화를 우선 적용하세요.
7. 프롬프트 가이드 Outpainting
시작하며
여러분이 Outpainting을 할 때 확장된 영역이 엉뚱한 내용으로 채워진 경험이 있나요? 왼쪽으로 확장했는데 오른쪽과 전혀 다른 스타일이 나오거나, 위아래로 늘렸는데 하늘과 땅이 뒤바뀌는 상황을 겪어본 적 있을 겁니다.
이런 문제는 AI가 확장 방향과 컨텍스트를 제대로 이해하지 못할 때 발생합니다. 단순히 "배경 확장"이라는 프롬프트만으로는 어느 쪽으로 무엇을 생성해야 할지 모호합니다.
특히 사방으로 동시에 확장할 때는 각 방향마다 다른 내용이 필요할 수 있습니다. 바로 이럴 때 필요한 것이 프롬프트 가이드 Outpainting입니다.
방향별로 구체적인 지시를 제공하여 의도한 대로 정확하게 확장할 수 있습니다.
개요
간단히 말해서, 프롬프트 가이드 Outpainting은 확장할 각 방향에 대해 명확한 내용 지시를 포함하는 프롬프트 작성 기법입니다. 위치 기반 프롬프트로 AI의 공간 이해를 돕습니다.
이 접근법이 필요한 이유는 제어력과 예측 가능성 때문입니다. 막연한 확장이 아니라 정확히 원하는 구성으로 캔버스를 넓힐 수 있습니다.
예를 들어, 인물 사진을 전신으로 확장할 때 "아래쪽에 다리와 바닥"이라고 명시하면 AI가 올바른 위치에 적절한 요소를 생성합니다. 기존에는 일반적인 프롬프트로 여러 번 시도하며 운에 맡겼다면, 이제는 구조화된 프롬프트와 ControlNet 같은 조건부 생성 도구로 방향성을 정확히 제어할 수 있습니다.
핵심 전략은 세 가지입니다. 첫째, 방향 키워드(left, right, top, bottom)를 프롬프트에 포함합니다.
둘째, 각 방향의 예상 내용을 구체적으로 서술합니다. 셋째, 원본 이미지의 컨텍스트 정보를 프롬프트에 반복하여 일관성을 유지합니다.
이러한 조합이 의도한 확장을 가능하게 합니다.
코드 예제
from diffusers import StableDiffusionInpaintPipeline
from PIL import Image, ImageDraw
import torch
pipe = StableDiffusionInpaintPipeline.from_pretrained(
"runwayml/stable-diffusion-inpainting",
torch_dtype=torch.float16
).to("cuda")
original = Image.open("portrait_upper_body.png")
orig_w, orig_h = original.size
# 아래쪽으로 확장 (상반신 → 전신)
new_height = orig_h + 256
canvas = Image.new('RGB', (orig_w, new_height), (128, 128, 128))
canvas.paste(original, (0, 0))
# 아래쪽 확장 영역만 마스크
mask = Image.new('L', (orig_w, new_height), 0)
draw = ImageDraw.Draw(mask)
draw.rectangle([0, orig_h, orig_w, new_height], fill=255)
# 방향과 내용을 명시한 프롬프트
prompt = """
professional portrait extending downward,
bottom part: full legs wearing dark jeans and sneakers,
standing on wooden floor,
maintain same lighting and photorealistic style,
studio photography, consistent with upper body
"""
negative_prompt = "cropped, cut off, incomplete body, floating, inconsistent perspective"
result = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=canvas,
mask_image=mask,
num_inference_steps=60,
guidance_scale=8.0
).images[0]
result.save("full_body_outpainted.png")
설명
이것이 하는 일: 이 코드는 상반신 초상화를 전신 사진으로 확장하면서, 프롬프트로 아래쪽에 생성될 내용을 구체적으로 지시합니다. 단순 확장이 아니라 의도한 구성대로 정확하게 이미지를 늘립니다.
첫 번째로, 아래쪽으로만 확장하는 캔버스를 만듭니다. 원본을 상단(0, 0)에 배치하여 아래쪽에만 빈 공간을 만듭니다.
이렇게 하면 AI가 "아래쪽 방향"으로 확장한다는 것을 공간적으로 이해합니다. 양쪽이나 위쪽이 아닌 정확히 아래만 마스크되어 있어 생성 영역이 명확합니다.
두 번째로, 프롬프트를 구조화하여 작성합니다. "extending downward"로 방향을 명시하고, "bottom part:"로 해당 영역의 내용을 시작합니다.
"full legs wearing dark jeans and sneakers"는 무엇을 생성할지 구체적으로 지정하고, "standing on wooden floor"는 배경까지 포함합니다. 이런 상세한 지시가 AI의 불확실성을 제거합니다.
세 번째로, 일관성 유지 지시를 포함합니다. "maintain same lighting"과 "photorealistic style"은 원본의 특성을 유지하라는 명령이고, "consistent with upper body"는 상반신과 스타일이 일치해야 함을 강조합니다.
이렇게 하면 확장 부분이 별개의 이미지처럼 보이지 않고 자연스럽게 연결됩니다. 네 번째로, negative prompt로 흔한 오류를 방지합니다.
"cropped"와 "cut off"는 다리가 잘린 채로 생성되는 것을 막고, "floating"은 바닥과 발이 접촉하지 않는 부자연스러운 결과를 방지합니다. "inconsistent perspective"는 원근감이 맞지 않는 것을 차단합니다.
마지막으로, guidance_scale을 8.0으로 약간 높여 프롬프트를 더 엄격하게 따르도록 합니다. outpainting에서는 7.5~9.0 범위가 좋으며, 너무 낮으면 프롬프트를 무시하고 엉뚱한 내용을 생성할 수 있습니다.
여러분이 이 코드를 사용하면 outpainting의 성공률을 크게 높일 수 있습니다. 첫 시도에서 원하는 결과를 얻을 확률이 70% 이상이 되며, 재생성 횟수가 줄어 시간과 비용이 절약됩니다.
또한 클라이언트에게 정확히 요구사항대로 확장된 이미지를 제공할 수 있습니다.
실전 팁
💡 다방향 확장 시 각 방향을 순차적으로 처리하세요. 먼저 좌우 확장 → 저장 → 그 결과로 상하 확장하면 각 단계에서 명확한 프롬프트를 사용할 수 있어 품질이 높아집니다.
💡 원본 이미지의 주요 요소를 프롬프트에 반복하세요. 예: 원본에 "빨간 드레스를 입은 여성"이 있다면 확장 프롬프트에도 "woman in red dress"를 포함하여 컨텍스트를 강화하세요.
💡 확장 비율을 제한하세요. 한 번에 원본의 50% 이상 확장하면 AI가 컨텍스트를 잃기 쉽습니다. 원본 512px이면 최대 256px씩 확장하고, 더 필요하면 다시 확장하세요.
💡 ControlNet의 depth나 canny edge를 활용하면 구조적 일관성이 향상됩니다. 원본의 depth map을 확장하여 조건으로 제공하면 원근감과 구조가 자연스럽게 이어집니다.
💡 참조 이미지를 함께 사용하세요. IP-Adapter로 유사한 전신 사진을 스타일 레퍼런스로 제공하면 AI가 "어떻게" 확장해야 할지 더 잘 이해합니다.
8. 이미지 복원과 Inpainting
시작하며
여러분이 오래된 사진이나 손상된 이미지를 복원해야 할 때 어떻게 하시나요? 찢어진 부분, 스크래치, 물 얼룩, 또는 의도적으로 제거하고 싶은 워터마크나 텍스트가 있는 이미지를 다루는 것은 까다로운 작업입니다.
이런 문제는 전통적으로 포토샵의 Content-Aware Fill이나 Clone Stamp로 수작업으로 처리했습니다. 하지만 넓은 영역이 손상되었거나 복잡한 텍스처를 복원해야 할 때는 몇 시간씩 걸리고, 결과도 자연스럽지 않을 수 있습니다.
바로 이럴 때 필요한 것이 Inpainting 기반 이미지 복원입니다. AI가 주변 컨텍스트를 분석하여 손상된 영역을 지능적으로 재구성하여 원본처럼 자연스러운 복원이 가능합니다.
개요
간단히 말해서, 이미지 복원 Inpainting은 손상되거나 제거할 영역을 마스크로 지정하고, AI가 주변 정보를 기반으로 그 부분을 재생성하는 기술입니다. 단순 복사가 아닌 지능적 재구성입니다.
이 방식이 강력한 이유는 컨텍스트 이해와 창의적 생성 능력 때문입니다. AI는 손상 주변의 패턴, 색상, 텍스처를 분석하여 그 영역에 있었을 법한 내용을 추론합니다.
예를 들어, 얼굴 사진의 스크래치를 복원할 때 피부 톤, 조명, 디테일을 자동으로 맞춰줍니다. 기존의 클론 스탬프가 다른 영역을 복사했다면, AI Inpainting은 완전히 새로운 픽셀을 생성하되 주변과 완벽하게 조화되도록 합니다.
특히 복잡한 배경이나 불규칙한 손상 패턴에서 훨씬 자연스러운 결과를 만듭니다. 복원의 핵심 전략은 프롬프트 최소화입니다.
새로운 내용을 추가하는 것이 아니라 원본을 재현하는 것이므로, 프롬프트는 일반적인 스타일 지시만 포함하고 구체적인 내용은 이미지가 결정하도록 합니다. strength를 높여 AI가 자유롭게 생성하되, guidance_scale을 낮춰 이미지 조건을 더 중시하게 만듭니다.
코드 예제
from diffusers import StableDiffusionInpaintPipeline
from PIL import Image, ImageDraw
import torch
pipe = StableDiffusionInpaintPipeline.from_pretrained(
"runwayml/stable-diffusion-inpainting",
torch_dtype=torch.float16
).to("cuda")
# 손상된 이미지와 손상 영역 마스크
damaged_image = Image.open("old_photo_with_scratch.png").resize((512, 512))
# 손상 영역을 자동 감지하거나 수동으로 마스크 생성
# 여기서는 수동 마스크 예시
damage_mask = Image.open("damage_mask.png").resize((512, 512))
# 복원용 프롬프트: 구체적 내용 없이 일반적 지시만
prompt = "high quality photograph, detailed, natural, photorealistic"
negative_prompt = "blurry, low quality, artifacts, noise, watermark, text"
# 복원 실행 - strength 높게, guidance_scale 낮게
restored = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=damaged_image,
mask_image=damage_mask,
num_inference_steps=75, # 높은 품질
guidance_scale=5.0, # 낮게 설정하여 원본 이미지 조건 우선
strength=0.95 # 높게 설정하여 자유로운 재생성
).images[0]
# 워터마크/텍스트 제거는 동일한 방식
watermark_image = Image.open("photo_with_watermark.png")
watermark_mask = Image.open("watermark_mask.png") # 워터마크 위치
clean = pipe(
prompt="clean photo, no text, no watermark",
negative_prompt="text, watermark, logo, signature",
image=watermark_image,
mask_image=watermark_mask,
num_inference_steps=50
).images[0]
설명
이것이 하는 일: 이 코드는 손상되거나 원하지 않는 요소가 있는 이미지를 AI가 지능적으로 복원합니다. 단순 지우기가 아니라 주변을 분석하여 그 자리에 있었을 법한 내용을 재생성합니다.
첫 번째로, 손상된 이미지와 손상 영역 마스크를 준비합니다. 마스크는 스크래치, 찢어진 부분, 물 얼룩 등 복원이 필요한 모든 영역을 흰색으로 표시합니다.
손상 감지를 자동화하려면 OpenCV의 morphological operations나 색상 이상치 감지를 사용할 수 있습니다. 수동으로 만들 때는 손상 경계보다 약간 넓게 마스크를 그려 완전한 제거를 보장하세요.
두 번째로, 복원 전용 프롬프트를 작성합니다. 이는 생성 Inpainting과 다른 접근입니다.
"high quality photograph"처럼 일반적인 품질 지시만 하고, 구체적인 객체나 색상은 언급하지 않습니다. 왜냐하면 AI가 원본 이미지의 손상되지 않은 부분에서 힌트를 얻어 복원해야 하기 때문입니다.
프롬프트가 너무 구체적이면 원본과 다른 내용이 생성될 수 있습니다. 세 번째로, 파라미터를 복원에 최적화합니다.
guidance_scale=5.0은 표준 7.5보다 낮은데, 이는 프롬프트보다 이미지 조건(원본과 마스크)을 더 중시하라는 의미입니다. 복원에서는 AI의 창의성보다 원본 충실도가 중요합니다.
strength=0.95는 높은 값으로, 손상 영역을 거의 완전히 새로 생성하되 주변 컨텍스트를 강하게 참조합니다. 네 번째로, 워터마크 제거는 동일한 기법을 사용하지만 프롬프트를 조정합니다.
"no text, no watermark"를 명시하고, negative prompt에 "text, watermark, logo"를 추가하여 AI가 텍스트성 요소를 절대 생성하지 않도록 합니다. 워터마크가 있던 자리는 배경 패턴으로 자연스럽게 채워집니다.
마지막으로, num_inference_steps를 75로 높여 복원 품질을 극대화합니다. 복원은 속도보다 품질이 중요하므로 더 많은 스텝을 사용합니다.
각 스텝마다 AI가 주변과의 일관성을 점진적으로 개선합니다. 여러분이 이 코드를 사용하면 수작업으로 수 시간 걸릴 복원 작업을 몇 초 만에 완료할 수 있습니다.
오래된 가족 사진 복원, 제품 사진에서 배경 잡티 제거, 스톡 이미지 워터마크 제거(합법적 용도) 등 다양한 실무에 활용할 수 있습니다. 결과는 전문 레터쳐가 작업한 것처럼 자연스럽습니다.
실전 팁
💡 큰 손상 영역은 여러 번 반복 복원하세요. 첫 번째로 대략적인 형태를 생성하고, 그 결과로 다시 inpainting하여 디테일을 정제하면 품질이 크게 향상됩니다.
💡 복원 전에 이미지를 업스케일하세요. 512px보다 큰 해상도에서 복원하면 더 정교한 디테일을 얻을 수 있습니다. Real-ESRGAN으로 먼저 2배 확대 → 복원 → 다시 다운샘플하는 워크플로우를 사용하세요.
💡 얼굴 복원에는 전문 모델을 사용하세요. CodeFormer나 GFPGAN 같은 얼굴 특화 모델이 Stable Diffusion보다 얼굴 디테일(눈, 코, 입) 복원에 훨씬 효과적입니다.
💡 색상 보정을 추가하세요. AI 복원 후 원본의 색감과 미묘하게 다를 수 있으므로, Histogram Matching으로 손상되지 않은 영역의 색상 분포를 복원 영역에 적용하세요.
💡 배치 복원 시 일관성을 위해 동일한 seed를 사용하세요. generator=torch.Generator().manual_seed(42)로 재현 가능한 복원을 수행하면 여러 이미지에 동일한 품질을 보장할 수 있습니다.