diff --git a/nipype/interfaces/niftyreg/__init__.py b/nipype/interfaces/niftyreg/__init__.py
index 9c0feeb2fb..64cc60a0ab 100644
--- a/nipype/interfaces/niftyreg/__init__.py
+++ b/nipype/interfaces/niftyreg/__init__.py
@@ -9,7 +9,7 @@
Top-level namespace for niftyreg.
"""
-from .base import no_niftyreg, get_custom_path
+from .base import no_nifty_package, get_custom_path
from .reg import RegAladin, RegF3D
from .regutils import (RegResample, RegJacobian, RegAverage, RegTools,
RegTransform, RegMeasure)
diff --git a/nipype/interfaces/niftyreg/base.py b/nipype/interfaces/niftyreg/base.py
index 9a21fcfd2e..a7672166fc 100644
--- a/nipype/interfaces/niftyreg/base.py
+++ b/nipype/interfaces/niftyreg/base.py
@@ -30,11 +30,11 @@
from ...utils.filemanip import split_filename
-def get_custom_path(command):
- return os.path.join(os.getenv('NIFTYREGDIR', ''), command)
+def get_custom_path(command, env_dir='NIFTYREGDIR'):
+ return os.path.join(os.getenv(env_dir, ''), command)
-def no_niftyreg(cmd='reg_f3d'):
+def no_nifty_package(cmd='reg_f3d'):
try:
return shutil.which(cmd) is None
except AttributeError: # Python < 3.3
@@ -64,7 +64,8 @@ def __init__(self, required_version=None, **inputs):
_version = self.get_version()
if _version:
_version = _version.decode("utf-8")
- if StrictVersion(_version) < StrictVersion(self._min_version):
+ if self._min_version is not None and \
+ StrictVersion(_version) < StrictVersion(self._min_version):
msg = 'A later version of Niftyreg is required (%s < %s)'
warn(msg % (_version, self._min_version))
if required_version is not None:
@@ -89,7 +90,7 @@ def check_version(self):
raise ValueError(err % (_version, self.required_version))
def get_version(self):
- if no_niftyreg(cmd=self.cmd):
+ if no_nifty_package(cmd=self.cmd):
return None
exec_cmd = ''.join((self.cmd, ' -v'))
return subprocess.check_output(exec_cmd, shell=True).strip()
diff --git a/nipype/interfaces/niftyreg/tests/test_reg.py b/nipype/interfaces/niftyreg/tests/test_reg.py
index 8f9a8eb886..9a3705fba7 100644
--- a/nipype/interfaces/niftyreg/tests/test_reg.py
+++ b/nipype/interfaces/niftyreg/tests/test_reg.py
@@ -4,13 +4,13 @@
import pytest
-from nipype.interfaces.niftyreg import (no_niftyreg, get_custom_path,
+from nipype.interfaces.niftyreg import (no_nifty_package, get_custom_path,
RegAladin, RegF3D)
from nipype.testing import example_data
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_aladin'),
+ no_nifty_package(cmd='reg_aladin'),
reason="niftyreg is not installed. reg_aladin not found.")
def test_reg_aladin():
""" tests for reg_aladin interface"""
@@ -48,7 +48,7 @@ def test_reg_aladin():
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_f3d'),
+ no_nifty_package(cmd='reg_f3d'),
reason="niftyreg is not installed. reg_f3d not found.")
def test_reg_f3d():
""" tests for reg_f3d interface"""
diff --git a/nipype/interfaces/niftyreg/tests/test_regutils.py b/nipype/interfaces/niftyreg/tests/test_regutils.py
index b2e1357811..4ad7973683 100644
--- a/nipype/interfaces/niftyreg/tests/test_regutils.py
+++ b/nipype/interfaces/niftyreg/tests/test_regutils.py
@@ -2,7 +2,7 @@
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
-from nipype.interfaces.niftyreg import (no_niftyreg, get_custom_path,
+from nipype.interfaces.niftyreg import (no_nifty_package, get_custom_path,
RegAverage, RegResample, RegJacobian,
RegTools, RegMeasure, RegTransform)
from nipype.testing import example_data
@@ -11,7 +11,7 @@
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_resample'),
+ no_nifty_package(cmd='reg_resample'),
reason="niftyreg is not installed. reg_resample not found.")
def test_reg_resample_res():
""" tests for reg_resample interface """
@@ -68,7 +68,7 @@ def test_reg_resample_res():
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_jacobian'),
+ no_nifty_package(cmd='reg_jacobian'),
reason="niftyreg is not installed. reg_jacobian not found.")
def test_reg_jacobian_jac():
""" Test interface for RegJacobian """
@@ -132,7 +132,7 @@ def test_reg_jacobian_jac():
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_tools'),
+ no_nifty_package(cmd='reg_tools'),
reason="niftyreg is not installed. reg_tools not found.")
def test_reg_tools_mul():
""" tests for reg_tools interface """
@@ -175,7 +175,7 @@ def test_reg_tools_mul():
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_average'),
+ no_nifty_package(cmd='reg_average'),
reason="niftyreg is not installed. reg_average not found.")
def test_reg_average():
""" tests for reg_average interface """
@@ -318,7 +318,7 @@ def test_reg_average():
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_transform'),
+ no_nifty_package(cmd='reg_transform'),
reason="niftyreg is not installed. reg_transform not found.")
def test_reg_transform_def():
""" tests for reg_transform interface """
@@ -427,7 +427,7 @@ def test_reg_transform_def():
@pytest.mark.skipif(
- no_niftyreg(cmd='reg_measure'),
+ no_nifty_package(cmd='reg_measure'),
reason="niftyreg is not installed. reg_measure not found.")
def test_reg_measure():
""" tests for reg_measure interface """
diff --git a/nipype/interfaces/niftyseg/__init__.py b/nipype/interfaces/niftyseg/__init__.py
new file mode 100644
index 0000000000..0cbb101aba
--- /dev/null
+++ b/nipype/interfaces/niftyseg/__init__.py
@@ -0,0 +1,17 @@
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:
+
+"""
+The niftyseg module provides classes for interfacing with the `NIFTYSEG
+`_ command line tools.
+
+Top-level namespace for niftyseg.
+"""
+
+from .em import EM
+from .label_fusion import LabelFusion, CalcTopNCC
+from .lesions import FillLesions
+from .maths import (UnaryMaths, BinaryMaths, BinaryMathsInteger, TupleMaths,
+ Merge)
+from .patchmatch import PatchMatch
+from .stats import UnaryStats, BinaryStats
diff --git a/nipype/interfaces/niftyseg/base.py b/nipype/interfaces/niftyseg/base.py
new file mode 100644
index 0000000000..80376a57c8
--- /dev/null
+++ b/nipype/interfaces/niftyseg/base.py
@@ -0,0 +1,44 @@
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:
+
+"""
+The niftyseg module provides classes for interfacing with `niftyseg
+`_ command line tools.
+These are the base tools for working with niftyseg.
+EM Statistical Segmentation tool is found in niftyseg/em.py
+Fill lesions tool is found in niftyseg/lesions.py
+Mathematical operation tool is found in niftyseg/maths.py
+Patch Match tool is found in niftyseg/patchmatch.py
+Statistical operation tool is found in niftyseg/stats.py
+Label Fusion and CalcTopNcc tools are in niftyseg/steps.py
+Examples
+--------
+See the docstrings of the individual classes for examples.
+"""
+
+from nipype.interfaces.niftyreg.base import NiftyRegCommand, no_nifty_package
+import subprocess
+import warnings
+
+
+warn = warnings.warn
+warnings.filterwarnings('always', category=UserWarning)
+
+
+class NiftySegCommand(NiftyRegCommand):
+ """
+ Base support interface for NiftySeg commands.
+ """
+ _suffix = '_ns'
+ _min_version = None
+
+ def __init__(self, **inputs):
+ super(NiftySegCommand, self).__init__(**inputs)
+
+ def get_version(self):
+ if no_nifty_package(cmd=self.cmd):
+ return None
+ # exec_cmd = ''.join((self.cmd, ' --version'))
+ exec_cmd = 'seg_EM --version'
+ # Using seg_EM for version (E.G: seg_stats --version doesn't work)
+ return subprocess.check_output(exec_cmd, shell=True).strip('\n')
diff --git a/nipype/interfaces/niftyseg/em.py b/nipype/interfaces/niftyseg/em.py
new file mode 100644
index 0000000000..102ee4d111
--- /dev/null
+++ b/nipype/interfaces/niftyseg/em.py
@@ -0,0 +1,146 @@
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:
+
+"""
+Nipype interface for seg_EM.
+
+The em module provides higher-level interfaces to some of the operations
+that can be performed with the seg_em command-line program.
+
+Examples
+--------
+See the docstrings of the individual classes for examples.
+
+Change directory to provide relative paths for doctests
+ >>> import os
+ >>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
+ >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data'))
+ >>> os.chdir(datadir)
+"""
+
+from ..base import (TraitedSpec, File, traits, CommandLineInputSpec,
+ InputMultiPath)
+from .base import NiftySegCommand
+from ..niftyreg.base import get_custom_path
+
+
+class EMInputSpec(CommandLineInputSpec):
+ """Input Spec for EM."""
+ in_file = File(argstr='-in %s',
+ exists=True,
+ mandatory=True,
+ desc='Input image to segment',
+ position=4)
+
+ mask_file = File(argstr='-mask %s',
+ exists=True,
+ desc='Filename of the ROI for label fusion')
+
+ # Priors
+ no_prior = traits.Int(argstr='-nopriors %s',
+ mandatory=True,
+ desc='Number of classes to use without prior',
+ xor=['prior_4D', 'priors'])
+
+ prior_4D = File(argstr='-prior4D %s',
+ exists=True,
+ mandatory=True,
+ desc='4D file containing the priors',
+ xor=['no_prior', 'priors'])
+
+ priors = InputMultiPath(argstr='%s',
+ mandatory=True,
+ desc='List of priors filepaths.',
+ xor=['no_prior', 'prior_4D'])
+
+ # iterations
+ max_iter = traits.Int(argstr='-max_iter %s', default=100,
+ desc='Maximum number of iterations')
+
+ min_iter = traits.Int(argstr='-min_iter %s', default=0,
+ desc='Minimun number of iterations')
+
+ # other options
+ bc_order_val = traits.Int(argstr='-bc_order %s', default=3,
+ desc='Polynomial order for the bias field')
+
+ mrf_beta_val = traits.Float(argstr='-mrf_beta %s',
+ desc='Weight of the Markov Random Field')
+
+ desc = 'Bias field correction will run only if the ratio of improvement \
+is below bc_thresh. (default=0 [OFF])'
+ bc_thresh_val = traits.Float(argstr='-bc_thresh %s', default=0, desc=desc)
+
+ desc = 'Amount of regularization over the diagonal of the covariance \
+matrix [above 1]'
+ reg_val = traits.Float(argstr='-reg %s', desc=desc)
+
+ desc = 'Outlier detection as in (Van Leemput TMI 2003). is the \
+Mahalanobis threshold [recommended between 3 and 7] is a convergence \
+ratio below which the outlier detection is going to be done [recommended 0.01]'
+ outlier_val = traits.Tuple(traits.Float(), traits.Float(),
+ argstr='-outlier %s %s',
+ desc=desc)
+
+ desc = 'Relax Priors [relaxation factor: 00 (recommended=2.0)] /only 3D/'
+ relax_priors = traits.Tuple(traits.Float(), traits.Float(),
+ argstr='-rf %s %s',
+ desc=desc)
+
+ # outputs
+ out_file = File(name_source=['in_file'],
+ name_template='%s_em.nii.gz',
+ argstr='-out %s',
+ desc='Output segmentation')
+ 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(name_source=['in_file'],
+ name_template='%s_outlier_em.nii.gz',
+ argstr='-out_outlier %s',
+ desc='Output outlierness image')
+
+
+class EMOutputSpec(TraitedSpec):
+ """Output Spec for EM."""
+ out_file = File(desc="Output segmentation")
+ out_bc_file = File(desc="Output bias corrected image")
+ out_outlier_file = File(desc='Output outlierness image')
+
+
+class EM(NiftySegCommand):
+ """Interface for executable seg_EM from NiftySeg platform.
+
+ seg_EM is a general purpose intensity based image segmentation tool. In
+ it's simplest form, it takes in one 2D or 3D image and segments it in n
+ classes.
+
+ For source code, see http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg
+ For Documentation, see:
+ http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg_documentation
+
+ Examples
+ --------
+ >>> from nipype.interfaces import niftyseg
+ >>> node = niftyseg.EM()
+ >>> node.inputs.in_file = 'im1.nii'
+ >>> node.inputs.no_prior = 4
+ >>> 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', env_dir='NIFTYSEGDIR')
+ _suffix = '_em'
+ input_spec = EMInputSpec
+ output_spec = EMOutputSpec
+
+ def _format_arg(self, opt, spec, val):
+ """Convert input to appropriate format for seg_EM."""
+ if opt == 'priors':
+ _nb_priors = len(self.inputs.priors)
+ return '-priors %d %s' % (_nb_priors, ' '.join(self.inputs.priors))
+ else:
+ return super(EM, self)._format_arg(opt, spec, val)
diff --git a/nipype/interfaces/niftyseg/label_fusion.py b/nipype/interfaces/niftyseg/label_fusion.py
new file mode 100644
index 0000000000..e3173d96bd
--- /dev/null
+++ b/nipype/interfaces/niftyseg/label_fusion.py
@@ -0,0 +1,334 @@
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:
+
+"""
+The fusion module provides higher-level interfaces to some of the operations
+that can be performed with the seg_LabFusion command-line program.
+
+Change directory to provide relative paths for doctests
+ >>> import os
+ >>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
+ >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data'))
+ >>> os.chdir(datadir)
+"""
+
+from builtins import str
+import os
+import warnings
+
+from ..base import (TraitedSpec, File, traits, isdefined, CommandLineInputSpec,
+ NipypeInterfaceError)
+from .base import NiftySegCommand
+from ..niftyreg.base import get_custom_path
+from ...utils.filemanip import load_json, save_json, split_filename
+
+
+warn = warnings.warn
+warnings.filterwarnings('always', category=UserWarning)
+
+
+class LabelFusionInput(CommandLineInputSpec):
+ """Input Spec for LabelFusion."""
+ in_file = File(argstr='-in %s',
+ exists=True,
+ mandatory=True,
+ position=1,
+ desc='Filename of the 4D integer label image.')
+
+ template_file = File(exists=True,
+ desc='Registered templates (4D Image)')
+
+ file_to_seg = File(exists=True,
+ mandatory=True,
+ desc='Original image to segment (3D Image)')
+
+ mask_file = File(argstr='-mask %s',
+ exists=True,
+ desc='Filename of the ROI for label fusion')
+
+ out_file = File(argstr='-out %s',
+ name_source=['in_file'],
+ name_template='%s',
+ desc='Output consensus segmentation')
+
+ prob_flag = traits.Bool(desc='Probabilistic/Fuzzy segmented image',
+ argstr='-outProb')
+
+ desc = 'Verbose level [0 = off, 1 = on, 2 = debug] (default = 0)'
+ verbose = traits.Enum('0', '1', '2',
+ desc=desc,
+ argstr='-v %s')
+
+ desc = 'Only consider non-consensus voxels to calculate statistics'
+ unc = traits.Bool(desc=desc, argstr='-unc')
+
+ classifier_type = traits.Enum('STEPS', 'STAPLE', 'MV', 'SBA',
+ argstr='-%s',
+ mandatory=True,
+ position=2,
+ desc='Type of Classifier Fusion.')
+
+ desc = "Gaussian kernel size in mm to compute the local similarity"
+ kernel_size = traits.Float(desc=desc)
+
+ template_num = traits.Int(desc='Number of labels to use')
+
+ # STAPLE and MV options
+ sm_ranking = traits.Enum('ALL', 'GNCC', 'ROINCC', 'LNCC',
+ argstr='-%s',
+ usedefault=True,
+ position=3,
+ desc='Ranking for STAPLE and MV')
+
+ dilation_roi = traits.Int(desc='Dilation of the ROI ( d>=1 )')
+
+ # STAPLE and STEPS options
+ desc = 'Proportion of the label (only for single labels).'
+ proportion = traits.Float(argstr='-prop %s', desc=desc)
+
+ desc = 'Update label proportions at each iteration'
+ prob_update_flag = traits.Bool(desc=desc, argstr='-prop_update')
+
+ desc = 'Value of P and Q [ 0 < (P,Q) < 1 ] (default = 0.99 0.99)'
+ set_pq = traits.Tuple(traits.Float, traits.Float,
+ argstr='-setPQ %f %f',
+ desc=desc)
+
+ mrf_value = traits.Float(argstr='-MRF_beta %f',
+ desc='MRF prior strength (between 0 and 5)')
+
+ desc = 'Maximum number of iterations (default = 15).'
+ max_iter = traits.Int(argstr='-max_iter %d', desc=desc)
+
+ desc = 'If percent of labels agree, then area is not uncertain.'
+ unc_thresh = traits.Float(argstr='-uncthres %f', desc=desc)
+
+ desc = 'Ratio for convergence (default epsilon = 10^-5).'
+ conv = traits.Float(argstr='-conv %f', desc=desc)
+
+
+class LabelFusionOutput(TraitedSpec):
+ """Output Spec for LabelFusion."""
+ out_file = File(exists=True, desc='image written after calculations')
+
+
+class LabelFusion(NiftySegCommand):
+ """Interface for executable seg_LabelFusion from NiftySeg platform using
+ type STEPS as classifier Fusion.
+
+ This executable implements 4 fusion strategies (-STEPS, -STAPLE, -MV or
+ - SBA), all of them using either a global (-GNCC), ROI-based (-ROINCC),
+ local (-LNCC) or no image similarity (-ALL). Combinations of fusion
+ algorithms and similarity metrics give rise to different variants of known
+ algorithms. As an example, using LNCC and MV as options will run a locally
+ weighted voting strategy with LNCC derived weights, while using STAPLE and
+ LNCC is equivalent to running STEPS as per its original formulation.
+ A few other options pertaining the use of an MRF (-MRF beta), the initial
+ sensitivity and specificity estimates and the use of only non-consensus
+ voxels (-unc) for the STAPLE and STEPS algorithm. All processing can be
+ masked (-mask), greatly reducing memory consumption.
+
+ As an example, the command to use STEPS should be:
+ seg_LabFusion -in 4D_Propragated_Labels_to_fuse.nii -out \
+ FusedSegmentation.nii -STEPS 2 15 TargetImage.nii \
+ 4D_Propagated_Intensities.nii
+
+ For source code, see http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg
+ For Documentation, see:
+ http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg_documentation
+
+ Examples
+ --------
+ >>> from nipype.interfaces import niftyseg
+ >>> node = niftyseg.LabelFusion()
+ >>> node.inputs.in_file = 'im1.nii'
+ >>> node.inputs.kernel_size = 2.0
+ >>> node.inputs.file_to_seg = 'im2.nii'
+ >>> node.inputs.template_file = 'im3.nii'
+ >>> node.inputs.template_num = 2
+ >>> node.inputs.classifier_type = 'STEPS'
+ >>> node.cmdline # doctest: +ALLOW_UNICODE
+ 'seg_LabFusion -in im1.nii -STEPS 2.000000 2 im2.nii im3.nii -out \
+im1_steps.nii'
+
+ """
+ _cmd = get_custom_path('seg_LabFusion', env_dir='NIFTYSEGDIR')
+ input_spec = LabelFusionInput
+ output_spec = LabelFusionOutput
+ _suffix = '_label_fused'
+
+ def _format_arg(self, opt, spec, val):
+ """Convert input to appropriate format for seg_maths."""
+ # Remove options if not STAPLE or STEPS as fusion type:
+ if opt in ['proportion', 'prob_update_flag', 'set_pq', 'mrf_value',
+ 'max_iter', 'unc_thresh', 'conv'] and\
+ self.inputs.classifier_type not in ['STAPLE', 'STEPS']:
+ return ''
+
+ if opt == 'sm_ranking':
+ return self.get_staple_args(val)
+
+ # Return options string if STEPS:
+ if opt == 'classifier_type' and val == 'STEPS':
+ return self.get_steps_args()
+
+ return super(LabelFusion, self)._format_arg(opt, spec, val)
+
+ def get_steps_args(self):
+ if not isdefined(self.inputs.template_file):
+ err = "LabelFusion requires a value for input 'template_file' \
+when 'classifier_type' is set to 'STEPS'."
+ raise NipypeInterfaceError(err)
+ if not isdefined(self.inputs.kernel_size):
+ err = "LabelFusion requires a value for input 'kernel_size' when \
+'classifier_type' is set to 'STEPS'."
+ raise NipypeInterfaceError(err)
+ if not isdefined(self.inputs.template_num):
+ err = "LabelFusion requires a value for input 'template_num' when \
+'classifier_type' is set to 'STEPS'."
+ raise NipypeInterfaceError(err)
+ return "-STEPS %f %d %s %s" % (self.inputs.kernel_size,
+ self.inputs.template_num,
+ self.inputs.file_to_seg,
+ self.inputs.template_file)
+
+ def get_staple_args(self, ranking):
+ classtype = self.inputs.classifier_type
+ if classtype not in ['STAPLE', 'MV']:
+ return None
+
+ if ranking == 'ALL':
+ return '-ALL'
+
+ if not isdefined(self.inputs.template_file):
+ err = "LabelFusion requires a value for input 'tramplate_file' \
+when 'classifier_type' is set to '%s' and 'sm_ranking' is set to '%s'."
+ raise NipypeInterfaceError(err % (classtype, ranking))
+ if not isdefined(self.inputs.template_num):
+ err = "LabelFusion requires a value for input 'template-num' when \
+'classifier_type' is set to '%s' and 'sm_ranking' is set to '%s'."
+ raise NipypeInterfaceError(err % (classtype, ranking))
+
+ if ranking == 'GNCC':
+ if not isdefined(self.inputs.template_num):
+ err = "LabelFusion requires a value for input 'template_num' \
+when 'classifier_type' is set to '%s' and 'sm_ranking' is set to '%s'."
+ raise NipypeInterfaceError(err % (classtype, ranking))
+
+ return "-%s %d %s %s" % (ranking,
+ self.inputs.template_num,
+ self.inputs.file_to_seg,
+ self.inputs.template_file)
+
+ elif ranking == 'ROINCC':
+ if not isdefined(self.inputs.dilation_roi):
+ err = "LabelFusion requires a value for input 'dilation_roi' \
+when 'classifier_type' is set to '%s' and 'sm_ranking' is set to '%s'."
+ raise NipypeInterfaceError(err % (classtype, ranking))
+
+ elif self.inputs.dilation_roi < 1:
+ err = "The 'dilation_roi' trait of a LabelFusionInput \
+instance must be an integer >= 1, but a value of '%s' was specified."
+ raise NipypeInterfaceError(err % self.inputs.dilation_roi)
+
+ return "-%s %d %d %s %s" % (ranking,
+ self.inputs.dilation_roi,
+ self.inputs.template_num,
+ self.inputs.file_to_seg,
+ self.inputs.template_file)
+ elif ranking == 'LNCC':
+ if not isdefined(self.inputs.kernel_size):
+ err = "LabelFusion requires a value for input 'kernel_size' \
+when 'classifier_type' is set to '%s' and 'sm_ranking' is set to '%s'."
+ raise NipypeInterfaceError(err % (classtype, ranking))
+
+ return "-%s %f %d %s %s" % (ranking,
+ self.inputs.kernel_size,
+ self.inputs.template_num,
+ self.inputs.file_to_seg,
+ self.inputs.template_file)
+
+ def _overload_extension(self, value, name=None):
+ path, base, _ = split_filename(value)
+ _, _, ext = split_filename(self.inputs.in_file)
+ suffix = self.inputs.classifier_type.lower()
+ return os.path.join(path, '{0}_{1}{2}'.format(base, suffix, ext))
+
+
+class CalcTopNCCInputSpec(CommandLineInputSpec):
+ """Input Spec for CalcTopNCC."""
+ in_file = File(argstr='-target %s',
+ exists=True,
+ mandatory=True,
+ desc='Target file',
+ position=1)
+
+ num_templates = traits.Int(argstr='-templates %s',
+ mandatory=True,
+ position=2,
+ desc='Number of Templates')
+
+ in_templates = traits.List(File(exists=True),
+ argstr="%s",
+ position=3,
+ mandatory=True)
+
+ top_templates = traits.Int(argstr='-n %s',
+ mandatory=True,
+ position=4,
+ desc='Number of Top Templates')
+
+ mask_file = File(argstr='-mask %s',
+ exists=True,
+ mandatory=False,
+ desc='Filename of the ROI for label fusion')
+
+
+class CalcTopNCCOutputSpec(TraitedSpec):
+ """Output Spec for CalcTopNCC."""
+ out_files = traits.Any(File(exists=True))
+
+
+class CalcTopNCC(NiftySegCommand):
+ """Interface for executable seg_CalcTopNCC from NiftySeg platform.
+
+ Examples
+ --------
+ >>> from nipype.interfaces import niftyseg
+ >>> node = niftyseg.CalcTopNCC()
+ >>> node.inputs.in_file = 'im1.nii'
+ >>> node.inputs.num_templates = 2
+ >>> node.inputs.in_templates = ['im2.nii', 'im3.nii']
+ >>> node.inputs.top_templates = 1
+ >>> node.cmdline # doctest: +ALLOW_UNICODE
+ 'seg_CalcTopNCC -target im1.nii -templates 2 im2.nii im3.nii -n 1'
+
+ """
+ _cmd = get_custom_path('seg_CalcTopNCC', env_dir='NIFTYSEGDIR')
+ _suffix = '_topNCC'
+ input_spec = CalcTopNCCInputSpec
+ output_spec = CalcTopNCCOutputSpec
+
+ def aggregate_outputs(self, runtime=None, needed_outputs=None):
+ outputs = self._outputs()
+ # local caching for backward compatibility
+ outfile = os.path.join(os.getcwd(), 'CalcTopNCC.json')
+ if runtime is None or not runtime.stdout:
+ try:
+ out_files = load_json(outfile)['files']
+ except IOError:
+ return self.run().outputs
+ else:
+ out_files = []
+ for line in runtime.stdout.split('\n'):
+ if line:
+ values = line.split()
+ if len(values) > 1:
+ out_files.append([str(val) for val in values])
+ else:
+ out_files.extend([str(val) for val in values])
+ if len(out_files) == 1:
+ out_files = out_files[0]
+ save_json(outfile, dict(files=out_files))
+ outputs.out_files = out_files
+ return outputs
diff --git a/nipype/interfaces/niftyseg/lesions.py b/nipype/interfaces/niftyseg/lesions.py
new file mode 100644
index 0000000000..69d616181b
--- /dev/null
+++ b/nipype/interfaces/niftyseg/lesions.py
@@ -0,0 +1,118 @@
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:
+
+"""
+Nipype interface for seg_FillLesions.
+
+The fusion module provides higher-level interfaces to some of the operations
+that can be performed with the seg_FillLesions command-line program.
+
+Examples
+--------
+See the docstrings of the individual classes for examples.
+
+Change directory to provide relative paths for doctests
+ >>> import os
+ >>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
+ >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data'))
+ >>> os.chdir(datadir)
+"""
+
+import warnings
+
+from ..base import TraitedSpec, File, traits, CommandLineInputSpec
+from .base import NiftySegCommand
+from ..niftyreg.base import get_custom_path
+
+
+warn = warnings.warn
+warnings.filterwarnings('always', category=UserWarning)
+
+
+class FillLesionsInputSpec(CommandLineInputSpec):
+ """Input Spec for FillLesions."""
+ # Mandatory input arguments
+ in_file = File(argstr='-i %s', exists=True, mandatory=True,
+ desc='Input image to fill lesions', position=1)
+
+ lesion_mask = File(argstr='-l %s', exists=True, mandatory=True,
+ desc='Lesion mask', position=2)
+
+ # Output file name
+ 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
+ desc = "Dilate the mask times (in voxels, by default 0)"
+ in_dilation = traits.Int(desc=desc, argstr='-dil %d')
+
+ desc = 'Percentage of minimum number of voxels between patches \
+(by default 0.5).'
+ match = traits.Float(desc=desc, argstr='-match %f')
+
+ desc = 'Minimum percentage of valid voxels in target patch \
+(by default 0).'
+ search = traits.Float(desc=desc, argstr='-search %f')
+
+ desc = 'Smoothing by (in minimal 6-neighbourhood voxels \
+(by default 0.1)).'
+ smooth = traits.Float(desc=desc, argstr='-smo %f')
+
+ desc = 'Search regions size respect biggest patch size (by default 4).'
+ size = traits.Int(desc=desc, argstr='-size %d')
+
+ desc = 'Patch cardinality weighting factor (by default 2).'
+ cwf = traits.Float(desc=desc, argstr='-cwf %f')
+
+ desc = 'Give a binary mask with the valid search areas.'
+ bin_mask = File(desc=desc, argstr='-mask %s')
+
+ desc = "Guizard et al. (FIN 2015) method, it doesn't include the \
+multiresolution/hierarchical inpainting part, this part needs to be done \
+with some external software such as reg_tools and reg_resample from NiftyReg. \
+By default it uses the method presented in Prados et al. (Neuroimage 2016)."
+ other = traits.Bool(desc=desc, argstr='-other')
+
+ use_2d = traits.Bool(desc='Uses 2D patches in the Z axis, by default 3D.',
+ argstr='-2D')
+
+ debug = traits.Bool(desc='Save all intermidium files (by default OFF).',
+ argstr='-debug')
+
+ desc = 'Set output (char, short, int, uchar, ushort, uint, \
+float, double).'
+ out_datatype = traits.String(desc=desc, argstr='-odt %s')
+
+ verbose = traits.Bool(desc='Verbose (by default OFF).',
+ argstr='-v')
+
+
+class FillLesionsOutputSpec(TraitedSpec):
+ """Output Spec for FillLesions."""
+ out_file = File(desc="Output segmentation")
+
+
+class FillLesions(NiftySegCommand):
+ """Interface for executable seg_FillLesions from NiftySeg platform.
+
+ Fill all the masked lesions with WM intensity average.
+
+ For source code, see http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg
+ For Documentation, see:
+ http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg_documentation
+
+ Examples
+ --------
+ >>> from nipype.interfaces import niftyseg
+ >>> node = niftyseg.FillLesions()
+ >>> node.inputs.in_file = 'im1.nii'
+ >>> node.inputs.lesion_mask = 'im2.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', env_dir='NIFTYSEGDIR')
+ input_spec = FillLesionsInputSpec
+ output_spec = FillLesionsOutputSpec
diff --git a/nipype/interfaces/niftyseg/maths.py b/nipype/interfaces/niftyseg/maths.py
new file mode 100644
index 0000000000..852fe699ce
--- /dev/null
+++ b/nipype/interfaces/niftyseg/maths.py
@@ -0,0 +1,537 @@
+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
+# vi: set ft=python sts=4 ts=4 sw=4 et:
+
+"""
+Nipype interface for seg_maths.
+
+The maths module provides higher-level interfaces to some of the operations
+that can be performed with the niftysegmaths (seg_maths) command-line program.
+
+Examples
+--------
+See the docstrings of the individual classes for examples.
+
+Change directory to provide relative paths for doctests
+ >>> import os
+ >>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
+ >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data'))
+ >>> os.chdir(datadir)
+"""
+
+import os
+
+from ..base import (TraitedSpec, File, traits, isdefined, CommandLineInputSpec,
+ NipypeInterfaceError)
+from .base import NiftySegCommand
+from ..niftyreg.base import get_custom_path
+from ...utils.filemanip import split_filename
+
+
+class MathsInput(CommandLineInputSpec):
+ """Input Spec for seg_maths interfaces."""
+ in_file = File(position=2,
+ argstr='%s',
+ exists=True,
+ mandatory=True,
+ desc='image to operate on')
+
+ out_file = File(name_source=['in_file'],
+ name_template='%s',
+ position=-2,
+ argstr='%s',
+ desc='image to write')
+
+ desc = 'datatype to use for output (default uses input type)'
+ output_datatype = traits.Enum('float', 'char', 'int', 'short',
+ 'double', 'input',
+ position=-3,
+ argstr='-odt %s',
+ desc=desc)
+
+
+class MathsOutput(TraitedSpec):
+ """Output Spec for seg_maths interfaces."""
+ out_file = File(desc='image written after calculations')
+
+
+class MathsCommand(NiftySegCommand):
+ """
+ Base Command Interface for seg_maths interfaces.
+
+ The executable seg_maths enables the sequential execution of arithmetic
+ operations, like multiplication (-mul), division (-div) or addition
+ (-add), binarisation (-bin) or thresholding (-thr) operations and
+ convolution by a Gaussian kernel (-smo). It also alows mathematical
+ morphology based operations like dilation (-dil), erosion (-ero),
+ connected components (-lconcomp) and hole filling (-fill), Euclidean
+ (- euc) and geodesic (-geo) distance transforms, local image similarity
+ metric calculation (-lncc and -lssd). Finally, it allows multiple
+ operations over the dimensionality of the image, from merging 3D images
+ together as a 4D image (-merge) or splitting (-split or -tp) 4D images
+ into several 3D images, to estimating the maximum, minimum and average
+ over all time-points, etc.
+ """
+ _cmd = get_custom_path('seg_maths', env_dir='NIFTYSEGDIR')
+ input_spec = MathsInput
+ output_spec = MathsOutput
+ _suffix = '_maths'
+
+ def _overload_extension(self, value, name=None):
+ path, base, _ = split_filename(value)
+ _, _, ext = split_filename(self.inputs.in_file)
+
+ suffix = self._suffix
+ if suffix != '_merged' and isdefined(self.inputs.operation):
+ suffix = '_' + self.inputs.operation
+
+ return os.path.join(path, '{0}{1}{2}'.format(base, suffix, ext))
+
+
+class UnaryMathsInput(MathsInput):
+ """Input Spec for seg_maths Unary operations."""
+ operation = traits.Enum('sqrt', 'exp', 'log', 'recip', 'abs', 'bin',
+ 'otsu', 'lconcomp', 'concomp6', 'concomp26',
+ 'fill', 'euc', 'tpmax', 'tmean', 'tmax', 'tmin',
+ 'splitlab', 'removenan', 'isnan', 'subsamp2',
+ 'scl', '4to5', 'range',
+ argstr='-%s', position=4, mandatory=True,
+ desc='operation to perform')
+
+
+class UnaryMaths(MathsCommand):
+ """Interface for executable seg_maths from NiftySeg platform.
+
+ Interface to use any unary mathematical operations that can be performed
+ with the seg_maths command-line program. See below for those operations:
+ -sqrt Square root of the image.
+ -exp Exponential root of the image.
+ -log Log of the image.
+ -recip Reciprocal (1/I) of the image.
+ -abs Absolute value of the image.
+ -bin Binarise the image.
+ -otsu Otsu thresholding of the current image.
+ -lconcomp Take the largest connected component
+ -concomp6 Label the different connected components with a 6NN
+ kernel
+ -concomp26 Label the different connected components with a 26NN
+ kernel
+ -fill Fill holes in binary object (e.g. fill ventricle in
+ brain mask).
+ -euc Euclidean distance trasnform
+ -tpmax Get the time point with the highest value (binarise 4D
+ probabilities)
+ -tmean Mean value of all time points.
+ -tmax Max value of all time points.
+ -tmin Mean value of all time points.
+ -splitlab Split the integer labels into multiple timepoints
+ -removenan Remove all NaNs and replace then with 0
+ -isnan Binary image equal to 1 if the value is NaN and 0
+ otherwise
+ -subsamp2 Subsample the image by 2 using NN sampling (qform and
+ sform scaled)
+ -scl Reset scale and slope info.
+ -4to5 Flip the 4th and 5th dimension.
+ -range Reset the image range to the min max
+
+ For source code, see http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg
+ For Documentation, see:
+ http://cmictig.cs.ucl.ac.uk/wiki/index.php/NiftySeg_documentation
+
+ Examples
+ --------
+ >>> import copy
+ >>> from nipype.interfaces import niftyseg
+ >>> unary = niftyseg.UnaryMaths()
+ >>> unary.inputs.output_datatype = 'float'
+ >>> unary.inputs.in_file = 'im1.nii'
+ >>> # Test sqrt operation
+ >>> unary_sqrt = copy.deepcopy(unary)
+ >>> unary_sqrt.inputs.operation = 'sqrt'
+ >>> unary_sqrt.cmdline # doctest: +ALLOW_UNICODE
+ 'seg_maths im1.nii -sqrt -odt float im1_sqrt.nii'
+ >>> unary_sqrt.run() # doctest: +SKIP
+ >>> # Test sqrt operation
+ >>> unary_abs = copy.deepcopy(unary)
+ >>> unary_abs.inputs.operation = 'abs'
+ >>> unary_abs.cmdline # doctest: +ALLOW_UNICODE
+ 'seg_maths im1.nii -abs -odt float im1_abs.nii'
+ >>> unary_abs.run() # doctest: +SKIP
+ >>> # Test bin operation
+ >>> unary_bin = copy.deepcopy(unary)
+ >>> unary_bin.inputs.operation = 'bin'
+ >>> unary_bin.cmdline # doctest: +ALLOW_UNICODE
+ 'seg_maths im1.nii -bin -odt float im1_bin.nii'
+ >>> unary_bin.run() # doctest: +SKIP
+ >>> # Test otsu operation
+ >>> unary_otsu = copy.deepcopy(unary)
+ >>> unary_otsu.inputs.operation = 'otsu'
+ >>> unary_otsu.cmdline # doctest: +ALLOW_UNICODE
+ 'seg_maths im1.nii -otsu -odt float im1_otsu.nii'
+ >>> unary_otsu.run() # doctest: +SKIP
+ >>> # Test isnan operation
+ >>> unary_isnan = copy.deepcopy(unary)
+ >>> unary_isnan.inputs.operation = 'isnan'
+ >>> unary_isnan.cmdline # doctest: +ALLOW_UNICODE
+ 'seg_maths im1.nii -isnan -odt float im1_isnan.nii'
+ >>> unary_isnan.run() # doctest: +SKIP
+
+ """
+ input_spec = UnaryMathsInput
+
+
+class BinaryMathsInput(MathsInput):
+ """Input Spec for seg_maths Binary operations."""
+ operation = traits.Enum('mul', 'div', 'add', 'sub', 'pow', 'thr', 'uthr',
+ 'smo', 'edge', 'sobel3', 'sobel5', 'min', 'smol',
+ 'geo', 'llsnorm', 'masknan', 'hdr_copy',
+ 'splitinter',
+ mandatory=True,
+ argstr='-%s',
+ position=4,
+ desc='operation to perform')
+
+ operand_file = File(exists=True,
+ argstr='%s',
+ mandatory=True,
+ position=5,
+ xor=['operand_value', 'operand_str'],
+ desc='second image to perform operation with')
+
+ operand_value = traits.Float(argstr='%.8f',
+ mandatory=True,
+ position=5,
+ xor=['operand_file', 'operand_str'],
+ desc='float value to perform operation with')
+
+ desc = 'string value to perform operation splitinter'
+ operand_str = traits.Enum('x', 'y', 'z',
+ argstr='%s',
+ mandatory=True,
+ position=5,
+ xor=['operand_value', 'operand_file'],
+ desc=desc)
+
+
+class BinaryMaths(MathsCommand):
+ """Interface for executable seg_maths from NiftySeg platform.
+
+ Interface to use any binary mathematical operations that can be performed
+ with the seg_maths command-line program. See below for those operations:
+ -mul Multiply image value or by other image.
+ -div Divide image by or by other image.
+ -add Add image by or by other image.
+ -sub Subtract image by or by other image.
+ -pow Image to the power of .
+ -thr Threshold the image below .
+ -uthr Threshold image above .
+ -smo Gaussian smoothing by std (in voxels
+ and up to 4-D).
+ -edge Calculate the edges of the image using a
+ threshold .
+ -sobel3 Calculate the edges of all timepoints using a
+ Sobel filter with a 3x3x3 kernel and applying
+ gaussian smoothing.
+ -sobel5 Calculate the edges of all timepoints using a
+ Sobel filter with a 5x5x5 kernel and applying
+ gaussian smoothing.
+ -min Get the min per voxel between and
+ .
+ -smol Gaussian smoothing of a 3D label image.
+ -geo Geodesic distance according to the speed
+ function
+ -llsnorm Linear LS normalisation between current and
+
+ -masknan Assign everything outside the mask (mask==0)
+ with NaNs
+ -hdr_copy Copy header from working image to and
+ save in