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, andloss = criterion(pred, target)scores, for each class channelc, a surrogate of the Hausdorff distance between the predicted and the labelled segmentation, afterLoss_ERof [KS19] (Eq. 12): the per-pixel error \(e\), weighted by theksuccessive 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:
predholds per-class probabilities(B, C, H, W)and nothing is applied to it, unlike the logits offocal_loss(): pass a softmax.targetholds int64 labels(B, 1, H, W)in[0, C). Channelcis compared withtarget == c, class 0 included.The
[0, C)check reads the data, so it is skipped wherever the call captures a graph (torch.compileortorch.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, withlamset so that both terms weigh the same (the paper recomputes it from their means after each epoch) or ramped up from a small value. Choosekfrom the expected error radius in pixels: an error deeper thankgets the same weight as onekdeep.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:
- 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
HausdorffERLoss3D: the same loss for volumes.DiceLoss: an overlap loss that takes logits, not probabilities.
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, andloss = criterion(pred, target)isHausdorffERLosson 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 alongD, and the distance \(d\) is the city-block distance in the volume.
- Parameters:
- 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
HausdorffERLoss: the same loss for images.
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)