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(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 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 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) # create param object param = Param() param_glcm = ParamGLCM() # set param arguments ad inputted by user param.fname_im = arguments.i param.fname_seg = arguments.m if arguments.ofolder is not None: param.path_results = arguments.ofolder if not os.path.isdir(param.path_results) and os.path.exists(param.path_results): printv("ERROR output directory %s is not a valid directory" % param.path_results, 1, 'error') if not os.path.exists(param.path_results): os.makedirs(param.path_results) if arguments.feature is not None: param_glcm.feature = arguments.feature if arguments.distance is not None: param_glcm.distance = arguments.distance if arguments.angle is not None: param_glcm.angle = arguments.angle if arguments.dim is not None: param.dim = arguments.dim if arguments.r is not None: param.rm_tmp = bool(arguments.r) verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level # create the GLCM constructor glcm = ExtractGLCM(param=param, param_glcm=param_glcm) # run the extraction fname_out_lst = glcm.extract() # remove tmp_dir if param.rm_tmp: rmtree(glcm.tmp_dir) printv('\nDone! To view results, type:', param.verbose) printv('fsleyes ' + arguments.i + ' ' + ' -cm red-yellow -a 70.0 '.join(fname_out_lst) + ' -cm Red-Yellow -a 70.0 & \n', param.verbose, 'info')
def main(args=None): parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) 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 verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject # call main function w = WarpTemplate(fname_src, fname_transfo, warp_atlas, warp_spinal_levels, folder_out, path_template, 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 main(): parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) # Initialization param = Param() input_t1 = arguments.t1 input_fname_output = None input_tr_min = 500 input_tr_max = 3500 input_tr = None verbose = 1 fname_output_file = arguments.o if arguments.ofig is not None: input_fname_output = arguments.ofig if arguments.b is not None: input_tr_min = arguments.b[0] input_tr_max = arguments.b[1] if arguments.tr is not None: input_tr = arguments.tr verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level graph = ErnstAngle(input_t1, tr=input_tr, fname_output=input_fname_output) if input_tr is not None: printv("\nValue of the Ernst Angle with T1=" + str(graph.t1) + "ms and TR=" + str(input_tr) + "ms :", verbose=verbose, type='info') printv(str(graph.getErnstAngle(input_tr))) if input_tr > input_tr_max: input_tr_max = input_tr + 500 elif input_tr < input_tr_min: input_tr_min = input_tr - 500 # save text file try: f = open(fname_output_file, 'w') f.write(str(graph.getErnstAngle(input_tr))) f.close() except: printv('\nERROR: Cannot open file' + fname_output_file, '1', 'error') if verbose == 2: graph.draw(input_tr_min, input_tr_max)
def main(args=None): """ Entry point for sct_apply_transfo :param args: list of input arguments. For parameters -w and -winv, args list should include a nested list for every item. Example: args=['-i', 'file.nii', '-w', ['warp1.nii', 'warp2.nii']] :return: """ # get parser args if args is None: args = None if sys.argv[1:] else ['--help'] else: # flatten the list of input arguments because -w and -winv carry a nested list lst = [] for line in args: lst.append(line) if isinstance(line, str) else lst.extend(line) args = lst parser = get_parser() arguments = parser.parse_args(args=args) input_filename = arguments.i fname_dest = arguments.d warp_filename = arguments.w warpinv_filename = arguments.winv transform = Transform(input_filename=input_filename, fname_dest=fname_dest, list_warp=warp_filename, list_warpinv=warpinv_filename) transform.crop = arguments.crop transform.output_filename = arguments.o transform.interp = arguments.x transform.remove_temp_files = arguments.r transform.verbose = arguments.v init_sct(log_level=transform.verbose, update=True) # Update log level transform.apply()
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 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 = 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(): """Main function.""" parser = get_parser() args = parser.parse_args(args=None if sys.argv[1:] else ['--help']) fname_image = args.i contrast_type = args.c ctr_algo = args.centerline brain_bool = bool(args.brain) if args.brain is None and contrast_type in ['t2s', 't2_ax']: brain_bool = False output_folder = args.ofolder if ctr_algo == 'file' and args.file_centerline is None: printv( 'Please use the flag -file_centerline to indicate the centerline filename.', 1, 'error') sys.exit(1) if args.file_centerline is not None: manual_centerline_fname = args.file_centerline ctr_algo = 'file' else: manual_centerline_fname = None remove_temp_files = args.r verbose = args.v init_sct(log_level=verbose, update=True) # Update log level algo_config_stg = '\nMethod:' algo_config_stg += '\n\tCenterline algorithm: ' + str(ctr_algo) algo_config_stg += '\n\tAssumes brain section included in the image: ' + str( brain_bool) + '\n' printv(algo_config_stg) # Segment image from spinalcordtoolbox.image import Image from spinalcordtoolbox.deepseg_lesion.core import deep_segmentation_MSlesion im_image = Image(fname_image) im_seg, im_labels_viewer, im_ctr = deep_segmentation_MSlesion( im_image, contrast_type, ctr_algo=ctr_algo, ctr_file=manual_centerline_fname, brain_bool=brain_bool, remove_temp_files=remove_temp_files, verbose=verbose) # Save segmentation fname_seg = os.path.abspath( os.path.join( output_folder, extract_fname(fname_image)[1] + '_lesionseg' + extract_fname(fname_image)[2])) im_seg.save(fname_seg) if ctr_algo == 'viewer': # Save labels fname_labels = os.path.abspath( os.path.join( output_folder, extract_fname(fname_image)[1] + '_labels-centerline' + extract_fname(fname_image)[2])) im_labels_viewer.save(fname_labels) if verbose == 2: # Save ctr fname_ctr = os.path.abspath( os.path.join( output_folder, extract_fname(fname_image)[1] + '_centerline' + extract_fname(fname_image)[2])) im_ctr.save(fname_ctr) display_viewer_syntax([fname_image, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
def main(args=None): # Initialization param = Param() start_time = time.time() parser = get_parser() arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) fname_anat = arguments.i fname_centerline = arguments.s param.algo_fitting = arguments.algo_fitting if arguments.smooth is not None: sigma = arguments.smooth remove_temp_files = arguments.r verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level # Display arguments printv('\nCheck input arguments...') printv(' Volume to smooth .................. ' + fname_anat) printv(' Centerline ........................ ' + fname_centerline) printv(' Sigma (mm) ........................ ' + str(sigma)) printv(' Verbose ........................... ' + str(verbose)) # Check that input is 3D: nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: printv('WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = extract_fname(fname_centerline) path_tmp = tmp_create(basename="smooth_spinalcord") # Copying input data to tmp folder printv('\nCopying input data to tmp folder and convert to nii...', verbose) copy(fname_anat, os.path.join(path_tmp, "anat" + ext_anat)) copy(fname_centerline, os.path.join(path_tmp, "centerline" + ext_centerline)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # convert to nii format convert('anat' + ext_anat, 'anat.nii') convert('centerline' + ext_centerline, 'centerline.nii') # Change orientation of the input image into RPI printv('\nOrient input volume to RPI orientation...') fname_anat_rpi = Image("anat.nii") \ .change_orientation("RPI", generate_path=True) \ .save() \ .absolutepath # Change orientation of the input image into RPI printv('\nOrient centerline to RPI orientation...') fname_centerline_rpi = Image("centerline.nii") \ .change_orientation("RPI", generate_path=True) \ .save() \ .absolutepath # Straighten the spinal cord # straighten segmentation printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) cache_sig = cache_signature(input_files=[fname_anat_rpi, fname_centerline_rpi], input_params={"x": "spline"}) cachefile = os.path.join(curdir, "straightening.cache") if 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 printv('Reusing existing warping field which seems to be valid', verbose, 'warning') copy(os.path.join(curdir, 'warp_curve2straight.nii.gz'), 'warp_curve2straight.nii.gz') copy(os.path.join(curdir, 'warp_straight2curve.nii.gz'), 'warp_straight2curve.nii.gz') copy(os.path.join(curdir, 'straight_ref.nii.gz'), 'straight_ref.nii.gz') # apply straightening run_proc(['sct_apply_transfo', '-i', fname_anat_rpi, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'anat_rpi_straight.nii', '-x', 'spline'], verbose) else: run_proc(['sct_straighten_spinalcord', '-i', fname_anat_rpi, '-o', 'anat_rpi_straight.nii', '-s', fname_centerline_rpi, '-x', 'spline', '-param', 'algo_fitting=' + param.algo_fitting], verbose) cache_save(cachefile, cache_sig) # move warping fields locally (to use caching next time) copy('warp_curve2straight.nii.gz', os.path.join(curdir, 'warp_curve2straight.nii.gz')) copy('warp_straight2curve.nii.gz', os.path.join(curdir, 'warp_straight2curve.nii.gz')) # Smooth the straightened image along z printv('\nSmooth the straightened image...') sigma_smooth = ",".join([str(i) for i in sigma]) sct_maths.main(args=['-i', 'anat_rpi_straight.nii', '-smooth', sigma_smooth, '-o', 'anat_rpi_straight_smooth.nii', '-v', '0']) # Apply the reversed warping field to get back the curved spinal cord printv('\nApply the reversed warping field to get back the curved spinal cord...') run_proc(['sct_apply_transfo', '-i', 'anat_rpi_straight_smooth.nii', '-o', 'anat_rpi_straight_smooth_curved.nii', '-d', 'anat.nii', '-w', 'warp_straight2curve.nii.gz', '-x', 'spline'], verbose) # replace zeroed voxels by original image (issue #937) printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.save('anat_rpi_straight_smooth_curved_nonzero.nii') # come back os.chdir(curdir) # Generate output file printv('\nGenerate output file...') generate_output_file(os.path.join(path_tmp, "anat_rpi_straight_smooth_curved_nonzero.nii"), file_anat + '_smooth' + ext_anat) # Remove temporary files if remove_temp_files == 1: printv('\nRemove temporary files...') rmtree(path_tmp) # Display elapsed time elapsed_time = time.time() - start_time printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's\n') display_viewer_syntax([file_anat, file_anat + '_smooth'], verbose=verbose)
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: 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): """ Main function :param args: :return: """ # initializations output_type = None 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 n_in = len(fname_in) verbose = arguments.v init_sct(log_level=verbose, update=True) # Update log level if arguments.o is not None: fname_out = arguments.o else: fname_out = None # Run command # Arguments are sorted alphabetically (not according to the usage order) if arguments.concat is not None: dim = arguments.concat assert dim in dim_list dim = dim_list.index(dim) im_out = [concat_data(fname_in, dim)] # TODO: adapt to fname_in elif arguments.copy_header is not None: if fname_out is None: raise ValueError("Need to specify output image with -o!") im_in = Image(fname_in[0]) im_dest = Image(arguments.copy_header) im_dest_new = im_in.copy() im_dest_new.data = im_dest.data.copy() # im_dest.header = im_in.header im_dest_new.absolutepath = im_dest.absolutepath im_out = [im_dest_new] elif arguments.display_warp: im_in = fname_in[0] visualize_warp(im_in, fname_grid=None, step=3, rm_tmp=True) im_out = None elif arguments.getorient: im_in = Image(fname_in[0]) orient = im_in.orientation im_out = None elif arguments.keep_vol is not None: index_vol = (arguments.keep_vol).split(',') for iindex_vol, vol in enumerate(index_vol): index_vol[iindex_vol] = int(vol) im_in = Image(fname_in[0]) im_out = [remove_vol(im_in, index_vol, todo='keep')] elif arguments.mcs: im_in = Image(fname_in[0]) if n_in != 1: printv(parser.error('ERROR: -mcs need only one input')) if len(im_in.data.shape) != 5: printv( parser.error( 'ERROR: -mcs input need to be a multi-component image')) im_out = multicomponent_split(im_in) elif arguments.omc: im_ref = Image(fname_in[0]) for fname in fname_in: im = Image(fname) if im.data.shape != im_ref.data.shape: printv( parser.error( 'ERROR: -omc inputs need to have all the same shapes')) del im im_out = [multicomponent_merge(fname_in)] # TODO: adapt to fname_in elif arguments.pad is not None: im_in = Image(fname_in[0]) ndims = len(im_in.data.shape) if ndims != 3: printv('ERROR: you need to specify a 3D input file.', 1, 'error') return pad_arguments = arguments.pad.split(',') if len(pad_arguments) != 3: printv('ERROR: you need to specify 3 padding values.', 1, 'error') padx, pady, padz = pad_arguments padx, pady, padz = int(padx), int(pady), int(padz) im_out = [ pad_image(im_in, pad_x_i=padx, pad_x_f=padx, pad_y_i=pady, pad_y_f=pady, pad_z_i=padz, pad_z_f=padz) ] elif arguments.pad_asym is not None: im_in = Image(fname_in[0]) ndims = len(im_in.data.shape) if ndims != 3: printv('ERROR: you need to specify a 3D input file.', 1, 'error') return pad_arguments = arguments.pad_asym.split(',') if len(pad_arguments) != 6: printv('ERROR: you need to specify 6 padding values.', 1, 'error') padxi, padxf, padyi, padyf, padzi, padzf = pad_arguments padxi, padxf, padyi, padyf, padzi, padzf = int(padxi), int(padxf), int( padyi), int(padyf), int(padzi), int(padzf) im_out = [ pad_image(im_in, pad_x_i=padxi, pad_x_f=padxf, pad_y_i=padyi, pad_y_f=padyf, pad_z_i=padzi, pad_z_f=padzf) ] elif arguments.remove_vol is not None: index_vol = (arguments.remove_vol).split(',') for iindex_vol, vol in enumerate(index_vol): index_vol[iindex_vol] = int(vol) im_in = Image(fname_in[0]) im_out = [remove_vol(im_in, index_vol, todo='remove')] elif arguments.setorient is not None: printv(fname_in[0]) im_in = Image(fname_in[0]) im_out = [change_orientation(im_in, arguments.setorient)] elif arguments.setorient_data is not None: im_in = Image(fname_in[0]) im_out = [ change_orientation(im_in, arguments.setorient_data, data_only=True) ] elif arguments.split is not None: dim = arguments.split assert dim in dim_list im_in = Image(fname_in[0]) dim = dim_list.index(dim) im_out = split_data(im_in, dim) elif arguments.type is not None: output_type = arguments.type im_in = Image(fname_in[0]) im_out = [im_in] # TODO: adapt to fname_in elif arguments.to_fsl is not None: space_files = arguments.to_fsl if len(space_files) > 2 or len(space_files) < 1: printv(parser.error('ERROR: -to-fsl expects 1 or 2 arguments')) return spaces = [Image(s) for s in space_files] if len(spaces) < 2: spaces.append(None) im_out = [ displacement_to_abs_fsl(Image(fname_in[0]), spaces[0], spaces[1]) ] else: im_out = None printv( parser.error( 'ERROR: you need to specify an operation to do on the input image' )) # in case fname_out is not defined, use first element of input file name list if fname_out is None: fname_out = fname_in[0] # Write output if im_out is not None: printv('Generate output files...', verbose) # if only one output if len(im_out) == 1 and arguments.split is None: im_out[0].save(fname_out, dtype=output_type, verbose=verbose) display_viewer_syntax([fname_out], verbose=verbose) if arguments.mcs: # use input file name and add _X, _Y _Z. Keep the same extension l_fname_out = [] for i_dim in range(3): l_fname_out.append( add_suffix(fname_out or fname_in[0], '_' + dim_list[i_dim].upper())) im_out[i_dim].save(l_fname_out[i_dim], verbose=verbose) display_viewer_syntax(fname_out) if arguments.split is not None: # use input file name and add _"DIM+NUMBER". Keep the same extension l_fname_out = [] for i, im in enumerate(im_out): l_fname_out.append( add_suffix(fname_out or fname_in[0], '_' + dim_list[dim].upper() + str(i).zfill(4))) im.save(l_fname_out[i]) display_viewer_syntax(l_fname_out) elif arguments.getorient: printv(orient) elif arguments.display_warp: printv('Warping grid generated.', verbose, 'info')
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': 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' 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: 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 check_dim(fname_image, dim_lst=[2, 3]) # Segment image 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, extract_fname(fname_image)[1] + '_seg' + 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') display_viewer_syntax([fname_image, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
def main(args=None): # initialize parameters param = Param() # call main function parser = get_parser() if args: arguments = parser.parse_args(args) else: arguments = parser.parse_args(args=None if sys.argv[1:] else ['--help']) fname_data = arguments.i fname_bvecs = arguments.bvec average = arguments.a verbose = int(arguments.v) init_sct(log_level=verbose, update=True) # Update log level remove_temp_files = arguments.r path_out = arguments.ofolder fname_bvals = arguments.bval if arguments.bvalmin: param.bval_min = arguments.bvalmin # Initialization start_time = time.time() # printv(arguments) printv('\nInput parameters:', verbose) printv(' input file ............' + fname_data, verbose) printv(' bvecs file ............' + fname_bvecs, verbose) printv(' bvals file ............' + fname_bvals, verbose) printv(' average ...............' + str(average), verbose) # Get full path fname_data = os.path.abspath(fname_data) fname_bvecs = os.path.abspath(fname_bvecs) if fname_bvals: fname_bvals = os.path.abspath(fname_bvals) # Extract path, file and extension path_data, file_data, ext_data = extract_fname(fname_data) # create temporary folder path_tmp = tmp_create(basename="dmri_separate") # copy files into tmp folder and convert to nifti printv('\nCopy files into temporary folder...', verbose) ext = '.nii' dmri_name = 'dmri' b0_name = file_data + '_b0' b0_mean_name = b0_name + '_mean' dwi_name = file_data + '_dwi' dwi_mean_name = dwi_name + '_mean' if not convert(fname_data, os.path.join(path_tmp, dmri_name + ext)): printv('ERROR in convert.', 1, 'error') copy(fname_bvecs, os.path.join(path_tmp, "bvecs"), verbose=verbose) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Get size of data im_dmri = Image(dmri_name + ext) printv('\nGet dimensions data...', verbose) nx, ny, nz, nt, px, py, pz, pt = im_dmri.dim printv('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), verbose) # Identify b=0 and DWI images printv(fname_bvals) index_b0, index_dwi, nb_b0, nb_dwi = identify_b0(fname_bvecs, fname_bvals, param.bval_min, verbose) # Split into T dimension printv('\nSplit along T dimension...', verbose) im_dmri_split_list = split_data(im_dmri, 3) for im_d in im_dmri_split_list: im_d.save() # Merge b=0 images printv('\nMerge b=0...', verbose) from sct_image import concat_data l = [] for it in range(nb_b0): l.append(dmri_name + '_T' + str(index_b0[it]).zfill(4) + ext) im_out = concat_data(l, 3).save(b0_name + ext) # Average b=0 images if average: printv('\nAverage b=0...', verbose) run_proc(['sct_maths', '-i', b0_name + ext, '-o', b0_mean_name + ext, '-mean', 't'], verbose) # Merge DWI l = [] for it in range(nb_dwi): l.append(dmri_name + '_T' + str(index_dwi[it]).zfill(4) + ext) im_out = concat_data(l, 3).save(dwi_name + ext) # Average DWI images if average: printv('\nAverage DWI...', verbose) run_proc(['sct_maths', '-i', dwi_name + ext, '-o', dwi_mean_name + ext, '-mean', 't'], verbose) # come back os.chdir(curdir) # Generate output files fname_b0 = os.path.abspath(os.path.join(path_out, b0_name + ext_data)) fname_dwi = os.path.abspath(os.path.join(path_out, dwi_name + ext_data)) fname_b0_mean = os.path.abspath(os.path.join(path_out, b0_mean_name + ext_data)) fname_dwi_mean = os.path.abspath(os.path.join(path_out, dwi_mean_name + ext_data)) printv('\nGenerate output files...', verbose) generate_output_file(os.path.join(path_tmp, b0_name + ext), fname_b0, verbose=verbose) generate_output_file(os.path.join(path_tmp, dwi_name + ext), fname_dwi, verbose=verbose) if average: generate_output_file(os.path.join(path_tmp, b0_mean_name + ext), fname_b0_mean, verbose=verbose) generate_output_file(os.path.join(path_tmp, dwi_mean_name + ext), fname_dwi_mean, verbose=verbose) # Remove temporary files if remove_temp_files == 1: printv('\nRemove temporary files...', verbose) rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) return fname_b0, fname_b0_mean, fname_dwi, fname_dwi_mean
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." 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 = 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: 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=",") 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') display_viewer_syntax([fname_input_data, file_output + '.nii.gz'], colormaps=['gray', 'red'], opacities=['', '0.7'])
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 # printv(arguments) printv('\nInput parameters:') printv(' Source .............. ' + fname_src) printv(' Destination ......... ' + fname_dest) printv(' Init transfo ........ ' + fname_initwarp) printv(' Mask ................ ' + fname_mask) printv(' Output name ......... ' + fname_output) # printv(' Algorithm ........... '+paramregmulti.algo) # printv(' Number of iterations '+paramregmulti.iter) # printv(' Metric .............. '+paramregmulti.metric) printv(' Remove temp files ... ' + str(remove_temp_files)) 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 printv('\nCheck if input data are 3D...', verbose) check_dim(fname_src, dim_lst=[3]) 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 == '': 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 # 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 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: printv( 'WARNING: Cannot generate QC because it requires destination segmentation.', 1, 'warning') if generate_warpinv: display_viewer_syntax([fname_src, fname_dest2src], verbose=verbose) 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: 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: printv( 'ERROR: your input image needs to be 3D in order to be segmented.', 1, 'error') path_data, file_data, ext_data = extract_fname(fname_data) path_tmp = tmp_create(basename="label_vertebrae") # 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 = zeros_like(im_data) # im_mask_viewer.absolutepath = add_suffix(fname_data_propseg, '_labels_viewer') controller = launch_centerline_dialog(im_data, im_mask_viewer, params) fname_labels_viewer = add_suffix(fname_data_propseg, '_labels_viewer') if not controller.saved: 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: 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(add_suffix(fname_data, "_seg"))) fname_centerline = os.path.join( folder_output, os.path.basename(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: mv( os.path.join( folder_output, add_suffix(os.path.basename(fname_data_propseg), "_seg")), fname_seg) mv( os.path.join( folder_output, 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(add_suffix(fname_data, "_labels_viewer"))) 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 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): # 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/r20201030/sct_testing_data-r20201030.zip", "https://osf.io/download/5f9c271187b7df04593b03e0/" ], "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) printv('Done!\n', verbose) return 0
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): # 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 len(initz) != 2: raise ValueError( '--initz takes two arguments: position in superior-inferior direction, label value' ) 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 len(initz) != 2: raise ValueError( '--initz takes two arguments: position in superior-inferior direction, label value' ) 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 laplacian = arguments.laplacian path_tmp = tmp_create(basename="label_vertebrae") # Copying input data to tmp folder 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 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 = cache_signature(input_files=[fname_in, fname_seg], ) cachefile = os.path.join(curdir, "straightening.cache") if 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 printv('Reusing existing warping field which seems to be valid', verbose, 'warning') copy(os.path.join(curdir, "warp_curve2straight.nii.gz"), 'warp_curve2straight.nii.gz') copy(os.path.join(curdir, "warp_straight2curve.nii.gz"), 'warp_straight2curve.nii.gz') 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), ]) cache_save(cachefile, cache_sig) # resample to 0.5mm isotropic to match template resolution 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 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 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 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: 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 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 printv('\nGet z and disc values from straight label...', verbose) init_disc = get_z_and_disc_values_from_label('labelz_straight.nii.gz') printv('.. ' + str(init_disc), verbose) # apply laplacian filtering if laplacian: 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 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 printv('\nClean labeled segmentation (correct interpolation errors)...', verbose) clean_labeled_segmentation('segmentation_labeled.nii', 'segmentation.nii', 'segmentation_labeled.nii') # label discs 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 = extract_fname(fname_seg) fname_seg_labeled = os.path.join(path_output, file_seg + '_labeled' + ext_seg) printv('\nGenerate output files...', verbose) generate_output_file(os.path.join(path_tmp, "segmentation_labeled.nii"), fname_seg_labeled) 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 generate_output_file(os.path.join(path_tmp, "warp_curve2straight.nii.gz"), os.path.join(path_output, "warp_curve2straight.nii.gz"), verbose=verbose) generate_output_file(os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose=verbose) 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: printv('\nRemove temporary files...', verbose) 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') display_viewer_syntax([fname_in, fname_seg_labeled], colormaps=['', 'subcortical'], opacities=['1', '0.5'])
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 = 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 = 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') 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()
if arguments.o is not None: path_output, fname_output, ext = extract_fname(arguments.o) cmd += ['-o', fname_output + ext] rm_tmp = bool(arguments.r) # # Computation of Dice coefficient using Python implementation. # # commented for now as it does not cover all the feature of isct_dice_coefficient # #from spinalcordtoolbox.image import Image, compute_dice # #dice = compute_dice(Image(fname_input1), Image(fname_input2), mode='3d', zboundaries=False) # #printv('Dice (python-based) = ' + str(dice), verbose) status, output = run_proc(cmd, verbose, is_sct_binary=True) os.chdir(curdir) # go back to original directory # copy output file into original directory if arguments.o is not None: copy(os.path.join(tmp_dir, fname_output + ext), os.path.join(path_output, fname_output + ext)) # remove tmp_dir if rm_tmp: rmtree(tmp_dir) printv(output, verbose) if __name__ == "__main__": init_sct() main(sys.argv[1:])
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: 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: 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() 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'))) display_viewer_syntax([fname_straight], verbose=verbose)