Example #1
0
    def class_specific_dice(self, y_true, y_seg, i):
        """
        Compute the class specific Dice.

        :param i: The i-th tissue class, default parameters: 0 for background; 1 for myocardium of the left ventricle;
            2 for left atrium; 3 for left ventricle; 4 for right atrium; 5 for right ventricle; 6 for ascending aorta;
            7 for pulmonary artery.
        """
        y_seg = utils.get_segmentation(y_seg, self.mode)

        if self.mode == 'tf':
            assert y_true.shape[1:] == y_seg.shape[1:], "The ground truth and prediction must be of equal shape! " \
                                                        "Ground truth shape: %s, " \
                                                        "prediction shape: %s" % (y_true.get_shape().as_list(),
                                                                                  y_seg.get_shape().as_list())

            top = 2 * tf.reduce_sum(y_true[..., i] * y_seg[..., i])
            bottom = tf.reduce_sum(y_true[..., i] + y_seg[..., i])
            dice = tf.divide(top, tf.maximum(bottom, self.eps), name='class%s_dice' % i)

        elif self.mode == 'np':
            assert y_true.shape == y_seg.shape, "The ground truth and prediction must be of equal shape! " \
                                                "Ground truth shape: %s, prediction shape: %s" % (y_true.shape,
                                                                                                  y_seg.shape)

            top = 2 * np.sum(y_true[..., i] * y_seg[..., i])
            bottom = np.sum(y_true[..., i] + y_seg[..., i])
            dice = np.divide(top, np.maximum(bottom, self.eps))

        return dice
Example #2
0
def average_surface_distance(predictions, labels, spacing_mm=(1, 1, 1)):
    """
    Return the average surface distances based on the predictions and labels.

    :param predictions: list of output predictions, of shape [1, *vol_shape, n_class]
    :param labels: list of ground truths, of shape [1, *vol_shape, n_class]
    :param spacing_mm: 3-element indicating voxel spacings in x, y, z direction.
    :return: An array of the ASD metric of each prediction.
    """
    assert len(predictions) == len(labels), "Number of predictions and labels don't equal."

    n = len(predictions)
    asd = np.empty([n])
    n_class = labels[0].shape[-1]
    SD = SurfaceDistance(spacing_mm)
    for i in range(n):
        class_asd = []
        mask_gt = labels[i].squeeze(0)
        mask_pred = utils.get_segmentation(predictions[i], mode='np').squeeze(0)
        for k in range(n_class):
            class_asd.append(SD.compute_average_surface_distance(mask_gt=mask_gt[..., k],
                                                                 mask_pred=mask_pred[..., k])
                             )

        asd[i] = np.mean(class_asd)

    return asd
Example #3
0
def hausdorff_distance(predictions, labels, spacing_mm=(1, 1, 1), percent=100):
    """
    Return the Hausdorff distances based on the predictions and labels.

    :param predictions: list of output predictions
    :param labels: list of ground truths
    :param spacing_mm: 3-element indicating voxel spacings in x, y, z direction.
    :param percent: percentile of the distances instead of the maximum distance, a value between 0 and 100
    :return: An array of the ASD metric of each prediction.
    """
    assert len(predictions) == len(labels), "Number of predictions and labels don't equal."

    n = len(predictions)
    hd = np.empty([n])
    n_class = labels[0].shape[-1]
    SD = SurfaceDistance(spacing_mm)
    for i in range(n):
        class_hd = []
        mask_gt = labels[i].squeeze(0)
        mask_pred = utils.get_segmentation(predictions[i], mode='np').squeeze(0)
        for k in range(n_class):
            class_hd.append(SD.compute_robust_hausdorff(mask_gt=mask_gt[..., k],
                                                        mask_pred=mask_pred[..., k],
                                                        percent=percent)
                            )

        hd[i] = np.mean(class_hd)

    return hd
