def tshift(self,
               in_file=None,
               out_file=None,
               suffix=None,
               interp="heptic"):

        mycwd = os.getcwd()
        os.chdir(self._output_dir)
        in_file, out_file = self.FuncHandler(in_file, out_file, suffix)
        #out_file = out_file.replace(".nii.gz","")
        mytshift = afni.TShift(in_file=in_file,
                               interp=interp,
                               outputtype='NIFTI_GZ')
        #https://nipype.readthedocs.io/en/latest/interfaces/generated/interfaces.afni/preprocess.html#tshift

        #mytshift.inputs.args = "-prefix %s & %s " % (out_file, out_file.)
        mytshift.run()
        os.rename(in_file.replace(".nii.gz", "_tshift.nii.gz"), out_file)
        os.chdir(mycwd)

        self.refit(in_file=out_file, args="-view orig")

        #remove temp files
        if type(in_file) == models.BIDSImageFile:
            in_file = os.path.join(self._output_dir, in_file.filename)
        if "_desc-temp" in in_file:
            os.remove(in_file)
Ejemplo n.º 2
0
    def slicetime(file, sliceorder):
        print "running slicetiming"
        slicetime = afni.TShift(outputtype='NIFTI_GZ')
        slicetime.inputs.in_file = file
        if type(sliceorder) == list:
            custom_order = open(os.path.abspath('afni_custom_order_file.txt'),
                                'w')
            tpattern = []
            for i in xrange(len(sliceorder)):
                tpattern.append((i * tr / float(Nz), sliceorder[i]))
                tpattern.sort(key=lambda x: x[1])
                for i, t in enumerate(tpattern):
                    print '%f\n' % (t[0])
                    custom_order.write('%f\n' % (t[0]))
            custom_order.close()
            order_file = 'afni_custom_order_file.txt'
        elif type(sliceorder) == str:
            order_file = sliceorder
        else:
            raise TypeError('sliceorder must be filepath or list')

        slicetime.inputs.args = '-tpattern @%s' % os.path.abspath(order_file)
        slicetime.inputs.tr = str(tr) + 's'
        slicetime.inputs.outputtype = 'NIFTI_GZ'
        slicetime.inputs.out_file = os.path.abspath(split_filename(file)[1] +\
                                                    "_tshift.nii.gz")

        res = slicetime.run()
        file_to_realign = res.outputs.out_file
        return file_to_realign
Ejemplo n.º 3
0
def test_tshift():
    input_map = dict(
        args=dict(argstr='%s', ),
        environ=dict(usedefault=True, ),
        ignore=dict(argstr='-ignore %s', ),
        ignore_exception=dict(usedefault=True, ),
        in_file=dict(
            argstr='%s',
            mandatory=True,
        ),
        interp=dict(argstr='-%s', ),
        out_file=dict(argstr='-prefix %s', ),
        outputtype=dict(),
        rlt=dict(argstr='-rlt', ),
        rltplus=dict(argstr='-rlt+', ),
        suffix=dict(),
        tpattern=dict(argstr='-tpattern %s', ),
        tr=dict(argstr='-TR %s', ),
        tslice=dict(
            argstr='-slice %s',
            xor=['tzero'],
        ),
        tzero=dict(
            argstr='-tzero %s',
            xor=['tslice'],
        ),
    )
    instance = afni.TShift()
    for key, metadata in input_map.items():
        for metakey, value in metadata.items():
            yield assert_equal, getattr(instance.inputs.traits()[key],
                                        metakey), value
Ejemplo n.º 4
0
def init_bold_stc_wf(tr, tpattern, name='bold_stc_wf'):
    """
    This workflow performs :abbr:`STC (slice-timing correction)` over the input
    :abbr:`BOLD (blood-oxygen-level dependent)` image.

    .. workflow::
        :graph2use: orig
        :simple_form: yes

        from fmriprep.workflows.bold import init_bold_stc_wf
        wf = init_bold_stc_wf(
            metadata={"RepetitionTime": 2.0,
                      "SliceTiming": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]},
            )

    **Parameters**

        name : str
            Name of workflow (default: ``bold_stc_wf``)

    **Inputs**

        bold_file
            BOLD series NIfTI file
        skip_vols
            Number of non-steady-state volumes detected at beginning of ``bold_file``

    **Outputs**

        stc_file
            Slice-timing corrected BOLD series NIfTI file

    """
    workflow = pe.Workflow(name=name)
    inputnode = pe.Node(niu.IdentityInterface(fields=['bold_file', 'skip_vols']), name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(fields=['stc_file']), name='outputnode')


    # It would be good to fingerprint memory use of afni.TShift
    slice_timing_correction = pe.Node(
        afni.TShift(tr=tr, tpattern=tpattern, outputtype='NIFTI_GZ'),
        name='slice_timing_correction')

    copy_xform = pe.Node(CopyXForm(), name='copy_xform')

    workflow.connect([
        (inputnode, slice_timing_correction, [('bold_file', 'in_file'),
                                              ('skip_vols', 'ignore')]),
        (slice_timing_correction, copy_xform, [('out_file', 'in_file')]),
        (inputnode, copy_xform, [('bold_file', 'hdr_file')]),
        (copy_xform, outputnode, [('out_file', 'stc_file')]),
    ])

    return workflow
Ejemplo n.º 5
0
def _slice_time(func_file,
                t_r,
                write_dir,
                caching=False,
                terminal_output='allatonce',
                environ=None):
    if environ is None:
        environ = {'AFNI_DECONFLICT': 'OVERWRITE'}

    if caching:
        memory = Memory(write_dir)
        tshift = memory.cache(afni.TShift)
        tshift.interface().set_default_terminal_output(terminal_output)
    else:
        tshift = afni.TShift(terminal_output=terminal_output).run

    out_tshift = tshift(in_file=func_file,
                        out_file=fname_presuffix(func_file,
                                                 suffix='_tshifted',
                                                 newpath=write_dir),
                        tpattern='altplus',
                        tr=str(t_r),
                        environ=environ)
    return out_tshift.outputs.out_file
Ejemplo n.º 6
0
    def __init__(self, settings):
        # call base constructor
        super().__init__(settings)

        # define input/output node
        self.set_input(['func'])
        self.set_output([
            'refimg', 'func_stc_despike', 'warp_func_2_refimg', 'func_aligned'
        ])

        # define datasink substitutions
        self.set_subs([('MOCOparams', '_moco')])

        # define datasink regular expression substitutions
        self.set_resubs([
            ('_moco_before\d{1,3}/', ''),  # get rid of _moco_before subfolders
            ('sub-(?P<subject>\w+)_',
             'func/sub-\g<subject>_'),  # place files under func folder
            ('func/(?P<filename>\S+).nii.gz', '\g<filename>.nii.gz'
             ),  # ...except for the QC files, it should NOT have a func 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
        ])

        # extract slice timing so we can pass it to slice time correction
        self.extract_stc = MapNode(Function(input_names=['epi', 'bids_dir'],
                                            output_names=['slicetiming', 'TR'],
                                            function=extract_slicetime),
                                   iterfield=['epi'],
                                   name='extract_slicetime')
        self.extract_stc.inputs.bids_dir = settings['bids_dir']

        # Despike epi data (create 2 for permutations with slice time correction)
        self.despike = MapNode(ExtendedDespike(
            args="-ignore {} -NEW -nomask".format(
                settings['func_reference_frame']),
            outputtype="NIFTI_GZ"),
                               iterfield=['in_file'],
                               name='despike')

        # skip despike node
        self.skip_despike = MapNode(IdentityInterface(fields=['epi']),
                                    iterfield=['epi'],
                                    name='skip_despike')

        # despike pool node
        self.despike_pool = MapNode(IdentityInterface(fields=['epi']),
                                    iterfield=['epi'],
                                    name='despike_pool')

        # timeshift data
        self.tshift = MapNode(afni.TShift(
            args="-heptic",
            ignore=settings['func_reference_frame'],
            tzero=0,
            outputtype="NIFTI_GZ"),
                              iterfield=['in_file', 'tpattern', 'tr'],
                              name='tshift')

        # skip stc node
        self.skip_stc = MapNode(IdentityInterface(fields=['epi']),
                                iterfield=['epi'],
                                name='skip_stc')

        # despike/stc pool node
        self.stc_despike_pool = MapNode(IdentityInterface(fields=['epi']),
                                        iterfield=['epi'],
                                        name='stc_despike_pool')

        # Setup basefile for volreg (pre slice time correction/despike)
        self.refrunonly = Node(  # this will grab only the first run to feed as a basefile
            Function(input_names=['epi', 'run'],
                     output_names=['epi'],
                     function=lambda epi, run: epi[run]),
            name='refrunonly')
        self.refrunonly.inputs.run = settings['func_reference_run']
        self.extractroi = Node(  # get reference frame of first run
            fsl.ExtractROI(t_min=settings['func_reference_frame'],
                           t_size=1,
                           output_type='NIFTI_GZ'),
            name='extractroi')

        # Setup basefile for volreg (post slice time correction/despike)
        self.refrunonly_post = Node(  # this will grab only the first run to feed as a basefile
            Function(input_names=['epi', 'run'],
                     output_names=['epi'],
                     function=lambda epi, run: epi[run]),
            name='refrunonly_post')
        self.refrunonly_post.inputs.run = settings['func_reference_run']
        self.extractroi_post = Node(  # get reference frame of first run
            fsl.ExtractROI(t_min=settings['func_reference_frame'],
                           t_size=1,
                           output_type='NIFTI_GZ'),
            name='extractroi_post')

        # Moco (after)
        self.moco = MapNode(Function(
            input_names=[
                'fixed_image', 'moving_image', 'transform', 'writewarp'
            ],
            output_names=['warp', 'mocoparams', 'warped_img'],
            function=antsMotionCorr),
                            iterfield=['moving_image'],
                            name='moco')
        self.moco.inputs.transform = 'Rigid'
        self.moco.inputs.writewarp = True
        self.moco.n_procs = settings['num_threads']

        # Moco (before)
        self.moco_before = MapNode(afni.Volreg(
            args="-heptic -maxite {}".format(25),
            verbose=True,
            zpad=10,
            outputtype="NIFTI_GZ"),
                                   iterfield=['in_file'],
                                   name='moco_before')

        # Calc FD
        self.calcFD = MapNode(Function(
            input_names=[
                'func', 'moco_params', 'brain_radius', 'threshold',
                'filtered_threshold', 'TR', 'min_bpm', 'max_bpm'
            ],
            output_names=['FD', 'tmask', 'filt_moco', 'filt_FD', 'filt_tmask'],
            function=calcFD),
                              iterfield=['moco_params', 'TR'],
                              name='calcFD')
        self.calcFD.inputs.min_bpm = settings['min_bpm']
        self.calcFD.inputs.max_bpm = settings['max_bpm']
        self.calcFD.inputs.brain_radius = settings['brain_radius']
        self.calcFD.inputs.threshold = settings['FD_threshold']
        self.calcFD.inputs.filtered_threshold = settings[
            'FD_filtered_threshold']
Ejemplo n.º 7
0
#Basic interface class generates identity mappings
NodeHash_6040006ae640 = pe.Node(utility.IdentityInterface(fields=['func','slicetiming_txt']), name = 'NodeName_6040006ae640')
NodeHash_6040006ae640.inputs.func = func
NodeHash_6040006ae640.inputs.slicetiming_txt = slicetiming_txt

#Custom interface wrapping function TR
NodeHash_6000004b9860 = pe.MapNode(interface = info_get.TR, name = 'NodeName_6000004b9860', iterfield = ['in_file'])

#Custom interface wrapping function Str2Float
NodeHash_6040006ae9a0 = pe.MapNode(interface = utils_convert.Str2Float, name = 'NodeName_6040006ae9a0', iterfield = ['str'])

#Custom interface wrapping function Float2Str
NodeHash_6040004aee80 = pe.MapNode(interface = utils_convert.Float2Str, name = 'NodeName_6040004aee80', iterfield = ['float'])

#Wraps command **3dTshift**
NodeHash_6040004ad140 = pe.MapNode(interface = afni.TShift(), name = 'NodeName_6040004ad140', iterfield = ['in_file', 'tr'])
NodeHash_6040004ad140.inputs.rltplus = True
NodeHash_6040004ad140.inputs.outputtype = "NIFTI_GZ"
NodeHash_6040004ad140.inputs.terminal_output = 'allatonce'

