Skip to content
hello tis
Go back

이미지 확장(아웃페인팅) 기능 PoC — 작은 모델로 품질 끌어올리기

개요

어쩌다 보니 이미지 확장 기능을 만들고 있다. 원본 이미지의 경계 너머로 캔버스를 넓히고, 그 빈 공간을 원본과 자연스럽게 이어지도록 채우는 기능이다.

원본 이미지의 경계 너머를 채우는 이미지 확장 기능 예시

상업적으로 사용 가능한 모델을 골라 PoC를 진행했다. 처음 시도한 20B 규모 모델은 RTX 4090에서 4bit 양자화를 해야 겨우 돌았고, 큰 이미지는 실패했다. 최종적으로는 1/5 크기인 FLUX.2 Klein 4B에 배경 캡션 → 배경 생성 → 경계 희석으로 이어지는 2-pass 파이프라인을 얹어 품질을 확보했다.

이 글은 모델 선정부터 프롬프팅, 경계의 자연스러움을 위한 전처리 튜닝(feathering·denoise), 그리고 ComfyUI PoC를 Python 서버로 재구현하기까지의 시행착오를 정리한 기록이다.

결과부터

핵심은 모델의 절대 크기가 아니라 작은 모델의 한계를 파이프라인 설계로 메우는 것이었다.

항목Qwen-Image + ControlNet (초기 시도)FLUX.2 Klein 4B (최종)
파라미터20B4B (약 1/5)
VRAM22GB (4bit NF4 양자화, peak 22.6GB)~13GB
RTX 4090(24GB) 여유거의 없음여유 있음
큰 이미지실패처리 가능
양자화필요불필요
라이선스Apache 2.0Apache 2.0

측정 환경: RTX 4090 24GB, 일반 사진 이미지 기준. VRAM은 추론 시 실측 peak 값이다.

실제 결과 품질을 초기 시도와 비교하면 다음과 같다.

초기 시도 (단일 pass)최종 (배경 캡션 + 2-pass)
프롬프트 없이 단일 pass로 확장해 원본과 어울리지 않는 결과배경 캡션과 2-pass를 적용해 경계가 자연스럽게 연결된 결과

딥다이브 1 : 인페인팅 vs 아웃페인팅

이미지 확장 PoC를 시작하며 두 가지 방식을 후보로 두었다. 인페인팅과 아웃페인팅이다.

구분인페인팅아웃페인팅
작업 위치이미지 내부이미지 외부(경계 너머)
캔버스 크기변화 없음확장됨
목적수정, 제거, 복원확장, 상상, 컨텍스트 생성
참고 데이터주변 픽셀(양방향)경계 픽셀(한쪽 방향으로 추론)
공간을 넓히고 인페인팅한 게 아웃페인팅 아닌가?

인페인팅을 사용하는 경우, 원본보다 넓은 공간을 할당한 후 마스킹된 영역을 생성하면 아웃페인팅과 결과가 같을까?

위와 같은 고민이 들 수 있다. (나도 그랬다.) 차이는 참고하는 데이터가 양방향 추론인지, 단방향 추론인지에 있다.

인페인팅은 양방향으로 참고해야 하는데, 경계 너머는 한쪽 데이터만 존재한다. 참고할 픽셀이 한쪽밖에 없으니 결과가 더욱 상상에 가까워진다. 대표적으로 아래와 같은 상황에 놓이게 된다.

한쪽 방향 추론만 가능해 확장 영역이 원본과 무관하게 생성된 예시

딥다이브 2 : 모델 리서치와 첫 실패

이미지 확장 기능을 출시해보기 위해 이것저것 시도해봤다. 상업 사용 가능 여부를 기준으로 후보 모델을 비교했다.

모델파라미터특징
FLUX.2 [klein] 4B4B최신 세대, 가볍고 빠름, Apache 2.0
Qwen-Image + InstantX ControlNet20B최신·고품질, 무거움, Apache 2.0
SDXL Inpainting (+ ControlNet)~3.3Bdiffusers 공식 아웃페인팅 가이드의 기준 경로, 자료·생태계 큼 (그러나 OpenRAIL 계열)

SDXL은 자료와 생태계가 크지만 라이선스가 OpenRAIL 계열이라 상업 사용 관점에서 걸림돌이 있었다.

먼저 Qwen-Image + InstantX ControlNet을 사용해봤다. 합계 메모리가 54GB 필요했다. 4bit NF4로 양자화해 22GB까지 낮췄다. RTX 4090은 24GB이므로 돌아는 갔다. 다만 이미지가 큰 경우엔 실패했다. 일반 이미지 처리 시 실측 peak는 ~22.6GB까지 올라갔다.

