Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enh: trait for imaging files + implementation in SPM preproc #1949

Merged
merged 11 commits into from
May 3, 2017
4 changes: 2 additions & 2 deletions nipype/interfaces/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
from ..utils.filemanip import (md5, hash_infile, FileNotFoundError, hash_timestamp,
split_filename, to_str)
from .traits_extension import (
traits, Undefined, TraitDictObject, TraitListObject, TraitError, isdefined, File,
Directory, DictStrStr, has_metadata)
traits, Undefined, TraitDictObject, TraitListObject, TraitError, isdefined,
File, Directory, DictStrStr, has_metadata, ImageFile)
from ..external.due import due

runtime_profile = str2bool(config.get('execution', 'profile_runtime'))
Expand Down
3 changes: 0 additions & 3 deletions nipype/interfaces/fsl/tests/test_auto_Eddy.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ def test_Eddy_inputs():
def test_Eddy_outputs():
output_map = dict(out_corrected=dict(),
out_movement_rms=dict(),
out_outlier_map=dict(),
out_outlier_n_sd_map=dict(),
out_outlier_n_sqr_sd_map=dict(),
out_outlier_report=dict(),
out_parameter=dict(),
out_restricted_movement_rms=dict(),
Expand Down
10 changes: 5 additions & 5 deletions nipype/interfaces/fsl/tests/test_auto_TOPUP.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ def test_TOPUP_inputs():
name_source=['in_file'],
name_template='%s_field',
),
out_warp_prefix=dict(argstr='--dfout=%s',
hash_files=False,
usedefault=True,
),
out_jac_prefix=dict(argstr='--jacout=%s',
hash_files=False,
usedefault=True,
Expand All @@ -68,6 +64,10 @@ def test_TOPUP_inputs():
name_source=['in_file'],
name_template='%s_topup.log',
),
out_warp_prefix=dict(argstr='--dfout=%s',
hash_files=False,
usedefault=True,
),
output_type=dict(),
readout_times=dict(mandatory=True,
requires=['encoding_direction'],
Expand Down Expand Up @@ -104,10 +104,10 @@ def test_TOPUP_outputs():
out_enc_file=dict(),
out_field=dict(),
out_fieldcoef=dict(),
out_jacs=dict(),
out_logfile=dict(),
out_movpar=dict(),
out_warps=dict(),
out_jacs=dict(),
)
outputs = TOPUP.output_spec()

Expand Down
25 changes: 24 additions & 1 deletion nipype/interfaces/spm/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from ... import logging
from ...utils import spm_docs as sd, NUMPY_MMAP
from ..base import (BaseInterface, traits, isdefined, InputMultiPath,
BaseInterfaceInputSpec, Directory, Undefined)
BaseInterfaceInputSpec, Directory, Undefined, ImageFile)
from ..matlab import MatlabCommand
from ...external.due import due, Doi, BibTeX

Expand Down Expand Up @@ -532,3 +532,26 @@ def _make_matlab_command(self, contents, postscript=None):
if postscript is not None:
mscript += postscript
return mscript

class ImageFileSPM(ImageFile):
"""
Defines an ImageFile trait specific to SPM interfaces.
"""

def __init__(self, value='', filter=None, auto_set=False, entries=0,
exists=False, types=['nifti1', 'nifti2'],
allow_compressed=False, **metadata):
""" Trait handles neuroimaging files.

Parameters
----------
types : list
Strings of file format types accepted
compressed : boolean
Indicates whether the file format can compressed
"""
self.types = types
self.allow_compressed = allow_compressed
super(ImageFileSPM, self).__init__(value, filter, auto_set, entries,
exists, types, allow_compressed,
**metadata)
92 changes: 45 additions & 47 deletions nipype/interfaces/spm/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@
from ..base import (OutputMultiPath, TraitedSpec, isdefined,
traits, InputMultiPath, File)
from .base import (SPMCommand, scans_for_fname, func_is_3d,
scans_for_fnames, SPMCommandInputSpec)
scans_for_fnames, SPMCommandInputSpec, ImageFileSPM)

__docformat__ = 'restructuredtext'



