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
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
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
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())