Example #1
0
def _transform_and_predict(model,
                           x,
                           block_shape,
                           rotation,
                           translation=[0, 0, 0],
                           verbose=False):
    """Predict on rigidly transformed features.

    The rigid transformation is applied to the volumes prior to prediction, and
    the prediced labels are transformed with the inverse warp, so that they are
    in the same space.

    Parameters
    ----------
    model: `tf.keras.Model`, model used for prediction.
    x: 3D array, volume of features.
    block_shape: tuple of length 3, shape of non-overlapping blocks to take
        from the features. This also corresponds to the input of the model, not
        including the batch or channel dimensions.
    rotation: tuple of length 3, rotation angle in radians in each dimension.
    translation: tuple of length 3, units of translation in each dimension.
    verbose: bool, whether to print progress bar.

    Returns
    -------
    Array of predictions with the same shape and in the same space as the
    original input features.
    """

    x = np.asarray(x).astype(np.float32)
    affine = get_affine(x.shape, rotation=rotation, translation=translation)
    inverse_affine = tf.linalg.inv(affine)
    x_warped = warp(x, affine, order=1)

    x_warped_blocks = to_blocks_numpy(x_warped, block_shape)
    x_warped_blocks = x_warped_blocks[..., np.newaxis]  # add grayscale channel
    x_warped_blocks = standardize_numpy(x_warped_blocks)
    y = model.predict(x_warped_blocks, batch_size=1, verbose=verbose)

    n_classes = y.shape[-1]
    if n_classes == 1:
        y = y.squeeze(-1)
    else:
        # Usually, the argmax would be taken to get the class membership of
        # each voxel, but if we get hard values, then we cannot average
        # multiple predictions.
        raise ValueError(
            "This function is not compatible with multi-class predictions.")

    y = from_blocks_numpy(y, x.shape)
    y = warp(y, inverse_affine, order=0).numpy()

    return y
Example #2
0
def predict_from_array(inputs,
                       model,
                       block_shape,
                       batch_size=1,
                       normalizer=None,
                       n_samples=1,
                       return_variance=False,
                       return_entropy=False):
    """Return a prediction given a filepath and an ndarray of features.

    Parameters
    ----------
    inputs: ndarray, array of features.
    model: `tf.keras.Model`, trained model.
    block_shape: tuple of length 3, shape of sub-volumes on which to
        predict.
    batch_size: int, number of sub-volumes per batch for predictions.
    normalizer: callable, function that accepts an ndarray and returns an
        ndarray. Called before separating volume into blocks.
    n_samples: The number of sampling. If set as 1, it will just return the
        single prediction value. The default value is 1
    return_variance: Boolean. If set True, it returns the running population
        variance along with mean. Note, if the n_samples is smaller or equal to 1,
        the variance will not be returned; instead it will return None
    return_entropy: Boolean. If set True, it returns the running entropy.
        along with mean.

    Returns
    -------
    ndarray of predictions.
    """
    if normalizer:
        features = normalizer(inputs)
    else:
        features = inputs
    if block_shape is not None:
        features = to_blocks_numpy(features, block_shape=block_shape)
    else:
        features = features[None]  # Add batch dimension.

    # Add a dimension for single channel.
    features = features[..., None]

    # Predict per block to reduce memory consumption.
    n_blocks = features.shape[0]
    n_batches = math.ceil(n_blocks / batch_size)

    if not return_variance and not return_entropy and n_samples == 1:
        outputs = model.predict(features, batch_size=1, verbose=0)
        if outputs.shape[-1] == 1:
            # Binarize according to threshold.
            outputs = outputs > 0.3
            outputs = outputs.squeeze(-1)
            # Nibabel doesn't like saving boolean arrays as Nifti.
            outputs = outputs.astype(np.uint8)
        else:
            # Hard classes for multi-class segmentation.
            outputs = np.argmax(outputs, -1)
        outputs = from_blocks_numpy(outputs, output_shape=inputs.shape)
        return outputs

    raise NotImplementedError(
        "Predicting from Bayesian nets is not implemented yet.")

    means = np.zeros_like(features)
    variances = np.zeros_like(features)
    entropies = np.zeros_like(features)
    progbar = tf.keras.utils.Progbar(n_batches)
    progbar.update(0)
    for j in range(0, n_blocks, batch_size):

        this_x = features[j:j + batch_size]

        new_prediction = model.predict(this_x, batch_size=1, verbose=0)

        prev_mean = np.zeros_like(new_prediction['probabilities'])
        curr_mean = new_prediction['probabilities']

        M = np.zeros_like(new_prediction['probabilities'])
        for n in range(1, n_samples):

            new_prediction = model.predict(this_x)
            prev_mean = curr_mean
            curr_mean = prev_mean + (new_prediction['probabilities'] -
                                     prev_mean) / float(n + 1)
            M = M + np.multiply(prev_mean - new_prediction['probabilities'],
                                curr_mean - new_prediction['probabilities'])

        means[j:j + batch_size] = np.argmax(curr_mean, axis=-1)  # max mean
        variances[j:j + batch_size] = np.sum(M / n_samples, axis=-1)
        entropies[j:j + batch_size] = -np.sum(np.multiply(
            np.log(curr_mean + 0.001), curr_mean),
                                              axis=-1)  # entropy
        progbar.add(1)

    total_means = from_blocks_numpy(means, output_shape=inputs.shape)
    total_variance = from_blocks_numpy(variances, output_shape=inputs.shape)
    total_entropy = from_blocks_numpy(entropies, output_shape=inputs.shape)

    mean_var_voxels = np.mean(total_variance)
    std_var_voxels = np.std(total_variance)

    include_variance = ((n_samples > 1) and (return_variance))
    if include_variance:
        if return_entropy:
            return total_means, total_variance, total_entropy
        else:
            return total_means, total_variance
    else:
        if return_entropy:
            return total_means, total_entropy
        else:
            return total_means,