Skip to content

Commit e6626af

Browse files
committed
enh: imagetype base for spm, new image formats, allow_compression
1 parent 519e96c commit e6626af

File tree

3 files changed

+78
-65
lines changed

3 files changed

+78
-65
lines changed

nipype/interfaces/spm/base.py

Lines changed: 25 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,27 @@ 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'], extensions=[],
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.compressed = compressed
555+
self.exts = extensions
556+
super(ImageFileSPM, self).__init__(value, filter, auto_set, entries,
557+
exists, types, allow_compressed,
558+
extensions, **metadata)

nipype/interfaces/spm/preprocess.py

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,18 @@
2323
from ...utils.filemanip import (fname_presuffix, filename_to_list,
2424
list_to_filename, split_filename)
2525
from ..base import (OutputMultiPath, TraitedSpec, isdefined,
26-
traits, InputMultiPath, File, ImageFile)
26+
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

3333

3434
class SliceTimingInputSpec(SPMCommandInputSpec):
35-
in_files = InputMultiPath(traits.Either(traits.List(ImageFile(
36-
exists=True,
37-
compressed=False)),
38-
ImageFile(exists=True,
39-
compressed=False)),
35+
in_files = InputMultiPath(traits.Either(traits.List(ImageFileSPM(
36+
exists=True)),
37+
ImageFileSPM(exists=True),
4038
field='scans',
4139
desc='list of filenames to apply slice timing',
4240
mandatory=True, copyfile=False)
@@ -121,10 +119,9 @@ def _list_outputs(self):
121119

122120

123121
class RealignInputSpec(SPMCommandInputSpec):
124-
in_files = InputMultiPath(traits.Either(traits.List(ImageFile(exists=True,
125-
compressed=False)),
126-
ImageFile(exists=True,
127-
compressed=False)), field='data',
122+
in_files = InputMultiPath(traits.Either(traits.List(
123+
ImageFileSPM(exists=True)),
124+
ImageFileSPM(exists=True)), field='data',
128125
mandatory=True, copyfile=True,
129126
desc='list of filenames to realign')
130127
jobtype = traits.Enum('estwrite', 'estimate', 'write',
@@ -277,10 +274,10 @@ def _list_outputs(self):
277274

278275

279276
class CoregisterInputSpec(SPMCommandInputSpec):
280-
target = ImageFile(exists=True, compressed=False, mandatory=True,
277+
target = ImageFileSPM(exists=True, mandatory=True,
281278
field='ref', desc='reference file to register to',
282279
copyfile=False)
283-
source = InputMultiPath(ImageFile(exists=True, compressed=False),
280+
source = InputMultiPath(ImageFileSPM(exists=True),
284281
field='source', desc='file to register to target',
285282
copyfile=True, mandatory=True)
286283
jobtype = traits.Enum('estwrite', 'estimate', 'write',
@@ -400,7 +397,7 @@ class NormalizeInputSpec(SPMCommandInputSpec):
400397
desc='template file to normalize to',
401398
mandatory=True, xor=['parameter_file'],
402399
copyfile=False)
403-
source = InputMultiPath(ImageFile(exists=True, compressed=False),
400+
source = InputMultiPath(ImageFileSPM(exists=True),
404401
field='subj.source', xor=['parameter_file'],
405402
desc='file to normalize to template',
406403
mandatory=True, copyfile=True)
@@ -563,23 +560,22 @@ def _list_outputs(self):
563560

564561

565562
class Normalize12InputSpec(SPMCommandInputSpec):
566-
image_to_align = ImageFile(exists=True, compressed=False, field='subj.vol',
563+
image_to_align = ImageFileSPM(exists=True, field='subj.vol',
567564
desc=('file to estimate normalization parameters '
568565
'with'),
569566
xor=['deformation_file'],
570567
mandatory=True, copyfile=True)
571568
apply_to_files = InputMultiPath(
572-
traits.Either(ImageFile(exists=True, compressed=False),
573-
traits.List(ImageFile(exists=True, compressed=False))),
569+
traits.Either(ImageFileSPM(exists=True),
570+
traits.List(ImageFileSPM(exists=True))),
574571
field='subj.resample',
575572
desc='files to apply transformation to',
576573
copyfile=True)
577-
deformation_file = ImageFile(field='subj.def', mandatory=True,
578-
compressed=False, xor=['image_to_align', 'tpm'],
574+
deformation_file = ImageFileSPM(field='subj.def', mandatory=True,
575+
xor=['image_to_align', 'tpm'], copyfile=False,
579576
desc=('file y_*.nii containing 3 deformation '
580577
'fields for the deformation in x, y and z '
581-
'dimension'),
582-
copyfile=False)
578+
'dimension'))
583579
jobtype = traits.Enum('estwrite', 'est', 'write', usedefault=True,
584580
desc='Estimate, Write or do Both')
585581
bias_regularization = traits.Enum(0, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1,
@@ -730,7 +726,7 @@ def _list_outputs(self):
730726

731727

732728
class SegmentInputSpec(SPMCommandInputSpec):
733-
data = InputMultiPath(ImageFile(exists=True, compressed=False), field='data',
729+
data = InputMultiPath(ImageFileSPM(exists=True), field='data',
734730
desc='one scan per subject',
735731
copyfile=False, mandatory=True)
736732
gm_output_type = traits.List(traits.Bool(), minlen=3, maxlen=3,
@@ -899,10 +895,9 @@ def _list_outputs(self):
899895

900896

901897
class NewSegmentInputSpec(SPMCommandInputSpec):
902-
channel_files = InputMultiPath(ImageFile(exists=True, compressed=False),
898+
channel_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
903899
desc="A list of files to be segmented",
904-
field='channel', copyfile=False,
905-
mandatory=True)
900+
field='channel', copyfile=False)
906901
channel_info = traits.Tuple(traits.Float(), traits.Float(),
907902
traits.Tuple(traits.Bool, traits.Bool),
908903
desc="""A tuple with the following fields:
@@ -911,8 +906,7 @@ class NewSegmentInputSpec(SPMCommandInputSpec):
911906
- which maps to save (Corrected, Field) - a tuple of two boolean values""",
912907
field='channel')
913908
tissues = traits.List(
914-
traits.Tuple(traits.Tuple(ImageFile(exists=True, compressed=False),
915-
traits.Int()),
909+
traits.Tuple(traits.Tuple(ImageFileSPM(exists=True),traits.Int()),
916910
traits.Int(), traits.Tuple(traits.Bool, traits.Bool),
917911
traits.Tuple(traits.Bool, traits.Bool)),
918912
desc="""A list of tuples (one per tissue) with the following fields:
@@ -1103,7 +1097,7 @@ def _list_outputs(self):
11031097

11041098

11051099
class SmoothInputSpec(SPMCommandInputSpec):
1106-
in_files = InputMultiPath(ImageFile(exists=True, compressed=False),
1100+
in_files = InputMultiPath(ImageFileSPM(exists=True),
11071101
field='data', desc='list of files to smooth',
11081102
mandatory=True, copyfile=False)
11091103
fwhm = traits.Either(traits.List(traits.Float(), minlen=3, maxlen=3),
@@ -1166,8 +1160,7 @@ def _list_outputs(self):
11661160

11671161

11681162
class DARTELInputSpec(SPMCommandInputSpec):
1169-
image_files = traits.List(traits.List(ImageFile(exists=True,
1170-
compressed=False)),
1163+
image_files = traits.List(traits.List(ImageFileSPM(exists=True)),
11711164
desc="A list of files to be segmented",
11721165
field='warp.images', copyfile=False,
11731166
mandatory=True)
@@ -1283,14 +1276,12 @@ def _list_outputs(self):
12831276

12841277

12851278
class DARTELNorm2MNIInputSpec(SPMCommandInputSpec):
1286-
template_file = ImageFile(exists=True, compressed=False,
1287-
desc="DARTEL template", field='mni_norm.template',
1288-
copyfile=False, mandatory=True)
1289-
flowfield_files = InputMultiPath(ImageFile(exists=True, compressed=False),
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,
12901282
desc="DARTEL flow fields u_rc1*",
1291-
field='mni_norm.data.subjs.flowfields',
1292-
mandatory=True)
1293-
apply_to_files = InputMultiPath(ImageFile(exists=True, compressed=False),
1283+
field='mni_norm.data.subjs.flowfields')
1284+
apply_to_files = InputMultiPath(ImageFileSPM(exists=True),
12941285
desc="Files to apply the transform to",
12951286
field='mni_norm.data.subjs.images',
12961287
mandatory=True, copyfile=False)
@@ -1380,14 +1371,12 @@ def _list_outputs(self):
13801371

13811372

13821373
class CreateWarpedInputSpec(SPMCommandInputSpec):
1383-
image_files = InputMultiPath(ImageFile(exists=True, compressed=False),
1374+
image_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
13841375
desc="A list of files to be warped",
1385-
field='crt_warped.images', copyfile=False,
1386-
mandatory=True)
1387-
flowfield_files = InputMultiPath(ImageFile(exists=True, compressed=False),
1376+
field='crt_warped.images', copyfile=False)
1377+
flowfield_files = InputMultiPath(ImageFileSPM(exists=True), copyfile=False,
13881378
desc="DARTEL flow fields u_rc1*",
13891379
field='crt_warped.flowfields',
1390-
copyfile=False,
13911380
mandatory=True)
13921381
iterations = traits.Range(low=0, high=9,
13931382
desc=("The number of iterations: log2(number of "
@@ -1450,10 +1439,10 @@ def _list_outputs(self):
14501439

14511440

14521441
class ApplyDeformationFieldInputSpec(SPMCommandInputSpec):
1453-
in_files = InputMultiPath(ImageFile(exists=True, compressed=False),
1442+
in_files = InputMultiPath(ImageFileSPM(exists=True),
14541443
mandatory=True, field='fnames')
14551444
deformation_field = File(exists=True, mandatory=True, field='comp{1}.def')
1456-
reference_volume = ImageFile(exists=True, mandatory=True, compressed=False,
1445+
reference_volume = ImageFileSPM(exists=True, mandatory=True,
14571446
field='comp{2}.id.space')
14581447
interp = traits.Range(low=0, high=7, field='interp',
14591448
desc='degree of b-spline used for interpolation')
@@ -1496,13 +1485,12 @@ def _list_outputs(self):
14961485
class VBMSegmentInputSpec(SPMCommandInputSpec):
14971486

14981487
in_files = InputMultiPath(
1499-
ImageFile(exists=True, compressed=False),
1488+
ImageFileSPM(exists=True),
15001489
desc="A list of files to be segmented",
15011490
field='estwrite.data', copyfile=False, mandatory=True)
15021491

1503-
tissues = ImageFile(
1504-
exists=True, compressed=False, field='estwrite.tpm',
1505-
desc='tissue probability map')
1492+
tissues = ImageFileSPM(
1493+
exists=True, field='estwrite.tpm', desc='tissue probability map')
15061494
gaussians_per_class = traits.Tuple(
15071495
(2, 2, 2, 3, 4, 2), *([traits.Int()] * 6),
15081496
usedefault=True,
@@ -1528,8 +1516,8 @@ class VBMSegmentInputSpec(SPMCommandInputSpec):
15281516

15291517
spatial_normalization = traits.Enum(
15301518
'high', 'low', usedefault=True,)
1531-
dartel_template = ImageFile(
1532-
exists=True, compressed=False,
1519+
dartel_template = ImageFileSPM(
1520+
exists=True,
15331521
field='estwrite.extopts.dartelwarp.normhigh.darteltpm')
15341522
use_sanlm_denoising_filter = traits.Range(
15351523
0, 2, 2, usedefault=True, field='estwrite.extopts.sanlm',

nipype/interfaces/traits_extension.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,13 @@ def validate(self, object, name, value):
178178
if isinstance(value, (str, bytes)):
179179
if not self.exists:
180180
return value
181-
182181
if os.path.isdir(value):
183182
return value
184183
else:
185184
raise TraitError(
186185
args='The trait \'{}\' of {} instance is {}, but the path '
187-
' \'{}\' does not exist.'.format(name, class_of(object),
188-
self.info_text, value))
186+
' \'{}\' does not exist.'.format(name,
187+
class_of(object), self.info_text, value))
189188

190189
self.error(object, name, value)
191190

@@ -231,11 +230,10 @@ def __init__(self, value='', auto_set=False, entries=0,
231230
'nifti1': [('.nii', '.nii.gz'),
232231
(('.hdr', '.img'), ('.hdr', '.img.gz'))],
233232
'mgh': [('.mgh', '.mgz'), ('.mgh', '.mgh.gz')],
234-
'nifti2': [('.nii', '.nii.gz'),
235-
(('.hdr', '.img'), ('.hdr', '.img.gz'))],
236-
'cifti2': [('.nii', '.nii.gz'),
237-
(('.hdr', '.img'), ('.hdr', '.img.gz'))],
238-
'DICOM': [('.dcm', '.dcm'), ('.IMA', '.IMA'), ('.tar', '.tar.gz')],
233+
'nifti2': [('.nii', '.nii.gz')],
234+
'cifti2': [('.nii', '.nii.gz')],
235+
'gifti': [('.gii', '.gii.gz')],
236+
'dicom': [('.dcm', '.dcm'), ('.IMA', '.IMA'), ('.tar', '.tar.gz')],
239237
'nrrd': [('.nrrd', 'nrrd'), ('nhdr', 'nhdr')],
240238
'afni': [('.HEAD', '.HEAD'), ('.BRIK', '.BRIK')]
241239
}
@@ -244,7 +242,7 @@ class ImageFile(File):
244242
""" Defines a trait of specific neuroimaging files """
245243

246244
def __init__(self, value='', filter=None, auto_set=False, entries=0,
247-
exists=False, types=[], compressed=True, extensions=[],
245+
exists=False, types=[], allow_compressed=True, extensions=[],
248246
**metadata):
249247
""" Trait handles neuroimaging files.
250248
@@ -256,22 +254,25 @@ def __init__(self, value='', filter=None, auto_set=False, entries=0,
256254
Indicates whether the file format can compressed
257255
"""
258256
self.types = types
259-
self.compressed = compressed
257+
self.allow_compressed = allow_compressed
260258
self.exts = extensions
261259
super(ImageFile, self).__init__(value, filter, auto_set, entries,
262260
exists, **metadata)
263261

264262
def grab_exts(self, exts=[]):
263+
# TODO: file type validation
265264
for fmt in self.types:
266265
if fmt in img_fmt_types:
267266
exts.extend(sum([[u for u in y[0]] if isinstance(y[0], tuple)
268267
else [y[0]] for y in img_fmt_types[fmt]], []))
269-
if self.compressed:
270-
exts.extend(sum([[u for u in y[-1]] if isinstance(y[-1], tuple)
271-
else [y[-1]] for y in img_fmt_types[fmt]], []))
268+
if self.allow_compressed:
269+
exts.extend(sum([[u for u in y[-1]] if isinstance(y[-1],
270+
tuple) else [y[-1]] for y in img_fmt_types[fmt]], []))
272271
else:
273272
raise AttributeError('Information has not been added for format'
274-
'type {} yet.'.format(fmt))
273+
' type {} yet. Supported formats include: '
274+
'{}'.format('hello',
275+
', '.join(img_fmt_types.keys())))
275276
return list(set(exts))
276277

277278
def validate(self, object, name, value):

0 commit comments

Comments
 (0)