Skip to content

Adding niftyseg #1911

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

Merged
merged 18 commits into from
May 3, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Symplifying the interfaces when possible using name_source/name_template
  • Loading branch information
byvernault committed Apr 20, 2017
commit ee44a576b2a3e188c78b1f53e4ce6c44d28ff23a
15 changes: 1 addition & 14 deletions nipype/interfaces/niftyseg/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

"""

from nipype.interfaces.base import CommandLine, isdefined
from nipype.interfaces.base import CommandLine
from nipype.utils.filemanip import split_filename
import os
import subprocess
Expand Down Expand Up @@ -89,16 +89,3 @@ def _gen_fname(self, basename, out_dir=None, suffix=None, ext=None):
if suffix is not None:
final_bn = ''.join((final_bn, suffix))
return os.path.abspath(os.path.join(out_dir, final_bn + final_ext))

def _gen_filename(self, name):
if name == 'out_file':
return self._gen_fname(self.inputs.in_file, suffix=self._suffix)
return None

def _list_outputs(self):
outputs = self.output_spec().get()
if isdefined(self.inputs.out_file):
outputs['out_file'] = self.inputs.out_file
else:
outputs['out_file'] = self._gen_filename('out_file')
return outputs
54 changes: 13 additions & 41 deletions nipype/interfaces/niftyseg/em.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
>>> os.chdir(datadir)
"""

import os

from ..base import (TraitedSpec, File, traits, isdefined, CommandLineInputSpec,
from ..base import (TraitedSpec, File, traits, CommandLineInputSpec,
InputMultiPath)
from .base import NiftySegCommand, get_custom_path

Expand Down Expand Up @@ -91,14 +89,17 @@ class EMInputSpec(CommandLineInputSpec):
desc=desc)

