Skip to content

Commit d358ad9

Browse files
authored
Merge pull request #2088 from rciric/fsl_motion_outliers
ENH: Add RMS framewise displacement following Jenkinson et al. (2002)
2 parents 9509b31 + e5e12b4 commit d358ad9

File tree

5 files changed

+38
-9
lines changed

5 files changed

+38
-9
lines changed

docs/outputs.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ frames with sudden and large motion or intensity spikes.
275275

276276
- ``framewise_displacement`` - is a quantification of the estimated bulk-head motion calculated using
277277
formula proposed by [Power2012]_;
278+
- ``rmsd`` - is a quantification of the estimated relative (frame-to-frame) bulk head motion
279+
calculated using the :abbr:`RMS (root mean square)` approach of [Jenkinson2002]_;
278280
- ``dvars`` - the derivative of RMS variance over voxels (or :abbr:`DVARS (derivative of
279281
RMS variance over voxels)`) [Power2012]_;
280282
- ``std_dvars`` - standardized :abbr:`DVARS (derivative of RMS variance over voxels)`;
@@ -529,6 +531,10 @@ See implementation on :mod:`~fmriprep.workflows.bold.confounds.init_bold_confs_w
529531
NeuroImage. 2013. doi:`10.1016/j.neuroimage.2013.05.116
530532
<https://doi.org/10.1016/j.neuroimage.2013.05.116>`_
531533
534+
.. [Jenkinson2002] Jenkinson M, Bannister P, Brady M, Smith S. Improved optimization for the
535+
robust and accurate linear registration and motion correction of brain images. Neuroimage.
536+
2002. doi:`10.1016/s1053-8119(02)91132-8 <https://doi.org/10.1016/s1053-8119(02)91132-8>`__.
537+
532538
.. [Lindquist2019] Lindquist, MA, Geuter, S, and Wager, TD, Caffo, BS,
533539
Modular preprocessing pipelines can reintroduce artifacts into fMRI data.
534540
Human Brain Mapping. 2019. doi: `10.1002/hbm.24528

fmriprep/interfaces/confounds.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class GatherConfoundsInputSpec(BaseInterfaceInputSpec):
2929
dvars = File(exists=True, desc='file containing DVARS')
3030
std_dvars = File(exists=True, desc='file containing standardized DVARS')
3131
fd = File(exists=True, desc='input framewise displacement')
32+
rmsd = File(exists=True, desc='input RMS framewise displacement')
3233
tcompcor = File(exists=True, desc='input tCompCorr')
3334
acompcor = File(exists=True, desc='input aCompCorr')
3435
cos_basis = File(exists=True, desc='input cosine basis')
@@ -82,6 +83,7 @@ def _run_interface(self, runtime):
8283
dvars=self.inputs.dvars,
8384
std_dvars=self.inputs.std_dvars,
8485
fdisp=self.inputs.fd,
86+
rmsd=self.inputs.rmsd,
8587
tcompcor=self.inputs.tcompcor,
8688
acompcor=self.inputs.acompcor,
8789
cos_basis=self.inputs.cos_basis,
@@ -135,7 +137,7 @@ def _run_interface(self, runtime):
135137

136138

137139
def _gather_confounds(signals=None, dvars=None, std_dvars=None, fdisp=None,
138-
tcompcor=None, acompcor=None, cos_basis=None,
140+
rmsd=None, tcompcor=None, acompcor=None, cos_basis=None,
139141
motion=None, aroma=None, newpath=None):
140142
r"""
141143
Load confounds from the filenames, concatenate together horizontally
@@ -186,6 +188,7 @@ def _adjust_indices(left_df, right_df):
186188
(std_dvars, 'Standardized DVARS'),
187189
(dvars, 'DVARS'),
188190
(fdisp, 'Framewise displacement'),
191+
(rmsd, 'Framewise displacement (RMS)'),
189192
(tcompcor, 'tCompCor'),
190193
(acompcor, 'aCompCor'),
191194
(cos_basis, 'Cosine basis'),

fmriprep/workflows/bold/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,8 @@ def init_func_preproc_wf(bold_file):
473473
(inputnode, bold_confounds_wf, [('t1w_tpms', 'inputnode.t1w_tpms'),
474474
('t1w_mask', 'inputnode.t1w_mask')]),
475475
(bold_hmc_wf, bold_confounds_wf, [
476-
('outputnode.movpar_file', 'inputnode.movpar_file')]),
476+
('outputnode.movpar_file', 'inputnode.movpar_file'),
477+
('outputnode.rmsd_file', 'inputnode.rmsd_file')]),
477478
(bold_reg_wf, bold_confounds_wf, [
478479
('outputnode.itk_t1_to_bold', 'inputnode.t1_bold_xform')]),
479480
(bold_reference_wf, bold_confounds_wf, [

fmriprep/workflows/bold/confounds.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ def init_bold_confs_wf(
9898
BOLD series mask
9999
movpar_file
100100
SPM-formatted motion parameters file
101+
rmsd_file
102+
Framewise displacement as measured by ``fsl_motion_outliers``.
101103
skip_vols
102104
number of non steady state volumes
103105
t1w_mask
@@ -140,6 +142,9 @@ def init_bold_confs_wf(
140142
Several confounding time-series were calculated based on the
141143
*preprocessed BOLD*: framewise displacement (FD), DVARS and
142144
three region-wise global signals.
145+
FD was computed using two formulations following Power (absolute sum of
146+
relative motions, @power_fd_dvars) and Jenkinson (relative root mean square
147+
displacement between affines, @mcflirt).
143148
FD and DVARS are calculated for each functional run, both using their
144149
implementations in *Nipype* [following the definitions by @power_fd_dvars].
145150
The three global signals are extracted within the CSF, the WM, and
@@ -173,8 +178,8 @@ def init_bold_confs_wf(
173178
were annotated as motion outliers.
174179
""".format(fd=regressors_fd_th, dv=regressors_dvars_th)
175180
inputnode = pe.Node(niu.IdentityInterface(
176-
fields=['bold', 'bold_mask', 'movpar_file', 'skip_vols',
177-
't1w_mask', 't1w_tpms', 't1_bold_xform']),
181+
fields=['bold', 'bold_mask', 'movpar_file', 'rmsd_file',
182+
'skip_vols', 't1w_mask', 't1w_tpms', 't1_bold_xform']),
178183
name='inputnode')
179184
outputnode = pe.Node(niu.IdentityInterface(
180185
fields=['confounds_file', 'confounds_metadata']),
@@ -258,6 +263,9 @@ def init_bold_confs_wf(
258263
add_motion_headers = pe.Node(
259264
AddTSVHeader(columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]),
260265
name="add_motion_headers", mem_gb=0.01, run_without_submitting=True)
266+
add_rmsd_header = pe.Node(
267+
AddTSVHeader(columns=["rmsd"]),
268+
name="add_rmsd_header", mem_gb=0.01, run_without_submitting=True)
261269
concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True)
262270

263271
# CompCor metadata
@@ -382,6 +390,7 @@ def _pick_wm(files):
382390

383391
# Collate computed confounds together
384392
(inputnode, add_motion_headers, [('movpar_file', 'in_file')]),
393+
(inputnode, add_rmsd_header, [('rmsd_file', 'in_file')]),
385394
(dvars, add_dvars_header, [('out_nstd', 'in_file')]),
386395
(dvars, add_std_dvars_header, [('out_std', 'in_file')]),
387396
(signals, concat, [('out_file', 'signals')]),
@@ -390,6 +399,7 @@ def _pick_wm(files):
390399
('pre_filter_file', 'cos_basis')]),
391400
(acompcor, concat, [('components_file', 'acompcor')]),
392401
(add_motion_headers, concat, [('out_file', 'motion')]),
402+
(add_rmsd_header, concat, [('out_file', 'rmsd')]),
393403
(add_dvars_header, concat, [('out_file', 'dvars')]),
394404
(add_std_dvars_header, concat, [('out_file', 'std_dvars')]),
395405

fmriprep/workflows/bold/hmc.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def init_bold_hmc_wf(mem_gb, omp_nthreads, name='bold_hmc_wf'):
5454
ITKTransform file aligning each volume to ``ref_image``
5555
movpar_file
5656
MCFLIRT motion parameters, normalized to SPM format (X, Y, Z, Rx, Ry, Rz)
57+
rms_file
58+
Framewise displacement as measured by ``fsl_motion_outliers`` [Jenkinson2002]_.
5759
5860
"""
5961
from niworkflows.engine.workflows import LiterateWorkflow as Workflow
@@ -68,15 +70,18 @@ def init_bold_hmc_wf(mem_gb, omp_nthreads, name='bold_hmc_wf'):
6870
`mcflirt` [FSL {fsl_ver}, @mcflirt].
6971
""".format(fsl_ver=fsl.Info().version() or '<ver>')
7072

71-
inputnode = pe.Node(niu.IdentityInterface(fields=['bold_file', 'raw_ref_image']),
72-
name='inputnode')
73+
inputnode = pe.Node(
74+
niu.IdentityInterface(fields=['bold_file', 'raw_ref_image']),
75+
name='inputnode')
7376
outputnode = pe.Node(
74-
niu.IdentityInterface(fields=['xforms', 'movpar_file']),
77+
niu.IdentityInterface(
78+
fields=['xforms', 'movpar_file', 'rmsd_file']),
7579
name='outputnode')
7680

7781
# Head motion correction (hmc)
78-
mcflirt = pe.Node(fsl.MCFLIRT(save_mats=True, save_plots=True),
79-
name='mcflirt', mem_gb=mem_gb * 3)
82+
mcflirt = pe.Node(
83+
fsl.MCFLIRT(save_mats=True, save_plots=True, save_rms=True),
84+
name='mcflirt', mem_gb=mem_gb * 3)
8085

8186
fsl2itk = pe.Node(MCFLIRT2ITK(), name='fsl2itk',
8287
mem_gb=0.05, n_procs=omp_nthreads)
@@ -85,13 +90,17 @@ def init_bold_hmc_wf(mem_gb, omp_nthreads, name='bold_hmc_wf'):
8590
name="normalize_motion",
8691
mem_gb=DEFAULT_MEMORY_MIN_GB)
8792

93+
def _pick_rel(rms_files):
94+
return rms_files[-1]
95+
8896
workflow.connect([
8997
(inputnode, mcflirt, [('raw_ref_image', 'ref_file'),
9098
('bold_file', 'in_file')]),
9199
(inputnode, fsl2itk, [('raw_ref_image', 'in_source'),
92100
('raw_ref_image', 'in_reference')]),
93101
(mcflirt, fsl2itk, [('mat_file', 'in_files')]),
94102
(mcflirt, normalize_motion, [('par_file', 'in_file')]),
103+
(mcflirt, outputnode, [(('rms_files', _pick_rel), 'rmsd_file')]),
95104
(fsl2itk, outputnode, [('out_file', 'xforms')]),
96105
(normalize_motion, outputnode, [('out_file', 'movpar_file')]),
97106
])

0 commit comments

Comments
 (0)