#Generic datasink module to store structured outputs
NodeHash_6080008b3d40 = pe.Node(interface = io.DataSink(), name = 'NodeName_6080008b3d40')
NodeHash_6080008b3d40.inputs.base_directory = SinkDir
NodeHash_6080008b3d40.inputs.regexp_substitutions = [("func_slicetimed/_NodeName_.{13}", "")]

#Basic interface class generates identity mappings
NodeHash_6080008b5660 = pe.Node(utility.IdentityInterface(fields=['func_slicetimed','TR']), name = 'NodeName_6080008b5660')

#Custom interface wrapping function JoinVal2Dict
NodeHash_6040004afde0 = pe.Node(interface = utils_convert.JoinVal2Dict, name = 'NodeName_6040004afde0')
Ejemplo n.º 8
0
def coregister_fmri_session(session_data,
                            t_r,
                            write_dir,
                            brain_volume,
                            use_rats_tool=True,
                            slice_timing=True,
                            prior_rigid_body_registration=False,
                            caching=False,
                            voxel_size_x=.1,
                            voxel_size_y=.1,
                            verbose=True,
                            **environ_kwargs):
    """
    Coregistration of the subject's functional and anatomical images.
    The functional volume is aligned to the anatomical, first with a rigid body
    registration and then on a per-slice basis (only a fine correction, this is
    mostly for correction of EPI distortion).


    Parameters
    ----------
    session_data : sammba.registration.SessionData
        Single animal data, giving paths to its functional and anatomical
        image, as well as it identifier.

    t_r : float
        Repetition time for the EPI, in seconds.

    write_dir : str
        Directory to save the output and temporary images.

    brain_volume : int
        Volume of the brain in mm3 used for brain extraction.
        Typically 400 for mouse and 1800 for rat.

    use_rats_tool : bool, optional
        If True, brain mask is computed using RATS Mathematical Morphology.
        Otherwise, a histogram-based brain segmentation is used.

    prior_rigid_body_registration : bool, optional
        If True, a rigid-body registration of the anat to the func is performed
        prior to the warp. Useful if the images headers have missing/wrong
        information.

    voxel_size_x : float, optional
        Resampling resolution for the x-axis, in mm.

    voxel_size_y : float, optional
        Resampling resolution for the y-axis, in mm.

    caching : bool, optional
        Wether or not to use caching.

    verbose : bool, optional
        If True, all steps are verbose. Note that caching implies some
        verbosity in any case.

    environ_kwargs : extra arguments keywords
        Extra arguments keywords, passed to interfaces environ variable.

    Returns
    -------
    the same sequence with each animal_data updated: the following attributes
    are added
        - `output_dir_` : str
                          Path to the output directory.
        - `coreg_func_` : str
                          Path to paths to the coregistered functional image.
        - `coreg_anat_` : str
                          Path to paths to the coregistered functional image.
        - `coreg_transform_` : str
                               Path to the transform from anat to func.

    Notes
    -----
    If `use_rats_tool` is turned on, RATS tool is used for brain extraction
    and has to be cited. For more information, see
    `RATS <http://www.iibi.uiowa.edu/content/rats-overview/>`_
    """
    func_filename = session_data.func
    anat_filename = session_data.anat

    environ = {'AFNI_DECONFLICT': 'OVERWRITE'}
    for (key, value) in environ_kwargs.items():
        environ[key] = value

    if verbose:
        terminal_output = 'allatonce'
    else:
        terminal_output = 'none'

    if use_rats_tool:
        if segmentation.interfaces.Info().version() is None:
            raise ValueError('Can not locate RATS')
        else:
            ComputeMask = segmentation.MathMorphoMask
    else:
        ComputeMask = segmentation.HistogramMask

    if ants.base.Info().version is None:
        raise ValueError('Can not locate ANTS')

    if caching:
        memory = Memory(write_dir)
        tshift = memory.cache(afni.TShift)
        clip_level = memory.cache(afni.ClipLevel)
        volreg = memory.cache(afni.Volreg)
        allineate = memory.cache(afni.Allineate)
        tstat = memory.cache(afni.TStat)
        compute_mask = memory.cache(ComputeMask)
        calc = memory.cache(afni.Calc)
        allineate = memory.cache(afni.Allineate)
        allineate2 = memory.cache(afni.Allineate)
        unifize = memory.cache(afni.Unifize)
        bias_correct = memory.cache(ants.N4BiasFieldCorrection)
        catmatvec = memory.cache(afni.CatMatvec)
        warp = memory.cache(afni.Warp)
        resample = memory.cache(afni.Resample)
        slicer = memory.cache(afni.ZCutUp)
        warp_apply = memory.cache(afni.NwarpApply)
        qwarp = memory.cache(afni.Qwarp)
        merge = memory.cache(afni.Zcat)
        copy_geom = memory.cache(fsl.CopyGeom)
        overwrite = False
        for step in [
                tshift, volreg, allineate, allineate2, tstat, compute_mask,
                calc, unifize, resample, slicer, warp_apply, qwarp, merge
        ]:
            step.interface().set_default_terminal_output(terminal_output)
    else:
        tshift = afni.TShift(terminal_output=terminal_output).run
        clip_level = afni.ClipLevel().run
        volreg = afni.Volreg(terminal_output=terminal_output).run
        allineate = afni.Allineate(terminal_output=terminal_output).run
        allineate2 = afni.Allineate(terminal_output=terminal_output
                                    ).run  # TODO: remove after fixed bug
        tstat = afni.TStat(terminal_output=terminal_output).run
        compute_mask = ComputeMask().run
        calc = afni.Calc(terminal_output=terminal_output).run
        unifize = afni.Unifize(terminal_output=terminal_output).run
        bias_correct = ants.N4BiasFieldCorrection(
            terminal_output=terminal_output).run
        catmatvec = afni.CatMatvec().run
        warp = afni.Warp().run
        resample = afni.Resample(terminal_output=terminal_output).run
        slicer = afni.ZCutUp(terminal_output=terminal_output).run
        warp_apply = afni.NwarpApply(terminal_output=terminal_output).run
        qwarp = afni.Qwarp(terminal_output=terminal_output).run
        merge = afni.Zcat(terminal_output=terminal_output).run
        copy_geom = fsl.CopyGeom(terminal_output=terminal_output).run
        overwrite = True

    session_data._check_inputs()
    output_dir = os.path.join(os.path.abspath(write_dir),
                              session_data.animal_id)
    session_data._set_output_dir_(output_dir)
    current_dir = os.getcwd()
    os.chdir(output_dir)
    output_files = []

    #######################################
    # Correct functional for slice timing #
    #######################################
    if slice_timing:
        out_tshift = tshift(in_file=func_filename,
                            outputtype='NIFTI_GZ',
                            tpattern='altplus',
                            tr=str(t_r),
                            environ=environ)
        func_filename = out_tshift.outputs.out_file
        output_files.append(func_filename)

    ################################################
    # Register functional volumes to the first one #
    ################################################
    # XXX why do you need a thresholded image ?
    out_clip_level = clip_level(in_file=func_filename)
    out_calc_threshold = calc(in_file_a=func_filename,
                              expr='ispositive(a-{0}) * a'.format(
                                  out_clip_level.outputs.clip_val),
                              outputtype='NIFTI_GZ')
    thresholded_filename = out_calc_threshold.outputs.out_file

    out_volreg = volreg(  # XXX dfile not saved
        in_file=thresholded_filename,
        outputtype='NIFTI_GZ',
        environ=environ,
        oned_file=fname_presuffix(thresholded_filename,
                                  suffix='Vr.1Dfile.1D',
                                  use_ext=False),
        oned_matrix_save=fname_presuffix(thresholded_filename,
                                         suffix='Vr.aff12.1D',
                                         use_ext=False))

    # Apply the registration to the whole head
    out_allineate = allineate(in_file=func_filename,
                              master=func_filename,
                              in_matrix=out_volreg.outputs.oned_matrix_save,
                              out_file=fname_presuffix(func_filename,
                                                       suffix='Av'),
                              environ=environ)

    # 3dAllineate removes the obliquity. This is not a good way to readd it as
    # removes motion correction info in the header if it were an AFNI file...as
    # it happens it's NIfTI which does not store that so irrelevant!
    out_copy_geom = copy_geom(dest_file=out_allineate.outputs.out_file,
                              in_file=out_volreg.outputs.out_file)

    allineated_filename = out_copy_geom.outputs.out_file

    # Create a (hopefully) nice mean image for use in the registration
    out_tstat = tstat(in_file=allineated_filename,
                      args='-mean',
                      outputtype='NIFTI_GZ',
                      environ=environ)

    # Update outputs
    output_files.extend([
        thresholded_filename, out_volreg.outputs.oned_matrix_save,
        out_volreg.outputs.out_file, out_volreg.outputs.md1d_file,
        allineated_filename, out_tstat.outputs.out_file
    ])

    ###########################################
    # Corret anat and func for intensity bias #
    ###########################################
    # Correct the functional average for intensities bias
    out_bias_correct = bias_correct(input_image=out_tstat.outputs.out_file)
    unbiased_func_filename = out_bias_correct.outputs.output_image

    # Bias correct the antomical image
    out_unifize = unifize(in_file=anat_filename,
                          outputtype='NIFTI_GZ',
                          environ=environ)
    unbiased_anat_filename = out_unifize.outputs.out_file

    # Update outputs
    output_files.extend([unbiased_func_filename, unbiased_anat_filename])

    #############################################
    # Rigid-body registration anat -> mean func #
    #############################################
    if prior_rigid_body_registration:
        # Mask the mean functional volume outside the brain.
        out_clip_level = clip_level(in_file=unbiased_func_filename)
        out_compute_mask_func = compute_mask(
            in_file=unbiased_func_filename,
            volume_threshold=brain_volume,
            intensity_threshold=int(out_clip_level.outputs.clip_val))
        out_cacl_func = calc(in_file_a=unbiased_func_filename,
                             in_file_b=out_compute_mask_func.outputs.out_file,
                             expr='a*b',
                             outputtype='NIFTI_GZ',
                             environ=environ)

        # Mask the anatomical volume outside the brain.
        out_clip_level = clip_level(in_file=unbiased_anat_filename)
        out_compute_mask_anat = compute_mask(
            in_file=unbiased_anat_filename,
            volume_threshold=brain_volume,
            intensity_threshold=int(out_clip_level.outputs.clip_val))
        out_cacl_anat = calc(in_file_a=unbiased_anat_filename,
                             in_file_b=out_compute_mask_anat.outputs.out_file,
                             expr='a*b',
                             outputtype='NIFTI_GZ',
                             environ=environ)

        # Compute the transformation from functional to anatomical brain
        # XXX: why in this sense
        out_allineate = allineate2(
            in_file=out_cacl_func.outputs.out_file,
            reference=out_cacl_anat.outputs.out_file,
            out_matrix=fname_presuffix(out_cacl_func.outputs.out_file,
                                       suffix='_shr.aff12.1D',
                                       use_ext=False),
            center_of_mass='',
            warp_type='shift_rotate',
            out_file=fname_presuffix(out_cacl_func.outputs.out_file,
                                     suffix='_shr'),
            environ=environ)
        rigid_transform_file = out_allineate.outputs.out_matrix
        output_files.extend([
            out_compute_mask_func.outputs.out_file,
            out_cacl_func.outputs.out_file,
            out_compute_mask_anat.outputs.out_file,
            out_cacl_anat.outputs.out_file, rigid_transform_file,
            out_allineate.outputs.out_file
        ])

        # apply the inverse transform to register the anatomical to the func
        catmatvec_out_file = fname_presuffix(rigid_transform_file,
                                             suffix='INV')
        out_catmatvec = catmatvec(in_file=[(rigid_transform_file, 'I')],
                                  oneline=True,
                                  out_file=catmatvec_out_file)
        output_files.append(out_catmatvec.outputs.out_file)
        out_allineate = allineate(in_file=unbiased_anat_filename,
                                  master=unbiased_func_filename,
                                  in_matrix=out_catmatvec.outputs.out_file,
                                  out_file=fname_presuffix(
                                      unbiased_anat_filename,
                                      suffix='_shr_in_func_space'),
                                  environ=environ)
        allineated_anat_filename = out_allineate.outputs.out_file
        output_files.append(allineated_anat_filename)
    else:
        allineated_anat_filename = unbiased_anat_filename

    ############################################
    # Nonlinear registration anat -> mean func #
    ############################################
    # 3dWarp doesn't put the obliquity in the header, so do it manually
    # This step generates one file per slice and per time point, so we are
    # making sure they are removed at the end
    out_warp = warp(in_file=allineated_anat_filename,
                    oblique_parent=unbiased_func_filename,
                    interp='quintic',
                    gridset=unbiased_func_filename,
                    outputtype='NIFTI_GZ',
                    verbose=True,
                    environ=environ)
    registered_anat_filename = out_warp.outputs.out_file
    registered_anat_oblique_filename = fix_obliquity(registered_anat_filename,
                                                     unbiased_func_filename,
                                                     verbose=verbose)

    # Concatenate all the anat to func tranforms
    mat_filename = fname_presuffix(registered_anat_filename,
                                   suffix='_warp.mat',
                                   use_ext=False)
    # XXX Handle this correctly according to caching
    if not os.path.isfile(mat_filename):
        np.savetxt(mat_filename, [out_warp.runtime.stdout], fmt='%s')
        output_files.append(mat_filename)

    transform_filename = fname_presuffix(registered_anat_filename,
                                         suffix='_anat_to_func.aff12.1D',
                                         use_ext=False)
    if prior_rigid_body_registration:
        _ = catmatvec(in_file=[(mat_filename, 'ONELINE'),
                               (rigid_transform_file, 'ONELINE')],
                      oneline=True,
                      out_file=transform_filename)
    else:
        _ = catmatvec(in_file=[(mat_filename, 'ONELINE')],
                      oneline=True,
                      out_file=transform_filename)

    ##################################################
    # Per-slice non-linear registration func -> anat #
    ##################################################
    # Slice anatomical image
    anat_img = nibabel.load(registered_anat_oblique_filename)
    anat_n_slices = anat_img.header.get_data_shape()[2]
    sliced_registered_anat_filenames = []
    for slice_n in range(anat_n_slices):
        out_slicer = slicer(in_file=registered_anat_oblique_filename,
                            keep='{0} {0}'.format(slice_n),
                            out_file=fname_presuffix(
                                registered_anat_oblique_filename,
                                suffix='Sl%d' % slice_n),
                            environ=environ)
        oblique_slice = fix_obliquity(out_slicer.outputs.out_file,
                                      registered_anat_oblique_filename,
                                      verbose=verbose)
        sliced_registered_anat_filenames.append(oblique_slice)

    # Slice mean functional
    sliced_bias_corrected_filenames = []
    img = nibabel.load(func_filename)
    n_slices = img.header.get_data_shape()[2]
    for slice_n in range(n_slices):
        out_slicer = slicer(in_file=unbiased_func_filename,
                            keep='{0} {0}'.format(slice_n),
                            out_file=fname_presuffix(unbiased_func_filename,
                                                     suffix='Sl%d' % slice_n),
                            environ=environ)
        oblique_slice = fix_obliquity(out_slicer.outputs.out_file,
                                      unbiased_func_filename,
                                      verbose=verbose)
        sliced_bias_corrected_filenames.append(oblique_slice)

    # Below line is to deal with slices where there is no signal (for example
    # rostral end of some anatomicals)

    # The inverse warp frequently fails, Resampling can help it work better
    # XXX why specifically .1 in voxel_size ?
    voxel_size_z = anat_img.header.get_zooms()[2]
    resampled_registered_anat_filenames = []
    for sliced_registered_anat_filename in sliced_registered_anat_filenames:
        out_resample = resample(in_file=sliced_registered_anat_filename,
                                voxel_size=(voxel_size_x, voxel_size_y,
                                            voxel_size_z),
                                outputtype='NIFTI_GZ',
                                environ=environ)
        resampled_registered_anat_filenames.append(
            out_resample.outputs.out_file)

    resampled_bias_corrected_filenames = []
    for sliced_bias_corrected_filename in sliced_bias_corrected_filenames:
        out_resample = resample(in_file=sliced_bias_corrected_filename,
                                voxel_size=(voxel_size_x, voxel_size_y,
                                            voxel_size_z),
                                outputtype='NIFTI_GZ',
                                environ=environ)
        resampled_bias_corrected_filenames.append(
            out_resample.outputs.out_file)

    # single slice non-linear functional to anatomical registration
    warped_slices = []
    warp_filenames = []
    for (resampled_bias_corrected_filename,
         resampled_registered_anat_filename) in zip(
             resampled_bias_corrected_filenames,
             resampled_registered_anat_filenames):
        warped_slice = fname_presuffix(resampled_bias_corrected_filename,
                                       suffix='_qw')
        out_qwarp = qwarp(
            in_file=resampled_bias_corrected_filename,
            base_file=resampled_registered_anat_filename,
            iwarp=True,  # XXX: is this necessary
            noneg=True,
            blur=[0],
            nmi=True,
            noXdis=True,
            allineate=True,
            allineate_opts='-parfix 1 0 -parfix 2 0 -parfix 3 0 '
            '-parfix 4 0 -parfix 5 0 -parfix 6 0 '
            '-parfix 7 0 -parfix 9 0 '
            '-parfix 10 0 -parfix 12 0',
            out_file=warped_slice,
            environ=environ)
        warped_slices.append(out_qwarp.outputs.warped_source)
        warp_filenames.append(out_qwarp.outputs.source_warp)
        output_files.append(out_qwarp.outputs.base_warp)
        # There are files geenrated by the allineate option
        output_files.extend([
            fname_presuffix(out_qwarp.outputs.warped_source, suffix='_Allin'),
            fname_presuffix(out_qwarp.outputs.warped_source,
                            suffix='_Allin.nii',
                            use_ext=False),
            fname_presuffix(out_qwarp.outputs.warped_source,
                            suffix='_Allin.aff12.1D',
                            use_ext=False)
        ])

    # Resample the mean volume back to the initial resolution,
    voxel_size = nibabel.load(unbiased_func_filename).header.get_zooms()
    resampled_warped_slices = []
    for warped_slice in warped_slices:
        out_resample = resample(in_file=warped_slice,
                                voxel_size=voxel_size,
                                outputtype='NIFTI_GZ',
                                environ=environ)
        resampled_warped_slices.append(out_resample.outputs.out_file)

    # fix the obliquity
    resampled_warped_slices_oblique = []
    for (sliced_registered_anat_filename,
         resampled_warped_slice) in zip(sliced_registered_anat_filenames,
                                        resampled_warped_slices):
        oblique_slice = fix_obliquity(resampled_warped_slice,
                                      sliced_registered_anat_filename,
                                      verbose=verbose)
        resampled_warped_slices_oblique.append(oblique_slice)

    # slice functional
    sliced_func_filenames = []
    for slice_n in range(n_slices):
        out_slicer = slicer(in_file=allineated_filename,
                            keep='{0} {0}'.format(slice_n),
                            out_file=fname_presuffix(allineated_filename,
                                                     suffix='Sl%d' % slice_n),
                            environ=environ)
        oblique_slice = fix_obliquity(out_slicer.outputs.out_file,
                                      allineated_filename,
                                      verbose=verbose)
        sliced_func_filenames.append(oblique_slice)

    # Apply the precomputed warp slice by slice
    warped_func_slices = []
    for (sliced_func_filename, warp_filename) in zip(sliced_func_filenames,
                                                     warp_filenames):
        out_warp_apply = warp_apply(in_file=sliced_func_filename,
                                    master=sliced_func_filename,
                                    warp=warp_filename,
                                    out_file=fname_presuffix(
                                        sliced_func_filename, suffix='_qw'),
                                    environ=environ)
        warped_func_slices.append(out_warp_apply.outputs.out_file)

    # Finally, merge all slices !
    out_merge_func = merge(in_files=warped_func_slices,
                           outputtype='NIFTI_GZ',
                           environ=environ)

    # Fix the obliquity
    merged_oblique = fix_obliquity(out_merge_func.outputs.out_file,
                                   allineated_filename,
                                   verbose=verbose)

    # Update the fmri data
    setattr(session_data, "coreg_func_", merged_oblique)
    setattr(session_data, "coreg_anat_", registered_anat_oblique_filename)
    setattr(session_data, "coreg_transform_", transform_filename)
    os.chdir(current_dir)

    # Collect the outputs
    output_files.extend(sliced_registered_anat_filenames +
                        sliced_bias_corrected_filenames +
                        resampled_registered_anat_filenames +
                        resampled_bias_corrected_filenames + warped_slices +
                        warp_filenames + resampled_warped_slices_oblique +
                        sliced_func_filenames + warped_func_slices)
    if not caching:
        for out_file in output_files:
            if os.path.isfile(out_file):
                os.remove(out_file)
