← All notes

Statistical note

Segmentation metrics: IoU, Dice, and boundary error

Overlap, class imbalance, object-level failure, and how to evaluate biological segmentation beyond one score.

11 min read

For predicted pixels PP and reference pixels GG,

IoU=PGPG,Dice=2PGP+G.IoU=\frac{|P\cap G|}{|P\cup G|},\qquad Dice=\frac{2|P\cap G|}{|P|+|G|}.

For a binary mask, Dice equals the pixel-level F1 score, and Dice=2IoU/(1+IoU)Dice=2IoU/(1+IoU).

Intersection, union, and boundary mismatch

import numpy as np
from sklearn.metrics import jaccard_score, f1_score

truth = np.array([[0, 1, 1], [0, 1, 0], [0, 0, 0]]).ravel()
pred  = np.array([[0, 1, 1], [0, 1, 1], [0, 0, 0]]).ravel()
print("IoU", jaccard_score(truth, pred))
print("Dice", f1_score(truth, pred))

Case study: cell segmentation

Pixel overlap can look excellent even when adjacent cells are merged, because most foreground pixels remain correct. Add object-level matching, split/merge counts, boundary distance, per-image distributions, and downstream measurement error. Define how empty masks and small objects are handled.

Macro averaging gives each class equal weight; micro averaging pools pixels and can be dominated by background. scikit-learn supplies overlap primitives, while object and boundary metrics often require domain-specific code.

Functions: sklearn.metrics.jaccard_score, f1_score, confusion_matrix, and multilabel_confusion_matrix.