Skip to content

Adding niftyseg #1911

New issue

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

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

Already on GitHub? Sign in to your account

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nipype/interfaces/niftyreg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
11 changes: 6 additions & 5 deletions nipype/interfaces/niftyreg/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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()
Expand Down
6 changes: 3 additions & 3 deletions nipype/interfaces/niftyreg/tests/test_reg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down Expand Up @@ -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"""
Expand Down
14 changes: 7 additions & 7 deletions nipype/interfaces/niftyreg/tests/test_regutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 """
Expand Down Expand Up @@ -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 """
Expand Down Expand Up @@ -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 """
Expand Down Expand Up @@ -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 """
Expand Down Expand Up @@ -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 """
Expand Down Expand Up @@ -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 """
Expand Down
17 changes: 17 additions & 0 deletions nipype/interfaces/niftyseg/__init__.py
Original file line number Diff line number Diff line change
@@ -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
<https://sourceforge.net/projects/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
44 changes: 44 additions & 0 deletions nipype/interfaces/niftyseg/base.py
Original file line number Diff line number Diff line change
@@ -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
<https://sourceforge.net/projects/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')
146 changes: 146 additions & 0 deletions nipype/interfaces/niftyseg/em.py
Original file line number Diff line number Diff line change
@@ -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). <fl1> is the \
Mahalanobis threshold [recommended between 3 and 7] <fl2> 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: 0<rf<1 (recommended=0.5), \
gaussian regularization: gstd>0 (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)
Loading