def Save_Directory_Statistics(input_directory, ROI_directory, output_csv, mask=False, mask_suffix='_mask', r2_thresholds=[.9]): """ Save ROI statistics into a giant csv file. """ # exclude_patients = ['CED_19', ] file_database = glob.glob(os.path.join(input_directory, '*blur*r2_' + str(r2_thresholds[0]) + '.nii*')) output_headers = ['filename','mean','median','min','max','std', 'total_voxels','removed_values', 'removed_percent', 'low_values', 'low_percent'] ROI_dict = {} for ROI in glob.glob(os.path.join(ROI_directory, '*.nii*')): ROI_dict[os.path.basename(os.path.normpath(ROI))[0:15]] = convert_input_2_numpy(ROI) for r2 in r2_thresholds: output_data = np.zeros((1+len(file_database), len(output_headers)),dtype=object) output_data[0,:] = output_headers with open(replace_suffix(output_csv, '', '_' + str(r2)), 'wb') as writefile: csvfile = csv.writer(writefile, delimiter=',') csvfile.writerow(output_data[0,:]) for row_idx, filename in enumerate(file_database): data_array = convert_input_2_numpy(filename) patient_visit_code = os.path.basename(os.path.normpath(filename))[0:15] roi_array = ROI_dict[patient_visit_code] r2_filename = str.split(filename, '_') r2_filename[-3] = 'r2' r2_filename = '_'.join(r2_filename) r2_array = convert_input_2_numpy(r2_filename) data_array[data_array<0] = -.01 data_array[r2_array<=r2] = -.01 data_array[roi_array<=0] = -.01 masked_data_array_ROI = np.ma.masked_where(data_array < 0, data_array) ROI_values = [np.ma.mean(masked_data_array_ROI), np.ma.median(masked_data_array_ROI), np.ma.min(masked_data_array_ROI), np.ma.max(masked_data_array_ROI), np.ma.std(masked_data_array_ROI), (roi_array > 0).sum(), ((data_array <= 0) & (roi_array > 0)).sum(), float(((data_array <= 0) & (roi_array > 0)).sum()) / float((roi_array > 0).sum()), ((r2_array >= r2) & (roi_array > 0)).sum(), float(((r2_array >= r2) & (roi_array > 0)).sum()) / float((roi_array > 0).sum())] print ROI_values output_data[row_idx+1] = [filename] + ROI_values csvfile.writerow(output_data[row_idx+1]) return
def split_image(input_volume, input_label_volume=None, label_indices=None, mask_value=0): """ This function takes in an image, optionally a label image, and optionally a set of indices, and returns one duplicate masked image for each given label. Useful for analyzing, say, multiple tumors, although expensive in memory. Useful when paired with the truncate_image function to reduce array memory. """ image_numpy = convert_input_2_numpy(input_volume) label_numpy = convert_input_2_numpy(input_label_volume) masked_images = [] if label_indices is None: if label_numpy is None: label_indices = np.unique(image_numpy) else: label_indices = np.unique(label_numpy) if mask_value in label_indices: label_indices = np.delete(np.array(label_indices), np.argwhere(label_indices == mask_value)) for idx in label_indices: masked_image = np.copy(image_numpy) masked_image[label_numpy != idx] = mask_value masked_images += [masked_image] return masked_images
def __init__(self, dce_data, ktrans_data, ve_data, n_samples=1000, gaussian_noise=[0, 0]): self.completed = False dce_raw, ve_raw, ktrans_raw = convert_input_2_numpy( dce_data), convert_input_2_numpy(ve_data), convert_input_2_numpy( ktrans_data) # Minit Test # dce_raw, ve_raw, ktrans_raw = dce_raw[0:30,0:30,0:30,:], ve_raw[0:30,0:30,0:30], ktrans_raw[0:30,0:30,0:30] dce_numpy = dce_raw.reshape(-1, dce_raw.shape[-1]) ve_numpy, ktrans_numpy = ve_raw.reshape( ve_raw.shape[0] * ve_raw.shape[1] * ve_raw.shape[2]), ktrans_raw.reshape(ktrans_raw.shape[0] * ktrans_raw.shape[1] * ktrans_raw.shape[2]) self.dce_data = [dce_numpy, ktrans_numpy, ve_numpy] self.data_shape = ktrans_raw.shape self.voxel_count = ktrans_numpy.size self.batch_id = 0
def Determine_R2_Cutoff_Point(input_directory, ROI_directory): """ Save ROI statistics into a giant csv file. """ file_database = glob.glob(os.path.join(input_directory, '*.nii*')) output_headers = [ 'filename', 'mean', 'median', 'std', 'min', 'max', 'total_voxels', 'removed_values', 'removed_percent', 'low_values', 'low_percent' ] output_data = np.zeros((1 + len(file_database), len(output_headers)), dtype=object) output_data[0, :] = output_headers ROI_dict = {} for ROI in glob.glob(os.path.join(ROI_directory, '*.nii*')): ROI_dict[os.path.basename( os.path.normpath(ROI))[0:15]] = convert_input_2_numpy(ROI) r2_masked_num, r2_total_num = [0] * 100, [0] * 100 np.set_printoptions(precision=2) np.set_printoptions(suppress=True) for row_idx, filename in enumerate(file_database): if 'ktrans' not in filename or '0.2' in filename: continue data_array = convert_input_2_numpy(filename) r2_array = convert_input_2_numpy( replace_suffix(filename, input_suffix=None, output_suffix='r2', suffix_delimiter='_')) # print replace_suffix(filename, input_suffix=None, output_suffix='r2', suffix_delimiter='_') patient_visit_code = os.path.basename(os.path.normpath(filename))[0:15] roi_array = ROI_dict[patient_visit_code] for r2_idx, r2_threshold in enumerate(np.arange(0, 1, .01)): r2_masked_num[r2_idx] += ((r2_array <= r2_threshold) & (roi_array > 0)).sum() r2_total_num[r2_idx] += (roi_array > 0).sum() print( np.array(r2_masked_num, dtype=float) / np.array(r2_total_num, dtype=float)) r2_percent_num = np.array(r2_masked_num, dtype=float) / np.array( r2_total_num, dtype=float) for r2_idx, r2_threshold in enumerate(range(0, 1, .01)): print(r2_threshold) print(r2_percent_num[r2_idx]) return
def Slicer_PkModeling(input_folder, Slicer_path="/opt/Slicer-4.5.0-1-linux-amd64/Slicer"): # os.chdir('C:/Users/azb22/Documents/Scripting/DCE_Motion_Phantom') # input_folder = '.' # Slicer_path = 'C:/Users/azb22/Documents/Software/SlicerNightly/Slicer_4.6.0/Slicer.exe' Slicer_Command = Slicer_path + ' --launch' T1Blood = '--T1Blood 1440' T1Tissue = '--T1Tissue 1000' relaxivity = '--relaxivity .0045' hematocrit = '--hematocrit .45' BAT_mode = '--BATCalculationMode UseConstantBAT' BAT_arrival = '--constantBAT 8' aif_mask_command = '--aifMask ' roi_mask_command = '--roiMask ' t1_map_command = '--T1Map ' # image_list = glob.glob(input_folder + '/*.nrrd') image_list = os.listdir(input_folder) for nrrd_image in image_list: if '.nrrd' in nrrd_image: move(nrrd_image, nrrd_image.replace(' ', '')) nrrd_image = nrrd_image.replace(' ', '') output_ktrans_image = str.split(nrrd_image, '.')[0] + '_ktrans.nii.gz' output_ve_image = str.split(nrrd_image, '.')[0] + '_ve.nii.gz' output_ktrans_command = '--outputKtrans ' + output_ktrans_image output_ve_command = '--outputVe ' + output_ve_image PkModeling_command = ' '.join([ Slicer_Command, 'PkModeling', nrrd_image, T1Blood, T1Tissue, relaxivity, hematocrit, BAT_mode, BAT_arrival, '--usePopAif', output_ve_command, output_ktrans_command ]) # call(PkModeling_command, shell=True) ktrans_array = convert_input_2_numpy(output_ktrans_image) ve_array = convert_input_2_numpy(output_ve_image) for z in range(ktrans_array.shape[-1]): ktrans_array[..., z] = medfilt(ktrans_array[..., z], [3, 3]) ve_array[..., z] = medfilt(ve_array[..., z], [3, 3]) ktrans_array[ve_array == 0] = .001 ve_array[ve_array == 0] = .001 save_numpy_2_nifti(ktrans_array, output_ktrans_image, output_ktrans_image) save_numpy_2_nifti(ve_array, output_ve_image, output_ve_image) return
def Preprocess_Volumes(input_directory, output_directory, r2_threshold=.9): if not os.path.exists(output_directory): os.mkdir(output_directory) file_database = glob.glob(os.path.join(input_directory, '*r2*.nii*')) print(os.path.join(input_directory, '*r2*.nii*')) for file in file_database: print(file) input_ktrans = replace_suffix(file, 'r2', 'ktrans') input_ve = replace_suffix(file, 'r2', 've') output_ktrans = os.path.join( output_directory, replace_suffix(os.path.basename(file), 'r2', 'ktrans_r2_' + str(r2_threshold))) output_ve = os.path.join( output_directory, replace_suffix(os.path.basename(file), 'r2', 've_r2_' + str(r2_threshold))) output_kep = os.path.join( output_directory, replace_suffix(os.path.basename(file), 'r2', 'kep_r2_' + str(r2_threshold))) output_r2 = os.path.join( output_directory, replace_suffix(os.path.basename(file), 'r2', 'r2_r2_' + str(r2_threshold))) print(input_ktrans) r2_map = np.nan_to_num(convert_input_2_numpy(file)) ktrans_map = convert_input_2_numpy(input_ktrans) ve_map = convert_input_2_numpy(input_ve) print((r2_map < r2_threshold).sum()) ve_map[ktrans_map > 10] = 0 ktrans_map[ktrans_map > 10] = 0 ktrans_map[ve_map > 1] = 0 ve_map[ve_map > 1] = 0 ktrans_map[r2_map < r2_threshold] = -.01 ve_map[r2_map < r2_threshold] = -.01 kep_map = np.nan_to_num(ktrans_map / ve_map) kep_map[r2_map < r2_threshold] = -.01 save_numpy_2_nifti(ktrans_map, input_ktrans, output_ktrans) save_numpy_2_nifti(ve_map, input_ktrans, output_ve) save_numpy_2_nifti(kep_map, input_ktrans, output_kep) save_numpy_2_nifti(r2_map, input_ktrans, output_r2)
def qtim_statistic(input_data, statistics, label_data='', mask_value=0, return_label=[], additional_parameters=''): """ TODO: Documentation. This should replace the existing statistics function for the feature extractor. """ outputs = [] if isinstance(statistics, str): statistics = [statistics,] input_numpy = convert_input_2_numpy(input_data) if label_data != '': input_numpy = crop_with_mask(input_numpy, label_data, mask_value=mask_value, return_labels=return_label) stats_numpy = np.ravel(input_numpy[input_numpy > mask_value]) for statistic in statistics: if statistics_dict[statistic] == []: print('No statistics by that keyword. Returning blank...') outputs += [''] try: outputs += [statistics_dict[statistic](stats_numpy)] except: outputs += ['NA'] return outputs
def intensity_range(input_volume, percentiles=[.25, .75]): """Retrieves a min and max of intensities at two specified percentiles on the intensity histogram. This could be useful for thresholding, normalizing, or other tasks. Likely redundant with existing numpy feature, TODO: remove Parameters ---------- input_volume : filename or numpy array Input data. If not in an array, will attempt to convert to array. percentiles : list, optional Histogram percentiles to return. Default is [.25, .75] Returns ------- list A two-item list of intensities at the given percentiles. """ image_numpy = convert_input_2_numpy(input_volume) intensity_range = [ np.percentile(image_numpy, percentiles[0], interpolation="nearest"), np.percentile(image_numpy, percentiles[1], interpolation="nearest") ] return intensity_range
def minimum_bounding_box(data_directories, modalities=['FLAIR_pp.nii.gz', 'T1post_pp.nii.gz']): max_dims = [0, 0, 0] patient_vols = [] for directory in data_directories: patients = glob.glob(os.path.join(directory, '*/')) for patient in patients: single_patient_vols = [] for modality in modalities: single_patient_vols += [ glob.glob(os.path.join(patient, modality))[0] ] patient_vols += [single_patient_vols] for p_idx, single_patient_vols in enumerate(patient_vols): for modality in single_patient_vols: array = convert_input_2_numpy(modality) # print array.shape cropped_array = crop2(array) # print cropped_array.shape for idx, dim in enumerate(max_dims): if cropped_array.shape[idx] > 200: print idx, cropped_array.shape[idx] print modality if cropped_array.shape[idx] > dim: max_dims[idx] = cropped_array.shape[idx] # print max_dims print max_dims
def Convert_NRRD_to_Nifti(input_4d_nrrd, reference_nifti): input_4d_numpy = convert_input_2_numpy(input_4d_nrrd) reference_nifti = reference_nifti create_4d_nifti_from_3d(input_4d_numpy, reference_nifti, os.path.splitext(input_4d_nrrd)[0] + '.nii.gz') return os.path.splitext(input_4d_nrrd)[0] + '.nii.gz'
def Slicer_Rotate(input_numpy, reference_nifti, affine_matrix, Slicer_path="/opt/Slicer-4.5.0-1-linux-amd64/Slicer"): save_numpy_2_nifti(input_numpy, reference_nifti, 'temp.nii.gz') save_affine(affine_matrix, 'temp.txt') Slicer_Command = [Slicer_path, '--launch', 'ResampleScalarVectorDWIVolume', 'temp.nii.gz', 'temp_out.nii.gz', '-f', 'temp.txt', '-i', 'bs'] call(' '.join(Slicer_Command), shell=True) return convert_input_2_numpy('temp_out.nii.gz')
def __init__(self, phantom_data_files, n_samples=1000, max_seq_len=65): self.data = [] self.labels = [] self.seqlen = [] self.completed = False dce_phantoms = [ convert_input_2_numpy(data) for data in phantom_data_files ] sample_per_phantom = n_samples / len(dce_phantoms) ktrans_values = np.array([.01, .02, .05, .1, .2, .5]) ktrans_values = np.repeat(np.repeat(ktrans_values, 10)[:, np.newaxis], 50, axis=1).T ve_values = np.array([.01, .05, .1, .2, .5]) ve_values = np.repeat(np.repeat(ve_values, 10)[:, np.newaxis], 60, axis=1) concentration_sample = list(np.ndindex(50, 60)) aif_sample = list(np.ndindex(50, 10)) indices = [(x, y) for x in concentration_sample for y in aif_sample] print ktrans_values.shape for phantom in dce_phantoms: seq_len = phantom.shape[-1] for i in range(sample_per_phantom): self.seqlen.append(seq_len) x, y = random.choice(indices) Intensity = phantom[x[0], x[1] + 10, :] AIF = phantom[y[0], y[1] + 70, :] ktrans = ktrans_values[x] ve = ve_values[x] s = [] for idx in xrange(len(Intensity)): s += [[Intensity[idx], AIF[idx]]] s += [[0., 0] for i in range(max_seq_len - seq_len)] self.data.append(s) self.labels.append([ktrans, ve]) self.batch_id = 0
def fill_in_convex_outline(input_data, output_file=None, reference_nifti=None, threshold_limit=[0, 100], color_threshold_limits=[[100, 300], [0, 100], [0, 100]], output_label_num=1): """ Thresholds a jpg according to certain color parameters. Uses a hole-filling algorithm to color in regions of interest. TODO: Reorganize into two separate tracks, instead of two winding tracks. """ image_nifti, image_type = convert_input_2_numpy(input_data, return_type=True) label_nifti = np.zeros_like(image_nifti) if image_type == 'image': red_range = np.logical_and( color_threshold_limits[0][0] < image_nifti[:, :, 0], image_nifti[:, :, 0] < color_threshold_limits[0][1]) green_range = np.logical_and( color_threshold_limits[1][0] < image_nifti[:, :, 1], image_nifti[:, :, 1] < color_threshold_limits[1][1]) blue_range = np.logical_and( color_threshold_limits[2][0] < image_nifti[:, :, 2], image_nifti[:, :, 2] < color_threshold_limits[2][1]) valid_range = np.logical_and(red_range, green_range, blue_range) label_nifti[valid_range] = 1 label_nifti = ndimage.morphology.binary_fill_holes( label_nifti[:, :, 0]).astype(label_nifti.dtype) if output_file is not None: misc.imsave(output_file, label_nifti * 255) else: image_nifti[image_nifti != 0] = output_label_num if image_nifti.ndim == 3: for z in range(image_nifti.shape[2]): label_nifti[..., z] = ndimage.morphology.binary_fill_holes( image_nifti[..., z]).astype(image_nifti.dtype) else: label_nifti = ndimage.morphology.binary_fill_holes( image_nifti).astype(image_nifti.dtype) print(np.sum(label_nifti), 'HOLE FILLED SUM') if output_file is not None: save_numpy_2_nifti(label_nifti, reference_nifti, output_file) return label_nifti
def calculate_prediction_dice(label_volume_1, label_volume_2): label_volume_1, label_volume_2, = convert_input_2_numpy( label_volume_1), convert_input_2_numpy(label_volume_2) im1 = np.asarray(label_volume_1).astype(np.bool) im2 = np.asarray(label_volume_2).astype(np.bool) if im1.shape != im2.shape: raise ValueError( "Shape mismatch: im1 and im2 must have the same shape.") im_sum = im1.sum() + im2.sum() if im_sum == 0: return empty_score # Compute Dice coefficient intersection = np.logical_and(im1, im2) return 2. * intersection.sum() / im_sum
def extract_maximal_slice(input_volume, input_label_volume='', mode='max_intensity', axis=2, mask_value=0, return_index=False): """ Extracts one slice from a presumably 3D volume. Either take the slice whose label has the greatest area (mode='max_label'), or whos sum of voxels has the greatest intensity (mode='max_intensity'), according to the provided axis variable. """ image_numpy = convert_input_2_numpy(input_volume) sum_dimensions = tuple( [int(x) for x in range(0, image_numpy.ndim) if x != axis]) if mode == 'max_intensity': flattened_image = np.sum(image_numpy, axis=sum_dimensions) elif mode == 'max_label': label_numpy = convert_input_2_numpy(input_label_volume) flattened_image = np.sum(label_numpy, axis=sum_dimensions) elif mode == 'non_mask': flattened_image = (image_numpy != mask_value).sum(axis=sum_dimensions) else: print( 'Invalid mode entered to extract_maximal_slice_3d. Returning original array..' ) return image_numpy # TODO: Put in support for highest_slice_index = np.argmax(flattened_image) try: highest_slice_index = highest_slice_index[0] except: pass if return_index: return get_arbitrary_axis_slice( image_numpy, axis, highest_slice_index), highest_slice_index return get_arbitrary_axis_slice(image_numpy, axis, highest_slice_index)
def Add_White_Noise(input_folder, noise_scale=1, noise_multiplier=10): input_niis = glob.glob(os.path.join(input_folder, '*Signal.nii*')) for input_4d_nifti in input_niis: input_numpy = convert_input_2_numpy(input_4d_nifti) for t in xrange(input_numpy.shape[-1]): input_numpy[..., t] = input_numpy[..., t] + np.random.normal(scale=noise_scale, size=input_numpy[..., t].shape).reshape(input_numpy[..., t].shape) * noise_multiplier save_numpy_2_nifti(input_numpy, input_4d_nifti, str.split(input_4d_nifti, '.')[0] + '_noise_' + str(noise_multiplier) +'.nii.gz')
def truncate_image(input_volume, mask_value=0, return_mask=False, padding=0, output_mask_filename=""): """ This function takes in an N-dimensional array and truncates all rows/columns/etc that contain only mask values. Useful for reducing computation time on functions whose running time scales exponentially with dimensions size. BUG: Currently seems to fail on axes with length 1. TODO: Truncate only on some axes. TODO: Add the option to add buffer pixels to meet output_dimensions. Parameters ---------- input_volume: N-dimensional array The volume to be truncated. Will be truncated in every axis. mask_value: int or float Vectors in an axis that are composed entirely of mask_value will be truncated. """ image_numpy = convert_input_2_numpy(input_volume) dims = image_numpy.shape truncate_ranges = [[0, 0] for x in dims] for axis, axis_length in enumerate(dims): start_flag = True for idx in range(axis_length): if (get_arbitrary_axis_slice(image_numpy, axis, idx) == mask_value).all(): if start_flag: truncate_ranges[axis][0] = idx + 1 else: start_flag = False truncate_ranges[axis][1] = idx + 1 if padding > 0: truncate_ranges = [[ max(0, x[0] - padding), min(dims[axis], x[1] + padding) ] for axis, x in enumerate(truncate_ranges)] truncate_slices = [slice(x[0], x[1]) for x in truncate_ranges] truncate_image_numpy = image_numpy[tuple(truncate_slices)] if return_mask: return truncate_image_numpy, mask_numpy else: return truncate_image_numpy
def get_shape(self): # TODO: Add support for non-nifti files. # Also this is not good. Perhaps specify shape in input? if self.output_shape is None: if self.data == []: return (0, ) else: return convert_input_2_numpy(self.data[0][0]).shape + (len( self.data[0]), ) else: return self.output_shape
def Add_Head_Jerks(input_folder, random_rotations=5, random_duration_range=[4,9], random_rotation_peaks=[[-4,4],[-4,4],[-4,4]], durations=7, timepoints=7, rotation_peaks=[4, 4, 0],): input_niis = glob.glob(os.path.join(input_folder, '*Signal*noise*')) print os.path.join(input_folder, '*Signal*noise*') input_niis = [x for x in input_niis if 'jerk' not in x] for input_4d_nifti in input_niis: print input_4d_nifti input_4d_numpy = convert_input_2_numpy(input_4d_nifti) print input_4d_numpy.shape output_motion_array = generate_identity_affine(input_4d_numpy.shape[-1]) if random_rotations > 0: total_jerk_windows = [] for random_rotation in xrange(random_rotations): # Will hang if more random_rotations are specified than can fit in available timepoints. overlapping = True while overlapping: random_duration = np.random.randint(*random_duration_range) random_timepoint = np.random.randint(0, input_4d_numpy.shape[-1]-random_duration) random_jerk_window = np.arange(random_timepoint, random_timepoint + random_duration) if not any(x in total_jerk_windows for x in random_jerk_window): overlapping = False total_jerk_windows.extend(random_jerk_window) random_motion = generate_motion_jerk(duration=random_duration, timepoint=random_timepoint, rotation_peaks=[np.random.randint(*random_rotation_peaks[0]),np.random.randint(*random_rotation_peaks[1]),np.random.randint(*random_rotation_peaks[2])], total_timepoints=input_4d_numpy.shape[-1]) print random_motion.shape print output_motion_array.shape for t in xrange(input_4d_numpy.shape[-1]): print output_motion_array[..., t] output_motion_array = compose_affines(output_motion_array, random_motion) output_4d_numpy = np.zeros_like(input_4d_numpy) for t in xrange(input_4d_numpy.shape[-1]): print output_motion_array[..., t] output_4d_numpy[..., t] = apply_affine(input_4d_numpy[...,t], output_motion_array[...,t], method='slicer', Slicer_path="C:/Users/azb22/Documents/Software/SlicerNightly/Slicer_4.6.0/Slicer.exe") else: pass save_numpy_2_nifti(output_4d_numpy, input_4d_nifti, str.split(input_4d_nifti, '.')[0] + '_jerk.nii.gz')
def __init__(self, phantom_data, gaussian_noise=[0, 0]): self.completed = False dce_raw = convert_input_2_numpy(phantom_data) self.aif = np.mean(dce_raw[:, 70:, :], axis=(0, 1)) self.data_shape = dce_raw[..., 0].shape dce_numpy = dce_raw.reshape(-1, dce_raw.shape[-1]) self.dce_data = dce_numpy self.voxel_count = dce_numpy[..., 0].size self.batch_id = 0
def read_image_files(image_files, return_affine=False): # Rename this function to something more descriptive? image_list = [] affine = None for image_file in image_files: image_list.append(convert_input_2_numpy(image_file)) # if affine # This is a little clunky. if return_affine: # This assumes all images share an affine matrix. # Replace with a better convert function, at some point. return np.stack([image for image in image_list], axis=-1), nib.load(image_files[0]).affine else: return np.stack([image for image in image_list], axis=-1)
def correlation_analysis(input_volume): image_numpy = convert_input_2_numpy(input_volume) displacement_list = np.mgrid[1:17:1, 1:17:1, 1:17:1].reshape(3, -1).T - 8 output_correlation_matrix = np.zeros((17, 17, 17), dtype=float) for displacement in displacement_list: print displacement x, y, z = displacement slice_list = [] displacement_slice_list = [] for axis in [x, y, z]: if axis < 0: slice_list += [slice(-axis, None, 1)] displacement_slice_list += [slice(0, axis, 1)] elif axis > 0: slice_list += [slice(0, -axis, 1)] displacement_slice_list += [slice(axis, None, 1)] else: slice_list += [slice(None)] displacement_slice_list += [slice(None)] print slice_list print displacement_slice_list compare_array_1 = image_numpy[slice_list] compare_array_2 = image_numpy[displacement_slice_list] print compare_array_1.shape print compare_array_2.shape correlation = np.corrcoef(compare_array_1.reshape(-1), compare_array_2.reshape(-1)) print correlation print '\n' output_correlation_matrix[x + 8, y + 8, z + 8] = correlation[1, 0] save_numpy_2_nifti( output_correlation_matrix, None, nifti_splitext(os.path.basename(input_volume))[0] + '_array' + nifti_splitext(os.path.basename(input_volume))[-1])
def get_arbitrary_axis_slice(input_volume, axis, slice_num): """ Returns a slice of numpy array according to an arbitrary axis. This is a convencience function mostly because I find Python's slice notation a bit cumbersome. """ image_numpy = convert_input_2_numpy(input_volume) image_slice = [] for dim in xrange(image_numpy.ndim): if dim == axis: if slice_num is list: image_slice += [slice(slice_num[0], slice_num[1])] else: image_slice += [slice(slice_num, slice_num + 1)] else: image_slice += [slice(None)] return image_numpy[tuple(image_slice)]
def __init__(self, dce_data, ktrans_data, ve_data, n_samples=1000, gaussian_noise=[0, 0], reconstruct=False, overwrite=False, masked=True): self.data = [] self.labels = [] self.seqlen = [] self.completed = False dce_raw, ve_raw, ktrans_raw = convert_input_2_numpy( dce_data), convert_input_2_numpy(ve_data), convert_input_2_numpy( ktrans_data) # Minit Test # dce_raw, ve_raw, ktrans_raw = dce_raw[0:30,0:30,0:30,:], ve_raw[0:30,0:30,0:30], ktrans_raw[0:30,0:30,0:30] dce_numpy = dce_raw.reshape(-1, dce_raw.shape[-1]) ve_numpy, ktrans_numpy = ve_raw.reshape( ve_raw.shape[0] * ve_raw.shape[1] * ve_raw.shape[2]), ktrans_raw.reshape(ktrans_raw.shape[0] * ktrans_raw.shape[1] * ktrans_raw.shape[2]) self.dce_data = [dce_numpy, ktrans_numpy, ve_numpy] self.data_shape = ktrans_raw.shape self.voxel_count = ktrans_numpy.size dce_seq_len = dce_numpy.shape[-1] dce_idx = np.arange(self.voxel_count) if masked: ktrans_high_mask = ktrans_numpy > .06 ktrans_low_mask = ktrans_numpy <= .06 dce_idx = dce_idx[ktrans_low_mask][0:int(np.ceil( 2 * n_samples / 9))].tolist() + dce_idx[ktrans_high_mask][ 0:int(np.ceil(7 * n_samples / 9)) + 1].tolist() # print np.min(ktrans_numpy), np.max(ktrans_numpy) for i in range(n_samples): # Random sequence length seq_len = dce_seq_len # Monitor sequence length for TensorFlow dynamic calculation self.seqlen.append(seq_len) Intensity = dce_numpy[dce_idx[i], :] ktrans = ktrans_numpy[dce_idx[i]] ve = ve_numpy[dce_idx[i]] s = Intensity s = [[i] for i in s] self.data.append(s) self.labels.append([ktrans, ve]) self.batch_id = 0
def Create_Ideal_DCE(input_folder, output_filepath = '', input_aif=''): input_DCEs = [] input_niis = glob.glob(os.path.join(input_folder, '*nrrd')) for nii in input_niis: if 'ktrans' in nii or 've' in nii: continue else: input_DCEs += [nii] for input_4d_nifti in input_DCEs: print 'Regenerating... ', input_4d_nifti # if output_filepath == '': output_filepath = str.split(input_4d_nifti, '.')[0] input_ktrans = output_filepath + '_ktrans.nii.gz' input_ve = output_filepath + '_ve.nii.gz' input_4d_nifti = Convert_NRRD_to_Nifti(input_4d_nifti, input_ktrans) input_numpy_4d = convert_input_2_numpy(input_4d_nifti) output_numpy_4d = np.zeros_like(input_numpy_4d) input_numpy_ktrans = convert_input_2_numpy(input_ktrans) input_numpy_ve = convert_input_2_numpy(input_ve) baseline_numpy = np.mean(input_numpy_4d[..., 0:7], axis=3) scan_time_seconds = 307.2 time_interval_seconds = float((scan_time_seconds) / input_numpy_4d.shape[-1]) time_interval_minutes = time_interval_seconds/60 time_series = np.arange(0, input_numpy_4d.shape[-1]) / (60 / time_interval_seconds) injection_start_time_seconds=38.4 T1_tissue=1000 T1_blood=1440 TR=3.8 flip_angle_degrees=25 relaxivity=.0045 hematocrit=.45 if input_aif == '': population_AIF = parker_model_AIF(scan_time_seconds, injection_start_time_seconds, time_interval_seconds, input_numpy_4d) concentration_AIF = population_AIF else: print 'extracting AIF...' AIF_label_numpy = convert_input_2_numpy(input_aif) AIF = generate_AIF(scan_time_seconds, injection_start_time_seconds, time_interval_seconds, input_numpy_4d, AIF_label_numpy) concentration_AIF = convert_intensity_to_concentration(AIF, T1_tissue, TR, flip_angle_degrees, injection_start_time_seconds, relaxivity, time_interval_seconds, hematocrit, T1_blood=T1_blood) for index in np.ndindex(input_numpy_ktrans.shape): output_numpy_4d[index] = np.array(estimate_concentration([input_numpy_ktrans[index],input_numpy_ve[index]], concentration_AIF, time_interval_minutes)) # Or load presaved.. # output_numpy_4d = nifti_2_numpy('DCE_MRI_Phantom_Regenerated_Concentrations.nii.gz') save_numpy_2_nifti(output_numpy_4d, input_4d_nifti, output_filepath + '_Regenerated_Concentrations.nii.gz') output_numpy_4d = revert_concentration_to_intensity(data_numpy=output_numpy_4d, reference_data_numpy=input_numpy_4d, T1_tissue=T1_tissue, TR=TR, flip_angle_degrees=flip_angle_degrees, injection_start_time_seconds=injection_start_time_seconds, relaxivity=relaxivity, time_interval_seconds=time_interval_seconds, hematocrit=hematocrit, T1_blood=0, T1_map = []) save_numpy_2_nifti(output_numpy_4d, input_4d_nifti, output_filepath + '_Regenerated_Signal.nii.gz') return
def return_connected_components(input_volume, mask_value=0, return_split=True, truncate=False, truncate_padding=0, output_filepath=None): """ This function takes in an N-dimensional array and uses scikit-image's measure.label function to split it into individual connected components. One can either return a split version of the original label, which will be stackd in a new batch dimension (N, ...), or return a renumbered version of the original label. One can also choose to truncate the output of the original image, instead returning a list of arrays of different sizes. Parameters ---------- input_volume: N-dimensional array The volume to be queried. mask_value: int or float Islands composed of "mask_value" will be ignored. return_split: bool Whether to a return a stacked output of equal-size binary arrays for each island, or to return one array with differently-labeled islands for each output. truncate: bool Whether or not to truncate the output. Irrelevant if return_split is False truncate_padding: int How many voxels of padding to leave when truncating. output_filepath: str If return_split is False, output will be saved to this file. If return_split is True, output will be save to this file with the suffix "_[#]" for island number Returns ------- output_array: N+1 or N-dimensional array Output array(s) depending on return_split """ image_numpy = convert_input_2_numpy(input_volume) connected_components = measure.label(image_numpy, background=mask_value, connectivity=2) if not return_split: if output_filepath is not None: save_numpy_2_nifti(connected_components, input_volume, output_filepath) return connected_components else: all_islands = split_image(connected_components) for island in all_islands: all_islands[island] = truncate_image( island, truncate_padding=truncate_padding) if output_filepath is not None: for island_idx, island in enumerate(all_islands): save_numpy_2_nifti( connected_components, input_volume, replace_suffix(output_filepath, '', str(island_idx))) return all_islands
def resample(input_data, output_filename='', input_transform=None, method="slicer", command="Slicer", temp_dir='./', interpolation='linear', dimensions=[1, 1, 1], reference_volume=None): """ A catch-all function for resampling. Will resample a 3D volume to given dimensions according to the method provided. TODO: Add resampling for 4D volumes. TODO: Add dimension, interpolation, reference parameter. Currently set to linear/isotropic. Parameters ---------- input_data: str or array Can be a 3D volume or a filename. output_filename: str Location to save output data to. If left as '', will return numpy array. input_transform: str detatails TBD, unimplemented method: str Will perform motion correction according to the provided method. Currently available: ['fsl'] command: str The literal command-line string to be inputted via Python's subprocess module. temp_dir: str If temporary files are created, they will be saved here. Returns ------- output: array Output data, only if output_filename is left as ''. """ skull_strip_methods = ['slicer'] if method not in skull_strip_methods: print( 'Input \"method\" parameter is not available. Available methods: ', skull_strip_methods) return if method == 'slicer': # A good reason to have a Class for qtim methods is to cut through all of this extra code. temp_input, temp_output = False, False if not isinstance(input_data, str): input_filename = os.path.join(temp_dir, 'temp.nii.gz') nifti_util.save_numpy_2_nifti(input_data, input_filename) temp_input = True else: input_filename = input_data if output_filename == '': temp_output = True output_filename = os.path.join(temp_dir, 'temp_out.nii.gz') dimensions = str(dimensions).strip('[]').replace(' ', '') if reference_volume or input_transform is not None: resample_command = [ command, '--launch', 'ResampleScalarVectorDWIVolume', input_filename, output_filename, '-R', reference_volume, '--interpolation', interpolation ] if input_transform is not None: resample_command += ['-f', input_transform] print(' '.join(resample_command)) subprocess.call(resample_command) else: resample_command = [ command, '--launch', 'ResampleScalarVolume', '-i', interpolation, '-s', dimensions, input_filename, output_filename ] print(' '.join(resample_command)) subprocess.call(resample_command) if temp_input: os.remove(input_filename) pass if temp_output: output = format_util.convert_input_2_numpy(output_filename) os.remove(output_filename) return output
def download_slices( data_directories, output_filepath='mri_slice.hdf5', modalities=['FLAIR_pp.nii.gz', 'T1post_pp.nii.gz', 'T2_pp.nii.gz'], preload_levels=True, levels=[4, 8, 16, 32, 64, 128, 256], verbose=True): max_dims = [0, 0, 0] patient_vols = [] for directory in data_directories: patients = glob.glob(os.path.join(directory, '*/')) for patient in patients: single_patient_vols = [] for modality in modalities: single_patient_vols += [ glob.glob(os.path.join(patient, modality))[0] ] patient_vols += [single_patient_vols] num_cases = 120 * len(patient_vols) hdf5_file = tables.open_file(output_filepath, mode='w') filters = tables.Filters(complevel=5, complib='blosc') hdf5_file.create_earray(hdf5_file.root, 'imagenames', tables.StringAtom(256), shape=(0, 1), filters=filters, expectedrows=num_cases) # If we want to pre-store different levels... if preload_levels: for dimension in levels: data_shape = (0, dimension, dimension, 3) hdf5_file.create_earray(hdf5_file.root, 'data_' + str(dimension), tables.Float32Atom(), shape=data_shape, filters=filters, expectedrows=num_cases) for p_idx, single_patient_vols in enumerate(patient_vols): filename = os.path.basename(os.path.dirname(single_patient_vols[0])) hdf5_file.root.imagenames.append( np.array(filename)[np.newaxis][np.newaxis]) for dimension in levels: try: if verbose: print 'Processing...', os.path.basename( filename), 'at', dimension, ', idx', p_idx volumes = np.stack([ convert_input_2_numpy(vol) for vol in single_patient_vols ], axis=3) for z in xrange(volumes.shape[2]): data = volumes[:, :, z, :] data = imresize(data, (dimension, dimension)) getattr(hdf5_file.root, 'data_' + str(dimension)).append(data[np.newaxis]) except KeyboardInterrupt: raise except: raise # print 'ERROR converting', filename, 'at dimension', dimension hdf5_file.close()
def create_mosaic(input_volume, outfile=None, label_volume=None, generate_outline=True, mask_value=0, step=1, dim=2, cols=8, label_buffer=5, rotate_90=3, flip=True, dpi=100): """This creates a mosaic of 2D images from a 3D Volume. Parameters ---------- input_volume : TYPE Any neuroimaging file with a filetype supported by qtim_tools, or existing numpy array. outfile : None, optional Where to save your output, in a filetype supported by matplotlib (e.g. .png). If label_volume : None, optional Whether to create your mosaic with an attached label filepath / numpy array. Will not perform volume transforms from header (yet) generate_outline : bool, optional If True, will generate outlines for label_volumes, instead of filled-in areas. Default is True. mask_value : int, optional Background value for label volumes. Default is 0. step : int, optional Will generate an image for every [step] slice. Default is 1. dim : int, optional Mosaic images will be sliced along this dimension. Default is 2, which often corresponds to axial. cols : int, optional How many columns in your output mosaic. Rows will be determined automatically. Default is 8. label_buffer : int, optional Images more than [label_buffer] slices away from a slice containing a label pixel will note be included. Default is 5. rotate_90 : int, optional If the output mosaic is incorrectly rotated, you may rotate clockwise [rotate_90] times. Default is 3. flip : bool, optional If the output is incorrectly flipped, you may set to True to flip the data. Default is True. No Longer Returned ------------------ Returns ------- output_array: N+1 or N-dimensional array The generated mosaic array. """ image_numpy = convert_input_2_numpy(input_volume) if step is None: step = 1 if label_volume is not None: label_numpy = convert_input_2_numpy(label_volume) if generate_outline: label_numpy = generate_label_outlines(label_numpy, dim, mask_value) # This is fun in a wacky way, but could probably be done more concisely and effeciently. mosaic_selections = [] for i in range(label_numpy.shape[dim]): label_slice = np.squeeze(label_numpy[[ slice(None) if k != dim else slice(i, i + 1) for k in range(3) ]]) if np.sum(label_slice) != 0: mosaic_selections += list( range(i - label_buffer, i + label_buffer)) mosaic_selections = np.unique(mosaic_selections) mosaic_selections = mosaic_selections[mosaic_selections >= 0] mosaic_selections = mosaic_selections[ mosaic_selections <= image_numpy.shape[dim]] mosaic_selections = mosaic_selections[::step] color_range_image = [np.min(image_numpy), np.max(image_numpy)] color_range_label = [np.min(label_numpy), np.max(label_numpy)] # One day, specify rotations by affine matrix. # Is test slice necessary? Operate directly on shape if possible. test_slice = np.rot90( np.squeeze(image_numpy[[ slice(None) if k != dim else slice(0, 1) for k in range(3) ]]), rotate_90) slice_width = test_slice.shape[1] slice_height = test_slice.shape[0] mosaic_image_numpy = np.zeros( (int(slice_height * np.ceil(float(len(mosaic_selections)) / float(cols))), int(test_slice.shape[1] * cols)), dtype=float) mosaic_label_numpy = np.zeros_like(mosaic_image_numpy) row_index = 0 col_index = 0 for i in mosaic_selections: image_slice = np.rot90( np.squeeze(image_numpy[[ slice(None) if k != dim else slice(i, i + 1) for k in range(3) ]]), rotate_90) label_slice = np.rot90( np.squeeze(label_numpy[[ slice(None) if k != dim else slice(i, i + 1) for k in range(3) ]]), rotate_90) # Again, specify from affine matrix if possible. if flip: image_slice = np.fliplr(image_slice) label_slice = np.fliplr(label_slice) if image_slice.size > 0: mosaic_image_numpy[ int(row_index):int(row_index + slice_height), int(col_index):int(col_index + slice_width)] = image_slice mosaic_label_numpy[ int(row_index):int(row_index + slice_height), int(col_index):int(col_index + slice_width)] = label_slice if col_index == mosaic_image_numpy.shape[1] - slice_width: col_index = 0 row_index += slice_height else: col_index += slice_width mosaic_label_numpy = np.ma.masked_where(mosaic_label_numpy == 0, mosaic_label_numpy) if outfile is not None: fig = plt.figure(figsize=(mosaic_image_numpy.shape[0] / 100, mosaic_image_numpy.shape[1] / 100), dpi=100, frameon=False) plt.margins(0, 0) plt.gca().set_axis_off() plt.gca().xaxis.set_major_locator(plt.NullLocator()) plt.gca().yaxis.set_major_locator(plt.NullLocator()) plt.imshow(mosaic_image_numpy, 'gray', vmin=color_range_image[0], vmax=color_range_image[1], interpolation='none') plt.imshow(mosaic_label_numpy, 'jet', vmin=color_range_label[0], vmax=color_range_label[1], interpolation='none') plt.savefig(outfile, bbox_inches='tight', pad_inches=0.0, dpi=1000) plt.clf() plt.close() return mosaic_image_numpy else: color_range_image = [np.min(image_numpy), np.max(image_numpy)] test_slice = np.rot90( np.squeeze(image_numpy[[ slice(None) if k != dim else slice(0, 1) for k in range(3) ]]), rotate_90) slice_width = test_slice.shape[1] slice_height = test_slice.shape[0] mosaic_selections = np.arange(image_numpy.shape[dim])[::step] mosaic_image_numpy = np.zeros( (int(slice_height * np.ceil(float(len(mosaic_selections)) / float(cols))), int(test_slice.shape[1] * cols)), dtype=float) row_index = 0 col_index = 0 for i in mosaic_selections: image_slice = np.squeeze(image_numpy[[ slice(None) if k != dim else slice(i, i + 1) for k in range(3) ]]) image_slice = np.rot90(image_slice, rotate_90) if flip: image_slice = np.fliplr(image_slice) mosaic_image_numpy[int(row_index):int(row_index + slice_height), int(col_index):int(col_index + slice_width)] = image_slice if col_index == mosaic_image_numpy.shape[1] - slice_width: col_index = 0 row_index += slice_height else: col_index += slice_width if outfile is not None: fig = plt.figure(figsize=(mosaic_image_numpy.shape[0] / 100, mosaic_image_numpy.shape[1] / 100), dpi=100, frameon=False) plt.margins(0, 0) plt.gca().set_axis_off() plt.gca().xaxis.set_major_locator(plt.NullLocator()) plt.gca().yaxis.set_major_locator(plt.NullLocator()) plt.imshow(mosaic_image_numpy, 'gray', vmin=color_range_image[0], vmax=color_range_image[1], interpolation='none') plt.savefig(outfile, bbox_inches='tight', pad_inches=0.0, dpi=dpi) plt.clf() plt.close() return mosaic_image_numpy
def store_preloaded_hdf5_file( data_directories, output_filepath, modalities=['FLAIR_pp.nii.gz', 'T1post_pp.nii.gz'], label='full_edemamask_pp.nii.gz', verbose=True, levels=[4, 8, 16, 32, 64, 128], boundary_padding=10, max_dimension=64, samples_per_patient=100, preload_levels=False, wholevolume=False): patient_vols = [] for directory in data_directories: patients = glob.glob(os.path.join(directory, '*/')) for patient in patients: single_patient_vols = [] for modality in modalities + [label]: if modality is None: continue single_patient_vols += [ glob.glob(os.path.join(patient, modality))[0] ] patient_vols += [single_patient_vols] if wholevolume: num_cases = len(patient_vols) else: num_cases = len(modalities) * len(patient_vols) hdf5_file = tables.open_file(output_filepath, mode='w') filters = tables.Filters(complevel=5, complib='blosc') hdf5_file.create_earray(hdf5_file.root, 'imagenames', tables.StringAtom(256), shape=(0, 1), filters=filters, expectedrows=num_cases) # If we want to pre-store different levels... if preload_levels: for dimension in levels: data_shape = (0, dimension + boundary_padding, dimension + boundary_padding, dimension + boundary_padding, 2) hdf5_file.create_earray(hdf5_file.root, 'data_' + str(dimension), tables.Float32Atom(), shape=data_shape, filters=filters, expectedrows=num_cases) else: # If we don't. if wholevolume: data_shape = (0, 200, 200, 200, len(modalities)) else: data_shape = (0, max_dimension + boundary_padding, max_dimension + boundary_padding, max_dimension + boundary_padding, len(modalities)) print data_shape hdf5_file.create_earray(hdf5_file.root, 'data', tables.Float32Atom(), shape=data_shape, filters=filters, expectedrows=num_cases) for p_idx, single_patient_vols in enumerate(patient_vols): hdf5_file.root.imagenames.append( np.array(os.path.basename(os.path.dirname( single_patient_vols[0])))[np.newaxis][np.newaxis]) print os.path.basename(os.path.dirname(single_patient_vols[0])) if label is not None: # Find tumor label center of mass label = single_patient_vols[-1] label_numpy = convert_input_2_numpy(label) label_center = [int(x) for x in center_of_mass(label_numpy)] # Load volumes, volumes = np.stack([ convert_input_2_numpy(vol) for vol in single_patient_vols[:-1] ], axis=3) # pad if necessary, using black magic pad_dims = [] radius = (max_dimension + boundary_padding) / 2 for idx, dim in enumerate(volumes.shape[:-1]): padding = (-1 * min(0, label_center[idx] - radius), -1 * min(0, dim - (label_center[idx] + radius))) pad_dims += [padding] pad_dims += [(0, 0)] print pad_dims volumes = np.pad(volumes, pad_dims, mode='constant') # and subsample, with more black magic ;) print label_center label_center = [ x + pad_dims[i][0] for i, x in enumerate(label_center) ] print label_center print volumes.shape patch = volumes[label_center[0] - radius:label_center[0] + radius, label_center[1] - radius:label_center[1] + radius, label_center[2] - radius:label_center[2] + radius, :] print patch.shape # Add to HDF5 getattr(hdf5_file.root, 'data').append(patch[np.newaxis]) save_numpy_2_nifti( patch[..., 1], single_patient_vols[0], os.path.join(os.path.dirname(single_patient_vols[0]), 'gan_patch.nii.gz')) elif wholevolume: # Load volumes, volumes = np.stack( [convert_input_2_numpy(vol) for vol in single_patient_vols], axis=3) # Crop volumes volumes = crop2(volumes) large = False # Skip strangely processed volumes for dim in volumes.shape: if dim > 200: large = True if large: continue same_size_volume = np.zeros((200, 200, 200, len(modalities))) same_size_volume[0:volumes.shape[0], 0:volumes.shape[1], 0:volumes.shape[2], :] = volumes # Add to HDF5 getattr(hdf5_file.root, 'data').append(same_size_volume[np.newaxis]) else: # Generic MRI patching goes on here.. continue if verbose: print 'Processed...', os.path.basename( os.path.dirname(single_patient_vols[0])), 'idx', p_idx # except KeyboardInterrupt: # raise # except: # print 'ERROR converting', filepath, 'at dimension', dimension hdf5_file.close() return