Пример #1
0
    def apply_transformation(self,
                             img_obj: ImageClass,
                             roi_list,
                             settings,
                             compute_features=False,
                             extract_images=False,
                             file_path=None):
        """Run feature extraction for transformed data"""

        feat_list = []

        # Transform sigma to voxel distance
        if self.img_average:
            # Generate average image
            img_trans_obj = self.average_image(img_obj=img_obj)

            # Export image
            if extract_images:
                img_trans_obj.export(file_path=file_path)

            # Compute features
            if compute_features:
                feat_list += [
                    calculate_features(
                        img_obj=img_trans_obj,
                        roi_list=roi_list,
                        settings=settings,
                        append_str=img_trans_obj.spat_transform + "_")
                ]

            # Clean up
            del img_trans_obj

        else:
            for curr_sigma in self.sigma:
                # Generate transformed image
                img_trans_obj = self.transform(img_obj=img_obj,
                                               sigma=curr_sigma)

                # Export image
                if extract_images:
                    img_trans_obj.export(file_path=file_path)

                # Compute features
                if compute_features:
                    feat_list += [
                        calculate_features(
                            img_obj=img_trans_obj,
                            roi_list=roi_list,
                            settings=settings,
                            append_str=img_trans_obj.spat_transform + "_")
                    ]

                # Clean up
                del img_trans_obj

        return feat_list
Пример #2
0
    def apply_transformation(self,
                             img_obj: ImageClass,
                             roi_list,
                             settings,
                             compute_features=False,
                             extract_images=False,
                             file_path=None):
        """Run feature extraction for transformed data"""

        feat_list = []

        # Generate transformed image
        img_trans_obj = self.transform(img_obj=img_obj)

        # Export image
        if extract_images:
            img_trans_obj.export(file_path=file_path)

        # Compute features
        if compute_features:
            feat_list += [
                calculate_features(img_obj=img_trans_obj,
                                   roi_list=roi_list,
                                   settings=settings,
                                   append_str=img_trans_obj.spat_transform +
                                   "_")
            ]

        # Clean up
        del img_trans_obj

        return feat_list
Пример #3
0
    def apply_transformation(self, img_obj: ImageClass, roi_list, settings, compute_features=False, extract_images=False, file_path=None):

        feat_list = []

        # Iterate over wavelet filters
        for current_filter_set in self.filter_list:

            # Copy roi list
            roi_trans_list = [roi_obj.copy() for roi_obj in roi_list]

            # Add spatially transformed image object. In case of rotational invariance, this is averaged.
            img_trans_obj = self.transform(img_obj=img_obj, filter_set=current_filter_set, mode=settings.img_transform.boundary_condition)

            # Export image
            if extract_images:
                img_trans_obj.export(file_path=file_path)

            # Compute features
            if compute_features:
                feat_list += [calculate_features(img_obj=img_trans_obj, roi_list=roi_trans_list, settings=settings,
                                                 append_str=img_trans_obj.spat_transform + "_")]
            # Clean up
            del img_trans_obj

        return feat_list
Пример #4
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())