확장된 이미지가 원본과 무관하다

프롬프트 없이 확장해 원본과 연관 없는 배경이 생성된 결과

확장된 이미지가 원본 이미지와 연관이 없어 보였다. 프롬프팅을 수정해 원본과 유사하게 유도했다.

a seamless natural continuation of the scene, high quality, highly detailed, sharp focus, photorealistic, consistent lighting and perspective

이미지 처리 프롬프팅에는 유의점이 있었다.

연관성은 개선됐지만 화질이 떨어진다

프롬프팅 수정 후 확장된 이미지와 원본의 연관성이 어느 정도 개선됐다. 대신 확장된 이미지의 화질이 떨어졌다. 품질이 낮은 이유는 VAE 타일링 여부 차이였다. 해당 옵션을 끄면 매끄러운 결과가 나올 예정이었다. 그러나 사용 중인 RTX 4090에서는 이 옵션을 사용하면 GPU 메모리 부족으로 구동이 불가능했다.

VAE 타일링으로 인해 확장 영역의 화질이 저하된 결과

이미지 모델을 낮춰서 화질이 떨어지는 문제를 개선할 필요가 있다고 생각했다.

최적화 과정 : ComfyUI로 전처리 튜닝

어도비는 이미지 확장을 참 잘 만들었다. 쟤네는 잘되는데 우리가 안 될 수는 없었다. 못 하면 잠 못 잘 것 같아서 마저 진행했다.

어도비 Generative Expand의 자연스러운 이미지 확장 결과

그래서 ComfyUI(desktop)를 사용해 전처리를 튜닝했다. 정성적 비교를 빠르게 반복하며 파라미터를 조정하는 방식이었다.

예시 1예시 2
ComfyUI에서 파라미터를 조정하며 비교한 튜닝 예시 1ComfyUI에서 파라미터를 조정하며 비교한 튜닝 예시 2

경계를 자연스럽게 잇기까지 네 번의 시도를 거쳤다.

시도 1시도 2시도 3시도 4
시도 1: 확장 영역이 원본과 매치되지 않는 결과시도 2: image caption 적용 후 매치가 개선된 결과시도 3: feathering 조절로 경계 톤이 유사해진 결과시도 4: denoise 조절로 경계가 자연스럽게 연결된 결과
확장된 외부 영역이 원본과 전혀 매치되지 않았다. 이미지를 묘사하는 프롬프팅이 필요하다고 판단해, image caption으로 원본을 묘사하도록 했다.image caption을 붙이자 확장 영역이 원본과 제법 잘 어울리기 시작했다. 다만 경계 부근이 아직 부자연스러워, feathering을 20px에서 80px로 넓혔다.경계 톤은 유사해졌지만 이음새는 여전히 어색했다. denoise를 조절해 모델을 한 번 더 돌려 경계를 다듬었다.백그라운드와 원본 간 경계가 자연스럽게 연결됐다.

1단계 : 생성 영역과의 맥락 불일치

image caption을 활용해 원본을 묘사하도록 했다. Florence2Model을 활용했다.

Florence-2 모델로 이미지 캡션을 생성하는 ComfyUI 노드 구성

결과적으로 기존 대비 유사한 맥락의 이미지를 생성하게 됐다. 그러나 기존 영역과 생성된 영역 간 경계가 발생했다.

AS-ISTO-BE
캡션 적용 전: 확장 영역이 원본 맥락과 다른 결과캡션 적용 후: 맥락은 유사해졌지만 경계가 남은 결과

2단계 : 경계선 블렌딩 (feathering + denoise)

feathering 조절

feathering은 원본과 새 영역의 경계를 부드럽게 섞는 폭이다. 아웃페인팅을 위한 이미지 패딩 노드의 가장자리 흐림 처리에 해당한다. 40 → 80으로 설정했다. 어느 정도 연결되는 부분이 생겼다.

AS-ISTO-BE
feathering 조절 전: 경계 부근이 부자연스러운 결과feathering 80px 적용 후: 경계 연결이 개선된 결과

denoise 조절

완성된 이미지 전체를 아주 약한 강도로 한 번 더 돌려서 톤을 통일한다. denoise를 0.2~0.3으로 낮게 설정하면 그림은 그대로 두고 경계 톤만 매끈하게 블렌딩한다.

denoise(노이즈 제거량)는 원본을 얼마나 지우고 새로 그릴지를 정하는 값이다. Stable Diffusion 계열은 노이즈에서 시작해 점점 그림을 복원하는 방식인데, denoise는 “시작할 때 원본에 노이즈를 얼마나 끼얹을지”를 정한다.

