|
24 | 24 | from nipy.labs.mask import compute_mask
|
25 | 25 | from nipy.algorithms.registration import FmriRealign4d as FR4d
|
26 | 26 | from nipy import save_image, load_image
|
| 27 | + from nipy.algorithms.registration import SpaceTimeRealign |
| 28 | + from nipy.algorithms.registration.groupwise_registration import SpaceRealign |
27 | 29 |
|
28 | 30 | from ..base import (TraitedSpec, BaseInterface, traits,
|
29 | 31 | BaseInterfaceInputSpec, isdefined, File,
|
@@ -82,7 +84,7 @@ class FmriRealign4dInputSpec(BaseInterfaceInputSpec):
|
82 | 84 | desc="File to realign")
|
83 | 85 | tr = traits.Float(desc="TR in seconds",
|
84 | 86 | mandatory=True)
|
85 |
| - slice_order = traits.List(traits.Int(), maxver=0.3, |
| 87 | + slice_order = traits.List(traits.Int(), |
86 | 88 | desc=('0 based slice order. This would be equivalent to entering'
|
87 | 89 | 'np.argsort(spm_slice_order) for this field. This effects'
|
88 | 90 | 'interleaved acquisition. This field will be deprecated in'
|
@@ -117,6 +119,7 @@ class FmriRealign4dOutputSpec(TraitedSpec):
|
117 | 119 | desc="Motion parameter files")
|
118 | 120 |
|
119 | 121 |
|
| 122 | +@np.deprecate_with_doc('Please use SpaceTimeRealign instead') |
120 | 123 | class FmriRealign4d(BaseInterface):
|
121 | 124 | """Simultaneous motion and slice timing correction algorithm
|
122 | 125 |
|
@@ -196,6 +199,131 @@ def _list_outputs(self):
|
196 | 199 | return outputs
|
197 | 200 |
|
198 | 201 |
|
| 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 | + |
199 | 327 | class TrimInputSpec(BaseInterfaceInputSpec):
|
200 | 328 | in_file = File(
|
201 | 329 | exists=True, mandatory=True,
|
|
0 commit comments