Exemple #1
0
def create_dfnd_mask(refimg, affine_func_2_anat, affine_anat_2_atlas,
                     warp_anat_2_atlas, reference):
    import os
    from p3.utility import get_basename

    # get the current node directory
    cwd = os.getcwd()

    # get filename to output
    out_file = os.path.join(cwd,
                            '{}_atlas.nii.gz'.format(get_basename(refimg)))
    mask_file = os.path.join(
        cwd, '{}_atlas_dfnd.nii.gz'.format(get_basename(refimg)))

    # create dfnd mask
    command = 'antsApplyTransforms -f 0.0 -d 3 -o {} -i {} -t {} -t {} -t {} -r {} -v'.format(
        out_file, refimg, warp_anat_2_atlas, affine_anat_2_atlas,
        affine_func_2_anat, reference)
    print(command)
    os.system(command)

    # convert to binary
    os.system('fslmaths {} -bin {}'.format(out_file, mask_file))

    return mask_file
Exemple #2
0
def format_reference(func, reference, bids_dir):
    import os
    import nibabel
    from p3.utility import get_basename
    from bids.grabbids import BIDSLayout

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # get filename to output
    formatted_reference = os.path.join(
        cwd, '{}_format4D.nii.gz'.format(get_basename(func)))

    # get dim 4 and TR of input image
    dim4 = nibabel.load(func).header.get_data_shape()[3]  # get the 4th dim
    TR = BIDSLayout(bids_dir).get_metadata(func)[
        'RepetitionTime']  # get the TR

    # make the reference image the same dims as the input
    print('Formatting reference image...')
    command = 'ImageMath 3 {} ReplicateImage {} {} {} 0'.format(
        formatted_reference, reference, dim4, TR)
    print(command)
    os.system(command)

    return (formatted_reference, dim4, TR)
Exemple #3
0
def applytransforms(in_file, reference4D, combined_transforms4D,
                    warp_func_2_refimg, dfnd_mask):
    import os
    from p3.utility import get_basename

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # get filename to output
    out_file = os.path.join(
        cwd, '{}_moco_atlas.nii.gz'.format(get_basename(in_file)))

    # set up command to run
    command = 'antsApplyTransforms -f 0.0 -d 4 -i {} -r {} -o {} -t {} -t {} -v'.format(
        in_file, reference4D, out_file, combined_transforms4D,
        warp_func_2_refimg)
    print(command)

    # apply transforms
    os.system(command)

    # mask the aligned func with the dfnd mask
    print('Applying dfnd mask...')
    os.system('fslmaths {0} -mul {1} {0}'.format(out_file, dfnd_mask))

    # return moco, atlas-aligned functional image
    return out_file
Exemple #4
0
def convertvsm2ANTSwarp(in_file, ped):
    """
        Convert the voxel shift map to ants warp

        Grabbed this from fmriprep
        (https://github.com/poldracklab/fmriprep/blob/master/fmriprep/interfaces/itk.py#L178)
    """
    import nibabel as nb
    import numpy as np
    import os
    from p3.utility import get_basename

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # load file
    nii = nb.load(in_file)

    phaseEncDim = {'x': 0, 'y': 1, 'z': 2}[ped[0]]

    if len(ped) == 2:
        phaseEncSign = 1.0
    else:
        phaseEncSign = -1.0

    # Fix header
    hdr = nii.header.copy()
    hdr.set_data_dtype(np.dtype('<f4'))
    hdr.set_intent('vector', (), '')

    # Get data, convert to mm
    data = nii.get_data()

    aff = np.diag([1.0, 1.0, -1.0])
    if np.linalg.det(aff) < 0 and phaseEncDim != 0:
        # Reverse direction since ITK is LPS
        aff *= -1.0
    aff = aff.dot(nii.affine[:3, :3])

    # scale the data correctly
    data *= phaseEncSign * nii.header.get_zooms()[phaseEncDim]

    # Add missing dimensions
    zeros = np.zeros_like(data)
    field = [zeros, zeros]
    field.insert(phaseEncDim, data)
    field = np.stack(field, -1)
    # Add empty axis
    field = field[:, :, :, np.newaxis, :]

    # Write out
    out_file = os.path.join(cwd,
                            '{}_antswarp.nii.gz'.format(get_basename(in_file)))
    nb.Nifti1Image(field.astype(np.dtype('<f4')), nii.affine,
                   hdr).to_filename(out_file)

    return out_file
