Skip to content

Commit 938e252

Browse files
committed
mv FD computation to new confounds module
1 parent 98b522c commit 938e252

File tree

2 files changed

+154
-125
lines changed

2 files changed

+154
-125
lines changed

nipype/algorithms/confounds.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
'''
5+
Algorithms to compute confounds in :abbr:`fMRI (functional MRI)`
6+
7+
Change directory to provide relative paths for doctests
8+
>>> import os
9+
>>> filepath = os.path.dirname(os.path.realpath(__file__))
10+
>>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
11+
>>> os.chdir(datadir)
12+
13+
'''
14+
from __future__ import print_function, division, unicode_literals, absolute_import
15+
from builtins import str, zip, range, open
16+
17+
import os.path as op
18+
import numpy as np
19+
20+
from .. import logging
21+
from ..external.due import due, BibTeX
22+
from ..interfaces.base import (traits, TraitedSpec, BaseInterface,
23+
BaseInterfaceInputSpec, File, isdefined)
24+
IFLOG = logging.getLogger('interface')
25+
26+
27+
class FramewiseDisplacementInputSpec(BaseInterfaceInputSpec):
28+
in_plots = File(exists=True, desc='motion parameters as written by FSL MCFLIRT')
29+
radius = traits.Float(50, usedefault=True,
30+
desc='radius in mm to calculate angular FDs, 50mm is the '
31+
'default since it is used in Power et al. 2012')
32+
out_file = File('fd_power_2012.txt', usedefault=True, desc='output file name')
33+
out_figure = File('fd_power_2012.pdf', usedefault=True, desc='output figure name')
34+
series_tr = traits.Float(desc='repetition time in sec.')
35+
save_plot = traits.Bool(False, usedefault=True, desc='write FD plot')
36+
normalize = traits.Bool(False, usedefault=True, desc='calculate FD in mm/s')
37+
figdpi = traits.Int(100, usedefault=True, desc='output dpi for the FD plot')
38+
figsize = traits.Tuple(traits.Float(11.7), traits.Float(2.3), usedefault=True,
39+
desc='output figure size')
40+
41+
class FramewiseDisplacementOutputSpec(TraitedSpec):
42+
out_file = File(desc='calculated FD per timestep')
43+
out_figure = File(desc='output image file')
44+
fd_average = traits.Float(desc='average FD')
45+
46+
class FramewiseDisplacement(BaseInterface):
47+
"""
48+
Calculate the :abbr:`FD (framewise displacement)` as in [Power2012]_.
49+
This implementation reproduces the calculation in fsl_motion_outliers
50+
51+
.. [Power2012] Power et al., Spurious but systematic correlations in functional
52+
connectivity MRI networks arise from subject motion, NeuroImage 59(3),
53+
2012. doi:`10.1016/j.neuroimage.2011.10.018
54+
<http://dx.doi.org/10.1016/j.neuroimage.2011.10.018>`_.
55+
56+
57+
"""
58+
59+
input_spec = FramewiseDisplacementInputSpec
60+
output_spec = FramewiseDisplacementOutputSpec
61+
62+
references_ = [{
63+
'entry': BibTeX("""\
64+
@article{power_spurious_2012,
65+
title = {Spurious but systematic correlations in functional connectivity {MRI} networks \
66+
arise from subject motion},
67+
volume = {59},
68+
doi = {10.1016/j.neuroimage.2011.10.018},
69+
number = {3},
70+
urldate = {2016-08-16},
71+
journal = {NeuroImage},
72+
author = {Power, Jonathan D. and Barnes, Kelly A. and Snyder, Abraham Z. and Schlaggar, \
73+
Bradley L. and Petersen, Steven E.},
74+
year = {2012},
75+
pages = {2142--2154},
76+
}
77+
"""),
78+
'tags': ['method']
79+
}]
80+
81+
def _run_interface(self, runtime):
82+
mpars = np.loadtxt(self.inputs.in_plots) # mpars is N_t x 6
83+
diff = mpars[:-1, :] - mpars[1:, :]
84+
diff[:, :3] *= self.inputs.radius
85+
fd_res = np.abs(diff).sum(axis=1)
86+
87+
self._results = {
88+
'out_file': op.abspath(self.inputs.out_file),
89+
'fd_average': float(fd_res.mean())
90+
}
91+
np.savetxt(self.inputs.out_file, fd_res)
92+
93+
94+
if self.inputs.save_plot:
95+
tr = None
96+
if isdefined(self.inputs.series_tr):
97+
tr = self.inputs.series_tr
98+
99+
if self.inputs.normalize and tr is None:
100+
IFLOG.warn('FD plot cannot be normalized if TR is not set')
101+
102+
self._results['out_figure'] = op.abspath(self.inputs.out_figure)
103+
fig = plot_confound(fd_res, self.inputs.figsize, 'FD', units='mm',
104+
series_tr=tr, normalize=self.inputs.normalize)
105+
fig.savefig(self._results['out_figure'], dpi=float(self.inputs.figdpi),
106+
format=self.inputs.out_figure[-3:],
107+
bbox_inches='tight')
108+
fig.clf()
109+
return runtime
110+
111+
def _list_outputs(self):
112+
return self._results
113+
114+
115+
def plot_confound(tseries, figsize, name, units=None,
116+
series_tr=None, normalize=False):
117+
"""
118+
A helper function to plot :abbr:`fMRI (functional MRI)` confounds.
119+
"""
120+
import matplotlib
121+
matplotlib.use('Agg')
122+
import matplotlib.pyplot as plt
123+
from matplotlib.gridspec import GridSpec
124+
from matplotlib.backends.backend_pdf import FigureCanvasPdf as FigureCanvas
125+
import seaborn as sns
126+
127+
fig = plt.Figure(figsize=figsize)
128+
FigureCanvas(fig)
129+
grid = GridSpec(1, 2, width_ratios=[3, 1], wspace=0.025)
130+
grid.update(hspace=1.0, right=0.95, left=0.1, bottom=0.2)
131+
132+
ax = fig.add_subplot(grid[0, :-1])
133+
if normalize and series_tr is not None:
134+
tseries /= series_tr
135+
136+
ax.plot(tseries)
137+
ax.set_xlim((0, len(tseries)))
138+
ylabel = name
139+
if units is not None:
140+
ylabel += (' speed [{}/s]' if normalize else ' [{}]').format(units)
141+
ax.set_ylabel(ylabel)
142+
143+
xlabel = 'Frame #'
144+
if series_tr is not None:
145+
xlabel = 'Frame # ({} sec TR)'.format(series_tr)
146+
ax.set_xlabel(xlabel)
147+
ylim = ax.get_ylim()
148+
149+
ax = fig.add_subplot(grid[0, -1])
150+
sns.distplot(tseries, vertical=True, ax=ax)
151+
ax.set_xlabel('Frames')
152+
ax.set_ylim(ylim)
153+
ax.set_yticklabels([])
154+
return fig