Ejemplo n.º 9
0
import sys
import nipype
import nipype.pipeline as pe

import nipype.interfaces.io as io
import nipype.interfaces.afni as afni

#Flexibly collect data from disk to feed into workflows.
io_SelectFiles = pe.Node(io.SelectFiles(templates={'subj':['001', '002']}), name='io_SelectFiles')
)
io_SelectFiles.inputs.base_directory = '/input'
io_SelectFiles.inputs.subj = ['001', '002']

#Wraps the executable command ``3dTshift``.
afni_TShift = pe.Node(interface = afni.TShift(), name='afni_TShift')

#Wraps the executable command ``3dvolreg``.
afni_Volreg = pe.Node(interface = afni.Volreg(), name='afni_Volreg')

#Wraps the executable command ``align_epi_anat.py``.
afni_AlignEpiAnatPy = pe.Node(interface = afni.AlignEpiAnatPy(), name='afni_AlignEpiAnatPy')
afni_AlignEpiAnatPy.inputs.epi_base = 0
afni_AlignEpiAnatPy.inputs.anat2epi = False
afni_AlignEpiAnatPy.inputs.epi2anat = True
afni_AlignEpiAnatPy.inputs.volreg = 'off'
afni_AlignEpiAnatPy.inputs.tshift = 'off'
afni_AlignEpiAnatPy.inputs.outputtype = 'NIFTI_GZ'

#Generic datasink module to store structured outputs
io_DataSink = pe.Node(interface = io.DataSink(), name='io_DataSink')
Ejemplo n.º 10
0
def init_bold_stc_wf(metadata, name='bold_stc_wf'):
    """
    This workflow performs :abbr:`STC (slice-timing correction)` over the input
    :abbr:`BOLD (blood-oxygen-level dependent)` image.

    .. workflow::
        :graph2use: orig
        :simple_form: yes

        from fmriprep.workflows.bold import init_bold_stc_wf
        wf = init_bold_stc_wf(
            metadata={"RepetitionTime": 2.0,
                      "SliceTiming": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]},
            )

    **Parameters**

        metadata : dict
            BIDS metadata for BOLD file
        name : str
            Name of workflow (default: ``bold_stc_wf``)

    **Inputs**

        bold_file
            BOLD series NIfTI file
        skip_vols
            Number of non-steady-state volumes detected at beginning of ``bold_file``

    **Outputs**

        stc_file
            Slice-timing corrected BOLD series NIfTI file

    """
    workflow = pe.Workflow(name=name)
    inputnode = pe.Node(
        niu.IdentityInterface(fields=['bold_file', 'skip_vols']),
        name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(fields=['stc_file']),
                         name='outputnode')

    LOGGER.log(25, 'Slice-timing correction will be included.')

    def create_custom_slice_timing_file_func(metadata):
        import os
        slice_timings_sec = ["%f" % t for t in metadata["SliceTiming"]]
        out_file = os.path.abspath("timings.1D")
        with open(out_file, "w") as fp:
            fp.write("\t".join(slice_timings_sec))
        return out_file

    create_custom_slice_timing_file = pe.Node(
        niu.Function(function=create_custom_slice_timing_file_func),
        name="create_custom_slice_timing_file",
        mem_gb=DEFAULT_MEMORY_MIN_GB)
    create_custom_slice_timing_file.inputs.metadata = metadata

    # It would be good to fingerprint memory use of afni.TShift
    slice_timing_correction = pe.Node(afni.TShift(
        outputtype='NIFTI_GZ', tr='{}s'.format(metadata["RepetitionTime"])),
                                      name='slice_timing_correction')

    copy_xform = pe.Node(CopyXForm(), name='copy_xform', mem_gb=0.1)

    def _prefix_at(x):
        return "@%s" % x

    workflow.connect([
        (inputnode, slice_timing_correction, [('bold_file', 'in_file'),
                                              ('skip_vols', 'ignore')]),
        (create_custom_slice_timing_file, slice_timing_correction,
         [(('out', _prefix_at), 'tpattern')]),
        (slice_timing_correction, copy_xform, [('out_file', 'in_file')]),
        (inputnode, copy_xform, [('bold_file', 'hdr_file')]),
        (copy_xform, outputnode, [('out_file', 'stc_file')]),
    ])

    return workflow
