Example #1
0
def preprocess_brain_image(image,
                           truncate_intensity=(0.01, 0.99),
                           brain_extraction_modality=None,
                           template_transform_type=None,
                           template="biobank",
                           do_bias_correction=True,
                           return_bias_field=False,
                           do_denoising=True,
                           intensity_matching_type=None,
                           reference_image=None,
                           intensity_normalization_type=None,
                           antsxnet_cache_directory=None,
                           verbose=True):

    """
    Basic preprocessing pipeline for T1-weighted brain MRI

    Standard preprocessing steps that have been previously described
    in various papers including the cortical thickness pipeline:

         https://www.ncbi.nlm.nih.gov/pubmed/24879923

    Arguments
    ---------
    image : ANTsImage
        input image

    truncate_intensity : 2-length tuple
        Defines the quantile threshold for truncating the image intensity

    brain_extraction_modality : string or None
        Perform brain extraction using antspynet tools.  One of "t1", "t1v0",
        "t1nobrainer", "t1combined", "flair", "t2", "bold", "fa", "t1infant",
        "t2infant", or None.

    template_transform_type : string
        See details in help for ants.registration.  Typically "Rigid" or
        "Affine".

    template : ANTs image (not skull-stripped)
        Alternatively, one can specify the default "biobank" or "croppedMni152"
        to download and use premade templates.

    do_bias_correction : boolean
        Perform N4 bias field correction.

    return_bias_field : boolean
        If True, return bias field as an additional output *without* bias
        correcting the preprocessed image.

    do_denoising : boolean
        Perform non-local means denoising.

    intensity_matching_type : string
        Either "regression" or "histogram". Only is performed if reference_image
        is not None.

    reference_image : ANTs image
        Reference image for intensity matching.

    intensity_normalization_type : string
        Either rescale the intensities to [0,1] (i.e., "01") or zero-mean, unit variance
        (i.e., "0mean").  If None normalization is not performed.

    antsxnet_cache_directory : string
        Destination directory for storing the downloaded template and model weights.
        Since these can be resused, if is None, these data will be downloaded to a
        ~/.keras/ANTsXNet/.

    verbose : boolean
        Print progress to the screen.

    Returns
    -------
    Dictionary with preprocessing information ANTs image (i.e., source_image) matched to the
    (reference_image).

    Example
    -------
    >>> import ants
    >>> image = ants.image_read(ants.get_ants_data('r16'))
    >>> preprocessed_image = preprocess_brain_image(image, do_brain_extraction=False)
    """

    from ..utilities import brain_extraction
    from ..utilities import regression_match_image
    from ..utilities import get_antsxnet_data

    preprocessed_image = ants.image_clone(image)

    if antsxnet_cache_directory == None:
        antsxnet_cache_directory = "ANTsXNet"

    # Truncate intensity
    if truncate_intensity is not None:
        quantiles = (image.quantile(truncate_intensity[0]), image.quantile(truncate_intensity[1]))
        if verbose == True:
            print("Preprocessing:  truncate intensities ( low =", quantiles[0], ", high =", quantiles[1], ").")

        preprocessed_image[image < quantiles[0]] = quantiles[0]
        preprocessed_image[image > quantiles[1]] = quantiles[1]

    # Brain extraction
    mask = None
    if brain_extraction_modality is not None:
        if verbose == True:
            print("Preprocessing:  brain extraction.")

        probability_mask = brain_extraction(preprocessed_image, modality=brain_extraction_modality,
            antsxnet_cache_directory=antsxnet_cache_directory, verbose=verbose)
        mask = ants.threshold_image(probability_mask, 0.5, 1, 1, 0)
        mask = ants.morphology(mask,"close",6).iMath_fill_holes()

    # Template normalization
    transforms = None
    if template_transform_type is not None:
        template_image = None
        if isinstance(template, str):
            template_file_name_path = get_antsxnet_data(template, antsxnet_cache_directory=antsxnet_cache_directory)
            template_image = ants.image_read(template_file_name_path)
        else:
            template_image = template

        if mask is None:
            registration = ants.registration(fixed=template_image, moving=preprocessed_image,
                type_of_transform=template_transform_type, verbose=verbose)
            preprocessed_image = registration['warpedmovout']
            transforms = dict(fwdtransforms=registration['fwdtransforms'],
                              invtransforms=registration['invtransforms'])
        else:
            template_probability_mask = brain_extraction(template_image, modality=brain_extraction_modality, 
                antsxnet_cache_directory=antsxnet_cache_directory, verbose=verbose)
            template_mask = ants.threshold_image(template_probability_mask, 0.5, 1, 1, 0)
            template_brain_image = template_mask * template_image

            preprocessed_brain_image = preprocessed_image * mask

            registration = ants.registration(fixed=template_brain_image, moving=preprocessed_brain_image,
                type_of_transform=template_transform_type, verbose=verbose)
            transforms = dict(fwdtransforms=registration['fwdtransforms'],
                              invtransforms=registration['invtransforms'])

            preprocessed_image = ants.apply_transforms(fixed = template_image, moving = preprocessed_image,
                transformlist=registration['fwdtransforms'], interpolator="linear", verbose=verbose)
            mask = ants.apply_transforms(fixed = template_image, moving = mask,
                transformlist=registration['fwdtransforms'], interpolator="genericLabel", verbose=verbose)

    # Do bias correction
    bias_field = None
    if do_bias_correction == True:
        if verbose == True:
            print("Preprocessing:  brain correction.")
        n4_output = None
        if mask is None:
            n4_output = ants.n4_bias_field_correction(preprocessed_image, shrink_factor=4, return_bias_field=return_bias_field, verbose=verbose)
        else:
            n4_output = ants.n4_bias_field_correction(preprocessed_image, mask, shrink_factor=4, return_bias_field=return_bias_field, verbose=verbose)
        if return_bias_field == True:
            bias_field = n4_output
        else:
            preprocessed_image = n4_output

    # Denoising
    if do_denoising == True:
        if verbose == True:
            print("Preprocessing:  denoising.")

        if mask is None:
            preprocessed_image = ants.denoise_image(preprocessed_image, shrink_factor=1)
        else:
            preprocessed_image = ants.denoise_image(preprocessed_image, mask, shrink_factor=1)

    # Image matching
    if reference_image is not None and intensity_matching_type is not None:
        if verbose == True:
            print("Preprocessing:  intensity matching.")

        if intensity_matching_type == "regression":
            preprocessed_image = regression_match_image(preprocessed_image, reference_image)
        elif intensity_matching_type == "histogram":
            preprocessed_image = ants.histogram_match_image(preprocessed_image, reference_image)
        else:
            raise ValueError("Unrecognized intensity_matching_type.")

    # Intensity normalization
    if intensity_normalization_type is not None:
        if verbose == True:
            print("Preprocessing:  intensity normalization.")

        if intensity_normalization_type == "01":
            preprocessed_image = (preprocessed_image - preprocessed_image.min())/(preprocessed_image.max() - preprocessed_image.min())
        elif intensity_normalization_type == "0mean":
            preprocessed_image = (preprocessed_image - preprocessed_image.mean())/preprocessed_image.std()
        else:
            raise ValueError("Unrecognized intensity_normalization_type.")

    return_dict = {'preprocessed_image' : preprocessed_image}
    if mask is not None:
        return_dict['brain_mask'] = mask
    if bias_field is not None:
        return_dict['bias_field'] = bias_field
    if transforms is not None:
        return_dict['template_transforms'] = transforms

    return(return_dict)
