Skip to content

Commit 4812f51

Browse files
committed
ENH: tidy up fsl-epi tools
* TOPUP: regarding #794, #796 I've cleaned up the docstring and implemented the three arguments that were still missing. The problem is that TOPUP admits one or more values for them, so the implementation of all the traits should be updated. * Eddy: the implementation of @JensNRAD seems to work out, I reviewed the docstring. Close #769. * Added deprecation notices and minor fixes.
1 parent f21aabe commit 4812f51

File tree

4 files changed

+156
-104
lines changed

4 files changed

+156
-104
lines changed

nipype/interfaces/fsl/epi.py

Lines changed: 145 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class PrepareFieldmapOutputSpec( TraitedSpec ):
4747
out_fieldmap = File( exists=True, desc='output name for prepared fieldmap' )
4848

4949
class PrepareFieldmap(FSLCommand):
50-
""" Interface for the fsl_prepare_fieldmap script (FSL 5.0)
50+
"""
51+
Interface for the fsl_prepare_fieldmap script (FSL 5.0)
5152
5253
Prepares a fieldmap suitable for FEAT from SIEMENS data - saves output in rad/s format
5354
e.g. fsl_prepare_fieldmap SIEMENS images_3_gre_field_mapping images_4_gre_field_mapping fmap_rads 2.65
@@ -134,6 +135,9 @@ class TOPUPInputSpec(FSLCommandInputSpec):
134135
out_logfile = File(argstr='--logout=%s', desc='name of log-file',
135136
name_source=['in_file'], name_template='%s_topup.log',
136137
keep_extension=True, hash_files=False)
138+
139+
# TODO: the following traits admit values separated by commas, one value per
140+
# registration level inside topup.
137141
warp_res = traits.Float(10.0, argstr='--warpres=%f',
138142
desc=('(approximate) resolution (in mm) of warp '
139143
'basis for the different sub-sampling levels'))
@@ -146,13 +150,24 @@ class TOPUPInputSpec(FSLCommandInputSpec):
146150
'arguments'))
147151
max_iter = traits.Int(5, argstr='--miter=%d',
148152
desc='max # of non-linear iterations')
149-
# @oesteban: I don't know how to implement these 3 parameters, AFAIK there's
150-
# no documentation.
151-
#lambda Weight of regularisation, default depending on --ssqlambda and --regmod switches. See user documetation.
152-
#ssqlambda If set (=1), lambda is weighted by current ssq, default 1
153-
#regmod Model for regularisation of warp-field [membrane_energy bending_energy], default bending_energy
154-
estmov = traits.Enum(1, 0,
155-
desc='estimate movements if set', argstr='--estmov=%d')
153+
reg_lambda = traits.Float(1.0, argstr='--miter=%0.f',
154+
desc='lambda weighting value of the regularisation term')
155+
ssqlambda = traits.Enum(1, 0, argstr='--ssqlambda=%d',
156+
desc=('Weight lambda by the current value of the ssd. If used (=1), '
157+
'the effective weight of regularisation term becomes higher for '
158+
'the initial iterations, therefore initial steps are a little '
159+
'smoother than they would without weighting. This reduces the '
160+
'risk of finding a local minimum.'))
161+
regmod = traits.Enum('bending_energy', 'membrane_energy', argstr='--regmod=%s',
162+
desc=('Regularisation term implementation. Defaults to bending_energy. '
163+
'Note that the two functions have vastly different scales. The '
164+
'membrane energy is based on the first derivatives and the bending '
165+
'energy on the second derivatives. The second derivatives will '
166+
'typically be much smaller than the first derivatives, so input '
167+
'lambda will have to be larger for bending_energy to yield '
168+
'approximately the same level of regularisation.'))
169+
estmov = traits.Enum(1, 0, argstr='--estmov=%d',
170+
desc='estimate movements if set')
156171
minmet = traits.Enum(0, 1, argstr='--minmet=%d',
157172
desc=('Minimisation method 0=Levenberg-Marquardt, '
158173
'1=Scaled Conjugate Gradient'))
@@ -183,24 +198,27 @@ class TOPUPOutputSpec(TraitedSpec):
183198

