def hausdorff_distance()

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