Ejemplo n.º 11
0
def mod_realign(node, in_file, tr, do_slicetime, sliceorder, parameters={}):
    import nipype.interfaces.fsl as fsl
    import nipype.interfaces.spm as spm
    import nipype.interfaces.nipy as nipy
    import os
    parameter_source = "FSL"
    keys = parameters.keys()
    if node == "nipy":
        realign = nipy.FmriRealign4d()
        realign.inputs.in_file = in_file
        realign.inputs.tr = tr
        if "loops" in keys:
            realign.inputs.loops = parameters["loops"]
        if "speedup" in keys:
            realign.inputs.speedup = parameters["speedup"]
        if "between_loops" in keys:
            realign.inputs.between_loops = parameters["between_loops"]
        if do_slicetime:
            realign.inputs.slice_order = sliceorder
            realign.inputs.time_interp = True

        res = realign.run()
        out_file = res.outputs.out_file
        par_file = res.outputs.par_file

    elif node == "fsl":
        if not isinstance(in_file, list):
            in_file = [in_file]
        out_file = []
        par_file = []
        # get the first volume of first run as ref file
        if not do_slicetime:
            extract = fsl.ExtractROI()
            extract.inputs.t_min = 0
            extract.inputs.t_size = 1
            extract.inputs.in_file = in_file[0]
            ref_vol = extract.run().outputs.roi_file

        for idx, file in enumerate(in_file):
            if do_slicetime:
                slicetime = fsl.SliceTimer()
                slicetime.inputs.in_file = file
                sliceorder_file = os.path.abspath('FSL_custom_order.txt')
                with open(sliceorder_file, 'w') as custom_order_fp:
                    for t in sliceorder:
                        custom_order_fp.write('%d\n' % (t + 1))
                slicetime.inputs.custom_order = sliceorder_file
                slicetime.inputs.time_repetition = tr
                res = slicetime.run()
                file_to_realign = res.outputs.slice_time_corrected_file
                if not idx:
                    extract = fsl.ExtractROI()
                    extract.inputs.t_min = 0
                    extract.inputs.t_size = 1
                    extract.inputs.in_file = file_to_realign
                    ref_vol = extract.run().outputs.roi_file
            else:
                file_to_realign = file
            realign = fsl.MCFLIRT(interpolation='spline', ref_file=ref_vol)
            realign.inputs.save_plots = True
            realign.inputs.mean_vol = True
            realign.inputs.in_file = file_to_realign
            realign.inputs.out_file = 'fsl_corr_' + \
                                      os.path.split(file_to_realign)[1]
            Realign_res = realign.run()
            out_file.append(Realign_res.outputs.out_file)
            par_file.append(Realign_res.outputs.par_file)

    elif node == 'spm':
        import numpy as np
        import nibabel as nib
        import nipype.interfaces.freesurfer as fs
        if not isinstance(in_file, list):
            in_file = [in_file]
        new_in_file = []
        for f in in_file:
            if f.endswith('.nii.gz'):
                convert = fs.MRIConvert()
                convert.inputs.in_file = f
                convert.inputs.out_type = 'nii'
                convert.inputs.in_type = 'niigz'
                f = convert.run().outputs.out_file
                new_in_file.append(f)
            else:
                new_in_file.append(f)
        if do_slicetime:
            img = nib.load(new_in_file[0])
            num_slices = img.shape[2]
            st = spm.SliceTiming()
            st.inputs.in_files = new_in_file
            st.inputs.num_slices = num_slices
            st.inputs.time_repetition = tr
            st.inputs.time_acquisition = tr - tr / num_slices
            st.inputs.slice_order = (np.asarray(sliceorder) +
                                     1).astype(int).tolist()
            st.inputs.ref_slice = 1
            res_st = st.run()
            file_to_realign = res_st.outputs.timecorrected_files
        else:
            file_to_realign = new_in_file
        par_file = []
        realign = spm.Realign()
        realign.inputs.in_files = file_to_realign
        #realign.inputs.out_prefix = 'spm_corr_'
        res = realign.run()
        parameters = res.outputs.realignment_parameters
        if not isinstance(parameters, list):
            parameters = [parameters]
        par_file = parameters
        parameter_source = 'SPM'
        fsl.ImageMaths(in_file=res.outputs.realigned_files,
                       out_file=res.outputs.realigned_files,
                       op_string='-nan').run()
        out_file = res.outputs.realigned_files
    elif node == 'afni':
        import nipype.interfaces.afni as afni
        import nibabel as nib
        import numpy as np
        if not isinstance(in_file, list):
            in_file = [in_file]
        img = nib.load(in_file[0])
        Nz = img.shape[2]
        out_file = []
        par_file = []
        # get the first volume of first run as ref file
        if not do_slicetime:
            extract = fsl.ExtractROI()
            extract.inputs.t_min = 0
            extract.inputs.t_size = 1
            extract.inputs.in_file = in_file[0]
            ref_vol = extract.run().outputs.roi_file

        for idx, file in enumerate(in_file):
            if do_slicetime:
                slicetime = afni.TShift()
                slicetime.inputs.in_file = file
                custom_order = open(
                    os.path.abspath('afni_custom_order_file.txt'), 'w')
                tpattern = []
                for i in xrange(len(sliceorder)):
                    tpattern.append((i * tr / float(Nz), sliceorder[i]))
                tpattern.sort(key=lambda x: x[1])
                for i, t in enumerate(tpattern):
                    print '%f\n' % (t[0])
                    custom_order.write('%f\n' % (t[0]))
                custom_order.close()

                slicetime.inputs.args = '-tpattern @%s' % os.path.abspath(
                    'afni_custom_order_file.txt')
                slicetime.inputs.tr = str(tr) + 's'
                slicetime.inputs.outputtype = 'NIFTI_GZ'
                res = slicetime.run()
                file_to_realign = res.outputs.out_file

                if not idx:
                    extract = fsl.ExtractROI()
                    extract.inputs.t_min = 0
                    extract.inputs.t_size = 1
                    extract.inputs.in_file = file_to_realign
                    ref_vol = extract.run().outputs.roi_file

            else:
                file_to_realign = file

            realign = afni.Volreg()
            realign.inputs.in_file = file_to_realign
            realign.inputs.out_file = "afni_corr_" + os.path.split(
                file_to_realign)[1]
            realign.inputs.oned_file = "afni_realignment_parameters.par"
            realign.inputs.basefile = ref_vol
            Realign_res = realign.run()
            out_file.append(Realign_res.outputs.out_file)

            parameters = Realign_res.outputs.oned_file
            if not isinstance(parameters, list):
                parameters = [parameters]
            for i, p in enumerate(parameters):
                foo = np.genfromtxt(p)
                boo = foo[:, [1, 2, 0, 4, 5, 3]]
                boo[:, :3] = boo[:, :3] * np.pi / 180
                np.savetxt(os.path.abspath('realignment_parameters_%d.par' %
                                           i),
                           boo,
                           delimiter='\t')
                par_file.append(
                    os.path.abspath('realignment_parameters_%d.par' % i))

            #par_file.append(Realign_res.outputs.oned_file)

    return out_file, par_file, parameter_source
Ejemplo n.º 12
0
def init_bold_stc_wf(metadata, name='bold_stc_wf'):
    """
    Create a workflow for :abbr:`STC (slice-timing correction)`.

    This workflow performs :abbr:`STC (slice-timing correction)` over the input
    :abbr:`BOLD (blood-oxygen-level dependent)` image.

    Workflow Graph
        .. workflow::
            :graph2use: orig
            :simple_form: yes

            from fmriprep.workflows.bold import init_bold_stc_wf
            wf = init_bold_stc_wf(
                metadata={"RepetitionTime": 2.0,
                          "SliceTiming": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]},
                )

    Parameters
    ----------
    metadata : :obj:`dict`
        BIDS metadata for BOLD file
    name : :obj:`str`
        Name of workflow (default: ``bold_stc_wf``)

    Inputs
    ------
    bold_file
        BOLD series NIfTI file
    skip_vols
        Number of non-steady-state volumes detected at beginning of ``bold_file``

    Outputs
    -------
    stc_file
        Slice-timing corrected BOLD series NIfTI file

    """
    from niworkflows.engine.workflows import LiterateWorkflow as Workflow
    from niworkflows.interfaces.utils import CopyXForm

    workflow = Workflow(name=name)
    workflow.__desc__ = """\
BOLD runs were slice-time corrected using `3dTshift` from
AFNI {afni_ver} [@afni, RRID:SCR_005927].
""".format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []]))
    inputnode = pe.Node(
        niu.IdentityInterface(fields=['bold_file', 'skip_vols']),
        name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(fields=['stc_file']),
                         name='outputnode')

    LOGGER.log(25, 'Slice-timing correction will be included.')

    # It would be good to fingerprint memory use of afni.TShift
    slice_timing_correction = pe.Node(afni.TShift(
        outputtype='NIFTI_GZ',
        tr='{}s'.format(metadata["RepetitionTime"]),
        slice_timing=metadata['SliceTiming'],
        slice_encoding_direction=metadata.get('SliceEncodingDirection', 'k')),
                                      name='slice_timing_correction')

    copy_xform = pe.Node(CopyXForm(), name='copy_xform', mem_gb=0.1)

    workflow.connect([
        (inputnode, slice_timing_correction, [('bold_file', 'in_file'),
                                              ('skip_vols', 'ignore')]),
        (slice_timing_correction, copy_xform, [('out_file', 'in_file')]),
        (inputnode, copy_xform, [('bold_file', 'hdr_file')]),
        (copy_xform, outputnode, [('out_file', 'stc_file')]),
    ])

    return workflow