184199

185200
class TOPUP(FSLCommand):
186-
""" Interface for FSL topup, a tool for estimating and correcting
187-
susceptibility induced distortions
188-
Reference: http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/TOPUP
189-
Example: http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/ExampleTopupFollowedByApplytopup
190-
191-
topup --imain=<some 4D image> --datain=<text file> --config=<text file with parameters> --coutname=my_field
192-
201+
"""
202+
Interface for FSL topup, a tool for estimating and correcting
203+
susceptibility induced distortions. See FSL documentation for
204+
`reference <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/TOPUP>`_,
205+
`usage examples
206+
<http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/ExampleTopupFollowedByApplytopup>`_,
207+
and `exemplary config files
208+
<https://github.com/ahheckel/FSL-scripts/blob/master/rsc/fsl/fsl4/topup/b02b0.cnf`_.
193209
194-
Examples
195-
--------
210+
Examples
211+
--------
196212
197-
>>> from nipype.interfaces.fsl import TOPUP
198-
>>> topup = TOPUP()
199-
>>> topup.inputs.in_file = "b0_b0rev.nii"
200-
>>> topup.inputs.encoding_file = "topup_encoding.txt"
201-
>>> topup.cmdline #doctest: +ELLIPSIS
202-
'topup --config=b02b0.cnf --datain=topup_encoding.txt --imain=b0_b0rev.nii --out=b0_b0rev_base --iout=b0_b0rev_corrected.nii.gz --fout=b0_b0rev_field.nii.gz --logout=b0_b0rev_topup.log'
203-
>>> res = topup.run() # doctest: +SKIP
213+
>>> from nipype.interfaces.fsl import TOPUP
214+
>>> topup = TOPUP()
215+
>>> topup.inputs.in_file = "b0_b0rev.nii"
216+
>>> topup.inputs.encoding_file = "topup_encoding.txt"
217+
>>> topup.cmdline #doctest: +ELLIPSIS
218+
'topup --config=b02b0.cnf --datain=topup_encoding.txt --imain=b0_b0rev.nii \
219+
--out=b0_b0rev_base --iout=b0_b0rev_corrected.nii.gz --fout=b0_b0rev_field.nii.gz \
220+
--logout=b0_b0rev_topup.log'
221+
>>> res = topup.run() # doctest: +SKIP
204222
205223
"""
206224
_cmd = 'topup'
@@ -297,24 +315,26 @@ class ApplyTOPUPOutputSpec( TraitedSpec ):
297315

298316

299317
class ApplyTOPUP( FSLCommand ):
300-
""" Interface for FSL topup, a tool for estimating and correcting susceptibility induced distortions.
301-
`General reference <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/ApplytopupUsersGuide>`_
302-
and `use example <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/ExampleTopupFollowedByApplytopup>`_.
318+
"""
319+
Interface for FSL topup, a tool for estimating and correcting susceptibility induced distortions.
320+
`General reference <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/ApplytopupUsersGuide>`_
321+
and `use example <http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/topup/ExampleTopupFollowedByApplytopup>`_.
303322
304323
305-
Examples
306-
--------
324+
Examples
325+
--------
307326
308-
>>> from nipype.interfaces.fsl import ApplyTOPUP
309-
>>> applytopup = ApplyTOPUP()
310-
>>> applytopup.inputs.in_files = [ "epi.nii", "epi_rev.nii" ]
311-
>>> applytopup.inputs.encoding_file = "topup_encoding.txt"
312-
>>> applytopup.inputs.in_index = [ 1,2 ]
313-
>>> applytopup.inputs.in_topup_fieldcoef = "topup_fieldcoef.nii.gz"
314-
>>> applytopup.inputs.in_topup_movpar = "topup_movpar.txt"
315-
>>> applytopup.cmdline #doctest: +ELLIPSIS
316-
'applytopup --datain=topup_encoding.txt --imain=epi.nii,epi_rev.nii --inindex=1,2 --topup=topup --out=epi_corrected.nii.gz'
317-
>>> res = applytopup.run() # doctest: +SKIP
327+
>>> from nipype.interfaces.fsl import ApplyTOPUP
328+
>>> applytopup = ApplyTOPUP()
329+
>>> applytopup.inputs.in_files = [ "epi.nii", "epi_rev.nii" ]
330+
>>> applytopup.inputs.encoding_file = "topup_encoding.txt"
331+
>>> applytopup.inputs.in_index = [ 1,2 ]
332+
>>> applytopup.inputs.in_topup_fieldcoef = "topup_fieldcoef.nii.gz"
333+
>>> applytopup.inputs.in_topup_movpar = "topup_movpar.txt"
334+
>>> applytopup.cmdline #doctest: +ELLIPSIS
335+
'applytopup --datain=topup_encoding.txt --imain=epi.nii,epi_rev.nii --inindex=1,2 \
336+
--topup=topup --out=epi_corrected.nii.gz'
337+
>>> res = applytopup.run() # doctest: +SKIP
318338
319339
"""
320340
_cmd = 'applytopup'
@@ -391,6 +411,7 @@ class Eddy(FSLCommand):
391411
392412
Examples
393413
--------
414+
394415
>>> from nipype.interfaces.fsl import Eddy
395416
>>> eddy = Eddy()
396417
>>> eddy.inputs.in_file = 'epi.nii'
@@ -400,7 +421,8 @@ class Eddy(FSLCommand):
400421
>>> eddy.inputs.in_bvec = 'bvecs.scheme'
401422
>>> eddy.inputs.in_bval = 'bvals.scheme'
402423
>>> eddy.cmdline #doctest: +ELLIPSIS
403-
'eddy --acqp=epi_acqp.txt --bvals=bvals.scheme --bvecs=bvecs.scheme --imain=epi.nii --index=epi_index.txt --mask=epi_mask.nii --out=.../eddy_corrected'
424+
'eddy --acqp=epi_acqp.txt --bvals=bvals.scheme --bvecs=bvecs.scheme --imain=epi.nii \
425+
--index=epi_index.txt --mask=epi_mask.nii --out=.../eddy_corrected'
404426
>>> res = eddy.run() # doctest: +SKIP
405427
406428
"""
@@ -428,6 +450,67 @@ def _list_outputs(self):
428450
return outputs
429451

430452

453+
class SigLossInputSpec(FSLCommandInputSpec):
454+
in_file = File(mandatory=True,
455+
exists=True,
456+
argstr='-i %s',
457+
desc='b0 fieldmap file')
458+
out_file = File(argstr='-s %s',
459+
desc='output signal loss estimate file',
460+
genfile=True)
461+
462+
mask_file = File(exists=True,
463+
argstr='-m %s',
464+
desc='brain mask file')
465+
echo_time = traits.Float(argstr='--te=%f',
466+
desc='echo time in seconds')
467+
slice_direction = traits.Enum('x','y','z',
468+
argstr='-d %s',
469+
desc='slicing direction')
470+
class SigLossOuputSpec(TraitedSpec):
471+
out_file = File(exists=True,
472+
desc='signal loss estimate file')
473+
474+
class SigLoss(FSLCommand):
475+
"""
476+
Estimates signal loss from a field map (in rad/s)
477+
478+
Examples
479+
--------
480+
481+
>>> from nipype.interfaces.fsl import SigLoss
482+
>>> sigloss = SigLoss()
483+
>>> sigloss.inputs.in_file = "phase.nii"
484+
>>> sigloss.inputs.echo_time = 0.03
485+
>>> sigloss.inputs.output_type = "NIFTI_GZ"
486+
>>> sigloss.cmdline #doctest: +ELLIPSIS
487+
'sigloss --te=0.030000 -i phase.nii -s .../phase_sigloss.nii.gz'
488+
>>> res = sigloss.run() # doctest: +SKIP
489+
490+
491+
"""
492+
input_spec = SigLossInputSpec
493+
output_spec = SigLossOuputSpec
494+
_cmd = 'sigloss'
495+
496+
def _list_outputs(self):
497+
outputs = self.output_spec().get()
498+
outputs['out_file'] = self.inputs.out_file
499+
if not isdefined(outputs['out_file']) and isdefined(self.inputs.in_file):
500+
outputs['out_file']=self._gen_fname(self.inputs.in_file,
501+
suffix='_sigloss')
502+
return outputs
503+
504+
def _gen_filename(self, name):
505+
if name=='out_file':
506+
return self._list_outputs()['out_file']
507+
return None
508+
509+
#######################################
510+
# deprecated interfaces
511+
#######################################
512+
513+
431514
class EPIDeWarpInputSpec(FSLCommandInputSpec):
432515
mag_file = File(exists=True,
433516
desc='Magnitude file',
@@ -472,7 +555,12 @@ class EPIDeWarpOutputSpec(TraitedSpec):
472555

473556

474557
class EPIDeWarp(FSLCommand):
475-
"""Wraps fieldmap unwarping script from Freesurfer's epidewarp.fsl_
558+
"""
559+
Wraps the unwarping script `epidewarp.fsl
560+
<http://surfer.nmr.mgh.harvard.edu/fswiki/epidewarp.fsl>`_.
561+
562+
.. warning:: deprecated in FSL, please use
563+
:func:`nipype.workflows.dmri.preprocess.epi.sdc_fmb` instead.
476564
477565
Examples
478566
--------
@@ -487,16 +575,24 @@ class EPIDeWarp(FSLCommand):
487575
'epidewarp.fsl --mag magnitude.nii --dph phase.nii --epi functional.nii --esp 0.58 --exfdw .../exfdw.nii.gz --nocleanup --sigma 2 --tediff 2.46 --tmpdir .../temp --vsm .../vsm.nii.gz'
488576
>>> res = dewarp.run() # doctest: +SKIP
489577
490-
References
491-
----------
492-
_epidewarp.fsl: http://surfer.nmr.mgh.harvard.edu/fswiki/epidewarp.fsl
493578
494579
"""
495580

