Skip to content

Commit 26e8c12

Browse files
authored
Merge pull request #1949 from mgxd/fix/realign
enh: trait for imaging files + implementation in SPM preproc
2 parents 592ce3a + bf564f7 commit 26e8c12

File tree

7 files changed

+162
-61
lines changed

7 files changed

+162
-61
lines changed

nipype/interfaces/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
from ..utils.filemanip import (md5, hash_infile, FileNotFoundError, hash_timestamp,
3939
split_filename, to_str)
4040
from .traits_extension import (
41-
traits, Undefined, TraitDictObject, TraitListObject, TraitError, isdefined, File,
42-
Directory, DictStrStr, has_metadata)
41+
traits, Undefined, TraitDictObject, TraitListObject, TraitError, isdefined,
42+
File, Directory, DictStrStr, has_metadata, ImageFile)
4343
from ..external.due import due
4444

4545
runtime_profile = str2bool(config.get('execution', 'profile_runtime'))

nipype/interfaces/fsl/tests/test_auto_Eddy.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@ def test_Eddy_inputs():
8888
def test_Eddy_outputs():
8989
output_map = dict(out_corrected=dict(),
9090
out_movement_rms=dict(),
91-
out_outlier_map=dict(),
92-
out_outlier_n_sd_map=dict(),
93-
out_outlier_n_sqr_sd_map=dict(),
9491
out_outlier_report=dict(),
9592
out_parameter=dict(),
9693
out_restricted_movement_rms=dict(),

nipype/interfaces/fsl/tests/test_auto_TOPUP.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ def test_TOPUP_inputs():
5454
name_source=['in_file'],
5555
name_template='%s_field',
5656
),
57-
out_warp_prefix=dict(argstr='--dfout=%s',
58-
hash_files=False,
59-
usedefault=True,
60-
),
6157
out_jac_prefix=dict(argstr='--jacout=%s',
6258
hash_files=False,
6359
usedefault=True,
@@ -68,6 +64,10 @@ def test_TOPUP_inputs():
6864
name_source=['in_file'],
6965
name_template='%s_topup.log',
7066
),
67+
out_warp_prefix=dict(argstr='--dfout=%s',
68+
hash_files=False,
69+
usedefault=True,
70+
),
7171
output_type=dict(),
7272
readout_times=dict(mandatory=True,
7373
requires=['encoding_direction'],
@@ -104,10 +104,10 @@ def test_TOPUP_outputs():
104104
out_enc_file=dict(),
105105
out_field=dict(),
106106
out_fieldcoef=dict(),
107+
out_jacs=dict(),
107108
out_logfile=dict(),
108109
out_movpar=dict(),
109110
out_warps=dict(),
110-
out_jacs=dict(),
111111
)
112112
outputs = TOPUP.output_spec()
113113

nipype/interfaces/spm/base.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from ... import logging
3131
from ...utils import spm_docs as sd, NUMPY_MMAP
3232
from ..base import (BaseInterface, traits, isdefined, InputMultiPath,
33-
BaseInterfaceInputSpec, Directory, Undefined)
33+
BaseInterfaceInputSpec, Directory, Undefined, ImageFile)
3434
from ..matlab import MatlabCommand
3535
from ...external.due import due, Doi, BibTeX
3636

@@ -532,3 +532,26 @@ def _make_matlab_command(self, contents, postscript=None):
532532
if postscript is not None:
533533
mscript += postscript
534534
return mscript
535+
536+
class ImageFileSPM(ImageFile):
537+
"""
538+
Defines an ImageFile trait specific to SPM interfaces.
539+
"""
540+
541+
def __init__(self, value='', filter=None, auto_set=False, entries=0,
542+
exists=False, types=['nifti1', 'nifti2'],
543+
allow_compressed=False, **metadata):
544+
""" Trait handles neuroimaging files.
545+
546+
Parameters
547+
----------
548+
types : list
549+
Strings of file format types accepted
550+
compressed : boolean
551+
Indicates whether the file format can compressed
552+
"""
553+
self.types = types
554+
self.allow_compressed = allow_compressed
555+
super(ImageFileSPM, self).__init__(value, filter, auto_set, entries,
556+
exists, types, allow_compressed,
557+
**metadata)