nipype/algorithms/misc.py

Lines changed: 0 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import warnings
3030

3131
from .. import logging
32-
from ..external.due import due, Doi, BibTeX
3332
from . import metrics as nam
3433
from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File,
3534
InputMultiPath, OutputMultiPath,
@@ -39,93 +38,6 @@
3938
iflogger = logging.getLogger('interface')
4039

4140

42-
class FramewiseDisplacementInputSpec(BaseInterfaceInputSpec):
43-
in_plots = File(exists=True, desc='motion parameters as written by FSL MCFLIRT')
44-
radius = traits.Float(50, usedefault=True,
45-
desc='radius in mm to calculate angular FDs, 50mm is the '
46-
'default since it is used in Power et al. 2012')
47-
out_file = File('fd_power_2012.txt', usedefault=True, desc='output file name')
48-
out_figure = File('fd_power_2012.pdf', usedefault=True, desc='output figure name')
49-
series_tr = traits.Float(desc='repetition time in sec.')
50-
save_plot = traits.Bool(False, usedefault=True, desc='write FD plot')
51-
normalize = traits.Bool(False, usedefault=True, desc='calculate FD in mm/s')
52-
figdpi = traits.Int(100, usedefault=True, desc='output dpi for the FD plot')
53-
figsize = traits.Tuple(traits.Float(11.7), traits.Float(2.3), usedefault=True,
54-
desc='output figure size')
55-
56-
class FramewiseDisplacementOutputSpec(TraitedSpec):
57-
out_file = File(desc='calculated FD per timestep')
58-
out_figure = File(desc='output image file')
59-
fd_average = traits.Float(desc='average FD')
60-
61-
class FramewiseDisplacement(BaseInterface):
62-
"""
63-
Calculate the :abbr:`FD (framewise displacement)` as in [Power2012]_.
64-
This implementation reproduces the calculation in fsl_motion_outliers
65-
66-
.. [Power2012] Power et al., Spurious but systematic correlations in functional
67-
connectivity MRI networks arise from subject motion, NeuroImage 59(3),
68-
2012. doi:`10.1016/j.neuroimage.2011.10.018
69-
<http://dx.doi.org/10.1016/j.neuroimage.2011.10.018>`_.
70-
71-
72-
"""
73-
74-
input_spec = FramewiseDisplacementInputSpec
75-
output_spec = FramewiseDisplacementOutputSpec
76-
77-
references_ = [{
78-
'entry': BibTeX("""\
79-
@article{power_spurious_2012,
80-
title = {Spurious but systematic correlations in functional connectivity {MRI} networks \
81-
arise from subject motion},
82-
volume = {59},
83-
doi = {10.1016/j.neuroimage.2011.10.018},
84-
number = {3},
85-
urldate = {2016-08-16},
86-
journal = {NeuroImage},
87-
author = {Power, Jonathan D. and Barnes, Kelly A. and Snyder, Abraham Z. and Schlaggar, \
88-
Bradley L. and Petersen, Steven E.},
89-
year = {2012},
90-
pages = {2142--2154},
91-
}
92-
"""),
93-
'tags': ['method']
94-
}]
95-
96-
def _run_interface(self, runtime):
97-
mpars = np.loadtxt(self.inputs.in_plots) # mpars is N_t x 6
98-
diff = mpars[:-1, :] - mpars[1:, :]
99-
diff[:, :3] *= self.inputs.radius
100-
fd_res = np.abs(diff).sum(axis=1)
101-
102-
self._results = {
103-
'out_file': op.abspath(self.inputs.out_file),
104-
'fd_average': float(fd_res.mean())
105-
}
106-
np.savetxt(self.inputs.out_file, fd_res)
107-
108-
109-
if self.inputs.save_plot:
110-
tr = None
111-
if isdefined(self.inputs.series_tr):
112-
tr = self.inputs.series_tr
113-
114-
if self.inputs.normalize and tr is None:
115-
iflogger.warn('FD plot cannot be normalized if TR is not set')
116-
117-
self._results['out_figure'] = op.abspath(self.inputs.out_figure)
118-
fig = plot_fd(fd_res, self.inputs.figsize, series_tr=tr,
119-
normalize=self.inputs.normalize)
120-
fig.savefig(self._results['out_figure'], dpi=float(self.inputs.figdpi),
121-
format=self.inputs.out_figure[-3:],
122-
bbox_inches='tight')
123-
return runtime
124-
125-
def _list_outputs(self):
126-
return self._results
127-
128-
12941
class PickAtlasInputSpec(BaseInterfaceInputSpec):
13042
atlas = File(exists=True, desc="Location of the atlas that will be used.",
13143
mandatory=True)
@@ -1552,43 +1464,6 @@ def merge_rois(in_files, in_idxs, in_ref,
15521464
return out_file
15531465

15541466

1555-
def plot_fd(fd_values, figsize, series_tr=None, normalize=False):
1556-
"""
1557-
A helper function to plot the framewise displacement
1558-
"""
1559-
import matplotlib
1560-
matplotlib.use('Agg')
1561-
import matplotlib.pyplot as plt
1562-
from matplotlib.gridspec import GridSpec
1563-
from matplotlib.backends.backend_pdf import FigureCanvasPdf as FigureCanvas
1564-
import seaborn as sns
1565-
1566-
fig = plt.Figure(figsize=figsize)
1567-
FigureCanvas(fig)
1568-
grid = GridSpec(1, 2, width_ratios=[3, 1], wspace=0.025)
1569-
grid.update(hspace=1.0, right=0.95, left=0.1, bottom=0.2)
1570-
1571-
ax = fig.add_subplot(grid[0, :-1])
1572-
if normalize and series_tr is not None:
1573-
fd_values /= series_tr
1574-
1575-
ax.plot(fd_values)
1576-
ax.set_xlim((0, len(fd_values)))
1577-
ax.set_ylabel('FD {}'.format('speed [mm/s]' if normalize else '[mm]'))
1578-
1579-
xlabel = 'Frame #'
1580-
if series_tr is not None:
1581-
xlabel = 'Frame # ({} sec TR)'.format(series_tr)
1582-
ax.set_xlabel(xlabel)
1583-
ylim = ax.get_ylim()
1584-
1585-
ax = fig.add_subplot(grid[0, -1])
1586-
sns.distplot(fd_values, vertical=True, ax=ax)
1587-
ax.set_xlabel('Frames')
1588-
ax.set_ylim(ylim)
1589-
ax.set_yticklabels([])
1590-
return fig
1591-
15921467
# Deprecated interfaces ------------------------------------------------------
15931468

15941469
class Distance(nam.Distance):

0 commit comments

Comments
 (0)