496581
_cmd = 'epidewarp.fsl'
497582
input_spec = EPIDeWarpInputSpec
498583
output_spec = EPIDeWarpOutputSpec
499584

585+
def __init__(self, **inputs):
586+
warnings.warn("Deprecated: Please use nipype.workflows.dmri.preprocess.epi.sdc_fmb instead",
587+
DeprecationWarning)
588+
return super(EPIDeWarp, self).__init__(**inputs)
589+
590+
def _run_interface(self, runtime):
591+
runtime = super(EPIDeWarp, self)._run_interface(runtime)
592+
if runtime.stderr:
593+
self.raise_exception(runtime)
594+
return runtime
595+
500596
def _gen_filename(self, name):
501597
if name == 'exfdw':
502598
if isdefined(self.inputs.exf_file):
@@ -539,59 +635,6 @@ def _list_outputs(self):
539635
return outputs
540636

541637

542-
class SigLossInputSpec(FSLCommandInputSpec):
543-
in_file = File(mandatory=True,
544-
exists=True,
545-
argstr='-i %s',
546-
desc='b0 fieldmap file')
547-
out_file = File(argstr='-s %s',
548-
desc='output signal loss estimate file',
549-
genfile=True)
550-
551-
mask_file = File(exists=True,
552-
argstr='-m %s',
553-
desc='brain mask file')
554-
echo_time = traits.Float(argstr='--te=%f',
555-
desc='echo time in seconds')
556-
slice_direction = traits.Enum('x','y','z',
557-
argstr='-d %s',
558-
desc='slicing direction')
559-
class SigLossOuputSpec(TraitedSpec):
560-
out_file = File(exists=True,
561-
desc='signal loss estimate file')
562-
563-
class SigLoss(FSLCommand):
564-
"""Estimates signal loss from a field map (in rad/s)
565-
566-
Examples
567-
--------
568-
569-
>>> from nipype.interfaces.fsl import SigLoss
570-
>>> sigloss = SigLoss()
571-
>>> sigloss.inputs.in_file = "phase.nii"
572-
>>> sigloss.inputs.echo_time = 0.03
573-
>>> sigloss.inputs.output_type = "NIFTI_GZ"
574-
>>> sigloss.cmdline #doctest: +ELLIPSIS
575-
'sigloss --te=0.030000 -i phase.nii -s .../phase_sigloss.nii.gz'
576-
>>> res = sigloss.run() # doctest: +SKIP
577-
"""
578-
input_spec = SigLossInputSpec
579-
output_spec = SigLossOuputSpec
580-
_cmd = 'sigloss'
581-
582-
def _list_outputs(self):
583-
outputs = self.output_spec().get()
584-
outputs['out_file'] = self.inputs.out_file
585-
if not isdefined(outputs['out_file']) and isdefined(self.inputs.in_file):
586-
outputs['out_file']=self._gen_fname(self.inputs.in_file,
587-
suffix='_sigloss')
588-
return outputs
589-
590-
def _gen_filename(self, name):
591-
if name=='out_file':
592-
return self._list_outputs()['out_file']
593-
return None
594-
595638
class EddyCorrectInputSpec(FSLCommandInputSpec):
596639
in_file = File(exists=True, desc='4D input file', argstr='%s', position=0,
597640
mandatory=True)
@@ -607,7 +650,9 @@ class EddyCorrectOutputSpec(TraitedSpec):
607650