Example #4
0
    def averaged_foreground_jaccard(self, y_true, y_seg):
        """
                Assume the first class is the background.
                """
        if self.mode == 'tf':
            assert y_true.shape[1:] == y_seg.shape[1:], "The ground truth and prediction must be of equal shape! " \
                                                        "Ground truth shape: %s, " \
                                                        "prediction shape: %s" % (y_true.get_shape().as_list(),
                                                                                  y_seg.get_shape().as_list())

            assert y_seg.get_shape().as_list()[-1] == self.n_class, "The number of classes of the segmentation " \
                                                                    "should be equal to %s!" % self.n_class
        elif self.mode == 'np':
            assert y_true.shape == y_seg.shape, "The ground truth and prediction must be of equal shape! " \
                                                "Ground truth shape: %s, prediction shape: %s" % (y_true.shape,
                                                                                                  y_seg.shape)

            assert y_seg.shape[-1], "The number of classes of the segmentation should be equal to %s!" % self.n_class

        y_seg = utils.get_segmentation(y_seg, self.mode)
        jaccard = 0.
        if self.mode == 'tf':
            y_true = tf.cast(y_true, dtype=tf.bool)
            y_seg = tf.cast(y_seg, dtype=tf.bool)
            for i in range(1, self.n_class):
                top = tf.reduce_sum(tf.cast(tf.logical_and(y_true[..., i], y_seg[..., i]), tf.float32))
                bottom = tf.reduce_sum(tf.cast(tf.logical_or(y_true[..., i], y_seg[..., i]), tf.float32))
                jaccard += top / (tf.maximum(bottom, self.eps))

            return tf.divide(jaccard, tf.cast(self.n_class - 1, dtype=tf.float32),
                             name='averaged_foreground_jaccard')

        elif self.mode == 'np':
            y_true = y_true.astype(np.bool)
            y_seg = y_seg.astype(np.bool)
            for i in range(1, self.n_class):
                top = np.sum(np.logical_and(y_true[..., i], y_seg[..., i]).astype(np.float32))
                bottom = np.sum(np.logical_or(y_true[..., i], y_seg[..., i]).astype(np.float32))
                jaccard += top / (np.maximum(bottom, self.eps))

            return np.divide(jaccard, self.n_class - 1)
Example #5
0
    def averaged_foreground_dice(self, y_true, y_seg):
        """
        Assume the first class is the background.
        """
        if self.mode == 'tf':
            assert y_true.shape[1:] == y_seg.shape[1:], "The ground truth and prediction must be of equal shape! " \
                                                        "Ground truth shape: %s, " \
                                                        "prediction shape: %s" % (y_true.get_shape().as_list(),
                                                                                  y_seg.get_shape().as_list())

            assert y_seg.get_shape().as_list()[-1] == self.n_class, "The number of classes of the segmentation " \
                                                                    "should be equal to %s!" % self.n_class
        elif self.mode == 'np':
            assert y_true.shape == y_seg.shape, "The ground truth and prediction must be of equal shape! " \
                                                "Ground truth shape: %s, prediction shape: %s" % (y_true.shape,
                                                                                                  y_seg.shape)

            assert y_seg.shape[-1], "The number of classes of the segmentation should be equal to %s!" % self.n_class

        y_seg = utils.get_segmentation(y_seg, self.mode)
        dice = 0.
        if self.mode == 'tf':
            for i in range(1, self.n_class):
                top = 2 * tf.reduce_sum(y_true[..., i] * y_seg[..., i])
                bottom = tf.reduce_sum(y_true[..., i] + y_seg[..., i])
                dice += top / (tf.maximum(bottom, self.eps))

            return tf.divide(dice, tf.cast(self.n_class - 1, dtype=tf.float32), name='averaged_foreground_dice')

        elif self.mode == 'np':
            for i in range(1, self.n_class):
                top = 2 * np.sum(y_true[..., i] * y_seg[..., i])
                bottom = np.sum(y_true[..., i] + y_seg[..., i])
                dice += top / (np.maximum(bottom, self.eps))

            return np.divide(dice, self.n_class - 1)