in mlebench/competitions/uw-madison-gi-tract-image-segmentation/grade.py [0:0]
def hausdorff_distance(predicted_mask: np.ndarray, true_mask: np.ndarray) -> float:
"""
Computes the hausdorff distance between two 3-dimensional binary masks (H, W, C)
Args:
predicted_mask: A 3D binary numpy array indicating where the segmentation is predicted
true_mask: A 3D binary numpy array indicating where the segmentation is
"""
# if both empty, return nan, if identical return 0, if only one is empty, return 1
if np.sum(predicted_mask) == 0 and np.sum(true_mask) == 0:
return np.nan
elif np.array_equal(predicted_mask, true_mask):
return 0
elif (np.sum(predicted_mask) == 0 and np.sum(true_mask) > 0) or (
np.sum(predicted_mask) > 0 and np.sum(true_mask) == 0
):
return 1
# if the pred mask is > 1 OOM larger than the true mask, return 1 to avoid unnecess heavy computation
if predicted_mask.sum() > 10 * true_mask.sum():
return 1
true_coordinates = np.argwhere(true_mask)
predicted_coordinates = np.argwhere(predicted_mask)
# normalize so that the coordinates are in the range [0, 1], to get a unit cube
normalization_constants = true_mask.shape
true_coordinates = true_coordinates / normalization_constants
predicted_coordinates = predicted_coordinates / normalization_constants
# Symmetrize Hausdorff distance
hausdorff_distance1, _, _ = directed_hausdorff(true_coordinates, predicted_coordinates)
hausdorff_distance2, _, _ = directed_hausdorff(predicted_coordinates, true_coordinates)
hausdorff_distance = max(hausdorff_distance1, hausdorff_distance2)
# normalize by the maximum distance possible in a unit cube, which is the diagonal
hausdorff_distance = hausdorff_distance / UNIT_CUBE_DIAGONAL
return hausdorff_distance