608651

609652
class EddyCorrect(FSLCommand):
610-
""" Deprecated! Please use create_eddy_correct_pipeline instead
653+
"""
654+
655+
.. warning:: Deprecated in FSL. Please use :class:`nipype.interfaces.fsl.epi.Eddy` instead
611656
612657
Example
613658
-------
@@ -623,7 +668,8 @@ class EddyCorrect(FSLCommand):
623668
output_spec = EddyCorrectOutputSpec
624669

625670
def __init__(self, **inputs):
626-
warnings.warn("Deprecated: Please use create_eddy_correct_pipeline instead", DeprecationWarning)
671+
warnings.warn("Deprecated: Please use nipype.interfaces.fsl.epi.Eddy instead",
672+
DeprecationWarning)
627673
return super(EddyCorrect, self).__init__(**inputs)
628674

629675
def _run_interface(self, runtime):

nipype/interfaces/fsl/tests/test_auto_Eddy.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ def test_Eddy_inputs():
3434
mandatory=True,
3535
),
3636
in_topup_fieldcoef=dict(argstr='--topup=%s',
37-
copyfile=False,
3837
requires=['in_topup_movpar'],
3938
),
40-
in_topup_movpar=dict(copyfile=False,
41-
requires=['in_topup_fieldcoef'],
39+
in_topup_movpar=dict(requires=['in_topup_fieldcoef'],
4240
),
4341
method=dict(argstr='--resamp=%s',
4442
),

nipype/interfaces/fsl/tests/test_auto_EddyCorrect.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ def test_EddyCorrect_inputs():
1616
position=0,
1717
),
1818
out_file=dict(argstr='%s',
19-
genfile=True,
20-
hash_files=False,
19+
name_source=['in_file'],
20+
name_template='%s_edc',
21+
output_name='eddy_corrected',
2122
position=1,
2223
),
2324
output_type=dict(),
2425
ref_num=dict(argstr='%d',
2526
mandatory=True,
2627
position=2,
28+
usedefault=True,
2729
),
2830
terminal_output=dict(mandatory=True,
2931
nohash=True,

nipype/interfaces/fsl/tests/test_auto_TOPUP.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,18 @@ def test_TOPUP_inputs():
6464
requires=['encoding_direction'],
6565
xor=['encoding_file'],
6666
),
67+
reg_lambda=dict(argstr='--miter=%0.f',
68+
),
69+
regmod=dict(argstr='--regmod=%s',
70+
),
6771
regrid=dict(argstr='--regrid=%d',
6872
),
6973
scale=dict(argstr='--scale=%d',
7074
),
7175
splineorder=dict(argstr='--splineorder=%d',
7276
),
77+
ssqlambda=dict(argstr='--ssqlambda=%d',
78+
),
7379
subsamp=dict(argstr='--subsamp=%d',
7480
),
7581
terminal_output=dict(mandatory=True,

0 commit comments

Comments
 (0)