Skip to content

Commit 90608b0

Browse files
committed
enh: added SpaceTimeRealign from nipy 0.4.dev
1 parent c2031ca commit 90608b0

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed

nipype/interfaces/nipy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .model import FitGLM, EstimateContrast
2-
from .preprocess import ComputeMask, FmriRealign4d
2+
from .preprocess import ComputeMask, FmriRealign4d, SpaceTimeRealigner
33
from .utils import Similarity

nipype/interfaces/nipy/preprocess.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from nipy.labs.mask import compute_mask
2525
from nipy.algorithms.registration import FmriRealign4d as FR4d
2626
from nipy import save_image, load_image
27+
from nipy.algorithms.registration import SpaceTimeRealign
28+
from nipy.algorithms.registration.groupwise_registration import SpaceRealign
2729

2830
from ..base import (TraitedSpec, BaseInterface, traits,
2931
BaseInterfaceInputSpec, isdefined, File,
@@ -82,7 +84,7 @@ class FmriRealign4dInputSpec(BaseInterfaceInputSpec):
8284
desc="File to realign")
8385
tr = traits.Float(desc="TR in seconds",
8486
mandatory=True)
85-
slice_order = traits.List(traits.Int(), maxver=0.3,
87+
slice_order = traits.List(traits.Int(),
8688
desc=('0 based slice order. This would be equivalent to entering'
8789
'np.argsort(spm_slice_order) for this field. This effects'
8890
'interleaved acquisition. This field will be deprecated in'
@@ -117,6 +119,7 @@ class FmriRealign4dOutputSpec(TraitedSpec):
117119
desc="Motion parameter files")
118120

119121

122+
@np.deprecate_with_doc('Please use SpaceTimeRealign instead')
120123
class FmriRealign4d(BaseInterface):
121124
"""Simultaneous motion and slice timing correction algorithm
122125
@@ -196,6 +199,131 @@ def _list_outputs(self):
196199
return outputs
197200

198201

202+
class SpaceTimeRealignerInputSpec(BaseInterfaceInputSpec):
203+
204+
in_file = InputMultiPath(exists=True,
205+
mandatory=True,
206+
desc="File to realign")
207+
tr = traits.Float(desc="TR in seconds", requires=['slice_times'])
208+
slice_times = traits.Either(traits.List(traits.Float()),
209+
traits.Enum('asc_alt_2', 'asc_alt_2_1',
210+
'asc_alt_half', 'asc_alt_siemens',
211+
'ascending', 'desc_alt_2',
212+
'desc_alt_half', 'descending'),
213+
desc=('Actual slice acquisition times.'))
214+
slice_info = traits.Either(traits.Int,
215+
traits.List(min_len=2, max_len=2),
216+
desc=('Single integer or length 2 sequence '
217+
'If int, the axis in `images` that is the '
218+
'slice axis. In a 4D image, this will '
219+
'often be axis = 2. If a 2 sequence, then'
220+
' elements are ``(slice_axis, '
221+
'slice_direction)``, where ``slice_axis`` '
222+
'is the slice axis in the image as above, '
223+
'and ``slice_direction`` is 1 if the '
224+
'slices were acquired slice 0 first, slice'
225+
' -1 last, or -1 if acquired slice -1 '
226+
'first, slice 0 last. If `slice_info` is '
227+
'an int, assume '
228+
'``slice_direction`` == 1.'),
229+
requires=['slice_times'],
230+
)
231+
232+
233+
class SpaceTimeRealignerOutputSpec(TraitedSpec):
234+
out_file = OutputMultiPath(File(exists=True),
235+
desc="Realigned files")
236+
par_file = OutputMultiPath(File(exists=True),
237+
desc=("Motion parameter files. Angles are not "
238+
"euler angles"))
239+
240+
241+
class SpaceTimeRealigner(BaseInterface):
242+
"""Simultaneous motion and slice timing correction algorithm
243+
244+
If slice_times is not specified, this algorithm performs spatial motion
245+
correction
246+
247+
This interface wraps nipy's SpaceTimeRealign algorithm [1]_ or simply the
248+
SpatialRealign algorithm when timing info is not provided.
249+
250+
Examples
251+
--------
252+
>>> from nipype.interfaces.nipy import SpaceTimeRealigner
253+
>>> #Run spatial realignment only
254+
>>> realigner = SpaceTimeRealigner()
255+
>>> realigner.inputs.in_file = ['functional.nii']
256+
>>> res = realigner.run() # doctest: +SKIP
257+
258+
>>> realigner = SpaceTimeRealigner()
259+
>>> realigner.inputs.in_file = ['functional.nii']
260+
>>> realigner.inputs.tr = 2
261+
>>> realigner.inputs.slice_times = range(0, 3, 67)
262+
>>> realigner.inputs.slice_info = 2
263+
>>> res = realigner.run() # doctest: +SKIP
264+
265+
266+
References
267+
----------
268+
.. [1] Roche A. A four-dimensional registration algorithm with \
269+
application to joint correction of motion and slice timing \
270+
in fMRI. IEEE Trans Med Imaging. 2011 Aug;30(8):1546-54. DOI_.
271+
272+
.. _DOI: http://dx.doi.org/10.1109/TMI.2011.2131152
273+
274+
"""
275+
276+
input_spec = SpaceTimeRealignerInputSpec
277+
output_spec = SpaceTimeRealignerOutputSpec
278+
keywords = ['slice timing', 'motion correction']
279+
280+
def _run_interface(self, runtime):
281+
282+
all_ims = [load_image(fname) for fname in self.inputs.in_file]
283+
284+
if not isdefined(self.inputs.slice_times):
285+
R = SpaceRealign(all_ims)
286+
else:
287+
R = SpaceTimeRealign(all_ims,
288+
tr=self.inputs.tr,
289+
slice_times=self.inputs.slice_times,
290+
slice_info=self.inputs.slice_info,
291+
)
292+
293+
R.estimate(refscan=None)
294+
295+
corr_run = R.resample()
296+
self._out_file_path = []
297+
self._par_file_path = []
298+
299+
for j, corr in enumerate(corr_run):
300+
self._out_file_path.append(os.path.abspath('corr_%s.nii.gz' %
301+
(split_filename(self.inputs.in_file[j])[1])))
302+
save_image(corr, self._out_file_path[j])
303+
304+
self._par_file_path.append(os.path.abspath('%s.par' %
305+
(os.path.split(self.inputs.in_file[j])[1])))
306+
mfile = open(self._par_file_path[j], 'w')
307+
motion = R._transforms[j]
308+
# nipy does not encode euler angles. return in original form of
309+
# translation followed by rotation vector see:
310+
# http://en.wikipedia.org/wiki/Rodrigues'_rotation_formula
311+
for i, mo in enumerate(motion):
312+
params = ['%.10f' % item for item in np.hstack((mo.translation,
313+
mo.rotation))]
314+
string = ' '.join(params) + '\n'
315+
mfile.write(string)
316+
mfile.close()
317+
318+
return runtime
319+
320+
def _list_outputs(self):
321+
outputs = self._outputs().get()
322+
outputs['out_file'] = self._out_file_path
323+
outputs['par_file'] = self._par_file_path
324+
return outputs
325+
326+
199327
class TrimInputSpec(BaseInterfaceInputSpec):
200328
in_file = File(
201329
exists=True, mandatory=True,

0 commit comments

Comments
 (0)