Ejemplo n.º 13
0
def SLICETIMER2():
    import nipype.pipeline.engine as pe
    import matplotlib
    import os
    os.system('clear')
    from glob import glob
    from nilearn import plotting
    from nilearn import image
    from nipype.interfaces.utility import Function
    import nipype.interfaces.utility as util
    import nipype.interfaces.fsl.utils as fsl
    from nipype.interfaces import afni

    #--- 2)  Record intial working directory

    INITDIR = os.getcwd()

    #--- 3) Prompt user for directory containing DICOM FILES

    NIFTIFILE = input(
        'Please drag in the nifti\n file you wish to slicetime\n(ensure there is no blank space at the end)\n'
    )

    SLICENUM = input('Please enter the slice number you want to correct to')

    print('alt+z = alternating in the plus direction\n')
    print('alt+z2  = alternating, starting at slice #1 instead of #0\n')
    print('alt-z = alternating in the minus direction\n')
    print('alt-z2   = alternating, starting at slice #nz-2 instead of #nz-1\n')
    print('seq+z = seqplus   = sequential in the plus direction\n')
    print('seq-z = seqminus  = sequential in the minus direction\n')

    AQORDER = input(
        'Please enter the string for slice acquisition order (see above)')

    TR = input('Please enter the TR(s)')

    inputnode = pe.Node(interface=util.IdentityInterface(
        fields=['file', 'slicenum', 'aqorder', 'tr']),
                        name='inputspec')

    if type(NIFTIFILE) == str:
        inputnode.inputs.file = NIFTIFILE
        NIFTIDIR = os.path.split(NIFTIFILE)[0]
        os.chdir(NIFTIDIR)
    elif type(NIFTIFILE) == list:
        inputnode.iterables = ([('file', NIFTIFILE)])
        NIFTIDIR = os.path.split(NIFTIFILE[0])[0]
        os.chdir(NIFTIDIR)

    if type(SLICENUM) == int:
        inputnode.inputs.slicenum = SLICENUM
    elif type(SLICENUM) == list:
        inputnode.iterables = ([('slicenum', SLICENUM)])

    if type(AQORDER) == str:
        inputnode.inputs.aqorder = AQORDER
    elif type(AQORDER) == list:
        inputnode.iterables = ([('aqorder', AQORDER)])

    inputnode.inputs.tr = str(TR) + 's'

    slicetimer = pe.Node(interface=afni.TShift(), name='SLICETIMED')
    slicetimer.inputs.outputtype = ('NIFTI_GZ')

    #--- 7) Set up workflow

    workflow = pe.Workflow(name='SLICETIMER2')
    workflow.base_dir = os.getcwd()

    def tshiftplot(pre_file, post_file):
        import matplotlib.patches as mpatches
        import matplotlib.pyplot as plt
        import matplotlib
        import numpy as np
        import nibabel as nib
        img1 = nib.load(pre_file)
        data = img1.get_data()
        img2 = nib.load(post_file)
        data2 = img2.get_data()
        fig, ax = plt.subplots(figsize=(5, 10))
        mid = round(data.shape[0] / 2)
        for i in range(data.shape[2]):
            for j in range(1):
                ax = plt.subplot2grid((data.shape[2], 1), (i, j))
                ax.plot(data[mid, mid, i, 1:20], 'ro')
                ax.plot(data[mid, mid, i, 1:20], 'r-')
                ax.plot(data2[mid, mid, i, 1:20], 'g^')
                ax.plot(data2[mid, mid, i, 1:20], 'g-')
                print('ploting slice' + ' ' + str(i + 1))
                ax.yaxis.set_visible(False)
        green_patch = mpatches.Patch(color='green', label='The shifted data')
        legend2 = plt.legend(handles=[green_patch], loc=4)
        plt.gca().add_artist(legend2)
        red_patch = mpatches.Patch(color='red', label='The original data')
        plt.legend(handles=[red_patch], loc=3)
        plt.show()

    TSHIFTPLOT = pe.Node(Function(input_names=['pre_file', 'post_file'],
                                  output_names=['fig'],
                                  function=tshiftplot),
                         name='TSHIFTPLOT')

    #--- 8) Connect nodes.

    workflow.connect(inputnode, 'file', slicetimer, 'in_file')
    workflow.connect(inputnode, 'slicenum', slicetimer, 'tslice')
    workflow.connect(inputnode, 'aqorder', slicetimer, 'tpattern')
    workflow.connect(inputnode, 'tr', slicetimer, 'tr')
    workflow.connect(inputnode, 'file', TSHIFTPLOT, 'pre_file')
    workflow.connect(slicetimer, 'out_file', TSHIFTPLOT, 'post_file')

    workflow.write_graph(graph2use='exec')

    #--- 9) Run workflow

    result = workflow.run()

    print "Node completed. Returning to intital directory\n"

    os.chdir(INITDIR)
Ejemplo n.º 14
0
#Generic datagrabber module that wraps around glob in an
io_S3DataGrabber = pe.Node(io.S3DataGrabber(outfields=["outfiles"]),
                           name='io_S3DataGrabber')
io_S3DataGrabber.inputs.bucket = 'openneuro'
io_S3DataGrabber.inputs.sort_filelist = True
io_S3DataGrabber.inputs.template = 'sub-01/anat/sub-01_T1w.nii.gz'
io_S3DataGrabber.inputs.anon = True
io_S3DataGrabber.inputs.bucket_path = 'ds000101/ds000101_R2.0.0/uncompressed/'
io_S3DataGrabber.inputs.local_directory = '/tmp'

#Wraps command **bet**
fsl_BET = pe.Node(interface=fsl.BET(), name='fsl_BET', iterfield=[''])

#Wraps command **3dTshift**
afni_TShift = pe.Node(interface=afni.TShift(),
                      name='afni_TShift',
                      iterfield=[''])

#Wraps command **3dUnifize**
afni_Unifize = pe.Node(interface=afni.Unifize(),
                       name='afni_Unifize',
                       iterfield=[''])

#Generic datagrabber module that wraps around glob in an
io_S3DataGrabber_2 = pe.Node(io.S3DataGrabber(), name='io_S3DataGrabber_2')

#Wraps command **fslreorient2std**
fsl_Reorient2Std = pe.Node(interface=fsl.Reorient2Std(),
                           name='fsl_Reorient2Std',
                           iterfield=[''])
Ejemplo n.º 15
0
def slt_workflow(slicetiming_txt="alt+z",SinkTag="func_preproc",wf_name="slicetiming_correction"):

    """
    Modified version of porcupine generated slicetiming code:

    `source: -`


    Creates a slice time corrected functional image.

    Workflow inputs:
        :param func: The reoriented functional file.
        :param SinkDir:
        :param SinkTag: The output directory in which the returned images (see workflow outputs) could be found in a subdirectory directory specific for this workflow.

    Workflow outputs:




        :return: slt_workflow - workflow




    Balint Kincses
    [email protected]
    2018


    """



    # This is a Nipype generator. Warning, here be dragons.
    # !/usr/bin/env python

    import sys
    import os
    import nipype
    import nipype.pipeline as pe
    import nipype.interfaces.utility as utility
    import PUMI.func_preproc.info.info_get as info_get
    import PUMI.utils.utils_convert as utils_convert
    import nipype.interfaces.afni as afni
    import PUMI.utils.globals as globals

    SinkDir = os.path.abspath(globals._SinkDir_ + "/" + SinkTag)
    if not os.path.exists(SinkDir):
        os.makedirs(SinkDir)

    # Basic interface class generates identity mappings
    inputspec = pe.Node(utility.IdentityInterface(fields=['func',
                                                          'slicetiming_txt']),
                                    name='inputspec')
    inputspec.inputs.func = func
    inputspec.inputs.slicetiming_txt = slicetiming_txt

    # Custom interface wrapping function TR
    #NodeHash_6000004b9860 = pe.MapNode(interface=info_get.TR, name='NodeName_6000004b9860', iterfield=['in_file'])
    TRvalue = pe.Node(interface=info_get.TR,
                      name='TRvalue')

    # Custom interface wrapping function Str2Float
    func_str2float = pe.Node(interface=utils_convert.Str2Float,
                                name='func_str2float')

    # Custom interface wrapping function Float2Str
    func_str2float_2 = pe.Node(interface=utils_convert.Float2Str,
                               name='func_str2float_2')

    # Wraps command **3dTshift**
    sltcor = pe.Node(interface=afni.TShift(),
                     name='sltcor')
    sltcor.inputs.rltplus = True
    sltcor.inputs.outputtype = "NIFTI_GZ"
    #sltcor.inputs.terminal_output = 'allatonce'

    # Basic interface class generates identity mappings
    outputspec = pe.Node(utility.IdentityInterface(fields=['slicetimed', 'TR']),
                                    name='outputspec')

    #todo: qc timeseries

    # Custom interface wrapping function JoinVal2Dict
    #func_joinval2dict = pe.Node(interface=utils_convert.JoinVal2Dict,
    #                            name='func_joinval2dict')

    # Generic datasink module to store structured outputs
    ds = pe.Node(interface=io.DataSink(),
                 name='ds')
    ds.inputs.base_directory = SinkDir
    #ds.inputs.regexp_substitutions = [("func_slicetimed/_NodeName_.{13}", "")]





    # Create a workflow to connect all those nodes
    analysisflow = nipype.Workflow(wf_name)
    analysisflow.connect(inputspec, 'slicetiming_txt', sltcor, 'tpattern')
    analysisflow.connect(func_str2float, 'float', outputspec, 'TR')
    analysisflow.connect(inputspec, 'func', sltcor, 'in_file')
    analysisflow.connect(inputspec, 'func', TRvalue, 'in_file')
    analysisflow.connect(func_str2float_2, 'str', sltcor, 'tr')
    analysisflow.connect(TRvalue, 'TR', func_str2float_2, 'float')
    #analysisflow.connect(ds, 'out_file', func_joinval2dict, 'keys')
    #analysisflow.connect(func_str2float, 'float', func_joinval2dict, 'vals')
    analysisflow.connect(TRvalue, 'TR', func_str2float, 'str')
    analysisflow.connect(sltcor, 'out_file', ds, 'slicetimed')
    analysisflow.connect(sltcor, 'out_file', outputspec, 'slicetimed')



    return analysisflow
