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) dim_list = ['x', 'y', 'z', 't'] fname_in = arguments.i fname_out = arguments.o output_type = arguments.type # Open file(s) im = Image(fname_in) data = im.data # 3d or 4d numpy array dim = im.dim # run command if arguments.otsu is not None: param = arguments.otsu data_out = sct_math.otsu(data, param) elif arguments.adap is not None: param = arguments.adap data_out = sct_math.adap(data, param[0], param[1]) elif arguments.otsu_median is not None: param = arguments.otsu_median data_out = sct_math.otsu_median(data, param[0], param[1]) elif arguments.thr is not None: param = arguments.thr data_out = sct_math.threshold(data, param) elif arguments.percent is not None: param = arguments.percent data_out = sct_math.perc(data, param) elif arguments.bin is not None: bin_thr = arguments.bin data_out = sct_math.binarize(data, bin_thr=bin_thr) elif arguments.add is not None: data2 = get_data_or_scalar(arguments.add, data) data_concat = sct_math.concatenate_along_4th_dimension(data, data2) data_out = np.sum(data_concat, axis=3) elif arguments.sub is not None: data2 = get_data_or_scalar(arguments.sub, data) data_out = data - data2 elif arguments.laplacian is not None: sigmas = arguments.laplacian if len(sigmas) == 1: sigmas = [sigmas for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv( parser.error( 'ERROR: -laplacian need the same number of inputs as the number of image dimension OR only one input' )) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i + 4] for i in range(3)] # smooth data data_out = sct_math.laplacian(data, sigmas) elif arguments.mul is not None: data2 = get_data_or_scalar(arguments.mul, data) data_concat = sct_math.concatenate_along_4th_dimension(data, data2) data_out = np.prod(data_concat, axis=3) elif arguments.div is not None: data2 = get_data_or_scalar(arguments.div, data) data_out = np.divide(data, data2) elif arguments.mean is not None: dim = dim_list.index(arguments.mean) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = np.mean(data, dim) elif arguments.rms is not None: dim = dim_list.index(arguments.rms) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = np.sqrt(np.mean(np.square(data.astype(float)), dim)) elif arguments.std is not None: dim = dim_list.index(arguments.std) if dim + 1 > len( np.shape(data)): # in case input volume is 3d and dim=t data = data[..., np.newaxis] data_out = np.std(data, dim, ddof=1) elif arguments.smooth is not None: sigmas = arguments.smooth if len(sigmas) == 1: sigmas = [sigmas[0] for i in range(len(data.shape))] elif len(sigmas) != len(data.shape): printv( parser.error( 'ERROR: -smooth need the same number of inputs as the number of image dimension OR only one input' )) # adjust sigma based on voxel size sigmas = [sigmas[i] / dim[i + 4] for i in range(3)] # smooth data data_out = sct_math.smooth(data, sigmas) elif arguments.dilate is not None: if arguments.shape in ['disk', 'square'] and arguments.dim is None: printv( parser.error( 'ERROR: -dim is required for -dilate with 2D morphological kernel' )) data_out = sct_math.dilate(data, size=arguments.dilate, shape=arguments.shape, dim=arguments.dim) elif arguments.erode is not None: if arguments.shape in ['disk', 'square'] and arguments.dim is None: printv( parser.error( 'ERROR: -dim is required for -erode with 2D morphological kernel' )) data_out = sct_math.erode(data, size=arguments.erode, shape=arguments.shape, dim=arguments.dim) elif arguments.denoise is not None: # parse denoising arguments p, b = 1, 5 # default arguments list_denoise = (arguments.denoise).split(",") for i in list_denoise: if 'p' in i: p = int(i.split('=')[1]) if 'b' in i: b = int(i.split('=')[1]) data_out = sct_math.denoise_nlmeans(data, patch_radius=p, block_radius=b) elif arguments.symmetrize is not None: data_out = (data + data[list(range(data.shape[0] - 1, -1, -1)), :, :]) / float(2) elif arguments.mi is not None: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments.mi) compute_similarity(im, im_2, fname_out, metric='mi', metric_full='Mutual information', verbose=verbose) data_out = None elif arguments.minorm is not None: im_2 = Image(arguments.minorm) compute_similarity(im, im_2, fname_out, metric='minorm', metric_full='Normalized Mutual information', verbose=verbose) data_out = None elif arguments.corr is not None: # input 1 = from flag -i --> im # input 2 = from flag -mi im_2 = Image(arguments.corr) compute_similarity(im, im_2, fname_out, metric='corr', metric_full='Pearson correlation coefficient', verbose=verbose) data_out = None # if no flag is set else: data_out = None printv( parser.error( 'ERROR: you need to specify an operation to do on the input image' )) if data_out is not None: # Write output nii_out = Image(fname_in) # use header of input file nii_out.data = data_out nii_out.save(fname_out, dtype=output_type) # TODO: case of multiple outputs # assert len(data_out) == n_out # if n_in == n_out: # for im_in, d_out, fn_out in zip(nii, data_out, fname_out): # im_in.data = d_out # im_in.absolutepath = fn_out # if arguments.w is not None: # im_in.hdr.set_intent('vector', (), '') # im_in.save() # elif n_out == 1: # nii[0].data = data_out[0] # nii[0].absolutepath = fname_out[0] # if arguments.w is not None: # nii[0].hdr.set_intent('vector', (), '') # nii[0].save() # elif n_out > n_in: # for dat_out, name_out in zip(data_out, fname_out): # im_out = nii[0].copy() # im_out.data = dat_out # im_out.absolutepath = name_out # if arguments.w is not None: # im_out.hdr.set_intent('vector', (), '') # im_out.save() # else: # printv(parser.usage.generate(error='ERROR: not the correct numbers of inputs and outputs')) # display message if data_out is not None: display_viewer_syntax([fname_out], verbose=verbose) else: printv('\nDone! File created: ' + fname_out, verbose, 'info')
def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) 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', '0', ]) 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) sct_apply_transfo.main(['-i', 'segmentation.nii', '-d', 'data_straightr.nii', '-w', 'warp_curve2straight.nii.gz', '-o', 'segmentation_straight.nii', '-x', 'linear', '-v', '0']) # Threshold segmentation at 0.5 img = Image('segmentation_straight.nii') img.data = threshold(img.data, 0.5) img.save() # 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) sct_apply_transfo.main(['-i', file_labelz, '-d', 'data_straightr.nii', '-w', 'warp_curve2straight.nii.gz', '-o', 'labelz_straight.nii.gz', '-x', 'nn', '-v', '0']) # 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) img = Image("data_straightr.nii") # apply std dev to each axis of the image sigmas = [1 for i in range(len(img.data.shape))] # adjust sigma based on voxel size sigmas = [sigmas[i] / img.dim[i + 4] for i in range(3)] # smooth data img.data = laplacian(img.data, sigmas) img.save() # 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) sct_apply_transfo.main(['-i', 'segmentation_straight_labeled.nii', '-d', 'segmentation.nii', '-w', 'warp_straight2curve.nii.gz', '-o', 'segmentation_labeled.nii', '-x', 'nn', '-v', '0']) 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'])