Skip to content

Commit fdb3227

Browse files
authored
Merge pull request #2635 from qtabs/addSPMrealingUnwarp
[ENH] Add interface to SPM realign_unwarp
2 parents 297a24c + 1b12a27 commit fdb3227

File tree

3 files changed

+251
-4
lines changed

3 files changed

+251
-4
lines changed

nipype/interfaces/spm/__init__.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
from .base import (Info, SPMCommand, logger, no_spm, scans_for_fname,
77
scans_for_fnames)
8-
from .preprocess import (FieldMap, SliceTiming, Realign, Coregister, Normalize,
9-
Normalize12, Segment, Smooth, NewSegment, DARTEL,
10-
DARTELNorm2MNI, CreateWarped, VBMSegment)
8+
from .preprocess import (FieldMap, SliceTiming, Realign, RealignUnwarp,
9+
Coregister, Normalize, Normalize12, Segment,
10+
Smooth, NewSegment, DARTEL, DARTELNorm2MNI,
11+
CreateWarped, VBMSegment)
1112
from .model import (Level1Design, EstimateModel, EstimateContrast, Threshold,
1213
OneSampleTTestDesign, TwoSampleTTestDesign,
1314
PairedTTestDesign, MultipleRegressionDesign)

nipype/interfaces/spm/preprocess.py

+247-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from ...utils.filemanip import (fname_presuffix, ensure_list,
1818
simplify_list, split_filename)
1919
from ..base import (OutputMultiPath, TraitedSpec, isdefined,
20-
traits, InputMultiPath, File, Str)
20+
traits, InputMultiPath, InputMultiObject, File, Str)
2121
from .base import (SPMCommand, scans_for_fname, func_is_3d,
2222
scans_for_fnames, SPMCommandInputSpec, ImageFileSPM)
2323

@@ -437,6 +437,252 @@ def _list_outputs(self):
437437
return outputs
438438

439439

