def test_nrrd2nifti(self): PREFIX = pjoin(outdir, 'nrrd2nifti_prefix') # run nrrd2nifti conversion conversion.nifti_write(REFERENCE_NRRD, PREFIX) # load converted output converted_nifti = load(PREFIX + '.nii.gz') converted_nifti_data = converted_nifti.get_data() converted_nifti_affine = converted_nifti.affine converted_nifti_bvals = conversion.read_bvals(PREFIX + '.bval') converted_nifti_bvecs = conversion.read_bvecs(PREFIX + '.bvec') # load converted reference converted_nifti = load(CONVERTED_NIFTI) reference_nifti_data = converted_nifti.get_data() reference_nifti_affine = converted_nifti.affine reference_nifti_bvals = conversion.read_bvals(CONVERTED_BVAL) reference_nifti_bvecs = conversion.read_bvecs(CONVERTED_BVEC) # test equality testing.assert_array_equal(reference_nifti_data, converted_nifti_data) testing.assert_array_equal(reference_nifti_affine, converted_nifti_affine) testing.assert_array_equal(reference_nifti_bvals, converted_nifti_bvals) testing.assert_array_equal(reference_nifti_bvecs, converted_nifti_bvecs)
def joinBshells(imgPath, ref_bvals_file=None, ref_bvals=None, sep_prefix=None): if ref_bvals_file: print('Reading reference b-shell file ...') ref_bvals = read_bvals(ref_bvals_file) print('Joining b-shells for', imgPath) imgPath = local.path(imgPath) img = load(imgPath._path) dim = img.header['dim'][1:5] inPrefix = abspath(imgPath).split('.nii')[0] directory = dirname(inPrefix) prefix = basename(inPrefix) bvalFile = inPrefix + '.bval' bvecFile = inPrefix + '.bvec' if sep_prefix: harmPrefix = pjoin(directory, sep_prefix + prefix) else: harmPrefix = inPrefix if not isfile(harmPrefix + '.bval'): copyfile(bvalFile, harmPrefix + '.bval') if not isfile(harmPrefix + '.bvec'): copyfile(bvecFile, harmPrefix + '.bvec') bvals = np.array(read_bvals(inPrefix + '.bval')) joinedDwi = np.zeros((dim[0], dim[1], dim[2], dim[3]), dtype='float32') for bval in ref_bvals: # ind= np.where(bval==bvals)[0] ind = np.where(abs(bval - bvals) <= BSHELL_MIN_DIST)[0] if bval == 0.: b0Img = load(inPrefix + '_b0.nii.gz') b0 = b0Img.get_data() for i in ind: joinedDwi[:, :, :, i] = b0 else: b0_bshell = load(harmPrefix + f'_b{int(bval)}.nii.gz').get_data() joinedDwi[:, :, :, ind] = b0_bshell[:, :, :, 1:] if not isfile(harmPrefix + '.nii.gz'): save_nifti(harmPrefix + '.nii.gz', joinedDwi, b0Img.affine, b0Img.header) else: print(harmPrefix + '.nii.gz', 'already exists, not overwritten.')
def bval_bvec_difference(imgPath_given_mat, imgPath_given_py, caselist): bval_diff_list = [] bval_total_diff = [] bval_mean_diff = [] bvec_diff_list = [] bvec_total_diff = [] bvec_mean_diff = [] for case in caselist: imgPath_mat = imgPath_given_mat.replace('XYZ', case) imgPath_py = imgPath_given_py.replace('XYZ', case) # load python bvals_py = np.array(read_bvals(imgPath_py.split('.nii')[0] + '.bval')) bvecs_py = np.array(read_bvecs(imgPath_py.split('.nii')[0] + '.bvec')) bvals_py, bvecs_py = stack_one_b0(bvals_py, bvecs=bvecs_py) bvals_py = bvals_py.astype('float64') bvecs_py = bvecs_py.astype('float64') # load matlab bvals_mat = np.array( read_bvals(imgPath_mat.split('.nii')[0] + '.bval')) bvecs_mat = np.array( read_bvecs(imgPath_mat.split('.nii')[0] + '.bvec')) bvals_mat = bvals_mat.astype('float64') bvecs_mat = bvecs_mat.astype('float64') # obtain case wise difference for bvals diff = 2 * abs(bvals_py - bvals_mat) / (bvals_py + bvals_mat) * 100 bval_diff_list.append(diff.flatten()) bval_mean_diff.append(diff.mean()) # obtain case wise difference diff = 2 * abs(bvecs_py - bvecs_mat) / (bvals_py + bvals_mat) * 100 bvec_diff_list.append(diff.flatten()) bvec_mean_diff.append(diff.mean()) print( f'Mean of mean relative percentage difference over all the bvals: {np.mean(bval_mean_diff)}' ) hist_calc(bval_diff_list, bins=BINS) print('\n\n') print( f'Mean of mean relative percentage difference over all the bvecs: {np.mean(bvec_mean_diff)}' ) hist_calc(bvec_diff_list, bins=BINS) print('\n\n')
def separateBshells(imgPath, ref_bvals_file=None, ref_bvals=None): if ref_bvals_file: print('Reading reference b-shell file ...') ref_bvals = read_bvals(ref_bvals_file) print('Separating b-shells for', imgPath) imgPath = local.path(imgPath) img = load(imgPath._path) dwi = img.get_data() inPrefix = abspath(imgPath).split('.nii')[0] bvals = np.array(read_bvals(inPrefix + '.bval')) bvecs = np.array(read_bvecs(inPrefix + '.bvec')) for bval in ref_bvals: # ind= np.where(bval==bvals)[0] ind = np.where(abs(bval - bvals) <= BSHELL_MIN_DIST)[0] N_b = len(ind) bPrefix = inPrefix + f'_b{int(bval)}' if bval == 0.: b0 = find_b0(dwi, ind) if isfile(bPrefix + '.nii.gz'): continue if bval == 0.: save_nifti(bPrefix + '.nii.gz', b0, img.affine, img.header) else: b0_bshell = np.zeros( (dwi.shape[0], dwi.shape[1], dwi.shape[2], N_b + 1), dtype='float32') b0_bshell[:, :, :, 0] = b0 b0_bshell[:, :, :, 1:] = dwi[:, :, :, ind] b0_bvals = [0.] + [bval] * N_b b0_bvecs = np.zeros((N_b + 1, 3), dtype='float32') b0_bvecs[1:, ] = bvecs[ind, :] save_nifti(bPrefix + '.nii.gz', b0_bshell, img.affine, img.header) write_bvals(bPrefix + '.bval', b0_bvals) write_bvecs(bPrefix + '.bvec', b0_bvecs)
def separateBshells(imgPath, ref_bvals_file=None, ref_bvals=None): if ref_bvals_file: print('Reading reference b-shell file ...') ref_bvals = read_bvals(ref_bvals_file) print('Separating b-shells for', imgPath) imgPath = local.path(imgPath) img = load(imgPath._path) dwi = img.get_data() inPrefix = abspath(imgPath).split('.nii')[0] bvals = np.array(read_bvals(inPrefix + '.bval')) bvecs = np.array(read_bvecs(inPrefix + '.bvec')) for bval in ref_bvals: # ind= np.where(bval==bvals)[0] ind = np.where(abs(bval - bvals) <= BSHELL_MIN_DIST)[0] N_b = len(ind) bPrefix = inPrefix + f'_b{int(bval)}' if bval == 0.: b0 = find_b0(dwi, ind) else: b0_bshell = np.zeros( (dwi.shape[0], dwi.shape[1], dwi.shape[2], N_b + 1), dtype='float32') b0_bshell[:, :, :, 0] = b0 b0_bshell[:, :, :, 1:] = dwi[:, :, :, ind] b0_bvals = [0.] + [bval] * N_b b0_bvecs = np.zeros((N_b + 1, 3), dtype='float32') b0_bvecs[1:, ] = bvecs[ind, :] bshell_written = load(bPrefix + '.nii.gz').get_fdata() bvals_written = read_bvals(bPrefix + '.bval') bvecs_written = read_bvecs(bPrefix + '.bvec') print('bshell difference', abs(b0_bshell - bshell_written).sum()) print('bvals difference', (np.array(b0_bvals) - np.array(bvals_written)).sum()) print('bvecs difference', (np.array(b0_bvecs) - np.array(bvecs_written)).sum())
def determineNshm(bvalFile): print( f'Determining maximum possible order of spherical harmonics for {bvalFile}\n' ) bvals = np.array(read_bvals(bvalFile)) N_b = len(np.where(bvals > B0_THRESH)[0]) if N_b < 6: raise ValueError( f'At least 6 gradients are necessary for each b-shell, b-shell has only {N_b}' ) elif N_b >= 6 and N_b < 15: N_shm = 2 elif N_b >= 15 and N_b < 28: N_shm = 4 elif N_b >= 28 and N_b < 45: N_shm = 6 else: N_shm = 8 print(f'Maximum possible order is {N_shm} for {N_b} non-zero gradients\n') return (N_shm, N_b)
def consistencyCheck(ref_csv, outputBshellFile=None, outPutResolutionFile=None): try: ref_imgs, _ = read_imgs_masks(ref_csv) except: ref_imgs = read_imgs(ref_csv) if isfile(outputBshellFile) and isfile(outPutResolutionFile): ref_bvals = read_bvals(outputBshellFile) ref_res = np.load(outPutResolutionFile) else: ref_bshell_img = ref_imgs[0] print(f'Using {ref_bshell_img} to determine b-shells') inPrefix = abspath(ref_bshell_img).split('.nii')[0] ref_bvals = findBShells(inPrefix + '.bval', outputBshellFile) ref_res = load(ref_bshell_img).header['pixdim'][1:4] np.save(outPutResolutionFile, ref_res) print('b-shells are', ref_bvals) print('\nSite', ref_csv, '\n') print('Checking consistency of b-shells among subjects') check_bshells(ref_imgs, ref_bvals) print('spatial resolution is', ref_res) print('Checking consistency of spatial resolution among subjects') check_resolution(ref_imgs, ref_res)
def findBShells(bvalFile, outputBshellFile= None): given_bvals= read_bvals(abspath(bvalFile)) # get unique bvalues in ascending order unique_bvals= np.unique(given_bvals) # identify b0s quantized_bvals= unique_bvals.copy() quantized_bvals[unique_bvals<=B0_THRESH]= 0. # round to multiple of B_QUANT (50 or 100) quantized_bvals= np.unique(np.round(quantized_bvals/B_QUANT)*B_QUANT) print('b-shell bvalues', quantized_bvals) for bval in quantized_bvals: print('Indices corresponding to b-shell', bval) print(np.where(abs(bval-given_bvals)<=BSHELL_MIN_DIST)[0],'\n') if outputBshellFile: print('Saving the b-shell bvalues in', outputBshellFile) write_bvals(outputBshellFile, quantized_bvals) return quantized_bvals
def main(self): prefix = self.dwi.name.split('.')[0] directory = self.dwi.parent self.b0_threshold = float(self.b0_threshold) if self.out is None: self.out = os.path.join(directory, prefix + '_bse.nii.gz') if self.dwi.endswith('.nii') or self.dwi.endswith('.nii.gz'): if not self.bval_file: self.bval_file = os.path.join(directory, prefix + '.bval') bvals = read_bvals(self.bval_file) idx = np.where([bval < self.b0_threshold for bval in bvals])[0] if len(idx) > 1: # default is the first b0 if not (self.minimum or self.average or self.all): fslroi[self.dwi, self.out, idx, 1] & FG elif self.minimum: fslroi[self.dwi, self.out, idx, np.argsort(bvals)[0]] & FG elif self.average: # Load the given dwi to get image data dwi = load_nifti(self.dwi._path) hdr = dwi.header mri = dwi.get_data() avg_bse = np.mean(mri[:, :, :, idx], axis=3) # Now write back the average bse save_nifti(self.out, avg_bse, dwi.affine, hdr) elif self.all: fslroi[self.dwi, self.out, idx, len(idx)] & FG else: raise Exception('No b0 image found. Check the bval file.') else: raise Exception("Invalid dwi format, must be a nifti image") if self.dwimask: ImageMath(3, self.out, 'm', self.out, self.dwimask)
def separateAllBshells(ref_csv, ref_bvals_file, ncpu=4, outPrefix=None): ref_bvals = read_bvals(ref_bvals_file) try: imgs, masks = read_imgs_masks(ref_csv) except: imgs = read_imgs(ref_csv) masks = None pool = Pool(int(ncpu)) for imgPath in imgs: pool.apply_async(separateBshells, kwds={ 'imgPath': imgPath, 'ref_bvals': ref_bvals }, error_callback=RAISE) pool.close() pool.join() if outPrefix: outPrefix = abspath(outPrefix) else: return for bval in ref_bvals: f = open(f'{outPrefix}_b{int(bval)}.csv', 'w') if masks: for imgPath, maskPath in zip(imgs, masks): inPrefix = abspath(imgPath).split('.nii')[0] bPrefix = inPrefix + f'_b{int(bval)}' f.write(f'{bPrefix}.nii.gz,{maskPath}\n') else: for imgPath in imgs: inPrefix = abspath(imgPath).split('.nii')[0] bPrefix = inPrefix + f'_b{int(bval)}' f.write(f'{bPrefix}.nii.gz\n') f.close()
def joinAllBshells(tar_csv, ref_bvals_file, separatedPrefix=None, ncpu=4): ref_bvals = read_bvals(ref_bvals_file) if tar_csv: try: imgs, _ = read_imgs_masks(tar_csv) except: imgs = read_imgs(tar_csv) pool = Pool(int(ncpu)) for imgPath in imgs: pool.apply_async(joinBshells, kwds=({ 'imgPath': imgPath, 'ref_bvals': ref_bvals, 'sep_prefix': separatedPrefix }), error_callback=RAISE) pool.close() pool.join()
def bet_mask(imgPath, maskPath, dim, bvalFile=None, thr=BET_THRESHOLD): with local.tempdir() as tmpdir: bsetmp = tmpdir / 'bse.nii.gz' if dim == 4: bvals = read_bvals(bvalFile) idx = np.where([bval < B0_THRESHOLD for bval in bvals])[0] if len(idx) >= 1: fslroi[imgPath, bsetmp, idx, 1] & FG bet[bsetmp, maskPath, '-m', '-n', '-f', thr] & FG else: raise Exception('No b0 image found. Check the bval file.') elif dim == 3: bet[imgPath, maskPath, '-m', '-n', '-f', thr] & FG else: raise ValueError('Input dimension should be 3 or 4')
def dwi_difference(imgPath_given_mat, imgPath_given_py, caselist): diff_list = [] total_diff = [] mean_diff = [] for case in caselist: imgPath_mat = imgPath_given_mat.replace('XYZ', case) imgPath_py = imgPath_given_py.replace('XYZ', case) # load python img_py_obj = nib.load(imgPath_py) img_py = img_py_obj.get_fdata() # keep only the first b0 bvals = np.array(read_bvals(imgPath_py.split('.nii')[0] + '.bval')) img_py = stack_one_b0(bvals, dwi=img_py) img_py = img_py.astype('float64') pyX, pyY, pyZ, _ = img_py.shape # load matlab img_mat = nib.load(imgPath_mat).get_fdata()[:pyX, :pyY, :pyZ, :] img_mat = img_mat.astype('float64') # obtain case wise difference diff = 2 * abs(img_py - img_mat) / (img_py + img_mat).clip(min=1.) * 100 diff_list.append(diff.flatten()) mean_diff.append(diff.mean()) # save the difference mask nib.Nifti1Image(diff, img_py_obj.affine, img_py_obj.header).to_filename( imgPath_mat.split('.nii')[0] + '_diff_mask.nii.gz') print( f'Mean of mean relative percentage difference over all the voxels: {np.mean(mean_diff)}' ) hist_calc(diff_list, bins=BINS)
def pre_process(input_file, target_list, b0_threshold=50.): from conversion import nifti_write, read_bvals from subprocess import Popen if path.isfile(input_file): # convert NRRD/NHDR to NIFIT as the first step # extract bse.py from just NIFTI later if input_file.endswith(SUFFIX_NRRD) | input_file.endswith(SUFFIX_NHDR): inPrefix = input_file.split('.')[0] nifti_write(input_file) input_file = inPrefix + '.nii.gz' inPrefix = input_file.split('.nii')[0] b0_nii = path.join(inPrefix + '_bse.nii.gz') dwi = nib.load(input_file) if len(dwi.shape) > 3: print("Extracting b0 volume...") bvals = np.array(read_bvals(input_file.split('.nii')[0] + '.bval')) where_b0 = np.where(bvals <= b0_threshold)[0] b0 = dwi.get_data()[..., where_b0].mean(-1) else: print("Loading b0 volume...") b0 = dwi.get_fdata() np.nan_to_num(b0).clip(min=0., out=b0) nib.Nifti1Image(b0, affine=dwi.affine, header=dwi.header).to_filename(b0_nii) target_list.append((b0_nii)) else: print("File not found ", input_file) sys.exit(1)
def _eddy_openmp(modData, modBvals, modBvecs, eddy_openmp_params): print('eddy_openmp/cuda parameters') print(eddy_openmp_params) print('') # eddy_openmp yields as many volumes as there are input volumes # this is the main output and consists of the input data after correction for # eddy currents, subject movement, and susceptibility if --topup was specified eddy_openmp[f'--imain={modData}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={modBvecs}', f'--bvals={modBvals}', f'--out={outPrefix}', f'--topup={topup_results}', eddy_openmp_params.split()] & FG # free space, see https://github.com/pnlbwh/pnlNipype/issues/82 if '--repol' in eddy_openmp_params: rm[f'{outPrefix}.eddy_outlier_free_data.nii.gz'] & FG bvals = np.array(read_bvals(modBvals)) ind= [i for i in range(len(bvals)) if bvals[i]>B0_THRESHOLD and bvals[i]<= REPOL_BSHELL_GREATER] if '--repol' in eddy_openmp_params and len(ind): print('\nDoing eddy_openmp/cuda again without --repol option ' f'to obtain eddy correction w/o outlier replacement for b<={REPOL_BSHELL_GREATER} shells\n') eddy_openmp_params = eddy_openmp_params.split() eddy_openmp_params.remove('--repol') print(eddy_openmp_params) print('') wo_repol_outDir = local.path(outPrefix).dirname.join('wo_repol') wo_repol_outDir.mkdir() wo_repol_outPrefix = pjoin(wo_repol_outDir, basename(outPrefix)) eddy_openmp[f'--imain={modData}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={modBvecs}', f'--bvals={modBvals}', f'--out={wo_repol_outPrefix}', f'--topup={topup_results}', eddy_openmp_params] & FG repol_bvecs = np.array(read_bvecs(outPrefix + '.eddy_rotated_bvecs')) wo_repol_bvecs = np.array(read_bvecs(wo_repol_outPrefix + '.eddy_rotated_bvecs')) merged_bvecs = repol_bvecs.copy() merged_bvecs[ind, :] = wo_repol_bvecs[ind, :] repol_data = load_nifti(outPrefix + '.nii.gz') wo_repol_data = load_nifti(wo_repol_outPrefix + '.nii.gz') merged_data = repol_data.get_fdata().copy() merged_data[..., ind] = wo_repol_data.get_fdata()[..., ind] save_nifti(outPrefix + '.nii.gz', merged_data, repol_data.affine, hdr=repol_data.header) # copy bval,bvec to have same prefix as that of eddy corrected volume write_bvecs(outPrefix + '.bvec', merged_bvecs) copyfile(modBvals, outPrefix + '.bval') # clean up rm['-r', wo_repol_outDir] & FG else: # copy bval,bvec to have same prefix as that of eddy corrected volume copyfile(outPrefix + '.eddy_rotated_bvecs', outPrefix + '.bvec') copyfile(modBvals, outPrefix + '.bval')
def main(self): # if self.force: # logging.info('Deleting previous output directory') # rm('-rf', self.outDir) temp= self.dwi_file.split(',') primaryVol= abspath(temp[0]) if len(temp)<2: raise AttributeError('Two volumes are required for --imain') else: secondaryVol= abspath(temp[1]) if self.b0_brain_mask: temp = self.b0_brain_mask.split(',') primaryMask = abspath(temp[0]) if len(temp) == 2: secondaryMask = abspath(temp[1]) else: secondaryMask = abspath(temp[0]) else: primaryMask=[] secondaryMask=[] # obtain 4D/3D info and time axis info dimension = load_nifti(primaryVol).header['dim'] dim1 = dimension[0] if dim1!=4: raise AttributeError('primary volume must be 4D, however, secondary can be 3D/4D') numVol1 = dimension[4] dimension = load_nifti(secondaryVol).header['dim'] dim2 = dimension[0] numVol2 = dimension[4] temp= self.bvals_file.split(',') if len(temp)>=1: primaryBval= abspath(temp[0]) if len(temp)==2: secondaryBval= abspath(temp[1]) elif len(temp)==1 and dim2==4: secondaryBval= primaryBval elif len(temp)==1 and dim2==3: secondaryBval=[] elif len(temp)==0: raise AttributeError('--bvals are required') temp= self.bvecs_file.split(',') if len(temp)>=1: primaryBvec= abspath(temp[0]) if len(temp)==2: secondaryBvec= abspath(temp[1]) elif len(temp) == 1 and dim2 == 4: secondaryBvec = primaryBvec elif len(temp)==1 and dim2==3: secondaryBvec=[] else: raise AttributeError('--bvecs are required') with TemporaryDirectory() as tmpdir: tmpdir= local.path(tmpdir) # mask both volumes, fslmaths can do that irrespective of dimension logging.info('Masking the volumes') primaryMaskedVol = tmpdir / 'primaryMasked.nii.gz' secondaryMaskedVol = tmpdir / 'secondaryMasked.nii.gz' if primaryMask: # mask the volume fslmaths[primaryVol, '-mas', primaryMask, primaryMaskedVol] & FG else: primaryMaskedVol= primaryVol if secondaryMask: # mask the volume fslmaths[secondaryVol, '-mas', secondaryMask, secondaryMaskedVol] & FG else: secondaryMaskedVol= secondaryVol logging.info('Extracting B0 from masked volumes') B0_PA= tmpdir / 'B0_PA.nii.gz' B0_AP= tmpdir / 'B0_AP.nii.gz' obtainB0(primaryMaskedVol, primaryBval, B0_PA, self.num_b0) if dim2==4: obtainB0(secondaryMaskedVol, secondaryBval, B0_AP, self.num_b0) else: B0_AP= secondaryMaskedVol B0_PA_AP_merged = tmpdir / 'B0_PA_AP_merged.nii.gz' with open(self.acqparams_file._path) as f: acqp= f.read().split('\n') logging.info('Writing acqparams.txt for topup') # firstDim: first acqp line should be replicated this number of times firstB0dim= load_nifti(str(B0_PA)).header['dim'][4] # secondDim: second acqp line should be replicated this number of times secondB0dim= load_nifti(str(B0_AP)).header['dim'][4] acqp_topup= tmpdir / 'acqp_topup.txt' with open(acqp_topup,'w') as f: for i in range(firstB0dim): f.write(acqp[0]+'\n') for i in range(secondB0dim): f.write(acqp[1]+'\n') logging.info('Merging B0_PA and BO_AP') fslmerge('-t', B0_PA_AP_merged, B0_PA, B0_AP) topup_params, applytopup_params, eddy_openmp_params= obtain_fsl_eddy_params(self.eddy_config_file._path) # Example for topup # === on merged b0 images === # https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide#Running_topup_on_the_b.3D0_volumes # topup --imain=both_b0 --datain=my_acq_param.txt --out=my_topup_results # === on all b0 images === # https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/TopupUsersGuide#Running_topup # topup --imain=all_my_b0_images.nii --datain=acquisition_parameters.txt --config=b02b0.cnf --out=my_output logging.info('Running topup') topup_results= tmpdir / 'topup_results' topup[f'--imain={B0_PA_AP_merged}', f'--datain={acqp_topup}', f'--out={topup_results}', '--verbose', topup_params.split()] & FG logging.info('Running applytopup') topupMask= tmpdir / 'topup_mask.nii.gz' # applytopup on primary4D,secondary4D/3D topupOut= tmpdir / 'topup_out.nii.gz' if dim2==4: applytopup[f'--imain={primaryMaskedVol},{secondaryMaskedVol}', f'--datain={self.acqparams_file}', '--inindex=1,2', f'--topup={topup_results}', f'--out={topupOut}', '--verbose', applytopup_params.split()] & FG else: applytopup[f'--imain={B0_PA},{B0_AP}', f'--datain={self.acqparams_file}', '--inindex=1,2', f'--topup={topup_results}', f'--out={topupOut}', '--verbose', applytopup_params.split()] & FG topupOutMean= tmpdir / 'topup_out_mean.nii.gz' fslmaths[topupOut, '-Tmean', topupOutMean] & FG bet[topupOutMean, topupMask._path.split('_mask.nii.gz')[0], '-m', '-n'] & FG # another approach could be # threshold mean of primary,secondary mask at 0.5 and obtain modified mask, use that mask for eddy_openmp # fslmerge[topupMask, '-t', primaryMask, secondaryMask] & FG # fslmaths[topupMask, '-Tmean', topupMask] & FG # fslmaths[topupMask, '-thr', '0.5', topupMask, '-odt' 'char'] & FG logging.info('Writing index.txt for topup') indexFile= tmpdir / 'index.txt' with open(indexFile, 'w') as f: for i in range(numVol1): f.write('1\n') outPrefix = tmpdir / basename(primaryVol).split('.')[0] + '_Ep_Ed' temp = self.whichVol.split(',') if len(temp)==1 and temp[0]=='1': # correct only primary4D volume eddy_openmp[f'--imain={primaryMaskedVol}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={primaryBvec}', f'--bvals={primaryBval}', f'--out={outPrefix}', f'--topup={topup_results}', '--verbose', eddy_openmp_params.split()] & FG elif len(temp)==2 and temp[1]=='2': # sylvain would like to correct both primary and secondary volumes with open(indexFile, 'a') as f: for i in range(numVol2): f.write('2\n') # join both bvalFiles bvals1= read_bvals(primaryBval) if dim2==4 and not secondaryBval: bvals2= bvals1.copy() elif dim2==4 and secondaryBval: bvals2= read_bvals(secondaryBval) elif dim2==3: bvals2=[0] combinedBvals = tmpdir / 'combinedBvals.txt' write_bvals(combinedBvals, bvals1+bvals2) # join both bvecFiles bvecs1= read_bvecs(primaryBvec) if dim2==4 and not secondaryBvec: bvecs2= bvecs1.copy() elif dim2==4 and secondaryBvec: bvecs2= read_bvecs(secondaryBvec) elif dim2==3: bvecs2=[[0,0,0]] # join both bvecFiles combinedBvecs = tmpdir / 'combinedBvecs.txt' write_bvecs(combinedBvecs, bvecs1+bvecs2) combinedData= tmpdir / 'combinedData.nii.gz' fslmerge('-t', combinedData, primaryMaskedVol, secondaryMaskedVol) eddy_openmp[f'--imain={combinedData}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={combinedBvecs}', f'--bvals={combinedBvals}', f'--out={outPrefix}', f'--topup={topup_results}', '--verbose', eddy_openmp_params.split()] & FG else: raise ValueError('Invalid --whichVol') # copy bval,bvec to have same prefix as that of eddy corrected volume copyfile(outPrefix+'.eddy_rotated_bvecs', outPrefix+'.bvec') copyfile(primaryBval, outPrefix+'.bval') # rename topupMask to have same prefix as that of eddy corrected volume topupMask.move(outPrefix+'_mask.nii.gz') tmpdir.move(self.outDir)
def _eddy_openmp(modData, modBvals, modBvecs, eddy_openmp_params): print('eddy_openmp/cuda parameters') print(eddy_openmp_params) print('') eddy_openmp[f'--imain={modData}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={modBvecs}', f'--bvals={modBvals}', f'--out={outPrefix}', f'--topup={topup_results}', eddy_openmp_params.split()] & FG bvals = np.array(read_bvals(modBvals)) ind = [ i for i in range(len(bvals)) if bvals[i] > B0_THRESHOLD and bvals[i] <= REPOL_BSHELL_GREATER ] if '--repol' in eddy_openmp_params and len(ind): print( '\nDoing eddy_openmp/cuda again without --repol option ' 'to obtain eddy correction w/o outlier replacement for b<=500 shells\n' ) eddy_openmp_params = eddy_openmp_params.split() eddy_openmp_params.remove('--repol') print(eddy_openmp_params) print('') wo_repol_outDir = local.path(outPrefix).dirname.join( 'wo_repol') wo_repol_outDir.mkdir() wo_repol_outPrefix = pjoin(wo_repol_outDir, basename(outPrefix)) eddy_openmp[f'--imain={modData}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={modBvecs}', f'--bvals={modBvals}', f'--out={wo_repol_outPrefix}', f'--topup={topup_results}', eddy_openmp_params] & FG repol_bvecs = np.array( read_bvecs(outPrefix + '.eddy_rotated_bvecs')) wo_repol_bvecs = np.array( read_bvecs(wo_repol_outPrefix + '.eddy_rotated_bvecs')) merged_bvecs = repol_bvecs.copy() merged_bvecs[ind, :] = wo_repol_bvecs[ind, :] repol_data = load(outPrefix + '.nii.gz') wo_repol_data = load(wo_repol_outPrefix + '.nii.gz') merged_data = repol_data.get_fdata().copy() merged_data[..., ind] = wo_repol_data.get_fdata()[..., ind] save_nifti(outPrefix + '.nii.gz', merged_data, repol_data.affine, hdr=repol_data.header) # copy bval,bvec to have same prefix as that of eddy corrected volume write_bvecs(outPrefix + '.bvec', merged_bvecs) copyfile(modBvals, outPrefix + '.bval') else: # copy bval,bvec to have same prefix as that of eddy corrected volume copyfile(outPrefix + '.eddy_rotated_bvecs', outPrefix + '.bvec') copyfile(modBvals, outPrefix + '.bval')
def main(self): from plumbum.cmd import eddy_openmp if self.useGpu: try: from plumbum.cmd import nvcc nvcc['--version'] & FG print('\nCUDA found, looking for available GPU\n') from GPUtil import getFirstAvailable getFirstAvailable() print('available GPU found, looking for eddy_cuda executable\n' 'make sure you have created a softlink according to ' 'https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide') from plumbum.cmd import eddy_cuda as eddy_openmp print('\neddy_cuda executable found\n') except: print( 'nvcc, available GPU, and/or eddy_cuda was not found, using eddy_openmp' ) prefix = self.dwi_file.name.split('.')[0] self.outDir.mkdir() outPrefix = pjoin(self.outDir._path, prefix + '_Ed') if not self.b0_brain_mask: logging.info('Mask not provided, creating mask ...') self.b0_brain_mask = outPrefix + '_mask.nii.gz' bet_mask(self.dwi_file, self.b0_brain_mask, 4, bvalFile=self.bvals_file, BET_THRESHOLD=self.betThreshold) _, _, eddy_openmp_params = obtain_fsl_eddy_params( self.eddy_config_file._path) print('eddy_openmp/cuda parameters') print(eddy_openmp_params) print('') eddy_openmp[f'--imain={self.dwi_file}', f'--mask={self.b0_brain_mask}', f'--acqp={self.acqparams_file}', f'--index={self.index_file}', f'--bvecs={self.bvecs_file}', f'--bvals={self.bvals_file}', f'--out={outPrefix}', eddy_openmp_params.split()] & FG # free space, see https://github.com/pnlbwh/pnlNipype/issues/82 if '--repol' in eddy_openmp_params: rm[f'{outPrefix}.eddy_outlier_free_data.nii.gz'] & FG bvals = np.array(read_bvals(self.bvals_file)) ind = [ i for i in range(len(bvals)) if bvals[i] > B0_THRESHOLD and bvals[i] <= REPOL_BSHELL_GREATER ] if '--repol' in eddy_openmp_params and len(ind): print( '\nDoing eddy_openmp/cuda again without --repol option ' 'to obtain eddy correction w/o outlier replacement for b<=500 shells\n' ) eddy_openmp_params = eddy_openmp_params.split() eddy_openmp_params.remove('--repol') print(eddy_openmp_params) print('') wo_repol_outDir = self.outDir.join('wo_repol') wo_repol_outDir.mkdir() wo_repol_outPrefix = pjoin(wo_repol_outDir, prefix + '_Ed') eddy_openmp[ f'--imain={self.dwi_file}', f'--mask={self.b0_brain_mask}', f'--acqp={self.acqparams_file}', f'--index={self.index_file}', f'--bvecs={self.bvecs_file}', f'--bvals={self.bvals_file}', f'--out={wo_repol_outPrefix}', eddy_openmp_params] & FG repol_bvecs = np.array( read_bvecs(outPrefix + '.eddy_rotated_bvecs')) wo_repol_bvecs = np.array( read_bvecs(wo_repol_outPrefix + '.eddy_rotated_bvecs')) merged_bvecs = repol_bvecs.copy() merged_bvecs[ind, :] = wo_repol_bvecs[ind, :] repol_data = load_nifti(outPrefix + '.nii.gz') wo_repol_data = load_nifti(wo_repol_outPrefix + '.nii.gz') merged_data = repol_data.get_fdata().copy() merged_data[..., ind] = wo_repol_data.get_fdata()[..., ind] save_nifti(outPrefix + '.nii.gz', merged_data, repol_data.affine, hdr=repol_data.header) # copy bval,bvec to have same prefix as that of eddy corrected volume write_bvecs(outPrefix + '.bvec', merged_bvecs) copyfile(self.bvals_file, outPrefix + '.bval') # clean up rm['-r', wo_repol_outDir] & FG else: # copy bval,bvec to have same prefix as that of eddy corrected volume copyfile(outPrefix + '.eddy_rotated_bvecs', outPrefix + '.bvec') copyfile(self.bvals_file, outPrefix + '.bval')
def main(self): if self.N_proc == '-1': self.N_proc = N_CPU # check directory existence check_dir(self.templatePath, self.force) ## check consistency of b-shells and spatial resolution ref_bvals_file = pjoin(self.templatePath, 'ref_bshell_bvalues.txt') ref_res_file = pjoin(self.templatePath, 'ref_res_file.npy') if self.ref_csv: if isfile(ref_bvals_file) and isfile(ref_res_file): remove(ref_bvals_file) remove(ref_res_file) consistencyCheck(self.ref_csv, ref_bvals_file, ref_res_file) if self.target_csv: consistencyCheck(self.target_csv, ref_bvals_file, ref_res_file) ## separate b-shells if self.ref_csv: refListOutPrefix = separateShellsWrapper(self.ref_csv, ref_bvals_file, self.N_proc) if self.target_csv: tarListOutPrefix = separateShellsWrapper(self.target_csv, ref_bvals_file, self.N_proc) ## define variables for template creation and data harmonization # variables common to all ref_bvals pipeline_vars = [ '--tar_name', self.target, '--nshm', self.N_shm, '--nproc', self.N_proc, '--template', self.templatePath, ] if self.reference: pipeline_vars.append(f'--ref_name {self.reference}') if self.N_zero: pipeline_vars.append(f'--nzero {self.N_zero}') if self.bvalMap: pipeline_vars.append(f'--bvalMap {self.bvalMap}') if self.resample: pipeline_vars.append(f'--resample {self.resample}') if self.denoise: pipeline_vars.append('--denoise') if self.travelHeads: pipeline_vars.append('--travelHeads') if self.force: pipeline_vars.append('--force') if self.debug: pipeline_vars.append('--debug') if self.verbose: pipeline_vars.append('--verbose') # the b-shell bvalues are sorted in descending order because we want to perform registration with highest bval ref_bvals = read_bvals(ref_bvals_file)[::-1] for bval in ref_bvals[:-1]: # pass the last bval which is 0. if self.create and not self.process: print('## template creation ##') check_call((' ').join([ pjoin(SCRIPTDIR, 'harmonization.py'), '--tar_list', tarListOutPrefix + f'_b{int(bval)}.csv', '--bshell_b', str(int(bval)), '--ref_list', refListOutPrefix + f'_b{int(bval)}.csv', '--create' ] + pipeline_vars), shell=True) elif not self.create and self.process: print('## data harmonization ##') check_call((' ').join([ pjoin(SCRIPTDIR, 'harmonization.py'), '--tar_list', tarListOutPrefix + f'_b{int(bval)}.csv', f'--ref_list {refListOutPrefix}_b{int(bval)}.csv' if self. ref_csv else '', '--bshell_b', str(int(bval)), '--process' ] + pipeline_vars), shell=True) elif self.create and self.process: check_call((' ').join([ pjoin(SCRIPTDIR, 'harmonization.py'), '--tar_list', tarListOutPrefix + f'_b{int(bval)}.csv', '--bshell_b', str(int(bval)), '--ref_list', refListOutPrefix + f'_b{int(bval)}.csv', '--create', '--process' ] + pipeline_vars), shell=True) if '--force' in pipeline_vars: pipeline_vars.remove('--force') ## join harmonized data if self.process: joinAllBshells(self.target_csv, ref_bvals_file, 'harmonized_', self.N_proc) if self.debug and self.ref_csv: joinAllBshells(self.ref_csv, ref_bvals_file, 'reconstructed_', self.N_proc)
def main(self): from plumbum.cmd import eddy_openmp # cli.NonexistentPath is already making sure it does not exist self.outDir.mkdir() if self.useGpu: try: from plumbum.cmd import nvcc nvcc['--version'] & FG print('\nCUDA found, looking for available GPU\n') from GPUtil import getFirstAvailable getFirstAvailable() print('available GPU found, looking for eddy_cuda executable\n' 'make sure you have created a softlink according to ' 'https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide') from plumbum.cmd import eddy_cuda as eddy_openmp print('\neddy_cuda executable found\n') except: print('nvcc, available GPU, and/or eddy_cuda was not found, using eddy_openmp') def _eddy_openmp(modData, modBvals, modBvecs, eddy_openmp_params): print('eddy_openmp/cuda parameters') print(eddy_openmp_params) print('') # eddy_openmp yields as many volumes as there are input volumes # this is the main output and consists of the input data after correction for # eddy currents, subject movement, and susceptibility if --topup was specified eddy_openmp[f'--imain={modData}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={modBvecs}', f'--bvals={modBvals}', f'--out={outPrefix}', f'--topup={topup_results}', eddy_openmp_params.split()] & FG # free space, see https://github.com/pnlbwh/pnlNipype/issues/82 if '--repol' in eddy_openmp_params: rm[f'{outPrefix}.eddy_outlier_free_data.nii.gz'] & FG bvals = np.array(read_bvals(modBvals)) ind= [i for i in range(len(bvals)) if bvals[i]>B0_THRESHOLD and bvals[i]<= REPOL_BSHELL_GREATER] if '--repol' in eddy_openmp_params and len(ind): print('\nDoing eddy_openmp/cuda again without --repol option ' f'to obtain eddy correction w/o outlier replacement for b<={REPOL_BSHELL_GREATER} shells\n') eddy_openmp_params = eddy_openmp_params.split() eddy_openmp_params.remove('--repol') print(eddy_openmp_params) print('') wo_repol_outDir = local.path(outPrefix).dirname.join('wo_repol') wo_repol_outDir.mkdir() wo_repol_outPrefix = pjoin(wo_repol_outDir, basename(outPrefix)) eddy_openmp[f'--imain={modData}', f'--mask={topupMask}', f'--acqp={self.acqparams_file}', f'--index={indexFile}', f'--bvecs={modBvecs}', f'--bvals={modBvals}', f'--out={wo_repol_outPrefix}', f'--topup={topup_results}', eddy_openmp_params] & FG repol_bvecs = np.array(read_bvecs(outPrefix + '.eddy_rotated_bvecs')) wo_repol_bvecs = np.array(read_bvecs(wo_repol_outPrefix + '.eddy_rotated_bvecs')) merged_bvecs = repol_bvecs.copy() merged_bvecs[ind, :] = wo_repol_bvecs[ind, :] repol_data = load_nifti(outPrefix + '.nii.gz') wo_repol_data = load_nifti(wo_repol_outPrefix + '.nii.gz') merged_data = repol_data.get_fdata().copy() merged_data[..., ind] = wo_repol_data.get_fdata()[..., ind] save_nifti(outPrefix + '.nii.gz', merged_data, repol_data.affine, hdr=repol_data.header) # copy bval,bvec to have same prefix as that of eddy corrected volume write_bvecs(outPrefix + '.bvec', merged_bvecs) copyfile(modBvals, outPrefix + '.bval') # clean up rm['-r', wo_repol_outDir] & FG else: # copy bval,bvec to have same prefix as that of eddy corrected volume copyfile(outPrefix + '.eddy_rotated_bvecs', outPrefix + '.bvec') copyfile(modBvals, outPrefix + '.bval') # if self.force: # logging.info('Deleting previous output directory') # rm('-rf', self.outDir) temp= self.dwi_file.split(',') primaryVol= abspath(temp[0]) if len(temp)<2: raise AttributeError('Two volumes are required for --imain') else: secondaryVol= abspath(temp[1]) primaryMask=[] secondaryMask=[] if self.b0_brain_mask: temp = self.b0_brain_mask.split(',') primaryMask = abspath(temp[0]) if len(temp) == 2: secondaryMask = abspath(temp[1]) # obtain 4D/3D info and time axis info dimension = load_nifti(primaryVol).header['dim'] dim1 = dimension[0] if dim1!=4: raise AttributeError('Primary volume must be 4D, however, secondary can be 3D/4D') numVol1 = dimension[4] dimension = load_nifti(secondaryVol).header['dim'] dim2 = dimension[0] numVol2 = dimension[4] temp= self.bvals_file.split(',') if len(temp)>=1: primaryBval= abspath(temp[0]) if len(temp)==2: secondaryBval= abspath(temp[1]) elif len(temp)==1 and dim2==4: secondaryBval= primaryBval elif len(temp)==1 and dim2==3: secondaryBval=[] elif len(temp)==0: raise AttributeError('--bvals are required') temp= self.bvecs_file.split(',') if len(temp)>=1: primaryBvec= abspath(temp[0]) if len(temp)==2: secondaryBvec= abspath(temp[1]) elif len(temp) == 1 and dim2 == 4: secondaryBvec = primaryBvec elif len(temp)==1 and dim2==3: secondaryBvec=[] else: raise AttributeError('--bvecs are required') with local.cwd(self.outDir): # mask both volumes, fslmaths can do that irrespective of dimension logging.info('Masking the volumes') primaryMaskedVol = 'primary_masked.nii.gz' secondaryMaskedVol = 'secondary_masked.nii.gz' if primaryMask: # mask the volume fslmaths[primaryVol, '-mas', primaryMask, primaryMaskedVol] & FG else: primaryMaskedVol= primaryVol if secondaryMask: # mask the volume fslmaths[secondaryVol, '-mas', secondaryMask, secondaryMaskedVol] & FG else: secondaryMaskedVol= secondaryVol logging.info('Extracting B0 from masked volumes') B0_PA= 'B0_PA.nii.gz' B0_AP= 'B0_AP.nii.gz' obtainB0(primaryMaskedVol, primaryBval, B0_PA, self.num_b0) if dim2==4: obtainB0(secondaryMaskedVol, secondaryBval, B0_AP, self.num_b0) else: B0_AP= secondaryMaskedVol B0_PA_AP_merged = 'B0_PA_AP_merged.nii.gz' with open(self.acqparams_file._path) as f: acqp= f.read().strip().split('\n') if len(acqp)!=2: raise ValueError('The acquisition parameter file must have exactly two lines') logging.info('Writing acqparams.txt for topup') # firstDim: first acqp line should be replicated this number of times firstB0dim= load_nifti(str(B0_PA)).header['dim'][4] # secondDim: second acqp line should be replicated this number of times secondB0dim= load_nifti(str(B0_AP)).header['dim'][4] acqp_topup= 'acqp_topup.txt' with open(acqp_topup,'w') as f: for i in range(firstB0dim): f.write(acqp[0]+'\n') for i in range(secondB0dim): f.write(acqp[1]+'\n') logging.info('Merging B0_PA and BO_AP') fslmerge('-t', B0_PA_AP_merged, B0_PA, B0_AP) topup_params, applytopup_params, eddy_openmp_params= obtain_fsl_eddy_params(self.eddy_config_file._path) # Example for topup # === on merged b0 images === # https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/eddy/UsersGuide#Running_topup_on_the_b.3D0_volumes # topup --imain=P2A_A2P_b0 --datain=acqparams.txt --config=b02b0.cnf --out=my_output --iout=my_output # # === on all b0 images === # https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/TopupUsersGuide#Running_topup # topup --imain=all_b0s --datain=acqparams.txt --config=b02b0.cnf --out=my_output --iout=my_output logging.info('Running topup') topup_results= 'topup_out' topupOut= 'topup_out.nii.gz' # topup --iout yields as many volumes as there are input volumes # --iout specifies the name of a 4D image file that contains unwarped and movement corrected images. # each volume in the --imain will have a corresponding corrected volume in --iout. # --iout is used for creating modified mask only # when primary4D,secondary4D/3D are already masked, this will be useful topup[f'--imain={B0_PA_AP_merged}', f'--datain={acqp_topup}', f'--out={topup_results}', f'--iout={topupOut}', topup_params.split()] & FG # provide topupOutMean for quality checking topupOutMean= 'topup_out_mean.nii.gz' fslmaths[topupOut, '-Tmean', topupOutMean] & FG logging.info('Running applytopup') # applytopup always yields one output file regardless of one or two input files # if two input files are provided, the resulting undistorted file will be a combination of the two # containing only as many volumes as there are in one file # B0_PA_correct, B0_AP_correct are for quality checking only # primaryMaskCorrect, secondaryMaskCorrect will be associated masks B0_PA_correct= 'B0_PA_corrected.nii.gz' applytopup[f'--imain={B0_PA}', f'--datain={self.acqparams_file}', '--inindex=1', f'--topup={topup_results}', f'--out={B0_PA_correct}', applytopup_params.split()] & FG B0_AP_correct= 'B0_AP_corrected.nii.gz' applytopup[f'--imain={B0_AP}', f'--datain={self.acqparams_file}', '--inindex=2', f'--topup={topup_results}', f'--out={B0_AP_correct}', applytopup_params.split()] & FG B0_PA_AP_corrected_merged= 'B0_PA_AP_corrected_merged' fslmerge('-t', B0_PA_AP_corrected_merged, B0_PA_correct, B0_AP_correct) fslmaths[B0_PA_AP_corrected_merged, '-Tmean', 'B0_PA_AP_corrected_mean'] & FG topupMask= 'topup_mask.nii.gz' # calculate topup mask if primaryMask and secondaryMask: fslmaths[primaryMask, '-mul', '1', primaryMask, '-odt', 'float'] fslmaths[secondaryMask, '-mul', '1', secondaryMask, '-odt', 'float'] applytopup_params+=' --interp=trilinear' # this straightforward way could be used ''' applytopup[f'--imain={primaryMask},{secondaryMask}', f'--datain={self.acqparams_file}', '--inindex=1,2', f'--topup={topup_results}', f'--out={topupMask}', applytopup_params.split()] & FG ''' # but let's do it step by step in order to have more control of the process # binarise the mean of corrected primary,secondary mask to obtain modified mask # use that mask for eddy_openmp primaryMaskCorrect = 'primary_mask_corrected.nii.gz' applytopup[f'--imain={primaryMask}', f'--datain={self.acqparams_file}', '--inindex=1', f'--topup={topup_results}', f'--out={primaryMaskCorrect}', applytopup_params.split()] & FG secondaryMaskCorrect = 'secondary_mask_corrected.nii.gz' applytopup[f'--imain={secondaryMask}', f'--datain={self.acqparams_file}', '--inindex=2', f'--topup={topup_results}', f'--out={secondaryMaskCorrect}', applytopup_params.split()] & FG fslmerge('-t', topupMask, primaryMaskCorrect, secondaryMaskCorrect) temp= load_nifti(topupMask) data= temp.get_fdata() data= abs(data[...,0])+ abs(data[...,1]) data[data!=0]= 1 # filter the mask to smooth edges # scale num of erosion followed by scale num of dilation # the greater the scale, the smoother the edges # scale=2 seems sufficient data= single_scale(data, int(self.scale)) save_nifti(topupMask, data.astype('uint8'), temp.affine, temp.header) else: # this block assumes the primary4D,secondary4D/3D are already masked # then toupOutMean is also masked # binarise the topupOutMean to obtain modified mask # use that mask for eddy_openmp # fslmaths[topupOutMean, '-bin', topupMask, '-odt', 'char'] & FG # if --mask is not provided at all, this block creates a crude mask # apply bet on the mean of topup output to obtain modified mask # use that mask for eddy_openmp bet[topupOutMean, topupMask.split('_mask.nii.gz')[0], '-m', '-n'] & FG logging.info('Writing index.txt for topup') indexFile= 'index.txt' with open(indexFile, 'w') as f: for i in range(numVol1): f.write('1\n') outPrefix = basename(primaryVol).split('.nii')[0] # remove _acq- outPrefix= outPrefix.replace('_acq-PA','') outPrefix= outPrefix.replace('_acq-AP','') # find dir field if '_dir-' in primaryVol and '_dir-' in secondaryVol and self.whichVol == '1,2': dir= load_nifti(primaryVol).shape[3]+ load_nifti(secondaryVol).shape[3] outPrefix= local.path(re.sub('_dir-(.+?)_', f'_dir-{dir}_', outPrefix)) outPrefix = outPrefix + '_EdEp' with open('.outPrefix.txt', 'w') as f: f.write(outPrefix) temp = self.whichVol.split(',') if len(temp)==1 and temp[0]=='1': # correct only primary4D volume _eddy_openmp(primaryMaskedVol, primaryBval, primaryBvec, eddy_openmp_params) elif len(temp)==2 and temp[1]=='2': # sylvain would like to correct both primary and secondary volumes with open(indexFile, 'a') as f: for i in range(numVol2): f.write('2\n') # join both bvalFiles bvals1= read_bvals(primaryBval) if dim2==4 and not secondaryBval: bvals2= bvals1.copy() elif dim2==4 and secondaryBval: bvals2= read_bvals(secondaryBval) elif dim2==3: bvals2=[0] combinedBvals = 'combinedBvals.txt' write_bvals(combinedBvals, bvals1+bvals2) # join both bvecFiles bvecs1= read_bvecs(primaryBvec) if dim2==4 and not secondaryBvec: bvecs2= bvecs1.copy() elif dim2==4 and secondaryBvec: bvecs2= read_bvecs(secondaryBvec) elif dim2==3: bvecs2=[[0,0,0]] # join both bvecFiles combinedBvecs = 'combinedBvecs.txt' write_bvecs(combinedBvecs, bvecs1+bvecs2) combinedData= 'combinedData.nii.gz' fslmerge('-t', combinedData, primaryMaskedVol, secondaryMaskedVol) _eddy_openmp(combinedData, combinedBvals, combinedBvecs, eddy_openmp_params) else: raise ValueError('Invalid --whichVol') # rename topupMask to have same prefix as that of eddy corrected volume move(topupMask, outPrefix + '_mask.nii.gz')