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'])
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 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 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 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 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 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 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 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')