class SliceTimingInputSpec(SPMCommandInputSpec):
in_files = InputMultiPath(traits.Either(traits.List(File(exists=True)),
File(exists=True)), field='scans',
in_files = InputMultiPath(traits.Either(traits.List(ImageFileSPM(
exists=True)),
ImageFileSPM(exists=True)),
field='scans',
desc='list of filenames to apply slice timing',
mandatory=True, copyfile=False)
num_slices = traits.Int(field='nslices',
Expand Down Expand Up @@ -116,8 +119,9 @@ def _list_outputs(self):


class RealignInputSpec(SPMCommandInputSpec):
in_files = InputMultiPath(traits.Either(traits.List(File(exists=True)),
File(exists=True)), field='data',
in_files = InputMultiPath(traits.Either(traits.List(
ImageFileSPM(exists=True)),
ImageFileSPM(exists=True)), field='data',
mandatory=True, copyfile=True,
desc='list of filenames to realign')
jobtype = traits.Enum('estwrite', 'estimate', 'write',
Expand Down Expand Up @@ -270,11 +274,12 @@ def _list_outputs(self):


class CoregisterInputSpec(SPMCommandInputSpec):
target = File(exists=True, field='ref', mandatory=True,
desc='reference file to register to', copyfile=False)
source = InputMultiPath(File(exists=True), field='source',
desc='file to register to target', copyfile=True,
mandatory=True)
target = ImageFileSPM(exists=True, mandatory=True,
field='ref', desc='reference file to register to',
copyfile=False)
source = InputMultiPath(ImageFileSPM(exists=True),
field='source', desc='file to register to target',
copyfile=True, mandatory=True)
jobtype = traits.Enum('estwrite', 'estimate', 'write',
desc='one of: estimate, write, estwrite',
usedefault=True)
Expand Down Expand Up @@ -392,9 +397,9 @@ class NormalizeInputSpec(SPMCommandInputSpec):
desc='template file to normalize to',
mandatory=True, xor=['parameter_file'],
copyfile=False)
source = InputMultiPath(File(exists=True), field='subj.source',
source = InputMultiPath(ImageFileSPM(exists=True),
field='subj.source', xor=['parameter_file'],
desc='file to normalize to template',
xor=['parameter_file'],
mandatory=True, copyfile=True)
jobtype = traits.Enum('estwrite', 'est', 'write', usedefault=True,
desc='Estimate, Write or do both')
Expand Down Expand Up @@ -555,22 +560,22 @@ def _list_outputs(self):


class Normalize12InputSpec(SPMCommandInputSpec):
image_to_align = File(exists=True, field='subj.vol',
image_to_align = ImageFileSPM(exists=True, field='subj.vol',
desc=('file to estimate normalization parameters '
'with'),
xor=['deformation_file'],
mandatory=True, copyfile=True)
apply_to_files = InputMultiPath(
traits.Either(File(exists=True), traits.List(File(exists=True))),
traits.Either(ImageFileSPM(exists=True),
traits.List(ImageFileSPM(exists=True))),
field='subj.resample',
desc='files to apply transformation to',
copyfile=True)
deformation_file = File(field='subj.def', mandatory=True,
xor=['image_to_align', 'tpm'],
deformation_file = ImageFileSPM(field='subj.def', mandatory=True,
xor=['image_to_align', 'tpm'], copyfile=False,
desc=('file y_*.nii containing 3 deformation '
'fields for the deformation in x, y and z '
'dimension'),
copyfile=False)
'dimension'))
jobtype = traits.Enum('estwrite', 'est', 'write', usedefault=True,
desc='Estimate, Write or do Both')
bias_regularization = traits.Enum(0, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1,
Expand Down Expand Up @@ -721,7 +726,7 @@ def _list_outputs(self):


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


class NewSegmentInputSpec(SPMCommandInputSpec):
channel_files = InputMultiPath(File(exists=True),
channel_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
desc="A list of files to be segmented",
field='channel', copyfile=False,
mandatory=True)
field='channel', copyfile=False)
channel_info = traits.Tuple(traits.Float(), traits.Float(),
traits.Tuple(traits.Bool, traits.Bool),
desc="""A tuple with the following fields:
Expand All @@ -902,7 +906,7 @@ class NewSegmentInputSpec(SPMCommandInputSpec):
- which maps to save (Corrected, Field) - a tuple of two boolean values""",
field='channel')
tissues = traits.List(
traits.Tuple(traits.Tuple(File(exists=True), traits.Int()),
traits.Tuple(traits.Tuple(ImageFileSPM(exists=True),traits.Int()),
traits.Int(), traits.Tuple(traits.Bool, traits.Bool),
traits.Tuple(traits.Bool, traits.Bool)),
desc="""A list of tuples (one per tissue) with the following fields:
Expand Down Expand Up @@ -1093,8 +1097,8 @@ def _list_outputs(self):


class SmoothInputSpec(SPMCommandInputSpec):
in_files = InputMultiPath(File(exists=True), field='data',
desc='list of files to smooth',
in_files = InputMultiPath(ImageFileSPM(exists=True),
field='data', desc='list of files to smooth',
mandatory=True, copyfile=False)
fwhm = traits.Either(traits.List(traits.Float(), minlen=3, maxlen=3),
traits.Float(), field='fwhm',
Expand Down Expand Up @@ -1156,7 +1160,7 @@ def _list_outputs(self):


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


class DARTELNorm2MNIInputSpec(SPMCommandInputSpec):
template_file = File(exists=True,
desc="DARTEL template",
field='mni_norm.template', copyfile=False,
mandatory=True)
flowfield_files = InputMultiPath(File(exists=True),
template_file = ImageFileSPM(exists=True, copyfile=False, mandatory=True,
desc="DARTEL template", field='mni_norm.template')
flowfield_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
desc="DARTEL flow fields u_rc1*",
field='mni_norm.data.subjs.flowfields',
mandatory=True)
apply_to_files = InputMultiPath(File(exists=True),
field='mni_norm.data.subjs.flowfields')
apply_to_files = InputMultiPath(ImageFileSPM(exists=True),
desc="Files to apply the transform to",
field='mni_norm.data.subjs.images',
mandatory=True, copyfile=False)
Expand Down Expand Up @@ -1370,14 +1371,12 @@ def _list_outputs(self):


class CreateWarpedInputSpec(SPMCommandInputSpec):
image_files = InputMultiPath(File(exists=True),
image_files = InputMultiPath(ImageFileSPM(exists=True), mandatory=True,
desc="A list of files to be warped",
field='crt_warped.images', copyfile=False,
mandatory=True)
flowfield_files = InputMultiPath(File(exists=True),
field='crt_warped.images', copyfile=False)
flowfield_files = InputMultiPath(ImageFileSPM(exists=True), copyfile=False,
desc="DARTEL flow fields u_rc1*",
field='crt_warped.flowfields',
copyfile=False,
mandatory=True)
iterations = traits.Range(low=0, high=9,
desc=("The number of iterations: log2(number of "
Expand Down Expand Up @@ -1440,10 +1439,10 @@ def _list_outputs(self):


class ApplyDeformationFieldInputSpec(SPMCommandInputSpec):
in_files = InputMultiPath(File(exists=True), mandatory=True,
field='fnames')
in_files = InputMultiPath(ImageFileSPM(exists=True),
mandatory=True, field='fnames')
deformation_field = File(exists=True, mandatory=True, field='comp{1}.def')
reference_volume = File(exists=True, mandatory=True,
reference_volume = ImageFileSPM(exists=True, mandatory=True,
field='comp{2}.id.space')
interp = traits.Range(low=0, high=7, field='interp',
desc='degree of b-spline used for interpolation')
Expand Down Expand Up @@ -1486,13 +1485,12 @@ def _list_outputs(self):
class VBMSegmentInputSpec(SPMCommandInputSpec):

in_files = InputMultiPath(
File(exists=True),
ImageFileSPM(exists=True),
desc="A list of files to be segmented",
field='estwrite.data', copyfile=False, mandatory=True)

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

spatial_normalization = traits.Enum(
'high', 'low', usedefault=True,)
dartel_template = File(
dartel_template = ImageFileSPM(
exists=True,
field='estwrite.extopts.dartelwarp.normhigh.darteltpm')
use_sanlm_denoising_filter = traits.Range(
Expand Down
16 changes: 16 additions & 0 deletions nipype/interfaces/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,3 +718,19 @@ def to_list(x):
failed_dict[key] = (value, newval)
return failed_dict

def test_ImageFile():
x = nib.BaseInterface().inputs

# setup traits
x.add_trait('nifti', nib.ImageFile(types=['nifti1', 'dicom']))
x.add_trait('anytype', nib.ImageFile())
x.add_trait('newtype', nib.ImageFile(types=['nifti10']))
x.add_trait('nocompress', nib.ImageFile(types=['mgh'],
allow_compressed=False))

with pytest.raises(nib.TraitError): x.nifti = 'test.mgz'
x.nifti = 'test.nii'
x.anytype = 'test.xml'
with pytest.raises(AttributeError): x.newtype = 'test.nii'
with pytest.raises(nib.TraitError): x.nocompress = 'test.nii.gz'
x.nocompress = 'test.mgh'
Loading