示例#1
0
    def process(self):
        """ Main pipeline """

        import os
        import logging
        from mirp.imageRead import load_image
        from mirp.imageProcess import crop_image, estimate_image_noise, interpolate_image,\
            interpolate_roi, divide_tumour_regions, resegmentise, calculate_features, transform_images, \
            create_tissue_mask, bias_field_correction, normalise_image
        from mirp.imagePerturbations import rotate_image, adapt_roi_size, randomise_roi_contours
        import copy

        # Configure logger
        logging.basicConfig(
            format=
            "%(levelname)s\t: %(processName)s \t %(asctime)s \t %(message)s",
            level=logging.INFO,
            stream=sys.stdout)

        # Initialise empty feature list
        feat_list = []

        # Notify
        logging.info(self._message_computation_initialisation())

        # Get iterables from current settings which lead to different image adaptations
        iter_set, n_outer_iter, n_inner_iter = self.get_iterable_parameters(
            settings=self.settings)

        # Load image and roi
        if self.keep_images_in_memory:
            base_img_obj, base_roi_list = load_image(
                image_folder=self.image_folder,
                modality=self.modality,
                roi_folder=self.roi_folder,
                registration_image_folder=self.roi_reg_img_folder,
                image_name=self.image_file_name_pattern,
                roi_names=self.roi_names,
                registration_image_name=self.
                registration_image_file_name_pattern)

            self.set_image_name(img_obj=base_img_obj)
        else:
            base_img_obj = base_roi_list = None

        # Iterate over iterable settings
        for ii in np.arange(0, n_outer_iter):

            # Log current iteration
            if n_outer_iter * n_inner_iter > 1:
                if n_inner_iter > 1:
                    logging.info(
                        f"Starting computations for {ii * n_inner_iter + 1} to {(ii+1) * n_inner_iter} of {n_outer_iter * n_inner_iter} perturbations."
                    )
                else:
                    logging.info(
                        f"Starting computations for {ii + 1} of {n_outer_iter} perturbations."
                    )
            else:
                logging.info("Starting computations.")

            ########################################################################################################
            # Load and pre-process image and roi
            ########################################################################################################

            # Use pre-loaded base image and roi_list (more memory used, but may be faster if loading over a network), or load from disk.
            if self.keep_images_in_memory:
                img_obj = base_img_obj.copy()
                roi_list = copy.deepcopy(base_roi_list)
            else:
                # Read image and ROI segmentations
                img_obj, roi_list = load_image(
                    image_folder=self.image_folder,
                    modality=self.modality,
                    roi_folder=self.roi_folder,
                    registration_image_folder=self.roi_reg_img_folder,
                    image_name=self.image_file_name_pattern,
                    roi_names=self.roi_names,
                    registration_image_name=self.
                    registration_image_file_name_pattern)
                self.set_image_name(img_obj=img_obj)

            # Crop slice stack
            if self.settings.vol_adapt.crop:
                img_obj, roi_list = crop_image(
                    img_obj=img_obj,
                    roi_list=roi_list,
                    boundary=self.settings.vol_adapt.crop_distance)

            # Extract diagnostic features from initial image and rois
            self.extract_diagnostic_features(img_obj=img_obj,
                                             roi_list=roi_list,
                                             append_str="init")

            ########################################################################################################
            # Update settings and initialise
            ########################################################################################################

            # Copy settings for current iteration run - this allows local changes to curr_setting
            curr_setting = copy.deepcopy(self.settings)

            # Update settings object with iterable settings
            curr_setting.vol_adapt.rot_angles = [iter_set.rot_angle[ii]]
            curr_setting.img_interpolate.new_spacing = [
                iter_set.vox_spacing[ii]
            ]
            curr_setting.vol_adapt.translate_x = [iter_set.translate_x[ii]]
            curr_setting.vol_adapt.translate_y = [iter_set.translate_y[ii]]
            curr_setting.vol_adapt.translate_z = [iter_set.translate_z[ii]]

            ########################################################################################################
            # Bias field correction and normalisation
            ########################################################################################################

            # Create a tissue mask
            if curr_setting.post_process.bias_field_correction or not curr_setting.post_process.intensity_normalisation == "none":
                tissue_mask = create_tissue_mask(img_obj=img_obj,
                                                 settings=curr_setting)

                if curr_setting.post_process.bias_field_correction:
                    # Perform bias field correction
                    img_obj = bias_field_correction(img_obj=img_obj,
                                                    settings=curr_setting,
                                                    mask=tissue_mask)

                # Normalise image
                img_obj = normalise_image(
                    img_obj=img_obj,
                    norm_method=curr_setting.post_process.
                    intensity_normalisation,
                    intensity_range=curr_setting.post_process.
                    intensity_normalisation_range,
                    saturation_range=curr_setting.post_process.
                    intensity_normalisation_saturation,
                    mask=tissue_mask)

            ########################################################################################################
            # Determine image noise levels (optional)
            ########################################################################################################

            # Initialise noise level with place holder value
            est_noise_level = -1.0

            # Determine image noise levels
            if curr_setting.vol_adapt.add_noise and curr_setting.vol_adapt.noise_level is None and est_noise_level == -1.0:
                est_noise_level = estimate_image_noise(img_obj=img_obj,
                                                       settings=curr_setting,
                                                       method="chang")
            elif curr_setting.vol_adapt.add_noise:
                est_noise_level = curr_setting.vol_adapt.noise_level

            ########################################################################################################
            # Base image-based operations - basic operations on base image (rotation, cropping, noise addition)
            # Note interpolation and translation are performed simultaneously, and interpolation is only done after
            # application of spatial filters
            ########################################################################################################

            # Rotate object
            img_obj, roi_list = rotate_image(img_obj=img_obj,
                                             roi_list=roi_list,
                                             settings=curr_setting)

            # Crop image to a box extending at most 15 cm around the combined ROI
            if curr_setting.vol_adapt.crop:
                img_obj, roi_list = crop_image(img_obj=img_obj,
                                               roi_list=roi_list,
                                               boundary=150.0,
                                               z_only=False)

            # Add random noise to an image
            if curr_setting.vol_adapt.add_noise:
                img_obj.add_noise(noise_level=est_noise_level, noise_iter=ii)

            ########################################################################################################
            # Interpolation of base image
            ########################################################################################################

            # Translate and interpolate image to isometric voxels
            img_obj = interpolate_image(img_obj=img_obj, settings=curr_setting)
            roi_list = interpolate_roi(roi_list=roi_list,
                                       img_obj=img_obj,
                                       settings=curr_setting)
            self.extract_diagnostic_features(img_obj=img_obj,
                                             roi_list=roi_list,
                                             append_str="interp")

            ########################################################################################################
            # ROI-based operations
            # These operations only affect the regions of interest
            ########################################################################################################

            # Adapt roi sizes by dilation and erosion
            roi_list = adapt_roi_size(roi_list=roi_list, settings=curr_setting)

            # Update roi using SLIC
            roi_list = randomise_roi_contours(roi_list=roi_list,
                                              img_obj=img_obj,
                                              settings=curr_setting)

            # Extract boundaries and tumour bulk
            roi_list = divide_tumour_regions(roi_list=roi_list,
                                             settings=curr_setting)

            # Resegmentise ROI based on intensities in the base images
            roi_list = resegmentise(img_obj=img_obj,
                                    roi_list=roi_list,
                                    settings=curr_setting)
            self.extract_diagnostic_features(img_obj=img_obj,
                                             roi_list=roi_list,
                                             append_str="reseg")

            # Compose ROI of heterogeneous supervoxels
            # roi_list = imageProcess.selectHeterogeneousSuperVoxels(img_obj=img_obj, roi_list=roi_list, settings=curr_setting,
            #                                                        file_str=os.path.join(self.write_path, self.subject + "_" + self.modality + "_" + self.data_str + "_" + self.date))

            ########################################################################################################
            # Base image computations and exports
            ########################################################################################################

            if self.extract_images:
                img_obj.export(file_path=self.write_path)
                for roi_obj in roi_list:
                    roi_obj.export(img_obj=img_obj, file_path=self.write_path)

            iter_feat_list = []
            if self.compute_features:
                iter_feat_list.append(
                    calculate_features(img_obj=img_obj,
                                       roi_list=roi_list,
                                       settings=curr_setting))

            ########################################################################################################
            # Image transformations
            ########################################################################################################

            if self.settings.img_transform.perform_img_transform:
                # Get image features from transformed images (may be empty if no features are computed)
                iter_feat_list += transform_images(
                    img_obj=img_obj,
                    roi_list=roi_list,
                    settings=curr_setting,
                    compute_features=self.compute_features,
                    extract_images=self.extract_images,
                    file_path=self.write_path)

            ########################################################################################################
            # Collect and combine features for current iteration
            ########################################################################################################

            if self.compute_features:
                feat_list.append(
                    self.collect_features(img_obj=img_obj,
                                          roi_list=roi_list,
                                          feat_list=iter_feat_list,
                                          settings=curr_setting))

            # Clean up
            del img_obj, roi_list

        ########################################################################################################
        # Feature aggregation over settings
        ########################################################################################################

        if self.compute_features:

            # Strip empty entries
            feat_list = [
                list_entry for list_entry in feat_list
                if list_entry is not None
            ]

            # Check if features were extracted
            if len(feat_list) == 0:
                logging.warning(self._message_warning_no_features_extracted())
                return None

            # Concatenate feat list
            df_feat = pd.concat(feat_list, axis=0)

            # Write to file
            file_name = "_".join([
                file_name_comp for file_name_comp in [
                    self.subject, self.modality, self.data_str, self.date,
                    self.settings.general.config_str, "features.csv"
                ] if file_name_comp != ""
            ]).replace(" ", "_")
            # file_name = self.subject + "_" + self.modality + "_" + self.data_str + "_" + self.date + "_" + self.settings.general.config_str + "_features.csv"
            df_feat.to_csv(path_or_buf=os.path.join(self.write_path,
                                                    file_name),
                           sep=";",
                           na_rep="NA",
                           index=False,
                           decimal=".")

            # Write successful completion to console or log
            logging.info(self._message_feature_extraction_finished())
