def precision(prediction: torch.Tensor, target: torch.Tensor, epsilon: float = 1e-6) -> torch.Tensor: """ Compute the precision score Args: prediction: Network output. Dimensions - (Batch, Class, Depth, Height, Width) target: Target values. The classes should be one-hot encoded. Dimensions - (Batch, Class, Depth, Height, Width) epsilon: Smooth factor to prevent division by 0. Returns: torch.Tensor: Precision score calculated for each class averaged across the whole batch """ assert utils.is_binary(prediction), "Predictions must be binary" assert utils.is_binary(target), "Target must be binary" volume_dims = list(range(2, target.dim())) true_positives = utils.calculate_intersection(prediction, target, dim=volume_dims) false_positive = utils.calculate_false_positives(prediction, target, dim=volume_dims) score = true_positives / ( true_positives + false_positive + epsilon) score = torch.mean(score, dim=BATCH_DIM) return score
def hausdorff95(prediction: torch.Tensor, target: torch.Tensor, merge_operation: Callable[ [torch.Tensor], float] = torch.max) -> torch.Tensor: """ 95th percentile of the Hausdorff Distance. Computes the 95th percentile of the (symmetric) Hausdorff Distance (HD) between the binary objects in two images. Heavily depends on medpy.metric.binary.hd95, so check it out for more details. Args: prediction: Network output. Dimensions - (Batch, Class, Depth, Height, Width) target: Target values. Dimensions - (Batch, Class, Depth, Height, Width) merge_operation: Operation to merge results from all the slices in the batch into a single value. Should be a function that accepts two dimensional tensors (Batch, Depth). Good examples are: torch.mean, torch.max, torch.min. Returns: torch.Tensor: Hausdorff distance for the provided volume """ assert utils.is_binary(prediction), "Predictions must be binary" assert utils.is_binary(target), "Target must be binary" prediction = prediction.cpu().detach().numpy() target = target.cpu().detach().numpy() volumes_count, _, slices_count, _, _ = prediction.shape results = np.zeros((volumes_count, slices_count)) for volume_idx in range(volumes_count): for slice_idx in range(slices_count): prediction_slice = prediction[ volume_idx, FIRST_CLASS, slice_idx, ...] target_slice = target[volume_idx, FIRST_CLASS, slice_idx, ...] if utils.has_only_zeros(prediction_slice) or \ utils.has_only_zeros(target_slice): results[volume_idx, slice_idx] = 0 else: results[volume_idx, slice_idx] = mp.hd95(prediction_slice, target_slice) return merge_operation(torch.from_numpy(results))
def test_if_returns_true_for_only_0s(self): assert utils.is_binary(torch.zeros(VOLUME_DIMS))
def test_if_returns_false_for_one_non_binary_value(self): input = torch.ones(VOLUME_DIMS) input[0, 0, 0] = 0.2 assert not utils.is_binary(input)
def test_if_returns_false_for_all_non_binary_values(self): input = torch.ones(VOLUME_DIMS) * 0.5 assert not utils.is_binary(input)