# outputs
out_file = File(argstr='-out %s',
genfile=True,
out_file = File(name_source=['in_file'],
name_template='%s_em.nii.gz',
argstr='-out %s',
desc='Output segmentation')
out_bc_file = File(argstr='-bc_out %s',
genfile=True,
out_bc_file = File(name_source=['in_file'],
name_template='%s_bc_em.nii.gz',
argstr='-bc_out %s',
desc='Output bias corrected image')
out_outlier_file = File(argstr='-out_outlier %s',
genfile=True,
out_outlier_file = File(name_source=['in_file'],
name_template='%s_outlier_em.nii.gz',
argstr='-out_outlier %s',
desc='Output outlierness image')


Expand Down Expand Up @@ -126,9 +127,9 @@ class EM(NiftySegCommand):
>>> node = niftyseg.EM()
>>> node.inputs.in_file = 'im1.nii'
>>> node.inputs.no_prior = 4
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'seg_EM -in im1.nii -nopriors 4 -bc_out .../im1_bc_em.nii \
-out .../im1_em.nii -out_outlier .../im1_outlier_em.nii'
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_EM -in im1.nii -nopriors 4 -bc_out im1_bc_em.nii.gz \
-out im1_em.nii.gz -out_outlier im1_outlier_em.nii.gz'

"""
_cmd = get_custom_path('seg_EM')
Expand All @@ -143,32 +144,3 @@ def _format_arg(self, opt, spec, val):
return '-priors %d %s' % (_nb_priors, ' '.join(self.inputs.priors))
else:
return super(EM, self)._format_arg(opt, spec, val)

def _list_outputs(self):
outputs = self.output_spec().get()
outputs['out_file'] = self.inputs.out_file
if not isdefined(self.inputs.out_file):
outputs['out_file'] = self._gen_fname(self.inputs.in_file,
suffix=self._suffix)
outputs['out_file'] = os.path.abspath(outputs['out_file'])
outputs['out_bc_file'] = self.inputs.out_bc_file
if not isdefined(self.inputs.out_bc_file):
outputs['out_bc_file'] = self._gen_fname(
self.inputs.in_file, suffix=('_bc%s' % self._suffix))
outputs['out_bc_file'] = os.path.abspath(outputs['out_bc_file'])
outputs['out_outlier_file'] = self.inputs.out_outlier_file
if not isdefined(self.inputs.out_outlier_file):
outputs['out_outlier_file'] = self._gen_fname(
self.inputs.in_file, suffix=('_outlier%s' % self._suffix))
outputs['out_outlier_file'] = os.path.abspath(
outputs['out_outlier_file'])
return outputs

def _gen_filename(self, name):
if name == 'out_file':
return self._list_outputs()['out_file']
if name == 'out_bc_file':
return self._list_outputs()['out_bc_file']
if name == 'out_outlier_file':
return self._list_outputs()['out_outlier_file']
return None
32 changes: 6 additions & 26 deletions nipype/interfaces/niftyseg/lesions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@
>>> os.chdir(datadir)
"""

import os
import warnings

from ..base import TraitedSpec, File, traits, isdefined, CommandLineInputSpec
from ..base import TraitedSpec, File, traits, CommandLineInputSpec
from .base import NiftySegCommand, get_custom_path


Expand All @@ -39,7 +38,9 @@ class FillLesionsInputSpec(CommandLineInputSpec):
desc='Lesion mask', position=2)

# Output file name
out_file = File(desc='The output filename of the fill lesions results',
out_file = File(name_source=['in_file'],
name_template='%s_lesions_filled.nii.gz',
desc='The output filename of the fill lesions results',
argstr='-o %s', position=3)

# Optional arguments
Expand Down Expand Up @@ -108,31 +109,10 @@ class FillLesions(NiftySegCommand):
>>> node = niftyseg.FillLesions()
>>> node.inputs.in_file = 'im1.nii'
>>> node.inputs.lesion_mask = 'im2.nii'
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'seg_FillLesions -i im1.nii -l im2.nii -o .../im1_lesions_filled.nii'
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_FillLesions -i im1.nii -l im2.nii -o im1_lesions_filled.nii.gz'

"""
_cmd = get_custom_path('seg_FillLesions')
input_spec = FillLesionsInputSpec
output_spec = FillLesionsOutputSpec

def _list_outputs(self):
outputs = self.output_spec().get()
outputs['out_file'] = self.inputs.out_file
if not isdefined(self.inputs.out_file):
outputs['out_file'] = self._gen_filename('out_file')
outputs['out_file'] = os.path.abspath(outputs['out_file'])
return outputs

def _parse_inputs(self, skip=None):
"""Set non-mandatory inputs if not given by user."""
skip = []
if not isdefined(self.inputs.out_file):
self.inputs.out_file = self._gen_filename('out_file')
return super(FillLesions, self)._parse_inputs(skip=skip)

def _gen_filename(self, name):
if name == 'out_file':
return self._gen_fname(self.inputs.in_file,
suffix='_lesions_filled')
return None
47 changes: 20 additions & 27 deletions nipype/interfaces/niftyseg/maths.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
>>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data'))
>>> os.chdir(datadir)
"""
import os

from ..base import (TraitedSpec, File, traits, isdefined, CommandLineInputSpec,
NipypeInterfaceError)
Expand Down Expand Up @@ -71,6 +70,7 @@ class MathsCommand(NiftySegCommand):
_cmd = get_custom_path('seg_maths')
input_spec = MathsInput
output_spec = MathsOutput
_suffix = '_maths'

def _list_outputs(self):
outputs = self.output_spec().get()
Expand All @@ -79,11 +79,11 @@ def _list_outputs(self):
if suffix != '_merged' and isdefined(self.inputs.operation):
suffix = '_' + self.inputs.operation

outputs['out_file'] = self.inputs.out_file
if not isdefined(self.inputs.out_file):
if isdefined(self.inputs.out_file):
outputs['out_file'] = self.inputs.out_file
else:
outputs['out_file'] = self._gen_fname(self.inputs.in_file,
suffix=suffix)
outputs['out_file'] = os.path.abspath(outputs['out_file'])
return outputs

def _gen_filename(self, name):
Expand Down Expand Up @@ -149,8 +149,8 @@ class UnaryMaths(MathsCommand):
>>> node.inputs.in_file = 'im1.nii'
>>> node.inputs.operation = 'sqrt'
>>> node.inputs.output_datatype = 'float'
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'seg_maths im1.nii -sqrt -odt float .../im1_sqrt.nii'
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_maths im1.nii -sqrt -odt float im1_sqrt.nii'

"""
input_spec = UnaryMathsInput
Expand Down Expand Up @@ -237,8 +237,8 @@ class BinaryMaths(MathsCommand):
>>> node.inputs.operation = 'sub'
>>> node.inputs.operand_file = 'im2.nii'
>>> node.inputs.output_datatype = 'float'
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'seg_maths im1.nii -sub im2.nii -odt float .../im1_sub.nii'
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_maths im1.nii -sub im2.nii -odt float im1_sub.nii'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all these interfaces would deserve some more doctests with more operations, so that we are sure that they work and that we don't break them in future maintenance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think I can follow fslmaths doctests to edit those ones?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I had in mind is something closer to the documentation of nipype.interfaces.ants.registration.Registration: https://github.com/nipy/nipype/blob/master/nipype/interfaces/ants/registration.py#L432-L640

Here, you'll see how the inputs to the interface are varied and then the new cmdline tested.

Copy link
Contributor Author

@byvernault byvernault May 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do that but for those interfaces it's pretty easy, only the operation changed. It's kind of the same than fslmaths.

I added different exemples for some operation but by doing so I realised that I am not checking the inputs for some argument (for example pow, thr, uthr, ... use only a float or min uses only a file). I am adding that to the interface.

Let me know if you approve this and you really want to see one test per operation.


"""
input_spec = BinaryMathsInput
Expand All @@ -256,19 +256,12 @@ def _format_arg(self, opt, spec, val):
return super(BinaryMaths, self)._format_arg(opt, spec, val)

def _list_outputs(self):
outputs = self.output_spec().get()
self._suffix = '_' + self.inputs.operation

outputs['out_file'] = self.inputs.out_file
if not isdefined(self.inputs.out_file):
if isdefined(self.inputs.operation) and \
self.inputs.operation == 'hdr_copy':
outputs['out_file'] = self._gen_fname(self.inputs.operand_file,
suffix=self._suffix)
else:
outputs['out_file'] = self._gen_fname(self.inputs.in_file,
suffix=self._suffix)
outputs['out_file'] = os.path.abspath(outputs['out_file'])
outputs = super(BinaryMaths, self)._list_outputs()

if self.inputs.operation == 'hdr_copy':
outputs['out_file'] = self._gen_fname(
self.inputs.operand_file,
suffix='_{0}'.format(self.inputs.operation))
return outputs


Expand Down Expand Up @@ -312,8 +305,8 @@ class BinaryMathsInteger(MathsCommand):
>>> node.inputs.operation = 'dil'
>>> node.inputs.operand_value = 2
>>> node.inputs.output_datatype = 'float'
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'seg_maths im1.nii -dil 2 -odt float .../im1_dil.nii'
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_maths im1.nii -dil 2 -odt float im1_dil.nii'

"""
input_spec = BinaryMathsInputInteger
Expand Down Expand Up @@ -383,8 +376,8 @@ class TupleMaths(MathsCommand):
>>> node.inputs.operand_file1 = 'im2.nii'
>>> node.inputs.operand_value2 = 2.0
>>> node.inputs.output_datatype = 'float'
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'seg_maths im1.nii -lncc im2.nii 2.00000000 -odt float .../im1_lncc.nii'
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_maths im1.nii -lncc im2.nii 2.00000000 -odt float im1_lncc.nii'

"""
input_spec = TupleMathsInput
Expand Down Expand Up @@ -424,9 +417,9 @@ class Merge(MathsCommand):
>>> node.inputs.merge_files = files
>>> node.inputs.dimension = 2
>>> node.inputs.output_datatype = 'float'
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_maths im1.nii -merge 2 2 im2.nii im3.nii -odt float \
.../im1_merged.nii'
im1_merged.nii'

"""
input_spec = MergeInput
Expand Down
27 changes: 7 additions & 20 deletions nipype/interfaces/niftyseg/patchmatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@ class PatchMatchInputSpec(CommandLineInputSpec):
position=2)

database_file = File(argstr='-db %s',
genfile=True,
exists=True,
mandatory=True,
desc='Database with the segmentations',
position=3)

# Output file name
out_file = File(desc='The output filename of the patchmatch results',
out_file = File(name_source=['in_file'],
name_template='%s_pm.nii.gz',
desc='The output filename of the patchmatch results',
argstr='-o %s',
position=4,
genfile=True)
position=4)

# Optional arguments
patch_size = traits.Int(desc="Patch size, #voxels",
Expand Down Expand Up @@ -105,25 +106,11 @@ class PatchMatch(NiftySegCommand):
>>> node.inputs.in_file = 'im1.nii'
>>> node.inputs.mask_file = 'im2.nii'
>>> node.inputs.database_file = 'db.xml'
>>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'seg_PatchMatch -i im1.nii -m im2.nii -db db.xml -o .../im1_pm.nii'
>>> node.cmdline # doctest: +ALLOW_UNICODE
'seg_PatchMatch -i im1.nii -m im2.nii -db db.xml -o im1_pm.nii.gz'

"""
_cmd = get_custom_path('seg_PatchMatch')
input_spec = PatchMatchInputSpec
output_spec = PatchMatchOutputSpec
_suffix = '_pm'

def _list_outputs(self):
outputs = self.output_spec().get()
outputs['out_file'] = self.inputs.out_file
if not isdefined(self.inputs.out_file):
outputs['out_file'] = self._gen_fname(self.inputs.in_file,
suffix=self._suffix)
outputs['out_file'] = os.path.abspath(outputs['out_file'])
return outputs

def _gen_filename(self, name):
if name == 'out_file':
return self._list_outputs()['out_file']
return None
Loading