Exemple #5
0
def combinetransforms(func,
                      reference,
                      dim4,
                      TR,
                      affine_func_2_anat,
                      affine_anat_2_atlas,
                      warp_anat_2_atlas,
                      warp_fmc=None):
    import os
    from p3.utility import get_basename

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # get filename to output
    name = get_basename(func)
    combined_transforms = os.path.join(
        cwd, '{}_combined_transforms.nii.gz'.format(name))
    combined_transforms4D = os.path.join(
        cwd, '{}_combined_transforms4D.nii.gz'.format(name))

    # set up transforms (check in field map correction files exist)
    # we exclude the func_2_refimg transform since it is already 4D
    if warp_fmc:
        transforms = '-t {} -t {} -t {} -t {}'.format(
            warp_anat_2_atlas,
            affine_anat_2_atlas,
            affine_func_2_anat,
            warp_fmc,
        )
    else:
        transforms = '-t {} -t {} -t {}'.format(
            warp_anat_2_atlas,
            affine_anat_2_atlas,
            affine_func_2_anat,
        )

    # convert the transforms to 4D
    print('Combining transforms into one warp displacement field...')
    command = 'antsApplyTransforms -f 0.0 -d 3 -o [{},1] {} -r {} -v'.format(
        combined_transforms, transforms, reference)
    print(command)
    os.system(command)

    # replicate the combined transform
    print('Replicating the combined transform into 4D...')
    command = 'ImageMath 3 {} ReplicateDisplacement {} {} {} 0'.format(
        combined_transforms4D, combined_transforms, dim4, TR)
    print(command)
    os.system(command)

    # return the 4D combined transform
    return combined_transforms4D
Exemple #6
0
def resample_2_epi(T1, epi, aparc_aseg=None):
    """
        Resample images to epi resolution
    """
    import os
    import shutil
    from p3.utility import get_basename

    # get cwd
    cwd = os.getcwd()

    # get first epi from list
    epi = epi[0]

    # get filename of T1
    out_file = os.path.join(cwd, '{}_funcres.nii.gz'.format(get_basename(T1)))

    # resample the T1
    os.system('3dresample -rmode Li -master {} -prefix {} -inset {}'.format(
        epi, out_file, T1))
    T1_epi = out_file

    # ONLY if aparc_aseg defined
    if aparc_aseg:
        # get filename of aparc_aseg
        out_file = os.path.join(
            cwd, '{}_funcres.nii.gz'.format(get_basename(aparc_aseg)))

        # resample the aparc_aseg
        os.system(
            '3dresample -rmode NN -master {} -prefix {} -inset {}'.format(
                epi, out_file, aparc_aseg))
        aparc_aseg_epi = out_file
    else:  # set to empty
        aparc_aseg_epi = ''

    # return resampled images
    return (T1_epi, aparc_aseg_epi)
Exemple #7
0
def antsMotionCorr(fixed_image, moving_image, transform, writewarp):
    import os
    from p3.utility import get_basename

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # strip filename extension
    name = get_basename(moving_image)
    output_basename = os.path.join(cwd, name)  # set the output basename
    output_mocoparams = os.path.join(cwd, '{}MOCOparams.csv'.format(name))
    output_warp = os.path.join(cwd, '{}Warp.nii.gz'.format(name))
    output_warpedimg = os.path.join(cwd, '{}_Warped.nii.gz'.format(name))

    # check write warp boolean
    if writewarp:
        writewarp = 1
    else:
        writewarp = 0

    # setup commandline execution
    command = 'antsMotionCorr -d 3 -o [{},{}] -m MI[{},{},{},{},{},{}] -t {}[{}] -u 1 -e 1 ' \
        '-s {} -f {} -i {} -n 1 -w {} -v'.format(
            output_basename,
            output_warpedimg,
            fixed_image,
            moving_image,
            1, # metric weight
            32, # number of bins
            'Regular', # sampling Strategy
            0.2, # sampling percentage
            transform,
            0.1, # gradient step
            '1x0', # smoothing sigmas
            '2x1', # shrink factors
            '20x5', # iterations,
            writewarp # set flag for writing warps
        )
    print(command)  # print command before running

    # run antsMotionCorr
    os.system(command)

    # remove the InverseWarp image to save space
    os.system('rm {}'.format(os.path.join(cwd, '*InverseWarp.nii.gz')))

    # return the outputs
    return (output_warp, output_mocoparams, output_warpedimg)
