Skip to content

Commit d6b6786

Browse files
author
qtabs
committed
BF - added SPM realign_unwarp function to SPM-preprocessing
1 parent cd5567b commit d6b6786

File tree

1 file changed

+246
-1
lines changed

1 file changed

+246
-1
lines changed

nipype/interfaces/spm/preprocess.py

Lines changed: 246 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
# Local imports
1717
from ...utils.filemanip import (fname_presuffix, ensure_list,
18-
simplify_list, split_filename)
18+
simplify_list, split_filename, filename_to_list)
1919
from ..base import (OutputMultiPath, TraitedSpec, isdefined,
2020
traits, InputMultiPath, File, Str)
2121
from .base import (SPMCommand, scans_for_fname, func_is_3d,
@@ -437,6 +437,251 @@ def _list_outputs(self):
437437
return outputs
438438

439439

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

0 commit comments

Comments
 (0)