Morphology#

The Hausdorff losses estimate the distance between the predicted and the labelled segmentation by eroding the error of the pixels that thresholding the prediction at 0.5 gets wrong. They take probabilities, not logits: pass a softmax. HausdorffERLoss takes images (B, C, H, W) and HausdorffERLoss3D volumes (B, C, D, H, W), with an int64 label map of one channel.

class kornia.losses.HausdorffERLoss(alpha=2.0, k=10, reduction='mean')[source]#

Compute the erosion-based Hausdorff loss of (B, C, H, W) probabilities against (B, 1, H, W) labels.

criterion = HausdorffERLoss(alpha, k, reduction) stores its arguments, and loss = criterion(pred, target) scores, for each class channel c, a surrogate of the Hausdorff distance between the predicted and the labelled segmentation, after Loss_ER of [KS19] (Eq. 12): the per-pixel error \(e\), weighted by the k successive erosions \(\varepsilon\) of the misclassified set that keep the pixel:

\[\mathcal{L} = e \sum_{i=1}^{k} i^\alpha \, \varepsilon^i([e > 0]), \qquad e = \max\left(\frac{(p_c - [y = c])^2 - 1/4}{3/4},\ 0\right)\]
Convention:
  • pred holds per-class probabilities (B, C, H, W) and nothing is applied to it, unlike the logits of focal_loss(): pass a softmax. target holds int64 labels (B, 1, H, W) in [0, C). Channel c is compared with target == c, class 0 included.

  • The [0, C) check reads the data, so it is skipped wherever the call captures a graph (torch.compile or torch.export). A captured call whose target holds an out-of-range label scores those voxels in no class and returns a finite loss instead of raising; an eager call still raises.

  • The default 'mean' is the plain mean of the 'none' tensor.

  • \(e\) is nonzero exactly where thresholding \(p_c\) at 0.5 gets the pixel wrong, \(|p_c - [y = c]| > 1/2\), and is 1 for a fully wrong pixel. A correct pixel adds nothing and gets no gradient, however uncertain; a probability of exactly 0.5 counts as correct. With two classes this is the argmax decision. With three or more it is a one-vs-rest margin, stricter than argmax: a pixel whose true class wins the argmax with \(p_c < 1/2\) still counts in channel \(c\).

  • \(\varepsilon\) takes the minimum over the pixel and its four neighbours. Pixels outside the image count as \(+\infty\), so the image border is not an edge of an error.

  • The weight of a misclassified pixel is \(\sum_{i=1}^{\min(k, d - 1)} i^\alpha\), for its city-block distance \(d\) to the nearest correctly classified pixel. It is a constant: the gradient reaches each misclassified pixel through \(e\), in proportion to that weight. The pixels on the edge of an error (\(d = 1\)) get none, and errors at most two pixels wide add nothing.

  • The weight measures how thick an error is, not how far it lies from the target, so a thin or small error far from the object, which can set the Hausdorff distance, adds little or nothing (#6680).

  • Use it as an auxiliary term next to an overlap loss, never alone, as the paper does: region_loss + lam * hausdorff_loss, with lam set so that both terms weigh the same (the paper recomputes it from their means after each epoch) or ramped up from a small value. Choose k from the expected error radius in pixels: an error deeper than k gets the same weight as one k deep.

  • The value is a unitless surrogate, at most \(\sum_{i=1}^{k} i^\alpha\) per pixel (385 at the defaults), not a distance in pixels.

  • The paper erodes \((p_c - [y = c])^2\) itself, by a convolution followed by a soft threshold at 0.5, which leaves nothing of a solid error after two erosions and ignores misclassified pixels whose neighbourhood-averaged squared error is below 1/2. On a binary prediction this loss equals the exact erosion of \(e\) (#6676).

Parameters:
  • alpha (float, optional) – exponent of the weight \((i + 1)^\alpha\) of erosion \(i = 0, \dots, k - 1\). Default: 2.0

  • k (int, optional) – number of erosions. Default: 10

  • reduction (str, optional) – 'mean' (default), 'sum' or 'none'. Default: "mean"

Shape:
  • pred: probabilities (B, C, H, W).

  • target: int64 class labels (B, 1, H, W).

  • Output: a scalar for 'mean' and 'sum', (C, B, 1, H, W) for 'none'.

See also

Examples

>>> hdloss = HausdorffERLoss()
>>> input = torch.randn(5, 3, 20, 20).softmax(1)
>>> target = (torch.rand(5, 1, 20, 20) * 2).long()
>>> res = hdloss(input, target)

Next to DiceLoss, which takes the logits and (B, H, W) labels:

>>> logits = torch.randn(5, 3, 20, 20)
>>> loss = kornia.losses.DiceLoss()(logits, target[:, 0]) + 0.1 * hdloss(logits.softmax(1), target)
class kornia.losses.HausdorffERLoss3D(alpha=2.0, k=10, reduction='mean')[source]#

Compute the erosion-based Hausdorff loss of (B, C, D, H, W) probabilities against volume labels.

criterion = HausdorffERLoss3D(alpha, k, reduction) stores its arguments, and loss = criterion(pred, target) is HausdorffERLoss on volumes [KS19].

Convention:
  • See the Convention block of HausdorffERLoss. The erosion takes the minimum over the voxel and its six face neighbours, so it also runs along D, and the distance \(d\) is the city-block distance in the volume.

Parameters:
  • alpha (float, optional) – exponent of the weight \((i + 1)^\alpha\) of erosion \(i = 0, \dots, k - 1\). Default: 2.0

  • k (int, optional) – number of erosions. Default: 10

  • reduction (str, optional) – 'mean' (default), 'sum' or 'none'. Default: "mean"

Shape:
  • pred: probabilities (B, C, D, H, W).

  • target: int64 class labels (B, 1, D, H, W).

  • Output: a scalar for 'mean' and 'sum', (C, B, 1, D, H, W) for 'none'.

See also

Examples

>>> hdloss = HausdorffERLoss3D()
>>> input = torch.randn(5, 3, 20, 20, 20).softmax(1)
>>> target = (torch.rand(5, 1, 20, 20, 20) * 2).long()
>>> res = hdloss(input, target)