示例#2
0
    def process_deep_learning(self,
                              output_slices=False,
                              crop_size=None,
                              center_crops_per_slice=True,
                              remove_empty_crops=True,
                              intensity_range=None,
                              normalisation="none",
                              as_numpy=False):

        import logging
        from mirp.imageRead import load_image
        from mirp.imageProcess import estimate_image_noise, interpolate_image, interpolate_roi, crop_image_to_size, saturate_image, normalise_image
        from mirp.imagePerturbations import rotate_image, adapt_roi_size, randomise_roi_contours
        from mirp.roiClass import merge_roi_objects
        import copy

        from mirp.imagePlot import plot_image

        # Configure logger
        logging.basicConfig(
            format=
            "%(levelname)s\t: %(processName)s \t %(asctime)s \t %(message)s",
            level=logging.INFO,
            stream=sys.stdout)

        # Notifications
        logging.info(
            "\nInitialising image and mask processing using %s images for %s.",
            self.modality + "_" + self.data_str + "_", self.subject)

        # Process input parameters.
        crop_as_3d = crop_size is None or len(crop_size) == 3

        # Set crop_size.
        if crop_size is None:
            crop_size = [np.nan, np.nan, np.nan]
        elif len(crop_size) == 1:
            crop_size = [np.nan, crop_size, crop_size]
        elif len(crop_size) == 2:
            crop_size = [np.nan, crop_size[0], crop_size[1]]
        elif len(crop_size) == 3:
            crop_size = [crop_size[0], crop_size[1], crop_size[2]]
        else:
            raise ValueError(
                f"The crop_size parameter is longer than 3: {len(crop_size)}")

        # Ignore settings for center_crops_per_slice and remove_empty_crops for 3D crops.
        if crop_as_3d:
            center_crops_per_slice = False
            remove_empty_crops = False

        # Set default intensity ranges.
        if intensity_range is None:
            intensity_range = [np.nan, np.nan]
        elif len(intensity_range) > 2:
            raise ValueError(
                f"The intensity_range parameter is longer than 2: {len(intensity_range)}"
            )

        # Get iterables from current settings which lead to different image adaptations
        iter_set, n_outer_iter, n_inner_iter = self.get_iterable_parameters(
            settings=self.settings)

        # Load image and roi
        if self.keep_images_in_memory:
            base_img_obj, base_roi_list = load_image(
                image_folder=self.image_folder,
                modality=self.modality,
                roi_folder=self.roi_folder,
                registration_image_folder=self.roi_reg_img_folder,
                image_name=self.image_file_name_pattern,
                roi_names=self.roi_names,
                registration_image_name=self.
                registration_image_file_name_pattern)
            self.set_image_name(img_obj=base_img_obj)
        else:
            base_img_obj = base_roi_list = None

        # Create lists for image objects and rois
        processed_image_list = []

        # Iterate over iterable settings
        for ii in np.arange(0, n_outer_iter):

            # Log current iteration
            if n_outer_iter * n_inner_iter > 1:
                if n_inner_iter > 1:
                    logging.info(
                        "\nProcessing image and mask for %s to %s of %s adaptations.\n",
                        str(ii * n_inner_iter + 1), str(
                            (ii + 1) * n_inner_iter),
                        str(n_outer_iter * n_inner_iter))
                else:
                    logging.info(
                        "\nProcessing image and mask for %s of %s adaptations.\n",
                        str(ii + 1), str(n_outer_iter))
            else:
                logging.info("\nStarting image and mask processing.\n")

            ########################################################################################################
            # Load and pre-process image and roi
            ########################################################################################################

            # Use pre-loaded base image and roi_list (more memory used, but may be faster if loading over a network), or load from disk.
            if self.keep_images_in_memory:
                img_obj = base_img_obj.copy()
                roi_list = copy.deepcopy(base_roi_list)
            else:
                # Read image and ROI segmentations
                img_obj, roi_list = load_image(
                    image_folder=self.image_folder,
                    modality=self.modality,
                    roi_folder=self.roi_folder,
                    registration_image_folder=self.roi_reg_img_folder,
                    image_name=self.image_file_name_pattern,
                    roi_names=self.roi_names,
                    registration_image_name=self.
                    registration_image_file_name_pattern)
                self.set_image_name(img_obj=img_obj)

            # Remove metadata
            img_obj.drop_metadata()
            for roi_obj in roi_list:
                roi_obj.drop_metadata()

            ########################################################################################################
            # Update settings and initialise
            ########################################################################################################

            # Copy settings for current iteration run - this allows local changes to curr_setting
            curr_setting = copy.deepcopy(self.settings)

            # Update settings object with iterable settings
            curr_setting.vol_adapt.rot_angles = [iter_set.rot_angle[ii]]
            curr_setting.img_interpolate.new_spacing = [
                iter_set.vox_spacing[ii]
            ]
            curr_setting.vol_adapt.translate_x = [iter_set.translate_x[ii]]
            curr_setting.vol_adapt.translate_y = [iter_set.translate_y[ii]]
            curr_setting.vol_adapt.translate_z = [iter_set.translate_z[ii]]

            ########################################################################################################
            # Determine image noise levels (optional)
            ########################################################################################################

            # Initialise noise level with place holder value
            est_noise_level = -1.0

            # Determine image noise levels
            if curr_setting.vol_adapt.add_noise and curr_setting.vol_adapt.noise_level is None and est_noise_level == -1.0:
                est_noise_level = estimate_image_noise(img_obj=img_obj,
                                                       settings=curr_setting,
                                                       method="chang")
            elif curr_setting.vol_adapt.add_noise:
                est_noise_level = curr_setting.vol_adapt.noise_level

            ########################################################################################################
            # Base image-based operations - basic operations on base image (rotation, cropping, noise addition)
            # Note interpolation and translation are performed simultaneously, and interpolation is only done after
            # application of spatial filters
            ########################################################################################################

            # Rotate object
            img_obj, roi_list = rotate_image(img_obj=img_obj,
                                             roi_list=roi_list,
                                             settings=curr_setting)

            # Add random noise to an image
            if curr_setting.vol_adapt.add_noise:
                img_obj.add_noise(noise_level=est_noise_level, noise_iter=ii)

            ########################################################################################################
            # Interpolation of base image
            ########################################################################################################

            # Translate and interpolate image to isometric voxels
            img_obj = interpolate_image(img_obj=img_obj, settings=curr_setting)
            roi_list = interpolate_roi(roi_list=roi_list,
                                       img_obj=img_obj,
                                       settings=curr_setting)

            ########################################################################################################
            # ROI-based operations
            # These operations only affect the regions of interest
            ########################################################################################################

            # Adapt roi sizes by dilation and erosion
            roi_list = adapt_roi_size(roi_list=roi_list, settings=curr_setting)

            # Update roi using SLIC
            roi_list = randomise_roi_contours(roi_list=roi_list,
                                              img_obj=img_obj,
                                              settings=curr_setting)

            ########################################################################################################
            # Standardise output
            ########################################################################################################

            # Set intensity range
            img_obj = saturate_image(img_obj=img_obj,
                                     intensity_range=intensity_range,
                                     fill_value=None)

            # Normalise the image to a standard range
            img_obj = normalise_image(img_obj=img_obj,
                                      norm_method=normalisation,
                                      intensity_range=intensity_range)

            ########################################################################################################
            # Collect output
            ########################################################################################################

            # Merge ROIs
            roi_obj = merge_roi_objects(roi_list=roi_list)

            # Crop slices
            if crop_as_3d:
                # Create 3D crop.
                img_obj, roi_obj = crop_image_to_size(img_obj=img_obj,
                                                      crop_size=crop_size,
                                                      roi_obj=roi_obj)

                img_list = [img_obj]
                roi_list = [roi_obj]

            elif not center_crops_per_slice:
                # Create 3D crop, then chop into slices.
                img_obj, roi_obj = crop_image_to_size(img_obj=img_obj,
                                                      crop_size=crop_size,
                                                      roi_obj=roi_obj)

                img_list = img_obj.get_slices()
                roi_list = roi_obj.get_slices()

            else:
                # Create 2D crops that are centered on the ROI.
                img_list = []
                roi_list = []

                for jj in np.arange(img_obj.size[0]):
                    slice_img_obj, slice_roi_obj = crop_image_to_size(
                        img_obj=img_obj.get_slices(slice_number=jj)[0],
                        roi_obj=roi_obj.get_slices(slice_number=jj)[0],
                        crop_size=crop_size)

                    img_list += [slice_img_obj]
                    roi_list += [slice_roi_obj]

            # Iterate over list to remove empty slices.
            if remove_empty_crops and not crop_as_3d:
                slice_empty = [
                    slice_roi_obj.is_empty() for slice_roi_obj in roi_list
                ]

                img_list = [
                    img_list[jj] for jj in range(len(slice_empty))
                    if not slice_empty[jj]
                ]
                roi_list = [
                    roi_list[jj] for jj in range(len(slice_empty))
                    if not slice_empty[jj]
                ]

            # Convert 3D crops to axial slices.
            if crop_as_3d and output_slices:
                img_list = img_list[0].get_slices()
                roi_list = roi_list[0].get_slices()

            # Check consistency
            if len(img_list) == 0:
                warn(
                    "No valid, non-empty image crops were created. A ROI may be missing?"
                )
                return None

            if all([slice_roi_obj.is_empty() for slice_roi_obj in roi_list]):
                warn(
                    "No image crops were created that contain a mask. A ROI may be missing?."
                )
                return None

            # Update the name of the images.
            for slice_img_obj in img_list:
                slice_img_obj.name = img_obj.name

            # Plot images
            if self.plot_images:
                for jj in np.arange(len(img_list)):
                    # Generate a file name that depends on the number of list elements.
                    file_name = "plot" if len(
                        img_list) == 1 else "plot_" + str(jj)

                    # Plot images.
                    plot_image(img_obj=img_list[jj],
                               roi_list=[roi_list[jj]],
                               slice_id="all",
                               file_path=self.write_path,
                               file_name=file_name,
                               g_range=[np.nan, np.nan])

            # Convert to numpy arrays, if required.
            if as_numpy:
                img_list = [
                    np.squeeze(slice_img_obj.get_voxel_grid())
                    for slice_img_obj in img_list
                ]
                roi_list = [
                    np.squeeze(slice_roi_obj.roi.get_voxel_grid())
                    for slice_roi_obj in roi_list
                ]

            # Return processed imaging.
            processed_image_list = []
            for jj in np.arange(len(img_list)):
                processed_image_list += [{
                    "name": img_obj.name,
                    "image": img_list[jj],
                    "mask": roi_list[jj]
                }]

        # Return list of processed images and masks
        return processed_image_list
