diff --git a/nipype/interfaces/niftyreg/__init__.py b/nipype/interfaces/niftyreg/__init__.py new file mode 100644 index 0000000000..9c0feeb2fb --- /dev/null +++ b/nipype/interfaces/niftyreg/__init__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +""" +The niftyreg module provides classes for interfacing with the `NiftyReg +`_ command line tools. + +Top-level namespace for niftyreg. +""" + +from .base import no_niftyreg, 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 new file mode 100644 index 0000000000..9a21fcfd2e --- /dev/null +++ b/nipype/interfaces/niftyreg/base.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +""" +The niftyreg module provides classes for interfacing with `niftyreg +`_ command line tools. + +These are the base tools for working with niftyreg. + +Registration tools are found in niftyreg/reg.py +Every other tool is found in niftyreg/regutils.py + +Examples +-------- +See the docstrings of the individual classes for examples. + +""" + +from __future__ import (print_function, division, unicode_literals, + absolute_import) +from builtins import property, super +from distutils.version import StrictVersion +import os +import shutil +import subprocess +from warnings import warn + +from ..base import CommandLine, isdefined, CommandLineInputSpec, traits +from ...utils.filemanip import split_filename + + +def get_custom_path(command): + return os.path.join(os.getenv('NIFTYREGDIR', ''), command) + + +def no_niftyreg(cmd='reg_f3d'): + try: + return shutil.which(cmd) is None + except AttributeError: # Python < 3.3 + return not any( + [os.path.isfile(os.path.join(path, cmd)) and + os.access(os.path.join(path, cmd), os.X_OK) + for path in os.environ["PATH"].split(os.pathsep)]) + + +class NiftyRegCommandInputSpec(CommandLineInputSpec): + """Input Spec for niftyreg interfaces.""" + # Set the number of omp thread to use + omp_core_val = traits.Int(desc='Number of openmp thread to use', + argstr='-omp %i') + + +class NiftyRegCommand(CommandLine): + """ + Base support interface for NiftyReg commands. + """ + _suffix = '_nr' + _min_version = '1.5.30' + + def __init__(self, required_version=None, **inputs): + super(NiftyRegCommand, self).__init__(**inputs) + self.required_version = required_version + _version = self.get_version() + if _version: + _version = _version.decode("utf-8") + if 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: + if StrictVersion(_version) != StrictVersion(required_version): + msg = 'The version of NiftyReg differs from the required' + msg += '(%s != %s)' + warn(msg % (_version, self.required_version)) + + def check_version(self): + _version = self.get_version() + if not _version: + raise Exception('Niftyreg not found') + # Decoding to string: + _version = _version.decode("utf-8") + if StrictVersion(_version) < StrictVersion(self._min_version): + err = 'A later version of Niftyreg is required (%s < %s)' + raise ValueError(err % (_version, self._min_version)) + if self.required_version: + if StrictVersion(_version) != StrictVersion(self.required_version): + err = 'The version of NiftyReg differs from the required' + err += '(%s != %s)' + raise ValueError(err % (_version, self.required_version)) + + def get_version(self): + if no_niftyreg(cmd=self.cmd): + return None + exec_cmd = ''.join((self.cmd, ' -v')) + return subprocess.check_output(exec_cmd, shell=True).strip() + + @property + def version(self): + return self.get_version() + + def exists(self): + return self.get_version() is not None + + def _run_interface(self, runtime): + # Update num threads estimate from OMP_NUM_THREADS env var + # Default to 1 if not set + if not isdefined(self.inputs.environ['OMP_NUM_THREADS']): + self.inputs.environ['OMP_NUM_THREADS'] = self.num_threads + return super(NiftyRegCommand, self)._run_interface(runtime) + + def _format_arg(self, name, spec, value): + if name == 'omp_core_val': + self.numthreads = value + return super(NiftyRegCommand, self)._format_arg(name, spec, value) + + def _gen_fname(self, basename, out_dir=None, suffix=None, ext=None): + if basename == '': + msg = 'Unable to generate filename for command %s. ' % self.cmd + msg += 'basename is not set!' + raise ValueError(msg) + _, final_bn, final_ext = split_filename(basename) + if out_dir is None: + out_dir = os.getcwd() + if ext is not None: + final_ext = ext + if suffix is not None: + final_bn = ''.join((final_bn, suffix)) + return os.path.abspath(os.path.join(out_dir, final_bn + final_ext)) diff --git a/nipype/interfaces/niftyreg/reg.py b/nipype/interfaces/niftyreg/reg.py new file mode 100644 index 0000000000..4abc2b78c9 --- /dev/null +++ b/nipype/interfaces/niftyreg/reg.py @@ -0,0 +1,403 @@ +# -*- coding: utf-8 -*- +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +""" +The reg module provides classes for interfacing with the `niftyreg +`_ registration command line tools. + +The interfaces were written to work with niftyreg version 1.5.10 + +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, division, unicode_literals, + absolute_import) +from builtins import staticmethod +import os +import warnings + +from ..base import TraitedSpec, File, traits, isdefined +from .base import get_custom_path, NiftyRegCommand, NiftyRegCommandInputSpec +from ...utils.filemanip import split_filename + + +warn = warnings.warn +warnings.filterwarnings('always', category=UserWarning) + + +class RegAladinInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegAladin. """ + # Input reference file + ref_file = File(exists=True, + desc='The input reference/target image', + argstr='-ref %s', + mandatory=True) + # Input floating file + flo_file = File(exists=True, + desc='The input floating/source image', + argstr='-flo %s', + mandatory=True) + # No symmetric flag + nosym_flag = traits.Bool(argstr='-noSym', + desc='Turn off symmetric registration') + # Rigid only registration + rig_only_flag = traits.Bool(argstr='-rigOnly', + desc='Do only a rigid registration') + # Directly optimise affine flag + desc = 'Directly optimise the affine parameters' + aff_direct_flag = traits.Bool(argstr='-affDirect', + desc=desc) + # Input affine + in_aff_file = File(exists=True, + desc='The input affine transformation', + argstr='-inaff %s') + # Input reference mask + rmask_file = File(exists=True, + desc='The input reference mask', + argstr='-rmask %s') + # Input floating mask + fmask_file = File(exists=True, + desc='The input floating mask', + argstr='-fmask %s') + # Maximum number of iterations + maxit_val = traits.Range(desc='Maximum number of iterations', + argstr='-maxit %d', low=0) + # Multiresolution levels + ln_val = traits.Range(desc='Number of resolution levels to create', + argstr='-ln %d', low=0) + # Number of resolution levels to process + lp_val = traits.Range(desc='Number of resolution levels to perform', + argstr='-lp %d', low=0) + # Smoothing to apply on reference image + desc = 'Amount of smoothing to apply to reference image' + smoo_r_val = traits.Float(desc=desc, + argstr='-smooR %f') + # Smoothing to apply on floating image + desc = 'Amount of smoothing to apply to floating image' + smoo_f_val = traits.Float(desc=desc, + argstr='-smooF %f') + # Use nifti header to initialise transformation + desc = 'Use nifti header to initialise transformation' + nac_flag = traits.Bool(desc=desc, + argstr='-nac') + # Use the input masks centre of mass to initialise the transformation + desc = 'Use the masks centre of mass to initialise the transformation' + cog_flag = traits.Bool(desc=desc, + argstr='-cog') + # Percent of blocks that are considered active. + v_val = traits.Range(desc='Percent of blocks that are active', + argstr='-pv %d', low=0) + # Percent of inlier blocks + i_val = traits.Range(desc='Percent of inlier blocks', argstr='-pi %d', + low=0) + # Lower threshold on reference image + ref_low_val = traits.Float(desc='Lower threshold value on reference image', + argstr='-refLowThr %f') + # Upper threshold on reference image + ref_up_val = traits.Float(desc='Upper threshold value on reference image', + argstr='-refUpThr %f') + # Lower threshold on floating image + flo_low_val = traits.Float(desc='Lower threshold value on floating image', + argstr='-floLowThr %f') + # Upper threshold on floating image + flo_up_val = traits.Float(desc='Upper threshold value on floating image', + argstr='-floUpThr %f') + # Platform to use + platform_val = traits.Int(desc='Platform index', + argstr='-platf %i') + # Platform to use + gpuid_val = traits.Int(desc='Device to use id', + argstr='-gpuid %i') + # Verbosity off + verbosity_off_flag = traits.Bool(argstr='-voff', + desc='Turn off verbose output') + + # Affine output transformation matrix file + aff_file = File(name_source=['flo_file'], + name_template='%s_aff.txt', + desc='The output affine matrix file', + argstr='-aff %s') + # Result warped image file + res_file = File(name_source=['flo_file'], + name_template='%s_res.nii.gz', + desc='The affine transformed floating image', + argstr='-res %s') + + +class RegAladinOutputSpec(TraitedSpec): + """ Output Spec for RegAladin. """ + aff_file = File(desc='The output affine file') + res_file = File(desc='The output transformed image') + desc = 'Output string in the format for reg_average' + avg_output = traits.String(desc=desc) + + +class RegAladin(NiftyRegCommand): + """Interface for executable reg_aladin from NiftyReg platform. + + Block Matching algorithm for symmetric global registration. + Based on Modat et al., "Global image registration using + asymmetric block-matching approach" + J. Med. Img. 1(2) 024003, 2014, doi: 10.1117/1.JMI.1.2.024003 + + For source code, see https://cmiclab.cs.ucl.ac.uk/mmodat/niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegAladin() + >>> node.inputs.ref_file = 'im1.nii' + >>> node.inputs.flo_file = 'im2.nii' + >>> node.inputs.rmask_file = 'mask.nii' + >>> node.inputs.omp_core_val = 4 + >>> node.cmdline # doctest: +ALLOW_UNICODE + 'reg_aladin -aff im2_aff.txt -flo im2.nii -omp 4 -ref im1.nii \ +-res im2_res.nii.gz -rmask mask.nii' + + """ + _cmd = get_custom_path('reg_aladin') + input_spec = RegAladinInputSpec + output_spec = RegAladinOutputSpec + + def _list_outputs(self): + outputs = super(RegAladin, self)._list_outputs() + + # Make a list of the linear transformation file and the input image + aff = os.path.abspath(outputs['aff_file']) + flo = os.path.abspath(self.inputs.flo_file) + outputs['avg_output'] = '%s %s' % (aff, flo) + return outputs + + +class RegF3DInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegF3D. """ + # Input reference file + ref_file = File(exists=True, + desc='The input reference/target image', + argstr='-ref %s', + mandatory=True) + # Input floating file + flo_file = File(exists=True, + desc='The input floating/source image', + argstr='-flo %s', + mandatory=True) + + # Input Affine file + aff_file = File(exists=True, + desc='The input affine transformation file', + argstr='-aff %s') + + # Input cpp file + incpp_file = File(exists=True, + desc='The input cpp transformation file', + argstr='-incpp %s') + + # Reference mask + rmask_file = File(exists=True, + desc='Reference image mask', + argstr='-rmask %s') + + # Smoothing kernel for reference + desc = 'Smoothing kernel width for reference image' + ref_smooth_val = traits.Float(desc=desc, argstr='-smooR %f') + # Smoothing kernel for floating + desc = 'Smoothing kernel width for floating image' + flo_smooth_val = traits.Float(desc=desc, argstr='-smooF %f') + + # Lower threshold for reference image + rlwth_thr_val = traits.Float(desc='Lower threshold for reference image', + argstr='--rLwTh %f') + # Upper threshold for reference image + rupth_thr_val = traits.Float(desc='Upper threshold for reference image', + argstr='--rUpTh %f') + # Lower threshold for reference image + flwth_thr_val = traits.Float(desc='Lower threshold for floating image', + argstr='--fLwTh %f') + # Upper threshold for reference image + fupth_thr_val = traits.Float(desc='Upper threshold for floating image', + argstr='--fUpTh %f') + + # Lower threshold for reference image + desc = 'Lower threshold for reference image at the specified time point' + rlwth2_thr_val = traits.Tuple(traits.Range(low=0), traits.Float, + desc=desc, argstr='-rLwTh %d %f') + # Upper threshold for reference image + desc = 'Upper threshold for reference image at the specified time point' + rupth2_thr_val = traits.Tuple(traits.Range(low=0), traits.Float, + desc=desc, argstr='-rUpTh %d %f') + # Lower threshold for reference image + desc = 'Lower threshold for floating image at the specified time point' + flwth2_thr_val = traits.Tuple(traits.Range(low=0), traits.Float, + desc=desc, argstr='-fLwTh %d %f') + # Upper threshold for reference image + desc = 'Upper threshold for floating image at the specified time point' + fupth2_thr_val = traits.Tuple(traits.Range(low=0), traits.Float, + desc=desc, argstr='-fUpTh %d %f') + + # Final grid spacing along the 3 axes + sx_val = traits.Float(desc='Final grid spacing along the x axes', + argstr='-sx %f') + sy_val = traits.Float(desc='Final grid spacing along the y axes', + argstr='-sy %f') + sz_val = traits.Float(desc='Final grid spacing along the z axes', + argstr='-sz %f') + + # Regularisation options + be_val = traits.Float(desc='Bending energy value', argstr='-be %f') + le_val = traits.Float(desc='Linear elasticity penalty term', + argstr='-le %f') + jl_val = traits.Float(desc='Log of jacobian of deformation penalty value', + argstr='-jl %f') + desc = 'Do not approximate the log of jacobian penalty at control points \ +only' + no_app_jl_flag = traits.Bool(argstr='-noAppJL', desc=desc) + + # Similarity measure options + desc = 'use NMI even when other options are specified' + nmi_flag = traits.Bool(argstr='--nmi', desc=desc) + desc = 'Number of bins in the histogram for reference image' + rbn_val = traits.Range(low=0, desc=desc, argstr='--rbn %d') + desc = 'Number of bins in the histogram for reference image' + fbn_val = traits.Range(low=0, desc=desc, argstr='--fbn %d') + desc = 'Number of bins in the histogram for reference image for given \ +time point' + rbn2_val = traits.Tuple(traits.Range(low=0), traits.Range(low=0), + desc=desc, argstr='-rbn %d %d') + + desc = 'Number of bins in the histogram for reference image for given \ +time point' + fbn2_val = traits.Tuple(traits.Range(low=0), traits.Range(low=0), + desc=desc, argstr='-fbn %d %d') + + lncc_val = traits.Float(desc='SD of the Gaussian for computing LNCC', + argstr='--lncc %f') + desc = 'SD of the Gaussian for computing LNCC for a given time point' + lncc2_val = traits.Tuple(traits.Range(low=0), traits.Float, + desc=desc, argstr='-lncc %d %f') + + ssd_flag = traits.Bool(desc='Use SSD as the similarity measure', + argstr='--ssd') + desc = 'Use SSD as the similarity measure for a given time point' + ssd2_flag = traits.Range(low=0, desc=desc, argstr='-ssd %d') + kld_flag = traits.Bool(desc='Use KL divergence as the similarity measure', + argstr='--kld') + desc = 'Use KL divergence as the similarity measure for a given time point' + kld2_flag = traits.Range(low=0, desc=desc, argstr='-kld %d') + amc_flag = traits.Bool(desc='Use additive NMI', argstr='-amc') + + nox_flag = traits.Bool(desc="Don't optimise in x direction", + argstr='-nox') + noy_flag = traits.Bool(desc="Don't optimise in y direction", + argstr='-noy') + noz_flag = traits.Bool(desc="Don't optimise in z direction", + argstr='-noz') + + # Optimization options + maxit_val = traits.Range(low=0, argstr='-maxit %d', + desc='Maximum number of iterations per level') + ln_val = traits.Range(low=0, argstr='-ln %d', + desc='Number of resolution levels to create') + lp_val = traits.Range(low=0, argstr='-lp %d', + desc='Number of resolution levels to perform') + nopy_flag = traits.Bool(desc='Do not use the multiresolution approach', + argstr='-nopy') + noconj_flag = traits.Bool(desc='Use simple GD optimization', + argstr='-noConj') + desc = 'Add perturbation steps after each optimization step' + pert_val = traits.Range(low=0, desc=desc, argstr='-pert %d') + + # F3d2 options + vel_flag = traits.Bool(desc='Use velocity field integration', + argstr='-vel') + fmask_file = File(exists=True, + desc='Floating image mask', + argstr='-fmask %s') + + # Other options + desc = 'Kernel width for smoothing the metric gradient' + smooth_grad_val = traits.Float(desc=desc, argstr='-smoothGrad %f') + # Padding value + pad_val = traits.Float(desc='Padding value', argstr='-pad %f') + # verbosity off + verbosity_off_flag = traits.Bool(argstr='-voff', + desc='Turn off verbose output') + + # Output CPP image file + cpp_file = File(name_source=['flo_file'], + name_template='%s_cpp.nii.gz', + desc='The output CPP file', + argstr='-cpp %s') + # Output warped image file + res_file = File(name_source=['flo_file'], + name_template='%s_res.nii.gz', + desc='The output resampled image', + argstr='-res %s') + + +class RegF3DOutputSpec(TraitedSpec): + """ Output Spec for RegF3D. """ + cpp_file = File(desc='The output CPP file') + res_file = File(desc='The output resampled image') + invcpp_file = File(desc='The output inverse CPP file') + invres_file = File(desc='The output inverse res file') + desc = 'Output string in the format for reg_average' + avg_output = traits.String(desc=desc) + + +class RegF3D(NiftyRegCommand): + """Interface for executable reg_f3d from NiftyReg platform. + + Fast Free-Form Deformation (F3D) algorithm for non-rigid registration. + Initially based on Modat et al., "Fast Free-Form Deformation using + graphics processing units", CMPB, 2010 + + For source code, see https://cmiclab.cs.ucl.ac.uk/mmodat/niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegF3D() + >>> node.inputs.ref_file = 'im1.nii' + >>> node.inputs.flo_file = 'im2.nii' + >>> node.inputs.rmask_file = 'mask.nii' + >>> node.inputs.omp_core_val = 4 + >>> node.cmdline # doctest: +ALLOW_UNICODE + 'reg_f3d -cpp im2_cpp.nii.gz -flo im2.nii -omp 4 -ref im1.nii \ +-res im2_res.nii.gz -rmask mask.nii' + + """ + _cmd = get_custom_path('reg_f3d') + input_spec = RegF3DInputSpec + output_spec = RegF3DOutputSpec + + @staticmethod + def _remove_extension(in_file): + dn, bn, _ = split_filename(in_file) + return os.path.join(dn, bn) + + def _list_outputs(self): + outputs = super(RegF3D, self)._list_outputs() + + if self.inputs.vel_flag is True: + res_name = self._remove_extension(outputs['res_file']) + cpp_name = self._remove_extension(outputs['cpp_file']) + outputs['invres_file'] = '%s_backward.nii.gz' % res_name + outputs['invcpp_file'] = '%s_backward.nii.gz' % cpp_name + + # Make a list of the linear transformation file and the input image + if self.inputs.vel_flag is True and isdefined(self.inputs.aff_file): + cpp_file = os.path.abspath(outputs['cpp_file']) + flo_file = os.path.abspath(self.inputs.flo_file) + outputs['avg_output'] = '%s %s %s' % (self.inputs.aff_file, + cpp_file, flo_file) + else: + cpp_file = os.path.abspath(outputs['cpp_file']) + flo_file = os.path.abspath(self.inputs.flo_file) + outputs['avg_output'] = '%s %s' % (cpp_file, flo_file) + + return outputs diff --git a/nipype/interfaces/niftyreg/regutils.py b/nipype/interfaces/niftyreg/regutils.py new file mode 100644 index 0000000000..14dc0592cf --- /dev/null +++ b/nipype/interfaces/niftyreg/regutils.py @@ -0,0 +1,718 @@ +# -*- coding: utf-8 -*- +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +"""The regutils module provides classes for interfacing with the `niftyreg +`_ utility command line tools. + +The interfaces were written to work with niftyreg version 1.5.10 + +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, division, unicode_literals, + absolute_import) +from builtins import len, open, property, super +import warnings +import os + +from ..base import TraitedSpec, File, traits, isdefined +from .base import get_custom_path, NiftyRegCommand, NiftyRegCommandInputSpec +from ...utils.filemanip import split_filename + + +warn = warnings.warn +warnings.filterwarnings('always', category=UserWarning) + + +class RegResampleInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegResample. """ + # Input reference file + ref_file = File(exists=True, + desc='The input reference/target image', + argstr='-ref %s', + mandatory=True) + # Input floating file + flo_file = File(exists=True, + desc='The input floating/source image', + argstr='-flo %s', + mandatory=True) + # Input deformation field + trans_file = File(exists=True, + desc='The input transformation file', + argstr='-trans %s') + + type = traits.Enum('res', 'blank', + argstr='-%s', + position=-2, + usedefault=True, + desc='Type of output') + + # Output file name + out_file = File(name_source=['flo_file'], + name_template='%s', + argstr='%s', + position=-1, + desc='The output filename of the transformed image') + + # Interpolation type + inter_val = traits.Enum('NN', 'LIN', 'CUB', 'SINC', + desc='Interpolation type', + argstr='-inter %d') + + # Padding value + pad_val = traits.Float(desc='Padding value', argstr='-pad %f') + + # Tensor flag + tensor_flag = traits.Bool(desc='Resample Tensor Map', argstr='-tensor ') + + # Verbosity off + verbosity_off_flag = traits.Bool(argstr='-voff', + desc='Turn off verbose output') + # PSF flag + desc = 'Perform the resampling in two steps to resample an image to a \ +lower resolution' + psf_flag = traits.Bool(argstr='-psf', desc=desc) + desc = 'Minimise the matrix metric (0) or the determinant (1) when \ +estimating the PSF [0]' + psf_alg = traits.Enum(0, 1, argstr='-psf_alg %d', desc=desc) + + +class RegResampleOutputSpec(TraitedSpec): + """ Output Spec for RegResample. """ + out_file = File(desc='The output filename of the transformed image') + + +class RegResample(NiftyRegCommand): + """Interface for executable reg_resample from NiftyReg platform. + + Tool to resample floating image in the space of a defined reference image + given a transformation parametrisation generated by reg_aladin, reg_f3d or + reg_transform + + For complete documentation, see https://cmiclab.cs.ucl.ac.uk/mmodat/\ + niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegResample() + >>> node.inputs.ref_file = 'im1.nii' + >>> node.inputs.flo_file = 'im2.nii' + >>> node.inputs.trans_file = 'warpfield.nii' + >>> node.inputs.inter_val = 'LIN' + >>> node.inputs.omp_core_val = 4 + >>> node.cmdline # doctest: +ALLOW_UNICODE + 'reg_resample -flo im2.nii -inter 1 -omp 4 -ref im1.nii -trans \ +warpfield.nii -res im2_res.nii.gz' + + """ + _cmd = get_custom_path('reg_resample') + input_spec = RegResampleInputSpec + output_spec = RegResampleOutputSpec + + # Need this overload to properly constraint the interpolation type input + def _format_arg(self, name, spec, value): + if name == 'inter_val': + inter_val = {'NN': 0, 'LIN': 1, 'CUB': 3, 'SINC': 5} + return spec.argstr % inter_val[value] + else: + return super(RegResample, self)._format_arg(name, spec, value) + + def _overload_extension(self, value, name=None): + path, base, _ = split_filename(value) + suffix = self.inputs.type + return os.path.join(path, '{0}_{1}.nii.gz'.format(base, suffix)) + + +class RegJacobianInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegJacobian. """ + # Reference file name + desc = 'Reference/target file (required if specifying CPP transformations.' + ref_file = File(exists=True, + desc=desc, + argstr='-ref %s') + # Input transformation file + trans_file = File(exists=True, + desc='The input non-rigid transformation', + argstr='-trans %s', + mandatory=True) + type = traits.Enum('jac', 'jacL', 'jacM', + usedefault=True, + argstr='-%s', + position=-2, + desc='Type of jacobian outcome') + out_file = File(name_source=['trans_file'], + name_template='%s', + desc='The output jacobian determinant file name', + argstr='%s', + position=-1) + + +class RegJacobianOutputSpec(TraitedSpec): + """ Output Spec for RegJacobian. """ + out_file = File(desc='The output file') + + +class RegJacobian(NiftyRegCommand): + """Interface for executable reg_resample from NiftyReg platform. + + Tool to generate Jacobian determinant maps from transformation + parametrisation generated by reg_f3d + + For source code, see https://cmiclab.cs.ucl.ac.uk/mmodat/niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegJacobian() + >>> node.inputs.ref_file = 'im1.nii' + >>> node.inputs.trans_file = 'warpfield.nii' + >>> node.inputs.omp_core_val = 4 + >>> node.cmdline # doctest: +ALLOW_UNICODE + 'reg_jacobian -omp 4 -ref im1.nii -trans warpfield.nii -jac \ +warpfield_jac.nii.gz' + + """ + _cmd = get_custom_path('reg_jacobian') + input_spec = RegJacobianInputSpec + output_spec = RegJacobianOutputSpec + + def _overload_extension(self, value, name=None): + path, base, _ = split_filename(value) + suffix = self.inputs.type + return os.path.join(path, '{0}_{1}.nii.gz'.format(base, suffix)) + + +class RegToolsInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegTools. """ + # Input image file + in_file = File(exists=True, + desc='The input image file path', + argstr='-in %s', + mandatory=True) + + # Output file path + out_file = File(name_source=['in_file'], + name_template='%s_tools.nii.gz', + desc='The output file name', + argstr='-out %s') + + # Make the output image isotropic + iso_flag = traits.Bool(argstr='-iso', desc='Make output image isotropic') + + # Set scale, slope to 0 and 1. + noscl_flag = traits.Bool(argstr='-noscl', + desc='Set scale, slope to 0 and 1') + + # Values outside the mask are set to NaN + mask_file = File(exists=True, + desc='Values outside the mask are set to NaN', + argstr='-nan %s') + + # Threshold the input image + desc = 'Binarise the input image with the given threshold' + thr_val = traits.Float(desc=desc, argstr='-thr %f') + + # Binarise the input image + bin_flag = traits.Bool(argstr='-bin', desc='Binarise the input image') + + # Compute the mean RMS between the two images + rms_val = File(exists=True, + desc='Compute the mean RMS between the images', + argstr='-rms %s') + + # Perform division by image or value + div_val = traits.Either(traits.Float, File(exists=True), + desc='Divide the input by image or value', + argstr='-div %s') + + # Perform multiplication by image or value + mul_val = traits.Either(traits.Float, File(exists=True), + desc='Multiply the input by image or value', + argstr='-mul %s') + + # Perform addition by image or value + add_val = traits.Either(traits.Float, File(exists=True), + desc='Add to the input image or value', + argstr='-add %s') + + # Perform subtraction by image or value + sub_val = traits.Either(traits.Float, File(exists=True), + desc='Add to the input image or value', + argstr='-sub %s') + + # Downsample the image by a factor of 2. + down_flag = traits.Bool(desc='Downsample the image by a factor of 2', + argstr='-down') + + # Smoothing using spline kernel + desc = 'Smooth the input image using a cubic spline kernel' + smo_s_val = traits.Tuple(traits.Float, traits.Float, traits.Float, + desc=desc, + argstr='-smoS %f %f %f') + + # Change the resolution of the input image + chg_res_val = traits.Tuple(traits.Float, traits.Float, traits.Float, + desc='Change the resolution of the input image', + argstr='-chgres %f %f %f') + + # Smoothing using Gaussian kernel + desc = 'Smooth the input image using a Gaussian kernel' + smo_g_val = traits.Tuple(traits.Float, traits.Float, traits.Float, + desc=desc, + argstr='-smoG %f %f %f') + + +class RegToolsOutputSpec(TraitedSpec): + """ Output Spec for RegTools. """ + out_file = File(desc='The output file', exists=True) + + +class RegTools(NiftyRegCommand): + """Interface for executable reg_tools from NiftyReg platform. + + Tool delivering various actions related to registration such as + resampling the input image to a chosen resolution or remove the nan and + inf in the input image by a specified value. + + For source code, see https://cmiclab.cs.ucl.ac.uk/mmodat/niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegTools() + >>> node.inputs.in_file = 'im1.nii' + >>> node.inputs.mul_val = 4 + >>> node.inputs.omp_core_val = 4 + >>> node.cmdline # doctest: +ALLOW_UNICODE + 'reg_tools -in im1.nii -mul 4.0 -omp 4 -out im1_tools.nii.gz' + + """ + _cmd = get_custom_path('reg_tools') + input_spec = RegToolsInputSpec + output_spec = RegToolsOutputSpec + _suffix = '_tools' + + +class RegAverageInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegAverage. """ + avg_files = traits.List(File(exist=True), + position=1, + argstr='-avg %s', + sep=' ', + xor=['avg_lts_files', 'avg_ref_file', + 'demean1_ref_file', 'demean2_ref_file', + 'demean3_ref_file', 'warp_files'], + desc='Averaging of images/affine transformations') + + desc = 'Robust average of affine transformations' + avg_lts_files = traits.List(File(exist=True), + position=1, + argstr='-avg_lts %s', + sep=' ', + xor=['avg_files', 'avg_ref_file', + 'demean1_ref_file', 'demean2_ref_file', + 'demean3_ref_file', 'warp_files'], + desc=desc) + + desc = 'All input images are resampled into the space of \ + and averaged. A cubic spline interpolation scheme is used for resampling' + avg_ref_file = File(position=1, + argstr='-avg_tran %s', + xor=['avg_files', 'avg_lts_files', 'demean1_ref_file', + 'demean2_ref_file', 'demean3_ref_file'], + requires=['warp_files'], + desc=desc) + + desc = 'Average images and demean average image that have affine \ +transformations to a common space' + demean1_ref_file = File(position=1, + argstr='-demean1 %s', + xor=['avg_files', 'avg_lts_files', 'avg_ref_file', + 'demean2_ref_file', 'demean3_ref_file'], + requires=['warp_files'], + desc=desc) + + desc = 'Average images and demean average image that have non-rigid \ +transformations to a common space' + demean2_ref_file = File(position=1, + argstr='-demean2 %s', + xor=['avg_files', 'avg_lts_files', 'avg_ref_file', + 'demean1_ref_file', 'demean3_ref_file'], + requires=['warp_files'], + desc=desc) + + desc = 'Average images and demean average image that have linear and \ +non-rigid transformations to a common space' + demean3_ref_file = File(position=1, + argstr='-demean3 %s', + xor=['avg_files', 'avg_lts_files', 'avg_ref_file', + 'demean1_ref_file', 'demean2_ref_file'], + requires=['warp_files'], + desc=desc) + + desc = 'transformation files and floating image pairs/triplets to the \ +reference space' + warp_files = traits.List(File(exist=True), + position=-1, + argstr='%s', + sep=' ', + xor=['avg_files', 'avg_lts_files'], + desc=desc) + + out_file = File(genfile=True, + position=0, + desc='Output file name', + argstr='%s') + + +class RegAverageOutputSpec(TraitedSpec): + """ Output Spec for RegAverage. """ + out_file = File(desc='Output file name') + + +class RegAverage(NiftyRegCommand): + """Interface for executable reg_average from NiftyReg platform. + + Compute average matrix or image from a list of matrices or image. + The tool can be use to resample images given input transformation + parametrisation as well as to demean transformations in Euclidean or + log-Euclidean space. + + This interface is different than the others in the way that the options + will be written in a command file that is given as a parameter. + + For source code, see https://cmiclab.cs.ucl.ac.uk/mmodat/niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegAverage() + >>> one_file = 'im1.nii' + >>> two_file = 'im2.nii' + >>> three_file = 'im3.nii' + >>> node.inputs.avg_files = [one_file, two_file, three_file] + >>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE + 'reg_average --cmd_file .../reg_average_cmd' + + """ + _cmd = get_custom_path('reg_average') + input_spec = RegAverageInputSpec + output_spec = RegAverageOutputSpec + _suffix = 'avg_out' + + def _gen_filename(self, name): + if name == 'out_file': + if isdefined(self.inputs.avg_lts_files): + return self._gen_fname(self._suffix, ext='.txt') + elif isdefined(self.inputs.avg_files): + _, _, _ext = split_filename(self.inputs.avg_files[0]) + if _ext not in ['.nii', '.nii.gz', '.hdr', '.img', '.img.gz']: + return self._gen_fname(self._suffix, ext=_ext) + return self._gen_fname(self._suffix, ext='.nii.gz') + + return None + + @property + def cmdline(self): + """ Rewrite the cmdline to write options in text_file.""" + argv = super(RegAverage, self).cmdline + reg_average_cmd = os.path.join(os.getcwd(), 'reg_average_cmd') + with open(reg_average_cmd, 'w') as f: + f.write(argv) + return '%s --cmd_file %s' % (self.cmd, reg_average_cmd) + + +class RegTransformInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegTransform. """ + ref1_file = File(exists=True, + desc='The input reference/target image', + argstr='-ref %s', + position=0) + + ref2_file = File(exists=True, + desc='The input second reference/target image', + argstr='-ref2 %s', + position=1, + requires=['ref1_file']) + + def_input = File(exists=True, + argstr='-def %s', + position=-2, + desc='Compute deformation field from transformation', + xor=['disp_input', 'flow_input', 'comp_input', + 'upd_s_form_input', 'inv_aff_input', + 'inv_nrr_input', 'half_input', 'make_aff_input', + 'aff_2_rig_input', 'flirt_2_nr_input']) + + disp_input = File(exists=True, + argstr='-disp %s', + position=-2, + desc='Compute displacement field from transformation', + xor=['def_input', 'flow_input', 'comp_input', + 'upd_s_form_input', 'inv_aff_input', + 'inv_nrr_input', 'half_input', 'make_aff_input', + 'aff_2_rig_input', 'flirt_2_nr_input']) + + flow_input = File(exists=True, + argstr='-flow %s', + position=-2, + desc='Compute flow field from spline SVF', + xor=['def_input', 'disp_input', 'comp_input', + 'upd_s_form_input', 'inv_aff_input', + 'inv_nrr_input', 'half_input', 'make_aff_input', + 'aff_2_rig_input', 'flirt_2_nr_input']) + + comp_input = File(exists=True, + argstr='-comp %s', + position=-3, + desc='compose two transformations', + xor=['def_input', 'disp_input', 'flow_input', + 'upd_s_form_input', 'inv_aff_input', + 'inv_nrr_input', 'half_input', 'make_aff_input', + 'aff_2_rig_input', 'flirt_2_nr_input'], + requires=['comp_input2']) + + comp_input2 = File(exists=True, + argstr='%s', + position=-2, + desc='compose two transformations') + + desc = 'Update s-form using the affine transformation' + upd_s_form_input = File(exists=True, + argstr='-updSform %s', + position=-3, + desc=desc, + xor=['def_input', 'disp_input', 'flow_input', + 'comp_input', 'inv_aff_input', + 'inv_nrr_input', 'half_input', + 'make_aff_input', 'aff_2_rig_input', + 'flirt_2_nr_input'], + requires=['upd_s_form_input2']) + + desc = 'Update s-form using the affine transformation' + upd_s_form_input2 = File(exists=True, + argstr='%s', + position=-2, + desc=desc, + requires=['upd_s_form_input']) + + inv_aff_input = File(exists=True, + argstr='-invAff %s', + position=-2, + desc='Invert an affine transformation', + xor=['def_input', 'disp_input', 'flow_input', + 'comp_input', 'upd_s_form_input', + 'inv_nrr_input', 'half_input', 'make_aff_input', + 'aff_2_rig_input', 'flirt_2_nr_input']) + + inv_nrr_input = traits.Tuple(File(exists=True), File(exists=True), + desc='Invert a non-linear transformation', + argstr='-invNrr %s %s', + position=-2, + xor=['def_input', 'disp_input', 'flow_input', + 'comp_input', 'upd_s_form_input', + 'inv_aff_input', 'half_input', + 'make_aff_input', 'aff_2_rig_input', + 'flirt_2_nr_input']) + + half_input = File(exists=True, + argstr='-half %s', + position=-2, + desc='Half way to the input transformation', + xor=['def_input', 'disp_input', 'flow_input', + 'comp_input', 'upd_s_form_input', + 'inv_aff_input', 'inv_nrr_input', 'make_aff_input', + 'aff_2_rig_input', 'flirt_2_nr_input']) + + argstr_tmp = '-makeAff %f %f %f %f %f %f %f %f %f %f %f %f' + make_aff_input = traits.Tuple(traits.Float, traits.Float, traits.Float, + traits.Float, traits.Float, traits.Float, + traits.Float, traits.Float, traits.Float, + traits.Float, traits.Float, traits.Float, + argstr=argstr_tmp, + position=-2, + desc='Make an affine transformation matrix', + xor=['def_input', 'disp_input', 'flow_input', + 'comp_input', 'upd_s_form_input', + 'inv_aff_input', 'inv_nrr_input', + 'half_input', 'aff_2_rig_input', + 'flirt_2_nr_input']) + + desc = 'Extract the rigid component from affine transformation' + aff_2_rig_input = File(exists=True, + argstr='-aff2rig %s', + position=-2, + desc=desc, + xor=['def_input', 'disp_input', 'flow_input', + 'comp_input', 'upd_s_form_input', + 'inv_aff_input', 'inv_nrr_input', 'half_input', + 'make_aff_input', 'flirt_2_nr_input']) + + desc = 'Convert a FLIRT affine transformation to niftyreg affine \ +transformation' + flirt_2_nr_input = traits.Tuple(File(exists=True), File(exists=True), + File(exists=True), + argstr='-flirtAff2NR %s %s %s', + position=-2, + desc=desc, + xor=['def_input', 'disp_input', + 'flow_input', 'comp_input', + 'upd_s_form_input', 'inv_aff_input', + 'inv_nrr_input', 'half_input', + 'make_aff_input', 'aff_2_rig_input']) + + out_file = File(genfile=True, + position=-1, + argstr='%s', + desc='transformation file to write') + + +class RegTransformOutputSpec(TraitedSpec): + """ Output Spec for RegTransform. """ + out_file = File(desc='Output File (transformation in any format)') + + +class RegTransform(NiftyRegCommand): + """Interface for executable reg_transform from NiftyReg platform. + + Tools to convert transformation parametrisation from one type to another + as well as to compose, inverse or half transformations. + + For source code, see https://cmiclab.cs.ucl.ac.uk/mmodat/niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegTransform() + >>> node.inputs.def_input = 'warpfield.nii' + >>> node.inputs.omp_core_val = 4 + >>> node.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE + 'reg_transform -omp 4 -def warpfield.nii .../warpfield_trans.nii.gz' + + """ + _cmd = get_custom_path('reg_transform') + input_spec = RegTransformInputSpec + output_spec = RegTransformOutputSpec + _suffix = '_trans' + + def _find_input(self): + inputs = [self.inputs.def_input, self.inputs.disp_input, + self.inputs.flow_input, self.inputs.comp_input, + self.inputs.comp_input2, self.inputs.upd_s_form_input, + self.inputs.inv_aff_input, self.inputs.inv_nrr_input, + self.inputs.half_input, self.inputs.make_aff_input, + self.inputs.aff_2_rig_input, self.inputs.flirt_2_nr_input] + entries = [] + for entry in inputs: + if isdefined(entry): + entries.append(entry) + _, _, ext = split_filename(entry) + if ext == '.nii' or ext == '.nii.gz' or ext == '.hdr': + return entry + if len(entries): + return entries[0] + return None + + def _gen_filename(self, name): + if name == 'out_file': + if isdefined(self.inputs.make_aff_input): + return self._gen_fname('matrix', suffix=self._suffix, + ext='.txt') + + if isdefined(self.inputs.comp_input) and \ + isdefined(self.inputs.comp_input2): + _, bn1, ext1 = split_filename(self.inputs.comp_input) + _, _, ext2 = split_filename(self.inputs.comp_input2) + if ext1 in ['.nii', '.nii.gz', '.hdr', '.img', '.img.gz'] or \ + ext2 in ['.nii', '.nii.gz', '.hdr', '.img', '.img.gz']: + return self._gen_fname(bn1, suffix=self._suffix, + ext='.nii.gz') + else: + return self._gen_fname(bn1, suffix=self._suffix, ext=ext1) + + if isdefined(self.inputs.flirt_2_nr_input): + return self._gen_fname(self.inputs.flirt_2_nr_input[0], + suffix=self._suffix, ext='.txt') + + input_to_use = self._find_input() + _, _, ext = split_filename(input_to_use) + if ext not in ['.nii', '.nii.gz', '.hdr', '.img', '.img.gz']: + return self._gen_fname(input_to_use, suffix=self._suffix, + ext=ext) + else: + return self._gen_fname(input_to_use, suffix=self._suffix, + ext='.nii.gz') + + return None + + def _list_outputs(self): + outputs = self.output_spec().get() + + if isdefined(self.inputs.out_file): + outputs['out_file'] = self.inputs.out_file + else: + outputs['out_file'] = self._gen_filename('out_file') + + return outputs + + +class RegMeasureInputSpec(NiftyRegCommandInputSpec): + """ Input Spec for RegMeasure. """ + # Input reference file + ref_file = File(exists=True, + desc='The input reference/target image', + argstr='-ref %s', + mandatory=True) + # Input floating file + flo_file = File(exists=True, + desc='The input floating/source image', + argstr='-flo %s', + mandatory=True) + measure_type = traits.Enum('ncc', 'lncc', 'nmi', 'ssd', + mandatory=True, + argstr='-%s', + desc='Measure of similarity to compute') + out_file = File(name_source=['flo_file'], + name_template='%s', + argstr='-out %s', + desc='The output text file containing the measure') + + +class RegMeasureOutputSpec(TraitedSpec): + """ Output Spec for RegMeasure. """ + out_file = File(desc='The output text file containing the measure') + + +class RegMeasure(NiftyRegCommand): + """Interface for executable reg_measure from NiftyReg platform. + + Given two input images, compute the specified measure(s) of similarity + + For source code, see https://cmiclab.cs.ucl.ac.uk/mmodat/niftyreg + + Examples + -------- + >>> from nipype.interfaces import niftyreg + >>> node = niftyreg.RegMeasure() + >>> node.inputs.ref_file = 'im1.nii' + >>> node.inputs.flo_file = 'im2.nii' + >>> node.inputs.measure_type = 'lncc' + >>> node.inputs.omp_core_val = 4 + >>> node.cmdline # doctest: +ALLOW_UNICODE + 'reg_measure -flo im2.nii -lncc -omp 4 -out im2_lncc.txt -ref im1.nii' + + """ + _cmd = get_custom_path('reg_measure') + input_spec = RegMeasureInputSpec + output_spec = RegMeasureOutputSpec + + def _overload_extension(self, value, name=None): + path, base, _ = split_filename(value) + suffix = self.inputs.measure_type + return os.path.join(path, '{0}_{1}.txt'.format(base, suffix)) diff --git a/nipype/interfaces/niftyreg/tests/__init__.py b/nipype/interfaces/niftyreg/tests/__init__.py new file mode 100644 index 0000000000..40a96afc6f --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/nipype/interfaces/niftyreg/tests/test_auto_NiftyRegCommand.py b/nipype/interfaces/niftyreg/tests/test_auto_NiftyRegCommand.py new file mode 100644 index 0000000000..c231aa52ac --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_NiftyRegCommand.py @@ -0,0 +1,23 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..base import NiftyRegCommand + + +def test_NiftyRegCommand_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 = NiftyRegCommand.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/niftyreg/tests/test_auto_RegAladin.py b/nipype/interfaces/niftyreg/tests/test_auto_RegAladin.py new file mode 100644 index 0000000000..6865be7e4c --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegAladin.py @@ -0,0 +1,94 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..reg import RegAladin + + +def test_RegAladin_inputs(): + input_map = dict(aff_direct_flag=dict(argstr='-affDirect', + ), + aff_file=dict(argstr='-aff %s', + name_source=['flo_file'], + name_template='%s_aff.txt', + ), + args=dict(argstr='%s', + ), + cog_flag=dict(argstr='-cog', + ), + environ=dict(nohash=True, + usedefault=True, + ), + flo_file=dict(argstr='-flo %s', + mandatory=True, + ), + flo_low_val=dict(argstr='-floLowThr %f', + ), + flo_up_val=dict(argstr='-floUpThr %f', + ), + fmask_file=dict(argstr='-fmask %s', + ), + gpuid_val=dict(argstr='-gpuid %i', + ), + i_val=dict(argstr='-pi %d', + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_aff_file=dict(argstr='-inaff %s', + ), + ln_val=dict(argstr='-ln %d', + ), + lp_val=dict(argstr='-lp %d', + ), + maxit_val=dict(argstr='-maxit %d', + ), + nac_flag=dict(argstr='-nac', + ), + nosym_flag=dict(argstr='-noSym', + ), + omp_core_val=dict(argstr='-omp %i', + ), + platform_val=dict(argstr='-platf %i', + ), + ref_file=dict(argstr='-ref %s', + mandatory=True, + ), + ref_low_val=dict(argstr='-refLowThr %f', + ), + ref_up_val=dict(argstr='-refUpThr %f', + ), + res_file=dict(argstr='-res %s', + name_source=['flo_file'], + name_template='%s_res.nii.gz', + ), + rig_only_flag=dict(argstr='-rigOnly', + ), + rmask_file=dict(argstr='-rmask %s', + ), + smoo_f_val=dict(argstr='-smooF %f', + ), + smoo_r_val=dict(argstr='-smooR %f', + ), + terminal_output=dict(nohash=True, + ), + v_val=dict(argstr='-pv %d', + ), + verbosity_off_flag=dict(argstr='-voff', + ), + ) + inputs = RegAladin.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_RegAladin_outputs(): + output_map = dict(aff_file=dict(), + avg_output=dict(), + res_file=dict(), + ) + outputs = RegAladin.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/niftyreg/tests/test_auto_RegAverage.py b/nipype/interfaces/niftyreg/tests/test_auto_RegAverage.py new file mode 100644 index 0000000000..616279e706 --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegAverage.py @@ -0,0 +1,73 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..regutils import RegAverage + + +def test_RegAverage_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + avg_files=dict(argstr='-avg %s', + position=1, + sep=' ', + xor=['avg_lts_files', 'avg_ref_file', 'demean1_ref_file', 'demean2_ref_file', 'demean3_ref_file', 'warp_files'], + ), + avg_lts_files=dict(argstr='-avg_lts %s', + position=1, + sep=' ', + xor=['avg_files', 'avg_ref_file', 'demean1_ref_file', 'demean2_ref_file', 'demean3_ref_file', 'warp_files'], + ), + avg_ref_file=dict(argstr='-avg_tran %s', + position=1, + requires=['warp_files'], + xor=['avg_files', 'avg_lts_files', 'demean1_ref_file', 'demean2_ref_file', 'demean3_ref_file'], + ), + demean1_ref_file=dict(argstr='-demean1 %s', + position=1, + requires=['warp_files'], + xor=['avg_files', 'avg_lts_files', 'avg_ref_file', 'demean2_ref_file', 'demean3_ref_file'], + ), + demean2_ref_file=dict(argstr='-demean2 %s', + position=1, + requires=['warp_files'], + xor=['avg_files', 'avg_lts_files', 'avg_ref_file', 'demean1_ref_file', 'demean3_ref_file'], + ), + demean3_ref_file=dict(argstr='-demean3 %s', + position=1, + requires=['warp_files'], + xor=['avg_files', 'avg_lts_files', 'avg_ref_file', 'demean1_ref_file', 'demean2_ref_file'], + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + omp_core_val=dict(argstr='-omp %i', + ), + out_file=dict(argstr='%s', + genfile=True, + position=0, + ), + terminal_output=dict(nohash=True, + ), + warp_files=dict(argstr='%s', + position=-1, + sep=' ', + xor=['avg_files', 'avg_lts_files'], + ), + ) + inputs = RegAverage.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_RegAverage_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = RegAverage.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/niftyreg/tests/test_auto_RegF3D.py b/nipype/interfaces/niftyreg/tests/test_auto_RegF3D.py new file mode 100644 index 0000000000..6e2a145eec --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegF3D.py @@ -0,0 +1,144 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..reg import RegF3D + + +def test_RegF3D_inputs(): + input_map = dict(aff_file=dict(argstr='-aff %s', + ), + amc_flag=dict(argstr='-amc', + ), + args=dict(argstr='%s', + ), + be_val=dict(argstr='-be %f', + ), + cpp_file=dict(argstr='-cpp %s', + name_source=['flo_file'], + name_template='%s_cpp.nii.gz', + ), + environ=dict(nohash=True, + usedefault=True, + ), + fbn2_val=dict(argstr='-fbn %d %d', + ), + fbn_val=dict(argstr='--fbn %d', + ), + flo_file=dict(argstr='-flo %s', + mandatory=True, + ), + flo_smooth_val=dict(argstr='-smooF %f', + ), + flwth2_thr_val=dict(argstr='-fLwTh %d %f', + ), + flwth_thr_val=dict(argstr='--fLwTh %f', + ), + fmask_file=dict(argstr='-fmask %s', + ), + fupth2_thr_val=dict(argstr='-fUpTh %d %f', + ), + fupth_thr_val=dict(argstr='--fUpTh %f', + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + incpp_file=dict(argstr='-incpp %s', + ), + jl_val=dict(argstr='-jl %f', + ), + kld2_flag=dict(argstr='-kld %d', + ), + kld_flag=dict(argstr='--kld', + ), + le_val=dict(argstr='-le %f', + ), + ln_val=dict(argstr='-ln %d', + ), + lncc2_val=dict(argstr='-lncc %d %f', + ), + lncc_val=dict(argstr='--lncc %f', + ), + lp_val=dict(argstr='-lp %d', + ), + maxit_val=dict(argstr='-maxit %d', + ), + nmi_flag=dict(argstr='--nmi', + ), + no_app_jl_flag=dict(argstr='-noAppJL', + ), + noconj_flag=dict(argstr='-noConj', + ), + nopy_flag=dict(argstr='-nopy', + ), + nox_flag=dict(argstr='-nox', + ), + noy_flag=dict(argstr='-noy', + ), + noz_flag=dict(argstr='-noz', + ), + omp_core_val=dict(argstr='-omp %i', + ), + pad_val=dict(argstr='-pad %f', + ), + pert_val=dict(argstr='-pert %d', + ), + rbn2_val=dict(argstr='-rbn %d %d', + ), + rbn_val=dict(argstr='--rbn %d', + ), + ref_file=dict(argstr='-ref %s', + mandatory=True, + ), + ref_smooth_val=dict(argstr='-smooR %f', + ), + res_file=dict(argstr='-res %s', + name_source=['flo_file'], + name_template='%s_res.nii.gz', + ), + rlwth2_thr_val=dict(argstr='-rLwTh %d %f', + ), + rlwth_thr_val=dict(argstr='--rLwTh %f', + ), + rmask_file=dict(argstr='-rmask %s', + ), + rupth2_thr_val=dict(argstr='-rUpTh %d %f', + ), + rupth_thr_val=dict(argstr='--rUpTh %f', + ), + smooth_grad_val=dict(argstr='-smoothGrad %f', + ), + ssd2_flag=dict(argstr='-ssd %d', + ), + ssd_flag=dict(argstr='--ssd', + ), + sx_val=dict(argstr='-sx %f', + ), + sy_val=dict(argstr='-sy %f', + ), + sz_val=dict(argstr='-sz %f', + ), + terminal_output=dict(nohash=True, + ), + vel_flag=dict(argstr='-vel', + ), + verbosity_off_flag=dict(argstr='-voff', + ), + ) + inputs = RegF3D.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_RegF3D_outputs(): + output_map = dict(avg_output=dict(), + cpp_file=dict(), + invcpp_file=dict(), + invres_file=dict(), + res_file=dict(), + ) + outputs = RegF3D.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/niftyreg/tests/test_auto_RegJacobian.py b/nipype/interfaces/niftyreg/tests/test_auto_RegJacobian.py new file mode 100644 index 0000000000..513f94388d --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegJacobian.py @@ -0,0 +1,48 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..regutils import RegJacobian + + +def test_RegJacobian_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + omp_core_val=dict(argstr='-omp %i', + ), + out_file=dict(argstr='%s', + name_source=['trans_file'], + name_template='%s', + position=-1, + ), + ref_file=dict(argstr='-ref %s', + ), + terminal_output=dict(nohash=True, + ), + trans_file=dict(argstr='-trans %s', + mandatory=True, + ), + type=dict(argstr='-%s', + position=-2, + usedefault=True, + ), + ) + inputs = RegJacobian.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_RegJacobian_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = RegJacobian.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/niftyreg/tests/test_auto_RegMeasure.py b/nipype/interfaces/niftyreg/tests/test_auto_RegMeasure.py new file mode 100644 index 0000000000..47d4d77049 --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegMeasure.py @@ -0,0 +1,47 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..regutils import RegMeasure + + +def test_RegMeasure_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + flo_file=dict(argstr='-flo %s', + mandatory=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + measure_type=dict(argstr='-%s', + mandatory=True, + ), + omp_core_val=dict(argstr='-omp %i', + ), + out_file=dict(argstr='-out %s', + name_source=['flo_file'], + name_template='%s', + ), + ref_file=dict(argstr='-ref %s', + mandatory=True, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = RegMeasure.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_RegMeasure_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = RegMeasure.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/niftyreg/tests/test_auto_RegResample.py b/nipype/interfaces/niftyreg/tests/test_auto_RegResample.py new file mode 100644 index 0000000000..2d26ae4379 --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegResample.py @@ -0,0 +1,63 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..regutils import RegResample + + +def test_RegResample_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + flo_file=dict(argstr='-flo %s', + mandatory=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + inter_val=dict(argstr='-inter %d', + ), + omp_core_val=dict(argstr='-omp %i', + ), + out_file=dict(argstr='%s', + name_source=['flo_file'], + name_template='%s', + position=-1, + ), + pad_val=dict(argstr='-pad %f', + ), + psf_alg=dict(argstr='-psf_alg %d', + ), + psf_flag=dict(argstr='-psf', + ), + ref_file=dict(argstr='-ref %s', + mandatory=True, + ), + tensor_flag=dict(argstr='-tensor ', + ), + terminal_output=dict(nohash=True, + ), + trans_file=dict(argstr='-trans %s', + ), + type=dict(argstr='-%s', + position=-2, + usedefault=True, + ), + verbosity_off_flag=dict(argstr='-voff', + ), + ) + inputs = RegResample.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_RegResample_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = RegResample.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/niftyreg/tests/test_auto_RegTools.py b/nipype/interfaces/niftyreg/tests/test_auto_RegTools.py new file mode 100644 index 0000000000..4fe0f1e7b1 --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegTools.py @@ -0,0 +1,69 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..regutils import RegTools + + +def test_RegTools_inputs(): + input_map = dict(add_val=dict(argstr='-add %s', + ), + args=dict(argstr='%s', + ), + bin_flag=dict(argstr='-bin', + ), + chg_res_val=dict(argstr='-chgres %f %f %f', + ), + div_val=dict(argstr='-div %s', + ), + down_flag=dict(argstr='-down', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + in_file=dict(argstr='-in %s', + mandatory=True, + ), + iso_flag=dict(argstr='-iso', + ), + mask_file=dict(argstr='-nan %s', + ), + mul_val=dict(argstr='-mul %s', + ), + noscl_flag=dict(argstr='-noscl', + ), + omp_core_val=dict(argstr='-omp %i', + ), + out_file=dict(argstr='-out %s', + name_source=['in_file'], + name_template='%s_tools.nii.gz', + ), + rms_val=dict(argstr='-rms %s', + ), + smo_g_val=dict(argstr='-smoG %f %f %f', + ), + smo_s_val=dict(argstr='-smoS %f %f %f', + ), + sub_val=dict(argstr='-sub %s', + ), + terminal_output=dict(nohash=True, + ), + thr_val=dict(argstr='-thr %f', + ), + ) + inputs = RegTools.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_RegTools_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = RegTools.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/niftyreg/tests/test_auto_RegTransform.py b/nipype/interfaces/niftyreg/tests/test_auto_RegTransform.py new file mode 100644 index 0000000000..67b8426129 --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_auto_RegTransform.py @@ -0,0 +1,98 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..regutils import RegTransform + + +def test_RegTransform_inputs(): + input_map = dict(aff_2_rig_input=dict(argstr='-aff2rig %s', + position=-2, + xor=['def_input', 'disp_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'flirt_2_nr_input'], + ), + args=dict(argstr='%s', + ), + comp_input=dict(argstr='-comp %s', + position=-3, + requires=['comp_input2'], + xor=['def_input', 'disp_input', 'flow_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + comp_input2=dict(argstr='%s', + position=-2, + ), + def_input=dict(argstr='-def %s', + position=-2, + xor=['disp_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + disp_input=dict(argstr='-disp %s', + position=-2, + xor=['def_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + environ=dict(nohash=True, + usedefault=True, + ), + flirt_2_nr_input=dict(argstr='-flirtAff2NR %s %s %s', + position=-2, + xor=['def_input', 'disp_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'aff_2_rig_input'], + ), + flow_input=dict(argstr='-flow %s', + position=-2, + xor=['def_input', 'disp_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + half_input=dict(argstr='-half %s', + position=-2, + xor=['def_input', 'disp_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + inv_aff_input=dict(argstr='-invAff %s', + position=-2, + xor=['def_input', 'disp_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + inv_nrr_input=dict(argstr='-invNrr %s %s', + position=-2, + xor=['def_input', 'disp_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'half_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + make_aff_input=dict(argstr='-makeAff %f %f %f %f %f %f %f %f %f %f %f %f', + position=-2, + xor=['def_input', 'disp_input', 'flow_input', 'comp_input', 'upd_s_form_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + omp_core_val=dict(argstr='-omp %i', + ), + out_file=dict(argstr='%s', + genfile=True, + position=-1, + ), + ref1_file=dict(argstr='-ref %s', + position=0, + ), + ref2_file=dict(argstr='-ref2 %s', + position=1, + requires=['ref1_file'], + ), + terminal_output=dict(nohash=True, + ), + upd_s_form_input=dict(argstr='-updSform %s', + position=-3, + requires=['upd_s_form_input2'], + xor=['def_input', 'disp_input', 'flow_input', 'comp_input', 'inv_aff_input', 'inv_nrr_input', 'half_input', 'make_aff_input', 'aff_2_rig_input', 'flirt_2_nr_input'], + ), + upd_s_form_input2=dict(argstr='%s', + position=-2, + requires=['upd_s_form_input'], + ), + ) + inputs = RegTransform.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_RegTransform_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = RegTransform.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/niftyreg/tests/test_reg.py b/nipype/interfaces/niftyreg/tests/test_reg.py new file mode 100644 index 0000000000..8f9a8eb886 --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_reg.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# 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_niftyreg, get_custom_path, + RegAladin, RegF3D) +from nipype.testing import example_data + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_aladin'), + reason="niftyreg is not installed. reg_aladin not found.") +def test_reg_aladin(): + """ tests for reg_aladin interface""" + # Create a reg_aladin object + nr_aladin = RegAladin() + + # Check if the command is properly defined + assert nr_aladin.cmd == get_custom_path('reg_aladin') + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + nr_aladin.run() + + # Assign some input data + ref_file = example_data('im1.nii') + flo_file = example_data('im2.nii') + rmask_file = example_data('mask.nii') + nr_aladin.inputs.ref_file = ref_file + nr_aladin.inputs.flo_file = flo_file + nr_aladin.inputs.rmask_file = rmask_file + nr_aladin.inputs.omp_core_val = 4 + + cmd_tmp = '{cmd} -aff {aff} -flo {flo} -omp 4 -ref {ref} -res {res} \ +-rmask {rmask}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_aladin'), + aff='im2_aff.txt', + flo=flo_file, + ref=ref_file, + res='im2_res.nii.gz', + rmask=rmask_file, + ) + + assert nr_aladin.cmdline == expected_cmd + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_f3d'), + reason="niftyreg is not installed. reg_f3d not found.") +def test_reg_f3d(): + """ tests for reg_f3d interface""" + # Create a reg_f3d object + nr_f3d = RegF3D() + + # Check if the command is properly defined + assert nr_f3d.cmd == get_custom_path('reg_f3d') + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + nr_f3d.run() + + # Assign some input data + ref_file = example_data('im1.nii') + flo_file = example_data('im2.nii') + rmask_file = example_data('mask.nii') + nr_f3d.inputs.ref_file = ref_file + nr_f3d.inputs.flo_file = flo_file + nr_f3d.inputs.rmask_file = rmask_file + nr_f3d.inputs.omp_core_val = 4 + nr_f3d.inputs.vel_flag = True + nr_f3d.inputs.be_val = 0.1 + nr_f3d.inputs.le_val = 0.1 + + cmd_tmp = '{cmd} -be 0.100000 -cpp {cpp} -flo {flo} -le 0.100000 -omp 4 \ +-ref {ref} -res {res} -rmask {rmask} -vel' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_f3d'), + cpp='im2_cpp.nii.gz', + flo=flo_file, + ref=ref_file, + res='im2_res.nii.gz', + rmask=rmask_file, + ) + + assert nr_f3d.cmdline == expected_cmd diff --git a/nipype/interfaces/niftyreg/tests/test_regutils.py b/nipype/interfaces/niftyreg/tests/test_regutils.py new file mode 100644 index 0000000000..b2e1357811 --- /dev/null +++ b/nipype/interfaces/niftyreg/tests/test_regutils.py @@ -0,0 +1,459 @@ +# -*- coding: utf-8 -*- +# 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, + RegAverage, RegResample, RegJacobian, + RegTools, RegMeasure, RegTransform) +from nipype.testing import example_data +import os +import pytest + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_resample'), + reason="niftyreg is not installed. reg_resample not found.") +def test_reg_resample_res(): + """ tests for reg_resample interface """ + # Create a reg_resample object + nr_resample = RegResample() + + # Check if the command is properly defined + assert nr_resample.cmd == get_custom_path('reg_resample') + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + nr_resample.run() + + # Resample res + ref_file = example_data('im1.nii') + flo_file = example_data('im2.nii') + trans_file = example_data('warpfield.nii') + nr_resample.inputs.ref_file = ref_file + nr_resample.inputs.flo_file = flo_file + nr_resample.inputs.trans_file = trans_file + nr_resample.inputs.inter_val = 'LIN' + nr_resample.inputs.omp_core_val = 4 + + cmd_tmp = '{cmd} -flo {flo} -inter 1 -omp 4 -ref {ref} -trans {trans} \ +-res {res}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_resample'), + flo=flo_file, + ref=ref_file, + trans=trans_file, + res='im2_res.nii.gz') + + assert nr_resample.cmdline == expected_cmd + + # test_reg_resample_blank() + nr_resample_2 = RegResample(type='blank', inter_val='LIN', omp_core_val=4) + ref_file = example_data('im1.nii') + flo_file = example_data('im2.nii') + trans_file = example_data('warpfield.nii') + nr_resample_2.inputs.ref_file = ref_file + nr_resample_2.inputs.flo_file = flo_file + nr_resample_2.inputs.trans_file = trans_file + + cmd_tmp = '{cmd} -flo {flo} -inter 1 -omp 4 -ref {ref} -trans {trans} \ +-blank {blank}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_resample'), + flo=flo_file, + ref=ref_file, + trans=trans_file, + blank='im2_blank.nii.gz') + + assert nr_resample_2.cmdline == expected_cmd + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_jacobian'), + reason="niftyreg is not installed. reg_jacobian not found.") +def test_reg_jacobian_jac(): + """ Test interface for RegJacobian """ + # Create a reg_jacobian object + nr_jacobian = RegJacobian() + + # Check if the command is properly defined + assert nr_jacobian.cmd == get_custom_path('reg_jacobian') + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + nr_jacobian.run() + + # Test Reg Jacobian: jac + ref_file = example_data('im1.nii') + trans_file = example_data('warpfield.nii') + nr_jacobian.inputs.ref_file = ref_file + nr_jacobian.inputs.trans_file = trans_file + nr_jacobian.inputs.omp_core_val = 4 + + cmd_tmp = '{cmd} -omp 4 -ref {ref} -trans {trans} -jac {jac}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_jacobian'), + ref=ref_file, + trans=trans_file, + jac='warpfield_jac.nii.gz') + + assert nr_jacobian.cmdline == expected_cmd + + # Test Reg Jacobian: jac m + nr_jacobian_2 = RegJacobian(type='jacM', omp_core_val=4) + ref_file = example_data('im1.nii') + trans_file = example_data('warpfield.nii') + nr_jacobian_2.inputs.ref_file = ref_file + nr_jacobian_2.inputs.trans_file = trans_file + + cmd_tmp = '{cmd} -omp 4 -ref {ref} -trans {trans} -jacM {jac}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_jacobian'), + ref=ref_file, + trans=trans_file, + jac='warpfield_jacM.nii.gz') + + assert nr_jacobian_2.cmdline == expected_cmd + + # Test Reg Jacobian: jac l + nr_jacobian_3 = RegJacobian(type='jacL', omp_core_val=4) + ref_file = example_data('im1.nii') + trans_file = example_data('warpfield.nii') + nr_jacobian_3.inputs.ref_file = ref_file + nr_jacobian_3.inputs.trans_file = trans_file + + cmd_tmp = '{cmd} -omp 4 -ref {ref} -trans {trans} -jacL {jac}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_jacobian'), + ref=ref_file, + trans=trans_file, + jac='warpfield_jacL.nii.gz') + + assert nr_jacobian_3.cmdline == expected_cmd + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_tools'), + reason="niftyreg is not installed. reg_tools not found.") +def test_reg_tools_mul(): + """ tests for reg_tools interface """ + # Create a reg_tools object + nr_tools = RegTools() + + # Check if the command is properly defined + assert nr_tools.cmd == get_custom_path('reg_tools') + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + nr_tools.run() + + # Test reg_tools: mul + in_file = example_data('im1.nii') + nr_tools.inputs.in_file = in_file + nr_tools.inputs.mul_val = 4 + nr_tools.inputs.omp_core_val = 4 + + cmd_tmp = '{cmd} -in {in_file} -mul 4.0 -omp 4 -out {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_tools'), + in_file=in_file, + out_file='im1_tools.nii.gz') + + assert nr_tools.cmdline == expected_cmd + + # Test reg_tools: iso + nr_tools_2 = RegTools(iso_flag=True, omp_core_val=4) + in_file = example_data('im1.nii') + nr_tools_2.inputs.in_file = in_file + + cmd_tmp = '{cmd} -in {in_file} -iso -omp 4 -out {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_tools'), + in_file=in_file, + out_file='im1_tools.nii.gz') + + assert nr_tools_2.cmdline == expected_cmd + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_average'), + reason="niftyreg is not installed. reg_average not found.") +def test_reg_average(): + """ tests for reg_average interface """ + # Create a reg_average object + nr_average = RegAverage() + + # Check if the command is properly defined + assert nr_average.cmd == get_custom_path('reg_average') + + # Average niis + one_file = example_data('im1.nii') + two_file = example_data('im2.nii') + three_file = example_data('im3.nii') + nr_average.inputs.avg_files = [one_file, two_file, three_file] + generated_cmd = nr_average.cmdline + + # Read the reg_average_cmd + reg_average_cmd = os.path.join(os.getcwd(), 'reg_average_cmd') + with open(reg_average_cmd, 'rb') as f_obj: + argv = f_obj.read() + os.remove(reg_average_cmd) + + expected_argv = '%s %s -avg %s %s %s' % (get_custom_path('reg_average'), + os.path.join(os.getcwd(), + 'avg_out.nii.gz'), + one_file, two_file, three_file) + + assert argv.decode('utf-8') == expected_argv + + # Test command line with text file + expected_cmd = ('%s --cmd_file %s' + % (get_custom_path('reg_average'), reg_average_cmd)) + + assert generated_cmd == expected_cmd + + # Test Reg Average: average txt + nr_average_2 = RegAverage() + one_file = example_data('TransformParameters.0.txt') + two_file = example_data('ants_Affine.txt') + three_file = example_data('elastix.txt') + nr_average_2.inputs.avg_files = [one_file, two_file, three_file] + generated_cmd = nr_average_2.cmdline + + # Read the reg_average_cmd + reg_average_cmd = os.path.join(os.getcwd(), 'reg_average_cmd') + with open(reg_average_cmd, 'rb') as f_obj: + argv = f_obj.read() + os.remove(reg_average_cmd) + + expected_argv = '%s %s -avg %s %s %s' % (get_custom_path('reg_average'), + os.path.join(os.getcwd(), + 'avg_out.txt'), + one_file, two_file, three_file) + + assert argv.decode('utf-8') == expected_argv + + # Test Reg Average: average list + nr_average_3 = RegAverage() + one_file = example_data('TransformParameters.0.txt') + two_file = example_data('ants_Affine.txt') + three_file = example_data('elastix.txt') + nr_average_3.inputs.avg_lts_files = [one_file, two_file, three_file] + generated_cmd = nr_average_3.cmdline + + # Read the reg_average_cmd + reg_average_cmd = os.path.join(os.getcwd(), 'reg_average_cmd') + with open(reg_average_cmd, 'rb') as f_obj: + argv = f_obj.read() + os.remove(reg_average_cmd) + + expected_argv = ('%s %s -avg_lts %s %s %s' + % (get_custom_path('reg_average'), + os.path.join(os.getcwd(), 'avg_out.txt'), + one_file, two_file, three_file)) + + assert argv.decode('utf-8') == expected_argv + + # Test Reg Average: average ref + nr_average_4 = RegAverage() + ref_file = example_data('anatomical.nii') + one_file = example_data('im1.nii') + two_file = example_data('im2.nii') + three_file = example_data('im3.nii') + trans1_file = example_data('roi01.nii') + trans2_file = example_data('roi02.nii') + trans3_file = example_data('roi03.nii') + nr_average_4.inputs.warp_files = [trans1_file, one_file, + trans2_file, two_file, + trans3_file, three_file] + nr_average_4.inputs.avg_ref_file = ref_file + generated_cmd = nr_average_4.cmdline + + # Read the reg_average_cmd + reg_average_cmd = os.path.join(os.getcwd(), 'reg_average_cmd') + with open(reg_average_cmd, 'rb') as f_obj: + argv = f_obj.read() + os.remove(reg_average_cmd) + + expected_argv = ('%s %s -avg_tran %s %s %s %s %s %s %s' + % (get_custom_path('reg_average'), + os.path.join(os.getcwd(), 'avg_out.nii.gz'), + ref_file, trans1_file, one_file, trans2_file, two_file, + trans3_file, three_file)) + + assert argv.decode('utf-8') == expected_argv + + # Test Reg Average: demean3 + nr_average_5 = RegAverage() + ref_file = example_data('anatomical.nii') + one_file = example_data('im1.nii') + two_file = example_data('im2.nii') + three_file = example_data('im3.nii') + aff1_file = example_data('TransformParameters.0.txt') + aff2_file = example_data('ants_Affine.txt') + aff3_file = example_data('elastix.txt') + trans1_file = example_data('roi01.nii') + trans2_file = example_data('roi02.nii') + trans3_file = example_data('roi03.nii') + nr_average_5.inputs.warp_files = [aff1_file, trans1_file, one_file, + aff2_file, trans2_file, two_file, + aff3_file, trans3_file, three_file] + nr_average_5.inputs.demean3_ref_file = ref_file + generated_cmd = nr_average_5.cmdline + + # Read the reg_average_cmd + reg_average_cmd = os.path.join(os.getcwd(), 'reg_average_cmd') + with open(reg_average_cmd, 'rb') as f_obj: + argv = f_obj.read() + os.remove(reg_average_cmd) + + expected_argv = ('%s %s -demean3 %s %s %s %s %s %s %s %s %s %s' + % (get_custom_path('reg_average'), + os.path.join(os.getcwd(), 'avg_out.nii.gz'), + ref_file, + aff1_file, trans1_file, one_file, + aff2_file, trans2_file, two_file, + aff3_file, trans3_file, three_file)) + + assert argv.decode('utf-8') == expected_argv + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_transform'), + reason="niftyreg is not installed. reg_transform not found.") +def test_reg_transform_def(): + """ tests for reg_transform interface """ + # Create a reg_transform object + nr_transform = RegTransform() + + # Check if the command is properly defined + assert nr_transform.cmd == get_custom_path('reg_transform') + + # Assign some input data + trans_file = example_data('warpfield.nii') + nr_transform.inputs.def_input = trans_file + nr_transform.inputs.omp_core_val = 4 + + cmd_tmp = '{cmd} -omp 4 -def {trans_file} {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_transform'), + trans_file=trans_file, + out_file=os.path.join(os.getcwd(), 'warpfield_trans.nii.gz')) + + assert nr_transform.cmdline == expected_cmd + + # Test reg_transform: def ref + nr_transform_2 = RegTransform(omp_core_val=4) + ref_file = example_data('im1.nii') + trans_file = example_data('warpfield.nii') + nr_transform_2.inputs.ref1_file = ref_file + nr_transform_2.inputs.def_input = trans_file + + cmd_tmp = '{cmd} -ref {ref_file} -omp 4 -def {trans_file} {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_transform'), + ref_file=ref_file, + trans_file=trans_file, + out_file=os.path.join(os.getcwd(), 'warpfield_trans.nii.gz')) + + assert nr_transform_2.cmdline == expected_cmd + + # Test reg_transform: comp nii + nr_transform_3 = RegTransform(omp_core_val=4) + ref_file = example_data('im1.nii') + trans_file = example_data('warpfield.nii') + trans2_file = example_data('anatomical.nii') + nr_transform_3.inputs.ref1_file = ref_file + nr_transform_3.inputs.comp_input2 = trans2_file + nr_transform_3.inputs.comp_input = trans_file + + cmd_tmp = '{cmd} -ref {ref_file} -omp 4 -comp {trans1} {trans2} {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_transform'), + ref_file=ref_file, + trans1=trans_file, + trans2=trans2_file, + out_file=os.path.join(os.getcwd(), 'warpfield_trans.nii.gz')) + + assert nr_transform_3.cmdline == expected_cmd + + # Test reg_transform: comp txt + nr_transform_4 = RegTransform(omp_core_val=4) + aff1_file = example_data('ants_Affine.txt') + aff2_file = example_data('elastix.txt') + nr_transform_4.inputs.comp_input2 = aff2_file + nr_transform_4.inputs.comp_input = aff1_file + + cmd_tmp = '{cmd} -omp 4 -comp {aff1} {aff2} {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_transform'), + aff1=aff1_file, + aff2=aff2_file, + out_file=os.path.join(os.getcwd(), 'ants_Affine_trans.txt')) + + assert nr_transform_4.cmdline == expected_cmd + + # Test reg_transform: comp + nr_transform_5 = RegTransform(omp_core_val=4) + trans_file = example_data('warpfield.nii') + aff_file = example_data('elastix.txt') + nr_transform_5.inputs.comp_input2 = trans_file + nr_transform_5.inputs.comp_input = aff_file + + cmd_tmp = '{cmd} -omp 4 -comp {aff} {trans} {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_transform'), + aff=aff_file, + trans=trans_file, + out_file=os.path.join(os.getcwd(), 'elastix_trans.nii.gz')) + + assert nr_transform_5.cmdline == expected_cmd + + # Test reg_transform: flirt + nr_transform_6 = RegTransform(omp_core_val=4) + aff_file = example_data('elastix.txt') + ref_file = example_data('im1.nii') + in_file = example_data('im2.nii') + nr_transform_6.inputs.flirt_2_nr_input = (aff_file, ref_file, in_file) + + cmd_tmp = '{cmd} -omp 4 -flirtAff2NR {aff} {ref} {in_file} {out_file}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_transform'), + aff=aff_file, + ref=ref_file, + in_file=in_file, + out_file=os.path.join(os.getcwd(), 'elastix_trans.txt')) + + assert nr_transform_6.cmdline == expected_cmd + + +@pytest.mark.skipif( + no_niftyreg(cmd='reg_measure'), + reason="niftyreg is not installed. reg_measure not found.") +def test_reg_measure(): + """ tests for reg_measure interface """ + # Create a reg_measure object + nr_measure = RegMeasure() + + # Check if the command is properly defined + assert nr_measure.cmd == get_custom_path('reg_measure') + + # test raising error with mandatory args absent + with pytest.raises(ValueError): + nr_measure.run() + + # Assign some input data + ref_file = example_data('im1.nii') + flo_file = example_data('im2.nii') + nr_measure.inputs.ref_file = ref_file + nr_measure.inputs.flo_file = flo_file + nr_measure.inputs.measure_type = 'lncc' + nr_measure.inputs.omp_core_val = 4 + + cmd_tmp = '{cmd} -flo {flo} -lncc -omp 4 -out {out} -ref {ref}' + expected_cmd = cmd_tmp.format( + cmd=get_custom_path('reg_measure'), + flo=flo_file, + out='im2_lncc.txt', + ref=ref_file) + + assert nr_measure.cmdline == expected_cmd diff --git a/nipype/interfaces/setup.py b/nipype/interfaces/setup.py index 4c79456824..72f48f6039 100644 --- a/nipype/interfaces/setup.py +++ b/nipype/interfaces/setup.py @@ -19,14 +19,15 @@ def configuration(parent_package='', top_path=None): config.add_subpackage('elastix') config.add_subpackage('freesurfer') config.add_subpackage('fsl') + config.add_subpackage('minc') + config.add_subpackage('mipav') config.add_subpackage('mne') config.add_subpackage('mrtrix') config.add_subpackage('mrtrix3') + config.add_subpackage('niftyreg') config.add_subpackage('nipy') config.add_subpackage('spm') config.add_subpackage('slicer') - config.add_subpackage('minc') - config.add_subpackage('mipav') config.add_data_dir('script_templates') config.add_data_dir('tests') diff --git a/nipype/workflows/smri/__init__.py b/nipype/workflows/smri/__init__.py index 64030857a9..3359fbdc6c 100644 --- a/nipype/workflows/smri/__init__.py +++ b/nipype/workflows/smri/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import print_function, division, unicode_literals, absolute_import -from . import freesurfer from . import ants +from . import freesurfer +from . import niftyreg diff --git a/nipype/workflows/smri/niftyreg/__init__.py b/nipype/workflows/smri/niftyreg/__init__.py new file mode 100644 index 0000000000..b9d0c9c85b --- /dev/null +++ b/nipype/workflows/smri/niftyreg/__init__.py @@ -0,0 +1,5 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +from .groupwise import (create_groupwise_average, create_nonlinear_gw_step, + create_linear_gw_step) diff --git a/nipype/workflows/smri/niftyreg/groupwise.py b/nipype/workflows/smri/niftyreg/groupwise.py new file mode 100644 index 0000000000..125a017866 --- /dev/null +++ b/nipype/workflows/smri/niftyreg/groupwise.py @@ -0,0 +1,386 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +''' +This file provides some common registration routines useful for a variety of +pipelines. + +Including linear and non-linear image co-registration +''' + +from builtins import str, range +import nipype.interfaces.utility as niu +import nipype.interfaces.niftyreg as niftyreg +import nipype.pipeline.engine as pe + + +def create_linear_gw_step(name="linear_gw_niftyreg", + demean=True, + linear_options_hash=None, + use_mask=False, + verbose=False): + """ + Creates a workflow that perform linear co-registration of a set of images + using RegAladin, producing an average image and a set of affine + transformation matrices linking each of the floating images to the average. + + Inputs:: + + inputspec.in_files - The input files to be registered + inputspec.ref_file - The initial reference image that the input files + are registered to + inputspec.rmask_file - Mask of the reference image + inputspec.in_aff_files - Initial affine transformation files + + Outputs:: + + outputspec.average_image - The average image + outputspec.aff_files - The affine transformation files + + Optional arguments:: + linear_options_hash - An options dictionary containing a list of + parameters for RegAladin that take + the same form as given in the interface (default None) + demean - Selects whether to demean the transformation matrices when + performing the averaging (default True) + initial_affines - Selects whether to iterate over initial affine + images, which we generally won't have (default False) + + Example + ------- + + >>> from nipype.workflows.smri.niftyreg import create_linear_gw_step + >>> lgw = create_linear_gw_step('my_linear_coreg') # doctest: +SKIP + >>> lgw.inputs.inputspec.in_files = ['file1.nii.gz', 'file2.nii.gz'] \ +# doctest: +SKIP + >>> lgw.inputs.inputspec.ref_file = ['ref.nii.gz'] # doctest: +SKIP + >>> lgw.run() # doctest: +SKIP + + """ + # Create the sub workflow + workflow = pe.Workflow(name=name) + workflow.base_output_dir = name + + # We need to create an input node for the workflow + inputnode = pe.Node(niu.IdentityInterface( + fields=['in_files', 'ref_file', 'rmask_file']), + name='inputspec') + + if linear_options_hash is None: + linear_options_hash = dict() + + # Rigidly register each of the images to the average + lin_reg = pe.MapNode(interface=niftyreg.RegAladin(**linear_options_hash), + name="lin_reg", iterfield=['flo_file']) + + if verbose is False: + lin_reg.inputs.verbosity_off_flag = True + + # Average the images + ave_ims = pe.Node(interface=niftyreg.RegAverage(), name="ave_ims") + + # We have a new average image and the affine + # transformations, which are returned as an output node. + outputnode = pe.Node(niu.IdentityInterface( + fields=['average_image', 'trans_files']), name='outputspec') + + # Connect the inputs to the lin_reg node + workflow.connect([ + (inputnode, lin_reg, [('ref_file', 'ref_file')]), + (inputnode, lin_reg, [('in_files', 'flo_file')]) + ]) + if use_mask: + workflow.connect(inputnode, 'rmask_file', lin_reg, 'rmask_file') + + if demean: + workflow.connect([ + (inputnode, ave_ims, [('ref_file', 'demean1_ref_file')]), + (lin_reg, ave_ims, [('avg_output', 'warp_files')]) + ]) + else: + workflow.connect(lin_reg, 'res_file', ave_ims, 'avg_files') + + # Connect up the output node + workflow.connect([ + (lin_reg, outputnode, [('aff_file', 'trans_files')]), + (ave_ims, outputnode, [('out_file', 'average_image')]) + ]) + + return workflow + + +def create_nonlinear_gw_step(name="nonlinear_gw_niftyreg", + demean=True, + nonlinear_options_hash=None, + initial_affines=False, + use_mask=False, + verbose=False): + """ + Creates a workflow that perform non-linear co-registrations of a set of + images using RegF3d, producing an non-linear average image and a set of + cpp transformation linking each of the floating images to the average. + + Inputs:: + inputspec.in_files - The input files to be registered + inputspec.ref_file - The initial reference image that the input files + are registered to + inputspec.rmask_file - Mask of the reference image + inputspec.in_trans_files - Initial transformation files (affine or + cpps) + + Outputs:: + + outputspec.average_image - The average image + outputspec.cpp_files - The bspline transformation files + + Optional arguments:: + nonlinear_options_hash - An options dictionary containing a list of + parameters for RegAladin that take the + same form as given in the interface (default None) + initial_affines - Selects whether to iterate over initial affine + images, which we generally won't have (default False) + + Example + ------- + >>> from nipype.workflows.smri.niftyreg import create_nonlinear_gw_step + >>> nlc = create_nonlinear_gw_step('nonlinear_coreg') # doctest: +SKIP + >>> nlc.inputs.inputspec.in_files = ['file1.nii.gz', 'file2.nii.gz'] \ +# doctest: +SKIP + >>> nlc.inputs.inputspec.ref_file = ['ref.nii.gz'] # doctest: +SKIP + >>> nlc.run() # doctest: +SKIP + + """ + + # Create the workflow + workflow = pe.Workflow(name=name) + workflow.base_output_dir = name + + # We need to create an input node for the workflow + inputnode = pe.Node(niu.IdentityInterface( + fields=['in_files', + 'ref_file', + 'rmask_file', + 'input_aff_files']), + name='inputspec') + + if nonlinear_options_hash is None: + nonlinear_options_hash = dict() + + # non-rigidly register each of the images to the average + # flo_file can take a list of files + # Need to be able to iterate over input affine files, but what about the + # cases where we have no input affine files? + # Passing empty strings are not valid filenames, and undefined fields can + # not be iterated over. + # Current simple solution, as this is not generally required, is to use a + # flag which specifies wherther to iterate + if initial_affines: + nonlin_reg = pe.MapNode(interface=niftyreg.RegF3D( + **nonlinear_options_hash), name="nonlin_reg", + iterfield=['flo_file', 'aff_file']) + else: + nonlin_reg = pe.MapNode(interface=niftyreg.RegF3D( + **nonlinear_options_hash), name="nonlin_reg", + iterfield=['flo_file']) + + if verbose is False: + nonlin_reg.inputs.verbosity_off_flag = True + + # Average the images + ave_ims = pe.Node(interface=niftyreg.RegAverage(), name="ave_ims") + + # We have a new centered average image, the resampled original images and + # the affine transformations, which are returned as an output node. + outputnode = pe.Node(niu.IdentityInterface( + fields=['average_image', + 'trans_files']), + name='outputspec') + + # Connect the inputs to the lin_reg node, which is split over in_files + workflow.connect([ + (inputnode, nonlin_reg, [('in_files', 'flo_file')]), + (inputnode, nonlin_reg, [('ref_file', 'ref_file')]) + ]) + + if use_mask: + workflow.connect(inputnode, 'rmask_file', nonlin_reg, 'rmask_file') + + # If we have initial affine transforms, we need to connect them in + if initial_affines: + workflow.connect(inputnode, 'input_aff_files', nonlin_reg, 'aff_file') + + if demean: + if 'vel_flag' in list(nonlinear_options_hash.keys()) and \ + nonlinear_options_hash['vel_flag'] is True and \ + initial_affines: + workflow.connect( + inputnode, 'ref_file', ave_ims, 'demean3_ref_file') + else: + workflow.connect( + inputnode, 'ref_file', ave_ims, 'demean2_ref_file') + workflow.connect(nonlin_reg, 'avg_output', ave_ims, 'warp_files') + else: + workflow.connect(nonlin_reg, 'res_file', ave_ims, 'avg_files') + + # Connect up the output node + workflow.connect([ + (nonlin_reg, outputnode, [('cpp_file', 'trans_files')]), + (ave_ims, outputnode, [('out_file', 'average_image')]) + ]) + + return workflow + + +# Creates an atlas image by iterative registration. An initial reference image +# can be provided, otherwise one will be made. +def create_groupwise_average(name="atlas_creation", + itr_rigid=3, + itr_affine=3, + itr_non_lin=5, + linear_options_hash=None, + nonlinear_options_hash=None, + use_mask=False, + verbose=False): + """ + Create the overall workflow that embeds all the rigid, affine and + non-linear components. + + Inputs:: + inputspec.in_files - The input files to be registered + inputspec.ref_file - The initial reference image that the input files + are registered to + inputspec.rmask_file - Mask of the reference image + inputspec.in_trans_files - Initial transformation files (affine or + cpps) + + Outputs:: + + outputspec.average_image - The average image + outputspec.cpp_files - The bspline transformation files + + Example + ------- + >>> from nipype.workflows.smri.niftyreg import create_groupwise_average + >>> node = create_groupwise_average('groupwise_av') # doctest: +SKIP + >>> node.inputs.inputspec.in_files = ['file1.nii.gz', 'file2.nii.gz'] \ +# doctest: +SKIP + >>> node.inputs.inputspec.ref_file = ['ref.nii.gz'] # doctest: +SKIP + >>> node.inputs.inputspec.rmask_file = ['mask.nii.gz'] # doctest: +SKIP + >>> node.run() # doctest: +SKIP + + """ + # Create workflow + workflow = pe.Workflow(name=name) + + if linear_options_hash is None: + linear_options_hash = dict() + + if nonlinear_options_hash is None: + nonlinear_options_hash = dict() + + # Create the input and output node + inputnode = pe.Node(niu.IdentityInterface( + fields=['in_files', + 'ref_file', + 'rmask_file']), + name='inputspec') + + outputnode = pe.Node(niu.IdentityInterface( + fields=['average_image', + 'trans_files']), + name='outputspec') + + # Create lists to store the rigid, affine and non-linear sub-workflow + lin_workflows = [] + nonlin_workflows = [] + + # Create the linear groupwise registration sub-workflows + for i in range(itr_rigid + itr_affine): + # Define is the sub-workflow is rigid or affine + if i >= itr_rigid: + linear_options_hash['rig_only_flag'] = False + else: + linear_options_hash['rig_only_flag'] = True + + # Define if the average image should be demean to ensure we have a + # barycenter + if (i < itr_rigid) or (i == (itr_rigid + itr_affine - 1)): + demean_arg = False + else: + demean_arg = True + + # Create the rigid or affine sub-workflow and add it to the relevant + # list + wf = create_linear_gw_step(name='lin_reg' + str(i), + linear_options_hash=linear_options_hash, + demean=demean_arg, verbose=verbose) + lin_workflows.append(wf) + + # Connect up the input data to the workflow + workflow.connect(inputnode, 'in_files', wf, 'inputspec.in_files') + if use_mask: + workflow.connect( + inputnode, 'rmask_file', wf, 'inputspec.rmask_file') + # If it exist, connect the previous workflow to the current one + if i == 0: + workflow.connect(inputnode, 'ref_file', wf, 'inputspec.ref_file') + else: + workflow.connect(lin_workflows[i - 1], 'outputspec.average_image', + wf, 'inputspec.ref_file') + + demean_arg = True + + # Create the nonlinear groupwise registration sub-workflows + for i in range(itr_non_lin): + + if len(lin_workflows) > 0: + initial_affines_arg = True + if i == (itr_non_lin - 1): + demean_arg = False + + wf = create_nonlinear_gw_step( + name='nonlin' + str(i), demean=demean_arg, + initial_affines=initial_affines_arg, + nonlinear_options_hash=nonlinear_options_hash, verbose=verbose) + + # Connect up the input data to the workflows + workflow.connect(inputnode, 'in_files', wf, 'inputspec.in_files') + if use_mask: + workflow.connect( + inputnode, 'rmask_file', wf, 'inputspec.rmask_file') + + if initial_affines_arg: + # Take the final linear registration results and use them to + # initialise the NR + workflow.connect(lin_workflows[-1], 'outputspec.trans_files', + wf, 'inputspec.input_aff_files') + + if i == 0: + if len(lin_workflows) > 0: + workflow.connect( + lin_workflows[-1], 'outputspec.average_image', + wf, 'inputspec.ref_file') + else: + workflow.connect(inputnode, 'ref_file', + wf, 'inputspec.ref_file') + else: + workflow.connect( + nonlin_workflows[i - 1], 'outputspec.average_image', + wf, 'inputspec.ref_file') + + nonlin_workflows.append(wf) + + # Set up the last workflow + lw = None + if len(nonlin_workflows) > 0: + lw = nonlin_workflows[-1] + elif len(lin_workflows) > 0: + lw = lin_workflows[-1] + + # Connect the data to return + workflow.connect([ + (lw, outputnode, [('outputspec.average_image', 'average_image')]), + (lw, outputnode, [('outputspec.trans_files', 'trans_files')]) + ]) + + return workflow