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 . + -splitinter Split interleaved slices in direction + into separate time points + + 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 + >>> binary = niftyseg.BinaryMaths() + >>> binary.inputs.in_file = 'im1.nii' + >>> binary.inputs.output_datatype = 'float' + >>> # Test sub operation + >>> binary_sub = copy.deepcopy(binary) + >>> binary_sub.inputs.operation = 'sub' + >>> binary_sub.inputs.operand_file = 'im2.nii' + >>> binary_sub.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -sub im2.nii -odt float im1_sub.nii' + >>> binary_sub.run() # doctest: +SKIP + >>> # Test mul operation + >>> binary_mul = copy.deepcopy(binary) + >>> binary_mul.inputs.operation = 'mul' + >>> binary_mul.inputs.operand_value = 2.0 + >>> binary_mul.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -mul 2.00000000 -odt float im1_mul.nii' + >>> binary_mul.run() # doctest: +SKIP + >>> # Test llsnorm operation + >>> binary_llsnorm = copy.deepcopy(binary) + >>> binary_llsnorm.inputs.operation = 'llsnorm' + >>> binary_llsnorm.inputs.operand_file = 'im2.nii' + >>> binary_llsnorm.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -llsnorm im2.nii -odt float im1_llsnorm.nii' + >>> binary_llsnorm.run() # doctest: +SKIP + >>> # Test splitinter operation + >>> binary_splitinter = copy.deepcopy(binary) + >>> binary_splitinter.inputs.operation = 'splitinter' + >>> binary_splitinter.inputs.operand_str = 'z' + >>> binary_splitinter.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -splitinter z -odt float im1_splitinter.nii' + >>> binary_splitinter.run() # doctest: +SKIP + + """ + input_spec = BinaryMathsInput + + def _format_arg(self, opt, spec, val): + """Convert input to appropriate format for seg_maths.""" + if opt == 'operand_str' and self.inputs.operation != 'splitinter': + err = 'operand_str set but with an operation different than \ +"splitinter"' + raise NipypeInterfaceError(err) + + if opt == 'operation': + # Only float + if val in ['pow', 'thr', 'uthr', 'smo', 'edge', 'sobel3', 'sobel5', + 'smol']: + if not isdefined(self.inputs.operand_value): + err = 'operand_value not set for {0}.'.format(val) + raise NipypeInterfaceError(err) + # only files + elif val in ['min', 'llsnorm', 'masknan', 'hdr_copy']: + if not isdefined(self.inputs.operand_file): + err = 'operand_file not set for {0}.'.format(val) + raise NipypeInterfaceError(err) + # splitinter: + elif val == 'splitinter': + if not isdefined(self.inputs.operand_str): + err = 'operand_str not set for splitinter.' + raise NipypeInterfaceError(err) + + if opt == 'operand_value' and float(val) == 0.0: + return '0' + + return super(BinaryMaths, self)._format_arg(opt, spec, val) + + def _overload_extension(self, value, name=None): + path = super(BinaryMaths, self)._overload_extension(value, name) + if self.inputs.operation == 'hdr_copy': + _, base, ext = split_filename(self.inputs.operand_file) + suffix = self.inputs.operation + path = os.path.join(path, '{0}{1}{2}'.format(base, suffix, ext)) + return path + + +class BinaryMathsInputInteger(MathsInput): + """Input Spec for seg_maths Binary operations that require integer.""" + operation = traits.Enum('dil', 'ero', 'tp', 'equal', 'pad', 'crop', + mandatory=True, + argstr='-%s', + position=4, + desc='operation to perform') + + operand_value = traits.Int(argstr='%d', + mandatory=True, + position=5, + desc='int value to perform operation with') + + +class BinaryMathsInteger(MathsCommand): + """Interface for executable seg_maths from NiftySeg platform. + + Interface to use any integer mathematical operations that can be performed + with the seg_maths command-line program. See below for those operations: + (requiring integer values) + -equal Get voxels equal to + -dil Dilate the image times (in voxels). + -ero Erode the image times (in voxels). + -tp Extract time point + -crop Crop voxels around each 3D volume. + -pad Pad voxels with NaN value around each 3D + volume. + + 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.niftyseg import BinaryMathsInteger + >>> binaryi = BinaryMathsInteger() + >>> binaryi.inputs.in_file = 'im1.nii' + >>> binaryi.inputs.output_datatype = 'float' + >>> # Test dil operation + >>> binaryi_dil = copy.deepcopy(binaryi) + >>> binaryi_dil.inputs.operation = 'dil' + >>> binaryi_dil.inputs.operand_value = 2 + >>> binaryi_dil.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -dil 2 -odt float im1_dil.nii' + >>> binaryi_dil.run() # doctest: +SKIP + >>> # Test dil operation + >>> binaryi_ero = copy.deepcopy(binaryi) + >>> binaryi_ero.inputs.operation = 'ero' + >>> binaryi_ero.inputs.operand_value = 1 + >>> binaryi_ero.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -ero 1 -odt float im1_ero.nii' + >>> binaryi_ero.run() # doctest: +SKIP + >>> # Test pad operation + >>> binaryi_pad = copy.deepcopy(binaryi) + >>> binaryi_pad.inputs.operation = 'pad' + >>> binaryi_pad.inputs.operand_value = 4 + >>> binaryi_pad.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -pad 4 -odt float im1_pad.nii' + >>> binaryi_pad.run() # doctest: +SKIP + + """ + input_spec = BinaryMathsInputInteger + + +class TupleMathsInput(MathsInput): + """Input Spec for seg_maths Tuple operations.""" + operation = traits.Enum('lncc', 'lssd', 'lltsnorm', + mandatory=True, + argstr='-%s', + position=4, + desc='operation to perform') + + operand_file1 = File(exists=True, + argstr='%s', + mandatory=True, + position=5, + xor=['operand_value1'], + desc='image to perform operation 1 with') + + desc = 'float value to perform operation 1 with' + operand_value1 = traits.Float(argstr='%.8f', + mandatory=True, + position=5, + xor=['operand_file1'], + desc=desc) + + operand_file2 = File(exists=True, + argstr='%s', + mandatory=True, + position=6, + xor=['operand_value2'], + desc='image to perform operation 2 with') + + desc = 'float value to perform operation 2 with' + operand_value2 = traits.Float(argstr='%.8f', + mandatory=True, + position=6, + xor=['operand_file2'], + desc=desc) + + +class TupleMaths(MathsCommand): + """Interface for executable seg_maths from NiftySeg platform. + + Interface to use any tuple mathematical operations that can be performed + with the seg_maths command-line program. See below for those operations: + -lncc Local CC between current img and + on a kernel with + -lssd Local SSD between current img and + on a kernel with + -lltsnorm Linear LTS normalisation assuming + percent outliers + + 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 + >>> tuple = niftyseg.TupleMaths() + >>> tuple.inputs.in_file = 'im1.nii' + >>> tuple.inputs.output_datatype = 'float' + + >>> # Test lncc operation + >>> tuple_lncc = copy.deepcopy(tuple) + >>> tuple_lncc.inputs.operation = 'lncc' + >>> tuple_lncc.inputs.operand_file1 = 'im2.nii' + >>> tuple_lncc.inputs.operand_value2 = 2.0 + >>> tuple_lncc.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -lncc im2.nii 2.00000000 -odt float im1_lncc.nii' + >>> tuple_lncc.run() # doctest: +SKIP + + >>> # Test lssd operation + >>> tuple_lssd = copy.deepcopy(tuple) + >>> tuple_lssd.inputs.operation = 'lssd' + >>> tuple_lssd.inputs.operand_file1 = 'im2.nii' + >>> tuple_lssd.inputs.operand_value2 = 1.0 + >>> tuple_lssd.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -lssd im2.nii 1.00000000 -odt float im1_lssd.nii' + >>> tuple_lssd.run() # doctest: +SKIP + + >>> # Test lltsnorm operation + >>> tuple_lltsnorm = copy.deepcopy(tuple) + >>> tuple_lltsnorm.inputs.operation = 'lltsnorm' + >>> tuple_lltsnorm.inputs.operand_file1 = 'im2.nii' + >>> tuple_lltsnorm.inputs.operand_value2 = 0.01 + >>> tuple_lltsnorm.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -lltsnorm im2.nii 0.01000000 -odt float \ +im1_lltsnorm.nii' + >>> tuple_lltsnorm.run() # doctest: +SKIP + """ + input_spec = TupleMathsInput + + +class MergeInput(MathsInput): + """Input Spec for seg_maths merge operation.""" + dimension = traits.Int(mandatory=True, + desc='Dimension to merge the images.') + + desc = 'List of images to merge to the working image .' + merge_files = traits.List(File(exists=True), + argstr='%s', + mandatory=True, + position=4, + desc=desc) + + +class Merge(MathsCommand): + """Interface for executable seg_maths from NiftySeg platform. + + Interface to use the merge operation that can be performed + with the seg_maths command-line program. See below for this option: + -merge Merge images and the working image in the + dimension + + 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.Merge() + >>> node.inputs.in_file = 'im1.nii' + >>> files = ['im2.nii', 'im3.nii'] + >>> node.inputs.merge_files = files + >>> node.inputs.dimension = 2 + >>> node.inputs.output_datatype = 'float' + >>> node.cmdline # doctest: +ALLOW_UNICODE + 'seg_maths im1.nii -merge 2 2 im2.nii im3.nii -odt float im1_merged.nii' + + """ + input_spec = MergeInput + _suffix = '_merged' + + def _format_arg(self, opt, spec, val): + """Convert input to appropriate format for seg_maths.""" + if opt == 'merge_files': + return "-merge %d %d %s" % (len(val), self.inputs.dimension, + ' '.join(val)) + + return super(Merge, self)._format_arg(opt, spec, val) diff --git a/nipype/interfaces/niftyseg/patchmatch.py b/nipype/interfaces/niftyseg/patchmatch.py new file mode 100644 index 0000000000..5a81583dc7 --- /dev/null +++ b/nipype/interfaces/niftyseg/patchmatch.py @@ -0,0 +1,117 @@ +# 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_DetectLesions 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) +""" + +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 PatchMatchInputSpec(CommandLineInputSpec): + """Input Spec for PatchMatch.""" + # Mandatory input arguments + in_file = File(argstr='-i %s', + exists=True, + mandatory=True, + desc='Input image to segment', + position=1) + + mask_file = File(argstr='-m %s', + exists=True, + mandatory=True, + desc='Input mask for the area where applies PatchMatch', + position=2) + + database_file = File(argstr='-db %s', + exists=True, + mandatory=True, + desc='Database with the segmentations', + position=3) + + # Output file name + 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) + + # Optional arguments + patch_size = traits.Int(desc="Patch size, #voxels", + argstr='-size %i', + mandatory=False) + + desc = "Constrained search area size, number of times bigger than the \ +patchsize" + cs_size = traits.Int(desc=desc, + argstr='-cs %i', + mandatory=False) + + match_num = traits.Int(desc="Number of better matching", + argstr='-match %i', + mandatory=False) + + pm_num = traits.Int(desc="Number of patchmatch executions", + argstr='-pm %i', + mandatory=False) + + desc = "Number of iterations for the patchmatch algorithm" + it_num = traits.Int(desc=desc, + argstr='-it %i', + mandatory=False) + + +class PatchMatchOutputSpec(TraitedSpec): + """OutputSpec for PatchMatch.""" + out_file = File(desc="Output segmentation") + + +class PatchMatch(NiftySegCommand): + """Interface for executable seg_PatchMatch from NiftySeg platform. + + The database file is a text file and in each line we have a template + file, a mask with the search region to consider and a file with the + label to propagate. + + Input image, input mask, template images from database and masks from + database must have the same 4D resolution (same number of XxYxZ voxels, + modalities and/or time-points). + Label files from database must have the same 3D resolution + (XxYxZ voxels) than input image but can have different number of + volumes than the input image allowing to propagate multiple labels + in the same execution. + + 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.PatchMatch() + >>> node.inputs.in_file = 'im1.nii' + >>> node.inputs.mask_file = 'im2.nii' + >>> node.inputs.database_file = 'db.xml' + >>> 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', env_dir='NIFTYSEGDIR') + input_spec = PatchMatchInputSpec + output_spec = PatchMatchOutputSpec + _suffix = '_pm' diff --git a/nipype/interfaces/niftyseg/stats.py b/nipype/interfaces/niftyseg/stats.py new file mode 100644 index 0000000000..b587392d29 --- /dev/null +++ b/nipype/interfaces/niftyseg/stats.py @@ -0,0 +1,242 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +""" +The stats module provides higher-level interfaces to some of the operations +that can be performed with the niftyseg stats (seg_stats) 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 __future__ import print_function +import numpy as np + +from ..base import TraitedSpec, File, traits, CommandLineInputSpec +from .base import NiftySegCommand +from ..niftyreg.base import get_custom_path + + +class StatsInput(CommandLineInputSpec): + """Input Spec for seg_stats interfaces.""" + in_file = File(position=2, + argstr='%s', + exists=True, + mandatory=True, + desc='image to operate on') + + # Constrains + mask_file = File(exists=True, + mandatory=False, + position=-2, + argstr='-m %s', + desc='statistics within the masked area') + + desc = 'Only estimate statistics if voxel is larger than ' + larger_voxel = traits.Float(argstr='-t %f', + mandatory=False, + position=-3, + desc=desc) + + +class StatsOutput(TraitedSpec): + """Output Spec for seg_stats interfaces.""" + output = traits.Array(desc='Output array from seg_stats') + + +class StatsCommand(NiftySegCommand): + """ + Base Command Interface for seg_stats interfaces. + + The executable seg_stats enables the estimation of image statistics on + continuous voxel intensities (average, standard deviation, min/max, robust + range, percentiles, sum, probabilistic volume, entropy, etc) either over + the full image or on a per slice basis (slice axis can be specified), + statistics over voxel coordinates (location of max, min and centre of + mass, bounding box, etc) and statistics over categorical images (e.g. per + region volume, count, average, Dice scores, etc). These statistics are + robust to the presence of NaNs, and can be constrained by a mask and/or + thresholded at a certain level. + """ + _cmd = get_custom_path('seg_stats', env_dir='NIFTYSEGDIR') + input_spec = StatsInput + output_spec = StatsOutput + + def _parse_stdout(self, stdout): + out = [] + for string_line in stdout.split("\n"): + print('parsing line {0}'.format(string_line)) + if string_line.startswith('#'): + continue + if len(string_line) <= 1: + continue + line = [float(s) for s in string_line.split()] + out.append(line) + return np.array(out).squeeze() + + def _run_interface(self, runtime): + print('parsing output in run_interface') + new_runtime = super(UnaryStats, self)._run_interface(runtime) + self.output = self._parse_stdout(new_runtime.stdout) + return new_runtime + + def _list_outputs(self): + outputs = self.output_spec().get() + outputs['output'] = self.output + return outputs + + +class UnaryStatsInput(StatsInput): + """Input Spec for seg_stats unary operations.""" + operation = traits.Enum('r', 'R', 'a', 's', 'v', 'vl', 'vp', 'n', 'np', + 'e', 'ne', 'x', 'X', 'c', 'B', 'xvox', 'xdim', + argstr='-%s', + position=4, + mandatory=True, + desc='operation to perform') + + +class UnaryStats(StatsCommand): + """ + Interface for executable seg_stats from NiftySeg platform. + + Interface to use any unary statistical operations that can be performed + with the seg_stats command-line program. See below for those operations: + -r | The range of all voxels. + -R | The robust range (assuming 2% outliers on both sides) + | of all voxels + -a | Average of all voxels + -s | Standard deviation of all voxels + -v | Volume of all voxels above 0 (<# voxels> * + | ) + -vl | Volume of each integer label (<# voxels per label> * + | ) + -vp | Volume of all probabilsitic voxels (sum() * + | ) + -n | Count of all voxels above 0 (<# voxels>) + -np | Sum of all fuzzy voxels (sum()) + -e | Entropy of all voxels + -ne | Normalized entropy of all voxels + -x | Location (i j k x y z) of the smallest value in the image + -X | Location (i j k x y z) of the largest value in the image + -c | Location (i j k x y z) of the centre of mass of the + | object + -B | Bounding box of all nonzero voxels + | [ xmin xsize ymin ysize zmin zsize ] + -xvox | Output the number of voxels in the x direction. + | Replace x with y/z for other directions. + -xdim | Output the voxel dimention in the x direction. + | Replace x with y/z for other directions. + + 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.UnaryStats() + >>> unary.inputs.in_file = 'im1.nii' + >>> # Test v operation + >>> unary_v = copy.deepcopy(unary) + >>> unary_v.inputs.operation = 'v' + >>> unary_v.cmdline # doctest: +ALLOW_UNICODE + 'seg_stats im1.nii -v' + >>> unary_v.run() # doctest: +SKIP + >>> # Test vl operation + >>> unary_vl = copy.deepcopy(unary) + >>> unary_vl.inputs.operation = 'vl' + >>> unary_vl.cmdline # doctest: +ALLOW_UNICODE + 'seg_stats im1.nii -vl' + >>> unary_vl.run() # doctest: +SKIP + >>> # Test x operation + >>> unary_x = copy.deepcopy(unary) + >>> unary_x.inputs.operation = 'x' + >>> unary_x.cmdline # doctest: +ALLOW_UNICODE + 'seg_stats im1.nii -x' + >>> unary_x.run() # doctest: +SKIP + + """ + input_spec = UnaryStatsInput + + +class BinaryStatsInput(StatsInput): + """Input Spec for seg_stats Binary operations.""" + operation = traits.Enum('p', 'sa', 'ss', 'svp', 'al', 'd', 'ncc', 'nmi', + 'Vl', 'Nl', + mandatory=True, + argstr='-%s', + position=4, + desc='operation to perform') + + operand_file = File(exists=True, + argstr="%s", + mandatory=True, + position=5, + xor=["operand_value"], + desc="second image to perform operation with") + + operand_value = traits.Float(argstr='%.8f', + mandatory=True, + position=5, + xor=["operand_file"], + desc='value to perform operation with') + + +class BinaryStats(StatsCommand): + """ + Interface for executable seg_stats from NiftySeg platform. + + Interface to use any binary statistical operations that can be performed + with the seg_stats command-line program. See below for those operations: + -p | The th percentile of all voxels intensity + | (float=[0,100]) + -sa | Average of all voxels + -ss | Standard deviation of all voxels + -svp | Volume of all probabilsitic voxels (sum() * + | ) + -al | Average value in for each label in + -d | Calculate the Dice score between all classes in + | and + -ncc | Normalized cross correlation between and + -nmi | Normalized Mutual Information between and + -Vl | Volume of each integer label . Save to file. + -Nl | Count of each label . Save to file. + + 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 + >>> binary = niftyseg.BinaryStats() + >>> binary.inputs.in_file = 'im1.nii' + >>> # Test sa operation + >>> binary_sa = copy.deepcopy(binary) + >>> binary_sa.inputs.operation = 'sa' + >>> binary_sa.inputs.operand_value = 2.0 + >>> binary_sa.cmdline # doctest: +ALLOW_UNICODE + 'seg_stats im1.nii -sa 2.00000000' + >>> binary_sa.run() # doctest: +SKIP + >>> # Test ncc operation + >>> binary_ncc = copy.deepcopy(binary) + >>> binary_ncc.inputs.operation = 'ncc' + >>> binary_ncc.inputs.operand_file = 'im2.nii' + >>> binary_ncc.cmdline # doctest: +ALLOW_UNICODE + 'seg_stats im1.nii -ncc im2.nii' + >>> binary_ncc.run() # doctest: +SKIP + >>> # Test Nl operation + >>> binary_nl = copy.deepcopy(binary) + >>> binary_nl.inputs.operation = 'Nl' + >>> binary_nl.inputs.operand_file = 'output.csv' + >>> binary_nl.cmdline # doctest: +ALLOW_UNICODE + 'seg_stats im1.nii -Nl output.csv' + >>> binary_nl.run() # doctest: +SKIP + + """ + input_spec = BinaryStatsInput diff --git a/nipype/interfaces/niftyseg/tests/__init__.py b/nipype/interfaces/niftyseg/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/interfaces/niftyseg/tests/test_auto_BinaryMaths.py b/nipype/interfaces/niftyseg/tests/test_auto_BinaryMaths.py new file mode 100644 index 0000000000..714e201fc3 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_BinaryMaths.py @@ -0,0 +1,63 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..maths import BinaryMaths + + +def test_BinaryMaths_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + operand_file=dict(argstr='%s', + mandatory=True, + position=5, + xor=['operand_value', 'operand_str'], + ), + operand_str=dict(argstr='%s', + mandatory=True, + position=5, + xor=['operand_value', 'operand_file'], + ), + operand_value=dict(argstr='%.8f', + mandatory=True, + position=5, + xor=['operand_file', 'operand_str'], + ), + operation=dict(argstr='-%s', + mandatory=True, + position=4, + ), + out_file=dict(argstr='%s', + name_source=['in_file'], + name_template='%s', + position=-2, + ), + output_datatype=dict(argstr='-odt %s', + position=-3, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = BinaryMaths.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_BinaryMaths_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = BinaryMaths.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_BinaryMathsInteger.py b/nipype/interfaces/niftyseg/tests/test_auto_BinaryMathsInteger.py new file mode 100644 index 0000000000..484f2ac3b4 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_BinaryMathsInteger.py @@ -0,0 +1,52 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..maths import BinaryMathsInteger + + +def test_BinaryMathsInteger_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + operand_value=dict(argstr='%d', + mandatory=True, + position=5, + ), + operation=dict(argstr='-%s', + mandatory=True, + position=4, + ), + out_file=dict(argstr='%s', + name_source=['in_file'], + name_template='%s', + position=-2, + ), + output_datatype=dict(argstr='-odt %s', + position=-3, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = BinaryMathsInteger.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_BinaryMathsInteger_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = BinaryMathsInteger.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_BinaryStats.py b/nipype/interfaces/niftyseg/tests/test_auto_BinaryStats.py new file mode 100644 index 0000000000..9546286202 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_BinaryStats.py @@ -0,0 +1,58 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..stats import BinaryStats + + +def test_BinaryStats_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + larger_voxel=dict(argstr='-t %f', + mandatory=False, + position=-3, + ), + mask_file=dict(argstr='-m %s', + mandatory=False, + position=-2, + ), + operand_file=dict(argstr='%s', + mandatory=True, + position=5, + xor=['operand_value'], + ), + operand_value=dict(argstr='%.8f', + mandatory=True, + position=5, + xor=['operand_file'], + ), + operation=dict(argstr='-%s', + mandatory=True, + position=4, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = BinaryStats.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_BinaryStats_outputs(): + output_map = dict(output=dict(), + ) + outputs = BinaryStats.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_CalcTopNCC.py b/nipype/interfaces/niftyseg/tests/test_auto_CalcTopNCC.py new file mode 100644 index 0000000000..750426acc1 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_CalcTopNCC.py @@ -0,0 +1,51 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..label_fusion import CalcTopNCC + + +def test_CalcTopNCC_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='-target %s', + mandatory=True, + position=1, + ), + in_templates=dict(argstr='%s', + mandatory=True, + position=3, + ), + mask_file=dict(argstr='-mask %s', + mandatory=False, + ), + num_templates=dict(argstr='-templates %s', + mandatory=True, + position=2, + ), + terminal_output=dict(nohash=True, + ), + top_templates=dict(argstr='-n %s', + mandatory=True, + position=4, + ), + ) + inputs = CalcTopNCC.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_CalcTopNCC_outputs(): + output_map = dict(out_files=dict(), + ) + outputs = CalcTopNCC.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_EM.py b/nipype/interfaces/niftyseg/tests/test_auto_EM.py new file mode 100644 index 0000000000..c42acf6a70 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_EM.py @@ -0,0 +1,80 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..em import EM + + +def test_EM_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + bc_order_val=dict(argstr='-bc_order %s', + ), + bc_thresh_val=dict(argstr='-bc_thresh %s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='-in %s', + mandatory=True, + position=4, + ), + mask_file=dict(argstr='-mask %s', + ), + max_iter=dict(argstr='-max_iter %s', + ), + min_iter=dict(argstr='-min_iter %s', + ), + mrf_beta_val=dict(argstr='-mrf_beta %s', + ), + no_prior=dict(argstr='-nopriors %s', + mandatory=True, + xor=['prior_4D', 'priors'], + ), + out_bc_file=dict(argstr='-bc_out %s', + name_source=['in_file'], + name_template='%s_bc_em.nii.gz', + ), + out_file=dict(argstr='-out %s', + name_source=['in_file'], + name_template='%s_em.nii.gz', + ), + out_outlier_file=dict(argstr='-out_outlier %s', + name_source=['in_file'], + name_template='%s_outlier_em.nii.gz', + ), + outlier_val=dict(argstr='-outlier %s %s', + ), + prior_4D=dict(argstr='-prior4D %s', + mandatory=True, + xor=['no_prior', 'priors'], + ), + priors=dict(argstr='%s', + mandatory=True, + xor=['no_prior', 'prior_4D'], + ), + reg_val=dict(argstr='-reg %s', + ), + relax_priors=dict(argstr='-rf %s %s', + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = EM.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_EM_outputs(): + output_map = dict(out_bc_file=dict(), + out_file=dict(), + out_outlier_file=dict(), + ) + outputs = EM.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_FillLesions.py b/nipype/interfaces/niftyseg/tests/test_auto_FillLesions.py new file mode 100644 index 0000000000..aae126636a --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_FillLesions.py @@ -0,0 +1,69 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..lesions import FillLesions + + +def test_FillLesions_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + bin_mask=dict(argstr='-mask %s', + ), + cwf=dict(argstr='-cwf %f', + ), + debug=dict(argstr='-debug', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_dilation=dict(argstr='-dil %d', + ), + in_file=dict(argstr='-i %s', + mandatory=True, + position=1, + ), + lesion_mask=dict(argstr='-l %s', + mandatory=True, + position=2, + ), + match=dict(argstr='-match %f', + ), + other=dict(argstr='-other', + ), + out_datatype=dict(argstr='-odt %s', + ), + out_file=dict(argstr='-o %s', + name_source=['in_file'], + name_template='%s_lesions_filled.nii.gz', + position=3, + ), + search=dict(argstr='-search %f', + ), + size=dict(argstr='-size %d', + ), + smooth=dict(argstr='-smo %f', + ), + terminal_output=dict(nohash=True, + ), + use_2d=dict(argstr='-2D', + ), + verbose=dict(argstr='-v', + ), + ) + inputs = FillLesions.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_FillLesions_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = FillLesions.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_LabelFusion.py b/nipype/interfaces/niftyseg/tests/test_auto_LabelFusion.py new file mode 100644 index 0000000000..b572ac940c --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_LabelFusion.py @@ -0,0 +1,76 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..label_fusion import LabelFusion + + +def test_LabelFusion_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + classifier_type=dict(argstr='-%s', + mandatory=True, + position=2, + ), + conv=dict(argstr='-conv %f', + ), + dilation_roi=dict(), + environ=dict(nohash=True, + usedefault=True, + ), + file_to_seg=dict(mandatory=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='-in %s', + mandatory=True, + position=1, + ), + kernel_size=dict(), + mask_file=dict(argstr='-mask %s', + ), + max_iter=dict(argstr='-max_iter %d', + ), + mrf_value=dict(argstr='-MRF_beta %f', + ), + out_file=dict(argstr='-out %s', + name_source=['in_file'], + name_template='%s', + ), + prob_flag=dict(argstr='-outProb', + ), + prob_update_flag=dict(argstr='-prop_update', + ), + proportion=dict(argstr='-prop %s', + ), + set_pq=dict(argstr='-setPQ %f %f', + ), + sm_ranking=dict(argstr='-%s', + position=3, + usedefault=True, + ), + template_file=dict(), + template_num=dict(), + terminal_output=dict(nohash=True, + ), + unc=dict(argstr='-unc', + ), + unc_thresh=dict(argstr='-uncthres %f', + ), + verbose=dict(argstr='-v %s', + ), + ) + inputs = LabelFusion.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_LabelFusion_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = LabelFusion.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_MathsCommand.py b/nipype/interfaces/niftyseg/tests/test_auto_MathsCommand.py new file mode 100644 index 0000000000..640c7088bf --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_MathsCommand.py @@ -0,0 +1,44 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..maths import MathsCommand + + +def test_MathsCommand_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + out_file=dict(argstr='%s', + name_source=['in_file'], + name_template='%s', + position=-2, + ), + output_datatype=dict(argstr='-odt %s', + position=-3, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = MathsCommand.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_MathsCommand_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = MathsCommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_Merge.py b/nipype/interfaces/niftyseg/tests/test_auto_Merge.py new file mode 100644 index 0000000000..3980bc9ac3 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_Merge.py @@ -0,0 +1,50 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..maths import Merge + + +def test_Merge_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + dimension=dict(mandatory=True, + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + merge_files=dict(argstr='%s', + mandatory=True, + position=4, + ), + out_file=dict(argstr='%s', + name_source=['in_file'], + name_template='%s', + position=-2, + ), + output_datatype=dict(argstr='-odt %s', + position=-3, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = Merge.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_Merge_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = Merge.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_NiftySegCommand.py b/nipype/interfaces/niftyseg/tests/test_auto_NiftySegCommand.py new file mode 100644 index 0000000000..55dc5d9d1d --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_NiftySegCommand.py @@ -0,0 +1,23 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..base import NiftySegCommand + + +def test_NiftySegCommand_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = NiftySegCommand.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + diff --git a/nipype/interfaces/niftyseg/tests/test_auto_StatsCommand.py b/nipype/interfaces/niftyseg/tests/test_auto_StatsCommand.py new file mode 100644 index 0000000000..c7c7f2b364 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_StatsCommand.py @@ -0,0 +1,44 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..stats import StatsCommand + + +def test_StatsCommand_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + larger_voxel=dict(argstr='-t %f', + mandatory=False, + position=-3, + ), + mask_file=dict(argstr='-m %s', + mandatory=False, + position=-2, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = StatsCommand.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_StatsCommand_outputs(): + output_map = dict(output=dict(), + ) + outputs = StatsCommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_TupleMaths.py b/nipype/interfaces/niftyseg/tests/test_auto_TupleMaths.py new file mode 100644 index 0000000000..9bd5ca771d --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_TupleMaths.py @@ -0,0 +1,68 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..maths import TupleMaths + + +def test_TupleMaths_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + operand_file1=dict(argstr='%s', + mandatory=True, + position=5, + xor=['operand_value1'], + ), + operand_file2=dict(argstr='%s', + mandatory=True, + position=6, + xor=['operand_value2'], + ), + operand_value1=dict(argstr='%.8f', + mandatory=True, + position=5, + xor=['operand_file1'], + ), + operand_value2=dict(argstr='%.8f', + mandatory=True, + position=6, + xor=['operand_file2'], + ), + operation=dict(argstr='-%s', + mandatory=True, + position=4, + ), + out_file=dict(argstr='%s', + name_source=['in_file'], + name_template='%s', + position=-2, + ), + output_datatype=dict(argstr='-odt %s', + position=-3, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = TupleMaths.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_TupleMaths_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = TupleMaths.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_UnaryMaths.py b/nipype/interfaces/niftyseg/tests/test_auto_UnaryMaths.py new file mode 100644 index 0000000000..ed98de196f --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_UnaryMaths.py @@ -0,0 +1,48 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..maths import UnaryMaths + + +def test_UnaryMaths_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + operation=dict(argstr='-%s', + mandatory=True, + position=4, + ), + out_file=dict(argstr='%s', + name_source=['in_file'], + name_template='%s', + position=-2, + ), + output_datatype=dict(argstr='-odt %s', + position=-3, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = UnaryMaths.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_UnaryMaths_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = UnaryMaths.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_auto_UnaryStats.py b/nipype/interfaces/niftyseg/tests/test_auto_UnaryStats.py new file mode 100644 index 0000000000..cfbde15114 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_auto_UnaryStats.py @@ -0,0 +1,48 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..stats import UnaryStats + + +def test_UnaryStats_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='%s', + mandatory=True, + position=2, + ), + larger_voxel=dict(argstr='-t %f', + mandatory=False, + position=-3, + ), + mask_file=dict(argstr='-m %s', + mandatory=False, + position=-2, + ), + operation=dict(argstr='-%s', + mandatory=True, + position=4, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = UnaryStats.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_UnaryStats_outputs(): + output_map = dict(output=dict(), + ) + outputs = UnaryStats.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value diff --git a/nipype/interfaces/niftyseg/tests/test_em_interfaces.py b/nipype/interfaces/niftyseg/tests/test_em_interfaces.py new file mode 100644 index 0000000000..810a782b63 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_em_interfaces.py @@ -0,0 +1,41 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +import pytest + +from nipype.interfaces.niftyreg import no_nifty_package, get_custom_path +from nipype.interfaces.niftyseg import EM +from nipype.testing import example_data + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_EM'), + reason="niftyseg is not installed") +def test_seg_em(): + + # Create a node object + seg_em = EM() + + # Check if the command is properly defined + cmd = get_custom_path('seg_EM', env_dir='NIFTYSEGDIR') + assert seg_em.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + seg_em.run() + + # Assign some input data + in_file = example_data('im1.nii') + seg_em.inputs.in_file = in_file + seg_em.inputs.no_prior = 4 + + cmd_tmp = '{cmd} -in {in_file} -nopriors 4 -bc_out {bc_out} -out \ +{out_file} -out_outlier {out_outlier}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + out_file='im1_em.nii.gz', + bc_out='im1_bc_em.nii.gz', + out_outlier='im1_outlier_em.nii.gz', + ) + + assert seg_em.cmdline == expected_cmd diff --git a/nipype/interfaces/niftyseg/tests/test_label_fusion.py b/nipype/interfaces/niftyseg/tests/test_label_fusion.py new file mode 100644 index 0000000000..f34fc9149f --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_label_fusion.py @@ -0,0 +1,125 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +import pytest + +from nipype.interfaces.niftyreg import no_nifty_package, get_custom_path +from nipype.interfaces.niftyseg import LabelFusion, CalcTopNCC +from nipype.testing import example_data + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_LabFusion'), + reason="niftyseg is not installed") +def test_seg_lab_fusion(): + """ Test interfaces for seg_labfusion""" + # Create a node object + steps = LabelFusion() + + # Check if the command is properly defined + cmd = get_custom_path('seg_LabFusion', env_dir='NIFTYSEGDIR') + assert steps.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + steps.run() + + # Assign some input data + in_file = example_data('im1.nii') + file_to_seg = example_data('im2.nii') + template_file = example_data('im3.nii') + steps.inputs.in_file = in_file + steps.inputs.kernel_size = 2.0 + steps.inputs.file_to_seg = file_to_seg + steps.inputs.template_file = template_file + steps.inputs.template_num = 2 + steps.inputs.classifier_type = 'STEPS' + + cmd_tmp = '{cmd} -in {in_file} -STEPS 2.000000 2 {file_to_seg} \ +{template_file} -out {out_file}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + file_to_seg=file_to_seg, + template_file=template_file, + out_file='im1_steps.nii', + ) + + assert steps.cmdline == expected_cmd + + # Staple + staple = LabelFusion(kernel_size=2.0, template_num=2, + classifier_type='STAPLE') + in_file = example_data('im1.nii') + file_to_seg = example_data('im2.nii') + template_file = example_data('im3.nii') + staple.inputs.in_file = in_file + staple.inputs.file_to_seg = file_to_seg + staple.inputs.template_file = template_file + + cmd_tmp = '{cmd} -in {in_file} -STAPLE -ALL -out {out_file}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + file_to_seg=file_to_seg, + template_file=template_file, + out_file='im1_staple.nii', + ) + + assert staple.cmdline == expected_cmd + + # Assign some input data + mv_node = LabelFusion(template_num=2, classifier_type='MV', + sm_ranking='ROINCC', dilation_roi=2) + in_file = example_data('im1.nii') + file_to_seg = example_data('im2.nii') + template_file = example_data('im3.nii') + mv_node.inputs.in_file = in_file + mv_node.inputs.file_to_seg = file_to_seg + mv_node.inputs.template_file = template_file + + cmd_tmp = '{cmd} -in {in_file} -MV -ROINCC 2 2 {file_to_seg} \ +{template_file} -out {out_file}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + file_to_seg=file_to_seg, + template_file=template_file, + out_file='im1_mv.nii', + ) + + assert mv_node.cmdline == expected_cmd + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_CalcTopNCC'), + reason="niftyseg is not installed") +def test_seg_calctopncc(): + """ Test interfaces for seg_CalctoNCC""" + # Create a node object + calctopncc = CalcTopNCC() + + # Check if the command is properly defined + cmd = get_custom_path('seg_CalcTopNCC', env_dir='NIFTYSEGDIR') + assert calctopncc.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + calctopncc.run() + + # Assign some input data + in_file = example_data('im1.nii') + file1 = example_data('im2.nii') + file2 = example_data('im3.nii') + calctopncc.inputs.in_file = in_file + calctopncc.inputs.num_templates = 2 + calctopncc.inputs.in_templates = [file1, file2] + calctopncc.inputs.top_templates = 1 + + cmd_tmp = '{cmd} -target {in_file} -templates 2 {file1} {file2} -n 1' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + file1=file1, + file2=file2, + ) + + assert calctopncc.cmdline == expected_cmd diff --git a/nipype/interfaces/niftyseg/tests/test_lesions.py b/nipype/interfaces/niftyseg/tests/test_lesions.py new file mode 100644 index 0000000000..55250bde92 --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_lesions.py @@ -0,0 +1,39 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +import pytest + +from nipype.interfaces.niftyreg import no_nifty_package, get_custom_path +from nipype.interfaces.niftyseg import FillLesions +from nipype.testing import example_data + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_FillLesions'), + reason="niftyseg is not installed") +def test_seg_filllesions(): + + # Create a node object + seg_fill = FillLesions() + + # Check if the command is properly defined + cmd = get_custom_path('seg_FillLesions', env_dir='NIFTYSEGDIR') + assert seg_fill.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + seg_fill.run() + + # Assign some input data + in_file = example_data('im1.nii') + lesion_mask = example_data('im2.nii') + seg_fill.inputs.in_file = in_file + seg_fill.inputs.lesion_mask = lesion_mask + + expected_cmd = '{cmd} -i {in_file} -l {lesion_mask} -o {out_file}'.format( + cmd=cmd, + in_file=in_file, + lesion_mask=lesion_mask, + out_file='im1_lesions_filled.nii.gz', + ) + + assert seg_fill.cmdline == expected_cmd diff --git a/nipype/interfaces/niftyseg/tests/test_maths.py b/nipype/interfaces/niftyseg/tests/test_maths.py new file mode 100644 index 0000000000..307adb503d --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_maths.py @@ -0,0 +1,169 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +import pytest + +from nipype.interfaces.niftyreg import no_nifty_package, get_custom_path +from nipype.interfaces.niftyseg import (UnaryMaths, BinaryMaths, + BinaryMathsInteger, TupleMaths, + Merge) +from nipype.testing import example_data + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_maths'), + reason="niftyseg is not installed") +def test_unary_maths(): + + # Create a node object + unarym = UnaryMaths() + + # Check if the command is properly defined + cmd = get_custom_path('seg_maths', env_dir='NIFTYSEGDIR') + assert unarym.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + unarym.run() + + # Assign some input data + in_file = example_data('im1.nii') + unarym.inputs.in_file = in_file + unarym.inputs.operation = 'otsu' + unarym.inputs.output_datatype = 'float' + + expected_cmd = '{cmd} {in_file} -otsu -odt float {out_file}'.format( + cmd=cmd, + in_file=in_file, + out_file='im1_otsu.nii') + + assert unarym.cmdline == expected_cmd + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_maths'), + reason="niftyseg is not installed") +def test_binary_maths(): + + # Create a node object + binarym = BinaryMaths() + + # Check if the command is properly defined + cmd = get_custom_path('seg_maths', env_dir='NIFTYSEGDIR') + assert binarym.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + binarym.run() + + # Assign some input data + in_file = example_data('im1.nii') + binarym.inputs.in_file = in_file + binarym.inputs.operand_value = 2.0 + binarym.inputs.operation = 'sub' + binarym.inputs.output_datatype = 'float' + + cmd_tmp = '{cmd} {in_file} -sub 2.00000000 -odt float {out_file}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + out_file='im1_sub.nii') + + assert binarym.cmdline == expected_cmd + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_maths'), + reason="niftyseg is not installed") +def test_int_binary_maths(): + + # Create a node object + ibinarym = BinaryMathsInteger() + + # Check if the command is properly defined + cmd = get_custom_path('seg_maths', env_dir='NIFTYSEGDIR') + assert ibinarym.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + ibinarym.run() + + # Assign some input data + in_file = example_data('im1.nii') + ibinarym.inputs.in_file = in_file + ibinarym.inputs.operand_value = 2 + ibinarym.inputs.operation = 'dil' + ibinarym.inputs.output_datatype = 'float' + + expected_cmd = '{cmd} {in_file} -dil 2 -odt float {out_file}'.format( + cmd=cmd, + in_file=in_file, + out_file='im1_dil.nii') + + assert ibinarym.cmdline == expected_cmd + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_maths'), + reason="niftyseg is not installed") +def test_tuple_maths(): + + # Create a node object + tuplem = TupleMaths() + + # Check if the command is properly defined + cmd = get_custom_path('seg_maths', env_dir='NIFTYSEGDIR') + assert tuplem.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + tuplem.run() + + # Assign some input data + in_file = example_data('im1.nii') + op_file = example_data('im2.nii') + tuplem.inputs.in_file = in_file + tuplem.inputs.operation = 'lncc' + tuplem.inputs.operand_file1 = op_file + tuplem.inputs.operand_value2 = 2.0 + tuplem.inputs.output_datatype = 'float' + + cmd_tmp = '{cmd} {in_file} -lncc {op} 2.00000000 -odt float {out_file}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + op=op_file, + out_file='im1_lncc.nii') + + assert tuplem.cmdline == expected_cmd + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_maths'), + reason="niftyseg is not installed") +def test_merge(): + + # Create a node object + merge = Merge() + + # Check if the command is properly defined + cmd = get_custom_path('seg_maths', env_dir='NIFTYSEGDIR') + assert merge.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + merge.run() + + # Assign some input data + in_file = example_data('im1.nii') + file1 = example_data('im2.nii') + file2 = example_data('im3.nii') + merge.inputs.in_file = in_file + merge.inputs.merge_files = [file1, file2] + merge.inputs.dimension = 2 + merge.inputs.output_datatype = 'float' + + cmd_tmp = '{cmd} {in_file} -merge 2 2 {f1} {f2} -odt float {out_file}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + f1=file1, + f2=file2, + out_file='im1_merged.nii') + + assert merge.cmdline == expected_cmd diff --git a/nipype/interfaces/niftyseg/tests/test_patchmatch.py b/nipype/interfaces/niftyseg/tests/test_patchmatch.py new file mode 100644 index 0000000000..b88552fb0d --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_patchmatch.py @@ -0,0 +1,43 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +import pytest + +from nipype.interfaces.niftyreg import no_nifty_package, get_custom_path +from nipype.interfaces.niftyseg import PatchMatch +from nipype.testing import example_data + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_PatchMatch'), + reason="niftyseg is not installed") +def test_seg_patchmatch(): + + # Create a node object + seg_patchmatch = PatchMatch() + + # Check if the command is properly defined + cmd = get_custom_path('seg_PatchMatch', env_dir='NIFTYSEGDIR') + assert seg_patchmatch.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + seg_patchmatch.run() + + # Assign some input data + in_file = example_data('im1.nii') + mask_file = example_data('im2.nii') + db_file = example_data('db.xml') + seg_patchmatch.inputs.in_file = in_file + seg_patchmatch.inputs.mask_file = mask_file + seg_patchmatch.inputs.database_file = db_file + + cmd_tmp = '{cmd} -i {in_file} -m {mask_file} -db {db} -o {out_file}' + expected_cmd = cmd_tmp.format( + cmd=cmd, + in_file=in_file, + mask_file=mask_file, + db=db_file, + out_file='im1_pm.nii.gz', + ) + + assert seg_patchmatch.cmdline == expected_cmd diff --git a/nipype/interfaces/niftyseg/tests/test_stats.py b/nipype/interfaces/niftyseg/tests/test_stats.py new file mode 100644 index 0000000000..ae3cfbfc6e --- /dev/null +++ b/nipype/interfaces/niftyseg/tests/test_stats.py @@ -0,0 +1,63 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +import pytest + +from nipype.interfaces.niftyreg import no_nifty_package, get_custom_path +from nipype.interfaces.niftyseg import UnaryStats, BinaryStats +from nipype.testing import example_data + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_stats'), + reason="niftyseg is not installed") +def test_unary_stats(): + """ Test for the seg_stats interfaces """ + # Create a node object + unarys = UnaryStats() + + # Check if the command is properly defined + cmd = get_custom_path('seg_stats', env_dir='NIFTYSEGDIR') + assert unarys.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + unarys.run() + + # Assign some input data + in_file = example_data('im1.nii') + unarys.inputs.in_file = in_file + unarys.inputs.operation = 'a' + + expected_cmd = '{cmd} {in_file} -a'.format( + cmd=cmd, + in_file=in_file) + + assert unarys.cmdline == expected_cmd + + +@pytest.mark.skipif(no_nifty_package(cmd='seg_stats'), + reason="niftyseg is not installed") +def test_binary_stats(): + """ Test for the seg_stats interfaces """ + # Create a node object + binarys = BinaryStats() + + # Check if the command is properly defined + cmd = get_custom_path('seg_stats', env_dir='NIFTYSEGDIR') + assert binarys.cmd == cmd + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + binarys.run() + + # Assign some input data + in_file = example_data('im1.nii') + binarys.inputs.in_file = in_file + binarys.inputs.operand_value = 2 + binarys.inputs.operation = 'sa' + + expected_cmd = '{cmd} {in_file} -sa 2.00000000'.format( + cmd=cmd, + in_file=in_file) + + assert binarys.cmdline == expected_cmd diff --git a/nipype/interfaces/setup.py b/nipype/interfaces/setup.py index 72f48f6039..9274224316 100644 --- a/nipype/interfaces/setup.py +++ b/nipype/interfaces/setup.py @@ -24,6 +24,7 @@ def configuration(parent_package='', top_path=None): config.add_subpackage('mne') config.add_subpackage('mrtrix') config.add_subpackage('mrtrix3') + config.add_subpackage('niftyseg') config.add_subpackage('niftyreg') config.add_subpackage('nipy') config.add_subpackage('spm') diff --git a/nipype/testing/data/db.xml b/nipype/testing/data/db.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/testing/data/output.csv b/nipype/testing/data/output.csv new file mode 100644 index 0000000000..e69de29bb2