def main(argv=None): parser = get_parser() arguments = parser.parse_args(argv) verbose = arguments.v set_global_loglevel(verbose=verbose) # Initialization param = Param() start_time = time.time() fname_anat = arguments.i fname_centerline = arguments.s param.algo_fitting = arguments.algo_fitting if arguments.smooth is not None: sigmas = arguments.smooth remove_temp_files = arguments.r if arguments.o is not None: fname_out = arguments.o else: fname_out = extract_fname(fname_anat)[1] + '_smooth.nii' # Display arguments printv('\nCheck input arguments...') printv(' Volume to smooth .................. ' + fname_anat) printv(' Centerline ........................ ' + fname_centerline) printv(' Sigma (mm) ........................ ' + str(sigmas)) printv(' Verbose ........................... ' + str(verbose)) # Check that input is 3D: nx, ny, nz, nt, px, py, pz, pt = Image(fname_anat).dim dim = 4 # by default, will be adjusted later if nt == 1: dim = 3 if nz == 1: dim = 2 if dim == 4: printv( 'WARNING: the input image is 4D, please split your image to 3D before smoothing spinalcord using :\n' 'sct_image -i ' + fname_anat + ' -split t -o ' + fname_anat, verbose, 'warning') printv('4D images not supported, aborting ...', verbose, 'error') # Extract path/file/extension path_anat, file_anat, ext_anat = extract_fname(fname_anat) path_centerline, file_centerline, ext_centerline = extract_fname( fname_centerline) path_tmp = tmp_create(basename="smooth_spinalcord") # Copying input data to tmp folder printv('\nCopying input data to tmp folder and convert to nii...', verbose) copy(fname_anat, os.path.join(path_tmp, "anat" + ext_anat)) copy(fname_centerline, os.path.join(path_tmp, "centerline" + ext_centerline)) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # convert to nii format im_anat = convert(Image('anat' + ext_anat)) im_anat.save('anat.nii', mutable=True, verbose=verbose) im_centerline = convert(Image('centerline' + ext_centerline)) im_centerline.save('centerline.nii', mutable=True, verbose=verbose) # Change orientation of the input image into RPI printv('\nOrient input volume to RPI orientation...') img_anat_rpi = Image("anat.nii").change_orientation("RPI") fname_anat_rpi = add_suffix(img_anat_rpi.absolutepath, "_rpi") img_anat_rpi.save(path=fname_anat_rpi, mutable=True) # Change orientation of the input image into RPI printv('\nOrient centerline to RPI orientation...') img_centerline_rpi = Image("centerline.nii").change_orientation("RPI") fname_centerline_rpi = add_suffix(img_centerline_rpi.absolutepath, "_rpi") img_centerline_rpi.save(path=fname_centerline_rpi, mutable=True) # Straighten the spinal cord # straighten segmentation printv('\nStraighten the spinal cord using centerline/segmentation...', verbose) cache_sig = cache_signature( input_files=[fname_anat_rpi, fname_centerline_rpi], input_params={"x": "spline"}) cachefile = os.path.join(curdir, "straightening.cache") if cache_valid(cachefile, cache_sig) and os.path.isfile( os.path.join( curdir, 'warp_curve2straight.nii.gz')) and os.path.isfile( os.path.join( curdir, 'warp_straight2curve.nii.gz')) and os.path.isfile( os.path.join(curdir, 'straight_ref.nii.gz')): # if they exist, copy them into current folder printv('Reusing existing warping field which seems to be valid', verbose, 'warning') copy(os.path.join(curdir, 'warp_curve2straight.nii.gz'), 'warp_curve2straight.nii.gz') copy(os.path.join(curdir, 'warp_straight2curve.nii.gz'), 'warp_straight2curve.nii.gz') copy(os.path.join(curdir, 'straight_ref.nii.gz'), 'straight_ref.nii.gz') # apply straightening run_proc([ 'sct_apply_transfo', '-i', fname_anat_rpi, '-w', 'warp_curve2straight.nii.gz', '-d', 'straight_ref.nii.gz', '-o', 'anat_rpi_straight.nii', '-x', 'spline' ], verbose) else: run_proc([ 'sct_straighten_spinalcord', '-i', fname_anat_rpi, '-o', 'anat_rpi_straight.nii', '-s', fname_centerline_rpi, '-x', 'spline', '-param', 'algo_fitting=' + param.algo_fitting ], verbose) cache_save(cachefile, cache_sig) # move warping fields locally (to use caching next time) copy('warp_curve2straight.nii.gz', os.path.join(curdir, 'warp_curve2straight.nii.gz')) copy('warp_straight2curve.nii.gz', os.path.join(curdir, 'warp_straight2curve.nii.gz')) # Smooth the straightened image along z printv('\nSmooth the straightened image...') img = Image("anat_rpi_straight.nii") out = img.copy() if len(sigmas) == 1: sigmas = [sigmas[0] for i in range(len(img.data.shape))] elif len(sigmas) != len(img.data.shape): raise ValueError( "-smooth need the same number of inputs as the number of image dimension OR only one input" ) sigmas = [sigmas[i] / img.dim[i + 4] for i in range(3)] out.data = smooth(out.data, sigmas) out.save(path="anat_rpi_straight_smooth.nii") # Apply the reversed warping field to get back the curved spinal cord printv( '\nApply the reversed warping field to get back the curved spinal cord...' ) run_proc([ 'sct_apply_transfo', '-i', 'anat_rpi_straight_smooth.nii', '-o', 'anat_rpi_straight_smooth_curved.nii', '-d', 'anat.nii', '-w', 'warp_straight2curve.nii.gz', '-x', 'spline' ], verbose) # replace zeroed voxels by original image (issue #937) printv('\nReplace zeroed voxels by original image...', verbose) nii_smooth = Image('anat_rpi_straight_smooth_curved.nii') data_smooth = nii_smooth.data data_input = Image('anat.nii').data indzero = np.where(data_smooth == 0) data_smooth[indzero] = data_input[indzero] nii_smooth.data = data_smooth nii_smooth.save('anat_rpi_straight_smooth_curved_nonzero.nii') # come back os.chdir(curdir) # Generate output file printv('\nGenerate output file...') generate_output_file( os.path.join(path_tmp, "anat_rpi_straight_smooth_curved_nonzero.nii"), fname_out) # Remove temporary files if remove_temp_files == 1: printv('\nRemove temporary files...') rmtree(path_tmp) # Display elapsed time elapsed_time = time.time() - start_time printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's\n') display_viewer_syntax([fname_anat, fname_out], 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) 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')