Ejemplo n.º 16
0
def hmc_afni(name='fMRI_HMC_afni',
             st_correct=False,
             despike=False,
             deoblique=False,
             start_idx=None,
             stop_idx=None):
    """
    A :abbr:`HMC (head motion correction)` workflow for
    functional scans

    .. workflow::

      from mriqc.workflows.functional import hmc_afni
      wf = hmc_afni()

    """

    workflow = pe.Workflow(name=name)

    inputnode = pe.Node(niu.IdentityInterface(
        fields=['in_file', 'fd_radius', 'start_idx', 'stop_idx']),
                        name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_fd']),
                         name='outputnode')

    if (start_idx is not None) or (stop_idx is not None):
        drop_trs = pe.Node(afni.Calc(expr='a', outputtype='NIFTI_GZ'),
                           name='drop_trs')
        workflow.connect([
            (inputnode, drop_trs, [('in_file', 'in_file_a'),
                                   ('start_idx', 'start_idx'),
                                   ('stop_idx', 'stop_idx')]),
        ])
    else:
        drop_trs = pe.Node(niu.IdentityInterface(fields=['out_file']),
                           name='drop_trs')
        workflow.connect([
            (inputnode, drop_trs, [('in_file', 'out_file')]),
        ])

    get_mean_RPI = pe.Node(afni.TStat(options='-mean', outputtype='NIFTI_GZ'),
                           name='get_mean_RPI')

    # calculate hmc parameters
    hmc = pe.Node(afni.Volreg(args='-Fourier -twopass',
                              zpad=4,
                              outputtype='NIFTI_GZ'),
                  name='motion_correct')

    get_mean_motion = get_mean_RPI.clone('get_mean_motion')
    hmc_A = hmc.clone('motion_correct_A')
    hmc_A.inputs.md1d_file = 'max_displacement.1D'

    # Compute the frame-wise displacement
    fdnode = pe.Node(nac.FramewiseDisplacement(normalize=False,
                                               parameter_source="AFNI"),
                     name='ComputeFD')

    workflow.connect([
        (inputnode, fdnode, [('fd_radius', 'radius')]),
        (get_mean_RPI, hmc, [('out_file', 'basefile')]),
        (hmc, get_mean_motion, [('out_file', 'in_file')]),
        (get_mean_motion, hmc_A, [('out_file', 'basefile')]),
        (hmc_A, outputnode, [('out_file', 'out_file')]),
        (hmc_A, fdnode, [('oned_file', 'in_file')]),
        (fdnode, outputnode, [('out_file', 'out_fd')]),
    ])

    # Slice timing correction, despiking, and deoblique

    st_corr = pe.Node(afni.TShift(outputtype='NIFTI_GZ'), name='TimeShifts')

    deoblique_node = pe.Node(afni.Refit(deoblique=True), name='deoblique')

    despike_node = pe.Node(afni.Despike(outputtype='NIFTI_GZ'), name='despike')

    if st_correct and despike and deoblique:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, despike_node, [('out_file', 'in_file')]),
            (despike_node, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, get_mean_RPI, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
            (deoblique_node, hmc_A, [('out_file', 'in_file')]),
        ])

    elif st_correct and despike:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, despike_node, [('out_file', 'in_file')]),
            (despike_node, get_mean_RPI, [('out_file', 'in_file')]),
            (despike_node, hmc, [('out_file', 'in_file')]),
            (despike_node, hmc_A, [('out_file', 'in_file')]),
        ])

    elif st_correct and deoblique:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, get_mean_RPI, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
            (deoblique_node, hmc_A, [('out_file', 'in_file')]),
        ])

    elif st_correct:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, get_mean_RPI, [('out_file', 'in_file')]),
            (st_corr, hmc, [('out_file', 'in_file')]),
            (st_corr, hmc_A, [('out_file', 'in_file')]),
        ])

    elif despike and deoblique:

        workflow.connect([
            (drop_trs, despike_node, [('out_file', 'in_file')]),
            (despike_node, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, get_mean_RPI, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
            (deoblique_node, hmc_A, [('out_file', 'in_file')]),
        ])

    elif despike:

        workflow.connect([
            (drop_trs, despike_node, [('out_file', 'in_file')]),
            (despike_node, get_mean_RPI, [('out_file', 'in_file')]),
            (despike_node, hmc, [('out_file', 'in_file')]),
            (despike_node, hmc_A, [('out_file', 'in_file')]),
        ])

    elif deoblique:

        workflow.connect([
            (drop_trs, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, get_mean_RPI, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
            (deoblique_node, hmc_A, [('out_file', 'in_file')]),
        ])

    else:

        workflow.connect([
            (drop_trs, get_mean_RPI, [('out_file', 'in_file')]),
            (drop_trs, hmc, [('out_file', 'in_file')]),
            (drop_trs, hmc_A, [('out_file', 'in_file')]),
        ])

    return workflow
Ejemplo n.º 17
0
    #motcor_sltimes_wf.connect(motion_correct, 'out_file', motion_sltime_correct, 'in_file')
    #motcor_sltimes_wf.connect(motion_sltime_correct, 'slice_time_corrected_file', outputspec, 'motion_sltime_corrected_files')
elif slice_timer is 'AFNI':
    # Motion correct functional runs
    motion_correct = pe.MapNode(afni.Volreg(outputtype='NIFTI_GZ'),
                                name='motion_correct',
                                iterfield=['in_file'])
    motcor_sltimes_wf.connect(datasource, 'mri_files', motion_correct,
                              'in_file')
    motcor_sltimes_wf.connect(extractref, 'roi_file', motion_correct,
                              'basefile')
    motcor_sltimes_wf.connect(motion_correct, 'oned_file', outputspec,
                              'motion_parameters')

    # Slice timing correction
    motion_sltime_correct = pe.MapNode(afni.TShift(outputtype='NIFTI_GZ'),
                                       name='motion_sltime_correct',
                                       iterfield=['in_file'])
    motion_sltime_correct.inputs.tr = '2.0s'
    motion_sltime_correct.inputs.tpattern = 'altplus'
    motcor_sltimes_wf.connect(motion_correct, 'out_file',
                              motion_sltime_correct, 'in_file')
    motcor_sltimes_wf.connect(motion_sltime_correct, 'out_file', outputspec,
                              'motion_sltime_corrected_files')

# Plot the estimated motion parameters
plot_motion = pe.MapNode(fsl.PlotMotionParams(in_source='fsl'),
                         name='plot_motion',
                         iterfield=['in_file'])
plot_motion.iterables = ('plot_type', ['rotations', 'translations'])
if slice_timer is 'NIPY':
Ejemplo n.º 18
0
def init_epi_hmc_wf(metadata, bold_file_size_gb, ignore,
                    name='epi_hmc_wf'):
    """
    Performs :abbr:`HMC (head motion correction)` over the input
    :abbr:`EPI (echo-planar imaging)` image.
    """
    workflow = pe.Workflow(name=name)
    inputnode = pe.Node(niu.IdentityInterface(fields=['epi']),
                        name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(
        fields=['xforms', 'epi_hmc', 'epi_split', 'epi_mask', 'ref_image',
                'ref_image_brain', 'movpar_file', 'n_volumes_to_discard',
                'epi_mask_report']), name='outputnode')

    def normalize_motion_func(in_file, format):
        import os
        import numpy as np
        from nipype.utils.misc import normalize_mc_params
        mpars = np.loadtxt(in_file)  # mpars is N_t x 6
        mpars = np.apply_along_axis(func1d=normalize_mc_params,
                                    axis=1, arr=mpars,
                                    source=format)
        np.savetxt("motion_params.txt", mpars)
        return os.path.abspath("motion_params.txt")

    normalize_motion = pe.Node(niu.Function(function=normalize_motion_func),
                               name="normalize_motion", run_without_submitting=True)
    normalize_motion.inputs.format = "FSL"

    # Head motion correction (hmc)
    hmc = pe.Node(fsl.MCFLIRT(
        save_mats=True, save_plots=True), name='EPI_hmc')
    hmc.interface.estimated_memory_gb = bold_file_size_gb * 3

    hcm2itk = pe.MapNode(c3.C3dAffineTool(fsl2ras=True, itk_transform=True),
                         iterfield=['transform_file'], name='hcm2itk')

    enhance_and_skullstrip_epi_wf = init_enhance_and_skullstrip_epi_wf()

    gen_ref = pe.Node(EstimateReferenceImage(), name="gen_ref")

    workflow.connect([
        (inputnode, gen_ref, [('epi', 'in_file')]),
        (gen_ref, enhance_and_skullstrip_epi_wf, [('ref_image', 'inputnode.in_file')]),
        (gen_ref, hmc, [('ref_image', 'ref_file')]),
        (enhance_and_skullstrip_epi_wf, outputnode, [('outputnode.bias_corrected_file', 'ref_image'),
                                                     ('outputnode.mask_file', 'epi_mask'),
                                                     ('outputnode.out_report', 'epi_mask_report'),
                                                     ('outputnode.skull_stripped_file', 'ref_image_brain')]),
    ])

    split = pe.Node(fsl.Split(dimension='t'), name='split')
    split.interface.estimated_memory_gb = bold_file_size_gb * 3

    if "SliceTiming" in metadata and 'slicetiming' not in ignore:
        LOGGER.info('Slice-timing correction will be included.')

        def create_custom_slice_timing_file_func(metadata):
            import os
            slice_timings = metadata["SliceTiming"]
            slice_timings_ms = [str(t) for t in slice_timings]
            out_file = "timings.1D"
            with open("timings.1D", "w") as fp:
                fp.write("\t".join(slice_timings_ms))

            return os.path.abspath(out_file)

        create_custom_slice_timing_file = pe.Node(
            niu.Function(function=create_custom_slice_timing_file_func),
            name="create_custom_slice_timing_file", run_without_submitting=True)
        create_custom_slice_timing_file.inputs.metadata = metadata

        slice_timing_correction = pe.Node(interface=afni.TShift(),
                                          name='slice_timing_correction')
        slice_timing_correction.inputs.outputtype = 'NIFTI_GZ'
        slice_timing_correction.inputs.tr = str(metadata["RepetitionTime"]) + "s"

        def prefix_at(x):
            return "@" + x

        workflow.connect([
            (inputnode, slice_timing_correction, [('epi', 'in_file')]),
            (gen_ref, slice_timing_correction, [('n_volumes_to_discard', 'ignore')]),
            (create_custom_slice_timing_file, slice_timing_correction, [
                (('out', prefix_at), 'tpattern')]),
            (slice_timing_correction, hmc, [('out_file', 'in_file')])
        ])

    else:
        workflow.connect([
            (inputnode, hmc, [('epi', 'in_file')])
        ])

    workflow.connect([
        (hmc, hcm2itk, [('mat_file', 'transform_file')]),
        (gen_ref, hcm2itk, [('ref_image', 'source_file'),
                            ('ref_image', 'reference_file')]),
        (hcm2itk, outputnode, [('itk_transform', 'xforms')]),
        (hmc, normalize_motion, [('par_file', 'in_file')]),
        (normalize_motion, outputnode, [('out', 'movpar_file')]),
        (inputnode, split, [('epi', 'in_file')]),
        (split, outputnode, [('out_files', 'epi_split')]),
    ])

    return workflow
Ejemplo n.º 19
0
def hmc_afni(name='fMRI_HMC_afni', st_correct=False, despike=False, deoblique=False):
    """A head motion correction (HMC) workflow for functional scans"""

    workflow = pe.Workflow(name=name)

    inputnode = pe.Node(niu.IdentityInterface(
        fields=['in_file', 'fd_radius', 'start_idx', 'stop_idx']), name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(
        fields=['out_file', 'out_fd']), name='outputnode')

    drop_trs = pe.Node(afni.Calc(expr='a', outputtype='NIFTI_GZ'),
                       name='drop_trs')

    reorient = pe.Node(afni.Resample(
        orientation='RPI', outputtype='NIFTI_GZ'), name='reorient')

    get_mean_RPI = pe.Node(afni.TStat(
        options='-mean', outputtype='NIFTI_GZ'), name='get_mean_RPI')

    # calculate hmc parameters
    hmc = pe.Node(
        afni.Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'),
        name='motion_correct')

    get_mean_motion = get_mean_RPI.clone('get_mean_motion')
    hmc_A = hmc.clone('motion_correct_A')
    hmc_A.inputs.md1d_file = 'max_displacement.1D'

    # Compute the frame-wise displacement
    calc_fd = pe.Node(niu.Function(
        function=fd_jenkinson, input_names=['in_file', 'rmax'],
        output_names=['out_fd']), name='calc_fd')

    workflow.connect([
        (inputnode, drop_trs, [('in_file', 'in_file_a'),
                               ('start_idx', 'start_idx'),
                               ('stop_idx', 'stop_idx')]),
        (inputnode, calc_fd, [('fd_radius', 'rmax')]),
        (reorient, get_mean_RPI, [('out_file', 'in_file')]),
        (reorient, hmc, [('out_file', 'in_file')]),
        (get_mean_RPI, hmc, [('out_file', 'basefile')]),
        (hmc, get_mean_motion, [('out_file', 'in_file')]),
        (reorient, hmc_A, [('out_file', 'in_file')]),
        (get_mean_motion, hmc_A, [('out_file', 'basefile')]),
        (hmc_A, outputnode, [('out_file', 'out_file')]),
        (hmc_A, calc_fd, [('oned_matrix_save', 'in_file')]),
        (calc_fd, outputnode, [('out_fd', 'out_fd')]),
    ])

    # Slice timing correction, despiking, and deoblique

    st_corr = pe.Node(afni.TShift(outputtype='NIFTI_GZ'), name='TimeShifts')

    deoblique_node = pe.Node(afni.Refit(deoblique=True), name='deoblique')

    despike_node = pe.Node(afni.Despike(outputtype='NIFTI_GZ'), name='despike')

    if st_correct and despike and deoblique:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, despike_node, [('out_file', 'in_file')]),
            (despike_node, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, reorient, [('out_file', 'in_file')])
        ])

    elif st_correct and despike:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, despike_node, [('out_file', 'in_file')]),
            (despike_node, reorient, [('out_file', 'in_file')]),
        ])

    elif st_correct and deoblique:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, reorient, [('out_file', 'in_file')])
        ])

    elif st_correct:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, reorient, [('out_file', 'in_file')])
        ])

    elif despike and deoblique:

        workflow.connect([
            (drop_trs, despike_node, [('out_file', 'in_file')]),
            (despike_node, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, reorient, [('out_file', 'in_file')])
        ])

    elif despike:

        workflow.connect([
            (drop_trs, despike_node, [('out_file', 'in_file')]),
            (despike_node, reorient, [('out_file', 'in_file')]),
        ])

    elif deoblique:

        workflow.connect([
            (drop_trs, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, reorient, [('out_file', 'in_file')])
        ])

    else:

        workflow.connect([
            (drop_trs, reorient, [('out_file', 'in_file')]),
        ])

    return workflow
