def test_function(script_name): if script_name == 'test_debug': return test_debug() # JULIEN else: # build script name fname_log = script_name + ".log" tmp_script_name = script_name result_folder = "results_"+script_name script_name = "test_"+script_name sct.create_folder(result_folder) os.chdir(result_folder) # display script name print_line('Checking '+script_name) # import function as a module script_tested = importlib.import_module(script_name) # test function status, output = script_tested.test(param.path_data) # write log file write_to_log_file(fname_log, output, 'w') # manage status if status == 0: print_ok() else: if status == 5: print_warning() else: print_fail() print output # go back to parent folder os.chdir('..') # return return status
def copy_mat_files(nt, list_file_mat, index, folder_out, param): """ Copy mat file from the grouped folder to the final folder (will be used by all individual ungrouped volumes) :param nt: int: Total number of volumes in native 4d data :param list_file_mat: list of list: File name of transformations :param index: list: Index to associate a given matrix file with a 3d volume (from the 4d native data) :param param: Param class :param folder_out: str: Output folder :return: None """ # create final mat folder sct.create_folder(folder_out) # Loop across registration matrices and copy to mat_final folder # First loop is accross z. If axial orientation, there is only one z (i.e., len(file_mat)=1) for iz in range(len(list_file_mat)): # Second loop is across ALL volumes of the input dmri dataset (corresponds to its 4th dimension: time) for it in range(nt): # Check if this index corresponds to a volume listed in the index list if it in index: file_mat = list_file_mat[iz][index.index(it)] fsrc = os.path.join(file_mat + param.suffix_mat) # Build final transfo file name file_mat_final = os.path.basename(file_mat)[:-9] + str(iz).zfill(4) + 'T' + str(it).zfill(4) fdest = os.path.join(folder_out, file_mat_final + param.suffix_mat) copyfile(fsrc, fdest)
def test_function(script_name): if script_name == 'test_debug': return test_debug() # JULIEN else: # build script name fname_log = script_name + ".log" tmp_script_name = script_name result_folder = "results_" + script_name script_name = "test_" + script_name sct.create_folder(result_folder) os.chdir(result_folder) # display script name print_line('Checking ' + script_name) # import function as a module script_tested = importlib.import_module(script_name) # test function status, output = script_tested.test(param.path_data) # write log file write_to_log_file(fname_log, output, 'w') # manage status if status == 0: print_ok() else: if status == 5: print_warning() else: print_fail() print output # go back to parent folder os.chdir('..') # return return status
def test_function(script_name): if script_name == 'test_debug': return test_debug() # JULIEN else: # Using the retest variable to recheck if we can perform tests after we downloaded the data retest = 1 # while condition values are arbitrary and are present to prevent infinite loop while 0 < retest < 3: # build script name fname_log = script_name + ".log" tmp_script_name = script_name result_folder = "results_"+script_name script_name = "test_"+script_name if retest == 1: # create folder and go in it sct.create_folder(result_folder) os.chdir(result_folder) # display script name print_line('Checking '+script_name) # import function as a module script_tested = importlib.import_module(script_name) # test function status, output = script_tested.test(param.path_data) # returning script_name to its original name script_name = tmp_script_name # manage status if status == 0: print_ok() retest = 0 else: print_fail() print output print "\nTest files missing, downloading them now \n" os.chdir('../..') downloaddata() param.path_data = sct.slash_at_the_end(os.path.abspath(param.path_data), 1) # check existence of testing data folder sct.check_folder_exist(param.path_data) os.chdir(param.path_tmp + result_folder) retest += 1 # log file write_to_log_file(fname_log, output, 'w') # go back to parent folder os.chdir('..') # end while loop # return return status
def checkFolderCreation(self, param): # check if the folder exist. If not, create it. sct.printv("Check folder existence...") result_creation = sct.create_folder(param) if result_creation == 2: sct.printv("ERROR: Permission denied for folder creation...", type="error") elif result_creation == 1: sct.printv("Folder "+param+" has been created.", 0, type='warning') return param
def checkFolderCreation(self, param): # check if the folder exist. If not, create it. if self.check_file_exist and self.parser.check_file_exist: result_creation = sct.create_folder(param) else: result_creation = 0 # no need for checking if result_creation == 2: raise OSError("Permission denied for folder creation {}".format(param)) elif result_creation == 1: sct.log.info("Folder " + param + " has been created.") return param
def checkFolderCreation(self, param): # check if the folder exist. If not, create it. if self.check_file_exist and self.parser.check_file_exist: result_creation = sct.create_folder(param) else: result_creation = 0 # no need for checking if result_creation == 2: raise OSError("Permission denied for folder creation {}".format(param)) elif result_creation == 1: logger.info("Folder " + param + " has been created.") return param
def test_function(script_name): # if script_name == 'test_debug': # return test_debug() # JULIEN # else: # build script name fname_log = '../' + script_name + ".log" tmp_script_name = script_name result_folder = "results_" + script_name script_name = "test_" + script_name sct.create_folder(result_folder) os.chdir(result_folder) # display script name print_line('Checking ' + script_name) # import function as a module script_tested = importlib.import_module(script_name) # test function result_test = script_tested.test(param.path_data) # test functions can return 2 or 3 variables, depending if there is results. # In this script, we look only at the first two variables. status, output = result_test[0], result_test[1] # write log file write_to_log_file(fname_log, output, 'w') # manage status if status == 0: print_ok() else: if status == 99: print_warning() else: print_fail() print output # go back to parent folder os.chdir('..') # return return status
def test_function(script_name): # if script_name == 'test_debug': # return test_debug() # JULIEN # else: # build script name fname_log = '../' + script_name + ".log" tmp_script_name = script_name result_folder = "results_"+script_name script_name = "test_"+script_name sct.create_folder(result_folder) os.chdir(result_folder) # display script name print_line('Checking '+script_name) # import function as a module script_tested = importlib.import_module(script_name) # test function result_test = script_tested.test(param.path_data) # test functions can return 2 or 3 variables, depending if there is results. # In this script, we look only at the first two variables. status, output = result_test[0], result_test[1] # write log file write_to_log_file(fname_log, output, 'w') # manage status if status == 0: print_ok() else: if status == 99: print_warning() else: print_fail() print output # go back to parent folder os.chdir('..') # return return status
def checkFolderCreation(self, param): # check if the folder exist. If not, create it. sct.printv("Check folder existence...") if self.parser.check_file_exist: result_creation = sct.create_folder(param) else: result_creation = 0 # no need for checking if result_creation == 2: sct.printv("ERROR: Permission denied for folder creation...", type="error") elif result_creation == 1: sct.printv("Folder "+param+" has been created.", 0, type='warning') # add slash at the end param = sct.slash_at_the_end(param, 1) return param
def checkFolderCreation(self, param): # check if the folder exist. If not, create it. if self.parser.check_file_exist: result_creation = sct.create_folder(param) else: result_creation = 0 # no need for checking if result_creation == 2: sct.printv("ERROR: Permission denied for folder creation...", type="error") elif result_creation == 1: sct.printv("Folder " + param + " has been created.", 0, type='warning') # add slash at the end param = sct.slash_at_the_end(param, 1) return param
def register_images(im_input, im_dest, mask='', paramreg=Paramreg(step='0', type='im', algo='Translation', metric='MI', iter='5', shrink='1', smooth='0', gradStep='0.5'), remove_tmp_folder=1): path_i, root_i, ext_i = sct.extract_fname(im_input) path_d, root_d, ext_d = sct.extract_fname(im_dest) path_m, root_m, ext_m = sct.extract_fname(mask) # set metricSize if paramreg.metric == 'MI': metricSize = '32' # corresponds to number of bins else: metricSize = '4' # corresponds to radius (for CC, MeanSquares...) # initiate default parameters of antsRegistration transformation ants_registration_params = { 'rigid': '', 'affine': '', 'compositeaffine': '', 'similarity': '', 'translation': '', 'bspline': ',10', 'gaussiandisplacementfield': ',3,0', 'bsplinedisplacementfield': ',5,10', 'syn': ',3,0', 'bsplinesyn': ',3,32' } # Get image dimensions and retrieve nz print '\nGet image dimensions of destination image...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(im_dest) print '.. matrix size: ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) print '.. voxel size: ' + str(px) + 'mm x ' + str(py) + 'mm x ' + str( pz) + 'mm' # Define x and y displacement as list x_displacement = [0 for i in range(nz)] y_displacement = [0 for i in range(nz)] theta_rotation = [0 for i in range(nz)] matrix_def = [0 for i in range(nz)] # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.' + time.strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print '\nCopy input data...' sct.run('cp ' + im_input + ' ' + path_tmp + '/' + root_i + ext_i) sct.run('cp ' + im_dest + ' ' + path_tmp + '/' + root_d + ext_d) if mask: sct.run('cp ' + mask + ' ' + path_tmp + '/mask.nii.gz') # go to temporary folder os.chdir(path_tmp) # Split input volume along z print '\nSplit input volume...' sct.run(sct.fsloutput + 'fslsplit ' + im_input + ' ' + root_i + '_z -z') #file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] # Split destination volume along z print '\nSplit destination volume...' sct.run(sct.fsloutput + 'fslsplit ' + im_dest + ' ' + root_d + '_z -z') #file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] # Split mask volume along z if mask: print '\nSplit mask volume...' sct.run(sct.fsloutput + 'fslsplit mask.nii.gz mask_z -z') #file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] im_dest_img = Image(im_dest) im_input_img = Image(im_input) coord_origin_dest = im_dest_img.transfo_pix2phys([[0, 0, 0]]) coord_origin_input = im_input_img.transfo_pix2phys([[0, 0, 0]]) coord_diff_origin_z = coord_origin_dest[0][2] - coord_origin_input[0][2] [[x_o, y_o, z_o]] = im_input_img.transfo_phys2pix([[0, 0, coord_diff_origin_z]]) # loop across slices for i in range(nz): # set masking num = numerotation(i) num_2 = numerotation(int(num) + z_o) if mask: masking = '-x mask_z' + num + '.nii' else: masking = '' cmd = ( 'isct_antsRegistration ' '--dimensionality 2 ' '--transform ' + paramreg.algo + '[' + paramreg.gradStep + ants_registration_params[paramreg.algo.lower()] + '] ' '--metric ' + paramreg.metric + '[' + root_d + '_z' + num + '.nii' + ',' + root_i + '_z' + num_2 + '.nii' + ',1,' + metricSize + '] ' #[fixedImage,movingImage,metricWeight +nb_of_bins (MI) or radius (other) '--convergence ' + paramreg.iter + ' ' '--shrink-factors ' + paramreg.shrink + ' ' '--smoothing-sigmas ' + paramreg.smooth + 'mm ' #'--restrict-deformation 1x1x0 ' # how to restrict? should not restrict here, if transform is precised...? '--output [transform_' + num + '] ' #--> file.txt (contains Tx,Ty) [outputTransformPrefix,<outputWarpedImage>,<outputInverseWarpedImage>] '--interpolation BSpline[3] ' + masking) try: sct.run(cmd) if paramreg.algo == 'Rigid' or paramreg.algo == 'Translation': f = 'transform_' + num + '0GenericAffine.mat' matfile = loadmat(f, struct_as_record=True) array_transfo = matfile['AffineTransform_double_2_2'] if i == 20 or i == 40: print i x_displacement[i] = -array_transfo[4][0] #is it? or is it y? y_displacement[i] = array_transfo[5][0] theta_rotation[i] = asin(array_transfo[2]) if paramreg.algo == 'Affine': f = 'transform_' + num + '0GenericAffine.mat' matfile = loadmat(f, struct_as_record=True) array_transfo = matfile['AffineTransform_double_2_2'] x_displacement[i] = -array_transfo[4][0] #is it? or is it y? y_displacement[i] = array_transfo[5][0] matrix_def[i] = [[array_transfo[0][0], array_transfo[1][0]], [array_transfo[2][0], array_transfo[3][0]] ] # comment savoir lequel est lequel? except: if paramreg.algo == 'Rigid' or paramreg.algo == 'Translation': x_displacement[i] = x_displacement[i - 1] #is it? or is it y? y_displacement[i] = y_displacement[i - 1] theta_rotation[i] = theta_rotation[i - 1] if paramreg.algo == 'Affine': x_displacement[i] = x_displacement[i - 1] y_displacement[i] = y_displacement[i - 1] matrix_def[i] = matrix_def[i - 1] # # get displacement form this slice and complete x and y displacement lists # with open('transform_'+num+'.csv') as f: # reader = csv.reader(f) # count = 0 # for line in reader: # count += 1 # if count == 2: # x_displacement[i] = line[0] # y_displacement[i] = line[1] # f.close() # # get matrix of transfo for a rigid transform (pb slicereg fait une rotation ie le deplacement n'est pas homogene par slice) # # recuperer le deplacement ne donnerait pas une liste mais un warping field: mieux vaut recup la matrice output # # pb du smoothing du deplacement par slice !! on peut smoother les param theta tx ty # if paramreg.algo == 'Rigid' or paramreg.algo == 'Translation': # f = 'transform_' +num+ '0GenericAffine.mat' # matfile = loadmat(f, struct_as_record=True) # array_transfo = matfile['AffineTransform_double_2_2'] # x_displacement[i] = -array_transfo[4][0] #is it? or is it y? # y_displacement[i] = array_transfo[5][0] # theta_rotation[i] = acos(array_transfo[0]) #TO DO: different treatment for other algo #Delete tmp folder os.chdir('../') if remove_tmp_folder: print('\nRemove temporary files...') sct.run('rm -rf ' + path_tmp) if paramreg.algo == 'Rigid': return x_displacement, y_displacement, theta_rotation # check if the displacement are not inverted (x_dis = -x_disp...) theta is in radian if paramreg.algo == 'Translation': return x_displacement, y_displacement if paramreg.algo == 'Affine': return x_displacement, y_displacement, matrix_def
def main(): # Initialization fname_anat = '' fname_point = '' slice_gap = param.gap remove_tmp_files = param.remove_tmp_files gaussian_kernel = param.gaussian_kernel start_time = time.time() # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') path_sct = sct.slash_at_the_end(path_sct, 1) # Parameters for debug mode if param.debug == 1: sct.printv('\n*** WARNING: DEBUG MODE ON ***\n\t\t\tCurrent working directory: '+os.getcwd(), 'warning') status, path_sct_testing_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') fname_anat = path_sct_testing_data+'/t2/t2.nii.gz' fname_point = path_sct_testing_data+'/t2/t2_centerline_init.nii.gz' slice_gap = 5 else: # Check input param try: opts, args = getopt.getopt(sys.argv[1:],'hi:p:g:r:k:') except getopt.GetoptError as err: print str(err) usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-i'): fname_anat = arg elif opt in ('-p'): fname_point = arg elif opt in ('-g'): slice_gap = int(arg) elif opt in ('-r'): remove_tmp_files = int(arg) elif opt in ('-k'): gaussian_kernel = int(arg) # display usage if a mandatory argument is not provided if fname_anat == '' or fname_point == '': usage() # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_point) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_point, file_point, ext_point = sct.extract_fname(fname_point) # extract path of schedule file # TODO: include schedule file in sct # TODO: check existence of schedule file file_schedule = path_sct + param.schedule_file # Get input image orientation input_image_orientation = get_orientation(fname_anat) # Display arguments print '\nCheck input arguments...' print ' Anatomical image: '+fname_anat print ' Orientation: '+input_image_orientation print ' Point in spinal cord: '+fname_point print ' Slice gap: '+str(slice_gap) print ' Gaussian kernel: '+str(gaussian_kernel) print ' Degree of polynomial: '+str(param.deg_poly) # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print '\nCopy input data...' sct.run('cp '+fname_anat+ ' '+path_tmp+'/tmp.anat'+ext_anat) sct.run('cp '+fname_point+ ' '+path_tmp+'/tmp.point'+ext_point) # go to temporary folder os.chdir(path_tmp) # convert to nii sct.run('fslchfiletype NIFTI tmp.anat') sct.run('fslchfiletype NIFTI tmp.point') # Reorient input anatomical volume into RL PA IS orientation print '\nReorient input volume to RL PA IS orientation...' #sct.run(sct.fsloutput + 'fslswapdim tmp.anat RL PA IS tmp.anat_orient') set_orientation('tmp.anat.nii', 'RPI', 'tmp.anat_orient.nii') # Reorient binary point into RL PA IS orientation print '\nReorient binary point into RL PA IS orientation...' sct.run(sct.fsloutput + 'fslswapdim tmp.point RL PA IS tmp.point_orient') set_orientation('tmp.point.nii', 'RPI', 'tmp.point_orient') # Get image dimensions print '\nGet image dimensions...' nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension('tmp.anat_orient') print '.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz) print '.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm' # Split input volume print '\nSplit input volume...' sct.run(sct.fsloutput + 'fslsplit tmp.anat_orient tmp.anat_orient_z -z') file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] # Get the coordinates of the input point print '\nGet the coordinates of the input point...' file = nibabel.load('tmp.point_orient.nii') data = file.get_data() x_init, y_init, z_init = (data > 0).nonzero() x_init = x_init[0] y_init = y_init[0] z_init = z_init[0] print '('+str(x_init)+', '+str(y_init)+', '+str(z_init)+')' # Extract the slice corresponding to z=z_init print '\nExtract the slice corresponding to z='+str(z_init)+'...' file_point_split = ['tmp.point_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] sct.run(sct.fsloutput+'fslroi tmp.point_orient '+file_point_split[z_init]+' 0 -1 0 -1 '+str(z_init)+' 1') # Create gaussian mask from point print '\nCreate gaussian mask from point...' file_mask_split = ['tmp.mask_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] sct.run(sct.fsloutput+'fslmaths '+file_point_split[z_init]+' -s '+str(gaussian_kernel)+' '+file_mask_split[z_init]) # Obtain max value from mask print '\nFind maximum value from mask...' file = nibabel.load(file_mask_split[z_init]+'.nii') data = file.get_data() max_value_mask = numpy.max(data) print '..'+str(max_value_mask) # Normalize mask beween 0 and 1 print '\nNormalize mask beween 0 and 1...' sct.run(sct.fsloutput+'fslmaths '+file_mask_split[z_init]+' -div '+str(max_value_mask)+' '+file_mask_split[z_init]) ## Take the square of the mask #print '\nCalculate the square of the mask...' #sct.run(sct.fsloutput+'fslmaths '+file_mask_split[z_init]+' -mul '+file_mask_split[z_init]+' '+file_mask_split[z_init]) # initialize variables file_mat = ['tmp.mat_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mat_inv = ['tmp.mat_inv_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mat_inv_cumul = ['tmp.mat_inv_cumul_z'+str(z).zfill(4) for z in range(0,nz,1)] # create identity matrix for initial transformation matrix fid = open(file_mat_inv_cumul[z_init], 'w') fid.write('%i %i %i %i\n' %(1, 0, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 1, 0, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # initialize centerline: give value corresponding to initial point x_centerline = [x_init] y_centerline = [y_init] z_centerline = [z_init] warning_count = 0 # go up (1), then down (2) in reference to the binary point for iUpDown in range(1, 3): if iUpDown == 1: # z increases slice_gap_signed = slice_gap elif iUpDown == 2: # z decreases slice_gap_signed = -slice_gap # reverse centerline (because values will be appended at the end) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # initialization before looping z_dest = z_init # point given by user z_src = z_dest + slice_gap_signed # continue looping if 0 < z < nz while 0 <= z_src and z_src <= nz-1: # print current z: print 'z='+str(z_src)+':' # estimate transformation sct.run(fsloutput+'flirt -in '+file_anat_split[z_src]+' -ref '+file_anat_split[z_dest]+' -schedule '+file_schedule+ ' -verbose 0 -omat '+file_mat[z_src]+' -cost normcorr -forcescaling -inweight '+file_mask_split[z_dest]+' -refweight '+file_mask_split[z_dest]) # display transfo status, output = sct.run('cat '+file_mat[z_src]) print output # check if transformation is bigger than 1.5x slice_gap tx = float(output.split()[3]) ty = float(output.split()[7]) norm_txy = numpy.linalg.norm([tx, ty],ord=2) if norm_txy > 1.5*slice_gap: print 'WARNING: Transformation is too large --> using previous one.' warning_count = warning_count + 1 # if previous transformation exists, replace current one with previous one if os.path.isfile(file_mat[z_dest]): sct.run('cp '+file_mat[z_dest]+' '+file_mat[z_src]) # estimate inverse transformation matrix sct.run('convert_xfm -omat '+file_mat_inv[z_src]+' -inverse '+file_mat[z_src]) # compute cumulative transformation sct.run('convert_xfm -omat '+file_mat_inv_cumul[z_src]+' -concat '+file_mat_inv[z_src]+' '+file_mat_inv_cumul[z_dest]) # apply inverse cumulative transformation to initial gaussian mask (to put it in src space) sct.run(fsloutput+'flirt -in '+file_mask_split[z_init]+' -ref '+file_mask_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul[z_src]+' -out '+file_mask_split[z_src]) # open inverse cumulative transformation file and generate centerline fid = open(file_mat_inv_cumul[z_src]) mat = fid.read().split() x_centerline.append(x_init + float(mat[3])) y_centerline.append(y_init + float(mat[7])) z_centerline.append(z_src) #z_index = z_index+1 # define new z_dest (target slice) and new z_src (moving slice) z_dest = z_dest + slice_gap_signed z_src = z_src + slice_gap_signed # Reconstruct centerline # ==================================================================================================== # reverse back centerline (because it's been reversed once, so now all values are in the right order) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # fit centerline in the Z-X plane using polynomial function print '\nFit centerline in the Z-X plane using polynomial function...' coeffsx = numpy.polyfit(z_centerline, x_centerline, deg=param.deg_poly) polyx = numpy.poly1d(coeffsx) x_centerline_fit = numpy.polyval(polyx, z_centerline) # calculate RMSE rmse = numpy.linalg.norm(x_centerline_fit-x_centerline)/numpy.sqrt( len(x_centerline) ) # calculate max absolute error max_abs = numpy.max( numpy.abs(x_centerline_fit-x_centerline) ) print '.. RMSE (in mm): '+str(rmse*px) print '.. Maximum absolute error (in mm): '+str(max_abs*px) # fit centerline in the Z-Y plane using polynomial function print '\nFit centerline in the Z-Y plane using polynomial function...' coeffsy = numpy.polyfit(z_centerline, y_centerline, deg=param.deg_poly) polyy = numpy.poly1d(coeffsy) y_centerline_fit = numpy.polyval(polyy, z_centerline) # calculate RMSE rmse = numpy.linalg.norm(y_centerline_fit-y_centerline)/numpy.sqrt( len(y_centerline) ) # calculate max absolute error max_abs = numpy.max( numpy.abs(y_centerline_fit-y_centerline) ) print '.. RMSE (in mm): '+str(rmse*py) print '.. Maximum absolute error (in mm): '+str(max_abs*py) # display if param.debug == 1: import matplotlib.pyplot as plt plt.figure() plt.plot(z_centerline,x_centerline,'.',z_centerline,x_centerline_fit,'r') plt.legend(['Data','Polynomial Fit']) plt.title('Z-X plane polynomial interpolation') plt.show() plt.figure() plt.plot(z_centerline,y_centerline,'.',z_centerline,y_centerline_fit,'r') plt.legend(['Data','Polynomial Fit']) plt.title('Z-Y plane polynomial interpolation') plt.show() # generate full range z-values for centerline z_centerline_full = [iz for iz in range(0, nz, 1)] # calculate X and Y values for the full centerline x_centerline_fit_full = numpy.polyval(polyx, z_centerline_full) y_centerline_fit_full = numpy.polyval(polyy, z_centerline_full) # Generate fitted transformation matrices and write centerline coordinates in text file print '\nGenerate fitted transformation matrices and write centerline coordinates in text file...' file_mat_inv_cumul_fit = ['tmp.mat_inv_cumul_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mat_cumul_fit = ['tmp.mat_cumul_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] fid_centerline = open('tmp.centerline_coordinates.txt', 'w') for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, x_centerline_fit_full[iz]-x_init) ) fid.write('%i %i %i %f\n' %(0, 1, 0, y_centerline_fit_full[iz]-y_init) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # compute forward cumulative fitted transformation matrix sct.run('convert_xfm -omat '+file_mat_cumul_fit[iz]+' -inverse '+file_mat_inv_cumul_fit[iz]) # write centerline coordinates in x, y, z format fid_centerline.write('%f %f %f\n' %(x_centerline_fit_full[iz], y_centerline_fit_full[iz], z_centerline_full[iz]) ) fid_centerline.close() # Prepare output data # ==================================================================================================== # write centerline as text file for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], 'w') fid.write('%i %i %i %f\n' %(1, 0, 0, x_centerline_fit_full[iz]-x_init) ) fid.write('%i %i %i %f\n' %(0, 1, 0, y_centerline_fit_full[iz]-y_init) ) fid.write('%i %i %i %i\n' %(0, 0, 1, 0) ) fid.write('%i %i %i %i\n' %(0, 0, 0, 1) ) fid.close() # write polynomial coefficients numpy.savetxt('tmp.centerline_polycoeffs_x.txt',coeffsx) numpy.savetxt('tmp.centerline_polycoeffs_y.txt',coeffsy) # apply transformations to data print '\nApply fitted transformation matrices...' file_anat_split_fit = ['tmp.anat_orient_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] file_mask_split_fit = ['tmp.mask_orient_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] file_point_split_fit = ['tmp.point_orient_fit_z'+str(z).zfill(4) for z in range(0,nz,1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run(fsloutput+'flirt -in '+file_anat_split[iz]+' -ref '+file_anat_split[iz]+' -applyxfm -init '+file_mat_cumul_fit[iz]+' -out '+file_anat_split_fit[iz]) # inverse cumulative transformation to mask sct.run(fsloutput+'flirt -in '+file_mask_split[z_init]+' -ref '+file_mask_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_mask_split_fit[iz]) # inverse cumulative transformation to point sct.run(fsloutput+'flirt -in '+file_point_split[z_init]+' -ref '+file_point_split[z_init]+' -applyxfm -init '+file_mat_inv_cumul_fit[iz]+' -out '+file_point_split_fit[iz]+' -interp nearestneighbour') # Merge into 4D volume print '\nMerge into 4D volume...' sct.run(fsloutput+'fslmerge -z tmp.anat_orient_fit tmp.anat_orient_fit_z*') sct.run(fsloutput+'fslmerge -z tmp.mask_orient_fit tmp.mask_orient_fit_z*') sct.run(fsloutput+'fslmerge -z tmp.point_orient_fit tmp.point_orient_fit_z*') # Copy header geometry from input data print '\nCopy header geometry from input data...' sct.run(fsloutput+'fslcpgeom tmp.anat_orient.nii tmp.anat_orient_fit.nii ') sct.run(fsloutput+'fslcpgeom tmp.anat_orient.nii tmp.mask_orient_fit.nii ') sct.run(fsloutput+'fslcpgeom tmp.anat_orient.nii tmp.point_orient_fit.nii ') # Reorient outputs into the initial orientation of the input image print '\nReorient the centerline into the initial orientation of the input image...' set_orientation('tmp.point_orient_fit.nii', input_image_orientation, 'tmp.point_orient_fit.nii') set_orientation('tmp.mask_orient_fit.nii', input_image_orientation, 'tmp.mask_orient_fit.nii') # Generate output file (in current folder) print '\nGenerate output file (in current folder)...' os.chdir('..') # come back to parent folder #sct.generate_output_file('tmp.centerline_polycoeffs_x.txt','./','centerline_polycoeffs_x','.txt') #sct.generate_output_file('tmp.centerline_polycoeffs_y.txt','./','centerline_polycoeffs_y','.txt') #sct.generate_output_file('tmp.centerline_coordinates.txt','./','centerline_coordinates','.txt') #sct.generate_output_file('tmp.anat_orient.nii','./',file_anat+'_rpi',ext_anat) #sct.generate_output_file('tmp.anat_orient_fit.nii', file_anat+'_rpi_align'+ext_anat) #sct.generate_output_file('tmp.mask_orient_fit.nii', file_anat+'_mask'+ext_anat) fname_output_centerline = sct.generate_output_file(path_tmp+'/tmp.point_orient_fit.nii', file_anat+'_centerline'+ext_anat) # Delete temporary files if remove_tmp_files == 1: print '\nRemove temporary files...' sct.run('rm -rf '+path_tmp) # print number of warnings print '\nNumber of warnings: '+str(warning_count)+' (if >10, you should probably reduce the gap and/or increase the kernel size' # display elapsed time elapsed_time = time.time() - start_time print '\nFinished! \n\tGenerated file: '+fname_output_centerline+'\n\tElapsed time: '+str(int(round(elapsed_time)))+'s\n'
def fmri_moco(param): file_data = 'fmri' ext_data = '.nii' mat_final = 'mat_final/' fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image(file_data + '.nii').dim sct.printv( ' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), param.verbose) # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) im_data = Image(file_data + ext_data) im_data_split_list = split_data(im_data, 3) for im in im_data_split_list: im.save() # assign an index to each volume index_fmri = range(0, nt) # Number of groups nb_groups = int(math.floor(nt / param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_fmri[(iGroup * param.group_size):((iGroup + 1) * param.group_size)]) # add the remaining images to the last DWI group nb_remaining = nt % param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_fmri[len(index_fmri) - nb_remaining:len(index_fmri)]) # groups for iGroup in range(nb_groups): sct.printv('\nGroup: ' + str((iGroup + 1)) + '/' + str(nb_groups), param.verbose) # get index index_fmri_i = group_indexes[iGroup] nt_i = len(index_fmri_i) # Merge Images sct.printv('Merge consecutive volumes...', param.verbose) file_data_merge_i = file_data + '_' + str(iGroup) # cmd = fsloutput + 'fslmerge -t ' + file_data_merge_i # for it in range(nt_i): # cmd = cmd + ' ' + file_data + '_T' + str(index_fmri_i[it]).zfill(4) im_fmri_list = [] for it in range(nt_i): im_fmri_list.append(im_data_split_list[index_fmri_i[it]]) im_fmri_concat = concat_data(im_fmri_list, 3) im_fmri_concat.setFileName(file_data_merge_i + ext_data) im_fmri_concat.save() # Average Images sct.printv('Average volumes...', param.verbose) file_data_mean = file_data + '_mean_' + str(iGroup) sct.run('sct_maths -i ' + file_data_merge_i + '.nii -o ' + file_data_mean + '.nii -mean t') # if not average_data_across_dimension(file_data_merge_i+'.nii', file_data_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_data_merge_i + ' -Tmean ' + file_data_mean # sct.run(cmd, param.verbose) # Merge groups means sct.printv('\nMerging volumes...', param.verbose) file_data_groups_means_merge = 'fmri_averaged_groups' im_mean_list = [] for iGroup in range(nb_groups): im_mean_list.append( Image(file_data + '_mean_' + str(iGroup) + ext_data)) im_mean_concat = concat_data(im_mean_list, 3) im_mean_concat.setFileName(file_data_groups_means_merge + ext_data) im_mean_concat.save() # Estimate moco sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion...', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'fmri_averaged_groups' param_moco.file_target = file_data + '_mean_' + param.num_target param_moco.path_out = '' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_groups' moco.moco(param_moco) # create final mat folder sct.create_folder(mat_final) # Copy registration matrices sct.printv('\nCopy transformations...', param.verbose) for iGroup in range(nb_groups): for data in range(len(group_indexes[iGroup])): sct.run( 'cp ' + 'mat_groups/' + 'mat.T' + str(iGroup) + ext_mat + ' ' + mat_final + 'mat.T' + str(group_indexes[iGroup][data]) + ext_mat, param.verbose) # Apply moco on all fmri data sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'fmri' param_moco.file_target = file_data + '_mean_' + str(0) param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". im_fmri = Image('fmri.nii') im_fmri_moco = Image('fmri_moco.nii') im_fmri_moco = copy_header(im_fmri, im_fmri_moco) im_fmri_moco.save() # Average volumes sct.printv('\nAveraging data...', param.verbose) sct.run('sct_maths -i fmri_moco.nii -o fmri_moco_mean.nii -mean t')
def moco(param): # retrieve parameters fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI file_data = param.file_data file_target = param.file_target folder_mat = sct.slash_at_the_end(param.mat_moco, 1) # output folder of mat file todo = param.todo suffix = param.suffix #file_schedule = param.file_schedule verbose = param.verbose ext = '.nii' # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # print arguments sct.printv('\nInput parameters:', param.verbose) sct.printv(' Input file ............' + file_data, param.verbose) sct.printv(' Reference file ........' + file_target, param.verbose) sct.printv(' Polynomial degree .....' + param.param[0], param.verbose) sct.printv(' Smoothing kernel ......' + param.param[1], param.verbose) sct.printv(' Gradient step .........' + param.param[2], param.verbose) sct.printv(' Metric ................' + param.param[3], param.verbose) sct.printv(' Todo ..................' + todo, param.verbose) sct.printv(' Mask .................' + param.fname_mask, param.verbose) sct.printv(' Output mat folder .....' + folder_mat, param.verbose) # create folder for mat files sct.create_folder(folder_mat) # Get size of data sct.printv('\nGet dimensions data...', verbose) data_im = Image(file_data + ext) nx, ny, nz, nt, px, py, pz, pt = data_im.dim sct.printv(('.. ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt)), verbose) # copy file_target to a temporary file sct.printv('\nCopy file_target to a temporary file...', verbose) sct.run('cp ' + file_target + ext + ' target.nii') file_target = 'target' # Split data along T dimension sct.printv('\nSplit data along T dimension...', verbose) data_split_list = split_data(data_im, dim=3) for im in data_split_list: im.save() file_data_splitT = file_data + '_T' # Motion correction: initialization index = np.arange(nt) file_data_splitT_num = [] file_data_splitT_moco_num = [] failed_transfo = [0 for i in range(nt)] file_mat = [[] for i in range(nt)] # Motion correction: Loop across T for indice_index in range(nt): # create indices and display stuff it = index[indice_index] file_data_splitT_num.append(file_data_splitT + str(it).zfill(4)) file_data_splitT_moco_num.append(file_data + suffix + '_T' + str(it).zfill(4)) sct.printv(('\nVolume ' + str((it)) + '/' + str(nt - 1) + ':'), verbose) file_mat[it] = folder_mat + 'mat.T' + str(it) # run 3D registration failed_transfo[it] = register(param, file_data_splitT_num[it], file_target, file_mat[it], file_data_splitT_moco_num[it]) # average registered volume with target image # N.B. use weighted averaging: (target * nb_it + moco) / (nb_it + 1) if param.iterative_averaging and indice_index < 10 and failed_transfo[it] == 0: sct.run('sct_maths -i ' + file_target + ext + ' -mul ' + str(indice_index + 1) + ' -o ' + file_target + ext) sct.run('sct_maths -i ' + file_target + ext + ' -add ' + file_data_splitT_moco_num[it] + ext + ' -o ' + file_target + ext) sct.run('sct_maths -i ' + file_target + ext + ' -div ' + str(indice_index + 2) + ' -o ' + file_target + ext) # Replace failed transformation with the closest good one sct.printv(('\nReplace failed transformations...'), verbose) fT = [i for i, j in enumerate(failed_transfo) if j == 1] gT = [i for i, j in enumerate(failed_transfo) if j == 0] for it in range(len(fT)): abs_dist = [abs(gT[i] - fT[it]) for i in range(len(gT))] if not abs_dist == []: index_good = abs_dist.index(min(abs_dist)) sct.printv(' transfo #' + str(fT[it]) + ' --> use transfo #' + str(gT[index_good]), verbose) # copy transformation sct.run('cp ' + file_mat[gT[index_good]] + 'Warp.nii.gz' + ' ' + file_mat[fT[it]] + 'Warp.nii.gz') # apply transformation sct.run('sct_apply_transfo -i ' + file_data_splitT_num[fT[it]] + '.nii -d ' + file_target + '.nii -w ' + file_mat[fT[it]] + 'Warp.nii.gz' + ' -o ' + file_data_splitT_moco_num[fT[it]] + '.nii' + ' -x ' + param.interp, verbose) else: # exit program if no transformation exists. sct.printv('\nERROR in ' + os.path.basename(__file__) + ': No good transformation exist. Exit program.\n', verbose, 'error') sys.exit(2) # Merge data along T file_data_moco = file_data + suffix if todo != 'estimate': sct.printv('\nMerge data back along T...', verbose) from sct_image import concat_data # im_list = [] fname_list = [] for indice_index in range(len(index)): # im_list.append(Image(file_data_splitT_moco_num[indice_index] + ext)) fname_list.append(file_data_splitT_moco_num[indice_index] + ext) im_out = concat_data(fname_list, 3) im_out.setFileName(file_data_moco + ext) im_out.save() # delete file target.nii (to avoid conflict if this function is run another time) sct.printv('\nRemove temporary file...', verbose) # os.remove('target.nii') sct.run('rm target.nii')
def get_centerline_from_point(input_image, point_file, gap=4, gaussian_kernel=4, remove_tmp_files=1): # Initialization fname_anat = input_image fname_point = point_file slice_gap = gap remove_tmp_files = remove_tmp_files gaussian_kernel = gaussian_kernel start_time = time() verbose = 1 # get path of the toolbox status, path_sct = commands.getstatusoutput("echo $SCT_DIR") path_sct = sct.slash_at_the_end(path_sct, 1) # Parameters for debug mode if param.debug == 1: sct.printv("\n*** WARNING: DEBUG MODE ON ***\n\t\t\tCurrent working directory: " + os.getcwd(), "warning") status, path_sct_testing_data = commands.getstatusoutput("echo $SCT_TESTING_DATA_DIR") fname_anat = path_sct_testing_data + "/t2/t2.nii.gz" fname_point = path_sct_testing_data + "/t2/t2_centerline_init.nii.gz" slice_gap = 5 # check existence of input files sct.check_file_exist(fname_anat) sct.check_file_exist(fname_point) # extract path/file/extension path_anat, file_anat, ext_anat = sct.extract_fname(fname_anat) path_point, file_point, ext_point = sct.extract_fname(fname_point) # extract path of schedule file # TODO: include schedule file in sct # TODO: check existence of schedule file file_schedule = path_sct + param.schedule_file # Get input image orientation input_image_orientation = get_orientation_3d(fname_anat, filename=True) # Display arguments print "\nCheck input arguments..." print " Anatomical image: " + fname_anat print " Orientation: " + input_image_orientation print " Point in spinal cord: " + fname_point print " Slice gap: " + str(slice_gap) print " Gaussian kernel: " + str(gaussian_kernel) print " Degree of polynomial: " + str(param.deg_poly) # create temporary folder print ("\nCreate temporary folder...") path_tmp = "tmp." + strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print "\nCopy input data..." sct.run("cp " + fname_anat + " " + path_tmp + "/tmp.anat" + ext_anat) sct.run("cp " + fname_point + " " + path_tmp + "/tmp.point" + ext_point) # go to temporary folder os.chdir(path_tmp) # convert to nii im_anat = convert("tmp.anat" + ext_anat, "tmp.anat.nii") im_point = convert("tmp.point" + ext_point, "tmp.point.nii") # Reorient input anatomical volume into RL PA IS orientation print "\nReorient input volume to RL PA IS orientation..." set_orientation(im_anat, "RPI") im_anat.setFileName("tmp.anat_orient.nii") # Reorient binary point into RL PA IS orientation print "\nReorient binary point into RL PA IS orientation..." # sct.run(sct.fsloutput + 'fslswapdim tmp.point RL PA IS tmp.point_orient') set_orientation(im_point, "RPI") im_point.setFileName("tmp.point_orient.nii") # Get image dimensions print "\nGet image dimensions..." nx, ny, nz, nt, px, py, pz, pt = Image("tmp.anat_orient.nii").dim print ".. matrix size: " + str(nx) + " x " + str(ny) + " x " + str(nz) print ".. voxel size: " + str(px) + "mm x " + str(py) + "mm x " + str(pz) + "mm" # Split input volume print "\nSplit input volume..." im_anat_split_list = split_data(im_anat, 2) file_anat_split = [] for im in im_anat_split_list: file_anat_split.append(im.absolutepath) im.save() im_point_split_list = split_data(im_point, 2) file_point_split = [] for im in im_point_split_list: file_point_split.append(im.absolutepath) im.save() # Extract coordinates of input point data_point = Image("tmp.point_orient.nii").data x_init, y_init, z_init = unravel_index(data_point.argmax(), data_point.shape) sct.printv("Coordinates of input point: (" + str(x_init) + ", " + str(y_init) + ", " + str(z_init) + ")", verbose) # Create 2D gaussian mask sct.printv("\nCreate gaussian mask from point...", verbose) xx, yy = mgrid[:nx, :ny] mask2d = zeros((nx, ny)) radius = round(float(gaussian_kernel + 1) / 2) # add 1 because the radius includes the center. sigma = float(radius) mask2d = exp(-(((xx - x_init) ** 2) / (2 * (sigma ** 2)) + ((yy - y_init) ** 2) / (2 * (sigma ** 2)))) # Save mask to 2d file file_mask_split = ["tmp.mask_orient_Z" + str(z).zfill(4) for z in range(0, nz, 1)] nii_mask2d = Image("tmp.anat_orient_Z0000.nii") nii_mask2d.data = mask2d nii_mask2d.setFileName(file_mask_split[z_init] + ".nii") nii_mask2d.save() # initialize variables file_mat = ["tmp.mat_Z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mat_inv = ["tmp.mat_inv_Z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mat_inv_cumul = ["tmp.mat_inv_cumul_Z" + str(z).zfill(4) for z in range(0, nz, 1)] # create identity matrix for initial transformation matrix fid = open(file_mat_inv_cumul[z_init], "w") fid.write("%i %i %i %i\n" % (1, 0, 0, 0)) fid.write("%i %i %i %i\n" % (0, 1, 0, 0)) fid.write("%i %i %i %i\n" % (0, 0, 1, 0)) fid.write("%i %i %i %i\n" % (0, 0, 0, 1)) fid.close() # initialize centerline: give value corresponding to initial point x_centerline = [x_init] y_centerline = [y_init] z_centerline = [z_init] warning_count = 0 # go up (1), then down (2) in reference to the binary point for iUpDown in range(1, 3): if iUpDown == 1: # z increases slice_gap_signed = slice_gap elif iUpDown == 2: # z decreases slice_gap_signed = -slice_gap # reverse centerline (because values will be appended at the end) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # initialization before looping z_dest = z_init # point given by user z_src = z_dest + slice_gap_signed # continue looping if 0 <= z < nz while 0 <= z_src < nz: # print current z: print "z=" + str(z_src) + ":" # estimate transformation sct.run( fsloutput + "flirt -in " + file_anat_split[z_src] + " -ref " + file_anat_split[z_dest] + " -schedule " + file_schedule + " -verbose 0 -omat " + file_mat[z_src] + " -cost normcorr -forcescaling -inweight " + file_mask_split[z_dest] + " -refweight " + file_mask_split[z_dest] ) # display transfo status, output = sct.run("cat " + file_mat[z_src]) print output # check if transformation is bigger than 1.5x slice_gap tx = float(output.split()[3]) ty = float(output.split()[7]) norm_txy = linalg.norm([tx, ty], ord=2) if norm_txy > 1.5 * slice_gap: print "WARNING: Transformation is too large --> using previous one." warning_count = warning_count + 1 # if previous transformation exists, replace current one with previous one if os.path.isfile(file_mat[z_dest]): sct.run("cp " + file_mat[z_dest] + " " + file_mat[z_src]) # estimate inverse transformation matrix sct.run("convert_xfm -omat " + file_mat_inv[z_src] + " -inverse " + file_mat[z_src]) # compute cumulative transformation sct.run( "convert_xfm -omat " + file_mat_inv_cumul[z_src] + " -concat " + file_mat_inv[z_src] + " " + file_mat_inv_cumul[z_dest] ) # apply inverse cumulative transformation to initial gaussian mask (to put it in src space) sct.run( fsloutput + "flirt -in " + file_mask_split[z_init] + " -ref " + file_mask_split[z_init] + " -applyxfm -init " + file_mat_inv_cumul[z_src] + " -out " + file_mask_split[z_src] ) # open inverse cumulative transformation file and generate centerline fid = open(file_mat_inv_cumul[z_src]) mat = fid.read().split() x_centerline.append(x_init + float(mat[3])) y_centerline.append(y_init + float(mat[7])) z_centerline.append(z_src) # z_index = z_index+1 # define new z_dest (target slice) and new z_src (moving slice) z_dest = z_dest + slice_gap_signed z_src = z_src + slice_gap_signed # Reconstruct centerline # ==================================================================================================== # reverse back centerline (because it's been reversed once, so now all values are in the right order) x_centerline.reverse() y_centerline.reverse() z_centerline.reverse() # fit centerline in the Z-X plane using polynomial function print "\nFit centerline in the Z-X plane using polynomial function..." coeffsx = polyfit(z_centerline, x_centerline, deg=param.deg_poly) polyx = poly1d(coeffsx) x_centerline_fit = polyval(polyx, z_centerline) # calculate RMSE rmse = linalg.norm(x_centerline_fit - x_centerline) / sqrt(len(x_centerline)) # calculate max absolute error max_abs = max(abs(x_centerline_fit - x_centerline)) print ".. RMSE (in mm): " + str(rmse * px) print ".. Maximum absolute error (in mm): " + str(max_abs * px) # fit centerline in the Z-Y plane using polynomial function print "\nFit centerline in the Z-Y plane using polynomial function..." coeffsy = polyfit(z_centerline, y_centerline, deg=param.deg_poly) polyy = poly1d(coeffsy) y_centerline_fit = polyval(polyy, z_centerline) # calculate RMSE rmse = linalg.norm(y_centerline_fit - y_centerline) / sqrt(len(y_centerline)) # calculate max absolute error max_abs = max(abs(y_centerline_fit - y_centerline)) print ".. RMSE (in mm): " + str(rmse * py) print ".. Maximum absolute error (in mm): " + str(max_abs * py) # display if param.debug == 1: import matplotlib.pyplot as plt plt.figure() plt.plot(z_centerline, x_centerline, ".", z_centerline, x_centerline_fit, "r") plt.legend(["Data", "Polynomial Fit"]) plt.title("Z-X plane polynomial interpolation") plt.show() plt.figure() plt.plot(z_centerline, y_centerline, ".", z_centerline, y_centerline_fit, "r") plt.legend(["Data", "Polynomial Fit"]) plt.title("Z-Y plane polynomial interpolation") plt.show() # generate full range z-values for centerline z_centerline_full = [iz for iz in range(0, nz, 1)] # calculate X and Y values for the full centerline x_centerline_fit_full = polyval(polyx, z_centerline_full) y_centerline_fit_full = polyval(polyy, z_centerline_full) # Generate fitted transformation matrices and write centerline coordinates in text file print "\nGenerate fitted transformation matrices and write centerline coordinates in text file..." file_mat_inv_cumul_fit = ["tmp.mat_inv_cumul_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mat_cumul_fit = ["tmp.mat_cumul_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] fid_centerline = open("tmp.centerline_coordinates.txt", "w") for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], "w") fid.write("%i %i %i %f\n" % (1, 0, 0, x_centerline_fit_full[iz] - x_init)) fid.write("%i %i %i %f\n" % (0, 1, 0, y_centerline_fit_full[iz] - y_init)) fid.write("%i %i %i %i\n" % (0, 0, 1, 0)) fid.write("%i %i %i %i\n" % (0, 0, 0, 1)) fid.close() # compute forward cumulative fitted transformation matrix sct.run("convert_xfm -omat " + file_mat_cumul_fit[iz] + " -inverse " + file_mat_inv_cumul_fit[iz]) # write centerline coordinates in x, y, z format fid_centerline.write( "%f %f %f\n" % (x_centerline_fit_full[iz], y_centerline_fit_full[iz], z_centerline_full[iz]) ) fid_centerline.close() # Prepare output data # ==================================================================================================== # write centerline as text file for iz in range(0, nz, 1): # compute inverse cumulative fitted transformation matrix fid = open(file_mat_inv_cumul_fit[iz], "w") fid.write("%i %i %i %f\n" % (1, 0, 0, x_centerline_fit_full[iz] - x_init)) fid.write("%i %i %i %f\n" % (0, 1, 0, y_centerline_fit_full[iz] - y_init)) fid.write("%i %i %i %i\n" % (0, 0, 1, 0)) fid.write("%i %i %i %i\n" % (0, 0, 0, 1)) fid.close() # write polynomial coefficients savetxt("tmp.centerline_polycoeffs_x.txt", coeffsx) savetxt("tmp.centerline_polycoeffs_y.txt", coeffsy) # apply transformations to data print "\nApply fitted transformation matrices..." file_anat_split_fit = ["tmp.anat_orient_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] file_mask_split_fit = ["tmp.mask_orient_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] file_point_split_fit = ["tmp.point_orient_fit_z" + str(z).zfill(4) for z in range(0, nz, 1)] for iz in range(0, nz, 1): # forward cumulative transformation to data sct.run( fsloutput + "flirt -in " + file_anat_split[iz] + " -ref " + file_anat_split[iz] + " -applyxfm -init " + file_mat_cumul_fit[iz] + " -out " + file_anat_split_fit[iz] ) # inverse cumulative transformation to mask sct.run( fsloutput + "flirt -in " + file_mask_split[z_init] + " -ref " + file_mask_split[z_init] + " -applyxfm -init " + file_mat_inv_cumul_fit[iz] + " -out " + file_mask_split_fit[iz] ) # inverse cumulative transformation to point sct.run( fsloutput + "flirt -in " + file_point_split[z_init] + " -ref " + file_point_split[z_init] + " -applyxfm -init " + file_mat_inv_cumul_fit[iz] + " -out " + file_point_split_fit[iz] + " -interp nearestneighbour" ) # Merge into 4D volume print "\nMerge into 4D volume..." # im_anat_list = [Image(fname) for fname in glob.glob('tmp.anat_orient_fit_z*.nii')] fname_anat_list = glob.glob("tmp.anat_orient_fit_z*.nii") im_anat_concat = concat_data(fname_anat_list, 2) im_anat_concat.setFileName("tmp.anat_orient_fit.nii") im_anat_concat.save() # im_mask_list = [Image(fname) for fname in glob.glob('tmp.mask_orient_fit_z*.nii')] fname_mask_list = glob.glob("tmp.mask_orient_fit_z*.nii") im_mask_concat = concat_data(fname_mask_list, 2) im_mask_concat.setFileName("tmp.mask_orient_fit.nii") im_mask_concat.save() # im_point_list = [Image(fname) for fname in glob.glob('tmp.point_orient_fit_z*.nii')] fname_point_list = glob.glob("tmp.point_orient_fit_z*.nii") im_point_concat = concat_data(fname_point_list, 2) im_point_concat.setFileName("tmp.point_orient_fit.nii") im_point_concat.save() # Copy header geometry from input data print "\nCopy header geometry from input data..." im_anat = Image("tmp.anat_orient.nii") im_anat_orient_fit = Image("tmp.anat_orient_fit.nii") im_mask_orient_fit = Image("tmp.mask_orient_fit.nii") im_point_orient_fit = Image("tmp.point_orient_fit.nii") im_anat_orient_fit = copy_header(im_anat, im_anat_orient_fit) im_mask_orient_fit = copy_header(im_anat, im_mask_orient_fit) im_point_orient_fit = copy_header(im_anat, im_point_orient_fit) for im in [im_anat_orient_fit, im_mask_orient_fit, im_point_orient_fit]: im.save() # Reorient outputs into the initial orientation of the input image print "\nReorient the centerline into the initial orientation of the input image..." set_orientation("tmp.point_orient_fit.nii", input_image_orientation, "tmp.point_orient_fit.nii") set_orientation("tmp.mask_orient_fit.nii", input_image_orientation, "tmp.mask_orient_fit.nii") # Generate output file (in current folder) print "\nGenerate output file (in current folder)..." os.chdir("..") # come back to parent folder fname_output_centerline = sct.generate_output_file( path_tmp + "/tmp.point_orient_fit.nii", file_anat + "_centerline" + ext_anat ) # Delete temporary files if remove_tmp_files == 1: print "\nRemove temporary files..." sct.run("rm -rf " + path_tmp, error_exit="warning") # print number of warnings print "\nNumber of warnings: " + str( warning_count ) + " (if >10, you should probably reduce the gap and/or increase the kernel size" # display elapsed time elapsed_time = time() - start_time print "\nFinished! \n\tGenerated file: " + fname_output_centerline + "\n\tElapsed time: " + str( int(round(elapsed_time)) ) + "s\n"
def main(): parser = get_parser() param = Param() args = sys.argv[1:] arguments = parser.parse(args) # get arguments fname_data = arguments['-i'] fname_seg = arguments['-s'] fname_landmarks = arguments['-l'] if '-ofolder' in arguments: path_output = arguments['-ofolder'] else: path_output = '' path_template = sct.slash_at_the_end(arguments['-t'], 1) contrast_template = arguments['-c'] ref = arguments['-ref'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) param.verbose = verbose # TODO: not clean, unify verbose or param.verbose in code, but not both if '-param-straighten' in arguments: param.param_straighten = arguments['-param-straighten'] # 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 # file_template_label = param.file_template_label zsubsample = param.zsubsample # smoothing_sigma = param.smoothing_sigma # retrieve template file names from sct_warp_template import get_file_label file_template_vertebral_labeling = get_file_label(path_template + 'template/', 'vertebral') file_template = get_file_label(path_template + 'template/', contrast_template.upper() + '-weighted') file_template_seg = get_file_label(path_template + 'template/', 'spinal cord') # start timer start_time = time.time() # get fname of the template + template objects fname_template = path_template + 'template/' + file_template fname_template_vertebral_labeling = path_template + 'template/' + file_template_vertebral_labeling fname_template_seg = path_template + 'template/' + file_template_seg # 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) # print 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(remove_temp_files), verbose) # create QC folder sct.create_folder(param.path_qc) # check if data, segmentation and landmarks are in the same space # JULIEN 2017-04-25: removed because of issue #1168 # sct.printv('\nCheck if data, segmentation and landmarks are in the same space...') # if not sct.check_if_same_space(fname_data, fname_seg): # sct.printv('ERROR: Data image and segmentation are not in the same space. Please check space and orientation of your files', verbose, 'error') # if not sct.check_if_same_space(fname_data, fname_landmarks): # sct.printv('ERROR: Data image and landmarks are not in the same space. Please check space and orientation of your files', verbose, 'error') # check input labels labels = check_labels(fname_landmarks) # create temporary folder path_tmp = sct.tmp_create(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) sct.run('sct_convert -i ' + fname_data + ' -o ' + path_tmp + ftmp_data) sct.run('sct_convert -i ' + fname_seg + ' -o ' + path_tmp + ftmp_seg) sct.run('sct_convert -i ' + fname_landmarks + ' -o ' + path_tmp + ftmp_label) sct.run('sct_convert -i ' + fname_template + ' -o ' + path_tmp + ftmp_template) sct.run('sct_convert -i ' + fname_template_seg + ' -o ' + path_tmp + ftmp_template_seg) # sct.run('sct_convert -i '+fname_template_label+' -o '+path_tmp+ftmp_template_label) # go to tmp folder os.chdir(path_tmp) # copy header of anat to segmentation (issue #1168) # from sct_image import copy_header # im_data = Image(ftmp_data) # im_seg = Image(ftmp_seg) # copy_header(im_data, im_seg) # im_seg.save() # im_label = Image(ftmp_label) # copy_header(im_data, im_label) # im_label.save() # Generate labels from template vertebral labeling sct.printv('\nGenerate labels from template vertebral labeling', verbose) sct.run('sct_label_utils -i ' + fname_template_vertebral_labeling + ' -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') # binarize segmentation (in case it has values below 0 caused by manual editing) sct.printv('\nBinarize segmentation', verbose) sct.run('sct_maths -i seg.nii.gz -bin 0.5 -o seg.nii.gz') # smooth segmentation (jcohenadad, issue #613) # sct.printv('\nSmooth segmentation...', verbose) # sct.run('sct_maths -i '+ftmp_seg+' -smooth 1.5 -o '+add_suffix(ftmp_seg, '_smooth')) # jcohenadad: updated 2016-06-16: DO NOT smooth the seg anymore. Issue # # sct.run('sct_maths -i '+ftmp_seg+' -smooth 0 -o '+add_suffix(ftmp_seg, '_smooth')) # ftmp_seg = add_suffix(ftmp_seg, '_smooth') # Switch between modes: subject->template or template->subject if ref == 'template': # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) sct.run('sct_resample -i ' + ftmp_data + ' -mm 1.0x1.0x1.0 -x linear -o ' + add_suffix(ftmp_data, '_1mm')) ftmp_data = add_suffix(ftmp_data, '_1mm') sct.run('sct_resample -i ' + ftmp_seg + ' -mm 1.0x1.0x1.0 -x linear -o ' + add_suffix(ftmp_seg, '_1mm')) ftmp_seg = add_suffix(ftmp_seg, '_1mm') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling with neighrest neighbour can make them disappear. Therefore a more clever approach is required. 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) sct.run('sct_image -i ' + ftmp_data + ' -setorient RPI -o ' + add_suffix(ftmp_data, '_rpi')) ftmp_data = add_suffix(ftmp_data, '_rpi') sct.run('sct_image -i ' + ftmp_seg + ' -setorient RPI -o ' + add_suffix(ftmp_seg, '_rpi')) ftmp_seg = add_suffix(ftmp_seg, '_rpi') sct.run('sct_image -i ' + ftmp_label + ' -setorient RPI -o ' + add_suffix(ftmp_label, '_rpi')) ftmp_label = add_suffix(ftmp_label, '_rpi') # get landmarks in native space # crop segmentation # output: segmentation_rpi_crop.nii.gz status_crop, output_crop = sct.run('sct_crop_image -i ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_crop') + ' -dim 2 -bzmax', verbose) ftmp_seg = add_suffix(ftmp_seg, '_crop') cropping_slices = output_crop.split('Dimension 2: ')[1].split('\n')[0].split(' ') # 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) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', '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: sct.run('sct_straighten_spinalcord -i ' + ftmp_seg + ' -s ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_straight') + ' -qc 0 -r 0 -v ' + str(verbose), verbose) # 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.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d ' + ftmp_data + ' -o warp_straight2curve.nii.gz') # 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 ' + ftmp_label) # Dilating the input label so they can be straighten without losing them sct.printv('\nDilating input labels using 3vox ball radius') sct.run('sct_maths -i ' + ftmp_label + ' -o ' + add_suffix(ftmp_label, '_dilate') + ' -dilate 3') 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) from msct_register_landmarks import register_landmarks try: register_landmarks(ftmp_label, ftmp_template_label, paramreg.steps['0'].dof, fname_affine='straight2templateAffine.txt', verbose=verbose) 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://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/', verbose=verbose, type='error') # 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(round(np.min(points_straight))), int(round(np.max(points_straight))) sct.run('sct_crop_image -i ' + ftmp_seg + ' -start ' + str(min_point) + ' -end ' + str(max_point) + ' -dim 2 -b 0 -o ' + add_suffix(ftmp_seg, '_black')) ftmp_seg = add_suffix(ftmp_seg, '_black') """ # binarize sct.printv('\nBinarize segmentation...', verbose) sct.run('sct_maths -i ' + ftmp_seg + ' -bin 0.5 -o ' + add_suffix(ftmp_seg, '_bin')) ftmp_seg = add_suffix(ftmp_seg, '_bin') # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = find_zmin_zmax(ftmp_seg) # crop template in z-direction (for faster processing) sct.printv('\nCrop data in template space (for faster processing)...', verbose) sct.run('sct_crop_image -i ' + ftmp_template + ' -o ' + add_suffix(ftmp_template, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template)) ftmp_template = add_suffix(ftmp_template, '_crop') sct.run('sct_crop_image -i ' + ftmp_template_seg + ' -o ' + add_suffix(ftmp_template_seg, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template)) ftmp_template_seg = add_suffix(ftmp_template_seg, '_crop') sct.run('sct_crop_image -i ' + ftmp_data + ' -o ' + add_suffix(ftmp_data, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template)) ftmp_data = add_suffix(ftmp_data, '_crop') sct.run('sct_crop_image -i ' + ftmp_seg + ' -o ' + add_suffix(ftmp_seg, '_crop') + ' -dim 2 -start ' + str(zmin_template) + ' -end ' + str(zmax_template)) ftmp_seg = add_suffix(ftmp_seg, '_crop') # sub-sample in z-direction 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 step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) # 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.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() 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) sct.run('sct_image -i ' + ftmp_data + ' -setorient RPI -o ' + add_suffix(ftmp_data, '_rpi')) ftmp_data = add_suffix(ftmp_data, '_rpi') sct.run('sct_image -i ' + ftmp_seg + ' -setorient RPI -o ' + add_suffix(ftmp_seg, '_rpi')) ftmp_seg = add_suffix(ftmp_seg, '_rpi') sct.run('sct_image -i ' + ftmp_label + ' -setorient RPI -o ' + add_suffix(ftmp_label, '_rpi')) ftmp_label = add_suffix(ftmp_label, '_rpi') # 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 ' + 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 = 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.setFileName('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) from msct_register_landmarks import register_landmarks 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=param.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://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/', 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 to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp + 'warp_template2anat.nii.gz', path_output + 'warp_template2anat.nii.gz', verbose) sct.generate_output_file(path_tmp + 'warp_anat2template.nii.gz', path_output + 'warp_anat2template.nii.gz', verbose) sct.generate_output_file(path_tmp + 'template2anat.nii.gz', path_output + 'template2anat' + ext_data, verbose) sct.generate_output_file(path_tmp + 'anat2template.nii.gz', path_output + 'anat2template' + ext_data, verbose) if ref == 'template': # copy straightening files in case subsequent SCT functions need them sct.generate_output_file(path_tmp + 'warp_curve2straight.nii.gz', path_output + 'warp_curve2straight.nii.gz', verbose) sct.generate_output_file(path_tmp + 'warp_straight2curve.nii.gz', path_output + 'warp_straight2curve.nii.gz', verbose) sct.generate_output_file(path_tmp + 'straight_ref.nii.gz', path_output + 'straight_ref.nii.gz', verbose) # Delete temporary files if remove_temp_files: sct.printv('\nDelete temporary files...', verbose) sct.run('rm -rf ' + path_tmp) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', verbose) if '-qc' in arguments and not arguments.get('-noqc', False): qc_path = arguments['-qc'] import spinalcordtoolbox.reports.qc as qc import spinalcordtoolbox.reports.slice as qcslice qc_param = qc.Params(fname_data, 'sct_register_to_template', args, 'Sagittal', qc_path) report = qc.QcReport(qc_param, '') @qc.QcImage(report, 'none', [qc.QcImage.no_seg_seg]) def test(qslice): return qslice.single() fname_template2anat = path_output + 'template2anat' + ext_data test(qcslice.SagittalTemplate2Anat(Image(fname_data), Image(fname_template2anat), Image(fname_seg))) sct.printv('Sucessfully generate the QC results in %s' % qc_param.qc_results) sct.printv('Use the following command to see the results in a browser') sct.printv('sct_qc -folder %s' % qc_path, type='info') # to view results sct.printv('\nTo view results, type:', verbose) sct.printv('fslview ' + fname_data + ' ' + path_output + 'template2anat -b 0,4000 &', verbose, 'info') sct.printv('fslview ' + fname_template + ' -b 0,5000 ' + path_output + 'anat2template &\n', verbose, 'info')
def test(path_data='', parameters=''): verbose = 0 dice_threshold = 0.9 add_path_for_template = False # if absolute path or no path to template is provided, then path to data should not be added. # initializations dice_template2anat = float('NaN') dice_anat2template = float('NaN') output = '' if not parameters: parameters = '-i t2/t2.nii.gz -l t2/labels.nii.gz -s t2/t2_seg.nii.gz ' \ '-param step=1,type=seg,algo=centermassrot,metric=MeanSquares:step=2,type=seg,algo=bsplinesyn,iter=5,metric=MeanSquares ' \ '-t template/ -r 0' add_path_for_template = True # in this case, path to data should be added parser = sct_register_to_template.get_parser() dict_param = parser.parse(parameters.split(), check_file_exist=False) if add_path_for_template: dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param), path_data, input_file=True) else: dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param), path_data, input_file=True, do_not_add_path=['-t']) param_with_path = parser.dictionary_to_string(dict_param_with_path) # Check if input files exist if not (os.path.isfile(dict_param_with_path['-i']) and os.path.isfile(dict_param_with_path['-l']) and os.path.isfile(dict_param_with_path['-s'])): status = 200 output = 'ERROR: the file(s) provided to test function do not exist in folder: ' + path_data return status, output, DataFrame(data={ 'status': int(status), 'output': output }, index=[path_data]) # return status, output, DataFrame( # data={'status': status, 'output': output, # 'dice_template2anat': float('nan'), 'dice_anat2template': float('nan')}, # index=[path_data]) # if template is not specified, use default # if not os.path.isdir(dict_param_with_path['-t']): # status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # dict_param_with_path['-t'] = path_sct + default_template # param_with_path = parser.dictionary_to_string(dict_param_with_path) # get contrast folder from -i option. # We suppose we can extract it as the first object when spliting with '/' delimiter. contrast_folder = '' input_filename = '' if dict_param['-i'][0] == '/': dict_param['-i'] = dict_param['-i'][1:] input_split = dict_param['-i'].split('/') if len(input_split) == 2: contrast_folder = input_split[0] + '/' input_filename = input_split[1] else: input_filename = input_split[0] if not contrast_folder: # if no contrast folder, send error. status = 201 output = 'ERROR: when extracting the contrast folder from input file in command line: ' + dict_param[ '-i'] + ' for ' + path_data return status, output, DataFrame(data={ 'status': int(status), 'output': output }, index=[path_data]) # return status, output, DataFrame( # data={'status': status, 'output': output, 'dice_template2anat': float('nan'), 'dice_anat2template': float('nan')}, index=[path_data]) # create output path # TODO: create function for that import time, random subject_folder = path_data.split('/') if subject_folder[-1] == '' and len(subject_folder) > 1: subject_folder = subject_folder[-2] else: subject_folder = subject_folder[-1] path_output = sct.slash_at_the_end( 'sct_register_to_template_' + subject_folder + '_' + time.strftime("%y%m%d%H%M%S") + '_' + str(random.randint(1, 1000000)), slash=1) param_with_path += ' -ofolder ' + path_output sct.create_folder(path_output) # log file # TODO: create function for that import sys fname_log = path_output + 'output.log' sct.pause_stream_logger() file_handler = sct.add_file_handler_to_logger(filename=fname_log, mode='w', log_format="%(message)s") # # stdout_log = file(fname_log, 'w') # redirect to log file # stdout_orig = sys.stdout # sys.stdout = stdout_log cmd = 'sct_register_to_template ' + param_with_path output += '\n====================================================================================================\n' + cmd + '\n====================================================================================================\n\n' # copy command time_start = time.time() try: status, o = sct.run(cmd, verbose) except: status, o = 1, 'ERROR: Function crashed!' output += o duration = time.time() - time_start # if command ran without error, test integrity if status == 0: # get filename_template_seg fname_template_seg = get_file_label( sct.slash_at_the_end(dict_param_with_path['-t'], 1) + 'template/', 'spinal cord', output='filewithpath') # apply transformation to binary mask: template --> anat sct.run( 'sct_apply_transfo -i ' + fname_template_seg + ' -d ' + dict_param_with_path['-s'] + ' -w ' + path_output + 'warp_template2anat.nii.gz' + ' -o ' + path_output + 'test_template2anat.nii.gz -x nn', verbose) # apply transformation to binary mask: anat --> template sct.run( 'sct_apply_transfo -i ' + dict_param_with_path['-s'] + ' -d ' + fname_template_seg + ' -w ' + path_output + 'warp_anat2template.nii.gz' + ' -o ' + path_output + 'test_anat2template.nii.gz -x nn', verbose) # compute dice coefficient between template segmentation warped into anat and segmentation from anat cmd = 'sct_dice_coefficient -i ' + dict_param_with_path[ '-s'] + ' -d ' + path_output + 'test_template2anat.nii.gz' status1, output1 = sct.run(cmd, verbose) # parse output and compare to acceptable threshold dice_template2anat = float( output1.split('3D Dice coefficient = ')[1].split('\n')[0]) if dice_template2anat < dice_threshold: status1 = 99 # compute dice coefficient between segmentation from anat warped into template and template segmentation # N.B. here we use -bmax because the FOV of the anat is smaller than the template cmd = 'sct_dice_coefficient -i ' + fname_template_seg + ' -d ' + path_output + 'test_anat2template.nii.gz -bmax 1' status2, output2 = sct.run(cmd, verbose) # parse output and compare to acceptable threshold dice_anat2template = float( output2.split('3D Dice coefficient = ')[1].split('\n')[0]) if dice_anat2template < dice_threshold: status2 = 99 # check if at least one integrity status was equal to 99 if status1 == 99 or status2 == 99: status = 99 # concatenate outputs output = output + output1 + output2 # transform results into Pandas structure results = DataFrame(data={ 'status': int(status), 'output': output, 'dice_template2anat': dice_template2anat, 'dice_anat2template': dice_anat2template, 'duration [s]': duration }, index=[path_data]) sct.log.info(output) sct.remove_handler(file_handler) sct.start_stream_logger() return status, output, results
def main(): # initialization start_time = time.time() path_out = '.' param_user = '' # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # get path of the toolbox status, param.path_sct = commands.getstatusoutput('echo $SCT_DIR') # Parameters for debug mode if param.debug: # get path of the testing data status, path_sct_data = commands.getstatusoutput( 'echo $SCT_TESTING_DATA_DIR') param.fname_data = path_sct_data + '/dmri/dmri.nii.gz' param.fname_bvecs = path_sct_data + '/dmri/bvecs.txt' param.fname_mask = path_sct_data + '/dmri/dmri.nii.gz' param.remove_tmp_files = 0 param.verbose = 1 param.run_eddy = 0 param.otsu = 0 param.group_size = 5 param.iterative_averaging = 1 else: # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:], 'hi:a:b:e:f:g:m:o:p:r:t:v:x:') except getopt.GetoptError: usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-a'): param.fname_bvals = arg elif opt in ('-b'): param.fname_bvecs = arg elif opt in ('-e'): param.run_eddy = int(arg) elif opt in ('-f'): param.spline_fitting = int(arg) elif opt in ('-g'): param.group_size = int(arg) elif opt in ('-i'): param.fname_data = arg elif opt in ('-m'): param.fname_mask = arg elif opt in ('-o'): path_out = arg elif opt in ('-p'): param_user = arg elif opt in ('-r'): param.remove_tmp_files = int(arg) elif opt in ('-t'): param.otsu = int(arg) elif opt in ('-v'): param.verbose = int(arg) elif opt in ('-x'): param.interp = arg # display usage if a mandatory argument is not provided if param.fname_data == '' or param.fname_bvecs == '': sct.printv( 'ERROR: All mandatory arguments are not provided. See usage.', 1, 'error') usage() # parse argument for param if not param_user == '': param.param = param_user.replace(' ', '').split( ',') # remove spaces and parse with comma # TODO: check integrity of input # param.param = [i for i in range(len(param_user))] del param_user sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............' + param.fname_data, param.verbose) sct.printv(' bvecs file ............' + param.fname_bvecs, param.verbose) sct.printv(' bvals file ............' + param.fname_bvals, param.verbose) sct.printv(' mask file .............' + param.fname_mask, param.verbose) # check existence of input files sct.printv('\nCheck file existence...', param.verbose) sct.check_file_exist(param.fname_data, param.verbose) sct.check_file_exist(param.fname_bvecs, param.verbose) if not param.fname_bvals == '': sct.check_file_exist(param.fname_bvals, param.verbose) if not param.fname_mask == '': sct.check_file_exist(param.fname_mask, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, param.verbose) # Copying input data to tmp folder # NB: cannot use c3d here because c3d cannot convert 4D data. sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) sct.run('cp ' + param.fname_data + ' ' + path_tmp + 'dmri' + ext_data, param.verbose) sct.run('cp ' + param.fname_bvecs + ' ' + path_tmp + 'bvecs.txt', param.verbose) if param.fname_mask != '': sct.run('cp ' + param.fname_mask + ' ' + path_tmp + 'mask' + ext_mask, param.verbose) # go to tmp folder os.chdir(path_tmp) # convert dmri to nii format convert('dmri' + ext_data, 'dmri.nii') # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = 'mask' + ext_mask # run moco dmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp + 'dmri' + param.suffix + '.nii', path_out + file_data + param.suffix + ext_data, param.verbose) sct.generate_output_file( path_tmp + 'b0_mean.nii', path_out + 'b0' + param.suffix + '_mean' + ext_data, param.verbose) sct.generate_output_file( path_tmp + 'dwi_mean.nii', path_out + 'dwi' + param.suffix + '_mean' + ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf ' + path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', param.verbose) #To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv( 'fslview -m ortho,ortho ' + param.path_out + file_data + param.suffix + ' ' + file_data + ' &\n', param.verbose, 'info')
def main(): parser = get_parser() arguments = parser.parse(sys.argv[1:]) if '-d' in arguments: param.download = int(arguments['-d']) if '-p' in arguments: param.path_data = arguments['-p'] if '-f' in arguments: param.function_to_test = arguments['-f'] if '-r' in arguments: param.remove_tmp_file = int(arguments['-r']) # path_data = param.path_data function_to_test = param.function_to_test # function_to_avoid = param.function_to_avoid remove_tmp_file = param.remove_tmp_file start_time = time.time() # # download data # if param.download: # downloaddata() # # get absolute path and add slash at the end param.path_data = sct.slash_at_the_end(os.path.abspath(param.path_data), 1) # check existence of testing data folder if not os.path.isdir(param.path_data) or param.download: downloaddata() # display path to data sct.printv('\nPath to testing data: '+param.path_data, param.verbose) # create temp folder that will have all results and go in it param.path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.create_folder(param.path_tmp) os.chdir(param.path_tmp) # get list of all scripts to test functions = fill_functions() # loop across all functions and test them status = [] [status.append(test_function(f)) for f in functions if function_to_test == f] if not status: for f in functions: status.append(test_function(f)) print 'status: '+str(status) # display elapsed time elapsed_time = time.time() - start_time print 'Finished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n' # remove temp files if param.remove_tmp_file: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf '+param.path_tmp, param.verbose) e = 0 if sum(status) != 0: e = 1 print e sys.exit(e)
def main(args=None): # initialization start_time = time.time() path_out = '.' param = Param() # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # get path of the toolbox # status, param.path_sct = commands.getstatusoutput('echo $SCT_DIR') # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] param.fname_bvecs = arguments['-bvec'] if '-bval' in arguments: param.fname_bvals = arguments['-bval'] if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-thr' in arguments: param.otsu = arguments['-thr'] if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_tmp_files = int(arguments['-r']) if '-v' in arguments: param.verbose = int(arguments['-v']) # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, param.verbose) # names of files in temporary folder ext = '.nii' dmri_name = 'dmri' mask_name = 'mask' bvecs_fname = 'bvecs.txt' # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, path_tmp + dmri_name + ext) sct.run('cp ' + param.fname_bvecs + ' ' + path_tmp + bvecs_fname, param.verbose) if param.fname_mask != '': sct.run( 'cp ' + param.fname_mask + ' ' + path_tmp + mask_name + ext_mask, param.verbose) # go to tmp folder os.chdir(path_tmp) # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = mask_name + ext_mask # run moco dmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp + dmri_name + param.suffix + ext, path_out + file_data + param.suffix + ext_data, param.verbose) sct.generate_output_file( path_tmp + 'b0_mean.nii', path_out + 'b0' + param.suffix + '_mean' + ext_data, param.verbose) sct.generate_output_file( path_tmp + 'dwi_mean.nii', path_out + 'dwi' + param.suffix + '_mean' + ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf ' + path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', param.verbose) # To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv( 'fslview -m ortho,ortho ' + param.path_out + file_data + param.suffix + ' ' + file_data + ' &\n', param.verbose, 'info')
def dmri_moco(param): file_data = 'dmri' ext_data = '.nii' file_b0 = 'b0' file_dwi = 'dwi' mat_final = 'mat_final/' file_dwi_group = 'dwi_averaged_groups' # no extension fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image(file_data + '.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), param.verbose) # Identify b=0 and DWI images sct.printv('\nIdentify b=0 and DWI images...', param.verbose) index_b0, index_dwi, nb_b0, nb_dwi = identify_b0('bvecs.txt', param.fname_bvals, param.bval_min, param.verbose) # check if dmri and bvecs are the same size if not nb_b0 + nb_dwi == nt: sct.printv( '\nERROR in ' + os.path.basename(__file__) + ': Size of data (' + str(nt) + ') and size of bvecs (' + str(nb_b0 + nb_dwi) + ') are not the same. Check your bvecs file.\n', 1, 'error') sys.exit(2) # Prepare NIFTI (mean/groups...) #=================================================================================================================== # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) status, output = sct.run( 'sct_split_data -i ' + file_data + ext_data + ' -dim t -suffix _T', param.verbose) # Merge b=0 images sct.printv('\nMerge b=0...', param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_b0 # for it in range(nb_b0): # cmd = cmd + ' ' + file_data + '_T' + str(index_b0[it]).zfill(4) cmd = 'sct_concat_data -dim t -o ' + file_b0 + ext_data + ' -i ' for it in range(nb_b0): cmd = cmd + file_data + '_T' + str( index_b0[it]).zfill(4) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string status, output = sct.run(cmd, param.verbose) sct.printv((' File created: ' + file_b0), param.verbose) # Average b=0 images sct.printv('\nAverage b=0...', param.verbose) file_b0_mean = file_b0 + '_mean' sct.run( 'sct_maths -i ' + file_b0 + '.nii' + ' -o ' + file_b0_mean + '.nii' + ' -mean t', param.verbose) # if not average_data_across_dimension(file_b0+'.nii', file_b0_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_b0 + ' -Tmean ' + file_b0_mean # status, output = sct.run(cmd, param.verbose) # Number of DWI groups nb_groups = int(math.floor(nb_dwi / param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_dwi[(iGroup * param.group_size):((iGroup + 1) * param.group_size)]) # add the remaining images to the last DWI group nb_remaining = nb_dwi % param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_dwi[len(index_dwi) - nb_remaining:len(index_dwi)]) # DWI groups for iGroup in range(nb_groups): sct.printv('\nDWI group: ' + str((iGroup + 1)) + '/' + str(nb_groups), param.verbose) # get index index_dwi_i = group_indexes[iGroup] nb_dwi_i = len(index_dwi_i) # Merge DW Images sct.printv('Merge DW images...', param.verbose) file_dwi_merge_i = file_dwi + '_' + str(iGroup) cmd = 'sct_concat_data -dim t -o ' + file_dwi_merge_i + ext_data + ' -i ' for it in range(nb_dwi_i): cmd = cmd + file_data + '_T' + str( index_dwi_i[it]).zfill(4) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_dwi_merge_i # for it in range(nb_dwi_i): # cmd = cmd +' ' + file_data + '_T' + str(index_dwi_i[it]).zfill(4) # Average DW Images sct.printv('Average DW images...', param.verbose) file_dwi_mean = file_dwi + '_mean_' + str(iGroup) sct.run( 'sct_maths -i ' + file_dwi_merge_i + '.nii' + ' -o ' + file_dwi_mean + '.nii' + ' -mean t', param.verbose) # if not average_data_across_dimension(file_dwi_merge_i+'.nii', file_dwi_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_dwi_merge_i + ' -Tmean ' + file_dwi_mean # sct.run(cmd, param.verbose) # Merge DWI groups means sct.printv('\nMerging DW files...', param.verbose) # file_dwi_groups_means_merge = 'dwi_averaged_groups' cmd = 'sct_concat_data -dim t -o ' + file_dwi_group + ext_data + ' -i ' for iGroup in range(nb_groups): cmd = cmd + file_dwi + '_mean_' + str(iGroup) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_dwi_group # for iGroup in range(nb_groups): # cmd = cmd + ' ' + file_dwi + '_mean_' + str(iGroup) # Average DW Images # TODO: USEFULL ??? sct.printv('\nAveraging all DW images...', param.verbose) fname_dwi_mean = 'dwi_mean' sct.run( 'sct_maths -i ' + file_dwi_group + '.nii' + ' -o ' + file_dwi_group + '_mean.nii' + ' -mean t', param.verbose) # if not average_data_across_dimension(file_dwi_group+'.nii', file_dwi_group+'_mean.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run(fsloutput + 'fslmaths ' + file_dwi_group + ' -Tmean ' + file_dwi_group+'_mean', param.verbose) # segment dwi images using otsu algorithm if param.otsu: sct.printv('\nSegment group DWI using OTSU algorithm...', param.verbose) # import module otsu = importlib.import_module('sct_otsu') # get class from module param_otsu = otsu.param() #getattr(otsu, param) param_otsu.fname_data = file_dwi_group + '.nii' param_otsu.threshold = param.otsu param_otsu.file_suffix = '_seg' # run otsu otsu.otsu(param_otsu) file_dwi_group = file_dwi_group + '_seg' # extract first DWI volume as target for registration nii = Image(file_dwi_group + '.nii') data_crop = nii.data[:, :, :, index_dwi[0]:index_dwi[0] + 1] nii.data = data_crop nii.setFileName('target_dwi.nii') nii.save() # START MOCO #=================================================================================================================== # Estimate moco on b0 groups sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on b=0 images...', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'b0' if index_dwi[0] != 0: # If first DWI is not the first volume (most common), then there is a least one b=0 image before. In that case # select it as the target image for registration of all b=0 param_moco.file_target = file_data + '_T' + str( index_b0[index_dwi[0] - 1]).zfill(4) else: # If first DWI is the first volume, then the target b=0 is the first b=0 from the index_b0. param_moco.file_target = file_data + '_T' + str(index_b0[0]).zfill(4) param_moco.path_out = '' param_moco.todo = 'estimate' param_moco.mat_moco = 'mat_b0groups' moco.moco(param_moco) # Estimate moco on dwi groups sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on DW images...', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = file_dwi_group param_moco.file_target = 'target_dwi' # target is the first DW image (closest to the first b=0) param_moco.path_out = '' #param_moco.todo = 'estimate' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_dwigroups' moco.moco(param_moco) # create final mat folder sct.create_folder(mat_final) # Copy b=0 registration matrices sct.printv('\nCopy b=0 registration matrices...', param.verbose) for it in range(nb_b0): sct.run( 'cp ' + 'mat_b0groups/' + 'mat.T' + str(it) + ext_mat + ' ' + mat_final + 'mat.T' + str(index_b0[it]) + ext_mat, param.verbose) # Copy DWI registration matrices sct.printv('\nCopy DWI registration matrices...', param.verbose) for iGroup in range(nb_groups): for dwi in range(len(group_indexes[iGroup])): sct.run( 'cp ' + 'mat_dwigroups/' + 'mat.T' + str(iGroup) + ext_mat + ' ' + mat_final + 'mat.T' + str(group_indexes[iGroup][dwi]) + ext_mat, param.verbose) # Spline Regularization along T if param.spline_fitting: moco.spline(mat_final, nt, nz, param.verbose, np.array(index_b0), param.plot_graph) # combine Eddy Matrices if param.run_eddy: param.mat_2_combine = 'mat_eddy' param.mat_final = mat_final moco.combine_matrix(param) # Apply moco on all dmri data sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'dmri' param_moco.file_target = file_dwi + '_mean_' + str( 0) # reference for reslicing into proper coordinate system param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". copy_header('dmri.nii', 'dmri_moco.nii') # generate b0_moco_mean and dwi_moco_mean cmd = 'sct_dmri_separate_b0_and_dwi -i dmri' + param.suffix + '.nii -b bvecs.txt -a 1' if not param.fname_bvals == '': cmd = cmd + ' -m ' + param.fname_bvals sct.run(cmd, param.verbose)
def test_function(param_test): """ Parameters ---------- file_testing Returns ------- path_output [str]: path where to output testing data """ # load modules of function to test module_function_to_test = importlib.import_module( param_test.function_to_test) module_testing = importlib.import_module('test_' + param_test.function_to_test) # retrieve subject name subject_folder = sct.slash_at_the_end(param_test.path_data, 0).split('/') subject_folder = subject_folder[-1] # build path_output variable path_testing = os.getcwd() + '/' param_test.path_output = sct.slash_at_the_end( param_test.function_to_test + '_' + subject_folder + '_' + time.strftime("%y%m%d%H%M%S") + '_' + str(random.randint(1, 1000000)), slash=1) sct.create_folder(param_test.path_output) param_test.path_output = path_testing + param_test.path_output # get parser information parser = module_function_to_test.get_parser() dict_args = parser.parse(shlex.split(param_test.args), check_file_exist=False) # TODO: if file in list does not exist, raise exception and assign status=200 # add data path to each input argument dict_args_with_path = parser.add_path_to_file(deepcopy(dict_args), param_test.path_data, input_file=True) # add data path to each output argument dict_args_with_path = parser.add_path_to_file( deepcopy(dict_args_with_path), param_test.path_output, input_file=False, output_file=True) # save into class param_test.dict_args_with_path = dict_args_with_path param_test.args_with_path = parser.dictionary_to_string( dict_args_with_path) # check if parser has key '-ofolder' that has not been added already. If so, then assign output folder if parser.options.has_key( '-ofolder') and '-ofolder' not in dict_args_with_path: param_test.args_with_path += ' -ofolder ' + param_test.path_output # open log file # Note: the statement below is not included in the if, because even if redirection does not occur, we want the file to be create otherwise write_to_log will fail param_test.fname_log = param_test.path_output + param_test.function_to_test + '.log' stdout_log = file(param_test.fname_log, 'w') # redirect to log file if param_test.redirect_stdout: param_test.stdout_orig = sys.stdout sys.stdout = stdout_log # initialize panda dataframe param_test.results = DataFrame(index=[subject_folder], data={ 'status': 0, 'output': '', 'path_data': param_test.path_data }) # retrieve input file (will be used later for integrity testing) if '-i' in dict_args: # check if list in case of multiple input files if not isinstance(dict_args_with_path['-i'], list): list_file_to_check = [dict_args_with_path['-i']] # assign field file_input for integrity testing param_test.file_input = dict_args['-i'].split('/')[-1] else: list_file_to_check = dict_args_with_path['-i'] # TODO: assign field file_input for integrity testing for file_to_check in list_file_to_check: # file_input = file_to_check.split('/')[1] # Check if input files exist if not (os.path.isfile(file_to_check)): param_test.status = 200 param_test.output += '\nERROR: This input file does not exist: ' + file_to_check write_to_log_file(param_test.fname_log, param_test.output, 'w') return update_param(param_test) # retrieve ground truth (will be used later for integrity testing) if '-igt' in dict_args: param_test.fname_gt = dict_args_with_path['-igt'] # Check if ground truth files exist if not os.path.isfile(param_test.fname_gt): param_test.status = 201 param_test.output += '\nERROR: The following file used for ground truth does not exist: ' + param_test.fname_gt write_to_log_file(param_test.fname_log, param_test.output, 'w') return update_param(param_test) # go to specific testing directory os.chdir(param_test.path_output) # run command cmd = param_test.function_to_test + param_test.args_with_path param_test.output += '\n====================================================================================================\n' + cmd + '\n====================================================================================================\n\n' # copy command time_start = time.time() try: param_test.status, o = sct.run(cmd, 0, error_exit='warning') if param_test.status: raise Exception except Exception, err: param_test.status = 1 param_test.output += str(err) write_to_log_file(param_test.fname_log, param_test.output, 'w') return update_param(param_test)
def test(path_data='', parameters=''): # initialization output = '' difference_threshold = 0.95 # parameters if not parameters: # parameters = '-i t2/t2.nii.gz -m '+mask_filename_tmp parameters = '-i t2/t2.nii.gz -m t2/t2_seg.nii.gz' # retrieve flags try: parser = sct_analyze_texture.get_parser() dict_param = parser.parse(parameters.split(), check_file_exist=False) dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param), path_data, input_file=True) subject_folder = path_data.split('/') if subject_folder[-1] == '' and len(subject_folder) > 1: subject_folder = subject_folder[-2] else: subject_folder = subject_folder[-1] path_output = sct.slash_at_the_end( 'sct_analyze_texture_' + subject_folder + '_' + time.strftime("%y%m%d%H%M%S") + '_' + str(random.randint(1, 1000000)), slash=1) sct.create_folder(path_output) dict_param_with_path['-ofolder'] = path_output dict_param_with_path['-feature'] = 'contrast' param_with_path = parser.dictionary_to_string(dict_param_with_path) # in case not all mandatory flags are filled except SyntaxError as err: print err status = 1 output = err return status, output, DataFrame(data={ 'status': int(status), 'output': output }, index=[path_data]) # Extract contrast contrast = '' input_filename = '' if dict_param['-i'][0] == '/': dict_param['-i'] = dict_param['-i'][1:] input_split = dict_param['-i'].split('/') if len(input_split) == 2: contrast = input_split[0] input_filename = input_split[1] else: input_filename = input_split[0] if not contrast: # if no contrast folder, send error. status = 1 output += '\nERROR: when extracting the contrast folder from input file in command line: ' + dict_param[ '-i'] + ' for ' + path_data return status, output, DataFrame(data={ 'status': status, 'output': output, 'texture_difference': float('nan') }, index=[path_data]) # log file import sys fname_log = path_output + 'output.log' stdout_log = file(fname_log, 'w') # redirect to log file stdout_orig = sys.stdout sys.stdout = stdout_log # Check if input files exist if not (os.path.isfile(dict_param_with_path['-i']) and os.path.isfile(dict_param_with_path['-m'])): status = 200 output += '\nERROR: the file(s) provided to test function do not exist in folder: ' + path_data write_to_log_file(fname_log, output, 'w') return status, output, DataFrame(data={ 'status': status, 'output': output, 'texture_difference': float('nan') }, index=[path_data]) # run command cmd = 'sct_analyze_texture ' + param_with_path output += '\n====================================================================================================\n'\ + cmd + \ '\n====================================================================================================\n\n' # copy command time_start = time.time() try: status, o = sct.run(cmd, 0) except: status, o = 1, 'ERROR: Function crashed!' output += o duration = time.time() - time_start # extract name of one texture file: inputname_contrast_1_mean.nii.gz # where inputname is the filename of the input image texture_test_filename = path_output + sct.add_suffix( input_filename, '_contrast_1_mean') texture_ref_filename = path_data + contrast + '/' + sct.add_suffix( input_filename, '_contrast_1_mean_ref') # if command ran without error, test integrity if status == 0: # Substract generated image and image from database diff_im = Image(texture_test_filename).data - Image( texture_ref_filename).data cmpt_diff_vox = np.count_nonzero(diff_im) cmpt_tot_vox = np.count_nonzero(Image(texture_ref_filename).data) difference_vox = float(cmpt_tot_vox - cmpt_diff_vox) / cmpt_tot_vox if difference_vox < difference_threshold: status = 99 else: difference_vox = 0.0 # transform results into Pandas structure results = DataFrame(data={ 'status': status, 'output': output, 'texture_similarity': difference_vox, 'duration [s]': duration }, index=[path_data]) sys.stdout.close() sys.stdout = stdout_orig # write log file write_to_log_file(fname_log, output, mode='r+', prepend=True) return status, output, results
def fmri_moco(param): file_data = 'fmri' ext_data = '.nii' mat_final = 'mat_final/' fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image(file_data+'.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), param.verbose) # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) status, output = sct.run('sct_split_data -i ' + file_data + ext_data + ' -dim t -suffix _T', param.verbose) # assign an index to each volume index_fmri = range(0, nt) # Number of groups nb_groups = int(math.floor(nt/param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_fmri[(iGroup*param.group_size):((iGroup+1)*param.group_size)]) # add the remaining images to the last DWI group nb_remaining = nt%param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_fmri[len(index_fmri)-nb_remaining:len(index_fmri)]) # groups for iGroup in range(nb_groups): sct.printv('\nGroup: ' +str((iGroup+1))+'/'+str(nb_groups), param.verbose) # get index index_fmri_i = group_indexes[iGroup] nt_i = len(index_fmri_i) # Merge Images sct.printv('Merge consecutive volumes...', param.verbose) file_data_merge_i = file_data + '_' + str(iGroup) # cmd = fsloutput + 'fslmerge -t ' + file_data_merge_i # for it in range(nt_i): # cmd = cmd + ' ' + file_data + '_T' + str(index_fmri_i[it]).zfill(4) cmd = 'sct_concat_data -dim t -o ' + file_data_merge_i + ext_data + ' -i ' for it in range(nt_i): cmd = cmd + file_data + '_T' + str(index_fmri_i[it]).zfill(4) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # Average Images sct.printv('Average volumes...', param.verbose) file_data_mean = file_data + '_mean_' + str(iGroup) sct.run('sct_maths -i '+file_data_merge_i+'.nii -o '+file_data_mean+'.nii -mean t') # if not average_data_across_dimension(file_data_merge_i+'.nii', file_data_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_data_merge_i + ' -Tmean ' + file_data_mean # sct.run(cmd, param.verbose) # Merge groups means sct.printv('\nMerging volumes...', param.verbose) file_data_groups_means_merge = 'fmri_averaged_groups' # cmd = fsloutput + 'fslmerge -t ' + file_data_groups_means_merge # for iGroup in range(nb_groups): # cmd = cmd + ' ' + file_data + '_mean_' + str(iGroup) cmd = 'sct_concat_data -dim t -o ' + file_data_groups_means_merge + ext_data + ' -i ' for iGroup in range(nb_groups): cmd = cmd + file_data + '_mean_' + str(iGroup) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # Estimate moco on dwi groups sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'fmri_averaged_groups' param_moco.file_target = file_data + '_mean_' + str(param.num_target) param_moco.path_out = '' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_groups' moco.moco(param_moco) # create final mat folder sct.create_folder(mat_final) # Copy registration matrices sct.printv('\nCopy transformations...', param.verbose) for iGroup in range(nb_groups): for data in range(len(group_indexes[iGroup])): # if param.slicewise: # for iz in range(nz): # sct.run('cp '+'mat_dwigroups/'+'mat.T'+str(iGroup)+'_Z'+str(iz)+ext_mat+' '+mat_final+'mat.T'+str(group_indexes[iGroup][dwi])+'_Z'+str(iz)+ext_mat, param.verbose) # else: sct.run('cp '+'mat_groups/'+'mat.T'+str(iGroup)+ext_mat+' '+mat_final+'mat.T'+str(group_indexes[iGroup][data])+ext_mat, param.verbose) # Apply moco on all fmri data sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'fmri' param_moco.file_target = file_data+'_mean_'+str(0) param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". copy_header('fmri.nii', 'fmri_moco.nii') # Average volumes sct.printv('\nAveraging data...', param.verbose) sct.run('sct_maths -i fmri_moco.nii -o fmri_moco_mean.nii -mean t')
def moco(param): # retrieve parameters fsloutput = "export FSLOUTPUTTYPE=NIFTI; " # for faster processing, all outputs are in NIFTI file_data = param.file_data file_target = param.file_target folder_mat = sct.slash_at_the_end(param.mat_moco, 1) # output folder of mat file todo = param.todo suffix = param.suffix # file_schedule = param.file_schedule verbose = param.verbose ext = ".nii" # get path of the toolbox status, path_sct = commands.getstatusoutput("echo $SCT_DIR") # print arguments sct.printv("\nInput parameters:", param.verbose) sct.printv(" Input file ............" + file_data, param.verbose) sct.printv(" Reference file ........" + file_target, param.verbose) sct.printv(" Polynomial degree ....." + param.param[0], param.verbose) sct.printv(" Smoothing kernel ......" + param.param[1], param.verbose) sct.printv(" Gradient step ........." + param.param[2], param.verbose) sct.printv(" Metric ................" + param.param[3], param.verbose) sct.printv(" Todo .................." + todo, param.verbose) sct.printv(" Mask ................." + param.fname_mask, param.verbose) sct.printv(" Output mat folder ....." + folder_mat, param.verbose) # create folder for mat files sct.create_folder(folder_mat) # Get size of data sct.printv("\nGet dimensions data...", verbose) data_im = Image(file_data + ext) nx, ny, nz, nt, px, py, pz, pt = data_im.dim sct.printv((".. " + str(nx) + " x " + str(ny) + " x " + str(nz) + " x " + str(nt)), verbose) # copy file_target to a temporary file sct.printv("\nCopy file_target to a temporary file...", verbose) sct.run("cp " + file_target + ext + " target.nii") file_target = "target" # Split data along T dimension sct.printv("\nSplit data along T dimension...", verbose) data_split_list = split_data(data_im, dim=3) for im in data_split_list: im.save() file_data_splitT = file_data + "_T" # Motion correction: initialization index = np.arange(nt) file_data_splitT_num = [] file_data_splitT_moco_num = [] failed_transfo = [0 for i in range(nt)] file_mat = [[] for i in range(nt)] # Motion correction: Loop across T for indice_index in range(nt): # create indices and display stuff it = index[indice_index] file_data_splitT_num.append(file_data_splitT + str(it).zfill(4)) file_data_splitT_moco_num.append(file_data + suffix + "_T" + str(it).zfill(4)) sct.printv(("\nVolume " + str((it)) + "/" + str(nt - 1) + ":"), verbose) file_mat[it] = folder_mat + "mat.T" + str(it) # run 3D registration failed_transfo[it] = register( param, file_data_splitT_num[it], file_target, file_mat[it], file_data_splitT_moco_num[it] ) # average registered volume with target image # N.B. use weighted averaging: (target * nb_it + moco) / (nb_it + 1) if param.iterative_averaging and indice_index < 10 and failed_transfo[it] == 0: sct.run("sct_maths -i " + file_target + ext + " -mul " + str(indice_index + 1) + " -o " + file_target + ext) sct.run( "sct_maths -i " + file_target + ext + " -add " + file_data_splitT_moco_num[it] + ext + " -o " + file_target + ext ) sct.run("sct_maths -i " + file_target + ext + " -div " + str(indice_index + 2) + " -o " + file_target + ext) # Replace failed transformation with the closest good one sct.printv(("\nReplace failed transformations..."), verbose) fT = [i for i, j in enumerate(failed_transfo) if j == 1] gT = [i for i, j in enumerate(failed_transfo) if j == 0] for it in range(len(fT)): abs_dist = [abs(gT[i] - fT[it]) for i in range(len(gT))] if not abs_dist == []: index_good = abs_dist.index(min(abs_dist)) sct.printv(" transfo #" + str(fT[it]) + " --> use transfo #" + str(gT[index_good]), verbose) # copy transformation sct.run("cp " + file_mat[gT[index_good]] + "Warp.nii.gz" + " " + file_mat[fT[it]] + "Warp.nii.gz") # apply transformation sct.run( "sct_apply_transfo -i " + file_data_splitT_num[fT[it]] + ".nii -d " + file_target + ".nii -w " + file_mat[fT[it]] + "Warp.nii.gz" + " -o " + file_data_splitT_moco_num[fT[it]] + ".nii" + " -x " + param.interp, verbose, ) else: # exit program if no transformation exists. sct.printv( "\nERROR in " + os.path.basename(__file__) + ": No good transformation exist. Exit program.\n", verbose, "error", ) sys.exit(2) # Merge data along T file_data_moco = file_data + suffix if todo != "estimate": sct.printv("\nMerge data back along T...", verbose) # cmd = fsloutput + 'fslmerge -t ' + file_data_moco # for indice_index in range(len(index)): # cmd = cmd + ' ' + file_data_splitT_moco_num[indice_index] from sct_image import concat_data im_list = [] for indice_index in range(len(index)): im_list.append(Image(file_data_splitT_moco_num[indice_index] + ext)) im_out = concat_data(im_list, 3) im_out.setFileName(file_data_moco + ext) im_out.save()
def register_images( im_input, im_dest, mask="", paramreg=Paramreg( step="0", type="im", algo="Translation", metric="MI", iter="5", shrink="1", smooth="0", gradStep="0.5" ), ants_registration_params={ "rigid": "", "affine": "", "compositeaffine": "", "similarity": "", "translation": "", "bspline": ",10", "gaussiandisplacementfield": ",3,0", "bsplinedisplacementfield": ",5,10", "syn": ",3,0", "bsplinesyn": ",1,3", }, remove_tmp_folder=1, ): """Slice-by-slice registration of two images. We first split the 3D images into 2D images (and the mask if inputted). Then we register slices of the two images that physically correspond to one another looking at the physical origin of each image. The images can be of different sizes but the destination image must be smaller thant the input image. We do that using antsRegistration in 2D. Once this has been done for each slices, we gather the results and return them. Algorithms implemented: translation, rigid, affine, syn and BsplineSyn. N.B.: If the mask is inputted, it must also be 3D and it must be in the same space as the destination image. input: im_input: name of moving image (type: string) im_dest: name of fixed image (type: string) mask[optional]: name of mask file (type: string) (parameter -x of antsRegistration) paramreg[optional]: parameters of antsRegistration (type: Paramreg class from sct_register_multimodal) ants_registration_params[optional]: specific algorithm's parameters for antsRegistration (type: dictionary) output: if algo==translation: x_displacement: list of translation along x axis for each slice (type: list) y_displacement: list of translation along y axis for each slice (type: list) if algo==rigid: x_displacement: list of translation along x axis for each slice (type: list) y_displacement: list of translation along y axis for each slice (type: list) theta_rotation: list of rotation angle in radian (and in ITK's coordinate system) for each slice (type: list) if algo==affine or algo==syn or algo==bsplinesyn: creation of two 3D warping fields (forward and inverse) that are the concatenations of the slice-by-slice warps. """ # Extracting names path_i, root_i, ext_i = sct.extract_fname(im_input) path_d, root_d, ext_d = sct.extract_fname(im_dest) # set metricSize if paramreg.metric == "MI": metricSize = "32" # corresponds to number of bins else: metricSize = "4" # corresponds to radius (for CC, MeanSquares...) # Get image dimensions and retrieve nz print "\nGet image dimensions of destination image..." nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(im_dest) print ".. matrix size: " + str(nx) + " x " + str(ny) + " x " + str(nz) print ".. voxel size: " + str(px) + "mm x " + str(py) + "mm x " + str(pz) + "mm" # Define x and y displacement as list x_displacement = [0 for i in range(nz)] y_displacement = [0 for i in range(nz)] theta_rotation = [0 for i in range(nz)] # create temporary folder print ("\nCreate temporary folder...") path_tmp = "tmp." + time.strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print "\nCopy input data..." sct.run("cp " + im_input + " " + path_tmp + "/" + root_i + ext_i) sct.run("cp " + im_dest + " " + path_tmp + "/" + root_d + ext_d) if mask: sct.run("cp " + mask + " " + path_tmp + "/mask.nii.gz") # go to temporary folder os.chdir(path_tmp) # Split input volume along z print "\nSplit input volume..." sct.run(sct.fsloutput + "fslsplit " + im_input + " " + root_i + "_z -z") # Split destination volume along z print "\nSplit destination volume..." sct.run(sct.fsloutput + "fslsplit " + im_dest + " " + root_d + "_z -z") # Split mask volume along z if mask: print "\nSplit mask volume..." sct.run(sct.fsloutput + "fslsplit mask.nii.gz mask_z -z") im_dest_img = Image(im_dest) im_input_img = Image(im_input) coord_origin_dest = im_dest_img.transfo_pix2phys([[0, 0, 0]]) coord_origin_input = im_input_img.transfo_pix2phys([[0, 0, 0]]) coord_diff_origin = (asarray(coord_origin_dest[0]) - asarray(coord_origin_input[0])).tolist() [x_o, y_o, z_o] = [ coord_diff_origin[0] * 1.0 / px, coord_diff_origin[1] * 1.0 / py, coord_diff_origin[2] * 1.0 / pz, ] if paramreg.algo == "BSplineSyN" or paramreg.algo == "SyN" or paramreg.algo == "Affine": list_warp_x = [] list_warp_x_inv = [] list_warp_y = [] list_warp_y_inv = [] name_warp_final = ( "Warp_total" ) # if modified, name should also be modified in msct_register (algo slicereg2d_bsplinesyn and slicereg2d_syn) # loop across slices for i in range(nz): # set masking num = numerotation(i) num_2 = numerotation(int(num) + int(z_o)) if mask: masking = "-x mask_z" + num + ".nii" else: masking = "" cmd = ( "isct_antsRegistration " "--dimensionality 2 " "--transform " + paramreg.algo + "[" + str(paramreg.gradStep) + ants_registration_params[paramreg.algo.lower()] + "] " "--metric " + paramreg.metric + "[" + root_d + "_z" + num + ".nii" + "," + root_i + "_z" + num_2 + ".nii" + ",1," + metricSize + "] " # [fixedImage,movingImage,metricWeight +nb_of_bins (MI) or radius (other) "--convergence " + str(paramreg.iter) + " " "--shrink-factors " + str(paramreg.shrink) + " " "--smoothing-sigmas " + str(paramreg.smooth) + "mm " #'--restrict-deformation 1x1x0 ' # how to restrict? should not restrict here, if transform is precised...? "--output [transform_" + num + "," + root_i + "_z" + num_2 + "reg.nii] " # --> file.mat (contains Tx,Ty, theta) "--interpolation BSpline[3] " + masking ) try: sct.run(cmd) if paramreg.algo == "Rigid" or paramreg.algo == "Translation": f = "transform_" + num + "0GenericAffine.mat" matfile = loadmat(f, struct_as_record=True) array_transfo = matfile["AffineTransform_double_2_2"] x_displacement[i] = array_transfo[4][0] # Tx in ITK'S coordinate system y_displacement[i] = array_transfo[5][0] # Ty in ITK'S and fslview's coordinate systems theta_rotation[i] = asin( array_transfo[2] ) # angle of rotation theta in ITK'S coordinate system (minus theta for fslview) if paramreg.algo == "Affine": # New process added for generating total nifti warping field from mat warp name_dest = root_d + "_z" + num + ".nii" name_reg = root_i + "_z" + num + "reg.nii" name_output_warp = "warp_from_mat_" + num_2 + ".nii.gz" name_output_warp_inverse = "warp_from_mat_" + num + "_inverse.nii.gz" name_warp_null = "warp_null_" + num + ".nii.gz" name_warp_null_dest = "warp_null_dest" + num + ".nii.gz" name_warp_mat = "transform_" + num + "0GenericAffine.mat" # Generating null nifti warping fields nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(name_reg) nx_d, ny_d, nz_d, nt_d, px_d, py_d, pz_d, pt_d = sct.get_dimension(name_dest) x_trans = [0 for i in range(nz)] x_trans_d = [0 for i in range(nz_d)] y_trans = [0 for i in range(nz)] y_trans_d = [0 for i in range(nz_d)] generate_warping_field(name_reg, x_trans=x_trans, y_trans=y_trans, fname=name_warp_null, verbose=0) generate_warping_field( name_dest, x_trans=x_trans_d, y_trans=y_trans_d, fname=name_warp_null_dest, verbose=0 ) # Concatenating mat wrp and null nifti warp to obtain equivalent nifti warp to mat warp sct.run( "isct_ComposeMultiTransform 2 " + name_output_warp + " -R " + name_reg + " " + name_warp_null + " " + name_warp_mat ) sct.run( "isct_ComposeMultiTransform 2 " + name_output_warp_inverse + " -R " + name_dest + " " + name_warp_null_dest + " -i " + name_warp_mat ) # Split the warping fields into two for displacement along x and y before merge sct.run( "isct_c3d -mcs " + name_output_warp + " -oo transform_" + num + "0Warp_x.nii.gz transform_" + num + "0Warp_y.nii.gz" ) sct.run( "isct_c3d -mcs " + name_output_warp_inverse + " -oo transform_" + num + "0InverseWarp_x.nii.gz transform_" + num + "0InverseWarp_y.nii.gz" ) # List names of warping fields for futur merge list_warp_x.append("transform_" + num + "0Warp_x.nii.gz") list_warp_x_inv.append("transform_" + num + "0InverseWarp_x.nii.gz") list_warp_y.append("transform_" + num + "0Warp_y.nii.gz") list_warp_y_inv.append("transform_" + num + "0InverseWarp_y.nii.gz") if paramreg.algo == "BSplineSyN" or paramreg.algo == "SyN": # Split the warping fields into two for displacement along x and y before merge # Need to separate the merge for x and y displacement as merge of 3d warping fields does not work properly sct.run( "isct_c3d -mcs transform_" + num + "0Warp.nii.gz -oo transform_" + num + "0Warp_x.nii.gz transform_" + num + "0Warp_y.nii.gz" ) sct.run( "isct_c3d -mcs transform_" + num + "0InverseWarp.nii.gz -oo transform_" + num + "0InverseWarp_x.nii.gz transform_" + num + "0InverseWarp_y.nii.gz" ) # List names of warping fields for futur merge list_warp_x.append("transform_" + num + "0Warp_x.nii.gz") list_warp_x_inv.append("transform_" + num + "0InverseWarp_x.nii.gz") list_warp_y.append("transform_" + num + "0Warp_y.nii.gz") list_warp_y_inv.append("transform_" + num + "0InverseWarp_y.nii.gz") # if an exception occurs with ants, take the last value for the transformation except: if paramreg.algo == "Rigid" or paramreg.algo == "Translation": x_displacement[i] = x_displacement[i - 1] y_displacement[i] = y_displacement[i - 1] theta_rotation[i] = theta_rotation[i - 1] if paramreg.algo == "BSplineSyN" or paramreg.algo == "SyN" or paramreg.algo == "Affine": print "Problem with ants for slice " + str(i) + ". Copy of the last warping field." sct.run("cp transform_" + numerotation(i - 1) + "0Warp.nii.gz transform_" + num + "0Warp.nii.gz") sct.run( "cp transform_" + numerotation(i - 1) + "0InverseWarp.nii.gz transform_" + num + "0InverseWarp.nii.gz" ) # Split the warping fields into two for displacement along x and y before merge sct.run( "isct_c3d -mcs transform_" + num + "0Warp.nii.gz -oo transform_" + num + "0Warp_x.nii.gz transform_" + num + "0Warp_y.nii.gz" ) sct.run( "isct_c3d -mcs transform_" + num + "0InverseWarp.nii.gz -oo transform_" + num + "0InverseWarp_x.nii.gz transform_" + num + "0InverseWarp_y.nii.gz" ) # List names of warping fields for futur merge list_warp_x.append("transform_" + num + "0Warp_x.nii.gz") list_warp_x_inv.append("transform_" + num + "0InverseWarp_x.nii.gz") list_warp_y.append("transform_" + num + "0Warp_y.nii.gz") list_warp_y_inv.append("transform_" + num + "0InverseWarp_y.nii.gz") if paramreg.algo == "BSplineSyN" or paramreg.algo == "SyN" or paramreg.algo == "Affine": print "\nMerge along z of the warping fields..." sct.run("fslmerge -z " + name_warp_final + "_x " + " ".join(list_warp_x)) sct.run("fslmerge -z " + name_warp_final + "_x_inverse " + " ".join(list_warp_x_inv)) sct.run("fslmerge -z " + name_warp_final + "_y " + " ".join(list_warp_y)) sct.run("fslmerge -z " + name_warp_final + "_y_inverse " + " ".join(list_warp_y_inv)) print "\nChange resolution of warping fields to match the resolution of the destination image..." sct.run("fslcpgeom " + im_dest + " " + name_warp_final + "_x.nii.gz") sct.run("fslcpgeom " + im_input + " " + name_warp_final + "_x_inverse.nii.gz") sct.run("fslcpgeom " + im_dest + " " + name_warp_final + "_y.nii.gz") sct.run("fslcpgeom " + im_input + " " + name_warp_final + "_y_inverse.nii.gz") print "\nMerge translation fields along x and y into one global warping field " sct.run( "isct_c3d " + name_warp_final + "_x.nii.gz " + name_warp_final + "_y.nii.gz -omc 2 " + name_warp_final + ".nii.gz" ) sct.run( "isct_c3d " + name_warp_final + "_x_inverse.nii.gz " + name_warp_final + "_y_inverse.nii.gz -omc 2 " + name_warp_final + "_inverse.nii.gz" ) print "\nCopy to parent folder..." sct.run("cp " + name_warp_final + ".nii.gz ../") sct.run("cp " + name_warp_final + "_inverse.nii.gz ../") # Delete tmp folder os.chdir("../") if remove_tmp_folder: print ("\nRemove temporary files...") sct.run("rm -rf " + path_tmp) if paramreg.algo == "Rigid": return x_displacement, y_displacement, theta_rotation if paramreg.algo == "Translation": return x_displacement, y_displacement
def fmri_moco(param): file_data = "fmri.nii" mat_final = 'mat_final/' ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) im_data = Image(param.fname_data) nx, ny, nz, nt, px, py, pz, pt = im_data.dim sct.printv( ' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), param.verbose) # Get orientation sct.printv('\nData orientation: ' + im_data.orientation, param.verbose) if im_data.orientation[2] in 'LR': param.is_sagittal = True sct.printv(' Treated as sagittal') elif im_data.orientation[2] in 'IS': param.is_sagittal = False sct.printv(' Treated as axial') else: param.is_sagittal = False sct.printv( 'WARNING: Orientation seems to be neither axial nor sagittal.') # Adjust group size in case of sagittal scan if param.is_sagittal and param.group_size != 1: sct.printv( 'For sagittal data group_size should be one for more robustness. Forcing group_size=1.', 1, 'warning') param.group_size = 1 # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) im_data_split_list = split_data(im_data, 3) for im in im_data_split_list: x_dirname, x_basename, x_ext = sct.extract_fname(im.absolutepath) # Make further steps slurp the data to avoid too many open files (#2149) im.absolutepath = os.path.join(x_dirname, x_basename + ".nii.gz") im.save() # assign an index to each volume index_fmri = list(range(0, nt)) # Number of groups nb_groups = int(math.floor(nt / param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_fmri[(iGroup * param.group_size):((iGroup + 1) * param.group_size)]) # add the remaining images to the last fMRI group nb_remaining = nt % param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_fmri[len(index_fmri) - nb_remaining:len(index_fmri)]) # groups for iGroup in tqdm(range(nb_groups), unit='iter', unit_scale=False, desc="Merge within groups", ascii=True, ncols=80): # get index index_fmri_i = group_indexes[iGroup] nt_i = len(index_fmri_i) # Merge Images file_data_merge_i = sct.add_suffix(file_data, '_' + str(iGroup)) # cmd = fsloutput + 'fslmerge -t ' + file_data_merge_i # for it in range(nt_i): # cmd = cmd + ' ' + file_data + '_T' + str(index_fmri_i[it]).zfill(4) im_fmri_list = [] for it in range(nt_i): im_fmri_list.append(im_data_split_list[index_fmri_i[it]]) im_fmri_concat = concat_data(im_fmri_list, 3, squeeze_data=True).save(file_data_merge_i) file_data_mean = sct.add_suffix(file_data, '_mean_' + str(iGroup)) if file_data_mean.endswith(".nii"): file_data_mean += ".gz" # #2149 if param.group_size == 1: # copy to new file name instead of averaging (faster) # note: this is a bandage. Ideally we should skip this entire for loop if g=1 convert(file_data_merge_i, file_data_mean) else: # Average Images sct.run([ 'sct_maths', '-i', file_data_merge_i, '-o', file_data_mean, '-mean', 't' ], verbose=0) # if not average_data_across_dimension(file_data_merge_i+'.nii', file_data_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_data_merge_i + ' -Tmean ' + file_data_mean # sct.run(cmd, param.verbose) # Merge groups means. The output 4D volume will be used for motion correction. sct.printv('\nMerging volumes...', param.verbose) file_data_groups_means_merge = 'fmri_averaged_groups.nii' im_mean_list = [] for iGroup in range(nb_groups): file_data_mean = sct.add_suffix(file_data, '_mean_' + str(iGroup)) if file_data_mean.endswith(".nii"): file_data_mean += ".gz" # #2149 im_mean_list.append(Image(file_data_mean)) im_mean_concat = concat_data(im_mean_list, 3).save(file_data_groups_means_merge) # Estimate moco sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion...', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'fmri_averaged_groups.nii' param_moco.file_target = sct.add_suffix(file_data, '_mean_' + param.num_target) if param_moco.file_target.endswith(".nii"): param_moco.file_target += ".gz" # #2149 param_moco.path_out = '' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_groups' file_mat = moco.moco(param_moco) # TODO: if g=1, no need to run the block below (already applied) if param.group_size == 1: # if flag g=1, it means that all images have already been corrected, so we just need to rename the file sct.mv('fmri_averaged_groups_moco.nii', 'fmri_moco.nii') else: # create final mat folder sct.create_folder(mat_final) # Copy registration matrices sct.printv('\nCopy transformations...', param.verbose) for iGroup in range(nb_groups): for data in range( len(group_indexes[iGroup]) ): # we cannot use enumerate because group_indexes has 2 dim. # fetch all file_mat_z for given t-group list_file_mat_z = file_mat[:, iGroup] # loop across file_mat_z and copy to mat_final folder for file_mat_z in list_file_mat_z: # we want to copy 'mat_groups/mat.ZXXXXTYYYYWarp.nii.gz' --> 'mat_final/mat.ZXXXXTYYYZWarp.nii.gz' # Notice the Y->Z in the under the T index: the idea here is to use the single matrix from each group, # and apply it to all images belonging to the same group. sct.copy( file_mat_z + ext_mat, mat_final + file_mat_z[11:20] + 'T' + str(group_indexes[iGroup][data]).zfill(4) + ext_mat) # Apply moco on all fmri data sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'fmri.nii' param_moco.file_target = sct.add_suffix(file_data, '_mean_' + str(0)) if param_moco.file_target.endswith(".nii"): param_moco.file_target += ".gz" param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' file_mat = moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". im_fmri = Image('fmri.nii') im_fmri_moco = Image('fmri_moco.nii') im_fmri_moco.header = im_fmri.header im_fmri_moco.save() # Extract and output the motion parameters if param.output_motion_param: from sct_image import multicomponent_split import csv #files_warp = [] files_warp_X, files_warp_Y = [], [] moco_param = [] for fname_warp in file_mat[0]: # Cropping the image to keep only one voxel in the XY plane im_warp = Image(fname_warp + ext_mat) im_warp.data = np.expand_dims(np.expand_dims( im_warp.data[0, 0, :, :, :], axis=0), axis=0) # These three lines allow to generate one file instead of two, containing X, Y and Z moco parameters #fname_warp_crop = fname_warp + '_crop_' + ext_mat #files_warp.append(fname_warp_crop) #im_warp.save(fname_warp_crop) # Separating the three components and saving X and Y only (Z is equal to 0 by default). im_warp_XYZ = multicomponent_split(im_warp) fname_warp_crop_X = fname_warp + '_crop_X_' + ext_mat im_warp_XYZ[0].save(fname_warp_crop_X) files_warp_X.append(fname_warp_crop_X) fname_warp_crop_Y = fname_warp + '_crop_Y_' + ext_mat im_warp_XYZ[1].save(fname_warp_crop_Y) files_warp_Y.append(fname_warp_crop_Y) # Calculating the slice-wise average moco estimate to provide a QC file moco_param.append([ np.mean(np.ravel(im_warp_XYZ[0].data)), np.mean(np.ravel(im_warp_XYZ[1].data)) ]) # These two lines allow to generate one file instead of two, containing X, Y and Z moco parameters #im_warp_concat = concat_data(files_warp, dim=3) #im_warp_concat.save('fmri_moco_params.nii') # Concatenating the moco parameters into a time series for X and Y components. im_warp_concat = concat_data(files_warp_X, dim=3) im_warp_concat.save('fmri_moco_params_X.nii') im_warp_concat = concat_data(files_warp_Y, dim=3) im_warp_concat.save('fmri_moco_params_Y.nii') # Writing a TSV file with the slicewise average estimate of the moco parameters, as it is a useful QC file. with open('fmri_moco_params.tsv', 'wt') as out_file: tsv_writer = csv.writer(out_file, delimiter='\t') tsv_writer.writerow(['X', 'Y']) for mocop in moco_param: tsv_writer.writerow([mocop[0], mocop[1]]) # Average volumes sct.printv('\nAveraging data...', param.verbose) sct_maths.main(args=[ '-i', 'fmri_moco.nii', '-o', 'fmri_moco_mean.nii', '-mean', 't', '-v', '0' ])
def segmentation(fname_input, output_dir, image_type): # parameters path_in, file_in, ext_in = sct.extract_fname(fname_input) segmentation_filename_old = path_in + "old/" + file_in + "_seg" + ext_in manual_segmentation_filename_old = path_in + "manual_" + file_in + ext_in detection_filename_old = path_in + "old/" + file_in + "_detection" + ext_in segmentation_filename_new = path_in + "new/" + file_in + "_seg" + ext_in manual_segmentation_filename_new = path_in + "manual_" + file_in + ext_in detection_filename_new = path_in + "new/" + file_in + "_detection" + ext_in # initialize results of segmentation and detection results_detection = [0, 0] results_segmentation = [0.0, 0.0] # perform PropSeg old version sct.run("rm -rf " + output_dir + "old") sct.create_folder(output_dir + "old") cmd = "sct_propseg_old -i " + fname_input + " -o " + output_dir + "old" + " -t " + image_type + " -detect-nii" sct.printv(cmd) status_propseg_old, output_propseg_old = commands.getstatusoutput(cmd) sct.printv(output_propseg_old) # check if spinal cord is correctly detected with old version of PropSeg cmd = "isct_check_detection.py -i " + detection_filename_old + " -t " + manual_segmentation_filename_old sct.printv(cmd) status_detection_old, output_detection_old = commands.getstatusoutput(cmd) sct.printv(output_detection_old) results_detection[0] = status_detection_old # compute Dice coefficient for old version of PropSeg cmd_validation = ( "sct_dice_coefficient " + segmentation_filename_old + " " + manual_segmentation_filename_old + " -bzmax" ) sct.printv(cmd_validation) status_validation_old, output_validation_old = commands.getstatusoutput(cmd_validation) print output_validation_old res = output_validation_old.split()[-1] if res != "nan": results_segmentation[0] = float(res) else: results_segmentation[0] = 0.0 # perform PropSeg new version sct.run("rm -rf " + output_dir + "new") sct.create_folder(output_dir + "new") cmd = "sct_propseg -i " + fname_input + " -o " + output_dir + "new" + " -t " + image_type + " -detect-nii" sct.printv(cmd) status_propseg_new, output_propseg_new = commands.getstatusoutput(cmd) sct.printv(output_propseg_new) # check if spinal cord is correctly detected with new version of PropSeg cmd = "isct_check_detection.py -i " + detection_filename_new + " -t " + manual_segmentation_filename_new sct.printv(cmd) status_detection_new, output_detection_new = commands.getstatusoutput(cmd) sct.printv(output_detection_new) results_detection[1] = status_detection_new # compute Dice coefficient for new version of PropSeg cmd_validation = ( "sct_dice_coefficient " + segmentation_filename_new + " " + manual_segmentation_filename_new + " -bzmax" ) sct.printv(cmd_validation) status_validation_new, output_validation_new = commands.getstatusoutput(cmd_validation) print output_validation_new res = output_validation_new.split()[-1] if res != "nan": results_segmentation[1] = float(res) else: results_segmentation[1] = 0.0 return results_detection, results_segmentation
def fmri_moco(param): file_data = 'fmri' ext_data = '.nii' mat_final = 'mat_final/' ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) im_data = Image(file_data + '.nii') nx, ny, nz, nt, px, py, pz, pt = im_data.dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), param.verbose) # Get orientation sct.printv('\nData orientation: ' + im_data.orientation, param.verbose) if im_data.orientation[2] in 'LR': param.is_sagittal = True sct.printv(' Treated as sagittal') elif im_data.orientation[2] in 'IS': param.is_sagittal = False sct.printv(' Treated as axial') else: param.is_sagittal = False sct.printv('WARNING: Orientation seems to be neither axial nor sagittal.') # Adjust group size in case of sagittal scan if param.is_sagittal and param.group_size != 1: sct.printv('For sagittal data group_size should be one for more robustness. Forcing group_size=1.', 1, 'warning') param.group_size = 1 # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) im_data = Image(file_data + ext_data) im_data_split_list = split_data(im_data, 3) for im in im_data_split_list: im.save() # assign an index to each volume index_fmri = list(range(0, nt)) # Number of groups nb_groups = int(math.floor(nt / param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_fmri[(iGroup * param.group_size):((iGroup + 1) * param.group_size)]) # add the remaining images to the last fMRI group nb_remaining = nt%param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_fmri[len(index_fmri) - nb_remaining:len(index_fmri)]) # groups for iGroup in tqdm(range(nb_groups), unit='iter', unit_scale=False, desc="Merge within groups", ascii=True, ncols=80): # get index index_fmri_i = group_indexes[iGroup] nt_i = len(index_fmri_i) # Merge Images file_data_merge_i = file_data + '_' + str(iGroup) # cmd = fsloutput + 'fslmerge -t ' + file_data_merge_i # for it in range(nt_i): # cmd = cmd + ' ' + file_data + '_T' + str(index_fmri_i[it]).zfill(4) im_fmri_list = [] for it in range(nt_i): im_fmri_list.append(im_data_split_list[index_fmri_i[it]]) im_fmri_concat = concat_data(im_fmri_list, 3, squeeze_data=True).save(file_data_merge_i + ext_data) file_data_mean = file_data + '_mean_' + str(iGroup) if param.group_size == 1: # copy to new file name instead of averaging (faster) # note: this is a bandage. Ideally we should skip this entire for loop if g=1 sct.copy(file_data_merge_i + '.nii', file_data_mean + '.nii') else: # Average Images sct.run(['sct_maths', '-i', file_data_merge_i + '.nii', '-o', file_data_mean + '.nii', '-mean', 't'], verbose=0) # if not average_data_across_dimension(file_data_merge_i+'.nii', file_data_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_data_merge_i + ' -Tmean ' + file_data_mean # sct.run(cmd, param.verbose) # Merge groups means. The output 4D volume will be used for motion correction. sct.printv('\nMerging volumes...', param.verbose) file_data_groups_means_merge = 'fmri_averaged_groups' im_mean_list = [] for iGroup in range(nb_groups): im_mean_list.append(Image(file_data + '_mean_' + str(iGroup) + ext_data)) im_mean_concat = concat_data(im_mean_list, 3).save(file_data_groups_means_merge + ext_data) # Estimate moco sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'fmri_averaged_groups' param_moco.file_target = file_data + '_mean_' + param.num_target param_moco.path_out = '' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_groups' file_mat = moco.moco(param_moco) # TODO: if g=1, no need to run the block below (already applied) if param.group_size == 1: # if flag g=1, it means that all images have already been corrected, so we just need to rename the file sct.mv('fmri_averaged_groups_moco.nii', 'fmri_moco.nii') else: # create final mat folder sct.create_folder(mat_final) # Copy registration matrices sct.printv('\nCopy transformations...', param.verbose) for iGroup in range(nb_groups): for data in range(len(group_indexes[iGroup])): # we cannot use enumerate because group_indexes has 2 dim. # fetch all file_mat_z for given t-group list_file_mat_z = file_mat[:, iGroup] # loop across file_mat_z and copy to mat_final folder for file_mat_z in list_file_mat_z: # we want to copy 'mat_groups/mat.ZXXXXTYYYYWarp.nii.gz' --> 'mat_final/mat.ZXXXXTYYYZWarp.nii.gz' # Notice the Y->Z in the under the T index: the idea here is to use the single matrix from each group, # and apply it to all images belonging to the same group. sct.copy(file_mat_z + ext_mat, mat_final + file_mat_z[11:20] + 'T' + str(group_indexes[iGroup][data]).zfill(4) + ext_mat) # Apply moco on all fmri data sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'fmri' param_moco.file_target = file_data + '_mean_' + str(0) param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". im_fmri = Image('fmri.nii') im_fmri_moco = Image('fmri_moco.nii') im_fmri_moco.header = im_fmri.header im_fmri_moco.save() # Average volumes sct.printv('\nAveraging data...', param.verbose) sct_maths.main(args=['-i', 'fmri_moco.nii', '-o', 'fmri_moco_mean.nii', '-mean', 't', '-v', '0'])
#!/usr/bin/env python # change type of template data import os import commands import sys from shutil import move # Get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # Append path that contains scripts, to be able to load modules sys.path.append(path_sct + '/scripts') from msct_image import Image import sct_utils as sct path_template = '/Users/julien/data/PAM50/template' folder_PAM50 = 'PAM50/template/' os.chdir(path_template) sct.create_folder(folder_PAM50) for file_template in ['MNI-Poly-AMU_T1.nii.gz', 'MNI-Poly-AMU_T2.nii.gz', 'MNI-Poly-AMU_T2star.nii.gz']: im = Image(file_template) # remove negative values data = im.data data[data<0] = 0 im.data = data im.changeType('uint16') file_new = file_template.replace('MNI-Poly-AMU', 'PAM50') im.setFileName(file_new) im.save() # move to folder move(file_new, folder_PAM50+file_new)
def main(): path_data = param.path_data function_to_test = param.function_to_test # function_to_avoid = param.function_to_avoid remove_tmp_file = param.remove_tmp_file # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:],'h:d:p:f:r:a:') except getopt.GetoptError: usage() for opt, arg in opts: if opt == '-h': usage() sys.exit(0) if opt == '-d': param.download = int(arg) if opt == '-p': param.path_data = arg if opt == '-f': function_to_test = arg # if opt == '-a': # function_to_avoid = arg if opt == '-r': remove_tmp_file = int(arg) start_time = time.time() # if function_to_avoid: # try: # functions.remove(function_to_avoid) # except ValueError: # print 'The function you want to avoid does not figure in the functions to test list' # download data if param.download: downloaddata() param.path_data = 'sct_testing_data/data' # get absolute path and add slash at the end param.path_data = sct.slash_at_the_end(os.path.abspath(param.path_data), 1) # check existence of testing data folder if not sct.check_folder_exist(param.path_data, 0): downloaddata() # display path to data sct.printv('\nPath to testing data:\n.. '+param.path_data, param.verbose) # create temp folder that will have all results and go in it param.path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.create_folder(param.path_tmp) os.chdir(param.path_tmp) # get list of all scripts to test functions = fill_functions() # loop across all functions and test them status = [] [status.append(test_function(f)) for f in functions if function_to_test == f] if not status: for f in functions: status.append(test_function(f)) print 'status: '+str(status) # display elapsed time elapsed_time = time.time() - start_time print 'Finished! Elapsed time: '+str(int(round(elapsed_time)))+'s\n' # remove temp files if param.remove_tmp_file: sct.printv('\nRemove temporary files...', param.verbose) sct.run('rm -rf '+param.path_tmp, param.verbose) e = 0 if sum(status) != 0: e = 1 print e sys.exit(e)
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 status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # 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) identity = int(arguments['-identity']) interp = arguments['-x'] remove_temp_files = int(arguments['-r']) verbose = int(arguments['-v']) # print arguments print '\nInput parameters:' print ' Source .............. '+fname_src print ' Destination ......... '+fname_dest print ' Init transfo ........ '+fname_initwarp print ' Mask ................ '+fname_mask print ' Output name ......... '+fname_output # print ' Algorithm ........... '+paramreg.algo # print ' Number of iterations '+paramreg.iter # print ' Metric .............. '+paramreg.metric print ' Remove temp files ... '+str(remove_temp_files) print ' 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 QC folder sct.create_folder(param.path_qc) # create temporary folder path_tmp = sct.tmp_create() # copy files to temporary folder from sct_convert import convert sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) convert(fname_src, path_tmp+'src.nii') convert(fname_dest, path_tmp+'dest.nii') if fname_src_seg: convert(fname_src_seg, path_tmp+'src_seg.nii') convert(fname_dest_seg, path_tmp+'dest_seg.nii') if fname_src_label: convert(fname_src_label, path_tmp+'src_label.nii') convert(fname_dest_label, path_tmp+'dest_label.nii') if fname_mask != '': convert(fname_mask, path_tmp+'mask.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient destination to RPI sct.run('sct_image -i dest.nii -setorient RPI -o dest_RPI.nii') if fname_dest_seg: sct.run('sct_image -i dest_seg.nii -setorient RPI -o dest_seg_RPI.nii') if fname_dest_label: sct.run('sct_image -i dest_label.nii -setorient RPI -o 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.run('cp '+fname_initwarp+' warp_forward_0.nii.gz', verbose) 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 dest.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 to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) # generate: src_reg fname_src2dest = sct.generate_output_file(path_tmp+'src_reg.nii', path_out+file_out+ext_out, verbose) # generate: forward warping field if fname_output_warp == '': fname_output_warp = path_out+'warp_'+file_src+'2'+file_dest+'.nii.gz' sct.generate_output_file(path_tmp+'warp_src2dest.nii.gz', fname_output_warp, verbose) if generate_warpinv: # generate: dest_reg fname_dest2src = sct.generate_output_file(path_tmp+'dest_reg.nii', path_out+file_out_inv+ext_dest, verbose) # generate: inverse warping field sct.generate_output_file(path_tmp+'warp_dest2src.nii.gz', 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.run('rm -rf '+path_tmp, verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', verbose) sct.printv('\nTo view results, type:', verbose) sct.printv('fslview '+fname_dest+' '+fname_src2dest+' &', verbose, 'info') if generate_warpinv: sct.printv('fslview '+fname_src+' '+fname_dest2src+' &\n', verbose, 'info')
def getRigidTransformFromLandmarks(points_dest, points_src, constraints='Tx_Ty_Tz_Rx_Ry_Rz', verbose=0, path_qc=None): """ Compute affine transformation to register landmarks :param points_src: :param points_dest: :param constraints: :param verbose: 0, 1, 2 :return: rotsc_matrix, translation_array, points_src_reg, points_src_barycenter """ # TODO: check input constraints from scipy.optimize import minimize # initialize default parameters init_param = [0, 0, 0, 0, 0, 0, 1, 1, 1] # initialize parameters for optimizer init_param_optimizer = [] # initialize dictionary to relate constraints index to dof dict_dof = {'Tx': 0, 'Ty': 1, 'Tz': 2, 'Rx': 3, 'Ry': 4, 'Rz': 5, 'Sx': 6, 'Sy': 7, 'Sz': 8} # extract constraints list_constraints = constraints.split('_') # loop across constraints and build initial_parameters for i in range(len(list_constraints)): init_param_optimizer.append(init_param[dict_dof[list_constraints[i]]]) # launch optimizer # res = minimize(minimize_transform, x0=init_param_optimizer, args=(points_src, points_dest, constraints), method='Nelder-Mead', tol=1e-8, options={'xtol': 1e-8, 'ftol': 1e-8, 'maxiter': 10000, 'maxfev': 10000, 'disp': show}) res = minimize(minimize_transform, x0=init_param_optimizer, args=(points_dest, points_src, constraints), method='Powell', tol=1e-8, options={'xtol': 1e-8, 'ftol': 1e-8, 'maxiter': 100000, 'maxfev': 100000, 'disp': verbose}) # res = minimize(minAffineTransform, x0=initial_parameters, args=points, method='COBYLA', tol=1e-8, options={'tol': 1e-8, 'rhobeg': 0.1, 'maxiter': 100000, 'catol': 0, 'disp': show}) # loop across constraints and update dof dof = init_param for i in range(len(list_constraints)): dof[dict_dof[list_constraints[i]]] = res.x[i] # convert dof to more intuitive variables tx, ty, tz, alpha, beta, gamma, scx, scy, scz = dof[0], dof[1], dof[2], dof[3], dof[4], dof[5], dof[6], dof[7], dof[8] # convert results to intuitive variables # tx, ty, tz, alpha, beta, gamma, scx, scy, scz = res.x[0], res.x[1], res.x[2], res.x[3], res.x[4], res.x[5], res.x[6], res.x[7], res.x[8] # build translation matrix translation_array = np.matrix([tx, ty, tz]) # build rotation matrix rotation_matrix = np.matrix([[np.cos(alpha) * np.cos(beta), np.cos(alpha) * np.sin(beta) * np.sin(gamma) - np.sin(alpha) * np.cos(gamma), np.cos(alpha) * np.sin(beta) * np.cos(gamma) + np.sin(alpha) * np.sin(gamma)], [np.sin(alpha) * np.cos(beta), np.sin(alpha) * np.sin(beta) * np.sin(gamma) + np.cos(alpha) * np.cos(gamma), np.sin(alpha) * np.sin(beta) * np.cos(gamma) - np.cos(alpha) * np.sin(gamma)], [-np.sin(beta), np.cos(beta) * np.sin(gamma), np.cos(beta) * np.cos(gamma)]]) # build scaling matrix scaling_matrix = np.matrix([[scx, 0.0, 0.0], [0.0, scy, 0.0], [0.0, 0.0, scz]]) # compute rotation+scaling matrix rotsc_matrix = scaling_matrix * rotation_matrix # compute center of mass from moving points (src) points_src_barycenter = np.mean(points_src, axis=0) # apply transformation to moving points (src) points_src_reg = ((rotsc_matrix * (np.matrix(points_src) - points_src_barycenter).T).T + points_src_barycenter) + translation_array # display results sct.printv('Matrix:\n' + str(rotation_matrix)) sct.printv('Center:\n' + str(points_src_barycenter)) sct.printv('Translation:\n' + str(translation_array)) if verbose == 2 and path_qc is not None: sct.create_folder(path_qc) import matplotlib # use Agg to prevent display matplotlib.use('Agg') import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = fig.gca(projection='3d') points_src_matrix = np.matrix(points_src) points_dest_matrix = np.matrix(points_dest) number_points = len(points_dest) ax.scatter([points_dest_matrix[i, 0] for i in range(0, number_points)], [points_dest_matrix[i, 1] for i in range(0, number_points)], [points_dest_matrix[i, 2] for i in range(0, number_points)], c='g', marker='+', s=500, label='dest') ax.scatter([points_src_matrix[i, 0] for i in range(0, number_points)], [points_src_matrix[i, 1] for i in range(0, number_points)], [points_src_matrix[i, 2] for i in range(0, number_points)], c='r', label='src') ax.scatter([points_src_reg[i, 0] for i in range(0, number_points)], [points_src_reg[i, 1] for i in range(0, number_points)], [points_src_reg[i, 2] for i in range(0, number_points)], c='b', label='src_reg') ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('z') ax.set_aspect('auto') plt.legend() # plt.show() plt.savefig(os.path.join(path_qc, 'getRigidTransformFromLandmarks_plot.png')) fig2 = plt.figure() plt.plot(sse_results) plt.grid() plt.title('#Iterations: ' + str(res.nit) + ', #FuncEval: ' + str(res.nfev) + ', Error: ' + str(res.fun)) plt.show() plt.savefig(os.path.join(path_qc, 'getRigidTransformFromLandmarks_iterations.png')) # transform numpy matrix to list structure because it is easier to handle points_src_reg = points_src_reg.tolist() return rotsc_matrix, translation_array, points_src_reg, points_src_barycenter
def main(path_out, param_user): # initialization start_time = time.time() # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # Parameters for debug mode if param.debug: # get path of the testing data status, path_sct_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') param.fname_data = path_sct_data+'/fmri/fmri.nii.gz' #param.fname_mask = path_sct_data+'/fmri/fmri.nii.gz' param.verbose = 1 param.group_size = 3 #param_user = '******' sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............'+param.fname_data, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, param.verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, path_tmp+'fmri.nii') # sct.run('cp '+param.fname_data+' '+path_tmp+'fmri'+ext_data, param.verbose) # # go to tmp folder os.chdir(path_tmp) # # # convert fmri to nii format # convert('fmri'+ext_data, 'fmri.nii') # run moco fmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) if os.path.isfile(path_tmp+'fmri'+param.suffix+'.nii'): print path_tmp+'fmri'+param.suffix+'.nii' print path_out+file_data+param.suffix+ext_data sct.generate_output_file(path_tmp+'fmri'+param.suffix+'.nii', path_out+file_data+param.suffix+ext_data, param.verbose) sct.generate_output_file(path_tmp+'fmri'+param.suffix+'_mean.nii', path_out+file_data+param.suffix+'_mean'+ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', param.verbose) #To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv('fslview -m ortho,ortho '+param.path_out+file_data+param.suffix+' '+file_data+' &\n', param.verbose, 'info')
def register_images(fname_source, fname_dest, mask='', paramreg=Paramreg(step='0', type='im', algo='Translation', metric='MI', iter='5', shrink='1', smooth='0', gradStep='0.5'), ants_registration_params={'rigid': '', 'affine': '', 'compositeaffine': '', 'similarity': '', 'translation': '','bspline': ',10', 'gaussiandisplacementfield': ',3,0', 'bsplinedisplacementfield': ',5,10', 'syn': ',3,0', 'bsplinesyn': ',1,3'}, remove_tmp_folder = 1): """Slice-by-slice registration of two images. We first split the 3D images into 2D images (and the mask if inputted). Then we register slices of the two images that physically correspond to one another looking at the physical origin of each image. The images can be of different sizes but the destination image must be smaller thant the input image. We do that using antsRegistration in 2D. Once this has been done for each slices, we gather the results and return them. Algorithms implemented: translation, rigid, affine, syn and BsplineSyn. N.B.: If the mask is inputted, it must also be 3D and it must be in the same space as the destination image. input: fname_source: name of moving image (type: string) fname_dest: name of fixed image (type: string) mask[optional]: name of mask file (type: string) (parameter -x of antsRegistration) paramreg[optional]: parameters of antsRegistration (type: Paramreg class from sct_register_multimodal) ants_registration_params[optional]: specific algorithm's parameters for antsRegistration (type: dictionary) output: if algo==translation: x_displacement: list of translation along x axis for each slice (type: list) y_displacement: list of translation along y axis for each slice (type: list) if algo==rigid: x_displacement: list of translation along x axis for each slice (type: list) y_displacement: list of translation along y axis for each slice (type: list) theta_rotation: list of rotation angle in radian (and in ITK's coordinate system) for each slice (type: list) if algo==affine or algo==syn or algo==bsplinesyn: creation of two 3D warping fields (forward and inverse) that are the concatenations of the slice-by-slice warps. """ # Extracting names path_i, root_i, ext_i = sct.extract_fname(fname_source) path_d, root_d, ext_d = sct.extract_fname(fname_dest) # set metricSize if paramreg.metric == 'MI': metricSize = '32' # corresponds to number of bins else: metricSize = '4' # corresponds to radius (for CC, MeanSquares...) # Get image dimensions and retrieve nz print '\nGet image dimensions of destination image...' nx, ny, nz, nt, px, py, pz, pt = Image(fname_dest).dim print '.. matrix size: '+str(nx)+' x '+str(ny)+' x '+str(nz) print '.. voxel size: '+str(px)+'mm x '+str(py)+'mm x '+str(pz)+'mm' # Define x and y displacement as list x_displacement = [0 for i in range(nz)] y_displacement = [0 for i in range(nz)] theta_rotation = [0 for i in range(nz)] # create temporary folder print('\nCreate temporary folder...') path_tmp = 'tmp.'+time.strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print '\nCopy input data...' sct.run('cp '+fname_source+ ' ' + path_tmp +'/'+ root_i+ext_i) sct.run('cp '+fname_dest+ ' ' + path_tmp +'/'+ root_d+ext_d) if mask: sct.run('cp '+mask+ ' '+path_tmp +'/mask.nii.gz') # go to temporary folder os.chdir(path_tmp) # Split input volume along z print '\nSplit input volume...' from sct_split_data import split_data split_data(fname_source, 2, '_z') # Split destination volume along z print '\nSplit destination volume...' split_data(fname_dest, 2, '_z') # Split mask volume along z if mask: print '\nSplit mask volume...' split_data('mask.nii.gz', 2, '_z') im_dest_img = Image(fname_dest) im_input_img = Image(fname_source) coord_origin_dest = im_dest_img.transfo_pix2phys([[0,0,0]]) coord_origin_input = im_input_img.transfo_pix2phys([[0,0,0]]) coord_diff_origin = (asarray(coord_origin_dest[0]) - asarray(coord_origin_input[0])).tolist() [x_o, y_o, z_o] = [coord_diff_origin[0] * 1.0/px, coord_diff_origin[1] * 1.0/py, coord_diff_origin[2] * 1.0/pz] if paramreg.algo == 'BSplineSyN' or paramreg.algo == 'SyN' or paramreg.algo == 'Affine': list_warp_x = [] list_warp_x_inv = [] list_warp_y = [] list_warp_y_inv = [] name_warp_final = 'Warp_total' #if modified, name should also be modified in msct_register (algo slicereg2d_bsplinesyn and slicereg2d_syn) # loop across slices for i in range(nz): # set masking num = numerotation(i) num_2 = numerotation(int(num) + int(z_o)) if mask: masking = '-x mask_z' +num+ '.nii' else: masking = '' cmd = ('isct_antsRegistration ' '--dimensionality 2 ' '--transform '+paramreg.algo+'['+str(paramreg.gradStep) + ants_registration_params[paramreg.algo.lower()]+'] ' '--metric '+paramreg.metric+'['+root_d+'_z'+ num +'.nii' +','+root_i+'_z'+ num_2 +'.nii' +',1,'+metricSize+'] ' #[fixedImage,movingImage,metricWeight +nb_of_bins (MI) or radius (other) '--convergence '+str(paramreg.iter)+' ' '--shrink-factors '+str(paramreg.shrink)+' ' '--smoothing-sigmas '+str(paramreg.smooth)+'mm ' #'--restrict-deformation 1x1x0 ' # how to restrict? should not restrict here, if transform is precised...? '--output [transform_' + num + ','+root_i+'_z'+ num_2 +'reg.nii] ' #--> file.mat (contains Tx,Ty, theta) '--interpolation BSpline[3] ' +masking) try: sct.run(cmd) if paramreg.algo == 'Rigid' or paramreg.algo == 'Translation': f = 'transform_' +num+ '0GenericAffine.mat' matfile = loadmat(f, struct_as_record=True) array_transfo = matfile['AffineTransform_double_2_2'] x_displacement[i] = array_transfo[4][0] # Tx in ITK'S coordinate system y_displacement[i] = array_transfo[5][0] # Ty in ITK'S and fslview's coordinate systems theta_rotation[i] = asin(array_transfo[2]) # angle of rotation theta in ITK'S coordinate system (minus theta for fslview) if paramreg.algo == 'Affine': # New process added for generating total nifti warping field from mat warp name_dest = root_d+'_z'+ num +'.nii' name_reg = root_i+'_z'+ num +'reg.nii' name_output_warp = 'warp_from_mat_' + num_2 + '.nii.gz' name_output_warp_inverse = 'warp_from_mat_' + num + '_inverse.nii.gz' name_warp_null = 'warp_null_' + num + '.nii.gz' name_warp_null_dest = 'warp_null_dest' + num + '.nii.gz' name_warp_mat = 'transform_' + num + '0GenericAffine.mat' # Generating null nifti warping fields nx, ny, nz, nt, px, py, pz, pt = Image(name_reg).dim nx_d, ny_d, nz_d, nt_d, px_d, py_d, pz_d, pt_d = Image(name_dest).dim x_trans = [0 for i in range(nz)] x_trans_d = [0 for i in range(nz_d)] y_trans= [0 for i in range(nz)] y_trans_d = [0 for i in range(nz_d)] generate_warping_field(name_reg, x_trans=x_trans, y_trans=y_trans, fname=name_warp_null, verbose=0) generate_warping_field(name_dest, x_trans=x_trans_d, y_trans=y_trans_d, fname=name_warp_null_dest, verbose=0) # Concatenating mat wrp and null nifti warp to obtain equivalent nifti warp to mat warp sct.run('isct_ComposeMultiTransform 2 ' + name_output_warp + ' -R ' + name_reg + ' ' + name_warp_null + ' ' + name_warp_mat) sct.run('isct_ComposeMultiTransform 2 ' + name_output_warp_inverse + ' -R ' + name_dest + ' ' + name_warp_null_dest + ' -i ' + name_warp_mat) # Split the warping fields into two for displacement along x and y before merge sct.run('isct_c3d -mcs ' + name_output_warp + ' -oo transform_'+num+'0Warp_x.nii.gz transform_'+num+'0Warp_y.nii.gz') sct.run('isct_c3d -mcs ' + name_output_warp_inverse + ' -oo transform_'+num+'0InverseWarp_x.nii.gz transform_'+num+'0InverseWarp_y.nii.gz') # List names of warping fields for futur merge list_warp_x.append('transform_'+num+'0Warp_x.nii.gz') list_warp_x_inv.append('transform_'+num+'0InverseWarp_x.nii.gz') list_warp_y.append('transform_'+num+'0Warp_y.nii.gz') list_warp_y_inv.append('transform_'+num+'0InverseWarp_y.nii.gz') if paramreg.algo == 'BSplineSyN' or paramreg.algo == 'SyN': # Split the warping fields into two for displacement along x and y before merge # Need to separate the merge for x and y displacement as merge of 3d warping fields does not work properly sct.run('isct_c3d -mcs transform_'+num+'0Warp.nii.gz -oo transform_'+num+'0Warp_x.nii.gz transform_'+num+'0Warp_y.nii.gz') sct.run('isct_c3d -mcs transform_'+num+'0InverseWarp.nii.gz -oo transform_'+num+'0InverseWarp_x.nii.gz transform_'+num+'0InverseWarp_y.nii.gz') # List names of warping fields for futur merge list_warp_x.append('transform_'+num+'0Warp_x.nii.gz') list_warp_x_inv.append('transform_'+num+'0InverseWarp_x.nii.gz') list_warp_y.append('transform_'+num+'0Warp_y.nii.gz') list_warp_y_inv.append('transform_'+num+'0InverseWarp_y.nii.gz') # if an exception occurs with ants, take the last value for the transformation except: if paramreg.algo == 'Rigid' or paramreg.algo == 'Translation': x_displacement[i] = x_displacement[i-1] y_displacement[i] = y_displacement[i-1] theta_rotation[i] = theta_rotation[i-1] if paramreg.algo == 'BSplineSyN' or paramreg.algo == 'SyN' or paramreg.algo == 'Affine': print'Problem with ants for slice '+str(i)+'. Copy of the last warping field.' sct.run('cp transform_' + numerotation(i-1) + '0Warp.nii.gz transform_' + num + '0Warp.nii.gz') sct.run('cp transform_' + numerotation(i-1) + '0InverseWarp.nii.gz transform_' + num + '0InverseWarp.nii.gz') # Split the warping fields into two for displacement along x and y before merge sct.run('isct_c3d -mcs transform_'+num+'0Warp.nii.gz -oo transform_'+num+'0Warp_x.nii.gz transform_'+num+'0Warp_y.nii.gz') sct.run('isct_c3d -mcs transform_'+num+'0InverseWarp.nii.gz -oo transform_'+num+'0InverseWarp_x.nii.gz transform_'+num+'0InverseWarp_y.nii.gz') # List names of warping fields for futur merge list_warp_x.append('transform_'+num+'0Warp_x.nii.gz') list_warp_x_inv.append('transform_'+num+'0InverseWarp_x.nii.gz') list_warp_y.append('transform_'+num+'0Warp_y.nii.gz') list_warp_y_inv.append('transform_'+num+'0InverseWarp_y.nii.gz') if paramreg.algo == 'BSplineSyN' or paramreg.algo == 'SyN' or paramreg.algo == 'Affine': print'\nMerge along z of the warping fields...' # from sct_concat_data import concat_data sct.run('sct_concat_data -i '+','.join(list_warp_x)+' -o '+name_warp_final+'_x.nii.gz -dim z') sct.run('sct_concat_data -i '+','.join(list_warp_x_inv)+' -o '+name_warp_final+'_x_inverse.nii.gz -dim z') sct.run('sct_concat_data -i '+','.join(list_warp_y)+' -o '+name_warp_final+'_y.nii.gz -dim z') sct.run('sct_concat_data -i '+','.join(list_warp_y_inv)+' -o '+name_warp_final+'_y_inverse.nii.gz -dim z') # concat_data(','.join(list_warp_x), name_warp_final+'_x.nii.gz', 2) # concat_data(','.join(list_warp_x_inv), name_warp_final+'_x_inverse.nii.gz', 2) # concat_data(','.join(list_warp_y), name_warp_final+'_y.nii.gz', 2) # concat_data(','.join(list_warp_y_inv), name_warp_final+'_y_inverse.nii.gz', 2) # sct.run('fslmerge -z ' + name_warp_final + '_x ' + " ".join(list_warp_x)) # sct.run('fslmerge -z ' + name_warp_final + '_x_inverse ' + " ".join(list_warp_x_inv)) # sct.run('fslmerge -z ' + name_warp_final + '_y ' + " ".join(list_warp_y)) # sct.run('fslmerge -z ' + name_warp_final + '_y_inverse ' + " ".join(list_warp_y_inv)) print'\nChange resolution of warping fields to match the resolution of the destination image...' from sct_copy_header import copy_header copy_header(fname_dest, name_warp_final + '_x.nii.gz') copy_header(fname_source, name_warp_final + '_x_inverse.nii.gz') copy_header(fname_dest, name_warp_final + '_y.nii.gz') copy_header(fname_source, name_warp_final + '_y_inverse.nii.gz') print'\nMerge translation fields along x and y into one global warping field ' sct.run('isct_c3d ' + name_warp_final + '_x.nii.gz ' + name_warp_final + '_y.nii.gz -omc 2 ' + name_warp_final + '.nii.gz') sct.run('isct_c3d ' + name_warp_final + '_x_inverse.nii.gz ' + name_warp_final + '_y_inverse.nii.gz -omc 2 ' + name_warp_final + '_inverse.nii.gz') print'\nCopy to parent folder...' sct.run('cp ' + name_warp_final + '.nii.gz ../') sct.run('cp ' + name_warp_final + '_inverse.nii.gz ../') #Delete tmp folder os.chdir('../') if remove_tmp_folder: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp) if paramreg.algo == 'Rigid': return x_displacement, y_displacement, theta_rotation if paramreg.algo == 'Translation': return x_displacement, y_displacement
def main(): parser = get_parser() param = Param() """ Rewrite arguments and set parameters""" arguments = parser.parse(sys.argv[1:]) (fname_data, fname_landmarks, path_output, path_template, contrast_template, ref, remove_temp_files, verbose, init_labels, first_label,nb_slice_to_mean)=rewrite_arguments(arguments) (param, paramreg)=write_paramaters(arguments,param,ref,verbose) if(init_labels): use_viewer_to_define_labels(fname_data,first_label,nb_slice_to_mean) # initialize other parameters # file_template_label = param.file_template_label zsubsample = param.zsubsample template = os.path.basename(os.path.normpath(pth_template)) # smoothing_sigma = param.smoothing_sigma # retrieve template file names from sct_warp_template import get_file_label file_template_vertebral_labeling = get_file_label(path_template+'template/', 'vertebral') file_template = get_file_label(path_template+'template/', contrast_template.upper()+'-weighted') file_template_seg = get_file_label(path_template+'template/', 'spinal cord') """ Start timer""" start_time = time.time() """ Manage file of templates""" (fname_template, fname_template_vertebral_labeling, fname_template_seg)=make_fname_of_templates(file_template,path_template,file_template_vertebral_labeling,file_template_seg) check_do_files_exist(fname_template,fname_template_vertebral_labeling,fname_template_seg,verbose) sct.printv(arguments(verbose, fname_data, fname_landmarks, fname_seg, path_template, remove_temp_files)) """ Create QC folder """ sct.create_folder(param.path_qc) """ Check if data, segmentation and landmarks are in the same space""" (ext_data, path_data, file_data)=check_data_segmentation_landmarks_same_space(fname_data, fname_seg, fname_landmarks,verbose) ''' Check input labels''' labels = check_labels(fname_landmarks) """ Create temporary folder, set temporary file names, copy files into it and go in it """ path_tmp = sct.tmp_create(verbose=verbose) (ftmp_data, ftmp_seg, ftmp_label, ftmp_template, ftmp_template_seg, ftmp_template_label)=set_temporary_files() copy_files_to_temporary_files(verbose, fname_data, path_tmp, ftmp_seg, ftmp_data, fname_seg, fname_landmarks, ftmp_label, fname_template, ftmp_template, fname_template_seg, ftmp_template_seg) os.chdir(path_tmp) ''' Generate labels from template vertebral labeling''' sct.printv('\nGenerate labels from template vertebral labeling', verbose) sct.run('sct_label_utils -i '+fname_template_vertebral_labeling+' -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') ''' Binarize segmentation (in case it has values below 0 caused by manual editing)''' sct.printv('\nBinarize segmentation', verbose) sct.run('sct_maths -i seg.nii.gz -bin 0.5 -o seg.nii.gz') # smooth segmentation (jcohenadad, issue #613) # sct.printv('\nSmooth segmentation...', verbose) # sct.run('sct_maths -i '+ftmp_seg+' -smooth 1.5 -o '+add_suffix(ftmp_seg, '_smooth')) # jcohenadad: updated 2016-06-16: DO NOT smooth the seg anymore. Issue # # sct.run('sct_maths -i '+ftmp_seg+' -smooth 0 -o '+add_suffix(ftmp_seg, '_smooth')) # ftmp_seg = add_suffix(ftmp_seg, '_smooth') # Switch between modes: subject->template or template->subject if ref == 'template': # resample data to 1mm isotropic sct.printv('\nResample data to 1mm isotropic...', verbose) sct.run('sct_resample -i '+ftmp_data+' -mm 1.0x1.0x1.0 -x linear -o '+add_suffix(ftmp_data, '_1mm')) ftmp_data = add_suffix(ftmp_data, '_1mm') sct.run('sct_resample -i '+ftmp_seg+' -mm 1.0x1.0x1.0 -x linear -o '+add_suffix(ftmp_seg, '_1mm')) ftmp_seg = add_suffix(ftmp_seg, '_1mm') # N.B. resampling of labels is more complicated, because they are single-point labels, therefore resampling with neighrest neighbour can make them disappear. Therefore a more clever approach is required. 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) sct.run('sct_image -i '+ftmp_data+' -setorient RPI -o '+add_suffix(ftmp_data, '_rpi')) ftmp_data = add_suffix(ftmp_data, '_rpi') sct.run('sct_image -i '+ftmp_seg+' -setorient RPI -o '+add_suffix(ftmp_seg, '_rpi')) ftmp_seg = add_suffix(ftmp_seg, '_rpi') sct.run('sct_image -i '+ftmp_label+' -setorient RPI -o '+add_suffix(ftmp_label, '_rpi')) ftmp_label = add_suffix(ftmp_label, '_rpi') # get landmarks in native space # crop segmentation # output: segmentation_rpi_crop.nii.gz status_crop, output_crop = sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -bzmax', verbose) ftmp_seg = add_suffix(ftmp_seg, '_crop') cropping_slices = output_crop.split('Dimension 2: ')[1].split('\n')[0].split(' ') # 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) if os.path.isfile('../warp_curve2straight.nii.gz') and os.path.isfile('../warp_straight2curve.nii.gz') and os.path.isfile('../straight_ref.nii.gz'): # if they exist, copy them into current folder sct.printv('WARNING: Straightening was already run previously. Copying warping fields...', verbose, 'warning') shutil.copy('../warp_curve2straight.nii.gz', 'warp_curve2straight.nii.gz') shutil.copy('../warp_straight2curve.nii.gz', 'warp_straight2curve.nii.gz') shutil.copy('../straight_ref.nii.gz', '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: sct.run('sct_straighten_spinalcord -i '+ftmp_seg+' -s '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_straight')+' -qc 0 -r 0 -v '+str(verbose), verbose) # 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.run('sct_concat_transfo -w warp_straight2curve.nii.gz -d '+ftmp_data+' -o warp_straight2curve.nii.gz') # 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 '+ftmp_label) # Dilating the input label so they can be straighten without losing them sct.printv('\nDilating input labels using 3vox ball radius') sct.run('sct_maths -i '+ftmp_label+' -o '+add_suffix(ftmp_label, '_dilate')+' -dilate 3') 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) from msct_register_landmarks import register_landmarks try: register_landmarks(ftmp_label, ftmp_template_label, paramreg.steps['0'].dof, fname_affine='straight2templateAffine.txt', verbose=verbose) 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://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/', verbose=verbose, type='error') # 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(round(np.min(points_straight))), int(round(np.max(points_straight))) sct.run('sct_crop_image -i ' + ftmp_seg + ' -start ' + str(min_point) + ' -end ' + str(max_point) + ' -dim 2 -b 0 -o ' + add_suffix(ftmp_seg, '_black')) ftmp_seg = add_suffix(ftmp_seg, '_black') """ # binarize sct.printv('\nBinarize segmentation...', verbose) sct.run('sct_maths -i '+ftmp_seg+' -bin 0.5 -o '+add_suffix(ftmp_seg, '_bin')) ftmp_seg = add_suffix(ftmp_seg, '_bin') # find min-max of anat2template (for subsequent cropping) zmin_template, zmax_template = find_zmin_zmax(ftmp_seg) # crop template in z-direction (for faster processing) sct.printv('\nCrop data in template space (for faster processing)...', verbose) sct.run('sct_crop_image -i '+ftmp_template+' -o '+add_suffix(ftmp_template, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_template = add_suffix(ftmp_template, '_crop') sct.run('sct_crop_image -i '+ftmp_template_seg+' -o '+add_suffix(ftmp_template_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_template_seg = add_suffix(ftmp_template_seg, '_crop') sct.run('sct_crop_image -i '+ftmp_data+' -o '+add_suffix(ftmp_data, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_data = add_suffix(ftmp_data, '_crop') sct.run('sct_crop_image -i '+ftmp_seg+' -o '+add_suffix(ftmp_seg, '_crop')+' -dim 2 -start '+str(zmin_template)+' -end '+str(zmax_template)) ftmp_seg = add_suffix(ftmp_seg, '_crop') # sub-sample in z-direction 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 step>1, apply warp_forward_concat to the src image to be used if i_step > 1: # sct.run('sct_apply_transfo -i '+src+' -d '+dest+' -w '+','.join(warp_forward)+' -o '+sct.add_suffix(src, '_reg')+' -x '+interp_step, verbose) # 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.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() 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) sct.run('sct_image -i ' + ftmp_data + ' -setorient RPI -o ' + add_suffix(ftmp_data, '_rpi')) ftmp_data = add_suffix(ftmp_data, '_rpi') sct.run('sct_image -i ' + ftmp_seg + ' -setorient RPI -o ' + add_suffix(ftmp_seg, '_rpi')) ftmp_seg = add_suffix(ftmp_seg, '_rpi') sct.run('sct_image -i ' + ftmp_label + ' -setorient RPI -o ' + add_suffix(ftmp_label, '_rpi')) ftmp_label = add_suffix(ftmp_label, '_rpi') # 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 '+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 = 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.setFileName('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) from msct_register_landmarks import register_landmarks 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=param.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://sourceforge.net/p/spinalcordtoolbox/wiki/create_labels/', 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 to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'warp_template2anat.nii.gz', path_output+'warp_template2anat.nii.gz', verbose) sct.generate_output_file(path_tmp+'warp_anat2template.nii.gz', path_output+'warp_anat2template.nii.gz', verbose) sct.generate_output_file(path_tmp+'template2anat.nii.gz', path_output+'template2anat'+ext_data, verbose) sct.generate_output_file(path_tmp+'anat2template.nii.gz', path_output+'anat2template'+ext_data, verbose) if ref == 'template': # copy straightening files in case subsequent SCT functions need them sct.generate_output_file(path_tmp+'warp_curve2straight.nii.gz', path_output+'warp_curve2straight.nii.gz', verbose) sct.generate_output_file(path_tmp+'warp_straight2curve.nii.gz', path_output+'warp_straight2curve.nii.gz', verbose) sct.generate_output_file(path_tmp+'straight_ref.nii.gz', path_output+'straight_ref.nii.gz', verbose) # Delete temporary files if remove_temp_files: sct.printv('\nDelete temporary files...', verbose) sct.run('rm -rf '+path_tmp) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', verbose) # to view results sct.printv('\nTo view results, type:', verbose) sct.printv('fslview '+fname_data+' '+path_output+'template2anat -b 0,4000 &', verbose, 'info') sct.printv('fslview '+fname_template+' -b 0,5000 '+path_output+'anat2template &\n', verbose, 'info')
def getRigidTransformFromLandmarks(points_dest, points_src, constraints='Tx_Ty_Tz_Rx_Ry_Rz', verbose=0, path_qc=None): """ Compute affine transformation to register landmarks :param points_src: :param points_dest: :param constraints: :param verbose: 0, 1, 2 :return: rotsc_matrix, translation_array, points_src_reg, points_src_barycenter """ # TODO: check input constraints from scipy.optimize import minimize # initialize default parameters init_param = [0, 0, 0, 0, 0, 0, 1, 1, 1] # initialize parameters for optimizer init_param_optimizer = [] # initialize dictionary to relate constraints index to dof dict_dof = {'Tx': 0, 'Ty': 1, 'Tz': 2, 'Rx': 3, 'Ry': 4, 'Rz': 5, 'Sx': 6, 'Sy': 7, 'Sz': 8} # extract constraints list_constraints = constraints.split('_') # loop across constraints and build initial_parameters for i in range(len(list_constraints)): init_param_optimizer.append(init_param[dict_dof[list_constraints[i]]]) # launch optimizer # res = minimize(minimize_transform, x0=init_param_optimizer, args=(points_src, points_dest, constraints), method='Nelder-Mead', tol=1e-8, options={'xtol': 1e-8, 'ftol': 1e-8, 'maxiter': 10000, 'maxfev': 10000, 'disp': show}) res = minimize(minimize_transform, x0=init_param_optimizer, args=(points_dest, points_src, constraints), method='Powell', tol=1e-8, options={'xtol': 1e-8, 'ftol': 1e-8, 'maxiter': 100000, 'maxfev': 100000, 'disp': verbose}) # res = minimize(minAffineTransform, x0=initial_parameters, args=points, method='COBYLA', tol=1e-8, options={'tol': 1e-8, 'rhobeg': 0.1, 'maxiter': 100000, 'catol': 0, 'disp': show}) # loop across constraints and update dof dof = init_param for i in range(len(list_constraints)): dof[dict_dof[list_constraints[i]]] = res.x[i] # convert dof to more intuitive variables tx, ty, tz, alpha, beta, gamma, scx, scy, scz = dof[0], dof[1], dof[2], dof[3], dof[4], dof[5], dof[6], dof[7], dof[8] # convert results to intuitive variables # tx, ty, tz, alpha, beta, gamma, scx, scy, scz = res.x[0], res.x[1], res.x[2], res.x[3], res.x[4], res.x[5], res.x[6], res.x[7], res.x[8] # build translation matrix translation_array = np.matrix([tx, ty, tz]) # build rotation matrix rotation_matrix = np.matrix([[np.cos(alpha) * np.cos(beta), np.cos(alpha) * np.sin(beta) * np.sin(gamma) - np.sin(alpha) * np.cos(gamma), np.cos(alpha) * np.sin(beta) * np.cos(gamma) + np.sin(alpha) * np.sin(gamma)], [np.sin(alpha) * np.cos(beta), np.sin(alpha) * np.sin(beta) * np.sin(gamma) + np.cos(alpha) * np.cos(gamma), np.sin(alpha) * np.sin(beta) * np.cos(gamma) - np.cos(alpha) * np.sin(gamma)], [-np.sin(beta), np.cos(beta) * np.sin(gamma), np.cos(beta) * np.cos(gamma)]]) # build scaling matrix scaling_matrix = np.matrix([[scx, 0.0, 0.0], [0.0, scy, 0.0], [0.0, 0.0, scz]]) # compute rotation+scaling matrix rotsc_matrix = scaling_matrix * rotation_matrix # compute center of mass from moving points (src) points_src_barycenter = np.mean(points_src, axis=0) # apply transformation to moving points (src) points_src_reg = ((rotsc_matrix * (np.matrix(points_src) - points_src_barycenter).T).T + points_src_barycenter) + translation_array # display results sct.printv('Matrix:\n' + str(rotation_matrix)) sct.printv('Center:\n' + str(points_src_barycenter)) sct.printv('Translation:\n' + str(translation_array)) if path_qc is not None: sct.create_folder(path_qc) import matplotlib # use Agg to prevent display matplotlib.use('Agg') import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = fig.gca(projection='3d') points_src_matrix = np.matrix(points_src) points_dest_matrix = np.matrix(points_dest) number_points = len(points_dest) ax.scatter([points_dest_matrix[i, 0] for i in range(0, number_points)], [points_dest_matrix[i, 1] for i in range(0, number_points)], [points_dest_matrix[i, 2] for i in range(0, number_points)], c='g', marker='+', s=500, label='dest') ax.scatter([points_src_matrix[i, 0] for i in range(0, number_points)], [points_src_matrix[i, 1] for i in range(0, number_points)], [points_src_matrix[i, 2] for i in range(0, number_points)], c='r', label='src') ax.scatter([points_src_reg[i, 0] for i in range(0, number_points)], [points_src_reg[i, 1] for i in range(0, number_points)], [points_src_reg[i, 2] for i in range(0, number_points)], c='b', label='src_reg') ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('z') ax.set_aspect('auto') plt.legend() # plt.show() plt.savefig(os.path.join(path_qc, 'getRigidTransformFromLandmarks_plot.png')) fig2 = plt.figure() plt.plot(sse_results) plt.grid() plt.title('#Iterations: ' + str(res.nit) + ', #FuncEval: ' + str(res.nfev) + ', Error: ' + str(res.fun)) plt.show() plt.savefig(os.path.join(path_qc, 'getRigidTransformFromLandmarks_iterations.png')) # transform numpy matrix to list structure because it is easier to handle points_src_reg = points_src_reg.tolist() return rotsc_matrix, translation_array, points_src_reg, points_src_barycenter
def main(args=None): # initialization start_time = time.time() path_out = '.' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] param.fname_bvecs = arguments['-bvec'] if '-bval' in arguments: param.fname_bvals = arguments['-bval'] if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-thr' in arguments: param.otsu = arguments['-thr'] if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) param.verbose = int(arguments.get('-v')) sct.init_sct(log_level=param.verbose, update=True) # Update log level # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) path_tmp = sct.tmp_create(basename="dmri_moco", verbose=param.verbose) # names of files in temporary folder mask_name = 'mask' bvecs_fname = 'bvecs.txt' # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, "dmri.nii")) sct.copy(param.fname_bvecs, os.path.join(path_tmp, bvecs_fname), verbose=param.verbose) if param.fname_mask != '': sct.copy(param.fname_mask, os.path.join(path_tmp, mask_name + ext_mask), verbose=param.verbose) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = mask_name + ext_mask # run moco fname_data_moco_tmp = dmri_moco(param) # generate b0_moco_mean and dwi_moco_mean args = ['-i', fname_data_moco_tmp, '-bvec', 'bvecs.txt', '-a', '1', '-v', '0'] if not param.fname_bvals == '': # if bvals file is provided args += ['-bval', param.fname_bvals] fname_b0, fname_b0_mean, fname_dwi, fname_dwi_mean = sct_dmri_separate_b0_and_dwi.main(args=args) # come back os.chdir(curdir) # Generate output files fname_dmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) fname_dmri_moco_b0_mean = sct.add_suffix(fname_dmri_moco, '_b0_mean') fname_dmri_moco_dwi_mean = sct.add_suffix(fname_dmri_moco, '_dwi_mean') sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(fname_data_moco_tmp, fname_dmri_moco, param.verbose) sct.generate_output_file(fname_b0_mean, fname_dmri_moco_b0_mean, param.verbose) sct.generate_output_file(fname_dwi_mean, fname_dmri_moco_dwi_mean, param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_dmri_moco, file_data], mode='ortho,ortho')
def dmri_moco(param): file_data = 'dmri.nii' file_data_dirname, file_data_basename, file_data_ext = sct.extract_fname(file_data) file_b0 = 'b0.nii' file_dwi = 'dwi.nii' ext_data = '.nii.gz' # workaround "too many open files" by slurping the data mat_final = 'mat_final/' file_dwi_group = 'dwi_averaged_groups.nii' ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) im_data = Image(file_data) nx, ny, nz, nt, px, py, pz, pt = im_data.dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), param.verbose) # Identify b=0 and DWI images index_b0, index_dwi, nb_b0, nb_dwi = sct_dmri_separate_b0_and_dwi.identify_b0('bvecs.txt', param.fname_bvals, param.bval_min, param.verbose) # check if dmri and bvecs are the same size if not nb_b0 + nb_dwi == nt: sct.printv('\nERROR in ' + os.path.basename(__file__) + ': Size of data (' + str(nt) + ') and size of bvecs (' + str(nb_b0 + nb_dwi) + ') are not the same. Check your bvecs file.\n', 1, 'error') sys.exit(2) # Prepare NIFTI (mean/groups...) #=================================================================================================================== # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) im_data_split_list = split_data(im_data, 3) for im in im_data_split_list: x_dirname, x_basename, x_ext = sct.extract_fname(im.absolutepath) im.absolutepath = os.path.join(x_dirname, x_basename + ".nii.gz") im.save() # Merge b=0 images sct.printv('\nMerge b=0...', param.verbose) im_b0_list = [] for it in range(nb_b0): im_b0_list.append(im_data_split_list[index_b0[it]]) im_b0_out = concat_data(im_b0_list, 3).save(file_b0) sct.printv((' File created: ' + file_b0), param.verbose) # Average b=0 images sct.printv('\nAverage b=0...', param.verbose) file_b0_mean = sct.add_suffix(file_b0, '_mean') sct.run(['sct_maths', '-i', file_b0, '-o', file_b0_mean, '-mean', 't'], param.verbose) # Number of DWI groups nb_groups = int(math.floor(nb_dwi / param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_dwi[(iGroup * param.group_size):((iGroup + 1) * param.group_size)]) # add the remaining images to the last DWI group nb_remaining = nb_dwi%param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_dwi[len(index_dwi) - nb_remaining:len(index_dwi)]) file_dwi_dirname, file_dwi_basename, file_dwi_ext = sct.extract_fname(file_dwi) # DWI groups file_dwi_mean = [] for iGroup in tqdm(range(nb_groups), unit='iter', unit_scale=False, desc="Merge within groups", ascii=True, ncols=80): # get index index_dwi_i = group_indexes[iGroup] nb_dwi_i = len(index_dwi_i) # Merge DW Images file_dwi_merge_i = os.path.join(file_dwi_dirname, file_dwi_basename + '_' + str(iGroup) + ext_data) im_dwi_list = [] for it in range(nb_dwi_i): im_dwi_list.append(im_data_split_list[index_dwi_i[it]]) im_dwi_out = concat_data(im_dwi_list, 3).save(file_dwi_merge_i) # Average DW Images file_dwi_mean_i = os.path.join(file_dwi_dirname, file_dwi_basename + '_mean_' + str(iGroup) + ext_data) file_dwi_mean.append(file_dwi_mean_i) sct.run(["sct_maths", "-i", file_dwi_merge_i, "-o", file_dwi_mean[iGroup], "-mean", "t"], 0) # Merge DWI groups means sct.printv('\nMerging DW files...', param.verbose) # file_dwi_groups_means_merge = 'dwi_averaged_groups' im_dw_list = [] for iGroup in range(nb_groups): im_dw_list.append(file_dwi_mean[iGroup]) im_dw_out = concat_data(im_dw_list, 3).save(file_dwi_group) # Average DW Images # TODO: USEFULL ??? sct.printv('\nAveraging all DW images...', param.verbose) sct.run(["sct_maths", "-i", file_dwi_group, "-o", file_dwi_group + '_mean' + ext_data, "-mean", "t"], param.verbose) # segment dwi images using otsu algorithm if param.otsu: sct.printv('\nSegment group DWI using OTSU algorithm...', param.verbose) # import module otsu = importlib.import_module('sct_otsu') # get class from module param_otsu = otsu.param() #getattr(otsu, param) param_otsu.fname_data = file_dwi_group param_otsu.threshold = param.otsu param_otsu.file_suffix = '_seg' # run otsu otsu.otsu(param_otsu) file_dwi_group = file_dwi_group + '_seg.nii' # START MOCO #=================================================================================================================== # Estimate moco on b0 groups sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on b=0 images...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'b0.nii' # identify target image if index_dwi[0] != 0: # If first DWI is not the first volume (most common), then there is a least one b=0 image before. In that case # select it as the target image for registration of all b=0 param_moco.file_target = os.path.join(file_data_dirname, file_data_basename + '_T' + str(index_b0[index_dwi[0] - 1]).zfill(4) + ext_data) else: # If first DWI is the first volume, then the target b=0 is the first b=0 from the index_b0. param_moco.file_target = os.path.join(file_data_dirname, file_data_basename + '_T' + str(index_b0[0]).zfill(4) + ext_data) param_moco.path_out = '' param_moco.todo = 'estimate' param_moco.mat_moco = 'mat_b0groups' file_mat_b0 = moco.moco(param_moco) # Estimate moco on dwi groups sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on DW images...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = file_dwi_group param_moco.file_target = file_dwi_mean[0] # target is the first DW image (closest to the first b=0) param_moco.path_out = '' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_dwigroups' file_mat_dwi = moco.moco(param_moco) # create final mat folder sct.create_folder(mat_final) # Copy b=0 registration matrices # TODO: use file_mat_b0 and file_mat_dwi instead of the hardcoding below sct.printv('\nCopy b=0 registration matrices...', param.verbose) for it in range(nb_b0): sct.copy('mat_b0groups/' + 'mat.Z0000T' + str(it).zfill(4) + ext_mat, mat_final + 'mat.Z0000T' + str(index_b0[it]).zfill(4) + ext_mat) # Copy DWI registration matrices sct.printv('\nCopy DWI registration matrices...', param.verbose) for iGroup in range(nb_groups): for dwi in range(len(group_indexes[iGroup])): # we cannot use enumerate because group_indexes has 2 dim. sct.copy('mat_dwigroups/' + 'mat.Z0000T' + str(iGroup).zfill(4) + ext_mat, mat_final + 'mat.Z0000T' + str(group_indexes[iGroup][dwi]).zfill(4) + ext_mat) # Spline Regularization along T if param.spline_fitting: moco.spline(mat_final, nt, nz, param.verbose, np.array(index_b0), param.plot_graph) # combine Eddy Matrices if param.run_eddy: param.mat_2_combine = 'mat_eddy' param.mat_final = mat_final moco.combine_matrix(param) # Apply moco on all dmri data sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = file_data param_moco.file_target = os.path.join(file_dwi_dirname, file_dwi_basename + '_mean_' + str(0) + ext_data) # reference for reslicing into proper coordinate system param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". im_dmri = Image(file_data) fname_data_moco = os.path.join(file_data_dirname, file_data_basename + param.suffix + '.nii') im_dmri_moco = Image(fname_data_moco) im_dmri_moco.header = im_dmri.header im_dmri_moco.save() return os.path.abspath(fname_data_moco)
def moco(param): # retrieve parameters file_data = param.file_data file_target = param.file_target folder_mat = param.mat_moco # output folder of mat file todo = param.todo suffix = param.suffix verbose = param.verbose # other parameters file_mask = 'mask.nii' sct.printv('\nInput parameters:', param.verbose) sct.printv(' Input file ............' + file_data, param.verbose) sct.printv(' Reference file ........' + file_target, param.verbose) sct.printv(' Polynomial degree .....' + param.poly, param.verbose) sct.printv(' Smoothing kernel ......' + param.smooth, param.verbose) sct.printv(' Gradient step .........' + param.gradStep, param.verbose) sct.printv(' Metric ................' + param.metric, param.verbose) sct.printv(' Sampling ..............' + param.sampling, param.verbose) sct.printv(' Todo ..................' + todo, param.verbose) sct.printv(' Mask .................' + param.fname_mask, param.verbose) sct.printv(' Output mat folder .....' + folder_mat, param.verbose) # create folder for mat files sct.create_folder(folder_mat) # Get size of data sct.printv('\nData dimensions:', verbose) im_data = Image(param.file_data) nx, ny, nz, nt, px, py, pz, pt = im_data.dim sct.printv( (' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt)), verbose) # copy file_target to a temporary file sct.printv('\nCopy file_target to a temporary file...', verbose) file_target = "target.nii.gz" convert(param.file_target, file_target) # If scan is sagittal, split src and target along Z (slice) if param.is_sagittal: dim_sag = 2 # TODO: find it # z-split data (time series) im_z_list = split_data(im_data, dim=dim_sag, squeeze_data=False) file_data_splitZ = [] for im_z in im_z_list: im_z.save() file_data_splitZ.append(im_z.absolutepath) # z-split target im_targetz_list = split_data(Image(file_target), dim=dim_sag, squeeze_data=False) file_target_splitZ = [] for im_targetz in im_targetz_list: im_targetz.save() file_target_splitZ.append(im_targetz.absolutepath) # z-split mask (if exists) if not param.fname_mask == '': im_maskz_list = split_data(Image(file_mask), dim=dim_sag, squeeze_data=False) file_mask_splitZ = [] for im_maskz in im_maskz_list: im_maskz.save() file_mask_splitZ.append(im_maskz.absolutepath) # initialize file list for output matrices file_mat = np.empty((nz, nt), dtype=object) # axial orientation else: file_data_splitZ = [file_data] # TODO: make it absolute like above file_target_splitZ = [file_target] # TODO: make it absolute like above # initialize file list for output matrices file_mat = np.empty((1, nt), dtype=object) # deal with mask if not param.fname_mask == '': convert(param.fname_mask, file_mask, squeeze_data=False) im_maskz_list = [Image(file_mask) ] # use a list with single element # Loop across file list, where each file is either a 2D volume (if sagittal) or a 3D volume (otherwise) # file_mat = tuple([[[] for i in range(nt)] for i in range(nz)]) file_data_splitZ_moco = [] sct.printv( '\nRegister. Loop across Z (note: there is only one Z if orientation is axial' ) for file in file_data_splitZ: iz = file_data_splitZ.index(file) # Split data along T dimension # sct.printv('\nSplit data along T dimension.', verbose) im_z = Image(file) list_im_zt = split_data(im_z, dim=3) file_data_splitZ_splitT = [] for im_zt in list_im_zt: im_zt.save(verbose=0) file_data_splitZ_splitT.append(im_zt.absolutepath) # file_data_splitT = file_data + '_T' # Motion correction: initialization index = np.arange(nt) file_data_splitT_num = [] file_data_splitZ_splitT_moco = [] failed_transfo = [0 for i in range(nt)] # Motion correction: Loop across T for indice_index in tqdm(range(nt), unit='iter', unit_scale=False, desc="Z=" + str(iz) + "/" + str(len(file_data_splitZ) - 1), ascii=True, ncols=80): # create indices and display stuff it = index[indice_index] file_mat[iz][it] = os.path.join( folder_mat, "mat.Z") + str(iz).zfill(4) + 'T' + str(it).zfill(4) file_data_splitZ_splitT_moco.append( sct.add_suffix(file_data_splitZ_splitT[it], '_moco')) # deal with masking if not param.fname_mask == '': input_mask = im_maskz_list[iz] else: input_mask = None # run 3D registration failed_transfo[it] = register(param, file_data_splitZ_splitT[it], file_target_splitZ[iz], file_mat[iz][it], file_data_splitZ_splitT_moco[it], im_mask=input_mask) # average registered volume with target image # N.B. use weighted averaging: (target * nb_it + moco) / (nb_it + 1) if param.iterAvg and indice_index < 10 and failed_transfo[ it] == 0 and not param.todo == 'apply': im_targetz = Image(file_target_splitZ[iz]) data_targetz = im_targetz.data data_mocoz = Image(file_data_splitZ_splitT_moco[it]).data data_targetz = (data_targetz * (indice_index + 1) + data_mocoz) / (indice_index + 2) im_targetz.data = data_targetz im_targetz.save(verbose=0) # Replace failed transformation with the closest good one fT = [i for i, j in enumerate(failed_transfo) if j == 1] gT = [i for i, j in enumerate(failed_transfo) if j == 0] for it in range(len(fT)): abs_dist = [np.abs(gT[i] - fT[it]) for i in range(len(gT))] if not abs_dist == []: index_good = abs_dist.index(min(abs_dist)) sct.printv( ' transfo #' + str(fT[it]) + ' --> use transfo #' + str(gT[index_good]), verbose) # copy transformation sct.copy(file_mat[iz][gT[index_good]] + 'Warp.nii.gz', file_mat[iz][fT[it]] + 'Warp.nii.gz') # apply transformation sct_apply_transfo.main(args=[ '-i', file_data_splitZ_splitT[fT[it]], '-d', file_target, '-w', file_mat[iz][fT[it]] + 'Warp.nii.gz', '-o', file_data_splitZ_splitT_moco[fT[it]], '-x', param.interp ]) else: # exit program if no transformation exists. sct.printv( '\nERROR in ' + os.path.basename(__file__) + ': No good transformation exist. Exit program.\n', verbose, 'error') sys.exit(2) # Merge data along T file_data_splitZ_moco.append(sct.add_suffix(file, suffix)) if todo != 'estimate': im_out = concat_data(file_data_splitZ_splitT_moco, 3) im_out.save(file_data_splitZ_moco[iz]) # If sagittal, merge along Z if param.is_sagittal: im_out = concat_data(file_data_splitZ_moco, 2) dirname, basename, ext = sct.extract_fname(file_data) path_out = os.path.join(dirname, basename + suffix + ext) im_out.save(path_out) return file_mat
def main(args=None): # initialization start_time = time.time() param = Param() # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_tmp_files = int(arguments['-r']) if '-v' in arguments: param.verbose = int(arguments['-v']) sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............' + param.fname_data, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.' + time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir ' + path_tmp, param.verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, path_tmp + 'fmri.nii') # go to tmp folder os.chdir(path_tmp) # run moco fmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) if os.path.isfile(path_tmp + 'fmri' + param.suffix + '.nii'): sct.printv(path_tmp + 'fmri' + param.suffix + '.nii') sct.printv(path_out + file_data + param.suffix + ext_data) sct.generate_output_file(path_tmp + 'fmri' + param.suffix + '.nii', path_out + file_data + param.suffix + ext_data, param.verbose) sct.generate_output_file( path_tmp + 'fmri' + param.suffix + '_mean.nii', path_out + file_data + param.suffix + '_mean' + ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf ' + path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', param.verbose) # To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv( 'fslview -m ortho,ortho ' + param.path_out + file_data + param.suffix + ' ' + file_data + ' &\n', param.verbose, 'info')
def main(args=None): # initialization start_time = time.time() path_out = '.' param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] param.fname_bvecs = arguments['-bvec'] if '-bval' in arguments: param.fname_bvals = arguments['-bval'] if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-thr' in arguments: param.otsu = arguments['-thr'] if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) param.verbose = int(arguments.get('-v')) sct.init_sct(log_level=param.verbose, update=True) # Update log level # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) path_tmp = sct.tmp_create(basename="dmri_moco", verbose=param.verbose) # names of files in temporary folder mask_name = 'mask' bvecs_fname = 'bvecs.txt' # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, "dmri.nii")) sct.copy(param.fname_bvecs, os.path.join(path_tmp, bvecs_fname), verbose=param.verbose) if param.fname_mask != '': sct.copy(param.fname_mask, os.path.join(path_tmp, mask_name + ext_mask), verbose=param.verbose) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = mask_name + ext_mask # run moco fname_data_moco_tmp = dmri_moco(param) # generate b0_moco_mean and dwi_moco_mean args = [ '-i', fname_data_moco_tmp, '-bvec', 'bvecs.txt', '-a', '1', '-v', '0' ] if not param.fname_bvals == '': # if bvals file is provided args += ['-bval', param.fname_bvals] fname_b0, fname_b0_mean, fname_dwi, fname_dwi_mean = sct_dmri_separate_b0_and_dwi.main( args=args) # come back os.chdir(curdir) # Generate output files fname_dmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) fname_dmri_moco_b0_mean = sct.add_suffix(fname_dmri_moco, '_b0_mean') fname_dmri_moco_dwi_mean = sct.add_suffix(fname_dmri_moco, '_dwi_mean') sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(fname_data_moco_tmp, fname_dmri_moco, param.verbose) sct.generate_output_file(fname_b0_mean, fname_dmri_moco_b0_mean, param.verbose) sct.generate_output_file(fname_dwi_mean, fname_dmri_moco_dwi_mean, param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_dmri_moco, file_data], mode='ortho,ortho')
def main(args=None): # initialization start_time = time.time() path_out = '.' param = Param() # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # get path of the toolbox # status, param.path_sct = sct.run('echo $SCT_DIR') # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] param.fname_bvecs = arguments['-bvec'] if '-bval' in arguments: param.fname_bvals = arguments['-bval'] if '-bvalmin' in arguments: param.bval_min = arguments['-bvalmin'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-thr' in arguments: param.otsu = arguments['-thr'] if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) if '-v' in arguments: param.verbose = int(arguments['-v']) # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) path_tmp = sct.tmp_create(basename="dmri_moco", verbose=param.verbose) # names of files in temporary folder ext = '.nii' dmri_name = 'dmri' mask_name = 'mask' bvecs_fname = 'bvecs.txt' # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, dmri_name + ext)) sct.copy(param.fname_bvecs, os.path.join(path_tmp, bvecs_fname), verbose=param.verbose) if param.fname_mask != '': sct.copy(param.fname_mask, os.path.join(path_tmp, mask_name + ext_mask), verbose=param.verbose) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = mask_name + ext_mask # run moco dmri_moco(param) # come back os.chdir(curdir) # Generate output files fname_dmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file( os.path.join(path_tmp, dmri_name + param.suffix + ext), os.path.join(path_out, file_data + param.suffix + ext_data), param.verbose) sct.generate_output_file( os.path.join(path_tmp, "b0_mean.nii"), os.path.join(path_out, 'b0' + param.suffix + '_mean' + ext_data), param.verbose) sct.generate_output_file( os.path.join(path_tmp, "dwi_mean.nii"), os.path.join(path_out, 'dwi' + param.suffix + '_mean' + ext_data), param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_dmri_moco, file_data], mode='ortho,ortho')
def moco(param): # retrieve parameters file_data = param.file_data file_target = param.file_target folder_mat = param.mat_moco # output folder of mat file todo = param.todo suffix = param.suffix verbose = param.verbose # other parameters file_mask = 'mask.nii' sct.printv('\nInput parameters:', param.verbose) sct.printv(' Input file ............' + file_data, param.verbose) sct.printv(' Reference file ........' + file_target, param.verbose) sct.printv(' Polynomial degree .....' + param.poly, param.verbose) sct.printv(' Smoothing kernel ......' + param.smooth, param.verbose) sct.printv(' Gradient step .........' + param.gradStep, param.verbose) sct.printv(' Metric ................' + param.metric, param.verbose) sct.printv(' Sampling ..............' + param.sampling, param.verbose) sct.printv(' Todo ..................' + todo, param.verbose) sct.printv(' Mask .................' + param.fname_mask, param.verbose) sct.printv(' Output mat folder .....' + folder_mat, param.verbose) # create folder for mat files sct.create_folder(folder_mat) # Get size of data sct.printv('\nData dimensions:', verbose) im_data = Image(param.file_data) nx, ny, nz, nt, px, py, pz, pt = im_data.dim sct.printv((' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt)), verbose) # copy file_target to a temporary file sct.printv('\nCopy file_target to a temporary file...', verbose) file_target = "target.nii.gz" convert(param.file_target, file_target) # If scan is sagittal, split src and target along Z (slice) if param.is_sagittal: dim_sag = 2 # TODO: find it # z-split data (time series) im_z_list = split_data(im_data, dim=dim_sag, squeeze_data=False) file_data_splitZ = [] for im_z in im_z_list: im_z.save() file_data_splitZ.append(im_z.absolutepath) # z-split target im_targetz_list = split_data(Image(file_target), dim=dim_sag, squeeze_data=False) file_target_splitZ = [] for im_targetz in im_targetz_list: im_targetz.save() file_target_splitZ.append(im_targetz.absolutepath) # z-split mask (if exists) if not param.fname_mask == '': im_maskz_list = split_data(Image(file_mask), dim=dim_sag, squeeze_data=False) file_mask_splitZ = [] for im_maskz in im_maskz_list: im_maskz.save() file_mask_splitZ.append(im_maskz.absolutepath) # initialize file list for output matrices file_mat = np.empty((nz, nt), dtype=object) # axial orientation else: file_data_splitZ = [file_data] # TODO: make it absolute like above file_target_splitZ = [file_target] # TODO: make it absolute like above # initialize file list for output matrices file_mat = np.empty((1, nt), dtype=object) # deal with mask if not param.fname_mask == '': convert(param.fname_mask, file_mask, squeeze_data=False) im_maskz_list = [Image(file_mask)] # use a list with single element # Loop across file list, where each file is either a 2D volume (if sagittal) or a 3D volume (otherwise) # file_mat = tuple([[[] for i in range(nt)] for i in range(nz)]) file_data_splitZ_moco = [] sct.printv('\nRegister. Loop across Z (note: there is only one Z if orientation is axial') for file in file_data_splitZ: iz = file_data_splitZ.index(file) # Split data along T dimension # sct.printv('\nSplit data along T dimension.', verbose) im_z = Image(file) list_im_zt = split_data(im_z, dim=3) file_data_splitZ_splitT = [] for im_zt in list_im_zt: im_zt.save(verbose=0) file_data_splitZ_splitT.append(im_zt.absolutepath) # file_data_splitT = file_data + '_T' # Motion correction: initialization index = np.arange(nt) file_data_splitT_num = [] file_data_splitZ_splitT_moco = [] failed_transfo = [0 for i in range(nt)] # Motion correction: Loop across T for indice_index in tqdm(range(nt), unit='iter', unit_scale=False, desc="Z=" + str(iz) + "/" + str(len(file_data_splitZ)-1), ascii=True, ncols=80): # create indices and display stuff it = index[indice_index] file_mat[iz][it] = os.path.join(folder_mat, "mat.Z") + str(iz).zfill(4) + 'T' + str(it).zfill(4) file_data_splitZ_splitT_moco.append(sct.add_suffix(file_data_splitZ_splitT[it], '_moco')) # deal with masking if not param.fname_mask == '': input_mask = im_maskz_list[iz] else: input_mask = None # run 3D registration failed_transfo[it] = register(param, file_data_splitZ_splitT[it], file_target_splitZ[iz], file_mat[iz][it], file_data_splitZ_splitT_moco[it], im_mask=input_mask) # average registered volume with target image # N.B. use weighted averaging: (target * nb_it + moco) / (nb_it + 1) if param.iterAvg and indice_index < 10 and failed_transfo[it] == 0 and not param.todo == 'apply': im_targetz = Image(file_target_splitZ[iz]) data_targetz = im_targetz.data data_mocoz = Image(file_data_splitZ_splitT_moco[it]).data data_targetz = (data_targetz * (indice_index + 1) + data_mocoz) / (indice_index + 2) im_targetz.data = data_targetz im_targetz.save(verbose=0) # Replace failed transformation with the closest good one fT = [i for i, j in enumerate(failed_transfo) if j == 1] gT = [i for i, j in enumerate(failed_transfo) if j == 0] for it in range(len(fT)): abs_dist = [np.abs(gT[i] - fT[it]) for i in range(len(gT))] if not abs_dist == []: index_good = abs_dist.index(min(abs_dist)) sct.printv(' transfo #' + str(fT[it]) + ' --> use transfo #' + str(gT[index_good]), verbose) # copy transformation sct.copy(file_mat[iz][gT[index_good]] + 'Warp.nii.gz', file_mat[iz][fT[it]] + 'Warp.nii.gz') # apply transformation sct_apply_transfo.main(args=['-i', file_data_splitZ_splitT[fT[it]], '-d', file_target, '-w', file_mat[iz][fT[it]] + 'Warp.nii.gz', '-o', file_data_splitZ_splitT_moco[fT[it]], '-x', param.interp]) else: # exit program if no transformation exists. sct.printv('\nERROR in ' + os.path.basename(__file__) + ': No good transformation exist. Exit program.\n', verbose, 'error') sys.exit(2) # Merge data along T file_data_splitZ_moco.append(sct.add_suffix(file, suffix)) if todo != 'estimate': im_out = concat_data(file_data_splitZ_splitT_moco, 3) im_out.save(file_data_splitZ_moco[iz]) # If sagittal, merge along Z if param.is_sagittal: im_out = concat_data(file_data_splitZ_moco, 2) dirname, basename, ext = sct.extract_fname(file_data) path_out = os.path.join(dirname, basename + suffix + ext) im_out.save(path_out) return file_mat
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 status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # 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) 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 QC folder sct.create_folder(param.path_qc) # create temporary folder path_tmp = sct.tmp_create() # copy files to temporary folder from sct_convert import convert sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) convert(fname_src, path_tmp + 'src.nii') convert(fname_dest, path_tmp + 'dest.nii') if fname_src_seg: convert(fname_src_seg, path_tmp + 'src_seg.nii') convert(fname_dest_seg, path_tmp + 'dest_seg.nii') if fname_src_label: convert(fname_src_label, path_tmp + 'src_label.nii') convert(fname_dest_label, path_tmp + 'dest_label.nii') if fname_mask != '': convert(fname_mask, path_tmp + 'mask.nii.gz') # go to tmp folder os.chdir(path_tmp) # reorient destination to RPI sct.run('sct_image -i dest.nii -setorient RPI -o dest_RPI.nii') if fname_dest_seg: sct.run('sct_image -i dest_seg.nii -setorient RPI -o dest_seg_RPI.nii') if fname_dest_label: sct.run( 'sct_image -i dest_label.nii -setorient RPI -o 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.run('cp '+fname_initwarp+' warp_forward_0.nii.gz', verbose) 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 to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) # generate: src_reg fname_src2dest = sct.generate_output_file(path_tmp + 'src_reg.nii', path_out + file_out + ext_out, verbose) # generate: forward warping field if fname_output_warp == '': fname_output_warp = path_out + 'warp_' + file_src + '2' + file_dest + '.nii.gz' sct.generate_output_file(path_tmp + 'warp_src2dest.nii.gz', fname_output_warp, verbose) if generate_warpinv: # generate: dest_reg fname_dest2src = sct.generate_output_file( path_tmp + 'dest_reg.nii', path_out + file_out_inv + ext_dest, verbose) # generate: inverse warping field sct.generate_output_file( path_tmp + 'warp_dest2src.nii.gz', 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.run('rm -rf ' + path_tmp, verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv( '\nFinished! Elapsed time: ' + str(int(round(elapsed_time))) + 's', verbose) sct.printv('\nTo view results, type:', verbose) if generate_warpinv: sct.printv('fslview ' + fname_src + ' ' + fname_dest2src + ' &', verbose, 'info') sct.printv('fslview ' + fname_dest + ' ' + fname_src2dest + ' &\n', verbose, 'info')
def register_images( im_input, im_dest, mask="", paramreg=Paramreg( step="0", type="im", algo="Translation", metric="MI", iter="5", shrink="1", smooth="0", gradStep="0.5" ), remove_tmp_folder=1, ): path_i, root_i, ext_i = sct.extract_fname(im_input) path_d, root_d, ext_d = sct.extract_fname(im_dest) path_m, root_m, ext_m = sct.extract_fname(mask) # set metricSize if paramreg.metric == "MI": metricSize = "32" # corresponds to number of bins else: metricSize = "4" # corresponds to radius (for CC, MeanSquares...) # initiate default parameters of antsRegistration transformation ants_registration_params = { "rigid": "", "affine": "", "compositeaffine": "", "similarity": "", "translation": "", "bspline": ",10", "gaussiandisplacementfield": ",3,0", "bsplinedisplacementfield": ",5,10", "syn": ",3,0", "bsplinesyn": ",3,32", } # Get image dimensions and retrieve nz print "\nGet image dimensions of destination image..." nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(im_dest) print ".. matrix size: " + str(nx) + " x " + str(ny) + " x " + str(nz) print ".. voxel size: " + str(px) + "mm x " + str(py) + "mm x " + str(pz) + "mm" # Define x and y displacement as list x_displacement = [0 for i in range(nz)] y_displacement = [0 for i in range(nz)] theta_rotation = [0 for i in range(nz)] matrix_def = [0 for i in range(nz)] # create temporary folder print ("\nCreate temporary folder...") path_tmp = "tmp." + time.strftime("%y%m%d%H%M%S") sct.create_folder(path_tmp) print "\nCopy input data..." sct.run("cp " + im_input + " " + path_tmp + "/" + root_i + ext_i) sct.run("cp " + im_dest + " " + path_tmp + "/" + root_d + ext_d) if mask: sct.run("cp " + mask + " " + path_tmp + "/mask.nii.gz") # go to temporary folder os.chdir(path_tmp) # Split input volume along z print "\nSplit input volume..." sct.run(sct.fsloutput + "fslsplit " + im_input + " " + root_i + "_z -z") # file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] # Split destination volume along z print "\nSplit destination volume..." sct.run(sct.fsloutput + "fslsplit " + im_dest + " " + root_d + "_z -z") # file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] # Split mask volume along z if mask: print "\nSplit mask volume..." sct.run(sct.fsloutput + "fslsplit mask.nii.gz mask_z -z") # file_anat_split = ['tmp.anat_orient_z'+str(z).zfill(4) for z in range(0,nz,1)] im_dest_img = Image(im_dest) im_input_img = Image(im_input) coord_origin_dest = im_dest_img.transfo_pix2phys([[0, 0, 0]]) coord_origin_input = im_input_img.transfo_pix2phys([[0, 0, 0]]) coord_diff_origin_z = coord_origin_dest[0][2] - coord_origin_input[0][2] [[x_o, y_o, z_o]] = im_input_img.transfo_phys2pix([[0, 0, coord_diff_origin_z]]) # loop across slices for i in range(nz): # set masking num = numerotation(i) num_2 = numerotation(int(num) + z_o) if mask: masking = "-x mask_z" + num + ".nii" else: masking = "" cmd = ( "isct_antsRegistration " "--dimensionality 2 " "--transform " + paramreg.algo + "[" + paramreg.gradStep + ants_registration_params[paramreg.algo.lower()] + "] " "--metric " + paramreg.metric + "[" + root_d + "_z" + num + ".nii" + "," + root_i + "_z" + num_2 + ".nii" + ",1," + metricSize + "] " # [fixedImage,movingImage,metricWeight +nb_of_bins (MI) or radius (other) "--convergence " + paramreg.iter + " " "--shrink-factors " + paramreg.shrink + " " "--smoothing-sigmas " + paramreg.smooth + "mm " #'--restrict-deformation 1x1x0 ' # how to restrict? should not restrict here, if transform is precised...? "--output [transform_" + num + "] " # --> file.txt (contains Tx,Ty) [outputTransformPrefix,<outputWarpedImage>,<outputInverseWarpedImage>] "--interpolation BSpline[3] " + masking ) try: sct.run(cmd) if paramreg.algo == "Rigid" or paramreg.algo == "Translation": f = "transform_" + num + "0GenericAffine.mat" matfile = loadmat(f, struct_as_record=True) array_transfo = matfile["AffineTransform_double_2_2"] if i == 20 or i == 40: print i x_displacement[i] = -array_transfo[4][0] # is it? or is it y? y_displacement[i] = array_transfo[5][0] theta_rotation[i] = asin(array_transfo[2]) if paramreg.algo == "Affine": f = "transform_" + num + "0GenericAffine.mat" matfile = loadmat(f, struct_as_record=True) array_transfo = matfile["AffineTransform_double_2_2"] x_displacement[i] = -array_transfo[4][0] # is it? or is it y? y_displacement[i] = array_transfo[5][0] matrix_def[i] = [ [array_transfo[0][0], array_transfo[1][0]], [array_transfo[2][0], array_transfo[3][0]], ] # comment savoir lequel est lequel? except: if paramreg.algo == "Rigid" or paramreg.algo == "Translation": x_displacement[i] = x_displacement[i - 1] # is it? or is it y? y_displacement[i] = y_displacement[i - 1] theta_rotation[i] = theta_rotation[i - 1] if paramreg.algo == "Affine": x_displacement[i] = x_displacement[i - 1] y_displacement[i] = y_displacement[i - 1] matrix_def[i] = matrix_def[i - 1] # # get displacement form this slice and complete x and y displacement lists # with open('transform_'+num+'.csv') as f: # reader = csv.reader(f) # count = 0 # for line in reader: # count += 1 # if count == 2: # x_displacement[i] = line[0] # y_displacement[i] = line[1] # f.close() # # get matrix of transfo for a rigid transform (pb slicereg fait une rotation ie le deplacement n'est pas homogene par slice) # # recuperer le deplacement ne donnerait pas une liste mais un warping field: mieux vaut recup la matrice output # # pb du smoothing du deplacement par slice !! on peut smoother les param theta tx ty # if paramreg.algo == 'Rigid' or paramreg.algo == 'Translation': # f = 'transform_' +num+ '0GenericAffine.mat' # matfile = loadmat(f, struct_as_record=True) # array_transfo = matfile['AffineTransform_double_2_2'] # x_displacement[i] = -array_transfo[4][0] #is it? or is it y? # y_displacement[i] = array_transfo[5][0] # theta_rotation[i] = acos(array_transfo[0]) # TO DO: different treatment for other algo # Delete tmp folder os.chdir("../") if remove_tmp_folder: print ("\nRemove temporary files...") sct.run("rm -rf " + path_tmp) if paramreg.algo == "Rigid": return ( x_displacement, y_displacement, theta_rotation, ) # check if the displacement are not inverted (x_dis = -x_disp...) theta is in radian if paramreg.algo == "Translation": return x_displacement, y_displacement if paramreg.algo == "Affine": return x_displacement, y_displacement, matrix_def
def dmri_moco(param): file_data = 'dmri.nii' file_data_dirname, file_data_basename, file_data_ext = sct.extract_fname( file_data) file_b0 = 'b0.nii' file_dwi = 'dwi.nii' ext_data = '.nii.gz' # workaround "too many open files" by slurping the data mat_final = 'mat_final/' file_dwi_group = 'dwi_averaged_groups.nii' ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) im_data = Image(file_data) nx, ny, nz, nt, px, py, pz, pt = im_data.dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), param.verbose) # Identify b=0 and DWI images index_b0, index_dwi, nb_b0, nb_dwi = sct_dmri_separate_b0_and_dwi.identify_b0( 'bvecs.txt', param.fname_bvals, param.bval_min, param.verbose) # check if dmri and bvecs are the same size if not nb_b0 + nb_dwi == nt: sct.printv( '\nERROR in ' + os.path.basename(__file__) + ': Size of data (' + str(nt) + ') and size of bvecs (' + str(nb_b0 + nb_dwi) + ') are not the same. Check your bvecs file.\n', 1, 'error') sys.exit(2) # Prepare NIFTI (mean/groups...) #=================================================================================================================== # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) im_data_split_list = split_data(im_data, 3) for im in im_data_split_list: x_dirname, x_basename, x_ext = sct.extract_fname(im.absolutepath) im.absolutepath = os.path.join(x_dirname, x_basename + ".nii.gz") im.save() # Merge b=0 images sct.printv('\nMerge b=0...', param.verbose) im_b0_list = [] for it in range(nb_b0): im_b0_list.append(im_data_split_list[index_b0[it]]) im_b0_out = concat_data(im_b0_list, 3).save(file_b0) sct.printv((' File created: ' + file_b0), param.verbose) # Average b=0 images sct.printv('\nAverage b=0...', param.verbose) file_b0_mean = sct.add_suffix(file_b0, '_mean') sct.run(['sct_maths', '-i', file_b0, '-o', file_b0_mean, '-mean', 't'], param.verbose) # Number of DWI groups nb_groups = int(math.floor(nb_dwi / param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_dwi[(iGroup * param.group_size):((iGroup + 1) * param.group_size)]) # add the remaining images to the last DWI group nb_remaining = nb_dwi % param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_dwi[len(index_dwi) - nb_remaining:len(index_dwi)]) file_dwi_dirname, file_dwi_basename, file_dwi_ext = sct.extract_fname( file_dwi) # DWI groups file_dwi_mean = [] for iGroup in tqdm(range(nb_groups), unit='iter', unit_scale=False, desc="Merge within groups", ascii=True, ncols=80): # get index index_dwi_i = group_indexes[iGroup] nb_dwi_i = len(index_dwi_i) # Merge DW Images file_dwi_merge_i = os.path.join( file_dwi_dirname, file_dwi_basename + '_' + str(iGroup) + ext_data) im_dwi_list = [] for it in range(nb_dwi_i): im_dwi_list.append(im_data_split_list[index_dwi_i[it]]) im_dwi_out = concat_data(im_dwi_list, 3).save(file_dwi_merge_i) # Average DW Images file_dwi_mean_i = os.path.join( file_dwi_dirname, file_dwi_basename + '_mean_' + str(iGroup) + ext_data) file_dwi_mean.append(file_dwi_mean_i) sct.run([ "sct_maths", "-i", file_dwi_merge_i, "-o", file_dwi_mean[iGroup], "-mean", "t" ], 0) # Merge DWI groups means sct.printv('\nMerging DW files...', param.verbose) # file_dwi_groups_means_merge = 'dwi_averaged_groups' im_dw_list = [] for iGroup in range(nb_groups): im_dw_list.append(file_dwi_mean[iGroup]) im_dw_out = concat_data(im_dw_list, 3).save(file_dwi_group) # Average DW Images # TODO: USEFULL ??? sct.printv('\nAveraging all DW images...', param.verbose) sct.run([ "sct_maths", "-i", file_dwi_group, "-o", file_dwi_group + '_mean' + ext_data, "-mean", "t" ], param.verbose) # segment dwi images using otsu algorithm if param.otsu: sct.printv('\nSegment group DWI using OTSU algorithm...', param.verbose) # import module otsu = importlib.import_module('sct_otsu') # get class from module param_otsu = otsu.param() #getattr(otsu, param) param_otsu.fname_data = file_dwi_group param_otsu.threshold = param.otsu param_otsu.file_suffix = '_seg' # run otsu otsu.otsu(param_otsu) file_dwi_group = file_dwi_group + '_seg.nii' # START MOCO #=================================================================================================================== # Estimate moco on b0 groups sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on b=0 images...', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'b0.nii' # identify target image if index_dwi[0] != 0: # If first DWI is not the first volume (most common), then there is a least one b=0 image before. In that case # select it as the target image for registration of all b=0 param_moco.file_target = os.path.join( file_data_dirname, file_data_basename + '_T' + str(index_b0[index_dwi[0] - 1]).zfill(4) + ext_data) else: # If first DWI is the first volume, then the target b=0 is the first b=0 from the index_b0. param_moco.file_target = os.path.join( file_data_dirname, file_data_basename + '_T' + str(index_b0[0]).zfill(4) + ext_data) param_moco.path_out = '' param_moco.todo = 'estimate' param_moco.mat_moco = 'mat_b0groups' file_mat_b0 = moco.moco(param_moco) # Estimate moco on dwi groups sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on DW images...', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = file_dwi_group param_moco.file_target = file_dwi_mean[ 0] # target is the first DW image (closest to the first b=0) param_moco.path_out = '' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_dwigroups' file_mat_dwi = moco.moco(param_moco) # create final mat folder sct.create_folder(mat_final) # Copy b=0 registration matrices # TODO: use file_mat_b0 and file_mat_dwi instead of the hardcoding below sct.printv('\nCopy b=0 registration matrices...', param.verbose) for it in range(nb_b0): sct.copy( 'mat_b0groups/' + 'mat.Z0000T' + str(it).zfill(4) + ext_mat, mat_final + 'mat.Z0000T' + str(index_b0[it]).zfill(4) + ext_mat) # Copy DWI registration matrices sct.printv('\nCopy DWI registration matrices...', param.verbose) for iGroup in range(nb_groups): for dwi in range( len(group_indexes[iGroup]) ): # we cannot use enumerate because group_indexes has 2 dim. sct.copy( 'mat_dwigroups/' + 'mat.Z0000T' + str(iGroup).zfill(4) + ext_mat, mat_final + 'mat.Z0000T' + str(group_indexes[iGroup][dwi]).zfill(4) + ext_mat) # Spline Regularization along T if param.spline_fitting: moco.spline(mat_final, nt, nz, param.verbose, np.array(index_b0), param.plot_graph) # combine Eddy Matrices if param.run_eddy: param.mat_2_combine = 'mat_eddy' param.mat_final = mat_final moco.combine_matrix(param) # Apply moco on all dmri data sct.printv( '\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv( '-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = file_data param_moco.file_target = os.path.join( file_dwi_dirname, file_dwi_basename + '_mean_' + str(0) + ext_data) # reference for reslicing into proper coordinate system param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". im_dmri = Image(file_data) fname_data_moco = os.path.join(file_data_dirname, file_data_basename + param.suffix + '.nii') im_dmri_moco = Image(fname_data_moco) im_dmri_moco.header = im_dmri.header im_dmri_moco.save() return os.path.abspath(fname_data_moco)
def main(): # initialization start_time = time.time() path_out = '.' param_user = '' # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # get path of the toolbox status, param.path_sct = commands.getstatusoutput('echo $SCT_DIR') # Parameters for debug mode if param.debug: # get path of the testing data status, path_sct_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') param.fname_data = path_sct_data+'/dmri/dmri.nii.gz' param.fname_bvecs = path_sct_data+'/dmri/bvecs.txt' param.fname_mask = path_sct_data+'/dmri/dmri.nii.gz' param.remove_tmp_files = 0 param.verbose = 1 param.run_eddy = 0 param.otsu = 0 param.group_size = 5 param.iterative_averaging = 1 else: # Check input parameters try: opts, args = getopt.getopt(sys.argv[1:], 'hi:a:b:e:f:g:m:o:p:r:t:v:x:') except getopt.GetoptError: usage() if not opts: usage() for opt, arg in opts: if opt == '-h': usage() elif opt in ('-a'): param.fname_bvals = arg elif opt in ('-b'): param.fname_bvecs = arg elif opt in ('-e'): param.run_eddy = int(arg) elif opt in ('-f'): param.spline_fitting = int(arg) elif opt in ('-g'): param.group_size = int(arg) elif opt in ('-i'): param.fname_data = arg elif opt in ('-m'): param.fname_mask = arg elif opt in ('-o'): path_out = arg elif opt in ('-p'): param_user = arg elif opt in ('-r'): param.remove_tmp_files = int(arg) elif opt in ('-t'): param.otsu = int(arg) elif opt in ('-v'): param.verbose = int(arg) elif opt in ('-x'): param.interp = arg # display usage if a mandatory argument is not provided if param.fname_data == '' or param.fname_bvecs == '': sct.printv('ERROR: All mandatory arguments are not provided. See usage.', 1, 'error') usage() # parse argument for param if not param_user == '': param.param = param_user.replace(' ', '').split(',') # remove spaces and parse with comma # TODO: check integrity of input # param.param = [i for i in range(len(param_user))] del param_user sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............'+param.fname_data, param.verbose) sct.printv(' bvecs file ............'+param.fname_bvecs, param.verbose) sct.printv(' bvals file ............'+param.fname_bvals, param.verbose) sct.printv(' mask file .............'+param.fname_mask, param.verbose) # check existence of input files sct.printv('\nCheck file existence...', param.verbose) sct.check_file_exist(param.fname_data, param.verbose) sct.check_file_exist(param.fname_bvecs, param.verbose) if not param.fname_bvals == '': sct.check_file_exist(param.fname_bvals, param.verbose) if not param.fname_mask == '': sct.check_file_exist(param.fname_mask, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, param.verbose) # Copying input data to tmp folder # NB: cannot use c3d here because c3d cannot convert 4D data. sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) sct.run('cp '+param.fname_data+' '+path_tmp+'dmri'+ext_data, param.verbose) sct.run('cp '+param.fname_bvecs+' '+path_tmp+'bvecs.txt', param.verbose) if param.fname_mask != '': sct.run('cp '+param.fname_mask+' '+path_tmp+'mask'+ext_mask, param.verbose) # go to tmp folder os.chdir(path_tmp) # convert dmri to nii format convert('dmri'+ext_data, 'dmri.nii') # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = 'mask'+ext_mask # run moco dmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+'dmri'+param.suffix+'.nii', path_out+file_data+param.suffix+ext_data, param.verbose) sct.generate_output_file(path_tmp+'b0_mean.nii', path_out+'b0'+param.suffix+'_mean'+ext_data, param.verbose) sct.generate_output_file(path_tmp+'dwi_mean.nii', path_out+'dwi'+param.suffix+'_mean'+ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', param.verbose) #To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv('fslview -m ortho,ortho '+param.path_out+file_data+param.suffix+' '+file_data+' &\n', param.verbose, 'info')
def test(path_data='', parameters=''): # initialization verbose = 0 output = '' # check if isct_propseg compatibility status_isct_propseg, output_isct_propseg = commands.getstatusoutput('isct_propseg') isct_propseg_version = output_isct_propseg.split('\n')[0] if isct_propseg_version != 'sct_propseg - Version 1.1 (2015-03-24)': status = 99 output += '\nERROR: isct_propseg does not seem to be compatible with your system or is no up-to-date... Please contact SCT administrators.' return status, output, DataFrame(data={'status': status, 'output': output}, index=[path_data]) # parameters if not parameters: parameters = '-i t2/t2.nii.gz -c t2' dice_threshold = 0.9 parser = sct_propseg.get_parser() dict_param = parser.parse(parameters.split(), check_file_exist=False) dict_param_with_path = parser.add_path_to_file(deepcopy(dict_param), path_data, input_file=True) param_with_path = parser.dictionary_to_string(dict_param_with_path) # Extract contrast contrast = '' input_filename = '' if dict_param['-i'][0] == '/': dict_param['-i'] = dict_param['-i'][1:] input_split = dict_param['-i'].split('/') if len(input_split) == 2: contrast = input_split[0] input_filename = input_split[1] else: input_filename = input_split[0] if not contrast: # if no contrast folder, send error. status = 1 output += '\nERROR: when extracting the contrast folder from input file in command line: ' + dict_param['-i'] + ' for ' + path_data return status, output, DataFrame(data={'status': status, 'output': output, 'dice_segmentation': float('nan')}, index=[path_data]) import time, random subject_folder = path_data.split('/') if subject_folder[-1] == '' and len(subject_folder) > 1: subject_folder = subject_folder[-2] else: subject_folder = subject_folder[-1] path_output = sct.slash_at_the_end('sct_propseg_' + subject_folder + '_' + time.strftime("%y%m%d%H%M%S") + '_' + str(random.randint(1, 1000000)), slash=1) param_with_path += ' -ofolder ' + path_output sct.create_folder(path_output) # log file import sys fname_log = path_output + 'output.log' stdout_log = file(fname_log, 'w') # redirect to log file stdout_orig = sys.stdout sys.stdout = stdout_log # Check if input files exist if not (os.path.isfile(dict_param_with_path['-i'])): status = 200 output += '\nERROR: the file(s) provided to test function do not exist in folder: ' + path_data write_to_log_file(fname_log, output, 'w') return status, output, DataFrame( data={'status': status, 'output': output, 'dice_segmentation': float('nan')}, index=[path_data]) # Check if ground truth files exist if not os.path.isfile(path_data + contrast + '/' + contrast + '_seg_manual.nii.gz'): status = 201 output += '\nERROR: the file *_labeled_center_manual.nii.gz does not exist in folder: ' + path_data write_to_log_file(fname_log, output, 'w') return status, output, DataFrame(data={'status': int(status), 'output': output}, index=[path_data]) # run command cmd = 'sct_propseg ' + param_with_path output += '\n====================================================================================================\n'\ + cmd + \ '\n====================================================================================================\n\n' # copy command time_start = time.time() try: status, o = sct.run(cmd, 0) except: status, o = 1, 'ERROR: Function crashed!' output += o duration = time.time() - time_start # extract name of manual segmentation # by convention, manual segmentation are called inputname_seg_manual.nii.gz where inputname is the filename # of the input image segmentation_filename = path_output + sct.add_suffix(input_filename, '_seg') manual_segmentation_filename = path_data + contrast + '/' + sct.add_suffix(input_filename, '_seg_manual') dice_segmentation = float('nan') # if command ran without error, test integrity if status == 0: # compute dice coefficient between generated image and image from database dice_segmentation = compute_dice(Image(segmentation_filename), Image(manual_segmentation_filename), mode='3d', zboundaries=False) if dice_segmentation < dice_threshold: status = 99 # transform results into Pandas structure results = DataFrame(data={'status': status, 'output': output, 'dice_segmentation': dice_segmentation, 'duration [s]': duration}, index=[path_data]) sys.stdout.close() sys.stdout = stdout_orig # write log file write_to_log_file(fname_log, output, mode='r+', prepend=True) return status, output, results
def compute_csa(fname_segmentation, name_method, volume_output, verbose, remove_temp_files, spline_smoothing, step, smoothing_param, figure_fit, name_output, slices, vert_levels, path_to_template, algo_fitting = 'hanning', type_window = 'hanning', window_length = 80): #param.algo_fitting = 'hanning' # Extract path, file and extension fname_segmentation = os.path.abspath(fname_segmentation) path_data, file_data, ext_data = sct.extract_fname(fname_segmentation) # create temporary folder sct.printv('\nCreate temporary folder...', verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, verbose) # Copying input data to tmp folder and convert to nii sct.printv('\nCopying input data to tmp folder and convert to nii...', verbose) sct.run('isct_c3d '+fname_segmentation+' -o '+path_tmp+'segmentation.nii') # go to tmp folder os.chdir(path_tmp) # Change orientation of the input segmentation into RPI sct.printv('\nChange orientation of the input segmentation into RPI...', verbose) fname_segmentation_orient = set_orientation('segmentation.nii', 'RPI', 'segmentation_orient.nii') # Get size of data sct.printv('\nGet data dimensions...', verbose) nx, ny, nz, nt, px, py, pz, pt = sct.get_dimension(fname_segmentation_orient) sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), verbose) # Open segmentation volume sct.printv('\nOpen segmentation volume...', verbose) file_seg = nibabel.load(fname_segmentation_orient) data_seg = file_seg.get_data() hdr_seg = file_seg.get_header() # # # Extract min and max index in Z direction X, Y, Z = (data_seg > 0).nonzero() # coords_seg = np.array([str([X[i], Y[i], Z[i]]) for i in xrange(0,len(Z))]) # don't know why but finding strings in array of array of strings is WAY faster than doing the same with integers min_z_index, max_z_index = min(Z), max(Z) Xp,Yp = (data_seg[:,:,0]>=0).nonzero() # X and Y range # # x_centerline = [0 for i in xrange(0,max_z_index-min_z_index+1)] # y_centerline = [0 for i in xrange(0,max_z_index-min_z_index+1)] # z_centerline = np.array([iz for iz in xrange(min_z_index, max_z_index+1)]) # # # Extract segmentation points and average per slice # for iz in xrange(min_z_index, max_z_index+1): # x_seg, y_seg = (data_seg[:,:,iz]>0).nonzero() # x_centerline[iz-min_z_index] = np.mean(x_seg) # y_centerline[iz-min_z_index] = np.mean(y_seg) # # # Fit the centerline points with spline and return the new fitted coordinates # x_centerline_fit, y_centerline_fit,x_centerline_deriv,y_centerline_deriv,z_centerline_deriv = b_spline_centerline(x_centerline,y_centerline,z_centerline) # extract centerline and smooth it x_centerline_fit, y_centerline_fit, z_centerline, x_centerline_deriv,y_centerline_deriv,z_centerline_deriv = smooth_centerline(fname_segmentation_orient, algo_fitting=algo_fitting, type_window=type_window, window_length=window_length, verbose = verbose) z_centerline_scaled = [x*pz for x in z_centerline] # # 3D plot of the fit # fig=plt.figure() # ax=Axes3D(fig) # ax.plot(x_centerline,y_centerline,z_centerline,zdir='z') # ax.plot(x_centerline_fit,y_centerline_fit,z_centerline,zdir='z') # plt.show() # Defining cartesian basis vectors x = np.array([1, 0, 0]) y = np.array([0, 1, 0]) z = np.array([0, 0, 1]) # Creating folder in which JPG files will be stored sct.printv('\nCreating folder in which JPG files will be stored...', verbose) sct.create_folder('JPG_Results') # Compute CSA sct.printv('\nCompute CSA...', verbose) # Empty arrays in which CSA for each z slice will be stored csa = [0.0 for i in xrange(0,max_z_index-min_z_index+1)] # sections_ortho_counting = [0 for i in xrange(0,max_z_index-min_z_index+1)] # sections_ortho_ellipse = [0 for i in xrange(0,max_z_index-min_z_index+1)] # sections_z_ellipse = [0 for i in xrange(0,max_z_index-min_z_index+1)] # sections_z_counting = [0 for i in xrange(0,max_z_index-min_z_index+1)] sct.printv('\nCross-Section Area:', verbose, 'bold') for iz in xrange(0, len(z_centerline)): # Equation of the the plane which is orthogonal to the spline at z=iz a = x_centerline_deriv[iz] b = y_centerline_deriv[iz] c = z_centerline_deriv[iz] #vector normal to the plane normal = normalize(np.array([a, b, c])) # angle between normal vector and z angle = np.arccos(np.dot(normal, z)) if name_method == 'counting_ortho_plane' or name_method == 'ellipse_ortho_plane': x_center = x_centerline_fit[iz] y_center = y_centerline_fit[iz] z_center = z_centerline[iz] # use of x in order to get orientation of each plane, basis_1 is in the plane ax+by+cz+d=0 basis_1 = normalize(np.cross(normal,x)) basis_2 = normalize(np.cross(normal,basis_1)) # maximum dimension of the tilted plane. Try multiply numerator by sqrt(2) ? max_diameter = (max([(max(X)-min(X))*px,(max(Y)-min(Y))*py]))/(np.cos(angle)) # Forcing the step to be the min of x and y scale (default value is 1 mm) step = min([px,py]) # discretized plane which will be filled with 0/1 plane_seg = np.zeros((int(max_diameter/step),int(max_diameter/step))) # how the plane will be skimmed through plane_grid = np.linspace(-int(max_diameter/2),int(max_diameter/2),int(max_diameter/step)) # we go through the plane for i_b1 in plane_grid : for i_b2 in plane_grid : point = np.array([x_center*px,y_center*py,z_center*pz]) + i_b1*basis_1 +i_b2*basis_2 # to which voxel belongs each point of the plane coord_voxel = str([ int(point[0]/px), int(point[1]/py), int(point[2]/pz)]) if (coord_voxel in coords_seg) is True : # if this voxel is 1 plane_seg[int((plane_grid==i_b1).nonzero()[0])][int((plane_grid==i_b2).nonzero()[0])] = 1 # number of voxels that are in the intersection of each plane and the nonzeros values of segmentation, times the area of one cell of the discretized plane if name_method == 'counting_ortho_plane': csa[iz] = len((plane_seg>0).nonzero()[0])*step*step # if verbose ==1 and name_method == 'counting_ortho_plane' : # print('Cross-Section Area : ' + str(csa[iz]) + ' mm^2') if name_method == 'ellipse_ortho_plane': # import scipy stuff from scipy.misc import imsave os.chdir('JPG_Results') imsave('plane_ortho_' + str(iz) + '.jpg', plane_seg) # Tresholded gradient image mag = edge_detection('plane_ortho_' + str(iz) + '.jpg') #Coordinates of the contour x_contour,y_contour = (mag>0).nonzero() x_contour = x_contour*step y_contour = y_contour*step #Fitting an ellipse fit = Ellipse_fit(x_contour,y_contour) # Semi-minor axis, semi-major axis a_ellipse, b_ellipse = ellipse_dim(fit) #Section = pi*a*b csa[iz] = a_ellipse*b_ellipse*np.pi # if verbose == 1 and name_method == 'ellipse_ortho_plane': # print('Cross-Section Area : ' + str(csa[iz]) + ' mm^2') # os.chdir('..') if name_method == 'counting_z_plane' or name_method == 'ellipse_z_plane': # getting the segmentation for each z plane x_seg, y_seg = (data_seg[:, :, iz+min_z_index] > 0).nonzero() seg = [[x_seg[i], y_seg[i]] for i in range(0, len(x_seg))] plane = np.zeros((max(Xp), max(Yp))) for i in seg: # filling the plane with 0 and 1 regarding to the segmentation plane[i[0] - 1][i[1] - 1] = data_seg[i[0] - 1, i[1] - 1, iz+min_z_index] if name_method == 'counting_z_plane': x, y = (plane > 0.0).nonzero() len_x = len(x) for i in range(0, len_x): csa[iz] += plane[x[i], y[i]]*px*py csa[iz] *= np.cos(angle) # if verbose == 1 and name_method == 'counting_z_plane': # print('Cross-Section Area : ' + str(csa[iz]) + ' mm^2') if name_method == 'ellipse_z_plane': # import scipy stuff from scipy.misc import imsave os.chdir('JPG_Results') imsave('plane_z_' + str(iz) + '.jpg', plane) # Tresholded gradient image mag = edge_detection('plane_z_' + str(iz) + '.jpg') x_contour,y_contour = (mag>0).nonzero() x_contour = x_contour*px y_contour = y_contour*py # Fitting an ellipse fit = Ellipse_fit(x_contour,y_contour) a_ellipse, b_ellipse = ellipse_dim(fit) csa[iz] = a_ellipse*b_ellipse*np.pi*np.cos(angle) # if verbose == 1 and name_method == 'ellipse_z_plane': # print('Cross-Section Area : ' + str(csa[iz]) + ' mm^2') if spline_smoothing == 1: sct.printv('\nSmoothing results with spline...', verbose) tck = scipy.interpolate.splrep(z_centerline_scaled, csa, s=smoothing_param) csa_smooth = scipy.interpolate.splev(z_centerline_scaled, tck) if figure_fit == 1: import matplotlib.pyplot as plt plt.figure() plt.plot(z_centerline_scaled, csa) plt.plot(z_centerline_scaled, csa_smooth) plt.legend(['CSA values', 'Smoothed values'], 2) plt.savefig('Spline_fit.png') csa = csa_smooth # update variable # Create output text file sct.printv('\nWrite text file...', verbose) file_results = open('csa.txt', 'w') for i in range(min_z_index, max_z_index+1): file_results.write(str(int(i)) + ',' + str(csa[i-min_z_index])+'\n') # Display results sct.printv('z='+str(i-min_z_index)+': '+str(csa[i-min_z_index])+' mm^2', verbose, 'bold') file_results.close() # output volume of csa values if volume_output: sct.printv('\nCreate volume of CSA values...', verbose) # get orientation of the input data orientation = get_orientation('segmentation.nii') data_seg = data_seg.astype(np.float32, copy=False) # loop across slices for iz in range(min_z_index, max_z_index+1): # retrieve seg pixels x_seg, y_seg = (data_seg[:, :, iz] > 0).nonzero() seg = [[x_seg[i],y_seg[i]] for i in range(0, len(x_seg))] # loop across pixels in segmentation for i in seg: # replace value with csa value data_seg[i[0], i[1], iz] = csa[iz-min_z_index] # create header hdr_seg.set_data_dtype('float32') # set imagetype to uint8 # save volume img = nibabel.Nifti1Image(data_seg, None, hdr_seg) nibabel.save(img, 'csa_RPI.nii') # Change orientation of the output centerline into input orientation fname_csa_volume = set_orientation('csa_RPI.nii', orientation, 'csa_RPI_orient.nii') # come back to parent folder os.chdir('..') # Generate output files sct.printv('\nGenerate output files...', verbose) sct.generate_output_file(path_tmp+'csa.txt', path_data+param.fname_csa) # extension already included in param.fname_csa if volume_output: sct.generate_output_file(fname_csa_volume, path_data+name_output) # extension already included in name_output # average csa across vertebral levels or slices if asked (flag -z or -l) if slices or vert_levels: if vert_levels and not path_to_template: sct.printv('\nERROR: Path to template is missing. See usage.\n', 1, 'error') sys.exit(2) elif vert_levels and path_to_template: abs_path_to_template = os.path.abspath(path_to_template) # go to tmp folder os.chdir(path_tmp) # create temporary folder sct.printv('\nCreate temporary folder to average csa...', verbose) path_tmp_extract_metric = sct.slash_at_the_end('label_temp', 1) sct.run('mkdir '+path_tmp_extract_metric, verbose) # Copying output CSA volume in the temporary folder sct.printv('\nCopy data to tmp folder...', verbose) sct.run('cp '+fname_segmentation+' '+path_tmp_extract_metric) # create file info_label path_fname_seg, file_fname_seg, ext_fname_seg = sct.extract_fname(fname_segmentation) create_info_label('info_label.txt', path_tmp_extract_metric, file_fname_seg+ext_fname_seg) if slices: # average CSA os.system("sct_extract_metric -i "+path_data+name_output+" -f "+path_tmp_extract_metric+" -m wa -o "+sct.slash_at_the_end(path_data)+"mean_csa -z "+slices) if vert_levels: sct.run('cp -R '+abs_path_to_template+' .') # average CSA os.system("sct_extract_metric -i "+path_data+name_output+" -f "+path_tmp_extract_metric+" -m wa -o "+sct.slash_at_the_end(path_data)+"mean_csa -v "+vert_levels) os.chdir('..') # Remove temporary files print('\nRemove temporary folder used to average CSA...') sct.run('rm -rf '+path_tmp_extract_metric) # Remove temporary files if remove_temp_files: print('\nRemove temporary files...') sct.run('rm -rf '+path_tmp)
def moco(param): # retrieve parameters fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI file_data = param.file_data file_target = param.file_target folder_mat = sct.slash_at_the_end(param.mat_moco, 1) # output folder of mat file todo = param.todo suffix = param.suffix #file_schedule = param.file_schedule verbose = param.verbose ext = '.nii' # get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # print arguments sct.printv('\nInput parameters:', param.verbose) sct.printv(' Input file ............'+file_data, param.verbose) sct.printv(' Reference file ........'+file_target, param.verbose) sct.printv(' Polynomial degree .....'+param.param[0], param.verbose) sct.printv(' Smoothing kernel ......'+param.param[1], param.verbose) sct.printv(' Gradient step .........'+param.param[2], param.verbose) sct.printv(' Metric ................'+param.param[3], param.verbose) sct.printv(' Todo ..................'+todo, param.verbose) sct.printv(' Mask .................'+param.fname_mask, param.verbose) sct.printv(' Output mat folder .....'+folder_mat, param.verbose) # create folder for mat files sct.create_folder(folder_mat) # Get size of data sct.printv('\nGet dimensions data...', verbose) data_im = Image(file_data+ext) nx, ny, nz, nt, px, py, pz, pt = data_im.dim sct.printv(('.. '+str(nx)+' x '+str(ny)+' x '+str(nz)+' x '+str(nt)), verbose) # copy file_target to a temporary file sct.printv('\nCopy file_target to a temporary file...', verbose) sct.run('cp '+file_target+ext+' target.nii') file_target = 'target' # Split data along T dimension sct.printv('\nSplit data along T dimension...', verbose) data_split_list = split_data(data_im, dim=3) for im in data_split_list: im.save() file_data_splitT = file_data + '_T' # Motion correction: initialization index = np.arange(nt) file_data_splitT_num = [] file_data_splitT_moco_num = [] failed_transfo = [0 for i in range(nt)] file_mat = [[] for i in range(nt)] # Motion correction: Loop across T for indice_index in range(nt): # create indices and display stuff it = index[indice_index] file_data_splitT_num.append(file_data_splitT + str(it).zfill(4)) file_data_splitT_moco_num.append(file_data + suffix + '_T' + str(it).zfill(4)) sct.printv(('\nVolume '+str((it))+'/'+str(nt-1)+':'), verbose) file_mat[it] = folder_mat + 'mat.T' + str(it) # run 3D registration failed_transfo[it] = register(param, file_data_splitT_num[it], file_target, file_mat[it], file_data_splitT_moco_num[it]) # average registered volume with target image # N.B. use weighted averaging: (target * nb_it + moco) / (nb_it + 1) if param.iterative_averaging and indice_index < 10 and failed_transfo[it] == 0: sct.run('sct_maths -i '+file_target+ext+' -mul '+str(indice_index+1)+' -o '+file_target+ext) sct.run('sct_maths -i '+file_target+ext+' -add '+file_data_splitT_moco_num[it]+ext+' -o '+file_target+ext) sct.run('sct_maths -i '+file_target+ext+' -div '+str(indice_index+2)+' -o '+file_target+ext) # Replace failed transformation with the closest good one sct.printv(('\nReplace failed transformations...'), verbose) fT = [i for i, j in enumerate(failed_transfo) if j == 1] gT = [i for i, j in enumerate(failed_transfo) if j == 0] for it in range(len(fT)): abs_dist = [abs(gT[i]-fT[it]) for i in range(len(gT))] if not abs_dist == []: index_good = abs_dist.index(min(abs_dist)) sct.printv(' transfo #'+str(fT[it])+' --> use transfo #'+str(gT[index_good]), verbose) # copy transformation sct.run('cp '+file_mat[gT[index_good]]+'Warp.nii.gz'+' '+file_mat[fT[it]]+'Warp.nii.gz') # apply transformation sct.run('sct_apply_transfo -i '+file_data_splitT_num[fT[it]]+'.nii -d '+file_target+'.nii -w '+file_mat[fT[it]]+'Warp.nii.gz'+' -o '+file_data_splitT_moco_num[fT[it]]+'.nii'+' -x '+param.interp, verbose) else: # exit program if no transformation exists. sct.printv('\nERROR in '+os.path.basename(__file__)+': No good transformation exist. Exit program.\n', verbose, 'error') sys.exit(2) # Merge data along T file_data_moco = file_data+suffix if todo != 'estimate': sct.printv('\nMerge data back along T...', verbose) from sct_image import concat_data # im_list = [] fname_list = [] for indice_index in range(len(index)): # im_list.append(Image(file_data_splitT_moco_num[indice_index] + ext)) fname_list.append(file_data_splitT_moco_num[indice_index] + ext) im_out = concat_data(fname_list, 3) im_out.setFileName(file_data_moco + ext) im_out.save() # delete file target.nii (to avoid conflict if this function is run another time) sct.printv('\nRemove temporary file...', verbose) #os.remove('target.nii') sct.run('rm target.nii')
def fmri_moco(param): file_data = "fmri.nii" mat_final = 'mat_final/' ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) im_data = Image(param.fname_data) nx, ny, nz, nt, px, py, pz, pt = im_data.dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz) + ' x ' + str(nt), param.verbose) # Get orientation sct.printv('\nData orientation: ' + im_data.orientation, param.verbose) if im_data.orientation[2] in 'LR': param.is_sagittal = True sct.printv(' Treated as sagittal') elif im_data.orientation[2] in 'IS': param.is_sagittal = False sct.printv(' Treated as axial') else: param.is_sagittal = False sct.printv('WARNING: Orientation seems to be neither axial nor sagittal.') # Adjust group size in case of sagittal scan if param.is_sagittal and param.group_size != 1: sct.printv('For sagittal data group_size should be one for more robustness. Forcing group_size=1.', 1, 'warning') param.group_size = 1 # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) im_data_split_list = split_data(im_data, 3) for im in im_data_split_list: x_dirname, x_basename, x_ext = sct.extract_fname(im.absolutepath) # Make further steps slurp the data to avoid too many open files (#2149) im.absolutepath = os.path.join(x_dirname, x_basename + ".nii.gz") im.save() # assign an index to each volume index_fmri = list(range(0, nt)) # Number of groups nb_groups = int(math.floor(nt / param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_fmri[(iGroup * param.group_size):((iGroup + 1) * param.group_size)]) # add the remaining images to the last fMRI group nb_remaining = nt%param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_fmri[len(index_fmri) - nb_remaining:len(index_fmri)]) # groups for iGroup in tqdm(range(nb_groups), unit='iter', unit_scale=False, desc="Merge within groups", ascii=True, ncols=80): # get index index_fmri_i = group_indexes[iGroup] nt_i = len(index_fmri_i) # Merge Images file_data_merge_i = sct.add_suffix(file_data, '_' + str(iGroup)) # cmd = fsloutput + 'fslmerge -t ' + file_data_merge_i # for it in range(nt_i): # cmd = cmd + ' ' + file_data + '_T' + str(index_fmri_i[it]).zfill(4) im_fmri_list = [] for it in range(nt_i): im_fmri_list.append(im_data_split_list[index_fmri_i[it]]) im_fmri_concat = concat_data(im_fmri_list, 3, squeeze_data=True).save(file_data_merge_i) file_data_mean = sct.add_suffix(file_data, '_mean_' + str(iGroup)) if file_data_mean.endswith(".nii"): file_data_mean += ".gz" # #2149 if param.group_size == 1: # copy to new file name instead of averaging (faster) # note: this is a bandage. Ideally we should skip this entire for loop if g=1 convert(file_data_merge_i, file_data_mean) else: # Average Images sct.run(['sct_maths', '-i', file_data_merge_i, '-o', file_data_mean, '-mean', 't'], verbose=0) # if not average_data_across_dimension(file_data_merge_i+'.nii', file_data_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_data_merge_i + ' -Tmean ' + file_data_mean # sct.run(cmd, param.verbose) # Merge groups means. The output 4D volume will be used for motion correction. sct.printv('\nMerging volumes...', param.verbose) file_data_groups_means_merge = 'fmri_averaged_groups.nii' im_mean_list = [] for iGroup in range(nb_groups): file_data_mean = sct.add_suffix(file_data, '_mean_' + str(iGroup)) if file_data_mean.endswith(".nii"): file_data_mean += ".gz" # #2149 im_mean_list.append(Image(file_data_mean)) im_mean_concat = concat_data(im_mean_list, 3).save(file_data_groups_means_merge) # Estimate moco sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'fmri_averaged_groups.nii' param_moco.file_target = sct.add_suffix(file_data, '_mean_' + param.num_target) if param_moco.file_target.endswith(".nii"): param_moco.file_target += ".gz" # #2149 param_moco.path_out = '' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_groups' file_mat = moco.moco(param_moco) # TODO: if g=1, no need to run the block below (already applied) if param.group_size == 1: # if flag g=1, it means that all images have already been corrected, so we just need to rename the file sct.mv('fmri_averaged_groups_moco.nii', 'fmri_moco.nii') else: # create final mat folder sct.create_folder(mat_final) # Copy registration matrices sct.printv('\nCopy transformations...', param.verbose) for iGroup in range(nb_groups): for data in range(len(group_indexes[iGroup])): # we cannot use enumerate because group_indexes has 2 dim. # fetch all file_mat_z for given t-group list_file_mat_z = file_mat[:, iGroup] # loop across file_mat_z and copy to mat_final folder for file_mat_z in list_file_mat_z: # we want to copy 'mat_groups/mat.ZXXXXTYYYYWarp.nii.gz' --> 'mat_final/mat.ZXXXXTYYYZWarp.nii.gz' # Notice the Y->Z in the under the T index: the idea here is to use the single matrix from each group, # and apply it to all images belonging to the same group. sct.copy(file_mat_z + ext_mat, mat_final + file_mat_z[11:20] + 'T' + str(group_indexes[iGroup][data]).zfill(4) + ext_mat) # Apply moco on all fmri data sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'fmri.nii' param_moco.file_target = sct.add_suffix(file_data, '_mean_' + str(0)) if param_moco.file_target.endswith(".nii"): param_moco.file_target += ".gz" param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". im_fmri = Image('fmri.nii') im_fmri_moco = Image('fmri_moco.nii') im_fmri_moco.header = im_fmri.header im_fmri_moco.save() # Average volumes sct.printv('\nAveraging data...', param.verbose) sct_maths.main(args=['-i', 'fmri_moco.nii', '-o', 'fmri_moco_mean.nii', '-mean', 't', '-v', '0'])
def main(args=None): # initialization start_time = time.time() param = Param() # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) if '-v' in arguments: param.verbose = int(arguments['-v']) sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............' + param.fname_data, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_tmp = sct.tmp_create(basename="fmri_moco", verbose=param.verbose) # Copying input data to tmp folder and convert to nii # TODO: no need to do that (takes time for nothing) sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, "fmri.nii"), squeeze_data=False) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # run moco fmri_moco(param) # come back os.chdir(curdir) # Generate output files fname_fmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) if os.path.isfile(os.path.join(path_tmp, "fmri" + param.suffix + '.nii')): sct.printv(os.path.join(path_tmp, "fmri" + param.suffix + '.nii')) sct.printv(os.path.join(path_out, file_data + param.suffix + ext_data)) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '.nii'), os.path.join(path_out, file_data + param.suffix + ext_data), param.verbose) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '_mean.nii'), os.path.join(path_out, file_data + param.suffix + '_mean' + ext_data), param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_fmri_moco, file_data], mode='ortho,ortho')
import os import commands import sys from shutil import move # Get path of the toolbox status, path_sct = commands.getstatusoutput('echo $SCT_DIR') # Append path that contains scripts, to be able to load modules sys.path.append(path_sct + '/scripts') from msct_image import Image import sct_utils as sct path_template = '/Users/julien/data/PAM50/template' folder_PAM50 = 'PAM50/template/' os.chdir(path_template) sct.create_folder(folder_PAM50) for file_template in [ 'MNI-Poly-AMU_T1.nii.gz', 'MNI-Poly-AMU_T2.nii.gz', 'MNI-Poly-AMU_T2star.nii.gz' ]: im = Image(file_template) # remove negative values data = im.data data[data < 0] = 0 im.data = data im.changeType('uint16') file_new = file_template.replace('MNI-Poly-AMU', 'PAM50') im.setFileName(file_new) im.save() # move to folder
def dmri_moco(param): file_data = 'dmri' ext_data = '.nii' file_b0 = 'b0' file_dwi = 'dwi' mat_final = 'mat_final/' file_dwi_group = 'dwi_averaged_groups' # no extension fsloutput = 'export FSLOUTPUTTYPE=NIFTI; ' # for faster processing, all outputs are in NIFTI ext_mat = 'Warp.nii.gz' # warping field # Get dimensions of data sct.printv('\nGet dimensions of data...', param.verbose) nx, ny, nz, nt, px, py, pz, pt = Image(file_data+'.nii').dim sct.printv(' ' + str(nx) + ' x ' + str(ny) + ' x ' + str(nz), param.verbose) # Identify b=0 and DWI images sct.printv('\nIdentify b=0 and DWI images...', param.verbose) index_b0, index_dwi, nb_b0, nb_dwi = identify_b0('bvecs.txt', param.fname_bvals, param.bval_min, param.verbose) # check if dmri and bvecs are the same size if not nb_b0 + nb_dwi == nt: sct.printv('\nERROR in '+os.path.basename(__file__)+': Size of data ('+str(nt)+') and size of bvecs ('+str(nb_b0+nb_dwi)+') are not the same. Check your bvecs file.\n', 1, 'error') sys.exit(2) # Prepare NIFTI (mean/groups...) #=================================================================================================================== # Split into T dimension sct.printv('\nSplit along T dimension...', param.verbose) status, output = sct.run('sct_split_data -i ' + file_data + ext_data + ' -dim t -suffix _T', param.verbose) # Merge b=0 images sct.printv('\nMerge b=0...', param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_b0 # for it in range(nb_b0): # cmd = cmd + ' ' + file_data + '_T' + str(index_b0[it]).zfill(4) cmd = 'sct_concat_data -dim t -o ' + file_b0 + ext_data + ' -i ' for it in range(nb_b0): cmd = cmd + file_data + '_T' + str(index_b0[it]).zfill(4) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string status, output = sct.run(cmd, param.verbose) sct.printv((' File created: ' + file_b0), param.verbose) # Average b=0 images sct.printv('\nAverage b=0...', param.verbose) file_b0_mean = file_b0+'_mean' sct.run('sct_maths -i '+file_b0+'.nii'+' -o '+file_b0_mean+'.nii'+' -mean t', param.verbose) # if not average_data_across_dimension(file_b0+'.nii', file_b0_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_b0 + ' -Tmean ' + file_b0_mean # status, output = sct.run(cmd, param.verbose) # Number of DWI groups nb_groups = int(math.floor(nb_dwi/param.group_size)) # Generate groups indexes group_indexes = [] for iGroup in range(nb_groups): group_indexes.append(index_dwi[(iGroup*param.group_size):((iGroup+1)*param.group_size)]) # add the remaining images to the last DWI group nb_remaining = nb_dwi%param.group_size # number of remaining images if nb_remaining > 0: nb_groups += 1 group_indexes.append(index_dwi[len(index_dwi)-nb_remaining:len(index_dwi)]) # DWI groups for iGroup in range(nb_groups): sct.printv('\nDWI group: ' +str((iGroup+1))+'/'+str(nb_groups), param.verbose) # get index index_dwi_i = group_indexes[iGroup] nb_dwi_i = len(index_dwi_i) # Merge DW Images sct.printv('Merge DW images...', param.verbose) file_dwi_merge_i = file_dwi + '_' + str(iGroup) cmd = 'sct_concat_data -dim t -o ' + file_dwi_merge_i + ext_data + ' -i ' for it in range(nb_dwi_i): cmd = cmd + file_data + '_T' + str(index_dwi_i[it]).zfill(4) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_dwi_merge_i # for it in range(nb_dwi_i): # cmd = cmd +' ' + file_data + '_T' + str(index_dwi_i[it]).zfill(4) # Average DW Images sct.printv('Average DW images...', param.verbose) file_dwi_mean = file_dwi + '_mean_' + str(iGroup) sct.run('sct_maths -i '+file_dwi_merge_i+'.nii'+' -o '+file_dwi_mean+'.nii'+' -mean t', param.verbose) # if not average_data_across_dimension(file_dwi_merge_i+'.nii', file_dwi_mean+'.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # cmd = fsloutput + 'fslmaths ' + file_dwi_merge_i + ' -Tmean ' + file_dwi_mean # sct.run(cmd, param.verbose) # Merge DWI groups means sct.printv('\nMerging DW files...', param.verbose) # file_dwi_groups_means_merge = 'dwi_averaged_groups' cmd = 'sct_concat_data -dim t -o ' + file_dwi_group + ext_data + ' -i ' for iGroup in range(nb_groups): cmd = cmd + file_dwi + '_mean_' + str(iGroup) + ext_data + ',' cmd = cmd[:-1] # remove ',' at the end of the string sct.run(cmd, param.verbose) # cmd = fsloutput + 'fslmerge -t ' + file_dwi_group # for iGroup in range(nb_groups): # cmd = cmd + ' ' + file_dwi + '_mean_' + str(iGroup) # Average DW Images # TODO: USEFULL ??? sct.printv('\nAveraging all DW images...', param.verbose) fname_dwi_mean = 'dwi_mean' sct.run('sct_maths -i '+file_dwi_group+'.nii'+' -o '+file_dwi_group+'_mean.nii'+' -mean t', param.verbose) # if not average_data_across_dimension(file_dwi_group+'.nii', file_dwi_group+'_mean.nii', 3): # sct.printv('ERROR in average_data_across_dimension', 1, 'error') # sct.run(fsloutput + 'fslmaths ' + file_dwi_group + ' -Tmean ' + file_dwi_group+'_mean', param.verbose) # segment dwi images using otsu algorithm if param.otsu: sct.printv('\nSegment group DWI using OTSU algorithm...', param.verbose) # import module otsu = importlib.import_module('sct_otsu') # get class from module param_otsu = otsu.param() #getattr(otsu, param) param_otsu.fname_data = file_dwi_group+'.nii' param_otsu.threshold = param.otsu param_otsu.file_suffix = '_seg' # run otsu otsu.otsu(param_otsu) file_dwi_group = file_dwi_group+'_seg' # extract first DWI volume as target for registration nii = Image(file_dwi_group+'.nii') data_crop = nii.data[:, :, :, index_dwi[0]:index_dwi[0]+1] nii.data = data_crop nii.setFileName('target_dwi.nii') nii.save() # START MOCO #=================================================================================================================== # Estimate moco on b0 groups sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on b=0 images...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco = param param_moco.file_data = 'b0' if index_dwi[0] != 0: # If first DWI is not the first volume (most common), then there is a least one b=0 image before. In that case # select it as the target image for registration of all b=0 param_moco.file_target = file_data + '_T' + str(index_b0[index_dwi[0]-1]).zfill(4) else: # If first DWI is the first volume, then the target b=0 is the first b=0 from the index_b0. param_moco.file_target = file_data + '_T' + str(index_b0[0]).zfill(4) param_moco.path_out = '' param_moco.todo = 'estimate' param_moco.mat_moco = 'mat_b0groups' moco.moco(param_moco) # Estimate moco on dwi groups sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Estimating motion on DW images...', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = file_dwi_group param_moco.file_target = 'target_dwi' # target is the first DW image (closest to the first b=0) param_moco.path_out = '' #param_moco.todo = 'estimate' param_moco.todo = 'estimate_and_apply' param_moco.mat_moco = 'mat_dwigroups' moco.moco(param_moco) # create final mat folder sct.create_folder(mat_final) # Copy b=0 registration matrices sct.printv('\nCopy b=0 registration matrices...', param.verbose) for it in range(nb_b0): sct.run('cp '+'mat_b0groups/'+'mat.T'+str(it)+ext_mat+' '+mat_final+'mat.T'+str(index_b0[it])+ext_mat, param.verbose) # Copy DWI registration matrices sct.printv('\nCopy DWI registration matrices...', param.verbose) for iGroup in range(nb_groups): for dwi in range(len(group_indexes[iGroup])): sct.run('cp '+'mat_dwigroups/'+'mat.T'+str(iGroup)+ext_mat+' '+mat_final+'mat.T'+str(group_indexes[iGroup][dwi])+ext_mat, param.verbose) # Spline Regularization along T if param.spline_fitting: moco.spline(mat_final, nt, nz, param.verbose, np.array(index_b0), param.plot_graph) # combine Eddy Matrices if param.run_eddy: param.mat_2_combine = 'mat_eddy' param.mat_final = mat_final moco.combine_matrix(param) # Apply moco on all dmri data sct.printv('\n-------------------------------------------------------------------------------', param.verbose) sct.printv(' Apply moco', param.verbose) sct.printv('-------------------------------------------------------------------------------', param.verbose) param_moco.file_data = 'dmri' param_moco.file_target = file_dwi+'_mean_'+str(0) # reference for reslicing into proper coordinate system param_moco.path_out = '' param_moco.mat_moco = mat_final param_moco.todo = 'apply' moco.moco(param_moco) # copy geometric information from header # NB: this is required because WarpImageMultiTransform in 2D mode wrongly sets pixdim(3) to "1". copy_header('dmri.nii', 'dmri_moco.nii') # generate b0_moco_mean and dwi_moco_mean cmd = 'sct_dmri_separate_b0_and_dwi -i dmri'+param.suffix+'.nii -b bvecs.txt -a 1' if not param.fname_bvals == '': cmd = cmd+' -m '+param.fname_bvals sct.run(cmd, param.verbose)
def main(): # initialization start_time = time.time() path_out = '.' param_user = '' # reducing the number of CPU used for moco (see issue #201) os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = "1" # get path of the toolbox status, param.path_sct = commands.getstatusoutput('echo $SCT_DIR') # Parameters for debug mode if param.debug: # get path of the testing data status, path_sct_data = commands.getstatusoutput('echo $SCT_TESTING_DATA_DIR') param.fname_data = path_sct_data+'/dmri/dmri.nii.gz' param.fname_bvecs = path_sct_data+'/dmri/bvecs.txt' param.fname_mask = path_sct_data+'/dmri/dmri.nii.gz' param.remove_tmp_files = 0 param.verbose = 1 param.run_eddy = 0 param.otsu = 0 param.group_size = 5 param.iterative_averaging = 1 else: parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] param.fname_bvecs = arguments['-bvec'] if '-bval' in arguments: param.fname_bvals = arguments['-bval'] if '-g' in arguments: param.group_size = arguments['-g'] if 'm' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.param = arguments['-param'] if '-thr' in arguments: param.otsu = arguments['-thr'] if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_tmp_files = int(arguments['-r']) if '-v' in arguments: param.verbose = int(arguments['-v']) ''' # Some old options that wasn't in the doc ... elif opt in ('-e'): param.run_eddy = int(arg) elif opt in ('-f'): param.spline_fitting = int(arg) ''' # Get full path param.fname_data = os.path.abspath(param.fname_data) param.fname_bvecs = os.path.abspath(param.fname_bvecs) if param.fname_bvals != '': param.fname_bvals = os.path.abspath(param.fname_bvals) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_mask, file_mask, ext_mask = sct.extract_fname(param.fname_mask) # create temporary folder sct.printv('\nCreate temporary folder...', param.verbose) path_tmp = sct.slash_at_the_end('tmp.'+time.strftime("%y%m%d%H%M%S"), 1) sct.run('mkdir '+path_tmp, param.verbose) # names of files in temporary folder ext = '.nii' dmri_name = 'dmri' mask_name = 'mask' bvecs_fname = 'bvecs.txt' # Copying input data to tmp folder sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) sct.run('cp '+param.fname_data+' '+path_tmp+dmri_name+ext_data, param.verbose) sct.run('cp '+param.fname_bvecs+' '+path_tmp+bvecs_fname, param.verbose) if param.fname_mask != '': sct.run('cp '+param.fname_mask+' '+path_tmp+mask_name+ext_mask, param.verbose) # go to tmp folder os.chdir(path_tmp) # convert dmri to nii format convert(dmri_name+ext_data, dmri_name+ext) # update field in param (because used later). # TODO: make this cleaner... if param.fname_mask != '': param.fname_mask = mask_name+ext_mask # run moco dmri_moco(param) # come back to parent folder os.chdir('..') # Generate output files path_out = sct.slash_at_the_end(path_out, 1) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(path_tmp+dmri_name+param.suffix+ext, path_out+file_data+param.suffix+ext_data, param.verbose) sct.generate_output_file(path_tmp+'b0_mean.nii', path_out+'b0'+param.suffix+'_mean'+ext_data, param.verbose) sct.generate_output_file(path_tmp+'dwi_mean.nii', path_out+'dwi'+param.suffix+'_mean'+ext_data, param.verbose) # Delete temporary files if param.remove_tmp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.run('rm -rf '+path_tmp, param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: '+str(int(round(elapsed_time)))+'s', param.verbose) #To view results sct.printv('\nTo view results, type:', param.verbose) sct.printv('fslview -m ortho,ortho '+param.path_out+file_data+param.suffix+' '+file_data+' &\n', param.verbose, 'info')
def segmentation(fname_input, output_dir, image_type): # parameters path_in, file_in, ext_in = sct.extract_fname(fname_input) segmentation_filename_old = os.path.join(path_in, 'old', file_in + '_seg' + ext_in) manual_segmentation_filename_old = os.path.join( path_in, 'manual_' + file_in + ext_in) detection_filename_old = os.path.join(path_in, 'old', file_in + '_detection' + ext_in) segmentation_filename_new = os.path.join(path_in, 'new', file_in + '_seg' + ext_in) manual_segmentation_filename_new = os.path.join( path_in, 'manual_' + file_in + ext_in) detection_filename_new = os.path.join(path_in, 'new', file_in + '_detection' + ext_in) # initialize results of segmentation and detection results_detection = [0, 0] results_segmentation = [0.0, 0.0] # perform PropSeg old version sct.rmtree(os.path.join(output_dir, 'old')) sct.create_folder(os.path.join(output_dir, 'old')) cmd = 'sct_propseg_old -i ' + fname_input \ + ' -o ' + os.path.join(output_dir, 'old') \ + ' -t ' + image_type \ + ' -detect-nii' sct.printv(cmd) status_propseg_old, output_propseg_old = sct.run(cmd) sct.printv(output_propseg_old) # check if spinal cord is correctly detected with old version of PropSeg cmd = "isct_check_detection.py -i " + detection_filename_old + " -t " + manual_segmentation_filename_old sct.printv(cmd) status_detection_old, output_detection_old = sct.run(cmd) sct.printv(output_detection_old) results_detection[0] = status_detection_old # compute Dice coefficient for old version of PropSeg cmd_validation = 'sct_dice_coefficient '+segmentation_filename_old \ + ' '+manual_segmentation_filename_old \ + ' -bzmax' sct.printv(cmd_validation) status_validation_old, output_validation_old = sct.run(cmd_validation) print(output_validation_old) res = output_validation_old.split()[-1] if res != 'nan': results_segmentation[0] = float(res) else: results_segmentation[0] = 0.0 # perform PropSeg new version sct.rmtree(os.path.join(output_dir, 'new')) sct.create_folder(os.path.join(output_dir, 'new')) cmd = 'sct_propseg -i ' + fname_input \ + ' -o ' + os.path.join(output_dir, 'new') \ + ' -t ' + image_type \ + ' -detect-nii' sct.printv(cmd) status_propseg_new, output_propseg_new = sct.run(cmd) sct.printv(output_propseg_new) # check if spinal cord is correctly detected with new version of PropSeg cmd = "isct_check_detection.py -i " + detection_filename_new + " -t " + manual_segmentation_filename_new sct.printv(cmd) status_detection_new, output_detection_new = sct.run(cmd) sct.printv(output_detection_new) results_detection[1] = status_detection_new # compute Dice coefficient for new version of PropSeg cmd_validation = 'sct_dice_coefficient '+segmentation_filename_new \ + ' '+manual_segmentation_filename_new \ + ' -bzmax' sct.printv(cmd_validation) status_validation_new, output_validation_new = sct.run(cmd_validation) print(output_validation_new) res = output_validation_new.split()[-1] if res != 'nan': results_segmentation[1] = float(res) else: results_segmentation[1] = 0.0 return results_detection, results_segmentation
def main(args=None): # initialization start_time = time.time() param = Param() # check user arguments if not args: args = sys.argv[1:] # Get parser info parser = get_parser() arguments = parser.parse(sys.argv[1:]) param.fname_data = arguments['-i'] if '-g' in arguments: param.group_size = arguments['-g'] if '-m' in arguments: param.fname_mask = arguments['-m'] if '-param' in arguments: param.update(arguments['-param']) if '-x' in arguments: param.interp = arguments['-x'] if '-ofolder' in arguments: path_out = arguments['-ofolder'] if '-r' in arguments: param.remove_temp_files = int(arguments['-r']) param.verbose = int(arguments.get('-v')) sct.init_sct(log_level=param.verbose, update=True) # Update log level sct.printv('\nInput parameters:', param.verbose) sct.printv(' input file ............' + param.fname_data, param.verbose) # Get full path param.fname_data = os.path.abspath(param.fname_data) if param.fname_mask != '': param.fname_mask = os.path.abspath(param.fname_mask) # Extract path, file and extension path_data, file_data, ext_data = sct.extract_fname(param.fname_data) path_tmp = sct.tmp_create(basename="fmri_moco", verbose=param.verbose) # Copying input data to tmp folder and convert to nii # TODO: no need to do that (takes time for nothing) sct.printv('\nCopying input data to tmp folder and convert to nii...', param.verbose) convert(param.fname_data, os.path.join(path_tmp, "fmri.nii"), squeeze_data=False) # go to tmp folder curdir = os.getcwd() os.chdir(path_tmp) # run moco fmri_moco(param) # come back os.chdir(curdir) # Generate output files fname_fmri_moco = os.path.join(path_out, file_data + param.suffix + ext_data) sct.create_folder(path_out) sct.printv('\nGenerate output files...', param.verbose) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '.nii'), fname_fmri_moco, param.verbose) sct.generate_output_file(os.path.join(path_tmp, "fmri" + param.suffix + '_mean.nii'), os.path.join(path_out, file_data + param.suffix + '_mean' + ext_data), param.verbose) # Delete temporary files if param.remove_temp_files == 1: sct.printv('\nDelete temporary files...', param.verbose) sct.rmtree(path_tmp, verbose=param.verbose) # display elapsed time elapsed_time = time.time() - start_time sct.printv('\nFinished! Elapsed time: ' + str(int(np.round(elapsed_time))) + 's', param.verbose) sct.display_viewer_syntax([fname_fmri_moco, file_data], mode='ortho,ortho')