인페인팅은 무엇을 다시 그리나
ComfyUI 공식 문서는 인페인팅을 화가에게 «고칠 자리를 알려 주고(마스크), 그 자리만 요구대로 다시 칠하게(인페인트) 하는 것»에 빗대어 설명한다. 여기서 핵심 단어는 마스크다. 마스크는 그림 위에 덮는 흑백 지도로, 칠한 칸은 «다시 그릴 곳», 칠하지 않은 칸은 «그대로 둘 곳»이라는 뜻이다.
그래서 인페인팅을 할 때 정하는 것은 세 가지뿐이다. 어디를 가릴지(마스크), 가린 자리에 무엇이 들어가야 하는지(프롬프트), 그리고 원래 그림을 얼마나 남길지(노드 갈래와 denoise). 앞의 두 가지는 직관적이지만 세 번째에서 대부분 막힌다. 같은 워크플로를 받아 왔는데 누구는 자연스럽게 고쳐지고 누구는 가린 자리가 회색 얼룩으로 남는 것도 대개 이 세 번째 자리 때문이다.
바깥으로 그림을 넓히는 아웃페인팅도 같은 원리다. ComfyUI 예제 페이지는 아웃페인팅을 «인페인팅과 같은 것»이라고 적고, 캔버스를 넓히면서 알맞은 마스크를 함께 만들어 주는 노드를 따로 둔다고 설명한다. 이 글의 뒷부분에서 따로 다룬다.
모델 — 전용 모델과 일반 모델
인페인팅은 일반 체크포인트로도 된다. ComfyUI 예제 페이지는 인페인팅 전용이 아닌 모델(anythingV3)로 인페인팅한 예를 함께 싣고 «인페인팅 모델이 아니어도 된다»고 적었다. 다만 공식 튜토리얼은 인페인팅 전용 모델을 쓴다. 튜토리얼이 쓰는 파일은 512-inpainting-ema.safetensors이고, Hugging Face의 Comfy-Org 저장소에서 받아 ComfyUI/models/checkpoints 폴더에 넣으라고 안내한다. 문서는 이 모델이 인페인팅을 위해 만들어져서 다시 그릴 영역을 더 잘 다루고 경계가 더 자연스럽게 이어진다고 설명한다.
정리하면 이렇다.
- 이미 쓰던 체크포인트로 시작해도 된다. 그림체를 유지하고 싶다면 원래 그림을 뽑은 그 모델로 고치는 편이 어울린다.
- 경계가 계속 어색하면 전용 모델을 시험한다. 공식 튜토리얼이 전용 모델을 고른 이유가 바로 그 경계 처리다.
- 체크포인트를 고르는 기준과 받는 곳은 체크포인트와 LoRA 고르기에 따로 정리했다.
튜토리얼의 전용 모델은 Stable Diffusion 2.1 파일을 모아 둔 저장소(stable_diffusion_2.1_repackaged)에 들어 있다. 다른 계열(예: SDXL)로 뽑은 그림을 고칠 때는 그 계열용 인페인팅 모델이 따로 있는지 모델 카드에서 먼저 확인한다.
마스크 그리기 — MaskEditor
ComfyUI에는 마스크를 그리는 편집기가 들어 있다. 공식 문서가 안내하는 순서는 이렇다.
- 고칠 그림을 Load Image 노드에 올린다.
- Load Image 노드를 마우스 오른쪽으로 눌러 Open in MaskEditor를 고른다.
- 오른쪽 패널에서 붓 크기 같은 설정을 바꿔 가며 다시 그릴 자리를 칠한다. 잘못 칠한 곳은 지우개로 지운다.
- 저장하면 마스크가 Load Image 노드에 붙는다. 이 노드의 MASK 출력이 뒤의 인코딩 노드로 들어간다.
마스크를 칠할 때는 고칠 대상보다 조금 넉넉하게 칠하는 편이 다루기 쉽다. 딱 맞게 칠하면 새로 그린 부분과 원래 그림이 만나는 경계가 좁아져서 이어 붙인 티가 나기 쉽기 때문이다. ComfyUI는 이 «조금 넉넉하게»를 숫자로 맞추는 값도 따로 두었는데, 뒤의 grow_mask_by 절에서 본다.
노드 두 갈래 — 새로 그리기와 원래 그림 살리기
마스크를 그린 뒤 인코딩하는 자리에서 길이 둘로 나뉜다. ComfyUI 기본 노드에 인페인팅용 인코딩 방법이 두 가지 있기 때문이다.
| 갈래 | 인코딩 노드 | denoise | 어울리는 일 |
|---|---|---|---|
| A. 새로 그리기 | VAE Encode (for Inpainting) | 1.0 | 가린 자리에 전혀 다른 것을 넣기, 물체 지우기 |
| B. 원래 그림 살리기 | VAE Encode → Set Latent Noise Mask | 낮춰도 됨 | 모양은 두고 질감 · 색만 다듬기 |
ComfyUI 제작자(comfyanonymous)는 GitHub 토론에서 이 둘을 이렇게 갈랐다. «VAE Encode for inpainting»은 denoise 100%로 써야 하는 «진짜 인페인팅»용이고, 이미지의 가린 부분에만 img2img를 하고 싶다면 대신 «Set Latent Noise Mask»를 쓰라는 것이다. 같은 답변에서 VAE Encode for inpainting은 인페인팅 전용 모델과 가장 잘 맞지만 다른 모델로도 된다고 덧붙였다.
A 갈래는 공식 튜토리얼이 쓰는 길이다. 튜토리얼은 이 노드를 VAE Encoder (for Inpainting)라고 부르고, 입력을 네 개로 설명한다. 인코딩할 그림(pixels), VAE 모델(vae), 다시 그릴 자리(mask), 마스크를 넓힐 픽셀 수(grow_mask_by)다. 화면에 뜨는 노드 이름은 소스 코드의 표시 이름 그대로 «VAE Encode (for Inpainting)»다.
B 갈래는 보통의 img2img와 같은 길에 마스크 한 단계를 끼운 것이다. 그림을 평범한 VAE Encode로 인코딩한 뒤 Set Latent Noise Mask 노드로 마스크를 붙이면, 샘플러는 마스크 안쪽만 다시 칠한다. 원래 그림의 모양이 잠재 공간에 그대로 남아 있으니 denoise를 낮추면 낮출수록 원래 그림에 가깝게 고쳐진다.
가린 칸은 회색으로 채워진다 — denoise를 1.0으로 두는 이유
A 갈래에서 denoise를 낮추면 왜 안 되는지는 소스 코드를 보면 바로 보인다. ComfyUI의 nodes.py에서 VAE Encode (for Inpainting)를 맡은 VAEEncodeForInpaint 클래스는 그림을 인코딩하기 전에 마스크 안쪽 픽셀을 모두 중간값 0.5로 바꾼다. 0.5는 밝기 한가운데, 곧 회색이다. 원래 그림에서 가린 자리의 정보는 인코딩 전에 지워지고, 잠재 공간에는 회색 칸이 들어간다.
이 상태에서 denoise를 1.0보다 낮추면 샘플러는 그 회색 칸을 «어느 정도 살려 둘 원본»으로 보고 덜 고친다. 그 결과가 가린 자리에 남는 회색 얼룩이다. denoise를 1.0으로 두면 회색 칸을 처음부터 새로 그리므로 이 문제가 없다. 제작자가 이 노드를 denoise 100%로 쓰라고 한 것과 코드의 동작이 맞아떨어진다.
소스 코드에서 하나 더 확인되는 것이 있다. 그림의 가로세로가 VAE의 축소 배율로 나누어떨어지지 않으면 이 노드는 가운데를 기준으로 가장자리를 조금 잘라 크기를 맞춘다. 인페인팅 결과가 원본보다 몇 픽셀 작게 나왔다면 이 때문일 수 있다.
옷 색만 바꾸거나 피부 질감만 다듬는 것처럼 «모양은 그대로, 표면만» 고치고 싶을 때는 A 갈래에서 denoise를 낮추지 말고 B 갈래(Set Latent Noise Mask)로 옮긴다. 두 갈래를 섞어 쓰는 것이 회색 얼룩의 가장 흔한 원인이다.
새로 그리기 갈래를 한 벌로 잇기
두 갈래 가운데 공식 튜토리얼이 쓰는 A 갈래를 처음부터 끝까지 이으면 다음과 같다. 새 노드는 인코딩 노드 하나뿐이고, 나머지는 첫 그림을 뽑을 때 쓰던 기본 워크플로 그대로다. 튜토리얼 역시 모델과 그림을 올린 뒤 Ctrl + Enter로 실행하는 세 단계로 안내한다.
| 순서 | 노드 | 무엇을 잇나 |
|---|---|---|
| 1 | Load Checkpoint | 인페인팅에 쓸 모델을 고른다. MODEL · CLIP · VAE 세 출력이 나온다 |
| 2 | CLIP Text Encode 두 개 | 가린 자리에 들어갈 것(긍정)과 피할 것(부정)을 적는다 |
| 3 | Load Image | 고칠 그림과, MaskEditor로 그린 마스크가 함께 나온다 |
| 4 | VAE Encode (for Inpainting) | IMAGE를 pixels에, MASK를 mask에, 1번의 VAE를 vae에 잇는다 |
| 5 | KSampler | 4번의 LATENT를 latent_image에 잇고 denoise는 1.0으로 둔다 |
| 6 | VAE Decode → Save Image | 결과를 그림으로 풀어 저장한다 |
B 갈래로 바꾸려면 4번 자리만 갈아 끼운다. 평범한 VAE Encode로 그림을 인코딩하고, 그 LATENT와 Load Image의 MASK를 Set Latent Noise Mask에 넣은 뒤, 그 출력을 KSampler로 보낸다. 나머지 노드는 손대지 않는다. 한 워크플로 안에 두 갈래를 모두 만들어 두고 KSampler로 가는 선만 바꿔 끼우면 같은 시드로 두 방식의 결과를 나란히 비교할 수 있다.
프롬프트는 가린 자리에 들어갈 것을 중심으로 쓴다. 인페인팅은 그림 전체가 아니라 가린 칸을 새로 채우는 일이므로, 그 칸에 무엇이 있어야 하는지를 적는 것이 출발점이다. 배경 한쪽의 물체를 지우는 경우라면 «그 자리에 원래 있어야 할 배경»을 적는다. 결과가 어색할 때 프롬프트를 다듬어 보라는 것은 공식 문서의 조언이기도 하다.
grow_mask_by — 마스크를 몇 픽셀 넓힐까
VAE Encode (for Inpainting)의 네 번째 입력 grow_mask_by는 마스크를 넓히는 값이다. 소스 코드에서 기본값은 6, 범위는 0부터 64까지, 1씩 바꿀 수 있다.
여기서 눈여겨볼 점은 이 값이 어디에 쓰이는가다. 소스를 보면 회색으로 채우는 칸은 사용자가 그린 원래 마스크 그대로이고, 넓힌 마스크는 샘플러에게 «여기까지 다시 그려라»라고 넘기는 노이즈 마스크로 따로 나간다. 즉 grow_mask_by를 키우면 회색 칸의 크기는 그대로인 채로, 그 둘레의 원래 그림까지 샘플러가 함께 다시 칠하게 된다. 새로 그린 부분과 원래 그림 사이에 이어 붙일 여유를 두는 값이라고 보면 된다.
- 0으로 두면 넓히지 않고 그린 마스크 그대로 쓴다.
- 경계에 선이나 색 차이가 보이면 이 값을 몇 단계씩 올려 가며 같은 시드로 비교한다.
- 너무 크게 잡으면 고치고 싶지 않던 주변까지 다시 그려진다. 작은 대상을 고칠 때는 기본값 근처에서 시작한다.
바깥으로 넓히기 — 아웃페인팅
그림을 옆이나 위로 넓히고 싶을 때도 같은 틀을 쓴다. 공식 아웃페인팅 튜토리얼은 인페인팅 튜토리얼과 같은 512-inpainting-ema.safetensors 모델을 쓰고, 캔버스를 넓히는 데 Pad Image for outpainting 노드를 쓴다. 이 노드는 left · top · right · bottom에 각 방향으로 넓힐 크기를 받고, feathering 값으로 원래 그림과 새로 생길 부분의 경계를 얼마나 부드럽게 이을지 정한다. 값이 클수록 경계가 부드러워진다고 문서는 설명한다.
예제 페이지의 설명대로 이 노드는 캔버스를 넓히면서 넓힌 자리를 가리는 마스크를 함께 만든다. 그래서 따로 MaskEditor를 열 필요 없이, 이 노드의 이미지와 마스크 출력을 인페인팅의 A 갈래에 그대로 이으면 된다. 넓힌 자리는 원래 비어 있던 칸이라 살릴 원본이 없으니, 새로 그리기 갈래(denoise 1.0)가 자연스럽다.
결과가 어색할 때 볼 순서
공식 문서는 결과가 마음에 들지 않을 때 시드를 여러 번 바꿔 보고, 프롬프트를 다듬고, 이미 고친 그림을 한 번 더 인페인팅해 보라고 권한다. 여기에 앞에서 확인한 동작을 더하면 다음 순서로 보는 것이 빠르다.
- 가린 자리에 회색이 남았다 — A 갈래에서 denoise를 낮춘 경우다. 1.0으로 되돌리거나 B 갈래로 옮긴다.
- 새로 그린 부분이 주변과 따로 논다 — grow_mask_by를 조금씩 올려 보고, 그래도 어색하면 인페인팅 전용 모델을 시험한다.
- 엉뚱한 것이 그려진다 — 프롬프트가 그림 전체를 설명하고 있지 않은지 본다. 가린 자리에 들어갈 것을 중심으로 고쳐 쓴다.
- 한 번에 안 된다 — 공식 문서의 조언대로 고친 결과를 다시 Load Image에 넣고 좁은 마스크로 한 번 더 고친다.
자주 묻는 것
인페인팅 전용 모델이 꼭 있어야 하나요?
아닙니다. ComfyUI 예제 페이지는 인페인팅 전용이 아닌 모델로 인페인팅한 예를 함께 싣고 일반 모델로도 된다고 적었습니다. 공식 튜토리얼은 경계가 더 자연스럽다는 이유로 전용 모델인 512-inpainting-ema.safetensors를 씁니다.
VAE Encode (for Inpainting)를 쓰면 가린 자리가 회색으로 나와요.
denoise를 1.0보다 낮게 둔 경우일 가능성이 큽니다. 이 노드는 인코딩 전에 가린 칸을 회색(0.5)으로 채우기 때문에, denoise를 낮추면 그 회색이 남습니다. ComfyUI 제작자도 이 노드는 denoise 100%로 쓰라고 설명했습니다.
원래 그림을 살리면서 일부만 조금 바꾸려면 어떻게 하나요?
VAE Encode로 인코딩한 뒤 Set Latent Noise Mask 노드로 마스크를 붙이고, KSampler의 denoise를 낮춥니다. 제작자는 가린 부분에만 img2img를 하고 싶을 때 이 방법을 쓰라고 안내했습니다.
grow_mask_by는 얼마로 두면 되나요?
소스 코드의 기본값은 6이고 0부터 64까지 고를 수 있습니다. 넓힌 마스크는 샘플러가 다시 그릴 범위로 쓰이므로, 경계가 티 나면 값을 조금씩 올려 같은 시드로 비교해 봅니다.
그림을 옆으로 넓히는 것도 같은 방법으로 하나요?
그렇습니다. Pad Image for outpainting 노드로 캔버스를 넓히면 넓힌 자리를 가리는 마스크도 함께 만들어집니다. 그 이미지와 마스크를 인페인팅 워크플로에 이어 새로 그리게 하면 됩니다.
참고 자료
- ComfyUI 공식 문서. Inpainting 튜토리얼 — 인페인팅 전용 모델과 저장 폴더, MaskEditor 여는 법, VAE Encoder (for Inpainting) 입력 네 개, 결과가 어색할 때의 조언. docs.comfy.org
- ComfyUI 공식 문서. Outpainting 튜토리얼 — Pad Image for outpainting 노드의 left · top · right · bottom · feathering. docs.comfy.org
- ComfyUI 소스 코드 nodes.py — VAEEncodeForInpaint의 grow_mask_by 기본값 · 범위, 가린 칸을 회색으로 채우는 부분, 넓힌 마스크를 넘기는 부분, 노드 표시 이름. github.com
- ComfyUI Examples. Inpaint examples — 일반 모델로도 인페인팅이 된다는 예, 아웃페인팅도 같은 작업이라는 설명. comfyanonymous.github.io
- comfyanonymous(ComfyUI 제작자) 답변. GitHub Discussions #639 — VAE Encode for inpainting은 denoise 100%로 쓰고, 원래 그림을 살리려면 Set Latent Noise Mask를 쓰라는 설명. github.com
- Hugging Face. Comfy-Org/stable_diffusion_2.1_repackaged — 튜토리얼이 쓰는 512-inpainting-ema.safetensors 가 올라 있는 저장소. huggingface.co