def main(): """Main function.""" parser = get_parser() args = parser.parse_args(args=None if sys.argv[1:] else ['--help']) fname_image = os.path.abspath(args.i) contrast_type = args.c ctr_algo = args.centerline if args.brain is None: if contrast_type in ['t2s', 'dwi']: brain_bool = False if contrast_type in ['t1', 't2']: brain_bool = True else: brain_bool = bool(args.brain) if bool(args.brain) and ctr_algo == 'svm': sct.printv( 'Please only use the flag "-brain 1" with "-centerline cnn".', 1, 'warning') sys.exit(1) kernel_size = args.kernel if kernel_size == '3d' and contrast_type == 'dwi': kernel_size = '2d' sct.printv( '3D kernel model for dwi contrast is not available. 2D kernel model is used instead.', type="warning") if ctr_algo == 'file' and args.file_centerline is None: sct.printv( 'Please use the flag -file_centerline to indicate the centerline filename.', 1, 'warning') sys.exit(1) if args.file_centerline is not None: manual_centerline_fname = args.file_centerline ctr_algo = 'file' else: manual_centerline_fname = None threshold = args.thr if threshold is not None: if threshold > 1.0 or (threshold < 0.0 and threshold != -1.0): raise SyntaxError( "Threshold should be between 0 and 1, or equal to -1 (no threshold)" ) remove_temp_files = args.r verbose = args.v init_sct(log_level=verbose, update=True) # Update log level path_qc = args.qc qc_dataset = args.qc_dataset qc_subject = args.qc_subject output_folder = args.ofolder # check if input image is 2D or 3D sct.check_dim(fname_image, dim_lst=[2, 3]) # Segment image from spinalcordtoolbox.image import Image from spinalcordtoolbox.deepseg_sc.core import deep_segmentation_spinalcord from spinalcordtoolbox.reports.qc import generate_qc im_image = Image(fname_image) # note: below we pass im_image.copy() otherwise the field absolutepath becomes None after execution of this function im_seg, im_image_RPI_upsamp, im_seg_RPI_upsamp = \ deep_segmentation_spinalcord(im_image.copy(), contrast_type, ctr_algo=ctr_algo, ctr_file=manual_centerline_fname, brain_bool=brain_bool, kernel_size=kernel_size, threshold_seg=threshold, remove_temp_files=remove_temp_files, verbose=verbose) # Save segmentation fname_seg = os.path.abspath( os.path.join( output_folder, sct.extract_fname(fname_image)[1] + '_seg' + sct.extract_fname(fname_image)[2])) im_seg.save(fname_seg) # Generate QC report if path_qc is not None: generate_qc(fname_image, fname_seg=fname_seg, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_deepseg_sc') sct.display_viewer_syntax([fname_image, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
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']) verbosity = arguments.v init_sct(log_level=verbosity, update=True) # Update log level input_filename = arguments.i output_fname = arguments.o img = Image(input_filename) dtype = None if arguments.add is not None: value = arguments.add out = sct_labels.add(img, value) elif arguments.create is not None: labels = arguments.create out = sct_labels.create_labels_empty(img, labels) elif arguments.create_add is not None: labels = arguments.create_add out = sct_labels.create_labels(img, labels) elif arguments.create_seg is not None: labels = arguments.create_seg out = sct_labels.create_labels_along_segmentation(img, labels) elif arguments.cubic_to_point: out = sct_labels.cubic_to_point(img) elif arguments.display: display_voxel(img, verbosity) return elif arguments.increment: out = sct_labels.increment_z_inverse(img) elif arguments.disc is not None: ref = Image(arguments.disc) out = sct_labels.labelize_from_discs(img, ref) elif arguments.vert_body is not None: levels = arguments.vert_body if len(levels) == 1 and levels[0] == 0: levels = None # all levels out = sct_labels.label_vertebrae(img, levels) elif arguments.vert_continuous: out = sct_labels.continuous_vertebral_levels(img) dtype = 'float32' elif arguments.MSE is not None: ref = Image(arguments.MSE) mse = sct_labels.compute_mean_squared_error(img, ref) printv(f"Computed MSE: {mse}") return elif arguments.remove_reference is not None: ref = Image(arguments.remove_reference) out = sct_labels.remove_missing_labels(img, ref) elif arguments.remove_sym is not None: # first pass use img as source ref = Image(arguments.remove_reference) out = sct_labels.remove_missing_labels(img, ref) # second pass use previous pass result as reference ref_out = sct_labels.remove_missing_labels(ref, out) ref_out.save(path=ref.absolutepath) elif arguments.remove is not None: labels = arguments.remove out = sct_labels.remove_labels_from_image(img, labels) elif arguments.keep is not None: labels = arguments.keep out = sct_labels.remove_other_labels_from_image(img, labels) elif arguments.create_viewer is not None: msg = "" if arguments.msg is None else f"{arguments.msg}\n" if arguments.ilabel is not None: input_labels_img = Image(arguments.ilabel) out = launch_manual_label_gui(img, input_labels_img, arguments.create_viewer, msg) else: out = launch_sagittal_viewer(img, arguments.create_viewer, msg) out.save(path=output_fname, dtype=dtype) if arguments.qc is not None: generate_qc(fname_in1=input_filename, fname_seg=output_fname, args=args, path_qc=os.path.abspath(arguments.qc), dataset=arguments.qc_dataset, subject=arguments.qc_subject, process='sct_label_utils')
#!/usr/bin/env python # -*- coding: utf-8 # pytest unit tests for spinalcordtoolbox.qmri from __future__ import print_function, absolute_import import numpy as np import nibabel import pytest from spinalcordtoolbox.qmri import mt from spinalcordtoolbox.image import Image from spinalcordtoolbox.utils import init_sct init_sct(log_level=2) # Set logger in debug mode VERBOSE = 0 # Set to 2 to save images, 0 otherwise def make_sct_image(data): """ :return: an Image (3D) in RAS+ (aka SCT LPI) space data: scalar """ affine = np.eye(4) nii = nibabel.nifti1.Nifti1Image(np.array([data, data]), affine) img = Image(nii.get_data(), hdr=nii.header, orientation="LPI", dim=nii.header.get_data_shape()) return img
'If provided, this string will be mentioned in the QC report as the subject the process ' 'was run on', required=False) parser.add_argument('-v', action='store_true', help="Verbose") return parser def main(args): from spinalcordtoolbox.reports.qc import generate_qc # Build args list (for display) args_disp = '-i ' + args.i if args.d: args_disp += ' -d ' + args.d if args.s: args_disp += ' -s ' + args.s generate_qc(fname_in1=args.i, fname_in2=args.d, fname_seg=args.s, args=args_disp, path_qc=args.qc, dataset=args.qc_dataset, subject=args.qc_subject, process=args.p) if __name__ == '__main__': arguments = get_parser().parse_args() init_sct(log_level=2 if arguments.v else 1) main(arguments)
def run_main(): init_sct() parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) # Input filename fname_input_data = arguments.i fname_data = os.path.abspath(fname_input_data) # Method used method = arguments.method # Contrast type contrast_type = arguments.c if method == 'optic' and not contrast_type: # Contrast must be error = "ERROR: -c is a mandatory argument when using 'optic' method." sct.printv(error, type='error') return # Gap between slices interslice_gap = arguments.gap param_centerline = ParamCenterline(algo_fitting=arguments.centerline_algo, smooth=arguments.centerline_smooth, minmax=True) # Output folder if arguments.o is not None: file_output = arguments.o else: path_data, file_data, ext_data = sct.extract_fname(fname_data) file_output = os.path.join(path_data, file_data + '_centerline') verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level if method == 'viewer': # Manual labeling of cord centerline im_labels = _call_viewer_centerline(Image(fname_data), interslice_gap=interslice_gap) elif method == 'fitseg': im_labels = Image(fname_data) elif method == 'optic': # Automatic detection of cord centerline im_labels = Image(fname_data) param_centerline.algo_fitting = 'optic' param_centerline.contrast = contrast_type else: sct.printv( "ERROR: The selected method is not available: {}. Please look at the help." .format(method), type='error') return # Extrapolate and regularize (or detect if optic) cord centerline im_centerline, arr_centerline, _, _ = get_centerline( im_labels, param=param_centerline, verbose=verbose) # save centerline as nifti (discrete) and csv (continuous) files im_centerline.save(file_output + '.nii.gz') np.savetxt(file_output + '.csv', arr_centerline.transpose(), delimiter=",") sct.display_viewer_syntax([fname_input_data, file_output + '.nii.gz'], colormaps=['gray', 'red'], opacities=['', '1']) path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject # Generate QC report if path_qc is not None: generate_qc(fname_input_data, fname_seg=file_output + '.nii.gz', args=sys.argv[1:], path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_get_centerline') sct.display_viewer_syntax([fname_input_data, file_output + '.nii.gz'], colormaps=['gray', 'red'], opacities=['', '0.7'])
def main(): # Default params param = Param() # Get parser info parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) fname_data = arguments.i if arguments.m is not None: fname_mask = arguments.m else: fname_mask = '' method = arguments.method if arguments.vol is not None: index_vol_user = arguments.vol else: index_vol_user = '' verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level # Check parameters if method == 'diff': if not fname_mask: sct.printv('You need to provide a mask with -method diff. Exit.', 1, type='error') # Load data and orient to RPI im_data = Image(fname_data).change_orientation('RPI') data = im_data.data if fname_mask: mask = Image(fname_mask).change_orientation('RPI').data # Retrieve selected volumes if index_vol_user: index_vol = parse_num_list(index_vol_user) else: index_vol = range(data.shape[3]) # Make sure user selected 2 volumes with diff method if method == 'diff': if not len(index_vol) == 2: sct.printv('Method "diff" should be used with exactly two volumes (specify with flag "-vol").', 1, 'error') # Compute SNR # NB: "time" is assumed to be the 4th dimension of the variable "data" if method == 'mult': # Compute mean and STD across time data_mean = np.mean(data[:, :, :, index_vol], axis=3) data_std = np.std(data[:, :, :, index_vol], axis=3, ddof=1) # Generate mask where std is different from 0 mask_std_nonzero = np.where(data_std > param.almost_zero) snr_map = np.zeros_like(data_mean) snr_map[mask_std_nonzero] = data_mean[mask_std_nonzero] / data_std[mask_std_nonzero] # Output SNR map fname_snr = sct.add_suffix(fname_data, '_SNR-' + method) im_snr = empty_like(im_data) im_snr.data = snr_map im_snr.save(fname_snr, dtype=np.float32) # Output non-zero mask fname_stdnonzero = sct.add_suffix(fname_data, '_mask-STD-nonzero' + method) im_stdnonzero = empty_like(im_data) data_stdnonzero = np.zeros_like(data_mean) data_stdnonzero[mask_std_nonzero] = 1 im_stdnonzero.data = data_stdnonzero im_stdnonzero.save(fname_stdnonzero, dtype=np.float32) # Compute SNR in ROI if fname_mask: mean_in_roi = np.average(data_mean[mask_std_nonzero], weights=mask[mask_std_nonzero]) std_in_roi = np.average(data_std[mask_std_nonzero], weights=mask[mask_std_nonzero]) snr_roi = mean_in_roi / std_in_roi # snr_roi = np.average(snr_map[mask_std_nonzero], weights=mask[mask_std_nonzero]) elif method == 'diff': data_2vol = np.take(data, index_vol, axis=3) # Compute mean in ROI data_mean = np.mean(data_2vol, axis=3) mean_in_roi = np.average(data_mean, weights=mask) data_sub = np.subtract(data_2vol[:, :, :, 1], data_2vol[:, :, :, 0]) _, std_in_roi = weighted_avg_and_std(data_sub, mask) # Compute SNR, correcting for Rayleigh noise (see eq. 7 in Dietrich et al.) snr_roi = (2/np.sqrt(2)) * mean_in_roi / std_in_roi # Display result if fname_mask: sct.printv('\nSNR_' + method + ' = ' + str(snr_roi) + '\n', type='info')
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 sct.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): sct.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: sct.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') sct.display_viewer_syntax([fname_in, fname_out], colormaps=['gray', 'red'])
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) if __name__ == "__main__": init_sct() param_default = Param() parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) overwrite = 0 # TODO: Not used. Why? fname_data = sct.get_absolute_path(arguments.i) path_label = arguments.f method = arguments.method fname_output = arguments.o append_csv = arguments.append combine_labels = arguments.combine labels_user = arguments.l adv_param_user = arguments.param # TODO: Not used. Why?
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(argv): # Print the sct startup info init_sct() # Parse the command line arguments parser = get_parser() args = parser.parse_args(argv if argv else ['--help']) # See if there's a configuration file and import those options if args.config is not None: print('configuring') with open(args.config, 'r') as conf: _, ext = os.path.splitext(args.config) if ext == '.json': config = json.load(conf) if ext == '.yml' or ext == '.yaml': config = yaml.load(conf, Loader=yaml.Loader) # Warn people if they're overriding their config file if len(argv) > 2: warnings.warn( UserWarning( 'Using the `-config|-c` flag with additional arguments is discouraged' )) # Check for unsupported arguments orig_keys = set(vars(args).keys()) config_keys = set(config.keys()) if orig_keys != config_keys: for k in config_keys.difference(orig_keys): del config[k] # Remove the unknown key warnings.warn( UserWarning( 'Unknown key "{}" found in your configuration file, ignoring.' .format(k))) # Update the default to match the config parser.set_defaults(**config) # Reparse the arguments args = parser.parse_args(argv) # Set up email notifications if desired do_email = args.email_to is not None if do_email: email_to = args.email_to if args.email_from is not None: email_from = args.email_from else: email_from = args.email_to smtp_host, smtp_port = args.email_host.split(":") smtp_port = int(smtp_port) email_pass = getpass('Please input your email password:\n') def send_notification(subject, message): send_email(email_to, email_from, subject=subject, message=message, passwd=email_pass, smtp_host=smtp_host, smtp_port=smtp_port) while True: send_test = input( 'Would you like to send a test email to validate your settings? [Y/n]:\n' ) if send_test.lower() in ['', 'y', 'n']: break else: print('Please input y or n') if send_test.lower() in ['', 'y']: send_notification('sct_run_batch: test notification', 'Looks good') # Set up output directories and create them if they don't already exist path_output = os.path.abspath(os.path.expanduser(args.path_output)) path_results = os.path.join(path_output, 'results') path_data_processed = os.path.join(path_output, 'data_processed') path_log = os.path.join(path_output, 'log') path_qc = os.path.join(path_output, 'qc') path_segmanual = os.path.abspath(os.path.expanduser(args.path_segmanual)) script = os.path.abspath(os.path.expanduser(args.script)) path_data = os.path.abspath(os.path.expanduser(args.path_data)) for pth in [ path_output, path_results, path_data_processed, path_log, path_qc ]: os.makedirs(pth, exist_ok=True) # Check that the script can be found if not os.path.exists(script): raise FileNotFoundError( 'Couldn\'t find the script script at {}'.format(script)) # Setup overall log batch_log = open(os.path.join(path_log, args.batch_log), 'w') # Duplicate init_sct message to batch_log print('\n--\nSpinal Cord Toolbox ({})\n'.format(__version__), file=batch_log, flush=True) # Tee IO to batch_log and std(out/err) orig_stdout = sys.stdout orig_stderr = sys.stderr sys.stdout = Tee(batch_log, orig_stdout) sys.stderr = Tee(batch_log, orig_stderr) def reset_streams(): sys.stdout = orig_stdout sys.stderr = orig_stderr # Display OS print("INFO SYSTEM") print("-----------") platform_running = sys.platform if platform_running.find('darwin') != -1: os_running = 'osx' elif platform_running.find('linux') != -1: os_running = 'linux' print('OS: ' + os_running + ' (' + platform.platform() + ')') # Display number of CPU cores print('CPU cores: Available: {} | Threads used by ITK Programs: {}'.format( multiprocessing.cpu_count(), args.itk_threads)) # Display RAM available print("RAM: Total {} MB | Available {} MB | Used {} MB".format( int(psutil.virtual_memory().total / 1024 / 1024), int(psutil.virtual_memory().available / 1024 / 1024), int(psutil.virtual_memory().used / 1024 / 1024), )) # Log the current arguments (in yaml because it's cleaner) print('\nINPUT ARGUMENTS') print("---------------") print(yaml.dump(vars(args))) # Display script version info print("SCRIPT") print("------") print("git commit: {}".format( __get_commit(path_to_git_folder=os.path.dirname(script)))) print("git origin: {}".format( __get_git_origin(path_to_git_folder=os.path.dirname(script)))) print("Copying script to output folder...") try: shutil.copy(args.script, args.path_output) print("{} -> {}".format(args.script, os.path.abspath(args.path_output))) except shutil.SameFileError: print("Input and output folder are the same. Skipping copy.") pass except IsADirectoryError: print("Input folder is a directory (not a file). Skipping copy.") pass # Display data version info print("\nDATA") print("----") print("git commit: {}".format(__get_commit(path_to_git_folder=path_data))) print("git origin: {}\n".format( __get_git_origin(path_to_git_folder=path_data))) # Find subjects and process inclusion/exclusions subject_dirs = [ f for f in os.listdir(path_data) if f.startswith(args.subject_prefix) ] # Handle inclusion lists assert not ((args.include is not None) and (args.include_list is not None)),\ 'Only one of `include` and `include-list` can be used' if args.include is not None: subject_dirs = [ f for f in subject_dirs if re.search(args.include, f) is not None ] if args.include_list is not None: # TODO decide if we should warn users if one of their inclusions isn't around subject_dirs = [f for f in subject_dirs if f in args.include_list] # Handle exclusions assert not ((args.exclude is not None) and (args.exclude_list is not None)),\ 'Only one of `exclude` and `exclude-list` can be used' if args.exclude is not None: subject_dirs = [ f for f in subject_dirs if re.search(args.exclude, f) is None ] if args.exclude_list is not None: subject_dirs = [f for f in subject_dirs if f not in args.exclude_list] # Determine the number of jobs we can run simulataneously if args.jobs < 1: jobs = multiprocessing.cpu_count() + args.jobs else: jobs = args.jobs print("RUNNING") print("-------") print("Running {} jobs in parallel.\n".format(jobs)) # Run the jobs, recording start and end times start = datetime.datetime.now() # Trap errors to send an email if a script fails. try: with multiprocessing.Pool(jobs) as p: run_single_dir = functools.partial( run_single, script=script, script_args=args.script_args, path_segmanual=path_segmanual, path_data=path_data, path_data_processed=path_data_processed, path_results=path_results, path_log=path_log, path_qc=path_qc, itk_threads=args.itk_threads, continue_on_error=args.continue_on_error) results = list(p.imap(run_single_dir, subject_dirs)) except Exception as e: if do_email: message = ( 'Oh no there has been the following error in your pipeline:\n\n' '{}'.format(e)) try: # I consider the multiprocessing error more significant than a potential email error, this # ensures that the multiprocessing error is signalled. send_notification('sct_run_batch errored', message) except Exception: raise e raise e else: raise e end = datetime.datetime.now() # Check for failed subjects fails = [ sd for (sd, ret) in zip(subject_dirs, results) if ret.returncode != 0 ] if len(fails) == 0: status_message = '\nHooray! your batch completed successfully :-)\n' else: status_message = ( '\nYour batch completed but some subjects may have not completed ' 'successfully, please consult the logs for:\n' '{}\n'.format('\n'.join(fails))) print(status_message) # Display timing duration = end - start timing_message = ('Started: {} | Ended: {} | Duration: {}\n'.format( start.strftime('%Hh%Mm%Ss'), end.strftime('%Hh%Mm%Ss'), (datetime.datetime.utcfromtimestamp(0) + duration).strftime('%Hh%Mm%Ss'))) print(timing_message) if do_email: send_notification('sct_run_batch: Run completed', status_message + timing_message) open_cmd = 'open' if sys.platform == 'darwin' else 'xdg-open' print( 'To open the Quality Control (QC) report on a web-browser, run the following:\n' '{} {}/index.html'.format(open_cmd, path_qc)) if args.zip: file_zip = 'sct_run_batch_{}'.format(time.strftime('%Y%m%d%H%M%S')) path_tmp = os.path.join(tempfile.mkdtemp(), file_zip) os.makedirs(os.path.join(path_tmp, file_zip)) for folder in [path_log, path_qc, path_results]: shutil.copytree( folder, os.path.join(path_tmp, file_zip, os.path.split(folder)[-1])) shutil.make_archive(os.path.join(path_output, file_zip), 'zip', path_tmp) shutil.rmtree(path_tmp) print("\nOutput zip archive: {}.zip".format( os.path.join(path_output, file_zip))) reset_streams() batch_log.close()
def main(args=None): # initializations initz = '' initcenter = '' fname_initlabel = '' file_labelz = 'labelz.nii.gz' param = Param() # check user arguments parser = get_parser() if args: arguments = parser.parse_args(args) else: arguments = parser.parse_args( args=None if sys.argv[1:] else ['--help']) fname_in = os.path.abspath(arguments.i) fname_seg = os.path.abspath(arguments.s) contrast = arguments.c path_template = os.path.abspath(arguments.t) scale_dist = arguments.scale_dist path_output = arguments.ofolder param.path_qc = arguments.qc if arguments.discfile is not None: fname_disc = os.path.abspath(arguments.discfile) else: fname_disc = None if arguments.initz is not None: initz = arguments.initz if arguments.initcenter is not None: initcenter = arguments.initcenter # if user provided text file, parse and overwrite arguments if arguments.initfile is not None: file = open(arguments.initfile, 'r') initfile = ' ' + file.read().replace('\n', '') arg_initfile = initfile.split(' ') for idx_arg, arg in enumerate(arg_initfile): if arg == '-initz': initz = [int(x) for x in arg_initfile[idx_arg + 1].split(',')] if arg == '-initcenter': initcenter = int(arg_initfile[idx_arg + 1]) if arguments.initlabel is not None: # get absolute path of label fname_initlabel = os.path.abspath(arguments.initlabel) if arguments.param is not None: param.update(arguments.param[0]) verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level remove_temp_files = arguments.r denoise = arguments.denoise laplacian = arguments.laplacian path_tmp = sct.tmp_create(basename="label_vertebrae", verbose=verbose) # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder...', verbose) Image(fname_in).save(os.path.join(path_tmp, "data.nii")) Image(fname_seg).save(os.path.join(path_tmp, "segmentation.nii")) # Go go temp folder curdir = os.getcwd() os.chdir(path_tmp) # Straighten spinal cord sct.printv('\nStraighten spinal cord...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) cache_sig = sct.cache_signature(input_files=[fname_in, fname_seg], ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile( os.path.join( curdir, "warp_curve2straight.nii.gz")) and os.path.isfile( os.path.join( curdir, "warp_straight2curve.nii.gz")) and os.path.isfile( os.path.join(curdir, "straight_ref.nii.gz")): # if they exist, copy them into current folder sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(os.path.join(curdir, "warp_curve2straight.nii.gz"), 'warp_curve2straight.nii.gz') sct.copy(os.path.join(curdir, "warp_straight2curve.nii.gz"), 'warp_straight2curve.nii.gz') sct.copy(os.path.join(curdir, "straight_ref.nii.gz"), 'straight_ref.nii.gz') # apply straightening s, o = run_proc([ 'sct_apply_transfo', '-i', 'data.nii', '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'data_straight.nii' ]) else: sct_straighten_spinalcord.main(args=[ '-i', 'data.nii', '-s', 'segmentation.nii', '-r', str(remove_temp_files), '-v', str(verbose), ]) sct.cache_save(cachefile, cache_sig) # resample to 0.5mm isotropic to match template resolution sct.printv('\nResample to 0.5mm isotropic...', verbose) s, o = run_proc([ 'sct_resample', '-i', 'data_straight.nii', '-mm', '0.5x0.5x0.5', '-x', 'linear', '-o', 'data_straightr.nii' ], verbose=verbose) # Apply straightening to segmentation # N.B. Output is RPI sct.printv('\nApply straightening to segmentation...', verbose) run_proc( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % ('segmentation.nii', 'data_straightr.nii', 'warp_curve2straight.nii.gz', 'segmentation_straight.nii', 'Linear'), verbose=verbose, is_sct_binary=True, ) # Threshold segmentation at 0.5 run_proc([ 'sct_maths', '-i', 'segmentation_straight.nii', '-thr', '0.5', '-o', 'segmentation_straight.nii' ], verbose) # If disc label file is provided, label vertebrae using that file instead of automatically if fname_disc: # Apply straightening to disc-label sct.printv('\nApply straightening to disc labels...', verbose) run_proc( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % (fname_disc, 'data_straightr.nii', 'warp_curve2straight.nii.gz', 'labeldisc_straight.nii.gz', 'NearestNeighbor'), verbose=verbose, is_sct_binary=True, ) label_vert('segmentation_straight.nii', 'labeldisc_straight.nii.gz', verbose=1) else: # create label to identify disc sct.printv('\nCreate label to identify disc...', verbose) fname_labelz = os.path.join(path_tmp, file_labelz) if initz or initcenter: if initcenter: # find z centered in FOV nii = Image('segmentation.nii').change_orientation("RPI") nx, ny, nz, nt, px, py, pz, pt = nii.dim # Get dimensions z_center = int(np.round(nz / 2)) # get z_center initz = [z_center, initcenter] im_label = create_labels_along_segmentation( Image('segmentation.nii'), [(initz[0], initz[1])]) im_label.data = dilate(im_label.data, 3, 'ball') im_label.save(fname_labelz) elif fname_initlabel: Image(fname_initlabel).save(fname_labelz) else: # automatically finds C2-C3 disc im_data = Image('data.nii') im_seg = Image('segmentation.nii') if not remove_temp_files: # because verbose is here also used for keeping temp files verbose_detect_c2c3 = 2 else: verbose_detect_c2c3 = 0 im_label_c2c3 = detect_c2c3(im_data, im_seg, contrast, verbose=verbose_detect_c2c3) ind_label = np.where(im_label_c2c3.data) if not np.size(ind_label) == 0: im_label_c2c3.data[ind_label] = 3 else: sct.printv( 'Automatic C2-C3 detection failed. Please provide manual label with sct_label_utils', 1, 'error') sys.exit() im_label_c2c3.save(fname_labelz) # dilate label so it is not lost when applying warping dilate(Image(fname_labelz), 3, 'ball').save(fname_labelz) # Apply straightening to z-label sct.printv('\nAnd apply straightening to label...', verbose) run_proc( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % (file_labelz, 'data_straightr.nii', 'warp_curve2straight.nii.gz', 'labelz_straight.nii.gz', 'NearestNeighbor'), verbose=verbose, is_sct_binary=True, ) # get z value and disk value to initialize labeling sct.printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') sct.printv('.. ' + str(init_disc), verbose) # denoise data if denoise: sct.printv('\nDenoise data...', verbose) run_proc([ 'sct_maths', '-i', 'data_straightr.nii', '-denoise', 'h=0.05', '-o', 'data_straightr.nii' ], verbose) # apply laplacian filtering if laplacian: sct.printv('\nApply Laplacian filter...', verbose) run_proc([ 'sct_maths', '-i', 'data_straightr.nii', '-laplacian', '1', '-o', 'data_straightr.nii' ], verbose) # detect vertebral levels on straight spinal cord init_disc[1] = init_disc[1] - 1 vertebral_detection('data_straightr.nii', 'segmentation_straight.nii', contrast, param, init_disc=init_disc, verbose=verbose, path_template=path_template, path_output=path_output, scale_dist=scale_dist) # un-straighten labeled spinal cord sct.printv('\nUn-straighten labeling...', verbose) run_proc( 'isct_antsApplyTransforms -d 3 -i %s -r %s -t %s -o %s -n %s' % ('segmentation_straight_labeled.nii', 'segmentation.nii', 'warp_straight2curve.nii.gz', 'segmentation_labeled.nii', 'NearestNeighbor'), verbose=verbose, is_sct_binary=True, ) # Clean labeled segmentation sct.printv( '\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii', 'segmentation.nii', 'segmentation_labeled.nii') # label discs sct.printv('\nLabel discs...', verbose) label_discs('segmentation_labeled.nii', verbose=verbose) # come back os.chdir(curdir) # Generate output files path_seg, file_seg, ext_seg = sct.extract_fname(fname_seg) fname_seg_labeled = os.path.join(path_output, file_seg + '_labeled' + ext_seg) sct.printv('\nGenerate output files...', verbose) sct.generate_output_file( os.path.join(path_tmp, "segmentation_labeled.nii"), fname_seg_labeled) sct.generate_output_file( os.path.join(path_tmp, "segmentation_labeled_disc.nii"), os.path.join(path_output, file_seg + '_labeled_discs' + ext_seg)) # copy straightening files in case subsequent SCT functions need them sct.generate_output_file(os.path.join(path_tmp, "warp_curve2straight.nii.gz"), os.path.join(path_output, "warp_curve2straight.nii.gz"), verbose=verbose) sct.generate_output_file(os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose=verbose) sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), verbose=verbose) # Remove temporary files if remove_temp_files == 1: sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp) # Generate QC report if param.path_qc is not None: path_qc = os.path.abspath(arguments.qc) qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject labeled_seg_file = os.path.join(path_output, file_seg + '_labeled' + ext_seg) generate_qc(fname_in, fname_seg=labeled_seg_file, args=args, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_label_vertebrae') sct.display_viewer_syntax([fname_in, fname_seg_labeled], colormaps=['', 'subcortical'], opacities=['1', '0.5'])
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) fname_mask = arguments.m fname_sc = arguments.s fname_ref = arguments.i # Path to template path_template = arguments.f # TODO: check this in the parser # if not os.path.isdir(path_template) and os.path.exists(path_template): # path_template = None # printv("ERROR output directory %s is not a valid directory" % path_template, 1, 'error') # Output Folder 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) # Remove temp folder if arguments.r is not None: rm_tmp = bool(arguments.r) else: rm_tmp = True # Verbosity verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level # create the Lesion constructor lesion_obj = AnalyzeLeion(fname_mask=fname_mask, fname_sc=fname_sc, fname_ref=fname_ref, path_template=path_template, path_ofolder=path_results, verbose=verbose) # run the analyze lesion_obj.analyze() # remove tmp_dir if rm_tmp: sct.rmtree(lesion_obj.tmp_dir) printv( '\nDone! To view the labeled lesion file (one value per lesion), type:', verbose) if fname_ref is not None: printv( 'fsleyes ' + fname_mask + ' ' + os.path.join(path_results, lesion_obj.fname_label) + ' -cm red-yellow -a 70.0 & \n', verbose, 'info') else: printv( 'fsleyes ' + os.path.join(path_results, lesion_obj.fname_label) + ' -cm red-yellow -a 70.0 & \n', verbose, 'info')
def main(args=None): if args is None: args = sys.argv[1:] # initialize parameters param = Param() # Initialization fname_output = '' path_out = '' fname_src_seg = '' fname_dest_seg = '' fname_src_label = '' fname_dest_label = '' generate_warpinv = 1 start_time = time.time() # get default registration parameters # step1 = Paramreg(step='1', type='im', algo='syn', metric='MI', iter='5', shrink='1', smooth='0', gradStep='0.5') step0 = Paramreg( step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5', slicewise='0', dof='Tx_Ty_Tz_Rx_Ry_Rz') # only used to put src into dest space step1 = Paramreg(step='1', type='im') paramregmulti = ParamregMultiStep([step0, step1]) parser = get_parser(paramregmulti=paramregmulti) arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) # get arguments fname_src = arguments.i fname_dest = arguments.d if arguments.iseg is not None: fname_src_seg = arguments.iseg if arguments.dseg is not None: fname_dest_seg = arguments.dseg if arguments.ilabel is not None: fname_src_label = arguments.ilabel if arguments.dlabel is not None: fname_dest_label = arguments.dlabel if arguments.o is not None: fname_output = arguments.o if arguments.ofolder is not None: path_out = arguments.ofolder if arguments.owarp is not None: fname_output_warp = arguments.owarp else: fname_output_warp = '' if arguments.initwarp is not None: fname_initwarp = os.path.abspath(arguments.initwarp) else: fname_initwarp = '' if arguments.initwarpinv is not None: fname_initwarpinv = os.path.abspath(arguments.initwarpinv) else: fname_initwarpinv = '' if arguments.m is not None: fname_mask = arguments.m else: fname_mask = '' padding = arguments.z if arguments.param is not None: paramregmulti_user = arguments.param # update registration parameters for paramStep in paramregmulti_user: paramregmulti.addStep(paramStep) path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject identity = arguments.identity interp = arguments.x remove_temp_files = arguments.r verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level # sct.printv(arguments) sct.printv('\nInput parameters:') sct.printv(' Source .............. ' + fname_src) sct.printv(' Destination ......... ' + fname_dest) sct.printv(' Init transfo ........ ' + fname_initwarp) sct.printv(' Mask ................ ' + fname_mask) sct.printv(' Output name ......... ' + fname_output) # sct.printv(' Algorithm ........... '+paramregmulti.algo) # sct.printv(' Number of iterations '+paramregmulti.iter) # sct.printv(' Metric .............. '+paramregmulti.metric) sct.printv(' Remove temp files ... ' + str(remove_temp_files)) sct.printv(' Verbose ............. ' + str(verbose)) # update param param.verbose = verbose param.padding = padding param.fname_mask = fname_mask param.remove_temp_files = remove_temp_files # Get if input is 3D sct.printv('\nCheck if input data are 3D...', verbose) sct.check_dim(fname_src, dim_lst=[3]) sct.check_dim(fname_dest, dim_lst=[3]) # Check if user selected type=seg, but did not input segmentation data if 'paramregmulti_user' in locals(): if True in [ 'type=seg' in paramregmulti_user[i] for i in range(len(paramregmulti_user)) ]: if fname_src_seg == '' or fname_dest_seg == '': sct.printv( '\nERROR: if you select type=seg you must specify -iseg and -dseg flags.\n', 1, 'error') # Put source into destination space using header (no estimation -- purely based on header) # TODO: Check if necessary to do that # TODO: use that as step=0 # sct.printv('\nPut source into destination space using header...', verbose) # run_proc('isct_antsRegistration -d 3 -t Translation[0] -m MI[dest_pad.nii,src.nii,1,16] -c 0 -f 1 -s 0 -o # [regAffine,src_regAffine.nii] -n BSpline[3]', verbose) # if segmentation, also do it for seg fname_src2dest, fname_dest2src, _, _ = \ register_wrapper(fname_src, fname_dest, param, paramregmulti, fname_src_seg=fname_src_seg, fname_dest_seg=fname_dest_seg, fname_src_label=fname_src_label, fname_dest_label=fname_dest_label, fname_mask=fname_mask, fname_initwarp=fname_initwarp, fname_initwarpinv=fname_initwarpinv, identity=identity, interp=interp, fname_output=fname_output, fname_output_warp=fname_output_warp, path_out=path_out) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) if path_qc is not None: if fname_dest_seg: generate_qc(fname_src2dest, fname_in2=fname_dest, fname_seg=fname_dest_seg, args=args, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_register_multimodal') else: sct.printv( 'WARNING: Cannot generate QC because it requires destination segmentation.', 1, 'warning') if generate_warpinv: sct.display_viewer_syntax([fname_src, fname_dest2src], verbose=verbose) sct.display_viewer_syntax([fname_dest, fname_src2dest], verbose=verbose)
def propseg(img_input, options_dict): """ :param img_input: source image, to be segmented :param options_dict: arguments as dictionary :return: segmented Image """ arguments = options_dict fname_input_data = img_input.absolutepath fname_data = os.path.abspath(fname_input_data) contrast_type = arguments.c contrast_type_conversion = { 't1': 't1', 't2': 't2', 't2s': 't2', 'dwi': 't1' } contrast_type_propseg = contrast_type_conversion[contrast_type] # Starting building the command cmd = ['isct_propseg', '-t', contrast_type_propseg] if arguments.ofolder is not None: folder_output = arguments.ofolder else: folder_output = './' cmd += ['-o', folder_output] if not os.path.isdir(folder_output) and os.path.exists(folder_output): logger.error("output directory %s is not a valid directory" % folder_output) if not os.path.exists(folder_output): os.makedirs(folder_output) if arguments.down is not None: cmd += ["-down", str(arguments.down)] if arguments.up is not None: cmd += ["-up", str(arguments.up)] remove_temp_files = arguments.r verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level # Update for propseg binary if verbose > 0: cmd += ["-verbose"] # Output options if arguments.mesh is not None: cmd += ["-mesh"] if arguments.centerline_binary is not None: cmd += ["-centerline-binary"] if arguments.CSF is not None: cmd += ["-CSF"] if arguments.centerline_coord is not None: cmd += ["-centerline-coord"] if arguments.cross is not None: cmd += ["-cross"] if arguments.init_tube is not None: cmd += ["-init-tube"] if arguments.low_resolution_mesh is not None: cmd += ["-low-resolution-mesh"] # TODO: Not present. Why is this here? Was this renamed? # if arguments.detect_nii is not None: # cmd += ["-detect-nii"] # TODO: Not present. Why is this here? Was this renamed? # if arguments.detect_png is not None: # cmd += ["-detect-png"] # Helping options use_viewer = None use_optic = True # enabled by default init_option = None rescale_header = arguments.rescale if arguments.init is not None: init_option = float(arguments.init) if init_option < 0: sct.printv( 'Command-line usage error: ' + str(init_option) + " is not a valid value for '-init'", 1, 'error') sys.exit(1) if arguments.init_centerline is not None: if str(arguments.init_centerline) == "viewer": use_viewer = "centerline" elif str(arguments.init_centerline) == "hough": use_optic = False else: if rescale_header is not 1: fname_labels_viewer = func_rescale_header(str( arguments.init_centerline), rescale_header, verbose=verbose) else: fname_labels_viewer = str(arguments.init_centerline) cmd += ["-init-centerline", fname_labels_viewer] use_optic = False if arguments.init_mask is not None: if str(arguments.init_mask) == "viewer": use_viewer = "mask" else: if rescale_header is not 1: fname_labels_viewer = func_rescale_header( str(arguments.init_mask), rescale_header) else: fname_labels_viewer = str(arguments.init_mask) cmd += ["-init-mask", fname_labels_viewer] use_optic = False if arguments.mask_correction is not None: cmd += ["-mask-correction", str(arguments.mask_correction)] if arguments.radius is not None: cmd += ["-radius", str(arguments.radius)] # TODO: Not present. Why is this here? Was this renamed? # if arguments.detect_n is not None: # cmd += ["-detect-n", str(arguments.detect_n)] # TODO: Not present. Why is this here? Was this renamed? # if arguments.detect_gap is not None: # cmd += ["-detect-gap", str(arguments.detect_gap)] # TODO: Not present. Why is this here? Was this renamed? # if arguments.init_validation is not None: # cmd += ["-init-validation"] if arguments.nbiter is not None: cmd += ["-nbiter", str(arguments.nbiter)] if arguments.max_area is not None: cmd += ["-max-area", str(arguments.max_area)] if arguments.max_deformation is not None: cmd += ["-max-deformation", str(arguments.max_deformation)] if arguments.min_contrast is not None: cmd += ["-min-contrast", str(arguments.min_contrast)] if arguments.d is not None: cmd += ["-d", str(arguments["-d"])] if arguments.distance_search is not None: cmd += ["-dsearch", str(arguments.distance_search)] if arguments.alpha is not None: cmd += ["-alpha", str(arguments.alpha)] # check if input image is in 3D. Otherwise itk image reader will cut the 4D image in 3D volumes and only take the first one. image_input = Image(fname_data) image_input_rpi = image_input.copy().change_orientation('RPI') nx, ny, nz, nt, px, py, pz, pt = image_input_rpi.dim if nt > 1: sct.printv( 'ERROR: your input image needs to be 3D in order to be segmented.', 1, 'error') path_data, file_data, ext_data = sct.extract_fname(fname_data) path_tmp = sct.tmp_create(basename="label_vertebrae", verbose=verbose) # rescale header (see issue #1406) if rescale_header is not 1: fname_data_propseg = func_rescale_header(fname_data, rescale_header) else: fname_data_propseg = fname_data # add to command cmd += ['-i', fname_data_propseg] # if centerline or mask is asked using viewer if use_viewer: from spinalcordtoolbox.gui.base import AnatomicalParams from spinalcordtoolbox.gui.centerline import launch_centerline_dialog params = AnatomicalParams() if use_viewer == 'mask': params.num_points = 3 params.interval_in_mm = 15 # superior-inferior interval between two consecutive labels params.starting_slice = 'midfovminusinterval' if use_viewer == 'centerline': # setting maximum number of points to a reasonable value params.num_points = 20 params.interval_in_mm = 30 params.starting_slice = 'top' im_data = Image(fname_data_propseg) im_mask_viewer = msct_image.zeros_like(im_data) # im_mask_viewer.absolutepath = sct.add_suffix(fname_data_propseg, '_labels_viewer') controller = launch_centerline_dialog(im_data, im_mask_viewer, params) fname_labels_viewer = sct.add_suffix(fname_data_propseg, '_labels_viewer') if not controller.saved: sct.printv( 'The viewer has been closed before entering all manual points. Please try again.', 1, 'error') sys.exit(1) # save labels controller.as_niftii(fname_labels_viewer) # add mask filename to parameters string if use_viewer == "centerline": cmd += ["-init-centerline", fname_labels_viewer] elif use_viewer == "mask": cmd += ["-init-mask", fname_labels_viewer] # If using OptiC elif use_optic: image_centerline = optic.detect_centerline(image_input, contrast_type, verbose) fname_centerline_optic = os.path.join(path_tmp, 'centerline_optic.nii.gz') image_centerline.save(fname_centerline_optic) cmd += ["-init-centerline", fname_centerline_optic] if init_option is not None: if init_option > 1: init_option /= (nz - 1) cmd += ['-init', str(init_option)] # enabling centerline extraction by default (needed by check_and_correct_segmentation() ) cmd += ['-centerline-binary'] # run propseg status, output = run_proc(cmd, verbose, raise_exception=False, is_sct_binary=True) # check status is not 0 if not status == 0: sct.printv( 'Automatic cord detection failed. Please initialize using -init-centerline or -init-mask (see help)', 1, 'error') sys.exit(1) # build output filename fname_seg = os.path.join( folder_output, os.path.basename(sct.add_suffix(fname_data, "_seg"))) fname_centerline = os.path.join( folder_output, os.path.basename(sct.add_suffix(fname_data, "_centerline"))) # in case header was rescaled, we need to update the output file names by removing the "_rescaled" if rescale_header is not 1: sct.mv( os.path.join( folder_output, sct.add_suffix(os.path.basename(fname_data_propseg), "_seg")), fname_seg) sct.mv( os.path.join( folder_output, sct.add_suffix(os.path.basename(fname_data_propseg), "_centerline")), fname_centerline) # if user was used, copy the labelled points to the output folder (they will then be scaled back) if use_viewer: fname_labels_viewer_new = os.path.join( folder_output, os.path.basename(sct.add_suffix(fname_data, "_labels_viewer"))) sct.copy(fname_labels_viewer, fname_labels_viewer_new) # update variable (used later) fname_labels_viewer = fname_labels_viewer_new # check consistency of segmentation if arguments.correct_seg: check_and_correct_segmentation(fname_seg, fname_centerline, folder_output=folder_output, threshold_distance=3.0, remove_temp_files=remove_temp_files, verbose=verbose) # copy header from input to segmentation to make sure qform is the same sct.printv( "Copy header input --> output(s) to make sure qform is the same.", verbose) list_fname = [fname_seg, fname_centerline] if use_viewer: list_fname.append(fname_labels_viewer) for fname in list_fname: im = Image(fname) im.header = image_input.header im.save(dtype='int8' ) # they are all binary masks hence fine to save as int8 return Image(fname_seg)
def main(args=None): """ Main function :param args: :return: """ dim_list = ['x', 'y', 'z', 't'] # Get parser args if args is None: args = None if sys.argv[1:] else ['--help'] parser = get_parser() arguments = parser.parse_args(args=args) fname_in = arguments.i fname_out = arguments.o verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level output_type = arguments.type # Open file(s) im = Image(fname_in) data = im.data # 3d or 4d numpy array dim = im.dim # run command if arguments.otsu is not None: param = arguments.otsu data_out = sct_math.otsu(data, param) elif arguments.adap is not None: param = arguments.adap data_out = sct_math.adap(data, param[0], param[1]) elif arguments.otsu_median is not None: param = arguments.otsu_median data_out = sct_math.otsu_median(data, param[0], param[1]) elif arguments.thr is not None: param = arguments.thr data_out = sct_math.threshold(data, param) elif arguments.percent is not None: param = arguments.percent data_out = sct_math.perc(data, param) elif arguments.bin is not None: bin_thr = arguments.bin data_out = sct_math.binarize(data, bin_thr=bin_thr) elif arguments.add is not None: data2 = get_data_or_scalar(arguments.add, data) data_concat = sct_math.concatenate_along_4th_dimension(data, data2) data_out = np.sum(data_concat, axis=3) elif arguments.sub is not None: data2 = get_data_or_scalar(arguments.sub, data) data_out = data - data2 elif arguments.laplacian is not None: sigmas = arguments.laplacian if len(sigmas) == 1: sigmas = [sigmas for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv( parser.error( 'ERROR: -laplacian need the same number of inputs as the number of image dimension OR only one input' )) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i + 4] for i in range(3)] # smooth data data_out = sct_math.laplacian(data, sigmas) elif arguments.mul is not None: data2 = get_data_or_scalar(arguments.mul, data) data_concat = sct_math.concatenate_along_4th_dimension(data, data2) data_out = np.prod(data_concat, axis=3) elif arguments.div is not None: data2 = get_data_or_scalar(arguments.div, data) data_out = np.divide(data, data2) elif arguments.mean is not None: dim = dim_list.index(arguments.mean) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = np.mean(data, dim) elif arguments.rms is not None: dim = dim_list.index(arguments.rms) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = np.sqrt(np.mean(np.square(data.astype(float)), dim)) elif arguments.std is not None: dim = dim_list.index(arguments.std) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = np.std(data, dim, ddof=1) elif arguments.smooth is not None: sigmas = arguments.smooth if len(sigmas) == 1: sigmas = [sigmas[0] for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv( parser.error( 'ERROR: -smooth need the same number of inputs as the number of image dimension OR only one input' )) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i + 4] for i in range(3)] # smooth data data_out = sct_math.smooth(data, sigmas) elif arguments.dilate is not None: if arguments.shape in ['disk', 'square'] and arguments.dim is None: printv( parser.error( 'ERROR: -dim is required for -dilate with 2D morphological kernel' )) data_out = sct_math.dilate(data, size=arguments.dilate, shape=arguments.shape, dim=arguments.dim) elif arguments.erode is not None: if arguments.shape in ['disk', 'square'] and arguments.dim is None: printv( parser.error( 'ERROR: -dim is required for -erode with 2D morphological kernel' )) data_out = sct_math.erode(data, size=arguments.erode, shape=arguments.shape, dim=arguments.dim) elif arguments.denoise is not None: # parse denoising arguments p, b = 1, 5 # default arguments list_denoise = (arguments.denoise).split(",") for i in list_denoise: if 'p' in i: p = int(i.split('=')[1]) if 'b' in i: b = int(i.split('=')[1]) data_out = sct_math.denoise_nlmeans(data, patch_radius=p, block_radius=b) elif arguments.symmetrize is not None: data_out = (data + data[list(range(data.shape[0] - 1, -1, -1)), :, :]) / float(2) elif arguments.mi is not None: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments.mi) compute_similarity(im, im_2, fname_out, metric='mi', metric_full='Mutual information', verbose=verbose) data_out = None elif arguments.minorm is not None: im_2 = Image(arguments.minorm) compute_similarity(im, im_2, fname_out, metric='minorm', metric_full='Normalized Mutual information', verbose=verbose) data_out = None elif arguments.corr is not None: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments.corr) compute_similarity(im, im_2, fname_out, metric='corr', metric_full='Pearson correlation coefficient', verbose=verbose) data_out = None # if no flag is set else: data_out = None printv( parser.error( 'ERROR: you need to specify an operation to do on the input image' )) if data_out is not None: # Write output nii_out = Image(fname_in) # use header of input file nii_out.data = data_out nii_out.save(fname_out, dtype=output_type) # TODO: case of multiple outputs # assert len(data_out) == n_out # if n_in == n_out: # for im_in, d_out, fn_out in zip(nii, data_out, fname_out): # im_in.data = d_out # im_in.absolutepath = fn_out # if arguments.w is not None: # im_in.hdr.set_intent('vector', (), '') # im_in.save() # elif n_out == 1: # nii[0].data = data_out[0] # nii[0].absolutepath = fname_out[0] # if arguments.w is not None: # nii[0].hdr.set_intent('vector', (), '') # nii[0].save() # elif n_out > n_in: # for dat_out, name_out in zip(data_out, fname_out): # im_out = nii[0].copy() # im_out.data = dat_out # im_out.absolutepath = name_out # if arguments.w is not None: # im_out.hdr.set_intent('vector', (), '') # im_out.save() # else: # printv(parser.usage.generate(error='ERROR: not the correct numbers of inputs and outputs')) # display message if data_out is not None: display_viewer_syntax([fname_out], verbose=verbose) else: printv('\nDone! File created: ' + fname_out, verbose, '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) input_filename = arguments.i centerline_file = arguments.s sc_straight = SpinalCordStraightener(input_filename, centerline_file) if arguments.dest is not None: sc_straight.use_straight_reference = True sc_straight.centerline_reference_filename = str(arguments.dest) if arguments.ldisc_input is not None: if not sc_straight.use_straight_reference: sct.printv('Warning: discs position are not taken into account if reference is not provided.') else: sc_straight.discs_input_filename = str(arguments.ldisc_input) sc_straight.precision = 4.0 if arguments.ldisc_dest is not None: if not sc_straight.use_straight_reference: sct.printv('Warning: discs position are not taken into account if reference is not provided.') else: sc_straight.discs_ref_filename = str(arguments.ldisc_dest) sc_straight.precision = 4.0 # Handling optional arguments sc_straight.remove_temp_files = arguments.r sc_straight.interpolation_warp = arguments.x sc_straight.output_filename = arguments.o sc_straight.path_output = arguments.ofolder path_qc = arguments.qc verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level sc_straight.verbose = verbose # if arguments.cpu_nb is not None: # sc_straight.cpu_number = arguments.cpu-nb) if arguments.disable_straight2curved: sc_straight.straight2curved = False if arguments.disable_curved2straight: sc_straight.curved2straight = False if arguments.speed_factor: sc_straight.speed_factor = arguments.speed_factor if arguments.xy_size: sc_straight.xy_size = arguments.xy_size sc_straight.param_centerline = ParamCenterline( algo_fitting = arguments.centerline_algo, smooth = arguments.centerline_smooth) if arguments.param is not None: params_user = arguments.param # update registration parameters for param in params_user: param_split = param.split('=') if param_split[0] == 'precision': sc_straight.precision = float(param_split[1]) if param_split[0] == 'threshold_distance': sc_straight.threshold_distance = float(param_split[1]) if param_split[0] == 'accuracy_results': sc_straight.accuracy_results = int(param_split[1]) if param_split[0] == 'template_orientation': sc_straight.template_orientation = int(param_split[1]) fname_straight = sc_straight.straighten() sct.printv("\nFinished! Elapsed time: {} s".format(sc_straight.elapsed_time), verbose) # Generate QC report if path_qc is not None: path_qc = os.path.abspath(path_qc) qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject generate_qc(fname_straight, args=arguments, path_qc=os.path.abspath(path_qc), dataset = qc_dataset, subject = qc_subject, process = os.path.basename(__file__.strip('.py'))) sct.display_viewer_syntax([fname_straight], verbose=verbose)
def main(args=None): # initializations param = Param() # check user arguments if args is None: args = sys.argv[1:] # get parser info parser = get_parser() arguments = parser.parse_args(args) param.download = int(arguments.download) param.path_data = arguments.path functions_to_test = arguments.function param.remove_tmp_file = int(arguments.remove_temps) jobs = arguments.jobs param.verbose = arguments.verbose init_sct(log_level=param.verbose, update=True) # Update log level start_time = time.time() # get absolute path and add slash at the end param.path_data = os.path.abspath(param.path_data) # check existence of testing data folder if not os.path.isdir(param.path_data) or param.download: downloaddata(param) # display path to data printv('\nPath to testing data: ' + param.path_data, param.verbose) # create temp folder that will have all results path_tmp = os.path.abspath(arguments.execution_folder or tmp_create()) # go in path data (where all scripts will be run) curdir = os.getcwd() os.chdir(param.path_data) functions_parallel = list() functions_serial = list() if functions_to_test: for f in functions_to_test: if f in get_functions_parallelizable(): functions_parallel.append(f) elif f in get_functions_nonparallelizable(): functions_serial.append(f) else: printv( 'Command-line usage error: Function "%s" is not part of the list of testing functions' % f, type='error') jobs = min(jobs, len(functions_parallel)) else: functions_parallel = get_functions_parallelizable() functions_serial = get_functions_nonparallelizable() if arguments.continue_from: first_func = arguments.continue_from if first_func in functions_parallel: functions_serial = [] functions_parallel = functions_parallel[functions_parallel. index(first_func):] elif first_func in functions_serial: functions_serial = functions_serial[functions_serial. index(first_func):] if arguments.check_filesystem and jobs != 1: print("Check filesystem used -> jobs forced to 1") jobs = 1 print("Will run through the following tests:") if functions_serial: print("- sequentially: {}".format(" ".join(functions_serial))) if functions_parallel: print("- in parallel with {} jobs: {}".format( jobs, " ".join(functions_parallel))) list_status = [] for name, functions in ( ("serial", functions_serial), ("parallel", functions_parallel), ): if not functions: continue if any([s for (f, s) in list_status]) and arguments.abort_on_failure: break try: if functions == functions_parallel and jobs != 1: pool = multiprocessing.Pool(processes=jobs) results = list() # loop across functions and run tests for f in functions: func_param = copy.deepcopy(param) func_param.path_output = f res = pool.apply_async(process_function_multiproc, ( f, func_param, )) results.append(res) else: pool = None for idx_function, f in enumerate(functions): print_line('Checking ' + f) if functions == functions_serial or jobs == 1: if arguments.check_filesystem: if os.path.exists(os.path.join(path_tmp, f)): shutil.rmtree(os.path.join(path_tmp, f)) sig_0 = fs_signature(path_tmp) func_param = copy.deepcopy(param) func_param.path_output = f res = process_function(f, func_param) if arguments.check_filesystem: sig_1 = fs_signature(path_tmp) fs_ok(sig_0, sig_1, exclude=(f, )) else: res = results[idx_function].get() list_output, list_status_function = res # manage status if any(list_status_function): if 1 in list_status_function: print_fail() status = (f, 1) else: print_warning() status = (f, 99) for output in list_output: for line in output.splitlines(): print(" %s" % line) else: print_ok() if param.verbose: for output in list_output: for line in output.splitlines(): print(" %s" % line) status = (f, 0) # append status function to global list of status list_status.append(status) if any([s for (f, s) in list_status ]) and arguments.abort_on_failure: break except KeyboardInterrupt: raise finally: if pool: pool.terminate() pool.join() print('status: ' + str([s for (f, s) in list_status])) if any([s for (f, s) in list_status]): print("Failures: {}".format(" ".join( [f for (f, s) in list_status if s]))) # display elapsed time elapsed_time = time.time() - start_time printv('Finished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's\n') # come back os.chdir(curdir) # remove temp files if param.remove_tmp_file and arguments.execution_folder is None: printv('\nRemove temporary files...', 0) rmtree(path_tmp) e = 0 if any([s for (f, s) in list_status]): e = 1 # print(e) sys.exit(e)
def __next__(self): if self.iteration <= self.num_of_frames: result = Image(self) sct.printv("Iteration #" + str(self.iteration)) result.data *= float(self.iteration) / float(self.num_of_frames) result.file_name = "tmp." + result.file_name + "_" + str( self.iteration) self.iteration += 1 return result, self.iteration else: raise StopIteration() if __name__ == "__main__": init_sct() # TODO: Convert to argparse when fixing this script # from msct_parser import Parser import sys parser = Parser(__file__) parser.usage.set_description( 'This script generates multiple images from a warping field.') parser.add_option(name="-i", type_value="file", description="source image (moving). Can be 3D or 4D.", mandatory=True, example="t2.nii.gz") parser.add_option(name="-o", type_value="file_output",
def main(args=None): # Dictionary containing list of URLs for data names. # Mirror servers are listed in order of decreasing priority. # If exists, favour release artifact straight from github dict_url = { "sct_example_data": [ "https://github.com/sct-data/sct_example_data/releases/download/r20180525/20180525_sct_example_data.zip", "https://osf.io/kjcgs/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20180525_sct_example_data.zip", ], "sct_testing_data": [ "https://github.com/sct-data/sct_testing_data/releases/download/r20200904/sct_testing_data-r20200904.zip", "https://osf.io/download/5f516b634f1e5e00226f0599/"], "PAM50": [ "https://github.com/sct-data/PAM50/releases/download/r20191029/20191029_pam50.zip", "https://osf.io/u79sr/download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20191029_PAM50.zip", ], "MNI-Poly-AMU": [ "https://github.com/sct-data/MNI-Poly-AMU/releases/download/r20170310/20170310_MNI-Poly-AMU.zip", "https://osf.io/sh6h4/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20170310_MNI-Poly-AMU.zip", ], "gm_model": [ "https://osf.io/ugscu/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20160922_gm_model.zip", ], "optic_models": [ "https://github.com/sct-data/optic_models/releases/download/r20170413/20170413_optic_models.zip", "https://osf.io/g4fwn/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20170413_optic_models.zip", ], "pmj_models": [ "https://github.com/sct-data/pmj_models/releases/download/r20170922/20170922_pmj_models.zip", "https://osf.io/4gufr/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20170922_pmj_models.zip", ], "binaries_linux": [ "https://osf.io/cs6zt/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20200801_sct_binaries_linux.tar.gz", ], "binaries_osx": [ "https://osf.io/874cy?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20200801_sct_binaries_osx.tar.gz", ], "course_hawaii17": "https://osf.io/6exht/?action=download", "course_paris18": [ "https://osf.io/9bmn5/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20180612_sct_course-paris18.zip", ], "course_london19": [ "https://osf.io/4q3u7/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20190121_sct_course-london19.zip", ], "course_beijing19": [ "https://osf.io/ef4xz/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20190802_sct_course-beijing19.zip", ], "deepseg_gm_models": [ "https://github.com/sct-data/deepseg_gm_models/releases/download/r20180205/20180205_deepseg_gm_models.zip", "https://osf.io/b9y4x/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20180205_deepseg_gm_models.zip", ], "deepseg_sc_models": [ "https://github.com/sct-data/deepseg_sc_models/releases/download/r20180610/20180610_deepseg_sc_models.zip", "https://osf.io/avf97/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20180610_deepseg_sc_models.zip", ], "deepseg_lesion_models": [ "https://github.com/sct-data/deepseg_lesion_models/releases/download/r20180613/20180613_deepseg_lesion_models.zip", "https://osf.io/eg7v9/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20180613_deepseg_lesion_models.zip", ], "c2c3_disc_models": [ "https://github.com/sct-data/c2c3_disc_models/releases/download/r20190117/20190117_c2c3_disc_models.zip", "https://osf.io/t97ap/?action=download", "https://www.neuro.polymtl.ca/_media/downloads/sct/20190117_c2c3_disc_models.zip", ], } parser = get_parser(dict_url) if args: arguments = parser.parse_args(args) else: arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) data_name = arguments.d verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level 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) sct.printv('Done!\n', verbose) return 0