nipype/interfaces/spm/preprocess.py

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@
2525
from ..base import (OutputMultiPath, TraitedSpec, isdefined,
2626
traits, InputMultiPath, File)
2727
from .base import (SPMCommand, scans_for_fname, func_is_3d,
28-
scans_for_fnames, SPMCommandInputSpec)
28+
scans_for_fnames, SPMCommandInputSpec, ImageFileSPM)
2929

3030
__docformat__ = 'restructuredtext'
3131

3232

33+
3334
class SliceTimingInputSpec(SPMCommandInputSpec):
34-
in_files = InputMultiPath(traits.Either(traits.List(File(exists=True)),
35-
File(exists=True)), field='scans',
35+
in_files = InputMultiPath(traits.Either(traits.List(ImageFileSPM(
36+
exists=True)),
37+
ImageFileSPM(exists=True)),
38+
field='scans',
3639
desc='list of filenames to apply slice timing',
3740
mandatory=True, copyfile=False)
3841
num_slices = traits.Int(field='nslices',
@@ -116,8 +119,9 @@ def _list_outputs(self):
116119

117120

118121
class RealignInputSpec(SPMCommandInputSpec):
119-
in_files = InputMultiPath(traits.Either(traits.List(File(exists=True)),
120-
File(exists=True)), field='data',
122+
in_files = InputMultiPath(traits.Either(traits.List(
123+
ImageFileSPM(exists=True)),
124+
ImageFileSPM(exists=True)), field='data',
121125
mandatory=True, copyfile=True,
122126
desc='list of filenames to realign')
123127
jobtype = traits.Enum('estwrite', 'estimate', 'write',
@@ -270,11 +274,12 @@ def _list_outputs(self):
270274

271275

272276
class CoregisterInputSpec(SPMCommandInputSpec):
273-
target = File(exists=True, field='ref', mandatory=True,
274-
desc='reference file to register to', copyfile=False)
275-
source = InputMultiPath(File(exists=True), field='source',
276-
desc='file to register to target', copyfile=True,
277-
mandatory=True)
277+
target = ImageFileSPM(exists=True, mandatory=True,
278+
field='ref', desc='reference file to register to',
279+
copyfile=False)
280+
source = InputMultiPath(ImageFileSPM(exists=True),
281+
field='source', desc='file to register to target',
282+
copyfile=True, mandatory=True)
278283
jobtype = traits.Enum('estwrite', 'estimate', 'write',
279284
desc='one of: estimate, write, estwrite',
280285
usedefault=True)
@@ -392,9 +397,9 @@ class NormalizeInputSpec(SPMCommandInputSpec):
392397
desc='template file to normalize to',
393398
mandatory=True, xor=['parameter_file'],
394399
copyfile=False)
395-
source = InputMultiPath(File(exists=True), field='subj.source',
400+
source = InputMultiPath(ImageFileSPM(exists=True),
401+
field='subj.source', xor=['parameter_file'],
396402
desc='file to normalize to template',
397-
xor=['parameter_file'],
398403
mandatory=True, copyfile=True)
399404
jobtype = traits.Enum('estwrite', 'est', 'write', usedefault=True,
400405
desc='Estimate, Write or do both')
@@ -555,22 +560,22 @@ def _list_outputs(self):
555560

556561

557562
class Normalize12InputSpec(SPMCommandInputSpec):
558-
image_to_align = File(exists=True, field='subj.vol',
563+
image_to_align = ImageFileSPM(exists=True, field='subj.vol',
559564
desc=('file to estimate normalization parameters '
560565
'with'),
561566
xor=['deformation_file'],
562567
mandatory=True, copyfile=True)
563568
apply_to_files = InputMultiPath(
564-
traits.Either(File(exists=True), traits.List(File(exists=True))),
569+
traits.Either(ImageFileSPM(exists=True),
570+
traits.List(ImageFileSPM(exists=True))),
565571
field='subj.resample',
566572
desc='files to apply transformation to',
567573
copyfile=True)
568-
deformation_file = File(field='subj.def', mandatory=True,
569-
xor=['image_to_align', 'tpm'],
574+
deformation_file = ImageFileSPM(field='subj.def', mandatory=True,
575+
xor=['image_to_align', 'tpm'], copyfile=False,
570576
desc=('file y_*.nii containing 3 deformation '
571577
'fields for the deformation in x, y and z '
572-
'dimension'),
573-
copyfile=False)
578+
'dimension'))
574579
jobtype = traits.Enum('estwrite', 'est', 'write', usedefault=True,
575580
desc='Estimate, Write or do Both')
576581
bias_regularization = traits.Enum(0, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1,
@@ -721,7 +726,7 @@ def _list_outputs(self):
721726

722727

723728
class SegmentInputSpec(SPMCommandInputSpec):
724-
data = InputMultiPath(File(exists=True), field='data',
729+
data = InputMultiPath(ImageFileSPM(exists=True), field='data',
725730
desc='one scan per subject',
726731
copyfile=False, mandatory=True)
727732
gm_output_type = traits.List(traits.Bool(), minlen=3, maxlen=3,
@@ -890,10 +895,9 @@ def _list_outputs(self):
890895

891896

892897
class NewSegmentInputSpec(SPMCommandInputSpec):
893-
channel_files = InputMultiPath(File(exists=True),
898+
channel_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
894899
desc="A list of files to be segmented",
895-
field='channel', copyfile=False,
896-
mandatory=True)
900+
field='channel', copyfile=False)
897901
channel_info = traits.Tuple(traits.Float(), traits.Float(),
898902
traits.Tuple(traits.Bool, traits.Bool),
899903
desc="""A tuple with the following fields:
@@ -902,7 +906,7 @@ class NewSegmentInputSpec(SPMCommandInputSpec):
902906
- which maps to save (Corrected, Field) - a tuple of two boolean values""",
903907
field='channel')
904908
tissues = traits.List(
905-
traits.Tuple(traits.Tuple(File(exists=True), traits.Int()),
909+
traits.Tuple(traits.Tuple(ImageFileSPM(exists=True),traits.Int()),
906910
traits.Int(), traits.Tuple(traits.Bool, traits.Bool),
907911
traits.Tuple(traits.Bool, traits.Bool)),
908912
desc="""A list of tuples (one per tissue) with the following fields:
@@ -1093,8 +1097,8 @@ def _list_outputs(self):
10931097

10941098

10951099
class SmoothInputSpec(SPMCommandInputSpec):
1096-
in_files = InputMultiPath(File(exists=True), field='data',
1097-
desc='list of files to smooth',
1100+
in_files = InputMultiPath(ImageFileSPM(exists=True),
1101+
field='data', desc='list of files to smooth',
10981102
mandatory=True, copyfile=False)
10991103
fwhm = traits.Either(traits.List(traits.Float(), minlen=3, maxlen=3),
11001104
traits.Float(), field='fwhm',
@@ -1156,7 +1160,7 @@ def _list_outputs(self):
11561160

11571161

11581162
class DARTELInputSpec(SPMCommandInputSpec):
1159-
image_files = traits.List(traits.List(File(exists=True)),
1163+
image_files = traits.List(traits.List(ImageFileSPM(exists=True)),
11601164
desc="A list of files to be segmented",
11611165
field='warp.images', copyfile=False,
11621166
mandatory=True)
@@ -1272,15 +1276,12 @@ def _list_outputs(self):
12721276

12731277

12741278
class DARTELNorm2MNIInputSpec(SPMCommandInputSpec):
1275-
template_file = File(exists=True,
1276-
desc="DARTEL template",
1277-
field='mni_norm.template', copyfile=False,
1278-
mandatory=True)
1279-
flowfield_files = InputMultiPath(File(exists=True),
1279+
template_file = ImageFileSPM(exists=True, copyfile=False, mandatory=True,
1280+
desc="DARTEL template", field='mni_norm.template')
1281+
flowfield_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
12801282
desc="DARTEL flow fields u_rc1*",
1281-
field='mni_norm.data.subjs.flowfields',
1282-
mandatory=True)
1283-
apply_to_files = InputMultiPath(File(exists=True),
1283+
field='mni_norm.data.subjs.flowfields')
1284+
apply_to_files = InputMultiPath(ImageFileSPM(exists=True),
12841285
desc="Files to apply the transform to",
12851286
field='mni_norm.data.subjs.images',
12861287
mandatory=True, copyfile=False)
@@ -1370,14 +1371,12 @@ def _list_outputs(self):
13701371

13711372

13721373
class CreateWarpedInputSpec(SPMCommandInputSpec):
1373-
image_files = InputMultiPath(File(exists=True),
1374+
image_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
13741375
desc="A list of files to be warped",
1375-
field='crt_warped.images', copyfile=False,
1376-
mandatory=True)
1377-
flowfield_files = InputMultiPath(File(exists=True),
1376+
field='crt_warped.images', copyfile=False)
1377+
flowfield_files = InputMultiPath(ImageFileSPM(exists=True), copyfile=False,
13781378
desc="DARTEL flow fields u_rc1*",
13791379
field='crt_warped.flowfields',
1380-
copyfile=False,
13811380
mandatory=True)
13821381
iterations = traits.Range(low=0, high=9,
13831382
desc=("The number of iterations: log2(number of "
@@ -1440,10 +1439,10 @@ def _list_outputs(self):
14401439

14411440

14421441
class ApplyDeformationFieldInputSpec(SPMCommandInputSpec):
1443-
in_files = InputMultiPath(File(exists=True), mandatory=True,
1444-
field='fnames')
1442+
in_files = InputMultiPath(ImageFileSPM(exists=True),
1443+
mandatory=True, field='fnames')
14451444
deformation_field = File(exists=True, mandatory=True, field='comp{1}.def')
1446-
reference_volume = File(exists=True, mandatory=True,
1445+
reference_volume = ImageFileSPM(exists=True, mandatory=True,
14471446
field='comp{2}.id.space')
14481447
interp = traits.Range(low=0, high=7, field='interp',
14491448
desc='degree of b-spline used for interpolation')
@@ -1486,13 +1485,12 @@ def _list_outputs(self):
14861485
class VBMSegmentInputSpec(SPMCommandInputSpec):
14871486

14881487
in_files = InputMultiPath(
1489-
File(exists=True),
1488+
ImageFileSPM(exists=True),
14901489
desc="A list of files to be segmented",
14911490
field='estwrite.data', copyfile=False, mandatory=True)
14921491

1493-
tissues = File(
1494-
exists=True, field='estwrite.tpm',
1495-
desc='tissue probability map')
1492+
tissues = ImageFileSPM(
1493+
exists=True, field='estwrite.tpm', desc='tissue probability map')
14961494
gaussians_per_class = traits.Tuple(
14971495
(2, 2, 2, 3, 4, 2), *([traits.Int()] * 6),
14981496
usedefault=True,
@@ -1518,7 +1516,7 @@ class VBMSegmentInputSpec(SPMCommandInputSpec):
15181516

15191517
spatial_normalization = traits.Enum(
15201518
'high', 'low', usedefault=True,)
1521-
dartel_template = File(
1519+
dartel_template = ImageFileSPM(
15221520
exists=True,
15231521
field='estwrite.extopts.dartelwarp.normhigh.darteltpm')
15241522
use_sanlm_denoising_filter = traits.Range(

nipype/interfaces/tests/test_base.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,3 +718,19 @@ def to_list(x):
718718
failed_dict[key] = (value, newval)
719719
return failed_dict
720720

721+
def test_ImageFile():
722+
x = nib.BaseInterface().inputs
723+
724+
# setup traits
725+
x.add_trait('nifti', nib.ImageFile(types=['nifti1', 'dicom']))
726+
x.add_trait('anytype', nib.ImageFile())
727+
x.add_trait('newtype', nib.ImageFile(types=['nifti10']))
728+
x.add_trait('nocompress', nib.ImageFile(types=['mgh'],
729+
allow_compressed=False))
730+
731+
with pytest.raises(nib.TraitError): x.nifti = 'test.mgz'
732+
x.nifti = 'test.nii'
733+
x.anytype = 'test.xml'
734+
with pytest.raises(AttributeError): x.newtype = 'test.nii'
735+
with pytest.raises(nib.TraitError): x.nocompress = 'test.nii.gz'
736+
x.nocompress = 'test.mgh'

0 commit comments

Comments
 (0)