🤖 AI/ML

ControlNet

이미지 생성 제어를 위한 신경망 구조

📖 상세 설명

ControlNet은 Stable Diffusion 같은 이미지 생성 모델에 추가적인 조건부 제어를 부여하는 신경망 구조입니다. 사용자가 제공한 참조 이미지(엣지, 포즈, 깊이맵 등)를 기반으로 생성 과정을 정밀하게 유도하여, 텍스트 프롬프트만으로는 어려운 구도와 형태 제어를 가능하게 합니다.

2023년 2월 스탠포드 대학 연구진이 발표한 ControlNet은 이미지 생성 AI의 제어 가능성을 획기적으로 높였습니다. 논문 공개 직후 GitHub에서 빠르게 확산되어, 현재 가장 널리 사용되는 Stable Diffusion 확장 기능 중 하나가 되었습니다.

ControlNet의 핵심 아이디어는 "Zero Convolution"과 사전 학습된 Diffusion 모델의 인코더 복사입니다. 원본 모델의 가중치는 고정(frozen)하고, 복사된 네트워크만 조건부 입력에 맞게 학습합니다. Zero Convolution 초기화로 학습 초기에는 원본 출력과 동일하게 시작하여 안정적인 학습이 가능합니다.

실무에서 ControlNet은 캐릭터 포즈 일관성 유지, 건축 렌더링의 구도 지정, 제품 사진 배경 변환, 애니메이션 프레임 보간 등에 활용됩니다. Canny Edge, OpenPose, Depth Map, Scribble 등 다양한 전처리 모듈과 조합하며, 여러 ControlNet을 동시에 사용해 복합 조건 제어도 가능합니다.

💻 코드 예제

from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
import torch
from PIL import Image
import cv2
import numpy as np

# Canny Edge ControlNet 모델 로드
controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/sd-controlnet-canny",
    torch_dtype=torch.float16
)

# SD 1.5 + ControlNet 파이프라인 구성
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    controlnet=controlnet,
    torch_dtype=torch.float16
).to("cuda")

# 입력 이미지에서 Canny Edge 추출
def get_canny_edge(image_path, low_threshold=100, high_threshold=200):
    image = cv2.imread(image_path)
    image = cv2.Canny(image, low_threshold, high_threshold)
    image = image[:, :, None]
    image = np.concatenate([image, image, image], axis=2)
    return Image.fromarray(image)

# 참조 이미지로 엣지맵 생성
control_image = get_canny_edge("reference_pose.png")

# ControlNet으로 이미지 생성
result = pipe(
    prompt="a professional photo of a woman in red dress, studio lighting",
    negative_prompt="blurry, low quality, distorted",
    image=control_image,  # Canny edge를 조건으로 사용
    num_inference_steps=30,
    guidance_scale=7.5,
    controlnet_conditioning_scale=1.0  # ControlNet 영향력 (0-2)
).images[0]

result.save("controlled_output.png")

# 여러 ControlNet 동시 사용 (Multi-ControlNet)
from diffusers import MultiControlNetModel

controlnets = MultiControlNetModel([
    ControlNetModel.from_pretrained("lllyasviel/sd-controlnet-canny"),
    ControlNetModel.from_pretrained("lllyasviel/sd-controlnet-openpose"),
])
# conditioning_scale를 리스트로: [canny_scale, pose_scale]

🗣️ 실무 대화 예시

AI 아트 제작 미팅에서

"캐릭터 포즈를 일관되게 유지하려면 OpenPose ControlNet이 최고예요. 참조 사진에서 스켈레톤 추출하고, 프롬프트로 의상이나 배경만 바꾸면 같은 포즈의 다양한 버전을 뽑을 수 있어요."

기술 블로그에서

"ControlNet의 conditioning_scale이 핵심입니다. 1.0이면 조건 이미지를 강하게 따르고, 0.5면 프롬프트와 반반 영향을 받죠. 창의적 변형을 원하면 0.3-0.7 사이가 좋고, 정확한 재현이 필요하면 1.0 이상을 쓰세요."

제품 사진 자동화 논의에서

"Depth ControlNet으로 제품 실루엣을 유지하면서 배경만 AI로 바꾸고 있어요. 기존에 스튜디오 촬영 20장 하던 걸 샘플 1장 + ControlNet으로 대체해서 비용을 70% 절감했습니다."

⚠️ 주의사항

1
전처리 품질이 결과 좌우

ControlNet 입력(Canny, OpenPose 등)의 품질이 출력을 결정합니다. 흐릿하거나 노이즈가 많은 전처리 결과는 생성 품질도 저하시킵니다.

2
모델 호환성 확인

ControlNet 모델은 특정 SD 버전에 맞춰 학습됩니다. SD 1.5용 ControlNet을 SDXL에 사용하면 작동하지 않습니다. 버전을 반드시 확인하세요.

3
VRAM 사용량 증가

ControlNet은 추가 네트워크이므로 VRAM 사용량이 증가합니다. Multi-ControlNet 사용 시 더욱 증가하니, 8GB GPU에서는 --medvram 옵션을 고려하세요.

🔗 관련 용어

📚 더 배우기