Exemple #8
0
def avganats(anat_list):
    import os
    from p3.utility import get_basename

    # get current path
    path = os.getcwd()

    # join list into string
    filelist = ' '.join(anat_list)

    # get filename of first file
    outfile = '{}_avg.nii.gz'.format(get_basename(anat_list[0]))

    os.system('3dMean -prefix {} {}'.format(outfile, filelist))

    # return avg anat
    return os.path.join(path, outfile)
Exemple #9
0
def fsl_prepare_fieldmap(phasediff, magnitude, TE):
    import os
    from p3.utility import get_basename

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # get filename to output
    out_file = os.path.join(
        cwd, '{}_fieldmap.nii.gz'.format(get_basename(phasediff)))

    # run prepare field map
    os.system('fsl_prepare_fieldmap SIEMENS {} {} {} {}'.format(
        phasediff, magnitude, out_file, TE))

    # return the fieldmap file
    return out_file
Exemple #10
0
def apply_warp(in_file, reference, transform):
    import os
    from p3.utility import get_basename

    # get cwd
    cwd = os.getcwd()

    # get filename to output
    out_file = os.path.join(cwd,
                            '{}_atlas.nii.gz'.format(get_basename(in_file)))

    # set up command to run
    command = 'antsApplyTransforms -f 0.0 -d 3 -i {} -r {} -o {} -t {} -v'.format(
        in_file, reference, out_file, transform)
    print(command)

    # run transform
    os.system(command)

    # return output
    return out_file
Exemple #11
0
def combinetransforms(avgepi, reference, unwarp, realign):
    import os
    from p3.utility import get_basename

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # get filename to output
    combined_transforms = os.path.join(
        cwd, '{}_unwarpedtransform.nii.gz'.format(get_basename(avgepi)))

    # set up transforms
    transforms = '-t {} -t {}'.format(realign, unwarp)

    # convert the transforms to 4D
    print('Combining transforms into one warp displacement field...')
    command = 'antsApplyTransforms -f 0.0 -d 3 -o [{},1] {} -r {} -v'.format(
        combined_transforms, transforms, reference)
    print(command)
    os.system(command)

    # return the 4D combined transform
    return combined_transforms
Exemple #12
0
def get_prefix(filename):
    from p3.utility import get_basename
    return '{}_'.format(get_basename(filename))