示例#3
0
    def process_deep_learning(self, output_slices=False):

        import logging
        from mirp.imageRead import load_image
        from mirp.imageProcess import estimate_image_noise, interpolate_image, interpolate_roi, crop_image_to_size, saturate_image, normalise_image
        from mirp.imagePerturbations import rotate_image, adapt_roi_size, randomise_roi_contours
        import copy

        from mirp.imagePlot import plot_image

        # Configure logger
        logging.basicConfig(
            format=
            "%(levelname)s\t: %(processName)s \t %(asctime)s \t %(message)s",
            level=logging.INFO)

        # Notifications
        logging.info(
            "Initialising image and mask processing using %s images for %s.",
            self.modality + "_" + self.data_str + "_", self.subject)

        # Get iterables from current settings which lead to different image adaptations
        iter_set, n_outer_iter, n_inner_iter = self.get_iterable_parameters(
            settings=self.settings)

        # Load image and roi
        if self.keep_images_in_memory:
            base_img_obj, base_roi_list = load_image(
                image_folder=self.image_folder,
                modality=self.modality,
                roi_folder=self.roi_folder,
                registration_image_folder=self.roi_reg_img_folder,
                image_name=self.image_file_name_pattern,
                roi_names=self.roi_names,
                registration_image_name=self.
                registration_image_file_name_pattern)
            self.set_image_name(img_obj=base_img_obj)
        else:
            base_img_obj = base_roi_list = None

        # Create lists for image objects and rois
        processed_image_list = []

        # Iterate over iterable settings
        for ii in np.arange(0, n_outer_iter):

            # Log current iteration
            if n_outer_iter * n_inner_iter > 1:
                if n_inner_iter > 1:
                    logging.info(
                        "Processing image and mask for %s to %s of %s adaptations.",
                        str(ii * n_inner_iter + 1), str(
                            (ii + 1) * n_inner_iter),
                        str(n_outer_iter * n_inner_iter))
                else:
                    logging.info(
                        "Processing image and mask for %s of %s adaptations.",
                        str(ii + 1), str(n_outer_iter))
            else:
                logging.info("Starting image and mask processing.")

            ########################################################################################################
            # Load and pre-process image and roi
            ########################################################################################################

            # Use pre-loaded base image and roi_list (more memory used, but may be faster if loading over a network), or load from disk.
            if self.keep_images_in_memory:
                img_obj = base_img_obj.copy()
                roi_list = copy.deepcopy(base_roi_list)
            else:
                # Read image and ROI segmentations
                img_obj, roi_list = load_image(
                    image_folder=self.image_folder,
                    modality=self.modality,
                    roi_folder=self.roi_folder,
                    registration_image_folder=self.roi_reg_img_folder,
                    image_name=self.image_file_name_pattern,
                    roi_names=self.roi_names,
                    registration_image_name=self.
                    registration_image_file_name_pattern)
                self.set_image_name(img_obj=img_obj)

            ########################################################################################################
            # Update settings and initialise
            ########################################################################################################

            # Copy settings for current iteration run - this allows local changes to curr_setting
            curr_setting = copy.deepcopy(self.settings)

            # Update settings object with iterable settings
            curr_setting.vol_adapt.rot_angles = [iter_set.rot_angle[ii]]
            curr_setting.img_interpolate.new_spacing = [
                iter_set.vox_spacing[ii]
            ]
            curr_setting.vol_adapt.translate_x = [iter_set.translate_x[ii]]
            curr_setting.vol_adapt.translate_y = [iter_set.translate_y[ii]]
            curr_setting.vol_adapt.translate_z = [iter_set.translate_z[ii]]

            ########################################################################################################
            # Determine image noise levels (optional)
            ########################################################################################################

            # Initialise noise level with place holder value
            est_noise_level = -1.0

            # Determine image noise levels
            if curr_setting.vol_adapt.add_noise and curr_setting.vol_adapt.noise_level is None and est_noise_level == -1.0:
                est_noise_level = estimate_image_noise(img_obj=img_obj,
                                                       settings=curr_setting,
                                                       method="chang")
            elif curr_setting.vol_adapt.add_noise:
                est_noise_level = curr_setting.vol_adapt.noise_level

            ########################################################################################################
            # Base image-based operations - basic operations on base image (rotation, cropping, noise addition)
            # Note interpolation and translation are performed simultaneously, and interpolation is only done after
            # application of spatial filters
            ########################################################################################################

            # Rotate object
            img_obj, roi_list = rotate_image(img_obj=img_obj,
                                             roi_list=roi_list,
                                             settings=curr_setting)

            # Add random noise to an image
            if curr_setting.vol_adapt.add_noise:
                img_obj.add_noise(noise_level=est_noise_level, noise_iter=ii)

            ########################################################################################################
            # Interpolation of base image
            ########################################################################################################

            # Translate and interpolate image to isometric voxels
            img_obj = interpolate_image(img_obj=img_obj, settings=curr_setting)
            roi_list = interpolate_roi(roi_list=roi_list,
                                       img_obj=img_obj,
                                       settings=curr_setting)

            ########################################################################################################
            # ROI-based operations
            # These operations only affect the regions of interest
            ########################################################################################################

            # Adapt roi sizes by dilation and erosion
            roi_list = adapt_roi_size(roi_list=roi_list, settings=curr_setting)

            # Update roi using SLIC
            roi_list = randomise_roi_contours(roi_list=roi_list,
                                              img_obj=img_obj,
                                              settings=curr_setting)

            ########################################################################################################
            # Standardise output
            ########################################################################################################

            # Crop image
            img_obj, roi_list = crop_image_to_size(
                img_obj=img_obj,
                crop_size=curr_setting.deep_learning.expected_size,
                roi_list=roi_list)

            # Set intensity range
            img_obj = saturate_image(
                img_obj=img_obj,
                intensity_range=curr_setting.deep_learning.intensity_range,
                fill_value=None)

            # Normalise the image to a standard range
            img_obj = normalise_image(
                img_obj=img_obj,
                norm_method=curr_setting.deep_learning.normalisation,
                intensity_range=curr_setting.deep_learning.intensity_range)

            ########################################################################################################
            # Collect output
            ########################################################################################################

            if self.extract_images:
                img_obj.export(file_path=self.write_path)
                for roi_obj in roi_list:
                    roi_obj.export(img_obj=img_obj, file_path=self.write_path)

            # Store processed imaging
            if output_slices:
                # 2D slices
                slice_img_obj_list = img_obj.get_slices()
                for jj in np.arange(len(slice_img_obj_list)):
                    for roi_obj in roi_list:
                        processed_image_list += [{
                            "image":
                            slice_img_obj_list[jj],
                            "mask":
                            roi_obj.get_slices(slice_number=jj)
                        }]
            else:
                # 3D volumes
                for roi_obj in roi_list:
                    processed_image_list += [{
                        "image": img_obj,
                        "mask": roi_obj
                    }]

            # Plot images
            if self.plot_images:
                plot_image(img_obj=img_obj,
                           roi_list=roi_list,
                           slice_id="all",
                           file_path=self.write_path,
                           file_name="plot",
                           g_range=[np.nan, np.nan])

        # Return list of processed images and masks
        return processed_image_list