Exemple #1
0
    def main(self):

        with TemporaryDirectory() as tmpdir:
            tmpdir = local.path(tmpdir)
            shortdwi = tmpdir / 'dwiShort.nii.gz'
            shortmask = tmpdir / 'maskShort.nii.gz'

            tmpdwi = tmpdir / 'dwi.nhdr'
            tmpdwimask = tmpdir / 'dwimask.nhdr'

            # TODO when UKFTractography supports float32, it should be removed
            # typecast to short
            short = load_nifti(self.dwi._path)
            save_nifti(shortdwi._path,
                       short.get_data().astype('int16'), short.affine,
                       short.header)

            short = load_nifti(self.dwimask._path)
            save_nifti(shortmask._path,
                       short.get_data().astype('int16'), short.affine,
                       short.header)

            # convert the dwi to NRRD
            nhdr_write(shortdwi._path, self.bvalFile._path,
                       self.bvecFile._path, tmpdwi._path)

            # convert the mask to NRRD
            nhdr_write(shortmask._path, None, None, tmpdwimask._path)

            key_val_pair = []

            if self.givenParams:
                key_val_pair = self.givenParams.split(',')

                for i in range(
                        0,
                        len(ukfdefaults) - 1, 2
                ):  # -1 to pass --recordTensors which is always a default

                    try:
                        ind = key_val_pair.index(ukfdefaults[i])
                        ukfdefaults[i + 1] = key_val_pair[ind + 1]
                        # since ukfdefault[i+1] has been already replaced by key_val_pair[ind+1]
                        # remove key_val_pair[ind] and [ind+1] to prevent double specification
                        key_val_pair[ind:ind + 2] = []
                    except ValueError:
                        pass

            params = [
                '--dwiFile', tmpdwi, '--maskFile', tmpdwimask, '--seedsFile',
                tmpdwimask, '--tracts', self.out
            ] + list(ukfdefaults) + key_val_pair

            logging.info('Peforming UKF tractography of {}'.format(tmpdwi))
            UKFTractography[params] & FG
Exemple #2
0
    def main(self):

        prefix = self.img.name.split('.')[0]
        directory = self.img.parent

        if self.out is None:
            self.out = os.path.join(directory, prefix)

        # bet changed in FSL 6.0.1, it creates a mask for every volume in 4D
        # if the image is 4D, baseline image should be extracted first

        dim = load_nifti(self.img._path).header['dim'][0]

        if dim == 4:
            if not self.bval_file:
                self.bval_file = os.path.join(directory, prefix + '.bval')

            bet_mask(self.img._path,
                     self.out,
                     4,
                     self.bval_file,
                     thr=self.bet_threshold)

        else:
            bet_mask(self.img._path, self.out, 3, thr=self.bet_threshold)
Exemple #3
0
def maskfilter(maskPath, scale, filtered_maskPath):
    '''
    This python executable replicates the functionality of
    https://github.com/MRtrix3/mrtrix3/blob/master/core/filter/mask_clean.h
    It performs a few erosion and dilation to remove islands of non-brain region in a brain mask.
    '''

    mask = load_nifti(maskPath)

    filtered_mask = single_scale(mask.get_fdata(), scale)

    save_nifti(filtered_maskPath, filtered_mask, mask.affine, mask.header)
Exemple #4
0
def fuseWeightedAvg(labels, weights, out, target_header):

    # for each label, fuse warped labelmaps to compute output labelmap
    print("Apply weights to warped training {} et al., fuse, and threshold".format(labels[0]))
    data= np.zeros(target_header['dim'][1:4], dtype= 'float32')
    for label, w in zip(labels, weights):
        data+= load_nifti(label._path).get_data()*w


    # out is {labelname}.nii.gz
    save_nifti(out, ((data>0.5)*1).astype('uint8'), target_header.get_best_affine(), target_header)

    print("Made labelmap: " + out)
Exemple #5
0
def fuseAvg(labels, out, target_header):
    from plumbum.cmd import AverageImages

    with TemporaryDirectory() as tmpdir:
        nii = local.path(tmpdir) / 'avg.nii.gz'
        AverageImages['3', nii, '0', labels] & FG

        img= load_nifti(nii._path)
        # Binary operation, if out>0.5, pipe the output and save as {labelname}.nii.gz
        # out is {labelname}.nii.gz
        save_nifti(out, ((img.get_data()>0.5)*1).astype('uint8'), target_header.get_best_affine(), target_header)


    print("Made labelmap: " + out)
