def main(argv=None): parser = get_parser() if sys.platform.startswith("win32"): # This isn't *really* a parsing error, but it feels a little more official to display the help with this error parser.error( "`sct_propseg` is not currently supported on native Windows installations. \n\n" "For spinal cord segmentation, please migrate to the new and improved `sct_deepseg_sc` tool, " "or consider using WSL to install SCT instead.\n\n" "For further updates on `sct_propseg` Windows support, please visit:\n" "https://github.com/spinalcordtoolbox/spinalcordtoolbox/issues/3694" ) arguments = parser.parse_args(argv) verbose = arguments.v set_loglevel(verbose=verbose) fname_input_data = os.path.abspath(arguments.i) img_input = Image(fname_input_data) img_seg = propseg(img_input, arguments) fname_seg = img_seg.absolutepath path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject if path_qc is not None: generate_qc(fname_in1=fname_input_data, fname_seg=fname_seg, args=arguments, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_propseg') display_viewer_syntax([fname_input_data, fname_seg], colormaps=['gray', 'red'], opacities=['', '1'])
def gen_qc(path_qc): t2_image = sct_test_path('t2', 't2.nii.gz') t2_seg = sct_test_path('t2', 't2_seg-manual.nii.gz') qc.generate_qc(fname_in1=t2_image, fname_seg=t2_seg, path_qc=path_qc, process="sct_deepseg_gm")
def main(args=None): if args is None: args = sys.argv[1:] # Get parser parser = get_parser() arguments = parser.parse(args) # Set param arguments ad inputted by user fname_in = arguments["-i"] contrast = arguments["-c"] # Segmentation or Centerline line if '-s' in arguments: 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 '-ofolder' in arguments: 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.get("-qc", None) # Remove temp folder rm_tmp = bool(int(arguments.get("-r", 1))) verbose = int(arguments.get('-v')) sct.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: generate_qc(fname_in, fname_seg=fname_out, args=args, path_qc=os.path.abspath(path_qc), process='sct_detect_pmj') sct.display_viewer_syntax([fname_in, fname_out], colormaps=['gray', 'red'])
def main(args=None): parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_src = arguments["-d"] fname_transfo = arguments["-w"] warp_atlas = int(arguments["-a"]) warp_spinal_levels = int(arguments["-s"]) folder_out = arguments['-ofolder'] path_template = arguments['-t'] verbose = int(arguments['-v']) path_qc = arguments.get("-qc", None) # 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) # Only deal with QC and verbose if white matter was warped (meaning, everything under template/) if "white matter" in spinalcordtoolbox.metadata.get_indiv_label_names( path_template): # Deal with QC report if path_qc is not None: fname_wm = os.path.join( w.folder_out, w.folder_template, spinalcordtoolbox.metadata.get_file_label( path_template, 'white matter')) generate_qc(fname_src, fname_seg=fname_wm, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), process='sct_warp_template') # Deal with verbose sct.display_viewer_syntax( [ fname_src, spinalcordtoolbox.metadata.get_file_label( path_template, 'T2-weighted template', output="filewithpath"), spinalcordtoolbox.metadata.get_file_label( path_template, 'gray matter', output="filewithpath"), spinalcordtoolbox.metadata.get_file_label( path_template, 'white matter', output="filewithpath") ], 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, ) else: if path_qc is not None: sct.printv( "QC not generated since expected labels are missing from template", type="warning")
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 gen_qc(i): t2_image = os.path.join(__sct_dir__, 'sct_testing_data', 't2', 't2.nii.gz') t2_seg = os.path.join(__sct_dir__, 'sct_testing_data', 't2', 't2_seg.nii.gz') qc.generate_qc(fname_in1=t2_image, fname_seg=t2_seg, path_qc="/tmp/qc", process="sct_deepseg_gm") return True
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) param = Param() fname_src = arguments.d fname_transfo = arguments.w warp_atlas = arguments.a warp_spinal_levels = arguments.s folder_out = arguments.ofolder path_template = arguments.t path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject folder_template = param.folder_template folder_atlas = param.folder_atlas folder_spinal_levels = param.folder_spinal_levels file_info_label = param.file_info_label list_labels_nn = param.list_labels_nn # call main function w = WarpTemplate(fname_src, fname_transfo, warp_atlas, warp_spinal_levels, folder_out, path_template, folder_template, folder_atlas, folder_spinal_levels, file_info_label, list_labels_nn, verbose) path_template = os.path.join(w.folder_out, w.folder_template) # Deal with QC report if path_qc is not None: try: fname_wm = os.path.join( w.folder_out, w.folder_template, spinalcordtoolbox.metadata.get_file_label(path_template, id_label=4)) # label = 'white matter mask (probabilistic)' generate_qc( fname_src, fname_seg=fname_wm, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_warp_template') # If label is missing, get_file_label() throws a RuntimeError except RuntimeError: printv("QC not generated since expected labels are missing from template", type="warning") # Deal with verbose try: display_viewer_syntax( [fname_src, spinalcordtoolbox.metadata.get_file_label(path_template, id_label=1, output="filewithpath"), # label = 'T2-weighted template' spinalcordtoolbox.metadata.get_file_label(path_template, id_label=5, output="filewithpath"), # label = 'gray matter mask (probabilistic)' spinalcordtoolbox.metadata.get_file_label(path_template, id_label=4, output="filewithpath")], # label = 'white matter mask (probabilistic)' colormaps=['gray', 'gray', 'red-yellow', 'blue-lightblue'], opacities=['1', '1', '0.5', '0.5'], minmax=['', '0,4000', '0.4,1', '0.4,1'], verbose=verbose) # If label is missing, continue silently except RuntimeError: pass
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) # initialization param = ParamMoco(is_diffusion=True, group_size=3, metric='MI', smooth='1') # Fetch user arguments param.fname_data = arguments.i param.fname_bvecs = arguments.bvec param.fname_bvals = arguments.bval param.bval_min = arguments.bvalmin param.group_size = arguments.g param.fname_mask = arguments.m param.interp = arguments.x param.path_out = arguments.ofolder param.remove_temp_files = arguments.r if arguments.param is not None: param.update(arguments.param) path_qc = arguments.qc qc_fps = arguments.qc_fps qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject qc_seg = arguments.qc_seg mutually_inclusive_args = (path_qc, qc_seg) is_qc_none, is_seg_none = [arg is None for arg in mutually_inclusive_args] if not (is_qc_none == is_seg_none): raise parser.error( "Both '-qc' and '-qc-seg' are required in order to generate a QC report." ) # run moco fname_output_image = moco_wrapper(param) set_global_loglevel( verbose) # moco_wrapper changes verbose to 0, see issue #3341 # QC report if path_qc is not None: generate_qc(fname_in1=fname_output_image, fname_in2=param.fname_data, fname_seg=qc_seg, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), fps=qc_fps, dataset=qc_dataset, subject=qc_subject, process='sct_dmri_moco') display_viewer_syntax([fname_output_image, param.fname_data], mode='ortho,ortho')
def main(arguments): fname_input_data = os.path.abspath(arguments["-i"]) img_input = Image(fname_input_data) img_seg = propseg(img_input, arguments) fname_seg = img_seg.absolutepath path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) if path_qc is not None: generate_qc(fname_in1=fname_input_data, fname_seg=fname_seg, args=args, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_propseg') sct.display_viewer_syntax([fname_input_data, fname_seg], colormaps=['gray', 'red'], opacities=['', '1'])
def gen_qc(args): i, path_qc = args t2_image = os.path.join(__sct_dir__, 'sct_testing_data', 't2', 't2.nii.gz') t2_seg = os.path.join(__sct_dir__, 'sct_testing_data', 't2', 't2_seg-manual.nii.gz') qc.generate_qc(fname_in1=t2_image, fname_seg=t2_seg, path_qc=path_qc, process="sct_deepseg_gm") return True
def main(arguments): fname_input_data = os.path.abspath(arguments["-i"]) img_input = Image(fname_input_data) img_seg = propseg(img_input, arguments) fname_seg = img_seg.absolutepath path_qc = arguments.get("-qc", None) if path_qc is not None: generate_qc(fname_in1=fname_input_data, fname_seg=fname_seg, args=args, path_qc=os.path.abspath(path_qc), process='sct_propseg') sct.display_viewer_syntax([fname_input_data, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
def main(args=None): parser = get_parser() arguments = parser.parse(sys.argv[1:]) fname_src = arguments["-d"] fname_transfo = arguments["-w"] warp_atlas = int(arguments["-a"]) warp_spinal_levels = int(arguments["-s"]) folder_out = arguments['-ofolder'] path_template = arguments['-t'] verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) # 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) # Only deal with QC and verbose if white matter was warped (meaning, everything under template/) if "white matter" in spinalcordtoolbox.metadata.get_indiv_label_names(path_template): # Deal with QC report if path_qc is not None: fname_wm = os.path.join(w.folder_out, w.folder_template, spinalcordtoolbox.metadata.get_file_label(path_template, 'white matter')) 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') # Deal with verbose sct.display_viewer_syntax( [ fname_src, spinalcordtoolbox.metadata.get_file_label(path_template, 'T2-weighted template', output="filewithpath"), spinalcordtoolbox.metadata.get_file_label(path_template, 'gray matter', output="filewithpath"), spinalcordtoolbox.metadata.get_file_label(path_template, 'white matter', output="filewithpath") ], 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, ) else: if path_qc is not None: sct.printv("QC not generated since expected labels are missing from template", type="warning" )
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv if argv else ['--help']) verbose = arguments.v set_global_loglevel(verbose=verbose) input_filename = arguments.i if arguments.o is not None: output_filename = arguments.o else: output_filename = add_suffix(input_filename, '_gmseg') use_tta = arguments.t model_name = arguments.m threshold = arguments.thr if threshold > 1.0 or threshold < 0.0: raise RuntimeError("Threshold should be between 0.0 and 1.0.") # Threshold zero means no thresholding if threshold == 0.0: threshold = None from spinalcordtoolbox.deepseg_gm import deepseg_gm deepseg_gm.check_backend() out_fname = deepseg_gm.segment_file(input_filename, output_filename, model_name, threshold, int(verbose), use_tta) path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject if path_qc is not None: generate_qc(fname_in1=input_filename, fname_seg=out_fname, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_deepseg_gm') display_viewer_syntax([input_filename, format(out_fname)], colormaps=['gray', 'red'], opacities=['1', '0.7'], verbose=verbose)
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) fname_input_data = os.path.abspath(arguments.i) img_input = Image(fname_input_data) img_seg = propseg(img_input, arguments) fname_seg = img_seg.absolutepath path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject if path_qc is not None: generate_qc(fname_in1=fname_input_data, fname_seg=fname_seg, args=arguments, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_propseg') display_viewer_syntax([fname_input_data, fname_seg], colormaps=['gray', 'red'], opacities=['', '1'])
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)
def main(arguments): fname_input_data = os.path.abspath(arguments.i) img_input = Image(fname_input_data) img_seg = propseg(img_input, arguments) fname_seg = img_seg.absolutepath path_qc = arguments.qc qc_dataset = arguments.qc_dataset qc_subject = arguments.qc_subject if path_qc is not None: generate_qc(fname_in1=fname_input_data, fname_seg=fname_seg, args=arguments, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_propseg') sct.display_viewer_syntax([fname_input_data, fname_seg], colormaps=['gray', 'red'], opacities=['', '1'])
def run_main(): parser = get_parser() arguments = parser.parse(sys.argv[1:]) input_filename = arguments["-i"] try: output_filename = arguments["-o"] except KeyError: output_filename = sct.add_suffix(input_filename, '_gmseg') use_tta = "-t" in arguments verbose = arguments["-v"] model_name = arguments["-m"] threshold = arguments['-thr'] if threshold > 1.0 or threshold < 0.0: raise RuntimeError("Threshold should be between 0.0 and 1.0.") # Threshold zero means no thresholding if threshold == 0.0: threshold = None from spinalcordtoolbox.deepseg_gm import deepseg_gm deepseg_gm.check_backend() out_fname = deepseg_gm.segment_file(input_filename, output_filename, model_name, threshold, int(verbose), use_tta) path_qc = arguments.get("-qc", None) if path_qc is not None: generate_qc(fname_in1=input_filename, fname_seg=out_fname, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), process='sct_deepseg_gm') sct.display_viewer_syntax( [input_filename, format(out_fname)], colormaps=['gray', 'red'], opacities=['1', '0.7'], verbose=verbose)
def run_main(): parser = get_parser() arguments = parser.parse(sys.argv[1:]) input_filename = arguments["-i"] try: output_filename = arguments["-o"] except KeyError: output_filename = sct.add_suffix(input_filename, '_gmseg') use_tta = "-t" in arguments model_name = arguments["-m"] threshold = arguments['-thr'] verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level if threshold > 1.0 or threshold < 0.0: raise RuntimeError("Threshold should be between 0.0 and 1.0.") # Threshold zero means no thresholding if threshold == 0.0: threshold = None from spinalcordtoolbox.deepseg_gm import deepseg_gm deepseg_gm.check_backend() out_fname = deepseg_gm.segment_file(input_filename, output_filename, model_name, threshold, int(verbose), use_tta) path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) if path_qc is not None: generate_qc(fname_in1=input_filename, fname_seg=out_fname, args=sys.argv[1:], path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_deepseg_gm') sct.display_viewer_syntax([input_filename, format(out_fname)], colormaps=['gray', 'red'], opacities=['1', '0.7'], verbose=verbose)
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv if argv else ['--help']) verbose = arguments.v set_global_loglevel(verbose=verbose) from spinalcordtoolbox.reports.qc import generate_qc # Build args list (for display) args_disp = '-i ' + arguments.i if arguments.d: args_disp += ' -d ' + arguments.d if arguments.s: args_disp += ' -s ' + arguments.s generate_qc(fname_in1=arguments.i, fname_in2=arguments.d, fname_seg=arguments.s, args=args_disp, path_qc=arguments.qc, dataset=arguments.qc_dataset, subject=arguments.qc_subject, process=arguments.p)
def main(args=None): if args is None: args = sys.argv[1:] # Get parser parser = get_parser() arguments = parser.parse(args) # Set param arguments ad inputted by user fname_in = arguments["-i"] contrast = arguments["-c"] # Segmentation or Centerline line if '-s' in arguments: 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 '-ofolder' in arguments: 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.get("-qc", None) # Remove temp folder rm_tmp = bool(int(arguments.get("-r", 1))) # Verbosity verbose = int(arguments.get("-v", 1)) # 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: generate_qc(fname_in, fname_seg=fname_out, args=args, path_qc=os.path.abspath(path_qc), process='sct_detect_pmj') sct.display_viewer_syntax([fname_in, fname_out], colormaps=['gray', 'red'])
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv if argv else ['--help']) verbose = arguments.v set_global_loglevel(verbose=verbose) # initializations initz = '' initcenter = '' fname_initlabel = '' file_labelz = 'labelz.nii.gz' param = Param() 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]) remove_temp_files = arguments.r clean_labels = arguments.clean_labels 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(argv=[ '-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( 'sct_apply_transfo -i %s -d %s -w %s -o %s -x %s' % (fname_disc, 'data_straightr.nii', 'warp_curve2straight.nii.gz', 'labeldisc_straight.nii.gz', 'label'), verbose=verbose) 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, ) if clean_labels: # 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) printv('\nUn-straighten labeled discs...', verbose) run_proc( 'sct_apply_transfo -i %s -d %s -w %s -o %s -x %s' % ('segmentation_straight_labeled_disc.nii', 'segmentation.nii', 'warp_straight2curve.nii.gz', 'segmentation_labeled_disc.nii', 'label'), verbose=verbose, is_sct_binary=True, ) # 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=argv, 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(argv: Sequence[str]): """ Main function. When this script is run via CLI, the main function is called using main(sys.argv[1:]). :param argv: A list of unparsed arguments, which is passed to ArgumentParser.parse_args() """ for i, arg in enumerate(argv): if arg == '-create-seg' and len(argv) > i+1 and '-1,' in argv[i+1]: raise DeprecationWarning("The use of '-1' for '-create-seg' has been deprecated. Please use " "'-create-seg-mid' instead.") parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) 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.create_seg_mid is not None: labels = [(-1, arguments.create_seg_mid)] 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, verbose) 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, parse_num_list(arguments.create_viewer), msg) else: out = launch_sagittal_viewer(img, parse_num_list(arguments.create_viewer), msg) printv("Generating output files...") out.save(path=output_fname, dtype=dtype) display_viewer_syntax([input_filename, output_fname]) if arguments.qc is not None: generate_qc(fname_in1=input_filename, fname_seg=output_fname, args=argv, path_qc=os.path.abspath(arguments.qc), dataset=arguments.qc_dataset, subject=arguments.qc_subject, process='sct_label_utils')
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(args=None): # initializations param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_seg = arguments['-s'] if '-l' in arguments: fname_landmarks = arguments['-l'] label_type = 'body' elif '-ldisc' in arguments: fname_landmarks = arguments['-ldisc'] label_type = 'disc' else: sct.printv('ERROR: Labels should be provided.', 1, 'error') if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = '' param.path_qc = arguments.get("-qc", None) path_template = arguments['-t'] contrast_template = arguments['-c'] ref = arguments['-ref'] param.remove_temp_files = int(arguments.get('-r')) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level param.verbose = verbose # TODO: not clean, unify verbose or param.verbose in code, but not both param_centerline = ParamCenterline( algo_fitting=arguments['-centerline-algo'], smooth=arguments['-centerline-smooth']) # registration parameters if '-param' in arguments: # reset parameters but keep step=0 (might be overwritten if user specified step=0) paramreg = ParamregMultiStep([step0]) if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # add user parameters for paramStep in arguments['-param']: paramreg.addStep(paramStep) else: paramreg = ParamregMultiStep([step0, step1, step2]) # if ref=subject, initialize registration using different affine parameters if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # initialize other parameters zsubsample = param.zsubsample # retrieve template file names file_template_vertebral_labeling = get_file_label(os.path.join(path_template, 'template'), 'vertebral labeling') file_template = get_file_label(os.path.join(path_template, 'template'), contrast_template.upper() + '-weighted template') file_template_seg = get_file_label(os.path.join(path_template, 'template'), 'spinal cord') # start timer start_time = time.time() # get fname of the template + template objects fname_template = os.path.join(path_template, 'template', file_template) fname_template_vertebral_labeling = os.path.join(path_template, 'template', file_template_vertebral_labeling) fname_template_seg = os.path.join(path_template, 'template', file_template_seg) fname_template_disc_labeling = os.path.join(path_template, 'template', 'PAM50_label_disc.nii.gz') # check file existence # TODO: no need to do that! sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_vertebral_labeling, verbose) sct.check_file_exist(fname_template_seg, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) # sct.printv(arguments) sct.printv('\nCheck parameters:', verbose) sct.printv(' Data: ' + fname_data, verbose) sct.printv(' Landmarks: ' + fname_landmarks, verbose) sct.printv(' Segmentation: ' + fname_seg, verbose) sct.printv(' Path template: ' + path_template, verbose) sct.printv(' Remove temp files: ' + str(param.remove_temp_files), verbose) # check input labels labels = check_labels(fname_landmarks, label_type=label_type) vertebral_alignment = False if len(labels) > 2 and label_type == 'disc': vertebral_alignment = True path_tmp = sct.tmp_create(basename="register_to_template", verbose=verbose) # set temporary file names ftmp_data = 'data.nii' ftmp_seg = 'seg.nii.gz' ftmp_label = 'label.nii.gz' ftmp_template = 'template.nii' ftmp_template_seg = 'template_seg.nii.gz' ftmp_template_label = 'template_label.nii.gz' # copy files to temporary folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) Image(fname_data).save(os.path.join(path_tmp, ftmp_data)) Image(fname_seg).save(os.path.join(path_tmp, ftmp_seg)) Image(fname_landmarks).save(os.path.join(path_tmp, ftmp_label)) Image(fname_template).save(os.path.join(path_tmp, ftmp_template)) Image(fname_template_seg).save(os.path.join(path_tmp, ftmp_template_seg)) Image(fname_template_vertebral_labeling).save(os.path.join(path_tmp, ftmp_template_label)) if label_type == 'disc': Image(fname_template_disc_labeling).save(os.path.join(path_tmp, ftmp_template_label)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Generate labels from template vertebral labeling if label_type == 'body': sct.printv('\nGenerate labels from template vertebral labeling', verbose) ftmp_template_label_, ftmp_template_label = ftmp_template_label, sct.add_suffix(ftmp_template_label, "_body") sct_label_utils.main(args=['-i', ftmp_template_label_, '-vert-body', '0', '-o', ftmp_template_label]) # check if provided labels are available in the template sct.printv('\nCheck if provided labels are available in the template', verbose) image_label_template = Image(ftmp_template_label) labels_template = image_label_template.getNonZeroCoordinates(sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv('ERROR: Wrong landmarks input. Labels must have correspondence in template space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), verbose, 'error') # if only one label is present, force affine transformation to be Tx,Ty,Tz only (no scaling) if len(labels) == 1: paramreg.steps['0'].dof = 'Tx_Ty_Tz' sct.printv('WARNING: Only one label is present. Forcing initial transformation to: ' + paramreg.steps['0'].dof, 1, 'warning') # Project labels onto the spinal cord centerline because later, an affine transformation is estimated between the # template's labels (centered in the cord) and the subject's labels (assumed to be centered in the cord). # If labels are not centered, mis-registration errors are observed (see issue #1826) ftmp_label = project_labels_on_spinalcord(ftmp_label, ftmp_seg, param_centerline) # binarize segmentation (in case it has values below 0 caused by manual editing) sct.printv('\nBinarize segmentation', verbose) ftmp_seg_, ftmp_seg = ftmp_seg, sct.add_suffix(ftmp_seg, "_bin") sct_maths.main(['-i', ftmp_seg_, '-bin', '0.5', '-o', ftmp_seg]) # Switch between modes: subject->template or template->subject if ref == 'template': # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) resample_file(ftmp_data, add_suffix(ftmp_data, '_1mm'), '1.0x1.0x1.0', 'mm', 'linear', verbose) ftmp_data = add_suffix(ftmp_data, '_1mm') resample_file(ftmp_seg, add_suffix(ftmp_seg, '_1mm'), '1.0x1.0x1.0', 'mm', 'linear', verbose) ftmp_seg = add_suffix(ftmp_seg, '_1mm') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling # with nearest neighbour can make them disappear. resample_labels(ftmp_label, ftmp_data, add_suffix(ftmp_label, '_1mm')) ftmp_label = add_suffix(ftmp_label, '_1mm') # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) ftmp_data = Image(ftmp_data).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg = Image(ftmp_seg).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_label = Image(ftmp_label).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop') if vertebral_alignment: # cropping the segmentation based on the label coverage to ensure good registration with vertebral alignment # See https://github.com/neuropoly/spinalcordtoolbox/pull/1669 for details image_labels = Image(ftmp_label) coordinates_labels = image_labels.getNonZeroCoordinates(sorting='z') nx, ny, nz, nt, px, py, pz, pt = image_labels.dim offset_crop = 10.0 * pz # cropping the image 10 mm above and below the highest and lowest label cropping_slices = [coordinates_labels[0].z - offset_crop, coordinates_labels[-1].z + offset_crop] # make sure that the cropping slices do not extend outside of the slice range (issue #1811) if cropping_slices[0] < 0: cropping_slices[0] = 0 if cropping_slices[1] > nz: cropping_slices[1] = nz msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, np.int32(np.round(cropping_slices))),))).save(ftmp_seg) else: # if we do not align the vertebral levels, we crop the segmentation from top to bottom im_seg_rpi = Image(ftmp_seg_) bottom = 0 for data in msct_image.SlicerOneAxis(im_seg_rpi, "IS"): if (data != 0).any(): break bottom += 1 top = im_seg_rpi.data.shape[2] for data in msct_image.SlicerOneAxis(im_seg_rpi, "SI"): if (data != 0).any(): break top -= 1 msct_image.spatial_crop(im_seg_rpi, dict(((2, (bottom, top)),))).save(ftmp_seg) # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) fn_warp_curve2straight = os.path.join(curdir, "warp_curve2straight.nii.gz") fn_warp_straight2curve = os.path.join(curdir, "warp_straight2curve.nii.gz") fn_straight_ref = os.path.join(curdir, "straight_ref.nii.gz") cache_input_files=[ftmp_seg] if vertebral_alignment: cache_input_files += [ ftmp_template_seg, ftmp_label, ftmp_template_label, ] cache_sig = sct.cache_signature( input_files=cache_input_files, ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile(fn_warp_curve2straight) and os.path.isfile(fn_warp_straight2curve) and os.path.isfile(fn_straight_ref): sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(fn_warp_curve2straight, 'warp_curve2straight.nii.gz') sct.copy(fn_warp_straight2curve, 'warp_straight2curve.nii.gz') sct.copy(fn_straight_ref, 'straight_ref.nii.gz') # apply straightening sct_apply_transfo.main(args=[ '-i', ftmp_seg, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', add_suffix(ftmp_seg, '_straight')]) else: from spinalcordtoolbox.straightening import SpinalCordStraightener sc_straight = SpinalCordStraightener(ftmp_seg, ftmp_seg) sc_straight.param_centerline = param_centerline sc_straight.output_filename = add_suffix(ftmp_seg, '_straight') sc_straight.path_output = './' sc_straight.qc = '0' sc_straight.remove_temp_files = param.remove_temp_files sc_straight.verbose = verbose if vertebral_alignment: sc_straight.centerline_reference_filename = ftmp_template_seg sc_straight.use_straight_reference = True sc_straight.discs_input_filename = ftmp_label sc_straight.discs_ref_filename = ftmp_template_label sc_straight.straighten() sct.cache_save(cachefile, cache_sig) # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER # re-define warping field using non-cropped space (to avoid issue #367) sct_concat_transfo.main(args=[ '-w', 'warp_straight2curve.nii.gz', '-d', ftmp_data, '-o', 'warp_straight2curve.nii.gz']) if vertebral_alignment: sct.copy('warp_curve2straight.nii.gz', 'warp_curve2straightAffine.nii.gz') else: # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run(['sct_label_utils', '-i', ftmp_template_label, '-o', ftmp_template_label, '-remove-reference', ftmp_label]) # Dilating the input label so they can be straighten without losing them sct.printv('\nDilating input labels using 3vox ball radius') sct_maths.main(['-i', ftmp_label, '-dilate', '3', '-o', add_suffix(ftmp_label, '_dilate')]) ftmp_label = add_suffix(ftmp_label, '_dilate') # Apply straightening to labels sct.printv('\nApply straightening to labels...', verbose) sct_apply_transfo.main(args=[ '-i', ftmp_label, '-o', add_suffix(ftmp_label, '_straight'), '-d', add_suffix(ftmp_seg, '_straight'), '-w', 'warp_curve2straight.nii.gz', '-x', 'nn']) ftmp_label = add_suffix(ftmp_label, '_straight') # Compute rigid transformation straight landmarks --> template landmarks sct.printv('\nEstimate transformation for step #0...', verbose) try: register_landmarks(ftmp_label, ftmp_template_label, paramreg.steps['0'].dof, fname_affine='straight2templateAffine.txt', verbose=verbose) except RuntimeError: raise('Input labels do not seem to be at the right place. Please check the position of the labels. ' 'See documentation for more details: https://www.slideshare.net/neuropoly/sct-course-20190121/42') # Concatenate transformations: curve --> straight --> affine sct.printv('\nConcatenate transformations: curve --> straight --> affine...', verbose) sct_concat_transfo.main(args=[ '-w', ['warp_curve2straight.nii.gz', 'straight2templateAffine.txt'], '-d', 'template.nii', '-o', 'warp_curve2straightAffine.nii.gz']) # Apply transformation sct.printv('\nApply transformation...', verbose) sct_apply_transfo.main(args=[ '-i', ftmp_data, '-o', add_suffix(ftmp_data, '_straightAffine'), '-d', ftmp_template, '-w', 'warp_curve2straightAffine.nii.gz']) ftmp_data = add_suffix(ftmp_data, '_straightAffine') sct_apply_transfo.main(args=[ '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_straightAffine'), '-d', ftmp_template, '-w', 'warp_curve2straightAffine.nii.gz', '-x', 'linear']) ftmp_seg = add_suffix(ftmp_seg, '_straightAffine') """ # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform. # Solution found: remove slices below and above landmarks to avoid rotation effects points_straight = [] for coord in landmark_template: points_straight.append(coord.z) min_point, max_point = int(np.round(np.min(points_straight))), int(np.round(np.max(points_straight))) ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_black') msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, (min_point,max_point)),))).save(ftmp_seg) """ # open segmentation im = Image(ftmp_seg) im_new = msct_image.empty_like(im) # binarize im_new.data = im.data > 0.5 # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = msct_image.find_zmin_zmax(im_new, threshold=0.5) # save binarized segmentation im_new.save(add_suffix(ftmp_seg, '_bin')) # unused? # crop template in z-direction (for faster processing) # TODO: refactor to use python module instead of doing i/o sct.printv('\nCrop data in template space (for faster processing)...', verbose) ftmp_template_, ftmp_template = ftmp_template, add_suffix(ftmp_template, '_crop') msct_image.spatial_crop(Image(ftmp_template_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_template) ftmp_template_seg_, ftmp_template_seg = ftmp_template_seg, add_suffix(ftmp_template_seg, '_crop') msct_image.spatial_crop(Image(ftmp_template_seg_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_template_seg) ftmp_data_, ftmp_data = ftmp_data, add_suffix(ftmp_data, '_crop') msct_image.spatial_crop(Image(ftmp_data_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_data) ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop') msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_seg) # sub-sample in z-direction # TODO: refactor to use python module instead of doing i/o sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose) sct.run(['sct_resample', '-i', ftmp_template, '-o', add_suffix(ftmp_template, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_template = add_suffix(ftmp_template, '_sub') sct.run(['sct_resample', '-i', ftmp_template_seg, '-o', add_suffix(ftmp_template_seg, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub') sct.run(['sct_resample', '-i', ftmp_data, '-o', add_suffix(ftmp_data, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_data = add_suffix(ftmp_data, '_sub') sct.run(['sct_resample', '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_seg = add_suffix(ftmp_seg, '_sub') # Registration straight spinal cord to template sct.printv('\nRegister straight spinal cord to template...', verbose) # loop across registration steps warp_forward = [] warp_inverse = [] for i_step in range(1, len(paramreg.steps)): sct.printv('\nEstimate transformation for step #' + str(i_step) + '...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = ftmp_data dest = ftmp_template interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = ftmp_seg dest = ftmp_template_seg interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': src_seg = ftmp_seg dest_seg = ftmp_template_seg # if step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # apply transformation from previous step, to use as new src for registration sct_apply_transfo.main(args=[ '-i', src, '-d', dest, '-w', warp_forward, '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step]) src = add_suffix(src, '_regStep' + str(i_step - 1)) if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': # also apply transformation to the seg sct_apply_transfo.main(args=[ '-i', src_seg, '-d', dest_seg, '-w', warp_forward, '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step]) src_seg = add_suffix(src_seg, '_regStep' + str(i_step - 1)) # register src --> dest # TODO: display param for debugging if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': # im_seg case warp_forward_out, warp_inverse_out = register([src, src_seg], [dest, dest_seg], paramreg, param, str(i_step)) else: warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.append(warp_inverse_out) # Concatenate transformations: anat --> template sct.printv('\nConcatenate transformations: anat --> template...', verbose) warp_forward.insert(0, 'warp_curve2straightAffine.nii.gz') sct_concat_transfo.main(args=[ '-w', warp_forward, '-d', 'template.nii', '-o', 'warp_anat2template.nii.gz']) # Concatenate transformations: template --> anat sct.printv('\nConcatenate transformations: template --> anat...', verbose) warp_inverse.reverse() if vertebral_alignment: warp_inverse.append('warp_straight2curve.nii.gz') sct_concat_transfo.main(args=[ '-w', warp_inverse, '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz']) else: warp_inverse.append('straight2templateAffine.txt') warp_inverse.append('warp_straight2curve.nii.gz') sct_concat_transfo.main(args=[ '-w', warp_inverse, '-winv', ['straight2templateAffine.txt'], '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz']) # register template->subject elif ref == 'subject': # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) ftmp_data = Image(ftmp_data).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg = Image(ftmp_seg).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_label = Image(ftmp_label).change_orientation("RPI", generate_path=True).save().absolutepath # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run(['sct_label_utils', '-i', ftmp_template_label, '-o', ftmp_template_label, '-remove-reference', ftmp_label]) # Add one label because at least 3 orthogonal labels are required to estimate an affine transformation. This # new label is added at the level of the upper most label (lowest value), at 1cm to the right. for i_file in [ftmp_label, ftmp_template_label]: im_label = Image(i_file) coord_label = im_label.getCoordinatesAveragedByValue() # N.B. landmarks are sorted by value # Create new label from copy import deepcopy new_label = deepcopy(coord_label[0]) # move it 5mm to the left (orientation is RAS) nx, ny, nz, nt, px, py, pz, pt = im_label.dim new_label.x = np.round(coord_label[0].x + 5.0 / px) # assign value 99 new_label.value = 99 # Add to existing image im_label.data[int(new_label.x), int(new_label.y), int(new_label.z)] = new_label.value # Overwrite label file # im_label.absolutepath = 'label_rpi_modif.nii.gz' im_label.save() # Bring template to subject space using landmark-based transformation sct.printv('\nEstimate transformation for step #0...', verbose) warp_forward = ['template2subjectAffine.txt'] warp_inverse = ['template2subjectAffine.txt'] try: register_landmarks(ftmp_template_label, ftmp_label, paramreg.steps['0'].dof, fname_affine=warp_forward[0], verbose=verbose, path_qc="./") except Exception: sct.printv('ERROR: input labels do not seem to be at the right place. Please check the position of the labels. See documentation for more details: https://www.slideshare.net/neuropoly/sct-course-20190121/42', verbose=verbose, type='error') # loop across registration steps for i_step in range(1, len(paramreg.steps)): sct.printv('\nEstimate transformation for step #' + str(i_step) + '...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = ftmp_template dest = ftmp_data interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = ftmp_template_seg dest = ftmp_seg interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') # apply transformation from previous step, to use as new src for registration sct_apply_transfo.main(args=[ '-i', src, '-d', dest, '-w', warp_forward, '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step]) src = add_suffix(src, '_regStep' + str(i_step - 1)) # register src --> dest # TODO: display param for debugging warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.insert(0, warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: template --> subject...', verbose) sct_concat_transfo.main(args=[ '-w', warp_forward, '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz']) sct.printv('\nConcatenate transformations: subject --> template...', verbose) sct_concat_transfo.main(args=[ '-w', warp_inverse, '-winv', ['template2subjectAffine.txt'], '-d', 'template.nii', '-o', 'warp_anat2template.nii.gz']) # Apply warping fields to anat and template sct.run(['sct_apply_transfo', '-i', 'template.nii', '-o', 'template2anat.nii.gz', '-d', 'data.nii', '-w', 'warp_template2anat.nii.gz', '-crop', '1'], verbose) sct.run(['sct_apply_transfo', '-i', 'data.nii', '-o', 'anat2template.nii.gz', '-d', 'template.nii', '-w', 'warp_anat2template.nii.gz', '-crop', '1'], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) fname_template2anat = os.path.join(path_output, 'template2anat' + ext_data) fname_anat2template = os.path.join(path_output, 'anat2template' + ext_data) sct.generate_output_file(os.path.join(path_tmp, "warp_template2anat.nii.gz"), os.path.join(path_output, "warp_template2anat.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "warp_anat2template.nii.gz"), os.path.join(path_output, "warp_anat2template.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "template2anat.nii.gz"), fname_template2anat, verbose) sct.generate_output_file(os.path.join(path_tmp, "anat2template.nii.gz"), fname_anat2template, verbose) if ref == 'template': # 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) sct.generate_output_file(os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), verbose) # Delete temporary files if param.remove_temp_files: sct.printv('\nDelete temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) if param.path_qc is not None: generate_qc(fname_data, fname_in2=fname_template2anat, fname_seg=fname_seg, args=args, path_qc=os.path.abspath(param.path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_register_to_template') sct.display_viewer_syntax([fname_data, fname_template2anat], verbose=verbose) sct.display_viewer_syntax([fname_template, fname_anat2template], verbose=verbose)
def main(args=None): # initializations initz = '' initcenter = '' fname_initlabel = '' file_labelz = 'labelz.nii.gz' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_in = os.path.abspath(arguments["-i"]) fname_seg = os.path.abspath(arguments['-s']) contrast = arguments['-c'] path_template = arguments['-t'] scale_dist = arguments['-scale-dist'] if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = os.curdir param.path_qc = arguments.get("-qc", None) if '-discfile' in arguments: fname_disc = os.path.abspath(arguments['-discfile']) else: fname_disc = None if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] # if user provided text file, parse and overwrite arguments if '-initfile' in arguments: 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 '-initlabel' in arguments: # get absolute path of label fname_initlabel = os.path.abspath(arguments['-initlabel']) if '-param' in arguments: param.update(arguments['-param'][0]) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level remove_temp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(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 = sct.run(['sct_apply_transfo', '-i', 'data.nii', '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'data_straight.nii']) else: cmd = ['sct_straighten_spinalcord', '-i', 'data.nii', '-s', 'segmentation.nii', '-r', str(remove_temp_files)] if param.path_qc is not None and os.environ.get("SCT_RECURSIVE_QC", None) == "1": cmd += ['-qc', param.path_qc] s, o = sct.run(cmd) 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 = sct.run(['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) sct.run('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 sct.run(['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) sct.run('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] # create single label and output as labels.nii.gz label = ProcessLabels('segmentation.nii', fname_output='tmp.labelz.nii.gz', coordinates=['{},{}'.format(initz[0], initz[1])]) im_label = label.process('create-seg') im_label.data = sct_maths.dilate(im_label.data, [3]) # TODO: create a dilation method specific to labels, # which does not apply a convolution across all voxels (highly inneficient) im_label.save(fname_labelz) elif fname_initlabel: import sct_label_utils # subtract "1" to label value because due to legacy, in this code the disc C2-C3 has value "2", whereas in the # recent version of SCT it is defined as "3". Therefore, when asking the user to define a label, we point to the # new definition of labels (i.e., C2-C3 = 3). sct_label_utils.main(['-i', fname_initlabel, '-add', '-1', '-o', fname_labelz]) else: # automatically finds C2-C3 disc im_data = Image('data.nii') im_seg = Image('segmentation.nii') im_label_c2c3 = detect_c2c3(im_data, im_seg, contrast) ind_label = np.where(im_label_c2c3.data) if not np.size(ind_label) == 0: # subtract "1" to label value because due to legacy, in this code the disc C2-C3 has value "2", whereas in the # recent version of SCT it is defined as "3". im_label_c2c3.data[ind_label] = 2 else: sct.printv('Automatic C2-C3 detection failed. Please provide manual label with sct_label_utils', 1, 'error') im_label_c2c3.save(fname_labelz) # dilate label so it is not lost when applying warping sct_maths.main(['-i', fname_labelz, '-dilate', '3', '-o', fname_labelz]) # Apply straightening to z-label sct.printv('\nAnd apply straightening to label...', verbose) sct.run('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) sct.run(['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) sct.run(['sct_maths', '-i', 'data_straightr.nii', '-laplacian', '1', '-o', 'data_straightr.nii'], verbose) # detect vertebral levels on straight spinal cord 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) sct.run('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) sct.generate_output_file(os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), 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(param.path_qc) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) 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): 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, parse_num_list(arguments.create_viewer), msg) else: out = launch_sagittal_viewer(img, parse_num_list(arguments.create_viewer), msg) printv("Generating output files...") out.save(path=output_fname, dtype=dtype) display_viewer_syntax([input_filename, output_fname]) 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')
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') paramreg = ParamregMultiStep([step0, step1]) parser = get_parser(paramreg=paramreg) arguments = parser.parse(args) # get arguments fname_src = arguments['-i'] fname_dest = arguments['-d'] if '-iseg' in arguments: fname_src_seg = arguments['-iseg'] if '-dseg' in arguments: fname_dest_seg = arguments['-dseg'] if '-ilabel' in arguments: fname_src_label = arguments['-ilabel'] if '-dlabel' in arguments: fname_dest_label = arguments['-dlabel'] if '-o' in arguments: fname_output = arguments['-o'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-owarp' in arguments: fname_output_warp = arguments['-owarp'] else: fname_output_warp = '' if '-initwarp' in arguments: fname_initwarp = os.path.abspath(arguments['-initwarp']) else: fname_initwarp = '' if '-initwarpinv' in arguments: fname_initwarpinv = os.path.abspath(arguments['-initwarpinv']) else: fname_initwarpinv = '' if '-m' in arguments: fname_mask = arguments['-m'] else: fname_mask = '' padding = arguments['-z'] if "-param" in arguments: paramreg_user = arguments['-param'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) identity = int(arguments['-identity']) interp = arguments['-x'] remove_temp_files = int(arguments['-r']) verbose = int(arguments.get('-v')) sct.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 ........... '+paramreg.algo) # sct.printv(' Number of iterations '+paramreg.iter) # sct.printv(' Metric .............. '+paramreg.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_if_3d(fname_src) sct.check_if_3d(fname_dest) # Check if user selected type=seg, but did not input segmentation data if 'paramreg_user' in locals(): if True in ['type=seg' in paramreg_user[i] for i in range(len(paramreg_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') # Extract path, file and extension path_src, file_src, ext_src = sct.extract_fname(fname_src) path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest) # check if source and destination images have the same name (related to issue #373) # If so, change names to avoid conflict of result files and warns the user suffix_src, suffix_dest = '_reg', '_reg' if file_src == file_dest: suffix_src, suffix_dest = '_src_reg', '_dest_reg' # define output folder and file name if fname_output == '': path_out = '' if not path_out else path_out # output in user's current directory file_out = file_src + suffix_src file_out_inv = file_dest + suffix_dest ext_out = ext_src else: path, file_out, ext_out = sct.extract_fname(fname_output) path_out = path if not path_out else path_out file_out_inv = file_out + '_inv' # create temporary folder path_tmp = sct.tmp_create() sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) Image(fname_src).save(os.path.join(path_tmp, "src.nii")) Image(fname_dest).save(os.path.join(path_tmp, "dest.nii")) if fname_src_seg: Image(fname_src_seg).save(os.path.join(path_tmp, "src_seg.nii")) if fname_dest_seg: Image(fname_dest_seg).save(os.path.join(path_tmp, "dest_seg.nii")) if fname_src_label: Image(fname_src_label).save(os.path.join(path_tmp, "src_label.nii")) Image(fname_dest_label).save(os.path.join(path_tmp, "dest_label.nii")) if fname_mask != '': Image(fname_mask).save(os.path.join(path_tmp, "mask.nii.gz")) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # reorient destination to RPI Image('dest.nii').change_orientation("RPI").save('dest_RPI.nii') if fname_dest_seg: Image('dest_seg.nii').change_orientation("RPI").save('dest_seg_RPI.nii') if fname_dest_label: Image('dest_label.nii').change_orientation("RPI").save('dest_label_RPI.nii') if identity: # overwrite paramreg and only do one identity transformation step0 = Paramreg(step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5') paramreg = ParamregMultiStep([step0]) # 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) # sct.run('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 # initialize list of warping fields warp_forward = [] warp_inverse = [] # initial warping is specified, update list of warping fields and skip step=0 if fname_initwarp: sct.printv('\nSkip step=0 and replace with initial transformations: ', param.verbose) sct.printv(' ' + fname_initwarp, param.verbose) # sct.copy(fname_initwarp, 'warp_forward_0.nii.gz') warp_forward = [fname_initwarp] start_step = 1 if fname_initwarpinv: warp_inverse = [fname_initwarpinv] else: sct.printv('\nWARNING: No initial inverse warping field was specified, therefore the inverse warping field ' 'will NOT be generated.', param.verbose, 'warning') generate_warpinv = 0 else: start_step = 0 # loop across registration steps for i_step in range(start_step, len(paramreg.steps)): sct.printv('\n--\nESTIMATE TRANSFORMATION FOR STEP #' + str(i_step), param.verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 'src.nii' dest = 'dest_RPI.nii' interp_step = 'spline' elif paramreg.steps[str(i_step)].type == 'seg': src = 'src_seg.nii' dest = 'dest_seg_RPI.nii' interp_step = 'nn' elif paramreg.steps[str(i_step)].type == 'label': src = 'src_label.nii' dest = 'dest_label_RPI.nii' interp_step = 'nn' else: # src = dest = interp_step = None sct.printv('ERROR: Wrong image type.', 1, 'error') # if step>0, apply warp_forward_concat to the src image to be used if i_step > 0: sct.printv('\nApply transformation from previous step', param.verbose) sct.run(['sct_apply_transfo', '-i', src, '-d', dest, '-w', ','.join(warp_forward), '-o', sct.add_suffix(src, '_reg'), '-x', interp_step], verbose) src = sct.add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.insert(0, warp_inverse_out) # Concatenate transformations sct.printv('\nConcatenate transformations...', verbose) sct.run(['sct_concat_transfo', '-w', ','.join(warp_forward), '-d', 'dest.nii', '-o', 'warp_src2dest.nii.gz'], verbose) sct.run(['sct_concat_transfo', '-w', ','.join(warp_inverse), '-d', 'src.nii', '-o', 'warp_dest2src.nii.gz'], verbose) # Apply warping field to src data sct.printv('\nApply transfo source --> dest...', verbose) sct.run(['sct_apply_transfo', '-i', 'src.nii', '-o', 'src_reg.nii', '-d', 'dest.nii', '-w', 'warp_src2dest.nii.gz', '-x', interp], verbose) sct.printv('\nApply transfo dest --> source...', verbose) sct.run(['sct_apply_transfo', '-i', 'dest.nii', '-o', 'dest_reg.nii', '-d', 'src.nii', '-w', 'warp_dest2src.nii.gz', '-x', interp], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) # generate: src_reg fname_src2dest = sct.generate_output_file(os.path.join(path_tmp, "src_reg.nii"), os.path.join(path_out, file_out + ext_out), verbose) # generate: forward warping field if fname_output_warp == '': fname_output_warp = os.path.join(path_out, 'warp_' + file_src + '2' + file_dest + '.nii.gz') sct.generate_output_file(os.path.join(path_tmp, "warp_src2dest.nii.gz"), fname_output_warp, verbose) if generate_warpinv: # generate: dest_reg fname_dest2src = sct.generate_output_file(os.path.join(path_tmp, "dest_reg.nii"), os.path.join(path_out, file_out_inv + ext_dest), verbose) # generate: inverse warping field sct.generate_output_file(os.path.join(path_tmp, "warp_dest2src.nii.gz"), os.path.join(path_out, 'warp_' + file_dest + '2' + file_src + '.nii.gz'), verbose) # Delete temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # 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 main(args): parser = get_parser() arguments = parser.parse(args) # Initialization slices = '' group_funcs = (('MEAN', func_wa), ('STD', func_std)) # functions to perform when aggregating metrics along S-I fname_segmentation = sct.get_absolute_path(arguments['-i']) fname_vert_levels = '' if '-o' in arguments: file_out = os.path.abspath(arguments['-o']) else: file_out = '' if '-append' in arguments: append = int(arguments['-append']) else: append = 0 if '-vert' in arguments: vert_levels = arguments['-vert'] else: vert_levels = '' if '-r' in arguments: remove_temp_files = arguments['-r'] if '-vertfile' in arguments: fname_vert_levels = arguments['-vertfile'] if '-perlevel' in arguments: perlevel = arguments['-perlevel'] else: perlevel = None if '-z' in arguments: slices = arguments['-z'] if '-perslice' in arguments: perslice = arguments['-perslice'] else: perslice = None if '-angle-corr' in arguments: if arguments['-angle-corr'] == '1': angle_correction = True elif arguments['-angle-corr'] == '0': angle_correction = False param_centerline = ParamCenterline( algo_fitting=arguments['-centerline-algo'], smooth=arguments['-centerline-smooth'], minmax=True) path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level # update fields metrics_agg = {} if not file_out: file_out = 'csa.csv' metrics, fit_results = process_seg.compute_shape(fname_segmentation, angle_correction=angle_correction, param_centerline=param_centerline, verbose=verbose) for key in metrics: metrics_agg[key] = aggregate_per_slice_or_level(metrics[key], slices=parse_num_list(slices), levels=parse_num_list(vert_levels), perslice=perslice, perlevel=perlevel, vert_level=fname_vert_levels, group_funcs=group_funcs) metrics_agg_merged = _merge_dict(metrics_agg) save_as_csv(metrics_agg_merged, file_out, fname_in=fname_segmentation, append=append) # QC report (only show CSA for clarity) if path_qc is not None: generate_qc(fname_segmentation, args=args, path_qc=os.path.abspath(path_qc), dataset=qc_dataset, subject=qc_subject, path_img=_make_figure(metrics_agg_merged, fit_results), process='sct_process_segmentation') sct.display_open(file_out)
def main(): """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(): """Main function.""" sct.init_sct() parser = get_parser() args = sys.argv[1:] arguments = parser.parse(args) fname_image = os.path.abspath(arguments['-i']) contrast_type = arguments['-c'] ctr_algo = arguments["-centerline"] if "-brain" not in args: if contrast_type in ['t2s', 'dwi']: brain_bool = False if contrast_type in ['t1', 't2']: brain_bool = True else: brain_bool = bool(int(arguments["-brain"])) kernel_size = arguments["-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 '-ofolder' not in args: output_folder = os.getcwd() else: output_folder = arguments["-ofolder"] if ctr_algo == 'file' and "-file_centerline" not in args: sct.log.warning('Please use the flag -file_centerline to indicate the centerline filename.') sys.exit(1) if "-file_centerline" in args: manual_centerline_fname = arguments["-file_centerline"] ctr_algo = 'file' else: manual_centerline_fname = None remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) path_qc = arguments.get("-qc", None) 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) algo_config_stg += '\n\tDimension of the segmentation kernel convolutions: ' + kernel_size + '\n' sct.printv(algo_config_stg) 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, im_labels_viewer, im_ctr = 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, 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) if ctr_algo == 'viewer': # Save labels fname_labels = os.path.abspath(os.path.join(output_folder, sct.extract_fname(fname_image)[1] + '_labels-centerline' + sct.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, sct.extract_fname(fname_image)[1] + '_centerline' + sct.extract_fname(fname_image)[2])) im_ctr.save(fname_ctr) if path_qc is not None: generate_qc(fname_image, fname_seg=fname_seg, args=args, path_qc=os.path.abspath(path_qc), process='sct_deepseg_sc') sct.display_viewer_syntax([fname_image, fname_seg], colormaps=['gray', 'red'], opacities=['', '0.7'])
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) 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 remove_temp_files = args.r verbose = args.v sct.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 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) algo_config_stg += '\n\tDimension of the segmentation kernel convolutions: ' + kernel_size + '\n' sct.printv(algo_config_stg) # 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, im_labels_viewer, im_ctr = \ 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, 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])) # copy q/sform from input image to output segmentation im_seg.copy_qform_from_ref(im_image) im_seg.save(fname_seg) if ctr_algo == 'viewer': # Save labels fname_labels = os.path.abspath(os.path.join(output_folder, sct.extract_fname(fname_image)[1] + '_labels-centerline' + sct.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, sct.extract_fname(fname_image)[1] + '_centerline' + sct.extract_fname(fname_image)[2])) im_ctr.save(fname_ctr) 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(): """Main function.""" sct.init_sct() parser = get_parser() args = sys.argv[1:] arguments = parser.parse(args) fname_image = os.path.abspath(arguments['-i']) contrast_type = arguments['-c'] ctr_algo = arguments["-centerline"] if "-brain" not in args: if contrast_type in ['t2s', 'dwi']: brain_bool = False if contrast_type in ['t1', 't2']: brain_bool = True else: brain_bool = bool(int(arguments["-brain"])) kernel_size = arguments["-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 '-ofolder' not in args: output_folder = os.getcwd() else: output_folder = arguments["-ofolder"] if ctr_algo == 'file' and "-file_centerline" not in args: logger.warning('Please use the flag -file_centerline to indicate the centerline filename.') sys.exit(1) if "-file_centerline" in args: manual_centerline_fname = arguments["-file_centerline"] ctr_algo = 'file' else: manual_centerline_fname = None remove_temp_files = int(arguments['-r']) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) 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) algo_config_stg += '\n\tDimension of the segmentation kernel convolutions: ' + kernel_size + '\n' sct.printv(algo_config_stg) 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, im_labels_viewer, im_ctr = 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, 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) if ctr_algo == 'viewer': # Save labels fname_labels = os.path.abspath(os.path.join(output_folder, sct.extract_fname(fname_image)[1] + '_labels-centerline' + sct.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, sct.extract_fname(fname_image)[1] + '_centerline' + sct.extract_fname(fname_image)[2])) im_ctr.save(fname_ctr) if path_qc is not None: generate_qc(fname_image, fname_seg=fname_seg, args=args, 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): 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 path of the toolbox path_sct = os.environ.get("SCT_DIR", os.path.dirname(os.path.dirname(__file__))) # 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') paramreg = ParamregMultiStep([step0, step1]) parser = get_parser(paramreg=paramreg) arguments = parser.parse(args) # get arguments fname_src = arguments['-i'] fname_dest = arguments['-d'] if '-iseg' in arguments: fname_src_seg = arguments['-iseg'] if '-dseg' in arguments: fname_dest_seg = arguments['-dseg'] if '-ilabel' in arguments: fname_src_label = arguments['-ilabel'] if '-dlabel' in arguments: fname_dest_label = arguments['-dlabel'] if '-o' in arguments: fname_output = arguments['-o'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-owarp' in arguments: fname_output_warp = arguments['-owarp'] else: fname_output_warp = '' if '-initwarp' in arguments: fname_initwarp = os.path.abspath(arguments['-initwarp']) else: fname_initwarp = '' if '-initwarpinv' in arguments: fname_initwarpinv = os.path.abspath(arguments['-initwarpinv']) else: fname_initwarpinv = '' if '-m' in arguments: fname_mask = arguments['-m'] else: fname_mask = '' padding = arguments['-z'] if "-param" in arguments: paramreg_user = arguments['-param'] # update registration parameters for paramStep in paramreg_user: paramreg.addStep(paramStep) path_qc = arguments.get("-qc", None) identity = int(arguments['-identity']) interp = arguments['-x'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) # 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 ........... '+paramreg.algo) # sct.printv(' Number of iterations '+paramreg.iter) # sct.printv(' Metric .............. '+paramreg.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_if_3d(fname_src) sct.check_if_3d(fname_dest) # Check if user selected type=seg, but did not input segmentation data if 'paramreg_user' in locals(): if True in [ 'type=seg' in paramreg_user[i] for i in range(len(paramreg_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') # Extract path, file and extension path_src, file_src, ext_src = sct.extract_fname(fname_src) path_dest, file_dest, ext_dest = sct.extract_fname(fname_dest) # check if source and destination images have the same name (related to issue #373) # If so, change names to avoid conflict of result files and warns the user suffix_src, suffix_dest = '_reg', '_reg' if file_src == file_dest: suffix_src, suffix_dest = '_src_reg', '_dest_reg' # define output folder and file name if fname_output == '': path_out = '' if not path_out else path_out # output in user's current directory file_out = file_src + suffix_src file_out_inv = file_dest + suffix_dest ext_out = ext_src else: path, file_out, ext_out = sct.extract_fname(fname_output) path_out = path if not path_out else path_out file_out_inv = file_out + '_inv' # create temporary folder path_tmp = sct.tmp_create() sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) Image(fname_src).save(os.path.join(path_tmp, "src.nii")) Image(fname_dest).save(os.path.join(path_tmp, "dest.nii")) if fname_src_seg: Image(fname_src_seg).save(os.path.join(path_tmp, "src_seg.nii")) if fname_dest_seg: Image(fname_dest_seg).save(os.path.join(path_tmp, "dest_seg.nii")) if fname_src_label: Image(fname_src_label).save(os.path.join(path_tmp, "src_label.nii")) Image(fname_dest_label).save(os.path.join(path_tmp, "dest_label.nii")) if fname_mask != '': Image(fname_mask).save(os.path.join(path_tmp, "mask.nii.gz")) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # reorient destination to RPI Image('dest.nii').change_orientation("RPI").save('dest_RPI.nii') if fname_dest_seg: Image('dest_seg.nii').change_orientation("RPI").save( 'dest_seg_RPI.nii') if fname_dest_label: Image('dest_label.nii').change_orientation("RPI").save( 'dest_label_RPI.nii') if identity: # overwrite paramreg and only do one identity transformation step0 = Paramreg(step='0', type='im', algo='syn', metric='MI', iter='0', shrink='1', smooth='0', gradStep='0.5') paramreg = ParamregMultiStep([step0]) # 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) # sct.run('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 # initialize list of warping fields warp_forward = [] warp_inverse = [] # initial warping is specified, update list of warping fields and skip step=0 if fname_initwarp: sct.printv('\nSkip step=0 and replace with initial transformations: ', param.verbose) sct.printv(' ' + fname_initwarp, param.verbose) # sct.copy(fname_initwarp, 'warp_forward_0.nii.gz') warp_forward = [fname_initwarp] start_step = 1 if fname_initwarpinv: warp_inverse = [fname_initwarpinv] else: sct.printv( '\nWARNING: No initial inverse warping field was specified, therefore the inverse warping field ' 'will NOT be generated.', param.verbose, 'warning') generate_warpinv = 0 else: start_step = 0 # loop across registration steps for i_step in range(start_step, len(paramreg.steps)): sct.printv('\n--\nESTIMATE TRANSFORMATION FOR STEP #' + str(i_step), param.verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = 'src.nii' dest = 'dest_RPI.nii' interp_step = 'spline' elif paramreg.steps[str(i_step)].type == 'seg': src = 'src_seg.nii' dest = 'dest_seg_RPI.nii' interp_step = 'nn' elif paramreg.steps[str(i_step)].type == 'label': src = 'src_label.nii' dest = 'dest_label_RPI.nii' interp_step = 'nn' else: # src = dest = interp_step = None sct.printv('ERROR: Wrong image type.', 1, 'error') # if step>0, apply warp_forward_concat to the src image to be used if i_step > 0: sct.printv('\nApply transformation from previous step', param.verbose) sct.run([ 'sct_apply_transfo', '-i', src, '-d', dest, '-w', ','.join(warp_forward), '-o', sct.add_suffix(src, '_reg'), '-x', interp_step ], verbose) src = sct.add_suffix(src, '_reg') # register src --> dest warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.insert(0, warp_inverse_out) # Concatenate transformations sct.printv('\nConcatenate transformations...', verbose) sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_forward), '-d', 'dest.nii', '-o', 'warp_src2dest.nii.gz' ], verbose) sct.run([ 'sct_concat_transfo', '-w', ','.join(warp_inverse), '-d', 'src.nii', '-o', 'warp_dest2src.nii.gz' ], verbose) # Apply warping field to src data sct.printv('\nApply transfo source --> dest...', verbose) sct.run([ 'sct_apply_transfo', '-i', 'src.nii', '-o', 'src_reg.nii', '-d', 'dest.nii', '-w', 'warp_src2dest.nii.gz', '-x', interp ], verbose) sct.printv('\nApply transfo dest --> source...', verbose) sct.run([ 'sct_apply_transfo', '-i', 'dest.nii', '-o', 'dest_reg.nii', '-d', 'src.nii', '-w', 'warp_dest2src.nii.gz', '-x', interp ], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) # generate: src_reg fname_src2dest = sct.generate_output_file( os.path.join(path_tmp, "src_reg.nii"), os.path.join(path_out, file_out + ext_out), verbose) # generate: forward warping field if fname_output_warp == '': fname_output_warp = os.path.join( path_out, 'warp_' + file_src + '2' + file_dest + '.nii.gz') sct.generate_output_file(os.path.join(path_tmp, "warp_src2dest.nii.gz"), fname_output_warp, verbose) if generate_warpinv: # generate: dest_reg fname_dest2src = sct.generate_output_file( os.path.join(path_tmp, "dest_reg.nii"), os.path.join(path_out, file_out_inv + ext_dest), verbose) # generate: inverse warping field sct.generate_output_file( os.path.join(path_tmp, "warp_dest2src.nii.gz"), os.path.join(path_out, 'warp_' + file_dest + '2' + file_src + '.nii.gz'), verbose) # Delete temporary files if remove_temp_files: sct.printv('\nRemove temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # 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), 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 main(argv=None): """ Main function :param argv: :return: """ parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) 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 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)
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): # initializations param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) fname_data = arguments['-i'] fname_seg = arguments['-s'] if '-l' in arguments: fname_landmarks = arguments['-l'] label_type = 'body' elif '-ldisc' in arguments: fname_landmarks = arguments['-ldisc'] label_type = 'disc' else: sct.printv('ERROR: Labels should be provided.', 1, 'error') if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = '' param.path_qc = arguments.get("-qc", None) path_template = arguments['-t'] contrast_template = arguments['-c'] ref = arguments['-ref'] param.remove_temp_files = int(arguments.get('-r')) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level param.verbose = verbose # TODO: not clean, unify verbose or param.verbose in code, but not both param.straighten_fitting = arguments['-straighten-fitting'] # if '-cpu-nb' in arguments: # arg_cpu = ' -cpu-nb '+str(arguments['-cpu-nb']) # else: # arg_cpu = '' # registration parameters if '-param' in arguments: # reset parameters but keep step=0 (might be overwritten if user specified step=0) paramreg = ParamregMultiStep([step0]) if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # add user parameters for paramStep in arguments['-param']: paramreg.addStep(paramStep) else: paramreg = ParamregMultiStep([step0, step1, step2]) # if ref=subject, initialize registration using different affine parameters if ref == 'subject': paramreg.steps['0'].dof = 'Tx_Ty_Tz_Rx_Ry_Rz_Sz' # initialize other parameters zsubsample = param.zsubsample # retrieve template file names file_template_vertebral_labeling = get_file_label(os.path.join(path_template, 'template'), 'vertebral labeling') file_template = get_file_label(os.path.join(path_template, 'template'), contrast_template.upper() + '-weighted template') file_template_seg = get_file_label(os.path.join(path_template, 'template'), 'spinal cord') # start timer start_time = time.time() # get fname of the template + template objects fname_template = os.path.join(path_template, 'template', file_template) fname_template_vertebral_labeling = os.path.join(path_template, 'template', file_template_vertebral_labeling) fname_template_seg = os.path.join(path_template, 'template', file_template_seg) fname_template_disc_labeling = os.path.join(path_template, 'template', 'PAM50_label_disc.nii.gz') # check file existence # TODO: no need to do that! sct.printv('\nCheck template files...') sct.check_file_exist(fname_template, verbose) sct.check_file_exist(fname_template_vertebral_labeling, verbose) sct.check_file_exist(fname_template_seg, verbose) path_data, file_data, ext_data = sct.extract_fname(fname_data) # sct.printv(arguments) sct.printv('\nCheck parameters:', verbose) sct.printv(' Data: ' + fname_data, verbose) sct.printv(' Landmarks: ' + fname_landmarks, verbose) sct.printv(' Segmentation: ' + fname_seg, verbose) sct.printv(' Path template: ' + path_template, verbose) sct.printv(' Remove temp files: ' + str(param.remove_temp_files), verbose) # check input labels labels = check_labels(fname_landmarks, label_type=label_type) vertebral_alignment = False if len(labels) > 2 and label_type == 'disc': vertebral_alignment = True path_tmp = sct.tmp_create(basename="register_to_template", verbose=verbose) # set temporary file names ftmp_data = 'data.nii' ftmp_seg = 'seg.nii.gz' ftmp_label = 'label.nii.gz' ftmp_template = 'template.nii' ftmp_template_seg = 'template_seg.nii.gz' ftmp_template_label = 'template_label.nii.gz' # copy files to temporary folder sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) Image(fname_data).save(os.path.join(path_tmp, ftmp_data)) Image(fname_seg).save(os.path.join(path_tmp, ftmp_seg)) Image(fname_landmarks).save(os.path.join(path_tmp, ftmp_label)) Image(fname_template).save(os.path.join(path_tmp, ftmp_template)) Image(fname_template_seg).save(os.path.join(path_tmp, ftmp_template_seg)) Image(fname_template_vertebral_labeling).save(os.path.join(path_tmp, ftmp_template_label)) if label_type == 'disc': Image(fname_template_disc_labeling).save(os.path.join(path_tmp, ftmp_template_label)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # Generate labels from template vertebral labeling if label_type == 'body': sct.printv('\nGenerate labels from template vertebral labeling', verbose) ftmp_template_label_, ftmp_template_label = ftmp_template_label, sct.add_suffix(ftmp_template_label, "_body") sct_label_utils.main(args=['-i', ftmp_template_label_, '-vert-body', '0', '-o', ftmp_template_label]) # check if provided labels are available in the template sct.printv('\nCheck if provided labels are available in the template', verbose) image_label_template = Image(ftmp_template_label) labels_template = image_label_template.getNonZeroCoordinates(sorting='value') if labels[-1].value > labels_template[-1].value: sct.printv('ERROR: Wrong landmarks input. Labels must have correspondence in template space. \nLabel max ' 'provided: ' + str(labels[-1].value) + '\nLabel max from template: ' + str(labels_template[-1].value), verbose, 'error') # if only one label is present, force affine transformation to be Tx,Ty,Tz only (no scaling) if len(labels) == 1: paramreg.steps['0'].dof = 'Tx_Ty_Tz' sct.printv('WARNING: Only one label is present. Forcing initial transformation to: ' + paramreg.steps['0'].dof, 1, 'warning') # Project labels onto the spinal cord centerline because later, an affine transformation is estimated between the # template's labels (centered in the cord) and the subject's labels (assumed to be centered in the cord). # If labels are not centered, mis-registration errors are observed (see issue #1826) ftmp_label = project_labels_on_spinalcord(ftmp_label, ftmp_seg) # binarize segmentation (in case it has values below 0 caused by manual editing) sct.printv('\nBinarize segmentation', verbose) ftmp_seg_, ftmp_seg = ftmp_seg, sct.add_suffix(ftmp_seg, "_bin") sct_maths.main(['-i', ftmp_seg_, '-bin', '0.5', '-o', ftmp_seg]) # Switch between modes: subject->template or template->subject if ref == 'template': # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) resample_file(ftmp_data, add_suffix(ftmp_data, '_1mm'), '1.0x1.0x1.0', 'mm', 'linear', verbose) ftmp_data = add_suffix(ftmp_data, '_1mm') resample_file(ftmp_seg, add_suffix(ftmp_seg, '_1mm'), '1.0x1.0x1.0', 'mm', 'linear', verbose) ftmp_seg = add_suffix(ftmp_seg, '_1mm') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling # with nearest neighbour can make them disappear. resample_labels(ftmp_label, ftmp_data, add_suffix(ftmp_label, '_1mm')) ftmp_label = add_suffix(ftmp_label, '_1mm') # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) ftmp_data = Image(ftmp_data).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg = Image(ftmp_seg).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_label = Image(ftmp_label).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop') if vertebral_alignment: # cropping the segmentation based on the label coverage to ensure good registration with vertebral alignment # See https://github.com/neuropoly/spinalcordtoolbox/pull/1669 for details image_labels = Image(ftmp_label) coordinates_labels = image_labels.getNonZeroCoordinates(sorting='z') nx, ny, nz, nt, px, py, pz, pt = image_labels.dim offset_crop = 10.0 * pz # cropping the image 10 mm above and below the highest and lowest label cropping_slices = [coordinates_labels[0].z - offset_crop, coordinates_labels[-1].z + offset_crop] # make sure that the cropping slices do not extend outside of the slice range (issue #1811) if cropping_slices[0] < 0: cropping_slices[0] = 0 if cropping_slices[1] > nz: cropping_slices[1] = nz msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, np.int32(np.round(cropping_slices))),))).save(ftmp_seg) else: # if we do not align the vertebral levels, we crop the segmentation from top to bottom im_seg_rpi = Image(ftmp_seg_) bottom = 0 for data in msct_image.SlicerOneAxis(im_seg_rpi, "IS"): if (data != 0).any(): break bottom += 1 top = im_seg_rpi.data.shape[2] for data in msct_image.SlicerOneAxis(im_seg_rpi, "SI"): if (data != 0).any(): break top -= 1 msct_image.spatial_crop(im_seg_rpi, dict(((2, (bottom, top)),))).save(ftmp_seg) # straighten segmentation sct.printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) # check if warp_curve2straight and warp_straight2curve already exist (i.e. no need to do it another time) fn_warp_curve2straight = os.path.join(curdir, "warp_curve2straight.nii.gz") fn_warp_straight2curve = os.path.join(curdir, "warp_straight2curve.nii.gz") fn_straight_ref = os.path.join(curdir, "straight_ref.nii.gz") cache_input_files=[ftmp_seg] if vertebral_alignment: cache_input_files += [ ftmp_template_seg, ftmp_label, ftmp_template_label, ] cache_sig = sct.cache_signature( input_files=cache_input_files, ) cachefile = os.path.join(curdir, "straightening.cache") if sct.cache_valid(cachefile, cache_sig) and os.path.isfile(fn_warp_curve2straight) and os.path.isfile(fn_warp_straight2curve) and os.path.isfile(fn_straight_ref): sct.printv('Reusing existing warping field which seems to be valid', verbose, 'warning') sct.copy(fn_warp_curve2straight, 'warp_curve2straight.nii.gz') sct.copy(fn_warp_straight2curve, 'warp_straight2curve.nii.gz') sct.copy(fn_straight_ref, 'straight_ref.nii.gz') # apply straightening sct.run(['sct_apply_transfo', '-i', ftmp_seg, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', add_suffix(ftmp_seg, '_straight')]) else: from spinalcordtoolbox.straightening import SpinalCordStraightener sc_straight = SpinalCordStraightener(ftmp_seg, ftmp_seg) sc_straight.algo_fitting = param.straighten_fitting sc_straight.output_filename = add_suffix(ftmp_seg, '_straight') sc_straight.path_output = './' sc_straight.qc = '0' sc_straight.remove_temp_files = param.remove_temp_files sc_straight.verbose = verbose if vertebral_alignment: sc_straight.centerline_reference_filename = ftmp_template_seg sc_straight.use_straight_reference = True sc_straight.discs_input_filename = ftmp_label sc_straight.discs_ref_filename = ftmp_template_label sc_straight.straighten() sct.cache_save(cachefile, cache_sig) # N.B. DO NOT UPDATE VARIABLE ftmp_seg BECAUSE TEMPORARY USED LATER # re-define warping field using non-cropped space (to avoid issue #367) s, o = sct.run(['sct_concat_transfo', '-w', 'warp_straight2curve.nii.gz', '-d', ftmp_data, '-o', 'warp_straight2curve.nii.gz']) if vertebral_alignment: sct.copy('warp_curve2straight.nii.gz', 'warp_curve2straightAffine.nii.gz') else: # Label preparation: # -------------------------------------------------------------------------------- # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run(['sct_label_utils', '-i', ftmp_template_label, '-o', ftmp_template_label, '-remove-reference', ftmp_label]) # Dilating the input label so they can be straighten without losing them sct.printv('\nDilating input labels using 3vox ball radius') sct_maths.main(['-i', ftmp_label, '-dilate', '3', '-o', add_suffix(ftmp_label, '_dilate')]) ftmp_label = add_suffix(ftmp_label, '_dilate') # Apply straightening to labels sct.printv('\nApply straightening to labels...', verbose) sct.run(['sct_apply_transfo', '-i', ftmp_label, '-o', add_suffix(ftmp_label, '_straight'), '-d', add_suffix(ftmp_seg, '_straight'), '-w', 'warp_curve2straight.nii.gz', '-x', 'nn']) ftmp_label = add_suffix(ftmp_label, '_straight') # Compute rigid transformation straight landmarks --> template landmarks sct.printv('\nEstimate transformation for step #0...', verbose) try: register_landmarks(ftmp_label, ftmp_template_label, paramreg.steps['0'].dof, fname_affine='straight2templateAffine.txt', verbose=verbose) except RuntimeError: raise('Input labels do not seem to be at the right place. Please check the position of the labels. ' 'See documentation for more details: https://www.slideshare.net/neuropoly/sct-course-20190121/42') # Concatenate transformations: curve --> straight --> affine sct.printv('\nConcatenate transformations: curve --> straight --> affine...', verbose) sct.run(['sct_concat_transfo', '-w', 'warp_curve2straight.nii.gz,straight2templateAffine.txt', '-d', 'template.nii', '-o', 'warp_curve2straightAffine.nii.gz']) # Apply transformation sct.printv('\nApply transformation...', verbose) sct.run(['sct_apply_transfo', '-i', ftmp_data, '-o', add_suffix(ftmp_data, '_straightAffine'), '-d', ftmp_template, '-w', 'warp_curve2straightAffine.nii.gz']) ftmp_data = add_suffix(ftmp_data, '_straightAffine') sct.run(['sct_apply_transfo', '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_straightAffine'), '-d', ftmp_template, '-w', 'warp_curve2straightAffine.nii.gz', '-x', 'linear']) ftmp_seg = add_suffix(ftmp_seg, '_straightAffine') """ # Benjamin: Issue from Allan Martin, about the z=0 slice that is screwed up, caused by the affine transform. # Solution found: remove slices below and above landmarks to avoid rotation effects points_straight = [] for coord in landmark_template: points_straight.append(coord.z) min_point, max_point = int(np.round(np.min(points_straight))), int(np.round(np.max(points_straight))) ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_black') msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, (min_point,max_point)),))).save(ftmp_seg) """ # open segmentation im = Image(ftmp_seg) im_new = msct_image.empty_like(im) # binarize im_new.data = im.data > 0.5 # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = msct_image.find_zmin_zmax(im_new, threshold=0.5) # save binarized segmentation im_new.save(add_suffix(ftmp_seg, '_bin')) # unused? # crop template in z-direction (for faster processing) # TODO: refactor to use python module instead of doing i/o sct.printv('\nCrop data in template space (for faster processing)...', verbose) ftmp_template_, ftmp_template = ftmp_template, add_suffix(ftmp_template, '_crop') msct_image.spatial_crop(Image(ftmp_template_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_template) ftmp_template_seg_, ftmp_template_seg = ftmp_template_seg, add_suffix(ftmp_template_seg, '_crop') msct_image.spatial_crop(Image(ftmp_template_seg_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_template_seg) ftmp_data_, ftmp_data = ftmp_data, add_suffix(ftmp_data, '_crop') msct_image.spatial_crop(Image(ftmp_data_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_data) ftmp_seg_, ftmp_seg = ftmp_seg, add_suffix(ftmp_seg, '_crop') msct_image.spatial_crop(Image(ftmp_seg_), dict(((2, (zmin_template,zmax_template)),))).save(ftmp_seg) # sub-sample in z-direction # TODO: refactor to use python module instead of doing i/o sct.printv('\nSub-sample in z-direction (for faster processing)...', verbose) sct.run(['sct_resample', '-i', ftmp_template, '-o', add_suffix(ftmp_template, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_template = add_suffix(ftmp_template, '_sub') sct.run(['sct_resample', '-i', ftmp_template_seg, '-o', add_suffix(ftmp_template_seg, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_template_seg = add_suffix(ftmp_template_seg, '_sub') sct.run(['sct_resample', '-i', ftmp_data, '-o', add_suffix(ftmp_data, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_data = add_suffix(ftmp_data, '_sub') sct.run(['sct_resample', '-i', ftmp_seg, '-o', add_suffix(ftmp_seg, '_sub'), '-f', '1x1x' + zsubsample], verbose) ftmp_seg = add_suffix(ftmp_seg, '_sub') # Registration straight spinal cord to template sct.printv('\nRegister straight spinal cord to template...', verbose) # loop across registration steps warp_forward = [] warp_inverse = [] for i_step in range(1, len(paramreg.steps)): sct.printv('\nEstimate transformation for step #' + str(i_step) + '...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = ftmp_data dest = ftmp_template interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = ftmp_seg dest = ftmp_template_seg interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': src_seg = ftmp_seg dest_seg = ftmp_template_seg # if step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # apply transformation from previous step, to use as new src for registration sct.run(['sct_apply_transfo', '-i', src, '-d', dest, '-w', ','.join(warp_forward), '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step], verbose) src = add_suffix(src, '_regStep' + str(i_step - 1)) if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': # also apply transformation to the seg sct.run(['sct_apply_transfo', '-i', src_seg, '-d', dest_seg, '-w', ','.join(warp_forward), '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step], verbose) src_seg = add_suffix(src_seg, '_regStep' + str(i_step - 1)) # register src --> dest # TODO: display param for debugging if paramreg.steps[str(i_step)].algo == 'centermassrot' and paramreg.steps[str(i_step)].rot_method == 'hog': # im_seg case warp_forward_out, warp_inverse_out = register([src, src_seg], [dest, dest_seg], paramreg, param, str(i_step)) else: warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.append(warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: anat --> template...', verbose) sct.run(['sct_concat_transfo', '-w', 'warp_curve2straightAffine.nii.gz,' + ','.join(warp_forward), '-d', 'template.nii', '-o', 'warp_anat2template.nii.gz'], verbose) # sct.run('sct_concat_transfo -w warp_curve2straight.nii.gz,straight2templateAffine.txt,'+','.join(warp_forward)+' -d template.nii -o warp_anat2template.nii.gz', verbose) sct.printv('\nConcatenate transformations: template --> anat...', verbose) warp_inverse.reverse() if vertebral_alignment: sct.run(['sct_concat_transfo', '-w', ','.join(warp_inverse) + ',warp_straight2curve.nii.gz', '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz'], verbose) else: sct.run(['sct_concat_transfo', '-w', ','.join(warp_inverse) + ',-straight2templateAffine.txt,warp_straight2curve.nii.gz', '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz'], verbose) # register template->subject elif ref == 'subject': # Change orientation of input images to RPI sct.printv('\nChange orientation of input images to RPI...', verbose) ftmp_data = Image(ftmp_data).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_seg = Image(ftmp_seg).change_orientation("RPI", generate_path=True).save().absolutepath ftmp_label = Image(ftmp_label).change_orientation("RPI", generate_path=True).save().absolutepath # Remove unused label on template. Keep only label present in the input label image sct.printv('\nRemove unused label on template. Keep only label present in the input label image...', verbose) sct.run(['sct_label_utils', '-i', ftmp_template_label, '-o', ftmp_template_label, '-remove-reference', ftmp_label]) # Add one label because at least 3 orthogonal labels are required to estimate an affine transformation. This # new label is added at the level of the upper most label (lowest value), at 1cm to the right. for i_file in [ftmp_label, ftmp_template_label]: im_label = Image(i_file) coord_label = im_label.getCoordinatesAveragedByValue() # N.B. landmarks are sorted by value # Create new label from copy import deepcopy new_label = deepcopy(coord_label[0]) # move it 5mm to the left (orientation is RAS) nx, ny, nz, nt, px, py, pz, pt = im_label.dim new_label.x = np.round(coord_label[0].x + 5.0 / px) # assign value 99 new_label.value = 99 # Add to existing image im_label.data[int(new_label.x), int(new_label.y), int(new_label.z)] = new_label.value # Overwrite label file # im_label.absolutepath = 'label_rpi_modif.nii.gz' im_label.save() # Bring template to subject space using landmark-based transformation sct.printv('\nEstimate transformation for step #0...', verbose) warp_forward = ['template2subjectAffine.txt'] warp_inverse = ['-template2subjectAffine.txt'] try: register_landmarks(ftmp_template_label, ftmp_label, paramreg.steps['0'].dof, fname_affine=warp_forward[0], verbose=verbose, path_qc="./") except Exception: sct.printv('ERROR: input labels do not seem to be at the right place. Please check the position of the labels. See documentation for more details: https://www.slideshare.net/neuropoly/sct-course-20190121/42', verbose=verbose, type='error') # loop across registration steps for i_step in range(1, len(paramreg.steps)): sct.printv('\nEstimate transformation for step #' + str(i_step) + '...', verbose) # identify which is the src and dest if paramreg.steps[str(i_step)].type == 'im': src = ftmp_template dest = ftmp_data interp_step = 'linear' elif paramreg.steps[str(i_step)].type == 'seg': src = ftmp_template_seg dest = ftmp_seg interp_step = 'nn' else: sct.printv('ERROR: Wrong image type.', 1, 'error') # apply transformation from previous step, to use as new src for registration sct.run(['sct_apply_transfo', '-i', src, '-d', dest, '-w', ','.join(warp_forward), '-o', add_suffix(src, '_regStep' + str(i_step - 1)), '-x', interp_step], verbose) src = add_suffix(src, '_regStep' + str(i_step - 1)) # register src --> dest # TODO: display param for debugging warp_forward_out, warp_inverse_out = register(src, dest, paramreg, param, str(i_step)) warp_forward.append(warp_forward_out) warp_inverse.insert(0, warp_inverse_out) # Concatenate transformations: sct.printv('\nConcatenate transformations: template --> subject...', verbose) sct.run(['sct_concat_transfo', '-w', ','.join(warp_forward), '-d', 'data.nii', '-o', 'warp_template2anat.nii.gz'], verbose) sct.printv('\nConcatenate transformations: subject --> template...', verbose) sct.run(['sct_concat_transfo', '-w', ','.join(warp_inverse), '-d', 'template.nii', '-o', 'warp_anat2template.nii.gz'], verbose) # Apply warping fields to anat and template sct.run(['sct_apply_transfo', '-i', 'template.nii', '-o', 'template2anat.nii.gz', '-d', 'data.nii', '-w', 'warp_template2anat.nii.gz', '-crop', '1'], verbose) sct.run(['sct_apply_transfo', '-i', 'data.nii', '-o', 'anat2template.nii.gz', '-d', 'template.nii', '-w', 'warp_anat2template.nii.gz', '-crop', '1'], verbose) # come back os.chdir(curdir) # Generate output files sct.printv('\nGenerate output files...', verbose) fname_template2anat = os.path.join(path_output, 'template2anat' + ext_data) fname_anat2template = os.path.join(path_output, 'anat2template' + ext_data) sct.generate_output_file(os.path.join(path_tmp, "warp_template2anat.nii.gz"), os.path.join(path_output, "warp_template2anat.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "warp_anat2template.nii.gz"), os.path.join(path_output, "warp_anat2template.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "template2anat.nii.gz"), fname_template2anat, verbose) sct.generate_output_file(os.path.join(path_tmp, "anat2template.nii.gz"), fname_anat2template, verbose) if ref == 'template': # 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) sct.generate_output_file(os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), verbose) # Delete temporary files if param.remove_temp_files: sct.printv('\nDelete temporary files...', verbose) sct.rmtree(path_tmp, verbose=verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', verbose) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) if param.path_qc is not None: generate_qc(fname_data, fname_in2=fname_template2anat, fname_seg=fname_seg, args=args, path_qc=os.path.abspath(param.path_qc), dataset=qc_dataset, subject=qc_subject, process='sct_register_to_template') sct.display_viewer_syntax([fname_data, fname_template2anat], verbose=verbose) sct.display_viewer_syntax([fname_template, fname_anat2template], verbose=verbose)
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) # get arguments fname_src = arguments['-i'] fname_dest = arguments['-d'] if '-iseg' in arguments: fname_src_seg = arguments['-iseg'] if '-dseg' in arguments: fname_dest_seg = arguments['-dseg'] if '-ilabel' in arguments: fname_src_label = arguments['-ilabel'] if '-dlabel' in arguments: fname_dest_label = arguments['-dlabel'] if '-o' in arguments: fname_output = arguments['-o'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-owarp' in arguments: fname_output_warp = arguments['-owarp'] else: fname_output_warp = '' if '-initwarp' in arguments: fname_initwarp = os.path.abspath(arguments['-initwarp']) else: fname_initwarp = '' if '-initwarpinv' in arguments: fname_initwarpinv = os.path.abspath(arguments['-initwarpinv']) else: fname_initwarpinv = '' if '-m' in arguments: fname_mask = arguments['-m'] else: fname_mask = '' padding = arguments['-z'] if "-param" in arguments: paramregmulti_user = arguments['-param'] # update registration parameters for paramStep in paramregmulti_user: paramregmulti.addStep(paramStep) path_qc = arguments.get("-qc", None) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) identity = int(arguments['-identity']) interp = arguments['-x'] remove_temp_files = int(arguments['-r']) verbose = int(arguments.get('-v')) sct.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) # sct.run('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 main(args=None): # initializations initz = '' initcenter = '' fname_initlabel = '' file_labelz = 'labelz.nii.gz' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(args) 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'] if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = os.curdir param.path_qc = arguments.get("-qc", None) if '-discfile' in arguments: fname_disc = os.path.abspath(arguments['-discfile']) else: fname_disc = None if '-initz' in arguments: initz = arguments['-initz'] if '-initcenter' in arguments: initcenter = arguments['-initcenter'] # if user provided text file, parse and overwrite arguments if '-initfile' in arguments: 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 '-initlabel' in arguments: # get absolute path of label fname_initlabel = os.path.abspath(arguments['-initlabel']) if '-param' in arguments: param.update(arguments['-param'][0]) verbose = int(arguments.get('-v')) sct.init_sct(log_level=verbose, update=True) # Update log level remove_temp_files = int(arguments['-r']) denoise = int(arguments['-denoise']) laplacian = int(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 = sct.run([ '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 = sct.run([ '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) sct.run( '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 sct.run([ '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) sct.run( '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] # create single label and output as labels.nii.gz label = ProcessLabels( 'segmentation.nii', fname_output='tmp.labelz.nii.gz', coordinates=['{},{}'.format(initz[0], initz[1])]) im_label = label.process('create-seg') im_label.data = dilate( im_label.data, 3, 'ball') # TODO: create a dilation method specific to labels, # which does not apply a convolution across all voxels (highly inneficient) 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) sct.run( '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) sct.run([ '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) sct.run([ '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) sct.run( '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) sct.generate_output_file( os.path.join(path_tmp, "warp_straight2curve.nii.gz"), os.path.join(path_output, "warp_straight2curve.nii.gz"), verbose) sct.generate_output_file(os.path.join(path_tmp, "straight_ref.nii.gz"), os.path.join(path_output, "straight_ref.nii.gz"), 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(param.path_qc) qc_dataset = arguments.get("-qc-dataset", None) qc_subject = arguments.get("-qc-subject", None) 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'])