Exemple #13
0
def calcFD(moco_params, brain_radius, threshold, filtered_threshold, TR,
           min_bpm, max_bpm):
    import os
    import math
    from p3.utility import get_basename
    import numpy as np
    from scipy import signal

    def respiration_iirnotch(TR_in_sec, bpm_min=18.582, bpm_max=25.7263):
        """ Function for calculating filter parameters for respiration filter

            Takes in the TR (optional min/max breaths-per-min, bpm_min, bpm_max).
            Returns the parameters for IIR Notch filter.
        """

        fs = 1.0 / TR_in_sec  # Sampling frequency (Hz)
        fn = fs / 2.0  # Nyquist frequency (Hz)

        # RR MIN
        rr_min = bpm_min / 60.0  # respiration rate minimum in Hz
        fa_min = abs(rr_min - math.floor(
            (rr_min + fn) / fs) * fs)  # Aliased minimum frequency (Hz)
        w0_min = fa_min / fn  # Normalized minimum frequency

        # RR MAX
        rr_max = bpm_max / 60.0  # respiration rate maximum in Hz
        fa_max = abs(rr_max - math.floor(
            (rr_max + fn) / fs) * fs)  # Aliased maximum frequency (Hz)
        w0_max = fa_max / fn  # Normalized maximum frequency

        # RR iirnotch filter
        w0 = np.mean([w0_min, w0_max])  # Mean normalized frequency
        bw = abs(w0_max - w0_min)  # Normalized bandwidth
        Q = w0 / bw  # Quality factor
        b, a = signal.iirnotch(w0, Q)  # Filter design

        return b, a

    # save to node folder (go up 2 directories bc of iterfield)
    cwd = os.path.dirname(os.path.dirname(os.getcwd()))

    # set output filename
    out_file = os.path.join(cwd, '{}.FD'.format(get_basename(moco_params)))
    tmask_out_file = os.path.join(cwd,
                                  '{}.tmask'.format(get_basename(moco_params)))
    filt_moco_file = os.path.join(
        cwd, '{}_filtered.1D'.format(get_basename(moco_params)))
    filt_out_file = os.path.join(
        cwd, '{}_filtered.FD'.format(get_basename(moco_params)))
    filt_tmask_out_file = os.path.join(
        cwd, '{}_filtered.tmask'.format(get_basename(moco_params)))

    # open file
    with open(moco_params, 'r') as moco_file:
        moco_nums = moco_file.readlines()

    # format the moco numbers from strings to float
    f_moco_nums = [
        tuple(map(float, list(filter(bool,
                                     moco_num.rstrip().split("  ")))))
        for moco_num in moco_nums
    ]
    fr_moco_nums = [(f_moco_num[0] * brain_radius * math.pi / 180,
                     f_moco_num[1] * brain_radius * math.pi / 180,
                     f_moco_num[2] * brain_radius * math.pi / 180,
                     f_moco_num[3], f_moco_num[4], f_moco_num[5])
                    for f_moco_num in f_moco_nums]

    # convert to numpy array and filter the motion numbers
    np_moco_nums = np.array(f_moco_nums)
    TR = float(TR)  # convert TR to float
    b, a = respiration_iirnotch(TR, min_bpm, max_bpm)  # create filter
    # filter data (run twice for 4th order)
    filt_moco_stage1 = signal.filtfilt(b,
                                       a,
                                       np_moco_nums,
                                       axis=0,
                                       padtype=None)
    filt_moco_stage2 = signal.filtfilt(b,
                                       a,
                                       filt_moco_stage1,
                                       axis=0,
                                       padtype=None)
    filt_moco = filt_moco_stage2
    # Convert rotations to mm
    filt_moco[:, 4:7] = filt_moco[:, 4:7] * brain_radius * math.pi / 180
    # Calculate FD values for the series
    filtered_FD = np.array([
        np.concatenate(
            (np.array([0]),
             np.sum(np.absolute(filt_moco[1:, :] - filt_moco[0:-1, :]),
                    axis=1)),
            axis=0)
    ]).transpose()
    # convert to lists
    filt_moco = np.ndarray.tolist(filt_moco)
    filtered_FD = [FD_val[0] for FD_val in np.ndarray.tolist(filtered_FD)]

    # calculate FD
    FD = [0]
    for f1, f2 in zip(fr_moco_nums[0:-1], fr_moco_nums[1:]):
        FD.append(sum(map(abs, [v1 - v2 for v1, v2 in zip(f1, f2)])))

    # write FD to file
    with open(out_file, 'w') as FD_file:
        for val in FD:
            FD_file.write(str(val))
            FD_file.write('\n')

    # write tmask to file
    with open(tmask_out_file, 'w') as tmask_file:
        for val in FD:
            tmask_file.write(str(int(val < threshold)))
            tmask_file.write('\n')

    # write filtered motion numbers to file
    with open(filt_moco_file, 'w') as fm_file:
        for val in filt_moco:
            fm_file.write(' '.join([str(v) for v in val]))
            fm_file.write('\n')

    # write filtered FD to file
    with open(filt_out_file, 'w') as filt_FD_file:
        for val in filtered_FD:
            filt_FD_file.write(str(val))
            filt_FD_file.write('\n')

    # write filtered tmask to file
    with open(filt_tmask_out_file, 'w') as filt_tmask_file:
        for val in filtered_FD:
            filt_tmask_file.write(str(int(val < filtered_threshold)))
            filt_tmask_file.write('\n')

    # return the FD file
    return out_file, tmask_out_file, filt_moco_file, filt_out_file, filt_tmask_out_file