440+
class RealignUnwarpInputSpec(SPMCommandInputSpec):
441+
442+
in_files = InputMultiObject(
443+
traits.Either(ImageFileSPM(exists=True),
444+
traits.List(ImageFileSPM(exists=True))),
445+
field='data.scans',
446+
mandatory=True,
447+
copyfile=True,
448+
desc='list of filenames to realign and unwarp')
449+
phase_map = File(
450+
field='data.pmscan',
451+
desc='Voxel displacement map to use in unwarping. Unlike SPM standard '
452+
'behaviour, the same map will be used for all sessions',
453+
copyfile=False)
454+
quality = traits.Range(
455+
low=0.0,
456+
high=1.0,
457+
field='eoptions.quality',
458+
desc='0.1 = fast, 1.0 = precise')
459+
fwhm = traits.Range(
460+
low=0.0,
461+
field='eoptions.fwhm',
462+
desc='gaussian smoothing kernel width')
463+
separation = traits.Range(
464+
low=0.0,
465+
field='eoptions.sep',
466+
desc='sampling separation in mm')
467+
register_to_mean = traits.Bool(
468+
field='eoptions.rtm',
469+
desc='Indicate whether realignment is done to the mean image')
470+
weight_img = File(
471+
exists=True,
472+
field='eoptions.weight',
473+
desc='filename of weighting image')
474+
interp = traits.Range(
475+
low=0,
476+
high=7,
477+
field='eoptions.einterp',
478+
desc='degree of b-spline used for interpolation')
479+
wrap = traits.List(
480+
traits.Int(),
481+
minlen=3,
482+
maxlen=3,
483+
field='eoptions.ewrap',
484+
desc='Check if interpolation should wrap in [x,y,z]')
485+
est_basis_func = traits.List(
486+
traits.Int(),
487+
minlen=2,
488+
maxlen=2,
489+
field='uweoptions.basfcn',
490+
desc='Number of basis functions to use for each dimension')
491+
est_reg_order = traits.Range(
492+
low=0,
493+
high=3,
494+
field='uweoptions.regorder',
495+
desc=('This parameter determines how to balance the compromise between likelihood '
496+
'maximization and smoothness maximization of the estimated field.'))
497+
est_reg_factor = traits.ListInt(
498+
[100000],
499+
field='uweoptions.lambda',
500+
minlen=1,
501+
maxlen=1,
502+
usedefault=True,
503+
desc='Regularisation factor. Default: 100000 (medium).')
504+
est_jacobian_deformations = traits.Bool(
505+
field='uweoptions.jm',
506+
desc=('Jacobian deformations. In theory a good idea to include them, '
507+
' in practice a bad idea. Default: No.'))
508+
est_first_order_effects = traits.List(
509+
traits.Int(),
510+
minlen=1,
511+
maxlen=6,
512+
field='uweoptions.fot',
513+
desc='First order effects should only depend on pitch and roll, i.e. [4 5]')
514+
est_second_order_effects = traits.List(
515+
traits.Int(),
516+
minlen=1,
517+
maxlen=6,
518+
field='uweoptions.sot',
519+
desc='List of second order terms to model second derivatives of.')
520+
est_unwarp_fwhm = traits.Range(
521+
low=0.0,
522+
field='uweoptions.uwfwhm',
523+
desc='gaussian smoothing kernel width for unwarp')
524+
est_re_est_mov_par = traits.Bool(
525+
field='uweoptions.rem',
526+
desc='Re-estimate movement parameters at each unwarping iteration.')
527+
est_num_of_interations = traits.ListInt(
528+
[5],
529+
field='uweoptions.noi',
530+
minlen=1,
531+
maxlen=1,
532+
usedfault=True,
533+
desc='Number of iterations.')
534+
est_taylor_expansion_point = traits.String(
535+
'Average',
536+
field='uweoptions.expround',
537+
usedefault=True,
538+
desc='Point in position space to perform Taylor-expansion around.')
539+
reslice_which = traits.ListInt(
540+
[2, 1],
541+
field='uwroptions.uwwhich',
542+
minlen=2,
543+
maxlen=2,
544+
usedefault=True,
545+
desc='determines which images to reslice')
546+
reslice_interp = traits.Range(
547+
low=0,
548+
high=7,
549+
field='uwroptions.rinterp',
550+
desc='degree of b-spline used for interpolation')
551+
reslice_wrap = traits.List(
552+
traits.Int(),
553+
minlen=3,
554+
maxlen=3,
555+
field='uwroptions.wrap',
556+
desc='Check if interpolation should wrap in [x,y,z]')
557+
reslice_mask = traits.Bool(
558+
field='uwroptions.mask',
559+
desc='True/False mask output image')
560+
out_prefix = traits.String(
561+
'u',
562+
field='uwroptions.prefix',
563+
usedefault=True,
564+
desc='realigned and unwarped output prefix')
565+
566+
567+
class RealignUnwarpOutputSpec(TraitedSpec):
568+
mean_image = File(exists=True, desc='Mean image file from the realignment & unwarping')
569+
modified_in_files = OutputMultiPath(
570+
traits.Either(traits.List(File(exists=True)), File(exists=True)),
571+
desc=('Copies of all files passed to '
572+
'in_files. Headers will have '
573+
'been modified to align all '
574+
'images with the first, or '
575+
'optionally to first do that, '
576+
'extract a mean image, and '
577+
're-align to that mean image.'))
578+
realigned_unwarped_files = OutputMultiPath(
579+
traits.Either(traits.List(File(exists=True)), File(exists=True)),
580+
desc='Realigned and unwarped files written to disc.')
581+
realignment_parameters = OutputMultiPath(
582+
File(exists=True),
583+
desc='Estimated translation and rotation parameters')
584+
585+
586+
class RealignUnwarp(SPMCommand):
587+
"""Use spm_uw_estimate for estimating within subject registration and unwarping
588+
of time series. Function accepts only one single field map. If in_files is a
589+
list of files they will be treated as separate sessions but associated to the
590+
same fieldmap.
591+
592+
http://www.fil.ion.ucl.ac.uk/spm/doc/manual.pdf#page=31
593+
594+
Examples
595+
--------
596+
597+
>>> import nipype.interfaces.spm as spm
598+
>>> realignUnwarp = spm.RealignUnwarp()
599+
>>> realignUnwarp.inputs.in_files = ['functional.nii', 'functional2.nii']
600+
>>> realignUnwarp.inputs.phase_map = 'voxeldisplacemap.vdm'
601+
>>> realignUnwarp.inputs.register_to_mean = True
602+
>>> realignUnwarp.run() # doctest: +SKIP
603+
604+
"""
605+
606+
input_spec = RealignUnwarpInputSpec
607+
output_spec = RealignUnwarpOutputSpec
608+
609+
_jobtype = 'spatial'
610+
_jobname = 'realignunwarp'
611+
612+
def _format_arg(self, opt, spec, val):
613+
"""Convert input to appropriate format for spm
614+
"""
615+
if opt == 'in_files':
616+
return scans_for_fnames(ensure_list(val),
617+
keep4d=False,
618+
separate_sessions=True)
619+
return super(RealignUnwarp, self)._format_arg(opt, spec, val)
620+
621+
622+
def _parse_inputs(self, skip=()):
623+
624+
spmdict = super(RealignUnwarp, self)._parse_inputs(skip=())[0]
625+
626+
if isdefined(self.inputs.phase_map):
627+
pmscan = spmdict['data']['pmscan']
628+
else:
629+
pmscan = ''
630+
631+
if isdefined(self.inputs.in_files):
632+
if isinstance(self.inputs.in_files, list):
633+
data = [dict(scans = sess, pmscan = pmscan)
634+
for sess in spmdict['data']['scans']]
635+
else:
636+
data = [dict(scans = spmdict['data']['scans'], pmscan = pmscan)]
637+
638+
spmdict['data'] = data
639+
640+
return [spmdict]
641+
642+
643+
def _list_outputs(self):
644+
outputs = self._outputs().get()
645+
resliced_all = self.inputs.reslice_which[0] > 0
646+
resliced_mean = self.inputs.reslice_which[1] > 0
647+
648+
if isdefined(self.inputs.in_files):
649+
outputs['realignment_parameters'] = []
650+
for imgf in self.inputs.in_files:
651+
if isinstance(imgf, list):
652+
tmp_imgf = imgf[0]
653+
else:
654+
tmp_imgf = imgf
655+
outputs['realignment_parameters'].append(fname_presuffix(tmp_imgf,
656+
prefix='rp_',
657+
suffix='.txt',
658+
use_ext=False))
659+
if not isinstance(imgf, list) and func_is_3d(imgf):
660+
break
661+
662+
if isinstance(self.inputs.in_files[0], list):
663+
first_image = self.inputs.in_files[0][0]
664+
else:
665+
first_image = self.inputs.in_files[0]
666+
667+
if resliced_mean:
668+
outputs['mean_image'] = fname_presuffix(first_image, prefix='meanu')
669+
670+
if resliced_all:
671+
outputs['realigned_unwarped_files'] = []
672+
for idx, imgf in enumerate(ensure_list(self.inputs.in_files)):
673+
realigned_run = []
674+
if isinstance(imgf, list):
675+
for i, inner_imgf in enumerate(ensure_list(imgf)):
676+
newfile = fname_presuffix(inner_imgf,
677+
prefix=self.inputs.out_prefix)
678+
realigned_run.append(newfile)
679+
else:
680+
realigned_run = fname_presuffix(imgf,
681+
prefix=self.inputs.out_prefix)
682+
outputs['realigned_unwarped_files'].append(realigned_run)
683+
return outputs
684+
685+
440686
class CoregisterInputSpec(SPMCommandInputSpec):
441687
target = ImageFileSPM(
442688
exists=True,

nipype/testing/data/voxeldisplacemap.vdm

Whitespace-only changes.

0 commit comments

Comments
 (0)