Mapa mental das três tarefas
Antes de escolher arquitetura, defina a tarefa com precisão cirúrgica: semantic (pixel → classe), instance (pixel → classe + id do objeto), panoptic (ambos, unificado). Cliente que pede “segmentar imagem” raramente sabe qual — pergunte até deixar claro.
U-Net do zero em PyTorch
import torch
import torch.nn as nn
def conv_block(cin, cout):
return nn.Sequential(
nn.Conv2d(cin, cout, 3, padding=1), nn.BatchNorm2d(cout), nn.ReLU(inplace=True),
nn.Conv2d(cout, cout, 3, padding=1), nn.BatchNorm2d(cout), nn.ReLU(inplace=True),
)
class UNet(nn.Module):
def __init__(self, n_classes=2):
super().__init__()
self.d1 = conv_block(3, 64)
self.d2 = conv_block(64, 128)
self.d3 = conv_block(128, 256)
self.bottleneck = conv_block(256, 512)
self.up3 = nn.ConvTranspose2d(512, 256, 2, stride=2)
self.u3 = conv_block(512, 256)
self.up2 = nn.ConvTranspose2d(256, 128, 2, stride=2)
self.u2 = conv_block(256, 128)
self.up1 = nn.ConvTranspose2d(128, 64, 2, stride=2)
self.u1 = conv_block(128, 64)
self.out = nn.Conv2d(64, n_classes, 1)
self.pool = nn.MaxPool2d(2)
def forward(self, x):
d1 = self.d1(x)
d2 = self.d2(self.pool(d1))
d3 = self.d3(self.pool(d2))
b = self.bottleneck(self.pool(d3))
u3 = self.u3(torch.cat([self.up3(b), d3], dim=1))
u2 = self.u2(torch.cat([self.up2(u3), d2], dim=1))
u1 = self.u1(torch.cat([self.up1(u2), d1], dim=1))
return self.out(u1)Loss: Dice + BCE
def dice_loss(logits, target, eps=1e-6):
probs = torch.sigmoid(logits)
inter = (probs * target).sum(dim=(2, 3))
union = probs.sum(dim=(2, 3)) + target.sum(dim=(2, 3))
dice = (2 * inter + eps) / (union + eps)
return 1 - dice.mean()
def combo_loss(logits, target):
bce = nn.functional.binary_cross_entropy_with_logits(logits, target)
return bce + dice_loss(logits, target)BCE puro falha com classes desbalanceadas (ex.: tumor ocupa 1% dos pixels). Dice é invariante a escala da classe. Combinar os dois é robusto. Para multi-classe, use Focal Loss + Dice.
SAM como ferramenta de anotação
from segment_anything import sam_model_registry, SamPredictor
import numpy as np, cv2
sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth").to("cuda")
predictor = SamPredictor(sam)
img = cv2.imread("foto.jpg")
predictor.set_image(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# Prompt = um ponto dentro do objeto
point_coords = np.array([[500, 375]])
point_labels = np.array([1]) # 1 = foreground
masks, scores, _ = predictor.predict(
point_coords=point_coords,
point_labels=point_labels,
multimask_output=True,
)
best_mask = masks[np.argmax(scores)]Workflow 2026 para novo dataset de segmentação: (1) SAM gera máscaras candidatas por clique, (2) anotador só corrige onde SAM errou, (3) treina U-Net/Mask2Former especializado. Reduz custo de rotulagem em 5–10x vs anotação manual pura.