Skip to content
AI360Xpert
Beta

IoU and Dice Overlap Scores

Two ratios over the same overlap that price segmentation agreement, one penalizing misses and false paint symmetrically.

IoU and Dice read the same overlap differently, so one mask gives two linked scores with Dice always higher
IoU and Dice read the same overlap differently, so one mask gives two linked scores with Dice always higher

Why Does This Exist?

Pixel accuracy rewards background: labeling everything road on a road-heavy photo scores well while finding nothing. Overlap ratios fix that by scoring only claimed and true foreground. IoU is the box standard, Dice the medical-mask standard, and segmentation reports need both without confusion.

This page gives formulas, one recomputed mask, and misread pitfalls. Box matching is in IoU.

Think of It Like This

Shared pizza slices counted two ways

Two friends claim slices. Shared slices are the overlap, all distinct slices claimed are the union, all slices touched counting shared twice is the Dice denominator.

Same pizza, two bills. Dice always looks kinder than IoU on the same share.

How It Actually Works

For sets AA truth and BB prediction, IoU=∣A∩B∣/∣A∪B∣\text{IoU} = |A \cap B| / |A \cup B| and Dice=2∣A∩B∣/(∣A∣+∣B∣)\text{Dice} = 2|A \cap B| / (|A| + |B|). Dice equals 2IoU/(1+IoU)2\text{IoU} / (1 + \text{IoU}), so Dice sits above IoU except at 0 and 1. Use IoU for detection-style reporting, Dice as loss-friendly overlap on small structures.

Worked mask example

Take 100 truth pixels with 80 shared, 10 missed and 10 false. Union is 80+10+10=11080 + 10 + 10 = 110. IoU is 80/110≈0.72780 / 110 \approx 0.727. Dice is 2×80/(100+100)=160/200=0.82 \times 80 / (100 + 100) = 160 / 200 = 0.8. Check the link: 2×0.727/1.727≈0.8422 \times 0.727 / 1.727 \approx 0.842? No, recompute honestly with ∣A∣=90|A| = 90? Wait truth 100 means ∣A∣=100|A| = 100, prediction 80+10=9080 + 10 = 90? Correct: missed 20? Let's fix cleanly: truth 100, shared 80, missed 20, false 10, prediction 90. Union 100+90−80=110100 + 90 - 80 = 110. IoU 80/110≈0.72780 / 110 \approx 0.727. Dice 160/190≈0.842160 / 190 \approx 0.842. Dice above IoU as claimed.

Code

Both ratios from the same counts.

def overlap_scores(shared: int, truth: int, pred: int) -> tuple[float, float]:    union = truth + pred - shared  # counted twice fix    iou = shared / max(union, 1)    dice = 2*shared / max(truth + pred, 1)    return round(iou, 3), round(dice, 3)

print(overlap_scores(80, 100, 90))# -> (0.727, 0.842)

Shared 80 with truth 100 and prediction 90 gives 0.727 and 0.842.

Watch Out For

Comparing Dice to IoU as if scales match

Dice 0.8 looks better than IoU 0.73 on identical masks. The symptom is cross-paper claims that vanish after conversion. Fix it by converting with Dice equals 2IoU/(1+IoU)2\text{IoU} / (1 + \text{IoU}) before comparing.

The Quick Version

  • IoU is overlap over union, Dice is twice overlap over total claimed.
  • Toy 80 shared with 100 truth and 90 predicted gives IoU about 0.727 and Dice about 0.842.
  • Dice sits above IoU except at 0 and 1 by exact conversion.
  • Use IoU for detection-style reports, Dice for small-structure overlap.
  • Convert before comparing across papers.