def ifolder2tmp(self): # copy input image if self.fname_mask is not None: copy(self.fname_mask, self.tmp_dir) self.fname_mask = ''.join(extract_fname(self.fname_mask)[1:]) else: printv('ERROR: No input image', self.verbose, 'error') # copy seg image if self.fname_sc is not None: copy(self.fname_sc, self.tmp_dir) self.fname_sc = ''.join(extract_fname(self.fname_sc)[1:]) # copy ref image if self.fname_ref is not None: copy(self.fname_ref, self.tmp_dir) self.fname_ref = ''.join(extract_fname(self.fname_ref)[1:]) # copy registered template if self.path_template is not None: copy(self.path_levels, self.tmp_dir) self.path_levels = ''.join(extract_fname(self.path_levels)[1:]) self.atlas_roi_lst = [] for fname_atlas_roi in os.listdir(self.path_atlas): if fname_atlas_roi.endswith('.nii.gz'): tract_id = int( fname_atlas_roi.split('_')[-1].split('.nii.gz')[0]) if tract_id < 36: # Not interested in CSF copy(os.path.join(self.path_atlas, fname_atlas_roi), self.tmp_dir) self.atlas_roi_lst.append(fname_atlas_roi) os.chdir(self.tmp_dir) # go to tmp directory
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) # create param objects param = Param() # set param arguments ad inputted by user list_fname_src = arguments.i fname_dest = arguments.d list_fname_warp = arguments.w param.fname_out = arguments.o # if arguments.ofolder is not None # path_results = arguments.ofolder if arguments.x is not None: param.interp = arguments.x if arguments.r is not None: param.rm_tmp = arguments.r # check if list of input files and warping fields have same length assert len(list_fname_src) == len( list_fname_warp), "ERROR: list of files are not of the same length" # merge src images to destination image try: merge_images(list_fname_src, fname_dest, list_fname_warp, param) except Exception as e: printv(str(e), 1, 'error') display_viewer_syntax([fname_dest, os.path.abspath(param.fname_out)])
def _measure_volume(self, im_data, p_lst, idx): for zz in range(im_data.shape[2]): self.volumes[zz, idx - 1] = np.sum(im_data[:, :, zz]) * p_lst[0] * p_lst[1] * p_lst[2] vol_tot_cur = np.sum(self.volumes[:, idx - 1]) self.measure_pd.loc[idx, 'volume [mm3]'] = vol_tot_cur printv(' Volume : ' + str(np.round(vol_tot_cur, 2)) + ' mm^3', self.verbose, type='info')
def main(args=None): """ Main function :param args: :return: """ # get parser args if args is None: args = None if sys.argv[1:] else ['--help'] parser = get_parser() arguments = parser.parse_args(args=args) param = Param() param.fname_data = os.path.abspath(arguments.i) if arguments.p is not None: param.process = (arguments.p).split(',') if param.process[0] not in param.process_list: printv(parser.error('ERROR: Process ' + param.process[0] + ' is not recognized.')) if arguments.size is not None: param.size = arguments.size if arguments.f is not None: param.shape = arguments.f if arguments.o is not None: param.fname_out = os.path.abspath(arguments.o) if arguments.r is not None: param.remove_temp_files = arguments.r param.verbose = arguments.v init_sct(log_level=param.verbose, update=True) # Update log level # run main program create_mask(param)
def update(self, param_user): # list_objects = param_user.split(',') for object in param_user: if len(object) < 2: printv('ERROR: Wrong usage.', 1, type='error') obj = object.split('=') setattr(self, obj[0], obj[1])
def mean_angle(self): im_metric_lst = [ self.fname_metric_lst[f].split('_' + str(self.param_glcm.distance) + '_')[0] + '_' for f in self.fname_metric_lst ] im_metric_lst = list(set(im_metric_lst)) printv('\nMean across angles...', self.param.verbose, 'normal') extension = extract_fname(self.param.fname_im)[2] for im_m in im_metric_lst: # Loop across GLCM texture properties # List images to mean im2mean_lst = [ im_m + str(self.param_glcm.distance) + '_' + a + extension for a in self.param_glcm.angle.split(',') ] # Average across angles and save it as wrk_folder/fnameIn_feature_distance_mean.extension fname_out = im_m + str( self.param_glcm.distance) + '_mean' + extension run_proc('sct_image -i ' + ' '.join(im2mean_lst) + ' -concat t -o ' + fname_out) run_proc('sct_maths -i ' + fname_out + ' -mean t -o ' + fname_out) self.fname_metric_lst[im_m + str(self.param_glcm.distance) + '_mean'] = fname_out
def main(argv): parser = get_parser() args = parser.parse_args(argv if argv else ['--help']) # Deal with task if args.list_tasks: deepseg.models.display_list_tasks() if args.install_task is not None: for name_model in deepseg.models.TASKS[args.install_task]['models']: deepseg.models.install_model(name_model) exit(0) # Deal with input/output if not os.path.isfile(args.i): parser.error("This file does not exist: {}".format(args.i)) # Check if at least a model or task has been specified if args.task is None: parser.error("You need to specify a task.") # Get pipeline model names name_models = deepseg.models.TASKS[args.task]['models'] # Run pipeline by iterating through the models fname_prior = None for name_model in name_models: # Check if this is an official model if name_model in list(deepseg.models.MODELS.keys()): # If it is, check if it is installed path_model = deepseg.models.folder(name_model) if not deepseg.models.is_valid(path_model): printv("Model {} is not installed. Installing it now...".format(name_model)) deepseg.models.install_model(name_model) # If it is not, check if this is a path to a valid model else: path_model = os.path.abspath(name_model) if not deepseg.models.is_valid(path_model): parser.error("The input model is invalid: {}".format(path_model)) # Call segment_nifti options = {**vars(args), "fname_prior": fname_prior} nii_seg = imed.utils.segment_volume(path_model, args.i, options=options) # Save output seg if 'o' in options and options['o'] is not None: fname_seg = options['o'] else: fname_seg = ''.join([sct.image.splitext(args.i)[0], '_seg.nii.gz']) # If output folder does not exist, create it path_out = os.path.dirname(fname_seg) if not (path_out == '' or os.path.exists(path_out)): os.makedirs(path_out) nib.save(nii_seg, fname_seg) # Use the result of the current model as additional input of the next model fname_prior = fname_seg display_viewer_syntax([args.i, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
def vertebral_detection_param(string): """Custom parser for vertebral_detection advanced parameters.""" param = param_default.copy() for key_value in string.split(','): try: key, value = key_value.split('=', maxsplit=1) except ValueError: raise argparse.ArgumentTypeError( f'advanced parameters should be of the form "parameter=value", got "{key_value}" instead' ) if key in param: try: param[key] = int(value) except ValueError: raise argparse.ArgumentTypeError( f'advanced parameter "{key}" needs an integer value, got "{value}" instead' ) elif key == 'gaussian_std': # TODO(issue#3706): remove 'gaussian_std' completely for v5.7 printv( 'WARNING: gaussian_std parameter is currently ignored, ' 'and will be removed in a later version.', 1, type='warning') else: raise argparse.ArgumentTypeError( f'Unknown advanced parameter: {key}') return param
def main(): # create param objects param = Param() # get parser parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) # set param arguments ad inputted by user list_fname_src = arguments.i fname_dest = arguments.d list_fname_warp = arguments.w param.fname_out = arguments.o # if arguments.ofolder is not None # path_results = arguments.ofolder if arguments.x is not None: param.interp = arguments.x if arguments.r is not None: param.rm_tmp = arguments.r param.verbose = arguments.v init_sct(log_level=param.verbose, update=True) # Update log level # check if list of input files and warping fields have same length assert len(list_fname_src) == len( list_fname_warp), "ERROR: list of files are not of the same length" # merge src images to destination image try: merge_images(list_fname_src, fname_dest, list_fname_warp, param) except Exception as e: printv(str(e), 1, 'error') display_viewer_syntax([fname_dest, os.path.abspath(param.fname_out)])
def main(argv=None): """ Main function :param argv: :return: """ parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) param = Param() param.fname_data = os.path.abspath(arguments.i) if arguments.p is not None: param.process = (arguments.p).split(',') if param.process[0] not in param.process_list: printv( parser.error('ERROR: Process ' + param.process[0] + ' is not recognized.')) if arguments.size is not None: param.size = arguments.size if arguments.f is not None: param.shape = arguments.f if arguments.o is not None: param.fname_out = os.path.abspath(arguments.o) if arguments.r is not None: param.remove_temp_files = arguments.r # run main program create_mask(param)
def normalize_slice(data, data_gm, data_wm, val_gm, val_wm, val_min=None, val_max=None): ''' Function to normalize the intensity of data to the GM and WM values given by val_gm and val_wm. All parameters are arrays Parameters ---------- data : ndarray: data to normalized data_gm : ndarray: data to get slice GM value from data_wm : ndarray: data to get slice WM value from val_gm : GM value to normalize data on val_wm : WM value to normalize data on Returns ------- ''' assert data.size == data_gm.size, "Data to normalized and GM data do not have the same shape." assert data.size == data_wm.size, "Data to normalized and WM data do not have the same shape." # avoid shape error because of 3D-like shape for 2D (x, x, 1) instead of (x,x) data_gm = data_gm.reshape(data.shape) data_wm = data_wm.reshape(data.shape) # put almost zero background to zero data[data < 0.01] = 0 # binarize GM and WM data if needed if np.min(data_gm) != 0 or np.max(data_gm) != 1: data_gm[data_gm < 0.5] = 0 data_gm[data_gm >= 0.5] = 1 if np.min(data_wm) != 0 or np.max(data_wm) != 1: data_wm[data_wm < 0.5] = 0 data_wm[data_wm >= 0.5] = 1 # get GM and WM values in slice data_in_gm = data[data_gm == 1] data_in_wm = data[data_wm == 1] med_data_gm = np.median(data_in_gm) med_data_wm = np.median(data_in_wm) std_data = np.mean([np.std(data_in_gm), np.std(data_in_wm)]) # compute normalized data # if median values are too close: use min and max to normalize data if abs(med_data_gm - med_data_wm) < std_data and val_min is not None and val_max is not None: try: min_data = min(np.min(data_in_gm[data_in_gm.nonzero()]), np.min(data_in_wm[data_in_wm.nonzero()])) max_data = max(np.max(data_in_gm[data_in_gm.nonzero()]), np.max(data_in_wm[data_in_wm.nonzero()])) new_data = ((data - min_data) * (val_max - val_min) / (max_data - min_data)) + val_min except ValueError: printv('WARNING: an incomplete slice will not be normalized', 1, 'warning') return data # else (=normal data): use median values to normalize data else: new_data = ((data - med_data_wm) * (val_gm - val_wm) / (med_data_gm - med_data_wm)) + val_wm # put almost zero background to zero new_data[data < 0.01] = 0 # put at 0 the background new_data[new_data < 0.01] = 0 # put at 0 the background # return normalized data return new_data
def tmp2ofolder(self): os.chdir(self.curdir) # go back to original directory printv('\nSave resulting files...', self.param.verbose, 'normal') for f in self.fname_metric_lst: # Copy from tmp folder to ofolder copy(os.path.join(self.tmp_dir, self.fname_metric_lst[f]), os.path.join(self.param.path_results, self.fname_metric_lst[f]))
def main(): parser = get_parser() args = parser.parse_args(args=None if sys.argv[1:] else ['--help']) # Deal with model if args.list_models: deepseg.models.display_list_models() # Deal with task if args.list_tasks: deepseg.models.display_list_tasks() if args.install_model is not None: deepseg.models.install_model(args.install_model) exit(0) if args.install_task is not None: for name_model in deepseg.models.TASKS[args.install_task]['models']: deepseg.models.install_model(name_model) exit(0) # Deal with input/output if not os.path.isfile(args.i): parser.error("This file does not exist: {}".format(args.i)) # Check if at least a model or task has been specified if args.model is None and args.task is None: parser.error("You need to specify a model or a task.") # Get pipeline model names if args.task is not None: name_models = deepseg.models.TASKS[args.task]['models'] if args.model is not None: name_models = args.model # Run pipeline by iterating through the models fname_prior = None for name_model in name_models: # Check if this is an official model if name_model in list(deepseg.models.MODELS.keys()): # If it is, check if it is installed path_model = deepseg.models.folder(name_model) if not deepseg.models.is_valid(path_model): printv("Model {} is not installed. Installing it now...".format(name_model)) deepseg.models.install_model(name_model) # If it is not, check if this is a path to a valid model else: path_model = os.path.abspath(name_model) if not deepseg.models.is_valid(path_model): parser.error("The input model is invalid: {}".format(path_model)) # Call segment_nifti fname_seg = deepseg.core.segment_nifti(args.i, path_model, fname_prior, vars(args)) # Use the result of the current model as additional input of the next model fname_prior = fname_seg display_viewer_syntax([args.i, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
def _measure_length(self, im_data, p_lst, idx): length_cur = np.sum([ np.cos(self.angles[zz]) * p_lst[2] for zz in np.unique(np.where(im_data)[2]) ]) self.measure_pd.loc[idx, 'length [mm]'] = length_cur printv(' (S-I) length : ' + str(np.round(length_cur, 2)) + ' mm', self.verbose, type='info')
def label_lesion(self): printv('\nLabel connected regions of the masked image...', self.verbose, 'normal') im = Image(self.fname_mask) im_2save = im.copy() im_2save.data = label(im.data, connectivity=2) im_2save.save(self.fname_label) self.measure_pd['label'] = [l for l in np.unique(im_2save.data) if l] printv('Lesion count = ' + str(len(self.measure_pd['label'])), self.verbose, 'info')
def resample_image(fname, suffix='_resampled.nii.gz', binary=False, npx=0.3, npy=0.3, thr=0.0, interpolation='spline'): """ Resampling function: add a padding, resample, crop the padding :param fname: name of the image file to be resampled :param suffix: suffix added to the original fname after resampling :param binary: boolean, image is binary or not :param npx: new pixel size in the x direction :param npy: new pixel size in the y direction :param thr: if the image is binary, it will be thresholded at thr (default=0) after the resampling :param interpolation: type of interpolation used for the resampling :return: file name after resampling (or original fname if it was already in the correct resolution) """ im_in = Image(fname) orientation = im_in.orientation if orientation != 'RPI': im_in.change_orientation('RPI') fname = add_suffix(im_in.absolutepath, "_rpi") im_in.save(path=fname, mutable=True) nx, ny, nz, nt, px, py, pz, pt = im_in.dim if np.round(px, 2) != np.round(npx, 2) or np.round(py, 2) != np.round(npy, 2): name_resample = extract_fname(fname)[1] + suffix if binary: interpolation = 'nn' if nz == 1: # when data is 2d: we convert it to a 3d image in order to avoid conversion problem with 2d data # TODO: check if this above problem is still present (now that we are using nibabel instead of nipy) run_proc(['sct_image', '-i', ','.join([fname, fname]), '-concat', 'z', '-o', fname]) run_proc(['sct_resample', '-i', fname, '-mm', str(npx) + 'x' + str(npy) + 'x' + str(pz), '-o', name_resample, '-x', interpolation]) if nz == 1: # when input data was 2d: re-convert data 3d-->2d run_proc(['sct_image', '-i', name_resample, '-split', 'z']) im_split = Image(name_resample.split('.nii.gz')[0] + '_Z0000.nii.gz') im_split.save(name_resample) if binary: img = Image(name_resample) img.data = binarize(img.data, thr) img.save() if orientation != 'RPI': img = Image(name_resample) img.change_orientation(orientation) name_resample = add_suffix(img.absolutepath, "_{}".format(orientation.lower())) img.save(path=name_resample, mutable=True) return name_resample else: if orientation != 'RPI': fname = add_suffix(fname, "_RPI") im_in = change_orientation(im_in, orientation).save(fname) printv('Image resolution already ' + str(npx) + 'x' + str(npy) + 'xpz') return fname
def main(): parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) # Set param arguments ad inputted by user fname_in = arguments.i contrast = arguments.c # Segmentation or Centerline line if arguments.s is not None: fname_seg = arguments.s if not os.path.isfile(fname_seg): fname_seg = None printv('WARNING: -s input file: "' + arguments.s + '" does not exist.\nDetecting PMJ without using segmentation information', 1, 'warning') else: fname_seg = None # Output Folder if arguments.ofolder is not None: path_results = arguments.ofolder if not os.path.isdir(path_results) and os.path.exists(path_results): printv("ERROR output directory %s is not a valid directory" % path_results, 1, 'error') if not os.path.exists(path_results): os.makedirs(path_results) else: path_results = '.' path_qc = arguments.qc # Remove temp folder rm_tmp = bool(arguments.r) verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level # Initialize DetectPMJ detector = DetectPMJ(fname_im=fname_in, contrast=contrast, fname_seg=fname_seg, path_out=path_results, verbose=verbose) # run the extraction fname_out, tmp_dir = detector.apply() # Remove tmp_dir if rm_tmp: rmtree(tmp_dir) # View results if fname_out is not None: if path_qc is not None: from spinalcordtoolbox.reports.qc import generate_qc generate_qc(fname_in, fname_seg=fname_out, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), process='sct_detect_pmj') display_viewer_syntax([fname_in, fname_out], colormaps=['gray', 'red'])
def measure(self): im_lesion = Image(self.fname_label) im_lesion_data = im_lesion.data p_lst = im_lesion.dim[4:7] # voxel size label_lst = [l for l in np.unique(im_lesion_data) if l] # lesion label IDs list if self.path_template is not None: if os.path.isfile(self.path_levels): img_vert = Image(self.path_levels) im_vert_data = img_vert.data self.vert_lst = [v for v in np.unique(im_vert_data) if v] # list of vertebral levels available in the input image else: im_vert_data = None printv('ERROR: the file ' + self.path_levels + ' does not exist. Please make sure the template was correctly registered and warped (sct_register_to_template or sct_register_multimodal and sct_warp_template)', type='error') # In order to open atlas images only one time atlas_data_dct = {} # dict containing the np.array of the registrated atlas for fname_atlas_roi in self.atlas_roi_lst: tract_id = int(fname_atlas_roi.split('_')[-1].split('.nii.gz')[0]) img_cur = Image(fname_atlas_roi) img_cur_copy = img_cur.copy() atlas_data_dct[tract_id] = img_cur_copy.data del img_cur self.volumes = np.zeros((im_lesion.dim[2], len(label_lst))) # iteration across each lesion to measure statistics for lesion_label in label_lst: im_lesion_data_cur = np.copy(im_lesion_data == lesion_label) printv('\nMeasures on lesion #' + str(lesion_label) + '...', self.verbose, 'normal') label_idx = self.measure_pd[self.measure_pd.label == lesion_label].index self._measure_volume(im_lesion_data_cur, p_lst, label_idx) self._measure_length(im_lesion_data_cur, p_lst, label_idx) self._measure_diameter(im_lesion_data_cur, p_lst, label_idx) # compute lesion distribution for each lesion if self.path_template is not None: self._measure_eachLesion_distribution(lesion_id=lesion_label, atlas_data=atlas_data_dct, im_vert=im_vert_data, im_lesion=im_lesion_data_cur, p_lst=p_lst) if self.path_template is not None: # compute total lesion distribution self._measure_totLesion_distribution(im_lesion=np.copy(im_lesion_data > 0), atlas_data=atlas_data_dct, im_vert=im_vert_data, p_lst=p_lst) if self.fname_ref is not None: # Compute mean and std value in each labeled lesion self._measure_within_im(im_lesion=im_lesion_data, im_ref=Image(self.fname_ref).data, label_lst=label_lst)
def update(self, param_user): list_objects = param_user.split(',') for object in list_objects: if len(object) < 2: printv('ERROR: Wrong usage.', 1, type='error') obj = object.split('=') if obj[0] == 'gaussian_std': setattr(self, obj[0], float(obj[1])) else: setattr(self, obj[0], int(obj[1]))
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) param = Param() fname_src = arguments.d fname_transfo = arguments.w warp_atlas = arguments.a warp_spinal_levels = arguments.s folder_out = arguments.ofolder path_template = arguments.t path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject folder_template = param.folder_template folder_atlas = param.folder_atlas folder_spinal_levels = param.folder_spinal_levels file_info_label = param.file_info_label list_labels_nn = param.list_labels_nn # call main function w = WarpTemplate(fname_src, fname_transfo, warp_atlas, warp_spinal_levels, folder_out, path_template, folder_template, folder_atlas, folder_spinal_levels, file_info_label, list_labels_nn, verbose) path_template = os.path.join(w.folder_out, w.folder_template) # Deal with QC report if path_qc is not None: try: fname_wm = os.path.join( w.folder_out, w.folder_template, spinalcordtoolbox.metadata.get_file_label(path_template, id_label=4)) # label = 'white matter mask (probabilistic)' generate_qc( fname_src, fname_seg=fname_wm, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_warp_template') # If label is missing, get_file_label() throws a RuntimeError except RuntimeError: printv("QC not generated since expected labels are missing from template", type="warning") # Deal with verbose try: display_viewer_syntax( [fname_src, spinalcordtoolbox.metadata.get_file_label(path_template, id_label=1, output="filewithpath"), # label = 'T2-weighted template' spinalcordtoolbox.metadata.get_file_label(path_template, id_label=5, output="filewithpath"), # label = 'gray matter mask (probabilistic)' spinalcordtoolbox.metadata.get_file_label(path_template, id_label=4, output="filewithpath")], # label = 'white matter mask (probabilistic)' colormaps=['gray', 'gray', 'red-yellow', 'blue-lightblue'], opacities=['1', '1', '0.5', '0.5'], minmax=['', '0,4000', '0.4,1', '0.4,1'], verbose=verbose) # If label is missing, continue silently except RuntimeError: pass
def _measure_diameter(self, im_data, p_lst, idx): area_lst = [ np.sum(im_data[:, :, zz]) * np.cos(self.angles[zz]) * p_lst[0] * p_lst[1] for zz in range(im_data.shape[2]) ] diameter_cur = 2 * np.sqrt(max(area_lst) / (4 * np.pi)) self.measure_pd.loc[idx, 'max_equivalent_diameter [mm]'] = diameter_cur printv(' Max. equivalent diameter : ' + str(np.round(diameter_cur, 2)) + ' mm', self.verbose, type='info')
def detect(self): """Run the classifier on self.slice2D_im.""" printv('\nRun PMJ detector', self.verbose, 'normal') os.environ["FSLOUTPUTTYPE"] = "NIFTI_PAIR" cmd_pmj = ['isct_spine_detect', self.pmj_model, self.slice2D_im.split('.nii')[0], self.dection_map_pmj] print(cmd_pmj) run_proc(cmd_pmj, verbose=0, is_sct_binary=True) img = nib.load(self.dection_map_pmj + '_svm.hdr') # convert .img and .hdr files to .nii nib.save(img, self.dection_map_pmj + '.nii') self.dection_map_pmj += '.nii' # fname of the resulting detection map
def _measure_within_im(self, im_lesion, im_ref, label_lst): printv('\nCompute reference image features...', self.verbose, 'normal') for lesion_label in label_lst: im_label_data_cur = im_lesion == lesion_label im_label_data_cur[np.where(im_ref == 0)] = 0 # if the ref object is eroded compared to the labeled object mean_cur, std_cur = np.mean(im_ref[np.where(im_label_data_cur)]), np.std(im_ref[np.where(im_label_data_cur)]) label_idx = self.measure_pd[self.measure_pd.label == lesion_label].index self.measure_pd.loc[label_idx, 'mean_' + extract_fname(self.fname_ref)[1]] = mean_cur self.measure_pd.loc[label_idx, 'std_' + extract_fname(self.fname_ref)[1]] = std_cur printv('Mean+/-std of lesion #' + str(lesion_label) + ' in ' + extract_fname(self.fname_ref)[1] + ' file: ' + str(np.round(mean_cur, 2)) + '+/-' + str(np.round(std_cur, 2)), self.verbose, type='info')
def tmp2ofolder(self): """Copy output files to the ofolder.""" os.chdir(self.curdir) # go back to original directory if self.pa_coord != -1: # If PMJ has been detected printv('\nSave resulting file...', self.verbose, 'normal') copy(os.path.abspath(os.path.join(self.tmp_dir, self.fname_out)), os.path.abspath(os.path.join(self.path_out, self.fname_out))) return os.path.join(self.path_out, self.fname_out) else: return None
def __init__(self, fname_mask, fname_sc, fname_ref, path_template, path_ofolder, verbose): self.fname_mask = fname_mask self.fname_sc = fname_sc self.fname_ref = fname_ref self.path_template = path_template self.path_ofolder = path_ofolder self.verbose = verbose self.wrk_dir = os.getcwd() if not set(np.unique(Image(fname_mask).data)) == set([0.0, 1.0]): if set(np.unique(Image(fname_mask).data)) == set([0.0]): printv('WARNING: Empty masked image', self.verbose, 'warning') else: printv("ERROR input file %s is not binary file with 0 and 1 values" % fname_mask, 1, 'error') # create tmp directory self.tmp_dir = tmp_create() # path to tmp directory # lesion file where each lesion has a different value self.fname_label = extract_fname(self.fname_mask)[1] + '_label' + extract_fname(self.fname_mask)[2] # initialization of measure sheet measure_lst = ['label', 'volume [mm3]', 'length [mm]', 'max_equivalent_diameter [mm]'] if self.fname_ref is not None: for measure in ['mean', 'std']: measure_lst.append(measure + '_' + extract_fname(self.fname_ref)[1]) measure_dct = {} for column in measure_lst: measure_dct[column] = None self.measure_pd = pd.DataFrame(data=measure_dct, index=range(0), columns=measure_lst) # orientation of the input image self.orientation = None # volume object self.volumes = None # initialization of proportion measures, related to registrated atlas if self.path_template is not None: self.path_atlas = os.path.join(self.path_template, "atlas") self.path_levels = os.path.join(self.path_template, "template", "PAM50_levels.nii.gz") else: self.path_atlas, self.path_levels = None, None self.vert_lst = None self.atlas_roi_lst = None self.distrib_matrix_dct = {} # output names self.pickle_name = extract_fname(self.fname_mask)[1] + '_analyzis.pkl' self.excel_name = extract_fname(self.fname_mask)[1] + '_analyzis.xls'
def show_total_results(self): printv('\n\nAveraged measures...', self.verbose, 'normal') for stg, key in zip([' Volume [mm^3] = ', ' (S-I) Length [mm] = ', ' Equivalent Diameter [mm] = '], ['volume [mm3]', 'length [mm]', 'max_equivalent_diameter [mm]']): printv(stg + str(np.round(np.mean(self.measure_pd[key]), 2)) + '+/-' + str(np.round(np.std(self.measure_pd[key]), 2)), self.verbose, type='info') printv('\nTotal volume = ' + str(np.round(np.sum(self.measure_pd['volume [mm3]']), 2)) + ' mm^3', self.verbose, 'info') printv('Lesion count = ' + str(len(self.measure_pd['volume [mm3]'].values)), self.verbose, 'info')
def ifolder2tmp(self): """Copy data to tmp folder.""" if self.fname_im is not None: # copy input image copy(self.fname_im, self.tmp_dir) self.fname_im = ''.join(extract_fname(self.fname_im)[1:]) else: printv('ERROR: No input image', self.verbose, 'error') if self.fname_seg is not None: # copy segmentation image copy(self.fname_seg, self.tmp_dir) self.fname_seg = ''.join(extract_fname(self.fname_seg)[1:]) self.curdir = os.getcwd() os.chdir(self.tmp_dir) # go to tmp directory
def compute_texture(self): offset = int(self.param_glcm.distance) printv('\nCompute texture metrics...', self.param.verbose, 'normal') # open image and re-orient it to RPI if needed im_tmp = Image(self.param.fname_im) if self.orientation_im != self.orientation_extraction: im_tmp.change_orientation(self.orientation_extraction) dct_metric = {} for m in self.metric_lst: im_2save = zeros_like(im_tmp, dtype='float64') dct_metric[m] = im_2save # dct_metric[m] = Image(self.fname_metric_lst[m]) with sct_progress_bar() as pbar: for im_z, seg_z, zz in zip(self.dct_im_seg['im'], self.dct_im_seg['seg'], range(len(self.dct_im_seg['im']))): for xx in range(im_z.shape[0]): for yy in range(im_z.shape[1]): if not seg_z[xx, yy]: continue if xx < offset or yy < offset: continue if xx > (im_z.shape[0] - offset - 1) or yy > (im_z.shape[1] - offset - 1): continue # to check if the whole glcm_window is in the axial_slice if False in np.unique(seg_z[xx - offset: xx + offset + 1, yy - offset: yy + offset + 1]): continue # to check if the whole glcm_window is in the mask of the axial_slice glcm_window = im_z[xx - offset: xx + offset + 1, yy - offset: yy + offset + 1] glcm_window = glcm_window.astype(np.uint8) dct_glcm = {} for a in self.param_glcm.angle.split(','): # compute the GLCM for self.param_glcm.distance and for each self.param_glcm.angle dct_glcm[a] = greycomatrix(glcm_window, [self.param_glcm.distance], [np.radians(int(a))], symmetric=self.param_glcm.symmetric, normed=self.param_glcm.normed) for m in self.metric_lst: # compute the GLCM property (m.split('_')[0]) of the voxel xx,yy,zz dct_metric[m].data[xx, yy, zz] = greycoprops(dct_glcm[m.split('_')[2]], m.split('_')[0])[0][0] pbar.set_postfix(pos="{}/{}".format(zz, len(self.dct_im_seg["im"]))) pbar.update(1) for m in self.metric_lst: fname_out = add_suffix("".join(extract_fname(self.param.fname_im)[1:]), '_' + m) dct_metric[m].save(fname_out) self.fname_metric_lst[m] = fname_out
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) data_name = arguments.d if arguments.o is not None: dest_folder = arguments.o else: dest_folder = os.path.join(os.path.abspath(os.curdir), data_name) url = DICT_URL[data_name] install_data(url, dest_folder, keep=arguments.k) printv('Done!\n', verbose)
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_loglevel(verbose=verbose) data_name = arguments.d if arguments.o is not None: dest_folder = arguments.o else: dest_folder = DATASET_DICT[data_name]['default_location'] url = DATASET_DICT[data_name]["mirrors"] install_data(url, dest_folder, keep=arguments.k) printv('Done!\n', verbose)