denoise결과언제 씀
1.0완전히 새로 그림 (원본 무시)txt2img, 아웃페인팅 새 영역
0.6~0.8원본 구도만 참고, 많이 변형스타일 크게 바꾸기
0.3~0.5원본 유지하며 다듬기이음새 통합, 디테일 보정
0.1~0.2원본 거의 그대로, 살짝만색·톤 미세 조정

즉, 위에서 만들어진 결과에 denoise 0.2~0.3을 세팅하고 돌리면 경계 블렌딩이 가능하다.

낮은 denoise로 경계를 블렌딩하는 ComfyUI 노드 구성

결과는 다음과 같다.

AS-ISTO-BE
denoise 적용 전: 이음새가 어색한 결과denoise 적용 후: 경계가 자연스럽게 연결된 결과

두 번째 결과도 만족스럽다. (오른쪽을 자세히 보면 기둥을 만들어서 소파를 숨김 처리해버렸다… 신기하다.)

마스크 생성백그라운드 이미지 생성백그라운드↔원본 경계 희석
아웃페인팅용 마스크생성된 백그라운드 이미지 1경계 희석 결과 1
아웃페인팅용 마스크생성된 백그라운드 이미지 2경계 희석 결과 2
아웃페인팅용 마스크생성된 백그라운드 이미지 3경계 희석 결과 3

3단계 : 원본 보존과 피사체 혼입 문제

문제 1 : 원본 이미지가 수정되는 이슈

원본 이미지가 수정되는 이슈가 있었다. 경계값을 넓게 포함해 마스크 영역을 지정하고, 외부 영역만 자연스럽게 개선하는 방향이 좋아 보였다.

AS-IS

마스크 생성백그라운드 이미지 생성백그라운드↔원본 경계 희석
수정 전 마스크원본이 함께 수정된 백그라운드 1원본이 변형된 경계 희석 결과 1
수정 전 마스크 2원본이 함께 수정된 백그라운드 2원본이 변형된 경계 희석 결과 2

TO-BE

마스크 생성백그라운드 이미지 생성백그라운드↔원본 경계 희석
경계를 넓게 지정한 마스크원본이 보존된 백그라운드 1원본이 보존된 경계 희석 결과 1
경계를 넓게 지정한 마스크원본이 보존된 백그라운드 2원본이 보존된 경계 희석 결과 2

문제 2 : 캡션에 피사체가 섞이는 문제

이미지 캡션 시 설명에 피사체가 묘사되면, 백그라운드에 피사체가 포함되는 것이 부자연스러움의 원인이 되는 듯했다. 다음 세 가지를 시도했다.

  1. 배경 설명을 받도록 수정. 기존 이미지 캡션에서 LLM을 바탕으로 배경을 묘사하도록 변경.
  2. 마스크 크기 조절 시도. → 변화가 발생했으나 미미했다.
  3. 기존 원본 데이터를 추가로 합성. → 원본 이미지가 유지되긴 했다.
마스크 생성백그라운드 이미지 생성백그라운드↔원본 경계 희석
피사체 혼입 개선 시도 마스크피사체가 혼입된 백그라운드피사체 혼입으로 부자연스러운 결과
원본 합성 시도 마스크원본 합성 후 백그라운드원본이 유지된 경계 희석 결과

마지막 결과는 일부 개선됐다.

마스크 생성백그라운드 이미지 생성백그라운드↔원본 경계 희석
최종 개선 마스크 1최종 개선 백그라운드 1최종 개선 경계 희석 결과 1
최종 개선 마스크 2최종 개선 백그라운드 2최종 개선 경계 희석 결과 2

ComfyUI PoC를 Python 서버로 재구현하기

ComfyUI로 파이프라인을 검증했다. 노드 단위로 중간 결과를 확인하며 파라미터를 좁혀갈 수 있었다.

최종 아웃페인팅 파이프라인의 ComfyUI 노드 그래프

그러나 ComfyUI 그래프는 그대로 사용할 수 없었다. ComfyUI는 검증과 디버깅에는 훌륭했지만, 서비스로 배포하기엔 제약이 존재했다. 그래서 검증이 끝난 그래프를 순수 Python(diffusers) 서버로 재구현했다. 저장소는 geon-chang/hello-image-outpainting에 공개해두었다.

2-pass 오케스트레이션은 ComfyUI 그래프의 실행 순서를 그대로 옮겼다. 전체 실행 흐름을 도식화하면 다음과 같다.

배경 캡션, 패딩, 1-pass 배경 창조, 2-pass 경계 리파인으로 이어지는 아웃페인팅 파이프라인 흐름도

백엔드 측면에서의 고민

마무리하며


Share this post:

Next Post
스토리지 서비스로 배운 속도와 안정성 트레이드오프