def get_dti_features(image, mask, meta_data): i_mask_array = sitkh.GetArrayFromImage(mask) i_mask_array = i_mask_array.astype(np.bool) i_mask_array = i_mask_array.flatten() array_list = list() for i_dti in image: i_dti = sitkh.GetArrayFromImage(i_dti).flatten() i_dti = i_dti[i_mask_array] array_list.append(i_dti) i_image_array = np.vstack(array_list) # Adjust such that everything is positive, needed for log i_image_array = i_image_array + np.min(i_image_array) + 1 b_values = meta_data['b_values'] A = np.vstack([b_values, np.ones(len(b_values))]).T # Do least squares fit and get the slope, which is the ADC # This will give us the ADC in mm^2/s ADC_tumor_voxels = -np.linalg.lstsq(A, np.log(i_image_array))[0][0, :] ADC_mean = np.mean(ADC_tumor_voxels) ADC_std = np.std(ADC_tumor_voxels) ADC_min = np.min(ADC_tumor_voxels) ADC_max = np.max(ADC_tumor_voxels) dti_features = [ADC_mean, ADC_std, ADC_min, ADC_max] dti_labels = ['ADC_mean', 'ADC_std', 'ADC_min', 'ADC_max'] return dti_features, dti_labels
def getfeatureimages(image, segmentation, gabor_settings=None, image_type=None, parameters=None, types=['LBP'], slicenum=None, save=False): if parameters is not None: # Load variables from the confilg file config = config_io.load_config(parameters) # Calculate the image features gabor_settings = config['ImageFeatures']['gabor_settings'] image_type = config['ImageFeatures']['image_type'] print('Calculating image features!') image_data = load_images(image, image_type, None, None) if type(segmentation) is list: segmentation = ''.join(segmentation) contours = [sitk.ReadImage(segmentation)] # FIXME: Bug in some of our own segmentations szi = image_data['images'][0].GetSize() szs = contours[0].GetSize() if szi != szs: message = ('Shapes of image({}) and mask ({}) do not match!').format( str(szi), str(szs)) print message # FIXME: Now excluding last slice c = contours[0] c = sitk.GetArrayFromImage(c) c = c[0:-1, :, :] contours = [sitk.GetImageFromArray(c)] szs = contours[0].GetSize() if szi != szs: message = ( 'Shapes of image({}) and mask ({}) do not match!').format( str(szi), str(szs)) raise IndexError(message) else: print("['FIXED'] Excluded last slice.") # Convert to arrays and get only masked slices i_image = image_data['images'][0] i_mask = contours[0] i_image_array = sitkh.GetArrayFromImage(i_image) i_mask_array = sitkh.GetArrayFromImage(i_mask) i_image_array, i_mask_array = ih.get_masked_slices_image( i_image_array, i_mask_array) if slicenum is None: slicenum = int(i_image_array.shape[2] / 2) i_image_array = np.squeeze(i_image_array[:, :, slicenum]) i_mask_array = np.squeeze(i_mask_array[:, :, slicenum]) if save: filename, file_extension = os.path.splitext('/path/to/somefile.ext') else: filename = None im = list() if 'LBP' in types: LBP_im = save_LBP_features(i_image_array, i_mask_array, filename) im.append(LBP_im) if 'Gabor' in types: Gabor_im = save_gabor_features(i_image_array, i_mask_array, gabor_settings, filename) im.append(Gabor_im) if 'Shape' in types: im.append(i_mask_array) if 'Histogram' in types: im.append(i_image_array) return im
def get_dti_post_features(image, mask, meta_data): i_mask_array = sitkh.GetArrayFromImage(mask) i_mask_array = i_mask_array.astype(np.bool) i_mask_array = i_mask_array.flatten() L1_image = sitkh.GetArrayFromImage(image[0]).flatten() L2_image = sitkh.GetArrayFromImage(image[1]).flatten() L3_image = sitkh.GetArrayFromImage(image[2]).flatten() L1_image = L1_image[i_mask_array] L2_image = L2_image[i_mask_array] L3_image = L3_image[i_mask_array] # A small fix because sometimes the eigenvalues are just below/around 0 # And will give error in division. if np.amin(L1_image) <= 0: L1_image = L1_image + np.abs(np.amin(L1_image)) + 0.00001 if np.amin(L2_image) <= 0: L2_image = L2_image + np.abs(np.amin(L2_image)) + 0.00001 if np.amin(L3_image) <= 0: L3_image = L3_image + np.abs(np.amin(L3_image)) + 0.00001 ADC_map = (L1_image + L2_image + L3_image) / 3.0 # ADC map is also mean diffusivity FA_numerator = 3.0 * ((L1_image - ADC_map)**2.0 + (L2_image - ADC_map)**2.0 + (L3_image - ADC_map)**2.0) FA_denominator = 2.0 * (L1_image**2.0 + L2_image**2.0 + L3_image**2.0) FA_map = np.sqrt(FA_numerator / FA_denominator) # print(min(L2_image)) # print(min(L3_image)) # Also volume ratio VR_map = 1.0 - L1_image * L2_image * L3_image / ADC_map**3.0 # Now get inside tumor # ADC_tumor = ADC_map[i_mask_array] # FA_tumor = FA_map[i_mask_array] # VR_tumor = VR_map[i_mask_array] ADC_min, ADC_max, ADC_mean, ADC_std, ADC_median, ADC_skew, ADC_kurtosis, ADC_range =\ get_statistical_moments(ADC_map) FA_min, FA_max, FA_mean, FA_std, FA_median, FA_skew, FA_kurtosis, FA_range =\ get_statistical_moments(FA_map) VR_min, VR_max, VR_mean, VR_std, VR_median, VR_skew, VR_kurtosis, VR_range =\ get_statistical_moments(VR_map) # dti_features = [ADC_min, ADC_max, ADC_mean, ADC_std, ADC_median, ADC_skew, ADC_kurtosis, ADC_range, # FA_min, FA_max, FA_mean, FA_std, FA_median, FA_skew, FA_kurtosis, FA_range, # VR_min, VR_max, VR_mean, VR_std, VR_median, VR_skew, VR_kurtosis, VR_range] # # panda_labels = ['ADC_min', 'ADC_max', 'ADC_mean', 'ADC_std', 'ADC_median', 'ADC_skew', 'ADC_kurtosis', 'ADC_range', # 'FA_min', 'FA_max', 'FA_mean', 'FA_std', 'FA_median', 'FA_skew', 'FA_kurtosis', 'FA_range', # 'VR_min', 'VR_max', 'VR_mean', 'VR_std', 'VR_median', 'VR_skew', 'VR_kurtosis', 'VR_range'] dti_features = [ ADC_min, ADC_max, ADC_mean, ADC_std, ADC_median, ADC_skew, ADC_kurtosis, ADC_range ] dti_labels = [ 'ADC_min', 'ADC_max', 'ADC_mean', 'ADC_std', 'ADC_median', 'ADC_skew', 'ADC_kurtosis', 'ADC_range' ] return dti_features, dti_labels
def get_shape_features_2D(mask, metadata=None): # Pre-allocation perimeter = list() convexity = list() area = list() rad_dist_avg = list() rad_dist_std = list() roughness_avg = list() roughness_std = list() cvar = list() prax = list() evar = list() solidity = list() compactness = list() rad_dist = list() rad_dist_norm = list() roughness = list() # Now calculate some of the edge shape features # NOTE: Due to conversion to array, first and third axis are switched mask = sitkh.GetArrayFromImage(mask) N_mask_slices = mask.shape[2] mask = label(mask, connectivity=3) for i_slice in range(0, N_mask_slices): # Iterate over all slices slicie = mask[:, :, i_slice] for nblob in range(0, np.max(slicie)): blobimage = np.zeros(slicie.shape) blobimage[slicie == nblob] = 1 if np.sum(blobimage) == 0: # No points in volume, therefore we ignore it. continue blobimage = blobimage.astype(np.uint8) blobimage = sitk.GetImageFromArray(blobimage) # Iterate over all blobs in a slice boundary_points = cf.get_smooth_contour(blobimage, N_min_smooth, N_max_smooth) if boundary_points.shape[0] <= 3: # Only 1 or 2 points in volume, which means it's not really a # volume, therefore we ignore it. continue rad_dist_i, rad_dist_norm_i = compute_radial_distance( boundary_points) rad_dist.append(rad_dist_i) rad_dist_norm.append(rad_dist_norm_i) perimeter.append(compute_perimeter(boundary_points)) area.append(compute_area(boundary_points)) compactness.append(compute_compactness(boundary_points)) roughness_i, roughness_avg_temp = compute_roughness( boundary_points, rad_dist_i) roughness_avg.append(roughness_avg_temp) roughness.append(roughness_i) cvar.append(compute_cvar(boundary_points)) prax.append(compute_prax(boundary_points)) evar.append(compute_evar(boundary_points)) # TODO: Move computing convexity into esf convex_hull = cf.convex_hull(blobimage) convexity.append(compute_perimeter(convex_hull) / perimeter[-1]) solidity.append(compute_area(convex_hull)/area[-1]) rad_dist_avg.append(np.mean(np.asarray(rad_dist_i))) rad_dist_std.append(np.std(np.asarray(rad_dist_i))) roughness_std.append(np.std(np.asarray(roughness_i))) compactness_avg = np.mean(compactness) compactness_std = np.std(compactness) convexity_avg = np.mean(convexity) convexity_std = np.std(convexity) rad_dist_avg = np.mean(rad_dist_avg) rad_dist_std = np.mean(rad_dist_std) roughness_avg = np.mean(roughness_avg) roughness_std = np.mean(roughness_std) cvar_avg = np.mean(cvar) cvar_std = np.std(cvar) prax_avg = np.mean(prax) prax_std = np.std(prax) evar_avg = np.mean(evar) evar_std = np.std(evar) solidity_avg = np.mean(solidity) solidity_std = np.std(solidity) shape_labels = ['sf_compactness_avg', 'sf_compactness_std', 'sf_rad_dist_avg', 'sf_rad_dist_std', 'sf_roughness_avg', 'sf_roughness_std', 'sf_convexity_avg', 'sf_convexity_std', 'sf_cvar_avg', 'sf_cvar_std', 'sf_prax_avg', 'sf_prax_std', 'sf_evar_avg', 'sf_evar_std', 'sf_solidity_avg', 'sf_solidity_std'] shape_features = [compactness_avg, compactness_std, rad_dist_avg, rad_dist_std, roughness_avg, roughness_std, convexity_avg, convexity_std, cvar_avg, cvar_std, prax_avg, prax_std, evar_avg, evar_std, solidity_avg, solidity_std] if metadata is not None: if (0x18, 0x50) in metadata.keys(): # import dicom as pydicom # metadata = pydicom.read_file(metadata) pixel_spacing = metadata[0x28, 0x30].value slice_thickness = int(metadata[0x18, 0x50].value) voxel_volume = pixel_spacing[0] * pixel_spacing[1] * slice_thickness volume = np.sum(mask) * voxel_volume shape_labels.append('sf_volume') shape_features.append(volume) return shape_features, shape_labels
def get_image_features(image_data, masks, parameters, mask_index=-1, multi_mask=False, config=None, output=None): if type(image_data) == pd.core.frame.DataFrame: images = image_data['images'] image_types = image_data['images'].keys() meta_data = image_data['metadata'] sem_data = image_data['semantics'] N_images = len(image_data.images) else: # Dictionary images = image_data['images'] image_types = image_data['image_type'] meta_data = image_data['metadata'] sem_data = image_data['semantics'] N_images = len(images) N_masks = len(masks) feature_values = list() feature_labels = list() if ~multi_mask and mask_index == -1: raise ValueError('Multi_mask was set to False, but no mask index was\ provided') if multi_mask and N_images != N_masks: raise ValueError('Multi_contour was set to True, but the number of\ contours does not match the number of images') if multi_mask: pass else: shape_mask = ih.get_masked_slices_mask(masks[mask_index]) print("Computing shape features.") shape_features, shape_labels = sf.get_shape_features( shape_mask, meta_data[0]) feature_values += shape_features feature_labels += shape_labels if config["orientation"]: print("Computing orientation features.") orientation_features, orientation_labels =\ of.get_orientation_features(shape_mask) feature_values += orientation_features feature_labels += orientation_labels if meta_data[0] is not None: print("Extracting patient features.") patient_features, patient_labels =\ pf.get_patient_features(meta_data[0], image_types[0]) feature_values += patient_features feature_labels += patient_labels if sem_data[0] is not None and output is not None: print("Extracting semantic features.") sem_features, sem_labels = semf.get_semantic_features( sem_data[0], output) feature_values += sem_features feature_labels += sem_labels for i_image, i_mask, i_image_type, i_meta_data in zip( images, masks, image_types, meta_data): if 'MR' in i_image_type: i_image_array = sitkh.GetArrayFromImage(i_image) i_mask_array = sitkh.GetArrayFromImage(i_mask) masked_voxels = ih.get_masked_voxels(i_image_array, i_mask_array) print("Computing histogram features.") histogram_features, histogram_labels =\ hf.get_histogram_features(masked_voxels, N_BINS) i_image_array, i_mask_array = ih.get_masked_slices_image( i_image_array, i_mask_array) print("Computing texture features.") texture_features, texture_labels =\ tf.get_texture_features(i_image_array, i_mask_array, parameters, config['texture']) feature_values += histogram_features + texture_features feature_labels += histogram_labels + texture_labels if config["coliage"]: print("Computing coliage features.") coliage_features, coliage_labels =\ cf.get_coliage_features(i_image_array, i_mask_array) feature_values += coliage_features feature_labels += coliage_labels if config["vessel"]: print("Computing vessel features.") vessel_features, vessel_labels =\ vesf.get_vessel_features(i_image_array, i_mask_array, parameters['vessel']) feature_values += vessel_features feature_labels += vessel_labels if config["log"]: print("Computing log features.") log_features, log_labels =\ logf.get_log_features(i_image_array, i_mask_array, parameters['log']) feature_values += log_features feature_labels += log_labels if config["phase"]: print("Computing phase features.") phase_features, phase_labels =\ phasef.get_phase_features(i_image_array, i_mask_array, parameters['phase']) feature_values += phase_features feature_labels += phase_labels elif 'DTI_post' in i_image_type: dti_features, dti_labels = dtif.get_dti_post_features( i_image, i_mask, i_meta_data) feature_values += dti_features feature_labels += dti_labels elif 'DTI' in i_image_type: dti_features, dti_labels = dtif.get_dti_features( i_image, i_mask, i_meta_data) feature_values += dti_features feature_labels += dti_labels elif 'CT' in i_image_type: i_image_array = sitkh.GetArrayFromImage(i_image) i_mask_array = sitkh.GetArrayFromImage(i_mask) masked_voxels = ih.get_masked_voxels(i_image_array, i_mask_array) print("Computing histogram features.") histogram_features, histogram_labels =\ hf.get_histogram_features(masked_voxels, N_BINS) i_image_array, i_mask_array = ih.get_masked_slices_image( i_image_array, i_mask_array) print("Computing texture features.") texture_features, texture_labels =\ tf.get_texture_features(i_image_array, i_mask_array, parameters, config['texture']) feature_values += histogram_features + texture_features feature_labels += histogram_labels + texture_labels if config["coliage"]: print("Computing coliage features.") coliage_features, coliage_labels =\ cf.get_coliage_features(i_image_array, i_mask_array) feature_values += coliage_features feature_labels += coliage_labels if config["vessel"]: print("Computing vessel features.") vessel_features, vessel_labels =\ vesf.get_vessel_features(i_image_array, i_mask_array, parameters['vessel']) feature_values += vessel_features feature_labels += vessel_labels if config["log"]: print("Computing log features.") log_features, log_labels =\ logf.get_log_features(i_image_array, i_mask_array, parameters['log']) feature_values += log_features feature_labels += log_labels if config["phase"]: print("Computing phase features.") phase_features, phase_labels =\ phasef.get_phase_features(i_image_array, i_mask_array, parameters['phase']) feature_values += phase_features feature_labels += phase_labels else: print("Invalid image type: {}").format(i_image_type) raise TypeError return feature_values, feature_labels
def get_image_features(image_data, masks, gabor_settings, mask_index=-1, multi_mask=False, config=None, output=None): if type(image_data) == pd.core.frame.DataFrame: images = image_data['images'] image_types = image_data['images'].keys() meta_data = image_data['metadata'] sem_data = image_data['semantics'] N_images = len(image_data.images) else: # Dictionary images = image_data['images'] image_types = image_data['image_type'] meta_data = image_data['metadata'] sem_data = image_data['semantics'] N_images = len(images) N_masks = len(masks) image_features = dict() if ~multi_mask and mask_index == -1: raise ValueError('Multi_mask was set to False, but no mask index was\ provided') if multi_mask and N_images != N_masks: raise ValueError('Multi_contour was set to True, but the number of\ contours does not match the number of images') if multi_mask: pass else: shape_mask = ih.get_masked_slices_mask(masks[mask_index]) shape_features = sf.get_shape_features(shape_mask, meta_data[0]) if config["orientation"]: orientation_features = of.get_orientation_features(shape_mask) image_features['orientation_features'] = orientation_features image_features['shape_features'] = shape_features if meta_data[0] is not None: patient_features = pf.get_patient_features(meta_data[0], image_types[0]) image_features['patient_features'] = patient_features if sem_data[0] is not None and output is not None: sem_features = semf.get_semantic_features(sem_data[0], output) image_features['semantic_features'] = sem_features for i_image, i_mask, i_image_type, i_meta_data in zip( images, masks, image_types, meta_data): if 'MR' in i_image_type: i_image_array = sitkh.GetArrayFromImage(i_image) i_mask_array = sitkh.GetArrayFromImage(i_mask) masked_voxels = ih.get_masked_voxels(i_image_array, i_mask_array) histogram_features = hf.get_histogram_features( masked_voxels, N_BINS) i_image_array, i_mask_array = ih.get_masked_slices_image( i_image_array, i_mask_array) texture_features = tf.get_texture_features(i_image_array, i_mask_array, gabor_settings, config['texture']) coliage_features = cf.get_coliage_featues(i_image_array, i_mask_array) # filter_features = ff.get_filter_features(i_image_array, # i_mask_array) # image_features[i_image_type] =\ # pd.concat([histogram_features, texture_features], # keys=['histogram_features', 'texture_features']) image_features['histogram_features'] = histogram_features image_features['texture_features'] = texture_features image_features['coliage_features'] = coliage_features # image_features['filter_features'] = filter_features # image_features[i_image_type] = histogram_features elif 'DTI_post' in i_image_type: dti_features = dtif.get_dti_post_features(i_image, i_mask, i_meta_data) image_features[i_image_type] = dti_features elif 'DTI' in i_image_type: dti_features = dtif.get_dti_features(i_image, i_mask, i_meta_data) image_features[i_image_type] = dti_features elif 'CT' in i_image_type: i_image_array = sitkh.GetArrayFromImage(i_image) i_mask_array = sitkh.GetArrayFromImage(i_mask) masked_voxels = ih.get_masked_voxels(i_image_array, i_mask_array) histogram_features = hf.get_histogram_features( masked_voxels, N_BINS) i_image_array, i_mask_array = ih.get_masked_slices_image( i_image_array, i_mask_array) texture_features = tf.get_texture_features(i_image_array, i_mask_array, gabor_settings, config['texture']) coliage_features = cf.get_coliage_featues(i_image_array, i_mask_array) # filter_features = ff.get_filter_features(i_image_array, # i_mask_array) # image_features[i_image_type] = histogram_features image_features['histogram_features'] = histogram_features image_features['texture_features'] = texture_features image_features['coliage_features'] = coliage_features # image_features['filter_features'] = filter_features else: print("Invalid image type: {}").format(i_image_type) raise TypeError # We also return just the arrray image_feature_array = list() for _, feattype in image_features.iteritems(): for _, features in feattype.iteritems(): image_feature_array.extend(features.values) print image_feature_array image_feature_array = np.asarray(image_feature_array) image_feature_array = image_feature_array.ravel() return image_features, image_feature_array
def get_image_features(image_data, mask, parameters, config=None, config_general=None, output=None): ''' Calculate features from a ROI of an image. Parameters ---------- image_data: Pandas DataFrame or dictionary, mandatory Contains the image, image type, metadata and semantics for the feature extraction. These have to be indexed by these keys. Should either be a Pandas DataFrame, in which the image type is he key of the images, or a dictionary, in which image type is a a separate field. The image should be a SimpleITK Image, the image type a string, the metadata a pydicom dicom type and the semantics a dictionary. mask: ITK Image, mandatory ROI to be used for feature extraction. parameters: dictionary, mandatory, Parameters for feature calculation. See the Github Wiki for the possible fields and their description. config: dictionary, mandatory Configuration for feature calculation. Mostly configures which features are calculated or not. See the Github Wiki for the possible fields and their description. config_general: dictionary, mandatory Configuration for general settings. Currently only configures settings for the Joblib Parallel function. See the Github Wiki for the possible fields and their description. output: string, mandatory path referring to the .hdf5 file to which the output should be written for the CalcFeatures function. This field is used to match the patient ID of the semantic features to this filename. Returns ---------- feature_values: list Contains the values for all extracted features. feature_labels: list Contains the labels for all extracted features. Each entry corresponds to the element with the same index from the feature_values object. ''' # Assign data to correct variables if type(image_data) == pd.core.frame.DataFrame: image_type = image_data['images'].keys()[0] meta_data = image_data['metadata'] sem_data = image_data['semantics'] image_data = image_data['images'] else: # Dictionary image_type = image_data['image_type'] meta_data = image_data['metadata'] sem_data = image_data['semantics'] image_data = image_data['images'] # Initialize feature value and label lists. feature_values = list() feature_labels = list() # Extract shape features shape_mask = ih.get_masked_slices_mask(mask) print("Computing shape features.") shape_features, shape_labels = sf.get_shape_features(shape_mask, meta_data) feature_values += shape_features feature_labels += shape_labels if config["orientation"]: print("Computing orientation features.") orientation_features, orientation_labels =\ of.get_orientation_features(shape_mask) feature_values += orientation_features feature_labels += orientation_labels if meta_data is not None: print("Extracting patient features.") patient_features, patient_labels =\ pf.get_patient_features(meta_data, image_type) feature_values += patient_features feature_labels += patient_labels if sem_data is not None and output is not None: print("Extracting semantic features.") sem_features, sem_labels = semf.get_semantic_features(sem_data, output) feature_values += sem_features feature_labels += sem_labels if 'DTI_post' in image_type: dti_features, dti_labels = dtif.get_dti_post_features( image_data, mask, meta_data) feature_values += dti_features feature_labels += dti_labels elif 'DTI' in image_type: dti_features, dti_labels = dtif.get_dti_features( image_data, mask, meta_data) feature_values += dti_features feature_labels += dti_labels elif any(type in image_type for type in ['MR', 'CT', 'PET', 'MG']): image_data_array = sitkh.GetArrayFromImage(image_data) mask_array = sitkh.GetArrayFromImage(mask) masked_voxels = ih.get_masked_voxels(image_data_array, mask_array) print("Computing histogram features.") histogram_features, histogram_labels =\ hf.get_histogram_features(masked_voxels, N_BINS) # NOTE: As a minimum of 4 voxels in each dimension is needed # for the SimpleITK log filter, we compute these features # on the full image if config["log"]: print("Computing log features.") log_features, log_labels =\ logf.get_log_features(image_data_array, mask_array, parameters['log']) feature_values += log_features feature_labels += log_labels image_data_array, mask_array = ih.get_masked_slices_image( image_data_array, mask_array) print("Computing texture features.") texture_features, texture_labels =\ tf.get_texture_features(image_data_array, mask_array, parameters, config['texture'], config_general) feature_values += histogram_features + texture_features feature_labels += histogram_labels + texture_labels if config["coliage"]: print("Computing coliage features.") coliage_features, coliage_labels =\ cf.get_coliage_features(image_data_array, mask_array) feature_values += coliage_features feature_labels += coliage_labels if config["vessel"]: print("Computing vessel features.") vessel_features, vessel_labels =\ vesf.get_vessel_features(image_data_array, mask_array, parameters['vessel']) feature_values += vessel_features feature_labels += vessel_labels if config["phase"]: print("Computing phase features.") phase_features, phase_labels =\ phasef.get_phase_features(image_data_array, mask_array, parameters['phase']) feature_values += phase_features feature_labels += phase_labels else: raise ae.PREDICTTypeError( ("Invalid image type: {}").format(image_type)) return feature_values, feature_labels