Example #2
0
    def test_morphology(self):
        fi = ants.image_read(ants.get_ants_data("r16"))
        mask = ants.get_mask(fi)
        dilated_ball = ants.morphology(mask,
                                       operation="dilate",
                                       radius=3,
                                       mtype="binary",
                                       shape="ball")
        eroded_box = ants.morphology(mask,
                                     operation="erode",
                                     radius=3,
                                     mtype="binary",
                                     shape="box")
        opened_annulus = ants.morphology(
            mask,
            operation="open",
            radius=5,
            mtype="binary",
            shape="annulus",
            thickness=2,
        )

        ## --- ##
        mtype = "binary"
        mask = mask.clone()
        for operation in {"dilate", "erode", "open", "close"}:
            for shape in {"ball", "box", "cross", "annulus", "polygon"}:
                morph = ants.morphology(mask,
                                        operation=operation,
                                        radius=3,
                                        mtype=mtype,
                                        shape=shape)
                self.assertTrue(
                    isinstance(morph, ants.core.ants_image.ANTsImage))
        # invalid operation
        with self.assertRaises(Exception):
            ants.morphology(mask,
                            operation="invalid-operation",
                            radius=3,
                            mtype=mtype,
                            shape=shape)

        mtype = "grayscale"
        img = ants.image_read(ants.get_ants_data("r16"))
        for operation in {"dilate", "erode", "open", "close"}:
            morph = ants.morphology(img,
                                    operation=operation,
                                    radius=3,
                                    mtype=mtype)
            self.assertTrue(isinstance(morph, ants.core.ants_image.ANTsImage))
        # invalid operation
        with self.assertRaises(Exception):
            ants.morphology(img,
                            operation="invalid-operation",
                            radius=3,
                            mtype="grayscale")

        # invalid morphology type
        with self.assertRaises(Exception):
            ants.morphology(img,
                            operation="dilate",
                            radius=3,
                            mtype="invalid-morphology")

        # invalid shape
        with self.assertRaises(Exception):
            ants.morphology(
                mask,
                operation="dilate",
                radius=3,
                mtype="binary",
                shape="invalid-shape",
            )