Ejemplo n.º 20
0
def afni_slicetime(in_file=traits.Undefined,
                   out_file=traits.Undefined,
                   tr=traits.Undefined,
                   tr_units='s',
                   ignore_first=traits.Undefined,
                   slice_mode=traits.Undefined,
                   ref_slice=traits.Undefined,
                   tzero=traits.Undefined,
                   out_type='NIFTI_GZ'):
    """ Return a nipype interface to the AFNI 3dTshift command.

    Parameters
    ----------
    in_file: str
        Path to the input file

    out_file: str
        Path to the output file.

    tr: int or str
        The TR of the input dataset

    tr_units: str
        The units of `tr`. Choices: ('s', 'ms')

    ignore_first: int
        Number of acquisitions thrown away from the beginning of the
        dataset.

    tzero: float
        Align each slice to time offset `tzero`.
        The value of 'tzero' must be between the
        minimum and maximum slice temporal offsets.
        Note: The default alignment time is the average
              of the 'tpattern' values (either from the
              dataset header or from the -tpattern option)

    ref_slice: int
        Align each slice to the time offset of the given `ref_slice` index.
        Note: only one of the tzero and slice options can be used.
        -tslice flag

    slice_mode: str
        Choices:
        'alt+z' or 'altplus'    = alternating in the plus direction
        'alt+z2'                = alternating, starting at slice #1 instead of #0
        'alt-z' or 'altminus'   = alternating in the minus direction
        'alt-z2'                = alternating, starting at slice #nz-2 instead of #nz-1
        'seq+z' or 'seqplus'    = sequential in the plus direction
        'seq-z' or 'seqminus'   = sequential in the minus direction
         @filename              = read temporal offsets from 'filename'

    out_type: str
        ('NIFTI_GZ' or 'AFNI' or 'NIFTI')
        AFNI output filetype

    Returns
    -------
    tshift: nipype interface
    """
    import nipype.interfaces.afni as afni

    tshift = afni.TShift()

    if isdefined(tr):
        tr = '{}{}'.format(tr, tr_units)

    if isdefined(in_file):
        if not isdefined(out_file):
            out_file = op.join(op.dirname(in_file),
                               'tshift_' + remove_ext(op.basename(in_file)))

    tshift.inputs.in_file = in_file
    tshift.inputs.out_file = out_file
    tshift.inputs.tpattern = slice_mode
    tshift.inputs.tzero = tzero
    tshift.inputs.tr = tr
    tshift.inputs.tslice = ref_slice
    tshift.inputs.ignore = ignore_first
    tshift.inputs.outputtype = out_type

    #res = tshift.run()
    return tshift
