def main(args): parser = get_parser() arguments = parser.parse(args) # Initialization slices = '' group_funcs = (('MEAN', func_wa), ('STD', func_std)) # functions to perform when aggregating metrics along S-I fname_segmentation = sct.get_absolute_path(arguments['-i']) fname_vert_levels = '' if '-o' in arguments: file_out = os.path.abspath(arguments['-o']) else: file_out = '' if '-append' in arguments: append = int(arguments['-append']) else: append = 0 if '-vert' in arguments: vert_levels = arguments['-vert'] else: vert_levels = '' if '-r' in arguments: remove_temp_files = arguments['-r'] if '-vertfile' in arguments: fname_vert_levels = arguments['-vertfile'] if '-perlevel' in arguments: perlevel = arguments['-perlevel'] else: perlevel = None if '-z' in arguments: slices = arguments['-z'] if '-perslice' in arguments: perslice = arguments['-perslice'] else: perslice = None if '-angle-corr' in arguments: if arguments['-angle-corr'] == '1': angle_correction = True elif arguments['-angle-corr'] == '0': angle_correction = False param_centerline = ParamCenterline( algo_fitting=arguments['-centerline-algo'], smooth=arguments['-centerline-smooth'], minmax=True) path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level # update fields metrics_agg = {} if not file_out: file_out = 'csa.csv' metrics, fit_results = process_seg.compute_shape(fname_segmentation, angle_correction=angle_correction, param_centerline=param_centerline, verbose=verbose) for key in metrics: metrics_agg[key] = aggregate_per_slice_or_level(metrics[key], slices=parse_num_list(slices), levels=parse_num_list(vert_levels), perslice=perslice, perlevel=perlevel, vert_level=fname_vert_levels, group_funcs=group_funcs) metrics_agg_merged = _merge_dict(metrics_agg) save_as_csv(metrics_agg_merged, file_out, fname_in=fname_segmentation, append=append) # QC report (only show CSA for clarity) if path_qc is not None: generate_qc(fname_segmentation, args=args, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, path_img=_make_figure(metrics_agg_merged, fit_results), process='sct_process_segmentation') sct.display_open(file_out)
def main(fname_data, path_label, method, slices, levels, fname_output, labels_user, append_csv, fname_vertebral_labeling="", perslice=1, perlevel=1, verbose=1, combine_labels=True): """ Extract metrics from MRI data based on mask (could be single file of folder to atlas) :param fname_data: data to extract metric from :param path_label: mask: could be single file or folder to atlas (which contains info_label.txt) :param method {'wa', 'bin', 'ml', 'map'} :param slices. Slices of interest. Accepted format: "0,1,2,3": slices 0,1,2,3 "0:3": slices 0,1,2,3 :param levels: Vertebral levels to extract metrics from. Should be associated with a template (e.g. PAM50/template/) or a specified file: fname_vertebral_labeling. Same format as slices_of_interest. :param fname_output: :param labels_user: :param append_csv: Append to csv file :param fname_normalizing_label: :param fname_vertebral_labeling: vertebral labeling to be used with vertebral_levels :param perslice: if user selected several slices, then the function outputs a metric within each slice instead of a single average output. :param perlevel: if user selected several levels, then the function outputs a metric within each vertebral level instead of a single average output. :param verbose :param combine_labels: bool: Combine labels into a single value :return: """ # check if path_label is a file (e.g., single binary mask) instead of a folder (e.g., SCT atlas structure which # contains info_label.txt file) if os.path.isfile(path_label): # Label is a single file indiv_labels_ids = [0] indiv_labels_files = [path_label] combined_labels_ids = [] label_struc = {0: LabelStruc(id=0, name=sct.extract_fname(path_label)[1], filename=path_label)} # set path_label to empty string, because indiv_labels_files will replace it from now on path_label = '' elif os.path.isdir(path_label): # Labels is an SCT atlas folder structure # Parse labels according to the file info_label.txt # Note: the "combined_labels_*" is a list of single labels that are defined in the section defined by the keyword # "# Keyword=CombinedLabels" in info_label.txt. # TODO: redirect to appropriate Sphinx documentation # TODO: output Class instead of multiple variables. # Example 1: # label_struc[2].id = (2) # label_struc[2].name = "left fasciculus cuneatus" # label_struc[2].filename = "PAM50_atlas_02.nii.gz" # Example 2: # label_struc[51].id = (1, 2, 3, ..., 29) # label_struc[51].name = "White Matter" # label_struc[51].filename = "" # no name because it is combined indiv_labels_ids, indiv_labels_names, indiv_labels_files, \ combined_labels_ids, combined_labels_names, combined_labels_id_groups, map_clusters \ = read_label_file(path_label, param_default.file_info_label) label_struc = {} # fill IDs for indiv labels for i_label in range(len(indiv_labels_ids)): label_struc[indiv_labels_ids[i_label]] = LabelStruc(id=indiv_labels_ids[i_label], name=indiv_labels_names[i_label], filename=indiv_labels_files[i_label], map_cluster=[indiv_labels_ids[i_label] in map_cluster for map_cluster in map_clusters].index(True)) # fill IDs for combined labels # TODO: problem for defining map_cluster: if labels overlap two regions, e.g. WM and GM (e.g. id=50), # map_cluster will take value 0, which is wrong. for i_label in range(len(combined_labels_ids)): label_struc[combined_labels_ids[i_label]] = LabelStruc(id=combined_labels_id_groups[i_label], name=combined_labels_names[i_label], map_cluster=[indiv_labels_ids[i_label] in map_cluster for map_cluster in map_clusters].index(True)) else: raise RuntimeError(path_label + ' does not exist') # check syntax of labels asked by user labels_id_user = check_labels(indiv_labels_ids + combined_labels_ids, parse_num_list(labels_user)) nb_labels = len(indiv_labels_files) # Load data and systematically reorient to RPI because we need the 3rd dimension to be z sct.printv('\nLoad metric image...', verbose) input_im = Image(fname_data).change_orientation("RPI") data = Metric(data=input_im.data, label='') # Load labels labels_tmp = np.empty([nb_labels], dtype=object) for i_label in range(nb_labels): im_label = Image(os.path.join(path_label, indiv_labels_files[i_label])).change_orientation("RPI") labels_tmp[i_label] = np.expand_dims(im_label.data, 3) # TODO: generalize to 2D input label labels = np.concatenate(labels_tmp[:], 3) # labels: (x,y,z,label) # Load vertebral levels if vertebral_levels: im_vertebral_labeling = Image(fname_vertebral_labeling).change_orientation("RPI") else: im_vertebral_labeling = None # Get dimensions of data and labels nx, ny, nz = data.data.shape nx_atlas, ny_atlas, nz_atlas, nt_atlas = labels.shape # Check dimensions consistency between atlas and data if (nx, ny, nz) != (nx_atlas, ny_atlas, nz_atlas): sct.printv('\nERROR: Metric data and labels DO NOT HAVE SAME DIMENSIONS.', 1, type='error') # Combine individual labels for estimation if combine_labels: # Add entry with internal ID value (99) which corresponds to combined labels label_struc[99] = LabelStruc(id=labels_id_user, name=','.join([str(i) for i in labels_id_user]), map_cluster=None) labels_id_user = [99] for id_label in labels_id_user: sct.printv('Estimation for label: '+label_struc[id_label].name, verbose) agg_metric = extract_metric(data, labels=labels, slices=slices, levels=levels, perslice=perslice, perlevel=perlevel, vert_level=im_vertebral_labeling, method=method, label_struc=label_struc, id_label=id_label, indiv_labels_ids=indiv_labels_ids) save_as_csv(agg_metric, fname_output, fname_in=fname_data, append=append_csv) append_csv = True # when looping across labels, need to append results in the same file sct.display_open(fname_output)
def main(args): parser = get_parser() arguments = parser.parse(args) param = Param() # Initialization slices = param.slices group_funcs = (('MEAN', func_wa), ('STD', func_std) ) # functions to perform when aggregating metrics along S-I fname_segmentation = sct.get_absolute_path(arguments['-i']) fname_vert_levels = '' if '-o' in arguments: file_out = os.path.abspath(arguments['-o']) else: file_out = '' if '-append' in arguments: append = int(arguments['-append']) else: append = 0 if '-vert' in arguments: vert_levels = arguments['-vert'] else: vert_levels = '' if '-r' in arguments: remove_temp_files = arguments['-r'] if '-vertfile' in arguments: fname_vert_levels = arguments['-vertfile'] if '-perlevel' in arguments: perlevel = arguments['-perlevel'] else: perlevel = Param().perlevel if '-z' in arguments: slices = arguments['-z'] if '-perslice' in arguments: perslice = arguments['-perslice'] else: perslice = Param().perslice if '-angle-corr' in arguments: if arguments['-angle-corr'] == '1': angle_correction = True elif arguments['-angle-corr'] == '0': angle_correction = False verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level # update fields metrics_agg = {} if not file_out: file_out = 'csa.csv' metrics = process_seg.compute_shape(fname_segmentation, algo_fitting='bspline', angle_correction=angle_correction, verbose=verbose) for key in metrics: metrics_agg[key] = aggregate_per_slice_or_level( metrics[key], slices=parse_num_list(slices), levels=parse_num_list(vert_levels), perslice=perslice, perlevel=perlevel, vert_level=fname_vert_levels, group_funcs=group_funcs) metrics_agg_merged = _merge_dict(metrics_agg) save_as_csv(metrics_agg_merged, file_out, fname_in=fname_segmentation, append=append) sct.display_open(file_out)
def main(args=None): parser = get_parser() if args: arguments = parser.parse_args(args) else: arguments = parser.parse_args( args=None if sys.argv[1:] else ['--help']) # Initialization slices = '' group_funcs = (('MEAN', func_wa), ('STD', func_std) ) # functions to perform when aggregating metrics along S-I fname_segmentation = sct.get_absolute_path(arguments.i) fname_vert_levels = '' if arguments.o is not None: file_out = os.path.abspath(arguments.o) else: file_out = '' if arguments.append is not None: append = arguments.append else: append = 0 if arguments.vert is not None: vert_levels = arguments.vert else: vert_levels = '' remove_temp_files = arguments.r if arguments.vertfile is not None: fname_vert_levels = arguments.vertfile if arguments.perlevel is not None: perlevel = arguments.perlevel else: perlevel = None if arguments.z is not None: slices = arguments.z if arguments.perslice is not None: perslice = arguments.perslice else: perslice = None angle_correction = arguments.angle_corr param_centerline = ParamCenterline(algo_fitting=arguments.centerline_algo, smooth=arguments.centerline_smooth, minmax=True) path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level # update fields metrics_agg = {} if not file_out: file_out = 'csa.csv' metrics, fit_results = process_seg.compute_shape( fname_segmentation, angle_correction=angle_correction, param_centerline=param_centerline, verbose=verbose) for key in metrics: if key == 'length': # For computing cord length, slice-wise length needs to be summed across slices metrics_agg[key] = aggregate_per_slice_or_level( metrics[key], slices=parse_num_list(slices), levels=parse_num_list(vert_levels), perslice=perslice, perlevel=perlevel, vert_level=fname_vert_levels, group_funcs=(('SUM', func_sum), )) else: # For other metrics, we compute the average and standard deviation across slices metrics_agg[key] = aggregate_per_slice_or_level( metrics[key], slices=parse_num_list(slices), levels=parse_num_list(vert_levels), perslice=perslice, perlevel=perlevel, vert_level=fname_vert_levels, group_funcs=group_funcs) metrics_agg_merged = merge_dict(metrics_agg) save_as_csv(metrics_agg_merged, file_out, fname_in=fname_segmentation, append=append) # QC report (only show CSA for clarity) if path_qc is not None: generate_qc(fname_segmentation, args=args, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, path_img=_make_figure(metrics_agg_merged, fit_results), process='sct_process_segmentation') sct.display_open(file_out)
def main(fname_data, path_label, method, slices, levels, fname_output, labels_user, append, fname_normalizing_label, normalization_method, label_to_fix, adv_param_user, fname_output_metric_map, fname_mask_weight, fname_vertebral_labeling="", perslice=1, perlevel=1, discard_negative_values=False, verbose=1): """ Extract metrics from MRI data based on mask (could be single file of folder to atlas) :param fname_data: data to extract metric from :param path_label: mask: could be single file or folder to atlas (which contains info_label.txt) :param method: :param slices_of_interest. Accepted format: "0,1,2,3": slices 0,1,2,3 "0:3": slices 0,1,2,3 :param vertebral_levels: Vertebral levels to extract metrics from. Should be associated with a template (e.g. PAM50/template/) or a specified file: fname_vertebral_labeling. Same format as slices_of_interest. :param fname_output: :param labels_user: :param overwrite: :param fname_normalizing_label: :param normalization_method: :param label_to_fix: :param adv_param_user: :param fname_output_metric_map: :param fname_mask_weight: :param fname_vertebral_labeling: vertebral labeling to be used with vertebral_levels :param perslice: if user selected several slices, then the function outputs a metric within each slice instead of a single average output. :param perlevel: if user selected several levels, then the function outputs a metric within each vertebral level instead of a single average output. :param discard_negative_values: Bool: Discard negative voxels when computing metrics statistics :param verbose :return: """ # check if path_label is a file (e.g., single binary mask) instead of a folder (e.g., SCT atlas structure which # contains info_label.txt file) if os.path.isfile(path_label): # Label is a single file indiv_labels_ids = [0] indiv_labels_names = [path_label] indiv_labels_files = [path_label] combined_labels_ids = [] combined_labels_names = [] combined_labels_id_groups = [] map_clusters = [] label_struc = {0: LabelStruc(id=0, name=sct.extract_fname(path_label)[1], filename=path_label)} # set path_label to empty string, because indiv_labels_files will replace it from now on path_label = '' elif os.path.isdir(path_label): # Labels is an SCT atlas folder structure # Parse labels according to the file info_label.txt # Note: the "combined_labels_*" is a list of single labels that are defined in the section defined by the keyword # "# Keyword=CombinedLabels" in info_label.txt. # TODO: redirect to appropriate Sphinx documentation # TODO: output Class instead of multiple variables. # Example 1: # label_struc[2].id = (2) # label_struc[2].name = "left fasciculus cuneatus" # label_struc[2].filename = "PAM50_atlas_02.nii.gz" # Example 2: # label_struc[51].id = (1, 2, 3, ..., 29) # label_struc[51].name = "White Matter" # label_struc[51].filename = "" # no name because it is combined indiv_labels_ids, indiv_labels_names, indiv_labels_files, \ combined_labels_ids, combined_labels_names, combined_labels_id_groups, map_clusters \ = read_label_file(path_label, param_default.file_info_label) label_struc = {} # fill IDs for indiv labels for i_label in range(len(indiv_labels_ids)): label_struc[indiv_labels_ids[i_label]] = LabelStruc(id=indiv_labels_ids[i_label], name=indiv_labels_names[i_label], filename=indiv_labels_files[i_label], map_cluster=[indiv_labels_ids[i_label] in map_cluster for map_cluster in map_clusters].index(True)) # fill IDs for combined labels for i_label in range(len(combined_labels_ids)): label_struc[combined_labels_ids[i_label]] = LabelStruc(id=combined_labels_id_groups[i_label], name=combined_labels_names[i_label], map_cluster=[indiv_labels_ids[i_label] in map_cluster for map_cluster in map_clusters].index(True)) else: sct.printv('\nERROR: ' + path_label + ' does not exist.', 1, 'error') # check syntax of labels asked by user labels_id_user = check_labels(indiv_labels_ids + combined_labels_ids, parse_num_list(labels_user)) nb_labels = len(indiv_labels_files) # Load data and systematically reorient to RPI because we need the 3rd dimension to be z sct.printv('\nLoad metric image...', verbose) input_im = Image(fname_data).change_orientation("RPI") data = Metric(data=input_im.data, label='') # Load labels labels_tmp = np.empty([nb_labels], dtype=object) for i_label in range(nb_labels): im_label = Image(os.path.join(path_label, indiv_labels_files[i_label])).change_orientation("RPI") labels_tmp[i_label] = np.expand_dims(im_label.data, 3) # TODO: generalize to 2D input label labels = np.concatenate(labels_tmp[:], 3) # labels: (x,y,z,label) # Load vertebral levels if vertebral_levels: im_vertebral_labeling = Image(fname_vertebral_labeling).change_orientation("RPI") else: im_vertebral_labeling = None # Get dimensions of data and labels nx, ny, nz = data.data.shape nx_atlas, ny_atlas, nz_atlas, nt_atlas = labels.shape # Check dimensions consistency between atlas and data if (nx, ny, nz) != (nx_atlas, ny_atlas, nz_atlas): sct.printv('\nERROR: Metric data and labels DO NOT HAVE SAME DIMENSIONS.', 1, type='error') for id_label in labels_id_user: sct.printv('Estimation for label: '+label_struc[id_label].name, verbose) agg_metric = extract_metric(data, labels=labels, slices=slices, levels=levels, perslice=perslice, perlevel=perlevel, vert_level=im_vertebral_labeling, method=method, label_struc=label_struc, id_label=id_label, indiv_labels_ids=indiv_labels_ids) save_as_csv(agg_metric, fname_output, fname_in=fname_data, append=append) append = True # when looping across labels, need to append results in the same file sct.display_open(fname_output)
def main(args): parser = get_parser() arguments = parser.parse(args) param = Param() # Initialization slices = param.slices group_funcs = (('MEAN', func_wa), ('STD', func_std)) # functions to perform when aggregating metrics along S-I fname_segmentation = sct.get_absolute_path(arguments['-i']) fname_vert_levels = '' if '-o' in arguments: file_out = os.path.abspath(arguments['-o']) else: file_out = '' if '-append' in arguments: append = int(arguments['-append']) else: append = 0 if '-vert' in arguments: vert_levels = arguments['-vert'] else: vert_levels = '' if '-r' in arguments: remove_temp_files = arguments['-r'] if '-vertfile' in arguments: fname_vert_levels = arguments['-vertfile'] if '-perlevel' in arguments: perlevel = arguments['-perlevel'] else: perlevel = Param().perlevel if '-z' in arguments: slices = arguments['-z'] if '-perslice' in arguments: perslice = arguments['-perslice'] else: perslice = Param().perslice if '-angle-corr' in arguments: if arguments['-angle-corr'] == '1': angle_correction = True elif arguments['-angle-corr'] == '0': angle_correction = False verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level # update fields metrics_agg = {} if not file_out: file_out = 'csa.csv' metrics = process_seg.compute_shape(fname_segmentation, algo_fitting='bspline', angle_correction=angle_correction, verbose=verbose) for key in metrics: metrics_agg[key] = aggregate_per_slice_or_level(metrics[key], slices=parse_num_list(slices), levels=parse_num_list(vert_levels), perslice=perslice, perlevel=perlevel, vert_level=fname_vert_levels, group_funcs=group_funcs) metrics_agg_merged = _merge_dict(metrics_agg) save_as_csv(metrics_agg_merged, file_out, fname_in=fname_segmentation, append=append) sct.display_open(file_out)