Exemple #14
0
def gett1name(T1):
    from p3.utility import get_basename
    return get_basename(T1)
Exemple #15
0
    def __init__(self, settings):
        # call base constructor
        super().__init__(settings)

        # define input/output node
        self.set_input([
            'func',  # I pass this in so I can get the TR info from BIDS
            'func_stc_despike',
            'warp_func_2_refimg',
            'warp_fmc',
            'refimg',
            'affine_func_2_anat',
            'warp_func_2_anat',
            'affine_anat_2_atlas',
            'warp_anat_2_atlas'
        ])
        self.set_output(['func_atlas'])

        # define datasink substitutions
        atlas_base = get_basename(settings['atlas'])  # get atlas basename
        self.set_subs([
            ('_flirt', '_funcres'),
            (atlas_base, 'atlas/{}'.format(atlas_base)
             )  # save resampled atlas to atlas folder
        ])
        self.set_resubs([
            ('sub-(?P<subject>\w+_)',
             'func/sub-\g<subject>'),  # place file under anat folder
            ('func/sub-(?P<subject>\w+)_ses-(?P<session>\w+)_',
             'func/ses-\g<session>/sub-\g<subject>_ses-\g<session>_'
             )  # add session folders if they exist
        ])

        # grab the resolution of the refimg
        self.get_resolution = Node(Function(input_names=['reference'],
                                            output_names=['resolution'],
                                            function=get_resolution),
                                   name='get_resolution')

        # resample atlas to epi space so we can use it as a reference
        self.resample = Node(fsl.FLIRT(no_search=True), name='resample')
        self.resample.inputs.in_file = set_atlas_path(settings['atlas'])
        self.resample.inputs.reference = set_atlas_path(settings['atlas'])

        # format the reference image (which should be the resampled atlas)
        self.format_reference = MapNode(Function(
            input_names=['func', 'reference', 'bids_dir'],
            output_names=['formatted_reference', 'dim4', 'TR'],
            function=format_reference),
                                        iterfield=['func'],
                                        name='format_reference')
        self.format_reference.inputs.bids_dir = settings['bids_dir']

        # combine 3D transforms and replicate to 4D
        self.combinetransforms = MapNode(
            Function(input_names=[
                'func', 'reference', 'dim4', 'TR', 'affine_func_2_anat',
                'affine_anat_2_atlas', 'warp_anat_2_atlas', 'warp_fmc'
            ],
                     output_names=['combined_transforms4D'],
                     function=combinetransforms),
            iterfield=['func', 'dim4', 'TR', 'warp_fmc'],
            name='combinetransforms')
        self.combinetransforms.n_procs = settings['num_threads']

        # align the reference image to atlas and create a dfnd mask from it
        self.create_dfnd_mask = Node(Function(input_names=[
            'refimg', 'affine_func_2_anat', 'affine_anat_2_atlas',
            'warp_anat_2_atlas', 'reference'
        ],
                                              output_names=['mask_file'],
                                              function=create_dfnd_mask),
                                     name='create_dfnd_mask')
        self.create_dfnd_mask.n_procs = settings['num_threads']

        # apply nonlinear transform
        self.applytransforms = MapNode(Function(input_names=[
            'in_file', 'reference4D', 'combined_transforms4D',
            'warp_func_2_refimg', 'dfnd_mask'
        ],
                                                output_names=['out_file'],
                                                function=applytransforms),
                                       iterfield=[
                                           'in_file', 'reference4D',
                                           'combined_transforms4D',
                                           'warp_func_2_refimg'
                                       ],
                                       name='applytransforms')
        self.applytransforms.n_procs = settings['num_threads']