Example #3
0
    def test_morphology(self):
        fi = ants.image_read(ants.get_ants_data('r16'))
        mask = fi > fi.mean()
        dilated_ball = ants.morphology(mask,
                                       operation='dilate',
                                       radius=3,
                                       mtype='binary',
                                       shape='ball')
        eroded_box = ants.morphology(mask,
                                     operation='erode',
                                     radius=3,
                                     mtype='binary',
                                     shape='box')
        opened_annulus = ants.morphology(mask,
                                         operation='open',
                                         radius=5,
                                         mtype='binary',
                                         shape='annulus',
                                         thickness=2)

        ## --- ##
        mtype = 'binary'
        mask = mask.clone()
        for operation in {'dilate', 'erode', 'open', 'close'}:
            for shape in {'ball', 'box', 'cross', 'annulus', 'polygon'}:
                morph = ants.morphology(mask,
                                        operation=operation,
                                        radius=3,
                                        mtype=mtype,
                                        shape=shape)
                self.assertTrue(
                    isinstance(morph, ants.core.ants_image.ANTsImage))
        # invalid operation
        with self.assertRaises(Exception):
            ants.morphology(mask,
                            operation='invalid-operation',
                            radius=3,
                            mtype=mtype,
                            shape=shape)

        mtype = 'grayscale'
        img = ants.image_read(ants.get_ants_data('r16'))
        for operation in {'dilate', 'erode', 'open', 'close'}:
            morph = ants.morphology(img,
                                    operation=operation,
                                    radius=3,
                                    mtype=mtype)
            self.assertTrue(isinstance(morph, ants.core.ants_image.ANTsImage))
        # invalid operation
        with self.assertRaises(Exception):
            ants.morphology(img,
                            operation='invalid-operation',
                            radius=3,
                            mtype='grayscale')

        #invalid morphology type
        with self.assertRaises(Exception):
            ants.morphology(img,
                            operation='dilate',
                            radius=3,
                            mtype='invalid-morphology')

        # invalid shape
        with self.assertRaises(Exception):
            ants.morphology(mask,
                            operation='dilate',
                            radius=3,
                            mtype='binary',
                            shape='invalid-shape')
