- Published on
Stable Diffusion 색감·품질 붕괴, VAE·fp16·sRGB로 복구
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서로 다른 체크포인트를 쓰는데도 결과가 유독 탁해지고(회색빛), 피부가 붉게 뜨거나, 그라데이션이 계단처럼 깨지고(밴딩), 디테일이 뭉개지는 경우가 있습니다. 흔히 “프롬프트 문제”로 오해하지만, 실제로는 VAE(디코더) 불일치, fp16 정밀도/클리핑, 색공간(ICC 프로파일) 처리가 겹치며 발생하는 경우가 많습니다.
이 글은 Stable Diffusion(WebUI/ComfyUI/파이프라인 공통)에서 색감·품질 붕괴를 재현 가능하게 진단하고, VAE 교체와 fp16 이슈 회피, 그리고 sRGB ICC 기반의 안전한 저장/후처리로 안정화하는 실전 체크리스트를 제공합니다.
증상으로 빠르게 원인 범주 나누기
아래 증상은 서로 비슷해 보여도 원인 레이어가 다릅니다.
1) 색이 탁하고 콘트라스트가 죽는다
- 전반적으로 회색 막이 낀 느낌
- 검은색이 검지 않고 떠 보임
- 채도가 낮아지고 피부 톤이 생기 없음
가능성이 큰 원인
- 체크포인트와 VAE 불일치
- 디코딩 과정에서의 정밀도 손실(
fp16) 또는 특정 백엔드의 디코드 버그
2) 과포화/붉은기/녹색기 등 색이 비정상적으로 튄다
- 특정 색 채널이 과하게 밀리는 느낌
- 같은 시드인데 저장 방식에 따라 색이 달라짐
가능성이 큰 원인
- ICC 프로파일 미포함/무시로 인한 색공간 해석 불일치
- 뷰어/편집기에서 색관리 동작이 제각각
3) 밴딩, 블록 노이즈, 디테일 뭉개짐
- 하늘/피부 그라데이션에 계단 현상
- 미세 디테일이 “물감처럼” 뭉침
가능성이 큰 원인
fp16연산에서의 클리핑/언더플로/오버플로- VAE 디코더에서의 정밀도 이슈
- 저장 포맷/후처리(특히 JPEG)로 인한 손실
핵심 개념: VAE, 정밀도, 색공간은 “서로 다른 층”이다
- VAE: latent를 RGB 이미지로 복원하는 디코더. 체크포인트마다 기대하는 VAE 성향이 달라서 불일치하면 색감/콘트라스트가 급격히 틀어질 수 있습니다.
fp16: GPU 메모리 절약을 위해 half precision을 쓰면 빠르고 가볍지만, 특정 연산 경로에서 값이 잘리거나(클리핑) 정밀도 손실이 누적될 수 있습니다.- sRGB/ICC: 생성된 픽셀 값 자체는 같아도 “이 숫자가 어떤 색공간의 값인가”를 명시하지 않으면, 앱마다 다르게 해석해 색이 달라 보입니다.
이 셋을 분리해 다루면, “왜 내 PC에서는 정상인데 업로드하면 이상해지지?” 같은 문제도 깔끔하게 정리됩니다.
1단계: VAE 교체로 색감 붕괴부터 잡기
언제 VAE를 의심해야 하나
- 같은 체크포인트/같은 프롬프트/같은 시드인데, 환경을 바꾸면 색이 확 달라짐
- 특정 모델에서만 유독 탁하거나 붉게 뜸
- WebUI에서
VAE: Automatic일 때만 이상하고, 특정 VAE를 고정하면 안정적
실전 권장 전략
- **VAE를 “자동”이 아닌 “명시적으로 고정”**합니다.
- 체크포인트 제작자가 권장하는 VAE가 있으면 그걸 우선 사용합니다.
- SD 1.5 계열과 SDXL 계열은 VAE 기대치가 다르므로 섞지 않습니다.
A1111(WebUI) 기준 체크
Settings의 VAE 관련 옵션에서- VAE를 자동 추정하지 말고 특정 파일로 고정
- “VAE를
fp16으로 돌릴지” 옵션이 있다면, 문제 재현 시fp32로 테스트
ComfyUI에서 VAE 교체 예시
아래는 VAE를 명시적으로 연결하는 전형적인 구성입니다.
CheckpointLoaderSimple
├─ model
├─ clip
└─ vae ───────────────┐
▼
VAEDecode ──► SaveImage
▲
│
samples (latent)
추가로, 외부 VAE를 쓰려면 VAELoader를 사용해 VAEDecode에 연결합니다.
VAELoader(vae_name="your_vae.safetensors") ──► VAEDecode
VAE 교체 테스트 팁(재현성 확보)
- 샘플러/스텝/CFG/시드/해상도를 고정
- 후처리(업스케일, 하이레즈, 리터치) 모두 끄고 베이스 생성만 비교
- 같은 latent를 저장/재디코드하는 기능이 있다면, “디코드만 바꿔서” 비교
2단계: fp16 클리핑/정밀도 이슈 회피하기
VAE를 바꿔도 밴딩이 남거나, 특정 상황에서만 결과가 무너진다면 fp16 경로를 의심해야 합니다. 특히 아래 조합에서 문제가 잘 드러납니다.
- 고해상도 + 하이레즈 픽스 + 디노이즈가 낮음
- 특정 VAE/특정 커스텀 노드에서 half 연산
- xFormers/SDPA/FlashAttention 등 백엔드 변경 후 품질 변화
빠른 진단: fp32로 한 번만 돌려보기
가능하면 다음 중 하나로 “품질이 돌아오는지” 확인합니다.
- VAE만
fp32로 디코드 - 전체 UNet까지
fp32로 실행(느리지만 진단에는 확실)
품질이 fp32에서 정상화되면, 원인은 대체로
- half 정밀도 누적 오차
- 특정 레이어에서의 오버플로/언더플로
- VAE 디코드에서의 클리핑 중 하나입니다.
Diffusers 파이프라인에서 안전한 설정 예시
아래 예시는 “학습이 아니라 추론” 기준이며, 문제 진단용으로 fp32 또는 VAE만 fp32로 두는 패턴을 보여줍니다.
import torch
from diffusers import StableDiffusionPipeline, AutoencoderKL
model_id = "runwayml/stable-diffusion-v1-5"
# 1) 기본 파이프라인은 fp16
pipe = StableDiffusionPipeline.from_pretrained(
model_id,
torch_dtype=torch.float16,
safety_checker=None,
)
pipe = pipe.to("cuda")
# 2) VAE만 fp32로 교체(디코드 품질 안정화에 자주 도움)
vae = AutoencoderKL.from_pretrained(
model_id,
subfolder="vae",
torch_dtype=torch.float32,
).to("cuda")
pipe.vae = vae
prompt = "portrait photo, natural skin tone, soft light"
image = pipe(prompt, num_inference_steps=30).images[0]
image.save("out.png")
핵심은 “모든 걸 fp32로”가 아니라, 문제의 병목인 VAE 디코드만 fp32로 두는 선택지가 꽤 효과적이라는 점입니다.
fp16 클리핑을 줄이는 운영 팁
- VAE/UNet 중 하나라도
fp32로 올렸을 때 개선되면, 그 레이어를 우선 고정 - 백엔드(xFormers/SDPA) 변경 후 품질이 흔들리면, 동일 시드로 A/B 테스트해 고정
- 저장 포맷은 우선 PNG로 고정(후처리 단계에서 손실 제거)
3단계: sRGB ICC 프로파일로 “보는 색”을 고정하기
모델이 생성한 픽셀 값이 정상이어도, 저장/업로드/뷰어에서 색이 달라지는 경우가 많습니다. 특히
- 어떤 뷰어는 ICC를 무시
- 어떤 플랫폼은 업로드 시 메타데이터를 제거
- 어떤 편집기는 작업 색공간을 임의로 바꾸거나 디스플레이 프로파일을 잘못 적용 같은 일이 벌어집니다.
결론: 최종 산출물은 sRGB로 명시하고 ICC를 포함하자
웹/모바일/대부분의 뷰어에서 가장 안전한 타겟은 sRGB입니다.
- 작업 중: 광색역(P3, AdobeRGB)을 쓰더라도
- 배포/업로드: sRGB로 변환 + ICC 포함
ImageMagick로 sRGB 변환 + ICC 포함
작업 파일이 어떤 색공간이든, 최종 결과는 sRGB로 맞추는 게 안전합니다.
# 입력이 어떤 프로파일이든 sRGB로 변환하고 프로파일을 포함
magick input.png -profile sRGB.icc -strip -profile sRGB.icc output.png
여기서 -strip은 메타데이터를 제거하지만 ICC까지 날릴 수 있으니, -strip 후에 다시 -profile로 sRGB를 넣는 순서를 권장합니다.
Pillow(Python)로 sRGB ICC 포함 저장
서버 사이드에서 생성 결과를 저장/리사이즈하면서 ICC를 포함하고 싶을 때 유용합니다.
from PIL import Image, ImageCms
img = Image.open("out.png").convert("RGB")
# sRGB 프로파일 생성(플랫폼에 따라 다를 수 있어, 운영에서는 파일로 관리 권장)
srgb = ImageCms.createProfile("sRGB")
# Pillow는 icc_profile 바이트를 요구
icc_bytes = ImageCms.ImageCmsProfile(srgb).tobytes()
img.save("out_srgb.png", icc_profile=icc_bytes)
운영 환경에서는 sRGB.icc 파일을 리포지토리에 포함하거나, 컨테이너 이미지에 넣어 항상 동일한 프로파일을 쓰는 편이 재현성이 좋습니다.
“업로드하면 색이 바뀌는” 케이스의 실전 대응
- 업로드 대상이 ICC를 제거하는 플랫폼이면, 플랫폼이 기대하는 sRGB 값으로 이미 변환해 두는 게 최선입니다.
- 썸네일 생성/리사이즈 파이프라인에서도 ICC를 유지하거나, 마지막에 다시 sRGB로 확정합니다.
체크리스트: 원인별로 가장 효율적인 해결 순서
A. 생성 직후부터 색이 이상하다
- VAE를 자동이 아닌 고정으로 변경
- 체크포인트 권장 VAE로 교체
- VAE 디코드만
fp32로 테스트
B. 생성물은 정상인데, 저장/업로드/뷰어에서 색이 바뀐다
- 최종 산출물을 sRGB로 변환
- ICC 프로파일 포함 저장
- 리사이즈/압축 파이프라인에서도 동일 정책 적용
C. 밴딩/뭉개짐이 랜덤하게 발생한다
- 동일 시드로
fp16vsfp32비교 - VAE
fp32고정으로 개선되는지 확인 - 백엔드/최적화 옵션 변경을 최소화하고 A/B 테스트로 고정
운영 관점: “재현 가능한 파이프라인”이 품질을 지킨다
Stable Diffusion 품질 이슈는 감각적으로 접근하면 끝이 없습니다. 대신 아래처럼 “환경을 고정”하면 문제를 빠르게 분리할 수 있습니다.
- 모델(체크포인트) 버전 고정
- VAE 파일명/해시 고정
fp16/fp32정책 고정(특히 VAE)- 최종 저장은 PNG + sRGB + ICC 포함
- 배치/서버에서 동일한 컨테이너 이미지로 실행
이 접근은 인프라/배포에서도 그대로 통합니다. 예를 들어, 환경 차이로만 발생하는 문제를 줄이려면 리소스/설정 편차를 빠르게 진단하는 습관이 중요한데, 쿠버네티스에서도 비슷합니다. 관련해서는 Kubernetes OOMKilled 메모리 튜닝 실전 가이드처럼 “재현 가능한 관측과 원인 분리”가 핵심입니다.
또한 생성 API를 외부에 노출해 운영한다면, 트래픽 급증 시 재시도/백오프 설계를 같이 챙겨야 합니다. 장애 시 품질 이슈로 오인되는 케이스도 많으니, OpenAI 429/Rate Limit 재시도·백오프 설계 같은 패턴을 참고해 호출 안정성을 확보하는 것도 도움이 됩니다.
마무리: 가장 흔한 정답 조합
색감·품질 붕괴를 가장 빠르게 복구하는 조합은 보통 다음 순서로 수렴합니다.
- VAE를 명시적으로 고정(자동 추정 끄기)
- 문제가 지속되면 VAE 디코드만
fp32로 - 최종 산출물은 PNG로 저장 + sRGB 변환 + ICC 포함
프롬프트를 바꾸기 전에, 디코딩과 색관리부터 고정하면 “같은 시드인데 왜 다르지?” 같은 스트레스를 크게 줄일 수 있습니다.