Exemple #6
0
    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)
Exemple #7
0
def fuseWeightedAvg(labels, weights, out):

    # for each label, fuse warped labelmaps to compute output labelmap
    print("Apply weights to warped training {} et al., fuse, and threshold".format(labels[0]))
    init= True
    for label, w in zip(labels, weights):
        img= load_nifti(label)
        if init:
            data=img.get_data()*w
            affine= img.affine
            init= False
        else:
            data+=img.get_data()*w



    # out is {labelname}.nii.gz
    save_nifti(out, ((data>0.5)*1).astype('uint8'), affine, img.header)

    print("Made labelmap: " + out)
    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)
Exemple #9
0
    def main(self):

        with tempfile.TemporaryDirectory() as tmpdir:

            tmpdir = local.path(tmpdir)

            b0masked = tmpdir / "b0masked.nii.gz"  # Sylvain wants both
            b0maskedbrain = tmpdir / "b0maskedbrain.nii.gz"

            t2masked = tmpdir / 't2masked.nii.gz'
            print('Masking the T2')
            ImageMath(3, t2masked, 'm', self.t2, self.t2mask)

            brain = tmpdir / "brain.nii.gz"
            wmparc = tmpdir / "wmparc.nii.gz"

            brainmgz = self.parent.fsdir / 'mri/brain.mgz'
            wmparcmgz = self.parent.fsdir / 'mri/wmparc.mgz'

            wmparcindwi = tmpdir / 'wmparcInDwi.nii.gz'  # Sylvain wants both
            wmparcinbrain = tmpdir / 'wmparcInBrain.nii.gz'

            print(
                "Making brain.nii.gz and wmparc.nii.gz from their mgz versions"
            )

            vol2vol = local[self.parent.fshome / 'bin/mri_vol2vol']
            label2vol = local[self.parent.fshome / 'bin/mri_label2vol']

            with local.env(SUBJECTS_DIR=''):
                vol2vol('--mov', brainmgz, '--targ', brainmgz, '--regheader',
                        '--o', brain)
                label2vol('--seg', wmparcmgz, '--temp', brainmgz,
                          '--regheader', wmparcmgz, '--o', wmparc)

            print('Extracting B0 from DWI and masking it')
            check_call((' ').join([
                pjoin(FILEDIR, 'bse.py'), '-i', self.parent.dwi, '-m',
                self.parent.dwimask, '-o', b0masked
            ]),
                       shell=True)
            print('Made masked B0')

            # rigid registration from t2 to brain.nii.gz
            pre = tmpdir / 'BrainToT2'
            BrainToT2Affine = pre + '0GenericAffine.mat'

            print('Computing rigid registration from brain.nii.gz to t2')
            rigid_registration(3, brain, t2masked, pre)
            # generates three files for rigid registration:
            # pre0GenericAffine.mat  preInverseWarped.nii.gz  preWarped.nii.gz

            # generates five files for default(rigid+affine+deformable syn) registration:
            # pre0GenericAffine.mat  pre1Warp.nii.gz  preWarped.nii.gz   pre1InverseWarp.nii.gz  preInverseWarped.nii.gz

            dwi_res = load_nifti(
                str(b0masked)).header['pixdim'][1:4].round(decimals=2)
            brain_res = load_nifti(
                str(brain)).header['pixdim'][1:4].round(decimals=2)
            print(f'DWI resolution: {dwi_res}')
            print(f'FreeSurfer brain resolution: {brain_res}')

            print('Registering wmparc to B0 through T2')
            registerFs2Dwi_T2(tmpdir, 'fsbrainToT2ToB0', b0masked, t2masked,
                              BrainToT2Affine, wmparc, wmparcindwi)

            if (dwi_res != brain_res).any():
                print(
                    'DWI resolution is different from FreeSurfer brain resolution'
                )
                print(
                    'wmparc wil be registered to both DWI and brain resolution'
                )
                print(
                    'Check output files wmparcInDwi.nii.gz and wmparcInBrain.nii.gz'
                )

                print('Resampling B0 to brain resolution')

                ResampleImageBySpacing('3', b0masked, b0maskedbrain,
                                       brain_res.tolist())

                print('Registering wmparc to resampled B0')
                registerFs2Dwi_T2(tmpdir, 'fsbrainToT2ToResampledB0',
                                  b0maskedbrain, t2masked, BrainToT2Affine,
                                  wmparc, wmparcinbrain)

            # copying images to outDir
            b0masked.copy(self.parent.out)
            wmparcindwi.copy(self.parent.out)

            if b0maskedbrain.exists():
                b0maskedbrain.copy(self.parent.out)
                wmparcinbrain.copy(self.parent.out)

            if self.parent.debug:
                tmpdir.copy(self.parent.out,
                            'fs2dwi-debug-' + str(os.getpid()))

        print('See output files in ', self.parent.out._path)
