ControlNet: condicionamento espacial preciso
- ⬜⚡ Stable Diffusion 3.5 e Flux: MMDiT e DiT por dentro(Diffusion Models & Geração Multimodal)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
O problema que ControlNet resolve
Diffusion + prompt textual é mágico, mas tem um teto: você não consegue ditar onde as coisas vão. "Um homem de óculos em pé" gera mil composições aleatórias. Se você precisa de uma pose específica, um layout específico de arquitetura, uma silhueta de produto — o prompt sozinho não basta.
Antes do ControlNet, o estado da arte era img2img com strength baixa (preserva composição mas perde fidelidade) ou inpaint manual. Nada satisfatório para fluxos profissionais. Zhang & Agrawala (ICCV 2023, "Adding Conditional Control to Text-to-Image Diffusion Models" — best paper) mudaram isso ao introduzir um mecanismo de condicionamento espacial plug-and-play que não destrói o modelo base.
A inovação central não é o conceito de "usar um hint extra" — é como integrar esse hint sem catastrophic forgetting. A resposta foi zero-conv + trainable copy do encoder.
A arquitetura em uma figura
Por que zero-conv funciona
Considere um bloco do decoder do U-Net no momento de combinar features:
No início do treino, Z = 0, logo h_out = U_dec(h_in) — exatamente como o U-Net original. O gradiente da loss flui de h_out pra Z (∂L/∂Z ≠ 0 imediatamente, mesmo que Z tenha pesos zero, porque a entrada ≠ 0). Z aprende a "abrir" o canal gradualmente, e C_enc segue ajustando seus pesos para passar features úteis.
A genialidade: você nunca tem um momento de "transição traumática". O modelo base nunca degrada, e a integração é suave. Mesmo princípio do adaLN-Zero do DiT — virou padrão para conditioning.
Tipos de condicionamento (do paper original e extensões)
| Tipo | Hint extraído por | Caso de uso |
|---|---|---|
| Canny edges | Algoritmo Canny clássico (OpenCV) | Preserva contornos exatos (produtos, arquitetura) |
| HED soft edges | Holistically-Nested Edge Detection (Xie & Tu 2015) | Edges mais suaves, "artistic" |
| Depth map | MiDaS (Ranftl et al. 2020) ou DPT | Composição 3D, profundidade plausível |
| Normal map | BAE-Net / Omnidata | Iluminação coerente, superfícies |
| Semantic segmentation | ADE20K / Mask2Former | Layout de cena ("céu aqui, prédio ali") |
| OpenPose | OpenPose keypoints / DWPose (Yang et al. 2023) | Pose humana precisa |
| M-LSD lines | M-LSD (Gu et al. 2022) | Linhas retas, arquitetura, interiores |
| Scribble | Desenho manual / sketch sintético | Composição rápida a partir de rascunho |
| Tile (upscale) | A imagem original (downsample) | Upscale com adição de detalhes |
| QR code | QR code renderizado | QR codes "artísticos" que ainda escaneiam |
| Reference-only | A imagem inteira (via attention) | Style/composição sem hint estrutural |
| IP-Adapter | CLIP/SigLIP vision encoder | Style + subject reference, leve (~50M) |
ControlNet na prática: ComfyUI / diffusers
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel
from diffusers.utils import load_image
import torch, cv2, numpy as np
# 1. Carrega base + ControlNet
controlnet = ControlNetModel.from_pretrained(
"diffusers/controlnet-canny-sdxl-1.0",
torch_dtype=torch.float16,
)
pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
controlnet=controlnet,
torch_dtype=torch.float16,
variant="fp16",
).to("cuda")
# 2. Extrai Canny da imagem de referência
ref = load_image("reference.png").resize((1024, 1024))
img = np.array(ref)
edges = cv2.Canny(img, 100, 200) # threshold low/high
edges = np.stack([edges] * 3, axis=-1) # 3 canais para o ControlNet
control_image = Image.fromarray(edges)
# 3. Geração condicionada
out = pipe(
prompt="cyberpunk neon city, ultra-detailed, 8k",
image=control_image,
controlnet_conditioning_scale=0.7, # 0..2 — força do condicionamento
num_inference_steps=30,
guidance_scale=7.5,
).images[0]
out.save("controlnet_out.png")IP-Adapter: style transfer leve
IP-Adapter (Ye et al. 2023) ataca o problema "estilo/subject reference" sem treinar um ControlNet pesado. A ideia: adicionar uma cross-attention paralela ao cross-attention de texto, condicionada num embedding da imagem de referência produzido por CLIP vision encoder.
Vantagens: ~22M params (SD 1.5) a ~50M (SDXL), treina em horas, integra suavemente com ControlNet. Variantes: IP-Adapter-FaceID (subject consistente para retratos), IP-Adapter-Plus (mais fidelidade).
Qual limite do condicionamento apenas por texto motivou a criação do controle estrutural?
Decisão: quando usar o quê
📋 Você precisa gerar variações de um produto preservando a silhueta exata
Canny extrai contornos com precisão; em conjunto com strength alto (0.8-1.0) preserva a forma do produto. Combinar com prompt criativo gera variações de cenário/iluminação sem mudar o produto.
Alt: img2img puro
Alt: Strength baixo preserva mas degrada qualidade; strength alto perde a silhueta
Alt: IP-Adapter
Alt: Bom para style reference mas não preserva contornos com precisão
Alt: ControlNet Depth
Alt: Preserva profundidade 3D mas não contornos exatos
📋 Você quer gerar várias imagens com o mesmo personagem em poses diferentes
FaceID mantém o rosto consistente; OpenPose define a pose. Combinação é o flow padrão para 'consistent character'. Alternativamente, treine um LoRA do personagem (próximo módulo) para máxima consistência.
Alt: Só ControlNet OpenPose
Alt: Mantém a pose mas o rosto/identidade varia
Alt: Só IP-Adapter
Alt: Identidade ok mas pose continua aleatória
ControlNet em SD3 e Flux
Para arquiteturas MMDiT/DiT, "copiar o encoder" não é tão simples — não há U-Net contrativo. As implementações para SD3 e Flux usam variantes:
Perguntas que sobram
❓ Posso treinar meu próprio ControlNet?
❓ ControlNet conflita com LoRA?
❓ Por que controlnet_scale > 1 dá artefatos?
❓ Existe um ControlNet 'universal' que aceita qualquer hint?
Papers e código
Próximo: treinar um LoRA de estilo/personagem em 30 minutos. Vamos ver kohya_ss, OneTrainer, ai-toolkit (Ostris), captioning automático, e como interpretar curvas de loss para identificar overfitting visual.
Perguntas frequentes
❓ O que o condicionamento espacial resolve?
❓ Dá para combinar mais de um condicionamento?
❓ Condicionamento substitui ajuste fino?
Fixando
A camada de saída inicializada em zero é o mecanismo central. O que ela evita?
Por que forçar a intensidade do controle acima do padrão produz artefatos?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…