Example #4
0
def brain_extraction(image,
                     modality,
                     antsxnet_cache_directory=None,
                     verbose=False):
    """
    Perform brain extraction using U-net and ANTs-based training data.  "NoBrainer"
    is also possible where brain extraction uses U-net and FreeSurfer training data
    ported from the

    https://github.com/neuronets/nobrainer-models

    Arguments
    ---------
    image : ANTsImage
        input image (or list of images for multi-modal scenarios).

    modality : string
        Modality image type.  Options include:
            * "t1": T1-weighted MRI---ANTs-trained.  Previous versions are specified as "t1.v0", "t1.v1".
            * "t1nobrainer": T1-weighted MRI---FreeSurfer-trained: h/t Satra Ghosh and Jakub Kaczmarzyk.
            * "t1combined": Brian's combination of "t1" and "t1nobrainer".  One can also specify
                            "t1combined[X]" where X is the morphological radius.  X = 12 by default.
            * "flair": FLAIR MRI.   Previous versions are specified as "flair.v0".
            * "t2": T2 MRI.  Previous versions are specified as "t2.v0".
            * "t2star": T2Star MRI.
            * "bold": 3-D mean BOLD MRI.  Previous versions are specified as "bold.v0".
            * "fa": fractional anisotropy.  Previous versions are specified as "fa.v0".
            * "t1t2infant": Combined T1-w/T2-w infant MRI h/t Martin Styner.
            * "t1infant": T1-w infant MRI h/t Martin Styner.
            * "t2infant": T2-w infant MRI h/t Martin Styner.

    antsxnet_cache_directory : string
        Destination directory for storing the downloaded template and model weights.
        Since these can be resused, if is None, these data will be downloaded to a
        ~/.keras/ANTsXNet/.

    verbose : boolean
        Print progress to the screen.

    Returns
    -------
    ANTs probability brain mask image.

    Example
    -------
    >>> probability_brain_mask = brain_extraction(brain_image, modality="t1")
    """

    from ..architectures import create_unet_model_3d
    from ..utilities import get_pretrained_network
    from ..utilities import get_antsxnet_data
    from ..architectures import create_nobrainer_unet_model_3d
    from ..utilities import decode_unet

    classes = ("background", "brain")
    number_of_classification_labels = len(classes)

    channel_size = 1
    if isinstance(image, list):
        channel_size = len(image)

    if antsxnet_cache_directory == None:
        antsxnet_cache_directory = "ANTsXNet"

    input_images = list()
    if channel_size == 1:
        input_images.append(image)
    else:
        input_images = image

    if input_images[0].dimension != 3:
        raise ValueError("Image dimension must be 3.")

    if "t1combined" in modality:
        # Need to change with voxel resolution
        morphological_radius = 12
        if '[' in modality and ']' in modality:
            morphological_radius = int(modality.split("[")[1].split("]")[0])

        brain_extraction_t1 = brain_extraction(
            image,
            modality="t1",
            antsxnet_cache_directory=antsxnet_cache_directory,
            verbose=verbose)
        brain_mask = ants.iMath_get_largest_component(
            ants.threshold_image(brain_extraction_t1, 0.5, 10000))
        brain_mask = ants.morphology(brain_mask, "close",
                                     morphological_radius).iMath_fill_holes()

        brain_extraction_t1nobrainer = brain_extraction(
            image * ants.iMath_MD(brain_mask, radius=morphological_radius),
            modality="t1nobrainer",
            antsxnet_cache_directory=antsxnet_cache_directory,
            verbose=verbose)
        brain_extraction_combined = ants.iMath_fill_holes(
            ants.iMath_get_largest_component(brain_extraction_t1nobrainer *
                                             brain_mask))

        brain_extraction_combined = brain_extraction_combined + ants.iMath_ME(
            brain_mask, morphological_radius) + brain_mask

        return (brain_extraction_combined)

    if modality != "t1nobrainer":

        #####################
        #
        # ANTs-based
        #
        #####################

        weights_file_name_prefix = None
        is_standard_network = False

        if modality == "t1.v0":
            weights_file_name_prefix = "brainExtraction"
        elif modality == "t1.v1":
            weights_file_name_prefix = "brainExtractionT1v1"
            is_standard_network = True
        elif modality == "t1":
            weights_file_name_prefix = "brainExtractionRobustT1"
            is_standard_network = True
        elif modality == "t2.v0":
            weights_file_name_prefix = "brainExtractionT2"
        elif modality == "t2":
            weights_file_name_prefix = "brainExtractionRobustT2"
            is_standard_network = True
        elif modality == "t2star":
            weights_file_name_prefix = "brainExtractionRobustT2Star"
            is_standard_network = True
        elif modality == "flair.v0":
            weights_file_name_prefix = "brainExtractionFLAIR"
        elif modality == "flair":
            weights_file_name_prefix = "brainExtractionRobustFLAIR"
            is_standard_network = True
        elif modality == "bold.v0":
            weights_file_name_prefix = "brainExtractionBOLD"
        elif modality == "bold":
            weights_file_name_prefix = "brainExtractionRobustBOLD"
            is_standard_network = True
        elif modality == "fa.v0":
            weights_file_name_prefix = "brainExtractionFA"
        elif modality == "fa":
            weights_file_name_prefix = "brainExtractionRobustFA"
            is_standard_network = True
        elif modality == "t1t2infant":
            weights_file_name_prefix = "brainExtractionInfantT1T2"
        elif modality == "t1infant":
            weights_file_name_prefix = "brainExtractionInfantT1"
        elif modality == "t2infant":
            weights_file_name_prefix = "brainExtractionInfantT2"
        else:
            raise ValueError("Unknown modality type.")

        if verbose == True:
            print("Brain extraction:  retrieving model weights.")

        weights_file_name = get_pretrained_network(
            weights_file_name_prefix,
            antsxnet_cache_directory=antsxnet_cache_directory)

        if verbose == True:
            print("Brain extraction:  retrieving template.")

        reorient_template_file_name_path = get_antsxnet_data(
            "S_template3", antsxnet_cache_directory=antsxnet_cache_directory)
        reorient_template = ants.image_read(reorient_template_file_name_path)
        if is_standard_network and modality != "t1.v1":
            ants.set_spacing(reorient_template, (1.5, 1.5, 1.5))
        resampled_image_size = reorient_template.shape

        number_of_filters = (8, 16, 32, 64)
        mode = "classification"
        if is_standard_network:
            number_of_filters = (16, 32, 64, 128)
            number_of_classification_labels = 1
            mode = "sigmoid"

        unet_model = create_unet_model_3d(
            (*resampled_image_size, channel_size),
            number_of_outputs=number_of_classification_labels,
            mode=mode,
            number_of_filters=number_of_filters,
            dropout_rate=0.0,
            convolution_kernel_size=3,
            deconvolution_kernel_size=2,
            weight_decay=1e-5)

        unet_model.load_weights(weights_file_name)

        if verbose == True:
            print("Brain extraction:  normalizing image to the template.")

        center_of_mass_template = ants.get_center_of_mass(reorient_template)
        center_of_mass_image = ants.get_center_of_mass(input_images[0])
        translation = np.asarray(center_of_mass_image) - np.asarray(
            center_of_mass_template)
        xfrm = ants.create_ants_transform(
            transform_type="Euler3DTransform",
            center=np.asarray(center_of_mass_template),
            translation=translation)

        batchX = np.zeros((1, *resampled_image_size, channel_size))

        for i in range(len(input_images)):
            warped_image = ants.apply_ants_transform_to_image(
                xfrm, input_images[i], reorient_template)
            if is_standard_network and modality != "t1.v1":
                batchX[0, :, :, :, i] = (ants.iMath(warped_image,
                                                    "Normalize")).numpy()
            else:
                warped_array = warped_image.numpy()
                batchX[0, :, :, :,
                       i] = (warped_array -
                             warped_array.mean()) / warped_array.std()

        if verbose == True:
            print("Brain extraction:  prediction and decoding.")

        predicted_data = unet_model.predict(batchX, verbose=verbose)
        probability_images_array = decode_unet(predicted_data,
                                               reorient_template)

        if verbose == True:
            print(
                "Brain extraction:  renormalize probability mask to native space."
            )

        xfrm_inv = xfrm.invert()
        probability_image = xfrm_inv.apply_to_image(
            probability_images_array[0][number_of_classification_labels - 1],
            input_images[0])

        return (probability_image)

    else:

        #####################
        #
        # NoBrainer
        #
        #####################

        if verbose == True:
            print("NoBrainer:  generating network.")

        model = create_nobrainer_unet_model_3d((None, None, None, 1))

        weights_file_name = get_pretrained_network(
            "brainExtractionNoBrainer",
            antsxnet_cache_directory=antsxnet_cache_directory)
        model.load_weights(weights_file_name)

        if verbose == True:
            print(
                "NoBrainer:  preprocessing (intensity truncation and resampling)."
            )

        image_array = image.numpy()
        image_robust_range = np.quantile(
            image_array[np.where(image_array != 0)], (0.02, 0.98))
        threshold_value = 0.10 * (image_robust_range[1] - image_robust_range[0]
                                  ) + image_robust_range[0]

        thresholded_mask = ants.threshold_image(image, -10000, threshold_value,
                                                0, 1)
        thresholded_image = image * thresholded_mask

        image_resampled = ants.resample_image(thresholded_image,
                                              (256, 256, 256),
                                              use_voxels=True)
        image_array = np.expand_dims(image_resampled.numpy(), axis=0)
        image_array = np.expand_dims(image_array, axis=-1)

        if verbose == True:
            print("NoBrainer:  predicting mask.")

        brain_mask_array = np.squeeze(
            model.predict(image_array, verbose=verbose))
        brain_mask_resampled = ants.copy_image_info(
            image_resampled, ants.from_numpy(brain_mask_array))
        brain_mask_image = ants.resample_image(brain_mask_resampled,
                                               image.shape,
                                               use_voxels=True,
                                               interp_type=1)

        spacing = ants.get_spacing(image)
        spacing_product = spacing[0] * spacing[1] * spacing[2]
        minimum_brain_volume = round(649933.7 / spacing_product)
        brain_mask_labeled = ants.label_clusters(brain_mask_image,
                                                 minimum_brain_volume)

        return (brain_mask_labeled)