PDI+VC · Exercício de Programação

EP09_05 — 🟠 Avaliação de Segmentação: IoU e Dice Pixel a Pixel

9.10.5 EP09_05 🟠 Avaliação de Segmentação: IoU e Dice Pixel a Pixel

O Bloco 2 da seção “Segmentação Semântica com Arquitetura U-Net” define, em poucas linhas, a função iou_mascaras, usada para medir a qualidade da linha de base morfológica clássica (suavização + Otsu + abertura) e, mais adiante, da própria U-Net treinada. Diferentemente do IoU do EP09_04 — calculado sobre caixas delimitadoras (regiões retangulares descritas por quatro números) —, o IoU de segmentação é calculado pixel a pixel: cada posição da imagem é comparada individualmente entre a máscara predita e a máscara de referência.

Você foi encarregado de generalizar essa avaliação, implementando não apenas o IoU pixel a pixel, mas também o coeficiente de Dice, outra métrica de sobreposição amplamente usada em segmentação médica (inclusive na função perda_dice, mencionada no mesmo bloco do capítulo como base da função de perda usada para treinar a U-Net).

9.10.5.1 📋 Diretrizes de Implementação

  1. Entrada: Ler as dimensões \(H \times W\) das máscaras.

  2. Máscara predita: Ler \(H\) linhas com \(W\) valores inteiros (0 ou 1) cada — por exemplo, a saída de uma U-Net após limiarização em \(0{,}5\) sobre a sigmoide, como no Bloco 4 do capítulo.

  3. Máscara de referência: Ler mais \(H\) linhas com \(W\) valores inteiros (0 ou 1) cada — o ground truth.

  4. Interseção e união: Considerando cada pixel como pertencente ao objeto quando seu valor é diferente de zero, \[ \text{interseção} = \sum_{i,j} \mathbb{1}[P_{ij}=1 \wedge R_{ij}=1], \qquad \text{união} = \sum_{i,j} \mathbb{1}[P_{ij}=1 \vee R_{ij}=1]. \]

  5. IoU pixel a pixel: \[ \text{IoU} = \frac{\text{interseção}}{\text{união}}. \]

  6. Coeficiente de Dice: \[ \text{Dice} = \frac{2 \cdot \text{interseção}}{|P| + |R|}, \] em que \(|P|\) e \(|R|\) são o número total de pixels de objeto em cada máscara.

  7. Convenção para máscaras vazias: se ambas as máscaras não possuem nenhum pixel de objeto (união \(= 0\) e \(|P|+|R|=0\)), considere a correspondência trivialmente perfeita: \(\text{IoU} = \text{Dice} = 1{,}0\).

  8. Saída: Duas linhas, IoU: X.XXXX e Dice: X.XXXX, cada valor com 4 casas decimais.

9.10.5.2 📌 Restrições Computacionais

  • Qualquer valor não nulo conta como objeto: trate valores diferentes de \(0\) (não apenas \(1\)) como pertencentes à máscara, replicando a checagem predita > 0 usada em iou_mascaras no capítulo.
  • Mesmas dimensões: as duas máscaras sempre possuem exatamente \(H \times W\) elementos.
  • Convenção de vazio: aplique a regra do item 7 apenas quando ambas as máscaras estiverem totalmente vazias; se apenas uma estiver vazia, a interseção é \(0\) e o IoU/Dice resultante também será \(0\).

9.10.5.3 🧠 Fundamentação Teórica

Elemento Papel na avaliação de segmentação
IoU pixel a pixel Generaliza a métrica do EP09_04 para regiões de forma arbitrária — não apenas retângulos — comparando máscara predita e referência posição a posição
Coeficiente de Dice Métrica relacionada ao IoU (sempre \(\text{Dice} \ge \text{IoU}\)), mais sensível a pequenas interseções e amplamente usada como função de perda em segmentação (função perda_dice do capítulo)
Convenção de máscaras vazias Evita divisão por zero e reconhece que “nenhum objeto previsto, nenhum objeto real” é, por definição, um acerto
Comparação clássico vs. U-Net O capítulo usa exatamente este tipo de métrica para justificar, numericamente, por que a U-Net supera a linha de base morfológica em cenários de baixo contraste

9.10.5.4 🧩 Métodos do morph.py que podem ajudar

  • mm.readImg(h, w, dtype='uint8') — lê diretamente cada máscara binária \(h \times w\) da entrada padrão (os valores \(0/1\) cabem perfeitamente no tipo inteiro padrão).
  • A própria função iou_mascaras, definida no Bloco 2 da seção de U-Net do capítulo (não faz parte do morph.py, mas do código do capítulo), é a inspiração direta deste exercício — vale reler aquelas poucas linhas antes de programar.
  • Para uma extensão opcional (não exigida por este EP), mm.connectedComponents ou mm.label0 (vistos no contexto de análise de componentes conexos) permitiriam rotular cada nódulo individualmente e calcular o IoU por componente, em vez de sobre a máscara inteira.

9.10.5.5 📦 Especificação de Entrada e Saída (VPL)

Entrada:

  • Linha 1: Inteiros \(H\) e \(W\).
  • Próximas \(H\) linhas: \(W\) valores inteiros (0 ou 1) — máscara predita.
  • Próximas \(H\) linhas: \(W\) valores inteiros (0 ou 1) — máscara de referência.

Saída:

  • Linha 1: IoU: X.XXXX.
  • Linha 2: Dice: X.XXXX.
Dica💡 Exemplo Ilustrativo

Considere uma máscara predita com um quadrado \(2\times2\) de pixels ativos e uma referência deslocada em uma coluna, sobrepondo-se em apenas metade da área:

Predita         Referência
0 0 0 0         0 0 0 0
0 1 1 0         0 0 1 1
0 1 1 0         0 0 1 1
0 0 0 0         0 0 0 0

Interseção \(=2\) pixels, união \(=6\) pixels (\(4+4-2\)), logo \(\text{IoU}=2/6\approx0{,}3333\) e \(\text{Dice}=2\cdot2/(4+4)=0{,}5000\) — repare que o Dice é sempre igual ou maior que o IoU para a mesma sobreposição.

9.10.5.6 📌 Exemplos

Entrada Saída Observação
4 4
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0
0 0 0 0
0 0 1 1
0 0 1 1
0 0 0 0
IoU: 0.3333
Dice: 0.5000
Máscaras \(4\times4\) com sobreposição parcial de 2 pixels.
🎮 Simulador: IoU e Dice Pixel a Pixel 🟠 Segmentação
Interseção (TP) Só predita (FP) Só referência (FN) Fundo (TN)
5×5
Quadrado
🔵 Máscara Predita
🟡 Máscara de Referência
🎯 Comparação Visual
📊 Cálculos e Fórmulas
Figura 9.47: Simulador EP09_05: Avaliação de Segmentação — IoU e Dice Pixel a Pixel
%%writefile EP09_05.py
# Código Python
Writing EP09_05.py
TestSuite("EP09_05.py").run()
✔️ EP09_05.cases já existe em casos/
📋 4 caso(s) carregado(s) de casos/EP09_05.cases

🔍 Testando Python: EP09_05.py
⚠️ EP09_05.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.