Exemple #10
0
    def main(self):

        with tempfile.TemporaryDirectory() as tmpdir:

            tmpdir = local.path(tmpdir)

            b0masked = tmpdir / "b0masked.nii.gz"  # Sylvain wants both
            b0maskedbrain = tmpdir / "b0maskedbrain.nii.gz"

            brain = tmpdir / "brain.nii.gz"
            wmparc = tmpdir / "wmparc.nii.gz"

            brainmgz = self.parent.fsdir / 'mri/brain.mgz'
            wmparcmgz = self.parent.fsdir / 'mri/wmparc.mgz'

            wmparcindwi = tmpdir / 'wmparcInDwi.nii.gz'  # Sylvain wants both
            wmparcinbrain = tmpdir / 'wmparcInBrain.nii.gz'

            print(
                "Making brain.nii.gz and wmparc.nii.gz from their mgz versions"
            )

            vol2vol = local[self.parent.fshome / 'bin/mri_vol2vol']
            label2vol = local[self.parent.fshome / 'bin/mri_label2vol']

            with local.env(SUBJECTS_DIR=''):
                vol2vol('--mov', brainmgz, '--targ', brainmgz, '--regheader',
                        '--o', brain)
                label2vol('--seg', wmparcmgz, '--temp', brainmgz,
                          '--regheader', wmparcmgz, '--o', wmparc)

            print('Extracting B0 from DWI and masking it')
            check_call((' ').join([
                pjoin(FILEDIR, 'bse.py'), '-i', self.parent.dwi, '-m',
                self.parent.dwimask, '-o', b0masked
            ]),
                       shell=True)
            print('Made masked B0')

            dwi_res = load_nifti(
                str(b0masked)).header['pixdim'][1:4].round(decimals=2)
            brain_res = load_nifti(
                str(brain)).header['pixdim'][1:4].round(decimals=2)
            print(f'DWI resolution: {dwi_res}')
            print(f'FreeSurfer brain resolution: {brain_res}')

            print('Registering wmparc to B0')
            registerFs2Dwi(tmpdir, 'fsbrainToB0', b0masked, brain, wmparc,
                           wmparcindwi)

            if (dwi_res != brain_res).any():
                print(
                    'DWI resolution is different from FreeSurfer brain resolution'
                )
                print(
                    'wmparc wil be registered to both DWI and brain resolution'
                )
                print(
                    'Check output files wmparcInDwi.nii.gz and wmparcInBrain.nii.gz'
                )

                print('Resampling B0 to brain resolution')

                ResampleImageBySpacing('3', b0masked, b0maskedbrain,
                                       brain_res.tolist())

                print('Registering wmparc to resampled B0')
                registerFs2Dwi(tmpdir, 'fsbrainToResampledB0', b0maskedbrain,
                               brain, wmparc, wmparcinbrain)

            # copying images to outDir
            b0masked.copy(self.parent.out)
            wmparcindwi.copy(self.parent.out)

            if b0maskedbrain.exists():
                b0maskedbrain.copy(self.parent.out)
                wmparcinbrain.copy(self.parent.out)

            if self.parent.debug:
                tmpdir.copy(self.parent.out,
                            'fs2dwi-debug-' + str(os.getpid()))

        print('See output files in ', self.parent.out._path)