Ejemplo n.º 21
0
def hmc_afni(settings, name='fMRI_HMC_afni', st_correct=False, despike=False,
             deoblique=False, start_idx=None, stop_idx=None):
    """
    A :abbr:`HMC (head motion correction)` workflow for
    functional scans

    .. workflow::

      from mriqc.workflows.functional import hmc_afni
      wf = hmc_afni({'biggest_file_size_gb': 1})

    """

    biggest_file_gb = settings.get("biggest_file_size_gb", 1)

    workflow = pe.Workflow(name=name)

    inputnode = pe.Node(niu.IdentityInterface(
        fields=['in_file', 'fd_radius', 'start_idx', 'stop_idx']), name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(
        fields=['out_file', 'out_fd']), name='outputnode')

    if (start_idx is not None) or (stop_idx is not None):
        drop_trs = pe.Node(afni.Calc(expr='a', outputtype='NIFTI_GZ'),
                           name='drop_trs')
        workflow.connect([
            (inputnode, drop_trs, [('in_file', 'in_file_a'),
                                   ('start_idx', 'start_idx'),
                                   ('stop_idx', 'stop_idx')]),
        ])
    else:
        drop_trs = pe.Node(niu.IdentityInterface(fields=['out_file']),
                           name='drop_trs')
        workflow.connect([
            (inputnode, drop_trs, [('in_file', 'out_file')]),
        ])

    gen_ref = pe.Node(nwr.EstimateReferenceImage(mc_method="AFNI"), name="gen_ref")

    # calculate hmc parameters
    hmc = pe.Node(
        afni.Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'),
        name='motion_correct', mem_gb=biggest_file_gb * 2.5)

    # Compute the frame-wise displacement
    fdnode = pe.Node(nac.FramewiseDisplacement(normalize=False,
                                               parameter_source="AFNI"),
                     name='ComputeFD')

    workflow.connect([
        (inputnode, fdnode, [('fd_radius', 'radius')]),
        (gen_ref, hmc, [('ref_image', 'basefile')]),
        (hmc, outputnode, [('out_file', 'out_file')]),
        (hmc, fdnode, [('oned_file', 'in_file')]),
        (fdnode, outputnode, [('out_file', 'out_fd')]),
    ])

    # Slice timing correction, despiking, and deoblique

    st_corr = pe.Node(afni.TShift(outputtype='NIFTI_GZ'), name='TimeShifts')

    deoblique_node = pe.Node(afni.Refit(deoblique=True), name='deoblique')

    despike_node = pe.Node(afni.Despike(outputtype='NIFTI_GZ'), name='despike')

    if st_correct and despike and deoblique:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, despike_node, [('out_file', 'in_file')]),
            (despike_node, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, gen_ref, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
        ])

    elif st_correct and despike:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, despike_node, [('out_file', 'in_file')]),
            (despike_node, gen_ref, [('out_file', 'in_file')]),
            (despike_node, hmc, [('out_file', 'in_file')]),
        ])

    elif st_correct and deoblique:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, gen_ref, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
        ])

    elif st_correct:

        workflow.connect([
            (drop_trs, st_corr, [('out_file', 'in_file')]),
            (st_corr, gen_ref, [('out_file', 'in_file')]),
            (st_corr, hmc, [('out_file', 'in_file')]),
        ])

    elif despike and deoblique:

        workflow.connect([
            (drop_trs, despike_node, [('out_file', 'in_file')]),
            (despike_node, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, gen_ref, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
        ])

    elif despike:

        workflow.connect([
            (drop_trs, despike_node, [('out_file', 'in_file')]),
            (despike_node, gen_ref, [('out_file', 'in_file')]),
            (despike_node, hmc, [('out_file', 'in_file')]),
        ])

    elif deoblique:

        workflow.connect([
            (drop_trs, deoblique_node, [('out_file', 'in_file')]),
            (deoblique_node, gen_ref, [('out_file', 'in_file')]),
            (deoblique_node, hmc, [('out_file', 'in_file')]),
        ])

    else:
        workflow.connect([
            (drop_trs, gen_ref, [('out_file', 'in_file')]),
            (drop_trs, hmc, [('out_file', 'in_file')]),
        ])

    return workflow
Ejemplo n.º 22
0
def init_single_subject_wf(
    name,
    output_dir,
    layout,
    bold_mag_files,
    bold_mag_metadata,
    bold_phase_files,
    bold_phase_metadata,
    sbref_mag_files,
    sbref_mag_metadata,
    sbref_phase_files,
    sbref_phase_metadata,
    t1w_files,
    t1w_metadata,
    t2w_files,
    t2w_metadata,
):

    workflow = pe.Workflow(name=name)

    # name the nodes
    inputnode = pe.Node(
        niu.IdentityInterface(fields=[
            'bold_mag_files',
            'bold_mag_metadata',
            'bold_phase_files',
            'bold_phase_metadata',
            'sbref_mag_files',
            'sbref_mag_metadata',
            'sbref_phase_files',
            'sbref_phase_metadata',
            't1w_files',
            't1w_metadata',
            't2w_files',
            't2w_metadata',
        ]),
        name='inputnode',
        iterables=[
            ('bold_mag_files', bold_mag_files),
            ('bold_mag_metadata', bold_mag_metadata),
            ('bold_phase_files', bold_phase_files),
            ('bold_phase_metadata', bold_phase_metadata),
            ('sbref_mag_files', sbref_mag_files),
            ('sbref_mag_metadata', sbref_mag_metadata),
            ('sbref_phase_files', sbref_phase_files),
            ('sbref_phase_metadata', sbref_phase_metadata),
        ],
        synchronize=True,
    )
    inputnode.inputs.t1w_files = t1w_files
    inputnode.inputs.t1w_metadata = t1w_metadata
    inputnode.inputs.t2w_files = t2w_files
    inputnode.inputs.t2w_metadata = t2w_metadata

    outputnode = pe.Node(
        niu.IdentityInterface(fields=[
            'preproc_bold_files', 'preproc_phase_files', 'motion_parameters'
        ]),
        name='outputnode',
    )

    # Generate single-band reference image-based field maps
    sbref_phdiff_wf = init_phdiff_wf(name='sbref_phdiff_wf',
                                     create_phasediff=True,
                                     omp_nthreads=1,
                                     fmap_bspline=None)
    workflow.connect(
        inputnode,
        ('sbref_phase_files', pick_first),
        sbref_phdiff_wf,
        'inputnode.phase1',
    )
    workflow.connect(
        inputnode,
        ('sbref_phase_files', pick_second),
        sbref_phdiff_wf,
        'inputnode.phase2',
    )
    workflow.connect(
        inputnode,
        ('sbref_mag_files', pick_first),
        sbref_phdiff_wf,
        'inputnode.magnitude',
    )
    workflow.connect(
        inputnode,
        ('sbref_phase_metadata', pick_first),
        sbref_phdiff_wf,
        'inputnode.phase1_metadata',
    )
    workflow.connect(
        inputnode,
        ('sbref_phase_metadata', pick_second),
        sbref_phdiff_wf,
        'inputnode.phase2_metadata',
    )

    # Generate dynamic field maps
    # Somehow select the first two echoes
    docma_wf = init_docma_wf(omp_nthreads=1, name='docma_wf')

    bold_mag_splitter = pe.MapNode(
        interface=fsl.Split(dimension='t'),
        name='bold_mag_splitter',
        iterfield=['in_file'],
    )
    bold_phase_splitter = pe.MapNode(
        interface=fsl.Split(dimension='t'),
        name='bold_phase_splitter',
        iterfield=['in_file'],
    )
    workflow.connect(inputnode, 'bold_mag_files', bold_mag_splitter, 'in_file')
    workflow.connect(inputnode, 'bold_phase_files', bold_phase_splitter,
                     'in_file')
    """
    bold_phdiff_wf = init_phdiff_wf(name='bold_phdiff_wf',
                                    create_phasediff=True,
                                    omp_nthreads=1,
                                    fmap_bspline=None)
    workflow.connect(inputnode, ('bold_phase_files', pick_first),
                     bold_phdiff_wf, 'inputnode.phase1')
    workflow.connect(inputnode, ('bold_phase_files', pick_second),
                     bold_phdiff_wf, 'inputnode.phase2')
    workflow.connect(inputnode, ('bold_mag_files', pick_first),
                     bold_phdiff_wf, 'inputnode.magnitude')
    workflow.connect(inputnode, ('bold_phase_metadata', pick_first),
                     bold_phdiff_wf, 'inputnode.phase1_metadata')
    workflow.connect(inputnode, ('bold_phase_metadata', pick_second),
                     bold_phdiff_wf, 'inputnode.phase2_metadata')

    """
    """
    # Skullstrip BOLD files on a volume-wise basis
    # Need to feed in 3D files from first echo
    bold_mag_buffer = pe.Node(
        interface=niu.IdentityInterface(fields=['bold_mag_files']),
        name='bold_mag_buffer')
    bold_mag_buffer.iterables = ('bold_mag_files', (bold_mag_splitter.outputs.out_files, pick_first))

    bold_skullstrip_wf = init_skullstrip_bold_wf(name='bold_skullstrip_wf')
    workflow.connect(bold_mag_buffer, 'bold_mag_files',
                     bold_skullstrip_wf, 'inputnode.in_file')

    # Apply volume-wise brain masks to corresponding volumes from all echoes
    bold_skullstrip_apply = pe.MapNode(
        fsl.ApplyMask(),
        name='bold_skullstrip_apply',
        iterfield=['in_file'],
    )
    workflow.connect(inputnode, 'bold_mag_files',
                     bold_skullstrip_apply, 'in_file')
    workflow.connect(bold_skullstrip_wf, 'outputnode.mask_file',
                     bold_skullstrip_apply, 'mask_file')
    """
    """
    # Unwarp BOLD data
    # Must be applied to each volume and each echo independently
    # Will also need to be done to the phase data, post preproc but pre-MC
    bold_unwarp_wf = init_sdc_unwarp_wf(name='bold_unwarp_wf',
                                        debug=False,
                                        omp_nthreads=1,
                                        fmap_demean=True)
    first_echo_metadata = pe.Node(interface=Function(['input'], ['output'], pick_first),
                                  name='first_echo_metadata')
    workflow.connect(bold_phdiff_wf, 'outputnode.fmap',
                     bold_unwarp_wf, 'inputnode.fmap')
    workflow.connect(bold_phdiff_wf, 'outputnode.fmap_mask',
                     bold_unwarp_wf, 'inputnode.fmap_mask')
    workflow.connect(bold_phdiff_wf, 'outputnode.fmap_ref',
                     bold_unwarp_wf, 'inputnode.fmap_ref')
    workflow.connect(inputnode, ('bold_mag_files', pick_first),
                     bold_unwarp_wf, 'inputnode.in_reference')
    workflow.connect(bold_skullstrip_apply, ('out_file', pick_first),
                     bold_unwarp_wf, 'inputnode.in_reference_brain')
    workflow.connect(bold_skullstrip_wf, ('outputnode.mask_file', pick_first),
                     bold_unwarp_wf, 'inputnode.in_mask')
    workflow.connect(inputnode, 'bold_mag_metadata',
                     first_echo_metadata, 'input')
    workflow.connect(first_echo_metadata, 'output',
                     bold_unwarp_wf, 'inputnode.metadata')
    """

    # Process BOLD phase data for distortion correction
    bold_phase_wf = init_phase_processing_wf(name='phase_processing_wf')
    workflow.connect(inputnode, 'bold_phase_files', bold_phase_wf,
                     'inputnode.phase_files')
    workflow.connect(inputnode, 'bold_mag_files', bold_phase_wf,
                     'inputnode.magnitude_files')

    # Phaseprep preprocessing workflow to prepare phase data for phase-denoising
    reference_wf = init_bold_reference_wf(name='reference_wf')
    workflow.connect(
        bold_motionCorrection_applyMag,
        ('out_file', pick_first),
        reference_wf,
        'inputnode.bold_file',
    )
    workflow.connect(inputnode, ('sbref_mag_files', pick_first), reference_wf,
                     'inputnode.sbref_file')

    # This workflow is set up for single-echo data, so we need to
    # split or iterate over echoes here
    phaseprep_wf = create_preprocess_phase_wf(name='phaseprep_wf')
    workflow.connect(inputnode, 'bold_phase_files', phaseprep_wf,
                     'inputnode.input_phase')
    workflow.connect(inputnode, 'bold_mag_files', phaseprep_wf,
                     'inputnode.input_mag')

    # These ones are constant across echoes
    workflow.connect(
        bold_motionCorrection_estimate,
        'oned_matrix_save',
        phaseprep_wf,
        'inputnode.motion_par',
    )
    workflow.connect(reference_wf, 'outputnode.bold_mask', phaseprep_wf,
                     'inputnode.mask_file')

    # Perform motion correction for first echo only
    bold_motionCorrection_estimate = pe.Node(
        interface=afni.Volreg(outputtype='NIFTI_GZ'),
        name='bold_motionCorrection_estimate',
    )

    get_motpar_name_node = pe.Node(
        interface=Function(['source_file'], ['out_file'], get_motpar_name),
        name='get_motpar_name',
    )
    workflow.connect(
        inputnode,
        ('bold_mag_files', pick_first),
        bold_motionCorrection_estimate,
        'in_file',
    )
    workflow.connect(inputnode, ('bold_mag_files', pick_first),
                     get_motpar_name_node, 'source_file')
    workflow.connect(
        get_motpar_name_node,
        'out_file',
        bold_motionCorrection_estimate,
        'oned_matrix_save',
    )
    workflow.connect(
        inputnode,
        ('sbref_mag_files', pick_first),
        bold_motionCorrection_estimate,
        'basefile',
    )

    # Apply motion parameters to all echoes, for both magnitude and phase data
    bold_motionCorrection_applyMag = pe.MapNode(
        interface=afni.Allineate(outputtype='NIFTI_GZ'),
        name='bold_motionCorrection_applyMag',
        iterfield=['in_file'],
    )
    workflow.connect(
        bold_motionCorrection_estimate,
        'oned_matrix_save',
        bold_motionCorrection_applyMag,
        'in_matrix',
    )
    workflow.connect(inputnode, 'bold_mag_files',
                     bold_motionCorrection_applyMag, 'in_file')
    workflow.connect(
        inputnode,
        ('sbref_mag_files', pick_first),
        bold_motionCorrection_applyMag,
        'reference',
    )

    # Perform slice timing correction on magnitude and phase data
    bold_stc_getParams = pe.MapNode(
        interface=Function(['metadata'], ['slice_timing'], get_slice_timing),
        name='bold_stc_getParams',
        iterfield=['metadata'],
    )
    workflow.connect(inputnode, 'bold_mag_metadata', bold_stc_getParams,
                     'metadata')

    bold_magnitude_stc = pe.MapNode(
        interface=afni.TShift(outputtype='NIFTI_GZ'),
        name='bold_magnitude_stc',
        iterfield=['in_file', 'slice_timing'],
    )
    workflow.connect(bold_motionCorrection_applyMag, 'out_file',
                     bold_magnitude_stc, 'in_file')
    workflow.connect(bold_stc_getParams, 'slice_timing', bold_magnitude_stc,
                     'slice_timing')

    # Use SBRef from first echo as reference image.
    # No need to coregister functional data to SBRef because it was used for
    # the motion correction.
    # Coregister reference image to structural
    coreg_est = pe.Node(interface=afni.Allineate(out_matrix='sbref2anat.1D'),
                        name='sbref2anat_estimate')
    workflow.connect(inputnode, ('sbref_mag_files', pick_first), coreg_est,
                     'in_file')
    workflow.connect(inputnode, ('t1w_files', pick_first), coreg_est,
                     'reference')

    # Apply xform to mag data
    coreg_apply_mag = pe.MapNode(
        interface=afni.Allineate(outputtype='NIFTI_GZ'),
        name='sbref2anat_apply_mag',
        iterfield=['in_file'],
    )
    workflow.connect(coreg_est, 'out_matrix', coreg_apply_mag, 'in_matrix')
    workflow.connect(bold_magnitude_stc, 'out_file', coreg_apply_mag,
                     'in_file')
    workflow.connect(inputnode, ('sbref_mag_files', pick_first),
                     coreg_apply_mag, 'reference')

    # Apply xform to phase data
    coreg_apply_phase = pe.MapNode(
        interface=afni.Allineate(outputtype='NIFTI_GZ'),
        name='sbref2anat_apply_phase',
        iterfield=['in_file'],
    )
    workflow.connect(coreg_est, 'out_matrix', coreg_apply_phase, 'in_matrix')
    workflow.connect(phaseprep_wf, 'outputnode.uw_phase', coreg_apply_phase,
                     'in_file')
    workflow.connect(inputnode, ('sbref_mag_files', pick_first),
                     coreg_apply_phase, 'reference')

    # Apply xform to magnitude sbref data
    coreg_apply_sbref = pe.MapNode(
        interface=afni.Allineate(outputtype='NIFTI_GZ'),
        name='sbref2anat_apply_sbref',
        iterfield=['in_file'],
    )
    workflow.connect(coreg_est, 'out_matrix', coreg_apply_sbref, 'in_matrix')
    workflow.connect(inputnode, 'sbref_mag_files', coreg_apply_sbref,
                     'in_file')
    workflow.connect(inputnode, ('sbref_mag_files', pick_first),
                     coreg_apply_sbref, 'reference')

    # Collect outputs
    workflow.connect(bold_motionCorrection_estimate, 'oned_file', outputnode,
                     'motion_parameters')
    workflow.connect(coreg_apply_mag, 'out_file', outputnode,
                     'preproc_bold_files')
    workflow.connect(coreg_apply_phase, 'out_file', outputnode,
                     'preproc_phase_files')

    # Output BOLD mag files
    derivativesnode1 = pe.MapNode(
        interface=Function(['in_file', 'output_dir'], ['out_file'],
                           copy_files),
        name='derivativesnode_bold_mag',
        iterfield=['in_file'],
    )
    derivativesnode1.inputs.output_dir = output_dir
    workflow.connect(outputnode, 'preproc_bold_files', derivativesnode1,
                     'in_file')
    # Output BOLD phase files
    derivativesnode2 = pe.MapNode(
        interface=Function(['in_file', 'output_dir'], ['out_file'],
                           copy_files),
        name='derivativesnode_bold_phase',
        iterfield=['in_file'],
    )
    derivativesnode2.inputs.output_dir = output_dir
    workflow.connect(outputnode, 'preproc_phase_files', derivativesnode2,
                     'in_file')
    # Output motion parameters
    derivativesnode3 = pe.Node(
        interface=Function(['in_file', 'output_dir'], ['out_file'],
                           copy_files),
        name='derivativesnode_motpars',
    )
    derivativesnode3.inputs.output_dir = output_dir
    workflow.connect(outputnode, 'motion_parameters', derivativesnode3,
                     'in_file')
    return workflow
Ejemplo n.º 23
0
AD.inputs.parameter_source = 'AFNI'
#AD.inputs.realigned_files = 'motion_func'
#AD.inputs.realignment_parameters = 'motion_par'
AD.inputs.zintensity_threshold = 1.0
AD.inputs.global_threshold = 9.0
#AD.inputs.use_norm = 'TRUE'

psb6351_wf.connect(volreg, 'out_file', AD, 'realigned_files')
psb6351_wf.connect(volreg, 'oned_file', AD, 'realignment_parameters')

# Below is the command that runs AFNI's 3dTshift command
# this is the node that performs the slice timing correction
# I input the study func files as a list and the slice timing
# as a list of lists. I'm using a MapNode to iterate over the two.
# this should allow me to parallelize this on the HPC
tshifter = pe.MapNode(afni.TShift(),
                      iterfield=['in_file', 'slice_timing'],
                      name='tshifter')
tshifter.inputs.tr = '1.76'
tshifter.inputs.slice_timing = slice_timing_list
tshifter.inputs.outputtype = 'NIFTI_GZ'
psb6351_wf.connect(volreg, 'out_file', tshifter, 'in_file')

# Calculate the transformation matrix from EPI space to FreeSurfer space
# using the BBRegister command
fs_register = pe.Node(fs.BBRegister(init='fsl'), name='fs_register')
fs_register.inputs.contrast_type = 't2'
fs_register.inputs.out_fsl_file = True
fs_register.inputs.subject_id = f'sub-{sids[0]}'
fs_register.inputs.subjects_dir = fs_dir
psb6351_wf.connect(extractref, 'roi_file', fs_register, 'source_file')
Ejemplo n.º 24
0
def init_bold_stc_wf(metadata, name='bold_stc_wf'):
    """
    This workflow performs :abbr:`STC (slice-timing correction)` over the input
    :abbr:`BOLD (blood-oxygen-level dependent)` image.

    .. workflow::
        :graph2use: orig
        :simple_form: yes

        from fmriprep.workflows.bold import init_bold_stc_wf
        wf = init_bold_stc_wf(
            metadata={"RepetitionTime": 2.0,
                      "SliceTiming": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]},
            )

    **Parameters**

        metadata : dict
            BIDS metadata for BOLD file
        name : str
            Name of workflow (default: ``bold_stc_wf``)

    **Inputs**

        bold_file
            BOLD series NIfTI file
        skip_vols
            Number of non-steady-state volumes detected at beginning of ``bold_file``

    **Outputs**

        stc_file
            Slice-timing corrected BOLD series NIfTI file

    """
    workflow = Workflow(name=name)
    workflow.__desc__ = """\
BOLD runs were slice-time corrected using `3dTshift` from
AFNI {afni_ver} [@afni, RRID:SCR_005927].
""".format(afni_ver=''.join(list(afni.TShift().version or '<ver>')))
    inputnode = pe.Node(niu.IdentityInterface(fields=['bold_file', 'skip_vols']), name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(fields=['stc_file']), name='outputnode')

    LOGGER.log(25, 'Slice-timing correction will be included.')

    # It would be good to fingerprint memory use of afni.TShift
    slice_timing_correction = pe.Node(
        afni.TShift(outputtype='NIFTI_GZ',
                    tr='{}s'.format(metadata["RepetitionTime"]),
                    slice_timing=metadata['SliceTiming']),
        name='slice_timing_correction')

    copy_xform = pe.Node(CopyXForm(), name='copy_xform', mem_gb=0.1)

    workflow.connect([
        (inputnode, slice_timing_correction, [('bold_file', 'in_file'),
                                              ('skip_vols', 'ignore')]),
        (slice_timing_correction, copy_xform, [('out_file', 'in_file')]),
        (inputnode, copy_xform, [('bold_file', 'hdr_file')]),
        (copy_xform, outputnode, [('out_file', 'stc_file')]),
    ])

    return workflow