Exemple #11
0
    def main(self):

        with TemporaryDirectory() as tmpdir:
            tmpdir = local.path(tmpdir)
            shortdwi = tmpdir / 'dwiShort.nii.gz'
            shortmask = tmpdir / 'maskShort.nii.gz'

            tmpdwi = tmpdir / 'dwi.nhdr'
            tmpdwimask = tmpdir / 'dwimask.nhdr'

            # TODO when UKFTractography supports float32, it should be removed
            # typecast to short
            short= load_nifti(self.dwi._path)
            save_nifti(shortdwi._path, short.get_data().astype('int16'), short.affine, short.header)

            short= load_nifti(self.dwimask._path)
            save_nifti(shortmask._path, short.get_data().astype('int16'), short.affine, short.header)


            if self.bhigh:
                from conversion import grad_remove

                bhigh_prefix= tmpdir / self.dwi.stem+ f'_bhigh_{self.bhigh}'
                bhigh_dwi= bhigh_prefix+'.nii.gz'
                grad_remove(shortdwi._path, bhigh_dwi, interval=[int(self.bhigh)+50,1e6], bvalFile=self.bvalFile, bvecFile=self.bvecFile)

                shortdwi= local.path(bhigh_dwi)
                self.bvalFile = local.path(bhigh_prefix + '.bval')
                self.bvecFile = local.path(bhigh_prefix + '.bvec')

                # preserve the filtered attributes in case the user wants to run UKFTractography separately in future
                shortdwi.copy(self.dwi.dirname)
                self.bvalFile.copy(self.dwi.dirname)
                self.bvecFile.copy(self.dwi.dirname)


            # convert the dwi to NRRD
            nhdr_write(shortdwi._path, self.bvalFile._path, self.bvecFile._path, tmpdwi._path)

            # convert the mask to NRRD
            nhdr_write(shortmask._path, None, None, tmpdwimask._path)

            key_val_pair=[]

            if self.givenParams:
                key_val_pair= self.givenParams.split(',')

                for i in range(0,len(ukfdefaults)-1,2): # -1 to pass --recordTensors which is always a default

                    try:
                        ind= key_val_pair.index(ukfdefaults[i])
                        ukfdefaults[i + 1] = key_val_pair[ind + 1]
                        # since ukfdefault[i+1] has been already replaced by key_val_pair[ind+1]
                        # remove key_val_pair[ind] and [ind+1] to prevent double specification
                        key_val_pair[ind:ind+2]=[]
                    except ValueError:
                        pass


            params = ['--dwiFile', tmpdwi, '--maskFile', tmpdwimask,
                      '--seedsFile', tmpdwimask, '--tracts', self.out] + list(ukfdefaults) + key_val_pair


            logging.info('Peforming UKF tractography of {}'.format(tmpdwi))
            UKFTractography[params] & FG
Exemple #12
0
    def main(self):

        if self.img_file.endswith('.nii') or self.img_file.endswith('.nii.gz'):
            mri = load_nifti(self.img_file._path)
        else:
            print('Invalid image format, accepts nifti only')
            exit(1)

        hdr = mri.header
        dim = hdr['dim'][0]

        if dim == 4:
            if not self.bvec_file and not self.bval_file:
                print('bvec and bvals files not specified, exiting ...')
                exit(1)

        elif dim == 3:
            spcdir_new = axis_align_3d(hdr)

        else:
            print('Invalid image dimension, has to be either 3 or 4')

        offset_orig = matrix(hdr.get_best_affine()[0:3, 3]).T
        spcdir_orig = hdr.get_best_affine()[0:3, 0:3]

        if self.axisAlign and not self.center:
            # pass spcdir_new and offset_orig

            if not self.out_prefix:
                self.out_prefix = self.img_file.split('.')[
                    0] + '-ax'  # a clever way to get prefix including path

            if dim == 4:
                spcdir_new = axis_align_dwi(hdr, self.bvec_file,
                                            self.bval_file, self.out_prefix)

            hdr_out = update_hdr(hdr, spcdir_new, offset_orig)

        elif not self.axisAlign and self.center:
            # pass spcdir_orig and offset_new

            if not self.out_prefix:
                self.out_prefix = self.img_file.split('.')[
                    0] + '-ce'  # a clever way to get prefix including path

            offset_new = -spcdir_orig @ matrix((hdr['dim'][1:4] - 1) / 2).T
            hdr_out = update_hdr(hdr, spcdir_orig, offset_new)

            # rename the bval file
            self.bval_file.copy(self.out_prefix + '.bval')
            # rename the bvec file
            self.bvec_file.copy(self.out_prefix + '.bvec')

        else:  # self.axisAlign and self.center:
            # pass spcdir_new and offset_new

            if not self.out_prefix:
                self.out_prefix = self.img_file.split('.')[
                    0] + '-xc'  # a clever way to get prefix including path

            if dim == 4:
                spcdir_new = axis_align_dwi(hdr, self.bvec_file,
                                            self.bval_file, self.out_prefix)

            offset_new = -spcdir_new @ matrix((hdr['dim'][1:4] - 1) / 2).T
            hdr_out = update_hdr(hdr, spcdir_new, offset_new)

        # write out the modified image
        save_nifti(self.out_prefix + '.nii.gz', mri.get_data(),
                   hdr_out.get_best_affine(), hdr_out)
Exemple #13
0
def makeAtlases(target, trainingTable, outPrefix, fusion, threads, debug):

    with TemporaryDirectory() as tmpdir:

        tmpdir = local.path(tmpdir)

        L= len(trainingTable)

        multiDataFrame= pd.concat([trainingTable, pd.DataFrame({'tmpdir': [tmpdir]*L, 'target': [str(target)]*L})], axis= 1)

        logging.info('Create {} atlases: compute transforms from images to target and apply over images'.format(L))

        pool = multiprocessing.Pool(threads)  # Use all available cores, otherwise specify the number you want as an argument

        pool.map_async(train2target, multiDataFrame.iterrows())

        pool.close()
        pool.join()

        logging.info('Fuse warped labelmaps to compute output labelmaps')
        atlasimages = tmpdir // 'atlas*.nii.gz'
        # sorting is required for applying weight to corresponding labelmap
        atlasimages.sort()

        if fusion.lower() == 'wavg':

            ALPHA_DEFAULT= 0.45

            logging.info('Compute MI between warped images and target')
            pool = multiprocessing.Pool(threads)
            for img in atlasimages:
                print('MI between {} and target'.format(img))
                miFile= img+'.txt'
                pool.apply_async(func= computeMI, args= (target, img, miFile, ))

            pool.close()
            pool.join()

            mis= []
            with open(tmpdir+'/MI.txt','w') as fw:

                for img in atlasimages:
                    with open(img+'.txt') as f:
                        mi= f.read().strip()
                        fw.write(img+','+mi+'\n')
                        mis.append(float(mi))

            weights = weightsFromMIExp(mis, ALPHA_DEFAULT)

        target_header= load_nifti(target._path).header
        pool = multiprocessing.Pool(threads)  # Use all available cores, otherwise specify the number you want as an argument
        for labelname in list(trainingTable)[1:]:  # list(d) gets column names

            out = os.path.abspath(outPrefix+ f'_{labelname}.nii.gz')
            if os.path.exists(out):
                os.remove(out)
            labelmaps = tmpdir // (labelname + '*')
            labelmaps.sort()

            if fusion.lower() == 'avg':
                print(' ')
                # parellelize
                # fuseAvg(labelmaps, out, target_header)
                pool.apply_async(func= fuseAvg, args= (labelmaps, out, target_header, ))

            elif fusion.lower() == 'antsjointfusion':
                print(' ')
                # atlasimages are the warped images
                # labelmaps are the warped labels
                # parellelize
                # fuseAntsJointFusion(target, atlasimages, labelmaps, out)
                pool.apply_async(func= fuseAntsJointFusion, args= (target, atlasimages, labelmaps, out, ))

            elif fusion.lower() == 'wavg':
                print(' ')
                # parellelize
                # fuseWeightedAvg(labelmaps, weights, out, target_header)
                pool.apply_async(func= fuseWeightedAvg, args= (labelmaps, weights, out, target_header, ))

            else:
                print('Unrecognized fusion option: {}. Skipping.'.format(fusion))

        pool.close()
        pool.join()

        if debug:
            tmpdir.copy(pjoin(dirname(outPrefix), 'atlas-debug-' + str(os.getpid())))
        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):

        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')
Exemple #16
0
    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')