From 8a824cb9dac256abcad86da069eb1c312d533713 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 26 Jan 2017 16:27:54 -0800 Subject: [PATCH 01/48] partial implementation of an interface for antsMotionCorr. Outputs still need work, will only handle a single metric type and do basic transformation --- nipype/interfaces/ants/preprocess.py | 182 +++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 nipype/interfaces/ants/preprocess.py diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py new file mode 100644 index 0000000000..df1e1f7282 --- /dev/null +++ b/nipype/interfaces/ants/preprocess.py @@ -0,0 +1,182 @@ +# -*- coding: utf-8 -*- +"""The ants module provides basic functions for interfacing with ants + functions. + + Change directory to provide relative paths for doctests + >>> import os + >>> filepath = os.path.dirname( os.path.realpath( __file__ ) ) + >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data')) + >>> os.chdir(datadir) +""" +import os + +from ..base import TraitedSpec, File, traits, isdefined +from .base import ANTSCommand, ANTSCommandInputSpec + +def _extant(field): + return (field is not None) and isdefined(field) + +class AntsMotionCorrInputSpec(ANTSCommandInputSpec): + '''Input spec for the antsMotionCorr command.''' + dimension_desc = ( + "This option forces the image to be treated as a " + "specified-dimensional image. If not specified, N4 tries to infer " + "the dimensionality from the input image." + ) + dimensionality = traits.Enum(3, 2, argstr='-d %d', usedefault=False, + position=0, desc=dimension_desc) + + average_image = File(argstr='-a %s', position=1, + desc="Average the input time series image.") + + output_average_image = File(argstr="%s", hash_files=False, desc="") + output_transform_prefix = traits.Str() + output_warped_image = File(hash_files=False, desc="") + + metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", + "Mattes", argstr="%s") + fixed_image = File(hash_files=False, desc="") + moving_image = File(hash_files=False, desc="") + metric_weight = traits.Float(1.0) + radius_or_bins = traits.Int(desc="") + sampling_strategy = traits.Enum("None", "Regular", "Random", None) + sampling_percentage = traits.Either(traits.Range(low=0.0, high=1.0), None) + + transformation_model = traits.Enum("Affine", "Rigid", argstr="%s") + gradient_step_length = traits.Float(requires=['transformation_model'], + desc='') + + iterations = traits.Int( + argstr="-i %d", + desc="Specify the number of iterations at each level." + ) + smoothing_sigmas = traits.Int( + argstr="-s %d", + desc="Specify the amount of smoothing at each level." + ) + shrink_factors = traits.Int( + argstr="-f %d", + desc=("Specify the shrink factor for the virtual domain (typically " + "the fixed image) at each level.") + ) + n_images = traits.Int( + argstr="-n %d", + desc=("This option sets the number of images to use to construct the " + "template image.") + ) + use_fixed_reference_image = traits.Bool( + argstr="-u %d", + default=True, + desc=("use a fixed reference image instead of the neighor in the time " + "series.") + ) + use_scales_estimator = traits.Bool( + argstr="-e %d", + default=True, + desc="use the scale estimator to control optimization." + ) + +class AntsMotionCorrOutputSpec(TraitedSpec): + '''Output spec for the antsMotionCorr command''' + average_image = File(exists=True, desc='Average of an image') + composite_transform = File(exists=True, desc='Composite transform file') + inverse_composite_transform = File(desc='Inverse composite transform file') + warped_image = File(desc="Outputs warped image") + inverse_warped_image = File(desc="Outputs the inverse of the warped image") + save_state = File(desc="The saved registration state to be restored") + +class AntsMotionCorr(ANTSCommand): + ''' + Examples + ------- + >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr + >>> ants_mc = AntsMotionCorr() + >>> ants_mc.inputs.dimensionality = 3 + >>> ants_mc.inputs.output_transform_prefix = "motcorr" + >>> ants_mc.inputs.output_warped_image = "warped.nii.gz" + >>> ants_mc.inputs.output_average_image = "average_image.nii.gz" + >>> ants_mc.inputs.metric_type = "GC" + >>> ants_mc.inputs.fixed_image = "average_image.nii.gz" + >>> ants_mc.inputs.moving_image = "input.nii.gz" + >>> ants_mc.inputs.metric_weight = 1 + >>> ants_mc.inputs.radius_or_bins = 1 + >>> ants_mc.inputs.sampling_strategy = "Random" + >>> ants_mc.inputs.sampling_percentage = 0.05 + >>> ants_mc.inputs.transformation_model = "Affine" + >>> ants_mc.inputs.gradient_step_length = 0.005 + >>> ants_mc.inputs.iterations = 10 + >>> ants_mc.inputs.smoothing_sigmas = 0 + >>> ants_mc.inputs.shrink_factors = 1 + >>> ants_mc.inputs.n_images = 10 + >>> ants_mc.inputs.use_fixed_reference_image = True + >>> ants_mc.inputs.use_scales_estimator = True + >>> print(ants_mc.cmdline) + antsMotionCorr -d 3 -i 10 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1 -s 0 -t Affine[0.005] -u 1 -e 1 + ''' + _cmd = 'antsMotionCorr' + input_spec = AntsMotionCorrInputSpec + output_spec = AntsMotionCorrOutputSpec + + + def _gen_filename(self, name): + return None + + def _format_arg(self, opt, spec, val): + if opt == 'metric_type': + return self._format_metric() + if opt == 'transformation_model': + return self._format_transform() + if opt == 'output_average_image': + return self._format_output() + return super(AntsMotionCorr, self)._format_arg(opt, spec, val) + + def _format_metric(self): + metric_str = ("-m {metric_type}[{fixed_image},{moving_image}," + "{metric_weight},{radius_or_bins},{sampling_strategy}," + "{sampling_percentage}]") + format_args = {} + if _extant(self.inputs.metric_type): + format_args['metric_type'] = self.inputs.metric_type + if _extant(self.inputs.fixed_image): + format_args['fixed_image'] = self.inputs.fixed_image + if _extant(self.inputs.moving_image): + format_args['moving_image'] = self.inputs.moving_image + if _extant(self.inputs.metric_weight): + format_args['metric_weight'] = self.inputs.metric_weight + if _extant(self.inputs.sampling_strategy): + format_args['sampling_strategy'] = self.inputs.sampling_strategy + if _extant(self.inputs.sampling_percentage): + format_args['sampling_percentage'] = self.inputs.sampling_percentage + if _extant(self.inputs.radius_or_bins): + format_args['radius_or_bins'] = self.inputs.radius_or_bins + return metric_str.format(**format_args) + + def _format_transform(self): + transform_str = "-t {}[{}]" + if (_extant(self.inputs.transformation_model) + and _extant(self.inputs.gradient_step_length)): + return transform_str.format(self.inputs.transformation_model, + self.inputs.gradient_step_length) + return "" + + def _format_output(self): + if (_extant(self.inputs.output_transform_prefix) + and _extant(self.inputs.output_warped_image) + and _extant(self.inputs.output_average_image)): + return "-o [{},{},{}]".format( + self.inputs.output_transform_prefix, + self.inputs.output_warped_image, + self.inputs.output_average_image + ) + elif _extant(self.inputs.output_average_image): + return "-o {}".format(self.inputs.output_average_image) + return "" + + # motcorr_avg.nii.gz motcorrMOCOparams.csv motcorr.nii.gz + def _list_outputs(self): + outputs = self._outputs().get() + if _extant(self.inputs.output_average_image): + outputs['average_image'] = ( + os.path.abspath(self.inputs.output_average_image) + ) + return outputs From 1e17d9e766319a9123dda0b3eecbe7c79956f1f4 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 27 Jan 2017 13:32:22 -0800 Subject: [PATCH 02/48] add documentation on where transformation format comes from. Added requires tags to metric arguments, and simplified processing of the metric argument formatting. --- nipype/interfaces/ants/preprocess.py | 39 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index df1e1f7282..3abe033a9f 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -35,12 +35,15 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", "Mattes", argstr="%s") - fixed_image = File(hash_files=False, desc="") - moving_image = File(hash_files=False, desc="") - metric_weight = traits.Float(1.0) - radius_or_bins = traits.Int(desc="") - sampling_strategy = traits.Enum("None", "Regular", "Random", None) - sampling_percentage = traits.Either(traits.Range(low=0.0, high=1.0), None) + fixed_image = File(hash_files=False, requires=['metric_type'], desc="") + moving_image = File(hash_files=False, requires=['metric_type'], + desc="This is the 4d image to be motion corrected") + metric_weight = traits.Float(1.0, requires=['metric_type']) + radius_or_bins = traits.Int(desc="", requires=['metric_type']) + sampling_strategy = traits.Enum("None", "Regular", "Random", None, + requires=['metric_type']) + sampling_percentage = traits.Either(traits.Range(low=0.0, high=1.0), None, + requires=['metric_type']) transformation_model = traits.Enum("Affine", "Rigid", argstr="%s") gradient_step_length = traits.Float(requires=['transformation_model'], @@ -112,6 +115,10 @@ class AntsMotionCorr(ANTSCommand): >>> ants_mc.inputs.use_scales_estimator = True >>> print(ants_mc.cmdline) antsMotionCorr -d 3 -i 10 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1 -s 0 -t Affine[0.005] -u 1 -e 1 + + Format and description of the affine motion correction parameters can in + this PDF starting on page 555 section 3.9.16 AffineTransform: + https://itk.org/ItkSoftwareGuide.pdf ''' _cmd = 'antsMotionCorr' input_spec = AntsMotionCorrInputSpec @@ -135,20 +142,12 @@ def _format_metric(self): "{metric_weight},{radius_or_bins},{sampling_strategy}," "{sampling_percentage}]") format_args = {} - if _extant(self.inputs.metric_type): - format_args['metric_type'] = self.inputs.metric_type - if _extant(self.inputs.fixed_image): - format_args['fixed_image'] = self.inputs.fixed_image - if _extant(self.inputs.moving_image): - format_args['moving_image'] = self.inputs.moving_image - if _extant(self.inputs.metric_weight): - format_args['metric_weight'] = self.inputs.metric_weight - if _extant(self.inputs.sampling_strategy): - format_args['sampling_strategy'] = self.inputs.sampling_strategy - if _extant(self.inputs.sampling_percentage): - format_args['sampling_percentage'] = self.inputs.sampling_percentage - if _extant(self.inputs.radius_or_bins): - format_args['radius_or_bins'] = self.inputs.radius_or_bins + metric_args = ["metric_type", "fixed_image", "moving_image", + "metric_weight", "radius_or_bins", "sampling_strategy", + "sampling_percentage"] + for metric_arg in metric_args: + if _extant(getattr(self.inputs, metric_arg)): + format_args[metric_arg] = getattr(self.inputs, metric_arg) return metric_str.format(**format_args) def _format_transform(self): From 3b0ccfa4b0aa828ab3b9cb68bee099d57715a4b4 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 27 Jan 2017 15:31:28 -0800 Subject: [PATCH 03/48] first attempt at adding antsMotionCorrStats interface. Added more outputs to antsMotionCorr in list outputs --- nipype/interfaces/ants/preprocess.py | 49 ++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 3abe033a9f..5617d8d82d 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -13,9 +13,46 @@ from ..base import TraitedSpec, File, traits, isdefined from .base import ANTSCommand, ANTSCommandInputSpec + def _extant(field): return (field is not None) and isdefined(field) +class AntsMotionCorrStatsInputSpec(ANTSCommandInputSpec): + ''' Input spec for the antsMotionCorrStats command ''' + mask = File(argstr="-x %s", mandatory=True, + desc="compute displacements within specified mask.") + moco = File( + argstr="-m %s", mandatory=True, + desc="motion correction parameter file to calculate statistics on" + ) + output = File(argstr="-o %s", hash_files=False, mandatory=True, + desc="csv file to output calculated statistics into") + framewise = traits.Bool(argstr="-f %d", + desc="do framwise summarywise stats") + output_spatial_map = File(argstr='-s %s', hash_files=False, + desc="File to output displacement magnitude to.") + +class AntsMotionCorrStatsOutputSpec(TraitedSpec): + ''' Output spec for the antsMotionCorrStats command ''' + spatial_map = File(exists=True) + output = File(exists=True) + +class AntsMotionCorrStats(ANTSCommand): + ''' Interface for the antsMotionCorrStats command ''' + + def _gen_filename(self, name): + return None + + def _list_outputs(self): + outputs = self._outputs().get() + if _extant(self.inputs.output_spatial_map): + outputs['spatial_map'] = ( + os.path.abspath(self.inputs.output_spatial_map) + ) + if _extant(self.inputs.output): + outputs['output'] = os.path.abspath(self.inputs.output) + return outputs + class AntsMotionCorrInputSpec(ANTSCommandInputSpec): '''Input spec for the antsMotionCorr command.''' dimension_desc = ( @@ -116,8 +153,8 @@ class AntsMotionCorr(ANTSCommand): >>> print(ants_mc.cmdline) antsMotionCorr -d 3 -i 10 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1 -s 0 -t Affine[0.005] -u 1 -e 1 - Format and description of the affine motion correction parameters can in - this PDF starting on page 555 section 3.9.16 AffineTransform: + Format and description of the affine motion correction parameters can be + found in this PDF starting on page 555 section 3.9.16 AffineTransform: https://itk.org/ItkSoftwareGuide.pdf ''' _cmd = 'antsMotionCorr' @@ -178,4 +215,12 @@ def _list_outputs(self): outputs['average_image'] = ( os.path.abspath(self.inputs.output_average_image) ) + if _extant(self.inputs.output_warped_image): + outputs['warped_image'] = ( + os.path.abspath(self.inputs.output_warped_image) + ) + if _extant(self.inputs.output_transform_prefix): + outputs['composite_transform'] = '{}MOCOparams.csv'.format( + self.inputs.output_tranform_prefix + ) return outputs From 47df18129e7f42740cca8f90b1813d597d6ff177 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 27 Jan 2017 16:46:04 -0800 Subject: [PATCH 04/48] add a gen_filename entry for output_average_image in antsMotionCorr, added default to dimensionality --- nipype/interfaces/ants/preprocess.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 5617d8d82d..ad0d164804 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -12,6 +12,7 @@ from ..base import TraitedSpec, File, traits, isdefined from .base import ANTSCommand, ANTSCommandInputSpec +from ..utils.filemanip import split_filename def _extant(field): @@ -61,19 +62,19 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): "the dimensionality from the input image." ) dimensionality = traits.Enum(3, 2, argstr='-d %d', usedefault=False, - position=0, desc=dimension_desc) + position=0, desc=dimension_desc, default=3) average_image = File(argstr='-a %s', position=1, desc="Average the input time series image.") - output_average_image = File(argstr="%s", hash_files=False, desc="") + output_average_image = File(argstr="%s", hash_files=False, desc="", genfile) output_transform_prefix = traits.Str() output_warped_image = File(hash_files=False, desc="") metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", "Mattes", argstr="%s") - fixed_image = File(hash_files=False, requires=['metric_type'], desc="") - moving_image = File(hash_files=False, requires=['metric_type'], + fixed_image = File(requires=['metric_type'], desc="") + moving_image = File(requires=['metric_type'], desc="This is the 4d image to be motion corrected") metric_weight = traits.Float(1.0, requires=['metric_type']) radius_or_bins = traits.Int(desc="", requires=['metric_type']) @@ -163,6 +164,10 @@ class AntsMotionCorr(ANTSCommand): def _gen_filename(self, name): + if name == 'output_average_image' and _extant(self.inputs.average): + pth, fname, ext = split_filename(self.inputs.average) + new_fname = '{}{}{}'.format(fname, '_avg', ext) + return os.path.join(pth, new_fname) return None def _format_arg(self, opt, spec, val): From 39d986a41f43e056d895a14dedf0769631b4759a Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 30 Jan 2017 10:42:02 -0800 Subject: [PATCH 05/48] fix syntax errors in ants preprocess --- nipype/interfaces/ants/preprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index ad0d164804..8a47911bf1 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -12,7 +12,7 @@ from ..base import TraitedSpec, File, traits, isdefined from .base import ANTSCommand, ANTSCommandInputSpec -from ..utils.filemanip import split_filename +from ...utils.filemanip import split_filename def _extant(field): @@ -67,7 +67,7 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): average_image = File(argstr='-a %s', position=1, desc="Average the input time series image.") - output_average_image = File(argstr="%s", hash_files=False, desc="", genfile) + output_average_image = File(argstr="%s", hash_files=False, desc="", genfile=True) output_transform_prefix = traits.Str() output_warped_image = File(hash_files=False, desc="") From 3b5f99d1feb17e45fe6790f892ae5bc4eddd426b Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Feb 2017 16:11:48 -0800 Subject: [PATCH 06/48] get argstrs in line for ants motion correction --- nipype/interfaces/ants/preprocess.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 8a47911bf1..56283bd8df 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -61,15 +61,17 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): "specified-dimensional image. If not specified, N4 tries to infer " "the dimensionality from the input image." ) - dimensionality = traits.Enum(3, 2, argstr='-d %d', usedefault=False, + dimensionality = traits.Enum(3, 2, argstr='-d %d', usedefault=True, position=0, desc=dimension_desc, default=3) - average_image = File(argstr='-a %s', position=1, + average_image = File(argstr='-a %s', position=1, exists=False, desc="Average the input time series image.") - output_average_image = File(argstr="%s", hash_files=False, desc="", genfile=True) + output_average_image = File(hash_files=False, desc="", argstr="%s", + genfile=True, exists=False, usedefault=True) output_transform_prefix = traits.Str() - output_warped_image = File(hash_files=False, desc="") + output_warped_image = File(hash_files=False, desc="", + exists=False) metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", "Mattes", argstr="%s") @@ -120,7 +122,7 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): class AntsMotionCorrOutputSpec(TraitedSpec): '''Output spec for the antsMotionCorr command''' average_image = File(exists=True, desc='Average of an image') - composite_transform = File(exists=True, desc='Composite transform file') + composite_transform = File(desc='Composite transform file') inverse_composite_transform = File(desc='Inverse composite transform file') warped_image = File(desc="Outputs warped image") inverse_warped_image = File(desc="Outputs the inverse of the warped image") @@ -164,10 +166,17 @@ class AntsMotionCorr(ANTSCommand): def _gen_filename(self, name): - if name == 'output_average_image' and _extant(self.inputs.average): - pth, fname, ext = split_filename(self.inputs.average) + if name == 'output_average_image': + if _extant(self.inputs.average_image): + pth, fname, ext = split_filename(self.inputs.average_image) + else: + pth, fname, ext = split_filename(self.inputs.fixed_image) new_fname = '{}{}{}'.format(fname, '_avg', ext) return os.path.join(pth, new_fname) + if name == 'ouput_warped_image' and _extant(self.inputs.fixed_image): + pth, fname, ext = split_filename(self.inputs.fixed_image) + new_fname = '{}{}{}'.format(fname, '_warped', ext) + return os.path.join(pth, new_fname) return None def _format_arg(self, opt, spec, val): @@ -176,6 +185,7 @@ def _format_arg(self, opt, spec, val): if opt == 'transformation_model': return self._format_transform() if opt == 'output_average_image': + self.inputs.output_average_image = self._gen_filename("output_average_image") return self._format_output() return super(AntsMotionCorr, self)._format_arg(opt, spec, val) @@ -198,7 +208,7 @@ def _format_transform(self): and _extant(self.inputs.gradient_step_length)): return transform_str.format(self.inputs.transformation_model, self.inputs.gradient_step_length) - return "" + return " bad format " def _format_output(self): if (_extant(self.inputs.output_transform_prefix) @@ -226,6 +236,6 @@ def _list_outputs(self): ) if _extant(self.inputs.output_transform_prefix): outputs['composite_transform'] = '{}MOCOparams.csv'.format( - self.inputs.output_tranform_prefix + self.inputs.output_transform_prefix ) return outputs From 1c57878ba2709c318b52951ac531ffbe8c6b0de0 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Feb 2017 21:57:41 -0800 Subject: [PATCH 07/48] add interface to convert ants motion correction matrices to fsl style motion parameters --- nipype/interfaces/ants/preprocess.py | 72 +++++++++++++++- .../ants/tests/test_auto_AntsMotionCorr.py | 83 +++++++++++++++++++ .../tests/test_auto_AntsMotionCorrStats.py | 25 ++++++ .../tests/test_auto_antsBrainExtraction.py | 16 ++++ 4 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py create mode 100644 nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 56283bd8df..1057385d6c 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -8,9 +8,14 @@ >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data')) >>> os.chdir(datadir) """ +import csv +import math import os - -from ..base import TraitedSpec, File, traits, isdefined + +from nipype.interfaces.base import BaseInterface, \ + BaseInterfaceInputSpec, traits, File, TraitedSpec +from ..base import (BaseInterface, BaseInterfaceInputSpec, TraitedSpec, File, + traits, isdefined) from .base import ANTSCommand, ANTSCommandInputSpec from ...utils.filemanip import split_filename @@ -26,7 +31,7 @@ class AntsMotionCorrStatsInputSpec(ANTSCommandInputSpec): argstr="-m %s", mandatory=True, desc="motion correction parameter file to calculate statistics on" ) - output = File(argstr="-o %s", hash_files=False, mandatory=True, + output = File(argstr="-o %s", hash_files=False, genfile=True, desc="csv file to output calculated statistics into") framewise = traits.Bool(argstr="-f %d", desc="do framwise summarywise stats") @@ -41,7 +46,13 @@ class AntsMotionCorrStatsOutputSpec(TraitedSpec): class AntsMotionCorrStats(ANTSCommand): ''' Interface for the antsMotionCorrStats command ''' + _cmd = 'antsMotionCorrStats' + input_spec = AntsMotionCorrStatsInputSpec + output_spec = AntsMotionCorrStatsOutputSpec + def _gen_filename(self, name): + if name == 'output': + return "frame_displacement.csv" return None def _list_outputs(self): @@ -107,18 +118,21 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): desc=("This option sets the number of images to use to construct the " "template image.") ) + use_fixed_reference_image = traits.Bool( argstr="-u %d", default=True, desc=("use a fixed reference image instead of the neighor in the time " "series.") ) + use_scales_estimator = traits.Bool( argstr="-e %d", default=True, desc="use the scale estimator to control optimization." ) + class AntsMotionCorrOutputSpec(TraitedSpec): '''Output spec for the antsMotionCorr command''' average_image = File(exists=True, desc='Average of an image') @@ -235,7 +249,57 @@ def _list_outputs(self): os.path.abspath(self.inputs.output_warped_image) ) if _extant(self.inputs.output_transform_prefix): - outputs['composite_transform'] = '{}MOCOparams.csv'.format( + fname = '{}MOCOparams.csv'.format( self.inputs.output_transform_prefix ) + outputs['composite_transform'] = os.path.abspath(fname) + return outputs + +class AntsMatrixConversionInputSpec(BaseInterfaceInputSpec): + matrix = File( + exists=True, + desc='Motion crrection matrices to be converted into FSL style motion parameters', + mandatory=True + ) + +class AntsMatrixConversionOutputSpec(TraitedSpec): + parameters = File(exists=True, desc="parameters to be output") + + +class AntsMatrixConversion(BaseInterface): + ''' Take antsMotionCorr motion output as input, convert to FSL style parameter files''' + input_spec = AntsMatrixConversionInputSpec + output_spec = AntsMatrixConversionOutputSpec + + def _run_interface(self, runtime): + in_fp = open(self.inputs.matrix) + in_data = csv.reader(in_fp) + pars = [] + + # Ants motion correction output has a single line header that we ignore + next(in_data) + + for x in in_data: + t1 = math.atan2(float(x[7]), float(x[10])) + c2 = math.sqrt((float(x[2]) * float(x[2])) + (float(x[3]) * float(x[3]))) + t2 = math.atan2(-float(x[4]), c2) + t3 = math.atan2(float(x[3]), float(x[2])) + parameters = "{:.8f} {:.8f} {:.8f} {:.8f} {:.8f} {:.8f}" + pars.append(parameters.format(t1, t2, t3, float(x[11]), float(x[12]), + float(x[13]))) + + pth, fname, _ = split_filename(self.inputs.matrix) + new_fname = '{}{}'.format(fname, '.par') + parameters = os.path.join(pth, new_fname) + with open(parameters, mode='wt') as out_fp: + out_fp.write('\n'.join(pars)) + in_fp.close() + return runtime + + def _list_outputs(self): + outputs = self._outputs().get() + pth, fname, _ = split_filename(self.inputs.matrix) + new_fname = '{}{}'.format(fname, '.par') + out_file = os.path.join(pth, new_fname) + outputs["parameters"] = out_file return outputs diff --git a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py new file mode 100644 index 0000000000..426fc9f959 --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py @@ -0,0 +1,83 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..preprocess import AntsMotionCorr + + +def test_AntsMotionCorr_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + average_image=dict(argstr='-a %s', + position=1, + ), + dimensionality=dict(argstr='-d %d', + position=0, + usedefault=False, + ), + environ=dict(nohash=True, + usedefault=True, + ), + fixed_image=dict(requires=['metric_type'], + ), + gradient_step_length=dict(requires=['transformation_model'], + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + iterations=dict(argstr='-i %d', + ), + metric_type=dict(argstr='%s', + ), + metric_weight=dict(requires=['metric_type'], + ), + moving_image=dict(requires=['metric_type'], + ), + n_images=dict(argstr='-n %d', + ), + num_threads=dict(nohash=True, + usedefault=True, + ), + output_average_image=dict(argstr='%s', + genfile=True, + hash_files=False, + ), + output_transform_prefix=dict(), + output_warped_image=dict(hash_files=False, + ), + radius_or_bins=dict(requires=['metric_type'], + ), + sampling_percentage=dict(requires=['metric_type'], + ), + sampling_strategy=dict(requires=['metric_type'], + ), + shrink_factors=dict(argstr='-f %d', + ), + smoothing_sigmas=dict(argstr='-s %d', + ), + terminal_output=dict(nohash=True, + ), + transformation_model=dict(argstr='%s', + ), + use_fixed_reference_image=dict(argstr='-u %d', + ), + use_scales_estimator=dict(argstr='-e %d', + ), + ) + inputs = AntsMotionCorr.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_AntsMotionCorr_outputs(): + output_map = dict(average_image=dict(), + composite_transform=dict(), + inverse_composite_transform=dict(), + inverse_warped_image=dict(), + save_state=dict(), + warped_image=dict(), + ) + outputs = AntsMotionCorr.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/ants/tests/test_auto_AntsMotionCorrStats.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py new file mode 100644 index 0000000000..09fb98b3d7 --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py @@ -0,0 +1,25 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..preprocess import AntsMotionCorrStats + + +def test_AntsMotionCorrStats_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + num_threads=dict(nohash=True, + usedefault=True, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = AntsMotionCorrStats.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/ants/tests/test_auto_antsBrainExtraction.py b/nipype/interfaces/ants/tests/test_auto_antsBrainExtraction.py index 9abcaa99bb..7597105a3f 100644 --- a/nipype/interfaces/ants/tests/test_auto_antsBrainExtraction.py +++ b/nipype/interfaces/ants/tests/test_auto_antsBrainExtraction.py @@ -55,7 +55,23 @@ def test_antsBrainExtraction_inputs(): def test_antsBrainExtraction_outputs(): output_map = dict(BrainExtractionBrain=dict(), + BrainExtractionCSF=dict(), + BrainExtractionGM=dict(), + BrainExtractionInitialAffine=dict(), + BrainExtractionInitialAffineFixed=dict(), + BrainExtractionInitialAffineMoving=dict(), + BrainExtractionLaplacian=dict(), BrainExtractionMask=dict(), + BrainExtractionPrior0GenericAffine=dict(), + BrainExtractionPrior1InverseWarp=dict(), + BrainExtractionPrior1Warp=dict(), + BrainExtractionPriorWarped=dict(), + BrainExtractionSegmentation=dict(), + BrainExtractionTemplateLaplacian=dict(), + BrainExtractionTmp=dict(), + BrainExtractionWM=dict(), + N4Corrected0=dict(), + N4Truncated0=dict(), ) outputs = antsBrainExtraction.output_spec() From 06de3051c39a9acc0b872afdc7b38767e0f264e2 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 11:25:54 -0800 Subject: [PATCH 08/48] fixup input spec --- nipype/interfaces/ants/preprocess.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 1057385d6c..8af6f0825f 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -12,8 +12,8 @@ import math import os -from nipype.interfaces.base import BaseInterface, \ - BaseInterfaceInputSpec, traits, File, TraitedSpec +from nipype.interfaces.base import (BaseInterface, BaseInterfaceInputSpec, + traits, File, TraitedSpec) from ..base import (BaseInterface, BaseInterfaceInputSpec, TraitedSpec, File, traits, isdefined) from .base import ANTSCommand, ANTSCommandInputSpec @@ -78,15 +78,14 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): average_image = File(argstr='-a %s', position=1, exists=False, desc="Average the input time series image.") - output_average_image = File(hash_files=False, desc="", argstr="%s", - genfile=True, exists=False, usedefault=True) + output_average_image = traits.Str(desc="Filename to save average of input image as.", argstr="%s") + output_transform_prefix = traits.Str() - output_warped_image = File(hash_files=False, desc="", - exists=False) + output_warped_image = Str(desc="Name to save motion corrected image as.") metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", "Mattes", argstr="%s") - fixed_image = File(requires=['metric_type'], desc="") + fixed_image = File(requires=['metric_type'], desc="Fixed image to do motion correction with respect to.") moving_image = File(requires=['metric_type'], desc="This is the 4d image to be motion corrected") metric_weight = traits.Float(1.0, requires=['metric_type']) @@ -120,15 +119,15 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): ) use_fixed_reference_image = traits.Bool( + True, argstr="-u %d", - default=True, desc=("use a fixed reference image instead of the neighor in the time " "series.") ) use_scales_estimator = traits.Bool( + True, argstr="-e %d", - default=True, desc="use the scale estimator to control optimization." ) @@ -237,7 +236,6 @@ def _format_output(self): return "-o {}".format(self.inputs.output_average_image) return "" - # motcorr_avg.nii.gz motcorrMOCOparams.csv motcorr.nii.gz def _list_outputs(self): outputs = self._outputs().get() if _extant(self.inputs.output_average_image): From df7204090fa159348b6bb43e320f0428b1f8c1f8 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 11:29:28 -0800 Subject: [PATCH 09/48] add descriptions to antsmotioncorrstats outputs --- nipype/interfaces/ants/preprocess.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 8af6f0825f..3781ef2a2f 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -40,8 +40,8 @@ class AntsMotionCorrStatsInputSpec(ANTSCommandInputSpec): class AntsMotionCorrStatsOutputSpec(TraitedSpec): ''' Output spec for the antsMotionCorrStats command ''' - spatial_map = File(exists=True) - output = File(exists=True) + spatial_map = File(desc="output image of displacement magnitude", exists=True) + output = File(desc="CSV file containg motion correction statistics", exists=True) class AntsMotionCorrStats(ANTSCommand): ''' Interface for the antsMotionCorrStats command ''' @@ -73,7 +73,7 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): "the dimensionality from the input image." ) dimensionality = traits.Enum(3, 2, argstr='-d %d', usedefault=True, - position=0, desc=dimension_desc, default=3) + position=0, desc=dimension_desc) average_image = File(argstr='-a %s', position=1, exists=False, desc="Average the input time series image.") @@ -134,9 +134,9 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): class AntsMotionCorrOutputSpec(TraitedSpec): '''Output spec for the antsMotionCorr command''' - average_image = File(exists=True, desc='Average of an image') - composite_transform = File(desc='Composite transform file') - inverse_composite_transform = File(desc='Inverse composite transform file') + average_image = File(exists=True, desc="Average of an image") + composite_transform = File(desc="Composite transform file") + inverse_composite_transform = File(desc="Inverse composite transform file") warped_image = File(desc="Outputs warped image") inverse_warped_image = File(desc="Outputs the inverse of the warped image") save_state = File(desc="The saved registration state to be restored") From 99c9fb5c6fa3aee11dd66798ff38adad4db3a6f4 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 11:36:03 -0800 Subject: [PATCH 10/48] instantiate Str trait properly --- nipype/interfaces/ants/preprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 3781ef2a2f..13b16c9571 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -81,7 +81,7 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): output_average_image = traits.Str(desc="Filename to save average of input image as.", argstr="%s") output_transform_prefix = traits.Str() - output_warped_image = Str(desc="Name to save motion corrected image as.") + output_warped_image = traits.Str(desc="Name to save motion corrected image as.") metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", "Mattes", argstr="%s") From 9bb5208f29b33b68e39a79c9cdf32ce46a13f182 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 11:39:32 -0800 Subject: [PATCH 11/48] remove trailing whitespace --- nipype/interfaces/ants/preprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 13b16c9571..488f515eef 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -11,7 +11,7 @@ import csv import math import os - + from nipype.interfaces.base import (BaseInterface, BaseInterfaceInputSpec, traits, File, TraitedSpec) from ..base import (BaseInterface, BaseInterfaceInputSpec, TraitedSpec, File, @@ -79,7 +79,7 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): desc="Average the input time series image.") output_average_image = traits.Str(desc="Filename to save average of input image as.", argstr="%s") - + output_transform_prefix = traits.Str() output_warped_image = traits.Str(desc="Name to save motion corrected image as.") From 691a7b59923253d3bac9724843ee4587706feb10 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Wed, 1 Mar 2017 14:46:03 -0800 Subject: [PATCH 12/48] Update preprocess.py --- nipype/interfaces/ants/preprocess.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 488f515eef..71ac8535eb 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -12,10 +12,8 @@ import math import os -from nipype.interfaces.base import (BaseInterface, BaseInterfaceInputSpec, - traits, File, TraitedSpec) from ..base import (BaseInterface, BaseInterfaceInputSpec, TraitedSpec, File, - traits, isdefined) + traits, isdefined, Str) from .base import ANTSCommand, ANTSCommandInputSpec from ...utils.filemanip import split_filename @@ -78,10 +76,10 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): average_image = File(argstr='-a %s', position=1, exists=False, desc="Average the input time series image.") - output_average_image = traits.Str(desc="Filename to save average of input image as.", argstr="%s") + output_average_image = Str(desc="Filename to save average of input image as.", argstr="%s") - output_transform_prefix = traits.Str() - output_warped_image = traits.Str(desc="Name to save motion corrected image as.") + output_transform_prefix = Str() + output_warped_image = Str(desc="Name to save motion corrected image as.") metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", "Mattes", argstr="%s") From 84c397de0308c640fdaea13eb137264e68d5066e Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 14:48:59 -0800 Subject: [PATCH 13/48] reorder genfilename logic --- nipype/interfaces/ants/preprocess.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 488f515eef..0c8e939c89 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -78,7 +78,7 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): average_image = File(argstr='-a %s', position=1, exists=False, desc="Average the input time series image.") - output_average_image = traits.Str(desc="Filename to save average of input image as.", argstr="%s") + output_average_image = traits.File(desc="Filename to save average of input image as.", genfile=True) output_transform_prefix = traits.Str() output_warped_image = traits.Str(desc="Name to save motion corrected image as.") @@ -180,10 +180,11 @@ class AntsMotionCorr(ANTSCommand): def _gen_filename(self, name): if name == 'output_average_image': - if _extant(self.inputs.average_image): - pth, fname, ext = split_filename(self.inputs.average_image) - else: - pth, fname, ext = split_filename(self.inputs.fixed_image) + if _extant(self.inputs.fixed_image): + return self.inputs.fixed_image + if not _extant(self.inputs.average_image): + raise ValueError("Either fixed_image or average_image must be defined") + pth, fname, ext = split_filename(self.inputs.average_image) new_fname = '{}{}{}'.format(fname, '_avg', ext) return os.path.join(pth, new_fname) if name == 'ouput_warped_image' and _extant(self.inputs.fixed_image): @@ -198,8 +199,12 @@ def _format_arg(self, opt, spec, val): if opt == 'transformation_model': return self._format_transform() if opt == 'output_average_image': - self.inputs.output_average_image = self._gen_filename("output_average_image") + if not _extant(self.inputs.output_average_image): + self.inputs.output_average_image = self._gen_filename("output_average_image") + else: + self.inputs.output_average_image = "wat.nii.gz" return self._format_output() + self.inputs.output_average_image = "wat.nii.gz" return super(AntsMotionCorr, self)._format_arg(opt, spec, val) def _format_metric(self): From 97b32efb77dca600e2fddee0736640a9aec93a89 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 15:18:20 -0800 Subject: [PATCH 14/48] fix output_average_image decleration. --- nipype/interfaces/ants/preprocess.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index d6acb8bc5b..4166fdde8a 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -76,9 +76,12 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): average_image = File(argstr='-a %s', position=1, exists=False, desc="Average the input time series image.") - output_average_image = traits.File(desc="Filename to save average of input image as.", genfile=True) + output_average_image = traits.File(desc="Filename to save average of input image as.", + genfile=True, hash_files=False, argstr='%s') - output_transform_prefix = Str() + output_transform_prefix = Str( + desc="string to prepend to file containg all of the transformation parameters" + ) output_warped_image = Str(desc="Name to save motion corrected image as.") metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", @@ -148,7 +151,6 @@ class AntsMotionCorr(ANTSCommand): >>> ants_mc.inputs.dimensionality = 3 >>> ants_mc.inputs.output_transform_prefix = "motcorr" >>> ants_mc.inputs.output_warped_image = "warped.nii.gz" - >>> ants_mc.inputs.output_average_image = "average_image.nii.gz" >>> ants_mc.inputs.metric_type = "GC" >>> ants_mc.inputs.fixed_image = "average_image.nii.gz" >>> ants_mc.inputs.moving_image = "input.nii.gz" @@ -197,10 +199,7 @@ def _format_arg(self, opt, spec, val): if opt == 'transformation_model': return self._format_transform() if opt == 'output_average_image': - if not _extant(self.inputs.output_average_image): - self.inputs.output_average_image = self._gen_filename("output_average_image") - else: - self.inputs.output_average_image = "wat.nii.gz" + self.inputs.output_average_image = self._gen_filename("output_average_image") return self._format_output() self.inputs.output_average_image = "wat.nii.gz" return super(AntsMotionCorr, self)._format_arg(opt, spec, val) From ff516679c954f042a75fdad5e22f56af83136885 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 15:23:11 -0800 Subject: [PATCH 15/48] remove test wat --- nipype/interfaces/ants/preprocess.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 4166fdde8a..3e69960baa 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -201,7 +201,6 @@ def _format_arg(self, opt, spec, val): if opt == 'output_average_image': self.inputs.output_average_image = self._gen_filename("output_average_image") return self._format_output() - self.inputs.output_average_image = "wat.nii.gz" return super(AntsMotionCorr, self)._format_arg(opt, spec, val) def _format_metric(self): From 722a51c919bea62e178b716a757ac17ccf50a915 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 1 Mar 2017 16:33:36 -0800 Subject: [PATCH 16/48] update example --- nipype/interfaces/ants/preprocess.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 3e69960baa..ab755814f3 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -147,25 +147,25 @@ class AntsMotionCorr(ANTSCommand): Examples ------- >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr - >>> ants_mc = AntsMotionCorr() - >>> ants_mc.inputs.dimensionality = 3 - >>> ants_mc.inputs.output_transform_prefix = "motcorr" - >>> ants_mc.inputs.output_warped_image = "warped.nii.gz" - >>> ants_mc.inputs.metric_type = "GC" - >>> ants_mc.inputs.fixed_image = "average_image.nii.gz" - >>> ants_mc.inputs.moving_image = "input.nii.gz" + >>> ants_mc = AntsMotionCorr() + >>> ants_mc.inputs.metric_type = 'GC' >>> ants_mc.inputs.metric_weight = 1 >>> ants_mc.inputs.radius_or_bins = 1 >>> ants_mc.inputs.sampling_strategy = "Random" >>> ants_mc.inputs.sampling_percentage = 0.05 - >>> ants_mc.inputs.transformation_model = "Affine" - >>> ants_mc.inputs.gradient_step_length = 0.005 >>> ants_mc.inputs.iterations = 10 >>> ants_mc.inputs.smoothing_sigmas = 0 >>> ants_mc.inputs.shrink_factors = 1 >>> ants_mc.inputs.n_images = 10 >>> ants_mc.inputs.use_fixed_reference_image = True >>> ants_mc.inputs.use_scales_estimator = True + >>> ants_mc.inputs.output_average_image = 'wat' + >>> ants_mc.inputs.output_warped_image = 'warped.nii.gz' + >>> ants_mc.inputs.output_transform_prefix = 'motcorr' + >>> ants_mc.inputs.transformation_model = 'Affine' + >>> ants_mc.inputs.gradient_step_length = 0.005 + >>> ants_mc.inputs.fixed_image = "average_image.nii.gz" + >>> ants_mc.inputs.moving_image = "input.nii.gz" >>> print(ants_mc.cmdline) antsMotionCorr -d 3 -i 10 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1 -s 0 -t Affine[0.005] -u 1 -e 1 From f4ab57e2e8350becb4965c516160dbef64cd59db Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 2 Mar 2017 09:32:51 -0800 Subject: [PATCH 17/48] remake specs --- .../tests/test_auto_AntsMatrixConversion.py | 27 +++++++++++++++++++ .../ants/tests/test_auto_AntsMotionCorr.py | 6 ++--- .../tests/test_auto_AntsMotionCorrStats.py | 26 ++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py diff --git a/nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py b/nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py new file mode 100644 index 0000000000..f7ecb64d2b --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py @@ -0,0 +1,27 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..preprocess import AntsMatrixConversion + + +def test_AntsMatrixConversion_inputs(): + input_map = dict(ignore_exception=dict(nohash=True, + usedefault=True, + ), + matrix=dict(mandatory=True, + ), + ) + inputs = AntsMatrixConversion.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_AntsMatrixConversion_outputs(): + output_map = dict(parameters=dict(), + ) + outputs = AntsMatrixConversion.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/ants/tests/test_auto_AntsMotionCorr.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py index 426fc9f959..d340a437bf 100644 --- a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py +++ b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py @@ -1,4 +1,5 @@ # AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals from ..preprocess import AntsMotionCorr @@ -10,7 +11,7 @@ def test_AntsMotionCorr_inputs(): ), dimensionality=dict(argstr='-d %d', position=0, - usedefault=False, + usedefault=True, ), environ=dict(nohash=True, usedefault=True, @@ -40,8 +41,7 @@ def test_AntsMotionCorr_inputs(): hash_files=False, ), output_transform_prefix=dict(), - output_warped_image=dict(hash_files=False, - ), + output_warped_image=dict(), radius_or_bins=dict(requires=['metric_type'], ), sampling_percentage=dict(requires=['metric_type'], diff --git a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py index 09fb98b3d7..e81fb4f95d 100644 --- a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py +++ b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py @@ -1,4 +1,5 @@ # AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals from ..preprocess import AntsMotionCorrStats @@ -8,12 +9,27 @@ def test_AntsMotionCorrStats_inputs(): environ=dict(nohash=True, usedefault=True, ), + framewise=dict(argstr='-f %d', + ), ignore_exception=dict(nohash=True, usedefault=True, ), + mask=dict(argstr='-x %s', + mandatory=True, + ), + moco=dict(argstr='-m %s', + mandatory=True, + ), num_threads=dict(nohash=True, usedefault=True, ), + output=dict(argstr='-o %s', + genfile=True, + hash_files=False, + ), + output_spatial_map=dict(argstr='-s %s', + hash_files=False, + ), terminal_output=dict(nohash=True, ), ) @@ -23,3 +39,13 @@ def test_AntsMotionCorrStats_inputs(): for metakey, value in list(metadata.items()): assert getattr(inputs.traits()[key], metakey) == value + +def test_AntsMotionCorrStats_outputs(): + output_map = dict(output=dict(), + spatial_map=dict(), + ) + outputs = AntsMotionCorrStats.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value From f28151b7f161a9e7022a608c0eeb540028a9b2ad Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 2 Mar 2017 10:59:54 -0800 Subject: [PATCH 18/48] add more descriptions --- nipype/interfaces/ants/preprocess.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index ab755814f3..d09db7f1b9 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -74,19 +74,28 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): position=0, desc=dimension_desc) average_image = File(argstr='-a %s', position=1, exists=False, - desc="Average the input time series image.") + desc="4D image to take an average of.") output_average_image = traits.File(desc="Filename to save average of input image as.", genfile=True, hash_files=False, argstr='%s') output_transform_prefix = Str( - desc="string to prepend to file containg all of the transformation parameters" + desc="string to prepend to file containg the transformation parameters" ) output_warped_image = Str(desc="Name to save motion corrected image as.") + metric_type_desc = ( + "GC : global correlation, CC: ANTS neighborhood cross correlation, " + "MI: Mutual information, and Demons: Thirion's Demons " + "(modified mean-squares). Note that the metricWeight is currently not " + "used. Rather, it is a temporary place holder until multivariate " + "metrics are available for a single stage." + ) + metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", - "Mattes", argstr="%s") - fixed_image = File(requires=['metric_type'], desc="Fixed image to do motion correction with respect to.") + "Mattes", argstr="%s", desc=metric_type_desc) + fixed_image = File(requires=['metric_type'], + desc="Fixed image to do motion correction with respect to.") moving_image = File(requires=['metric_type'], desc="This is the 4d image to be motion corrected") metric_weight = traits.Float(1.0, requires=['metric_type']) @@ -147,7 +156,7 @@ class AntsMotionCorr(ANTSCommand): Examples ------- >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr - >>> ants_mc = AntsMotionCorr() + >>> ants_mc = AntsMotionCorr() >>> ants_mc.inputs.metric_type = 'GC' >>> ants_mc.inputs.metric_weight = 1 >>> ants_mc.inputs.radius_or_bins = 1 From 40edcbf2f467030d0cc2c7fff1ff82c9f7729c5a Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 2 Mar 2017 12:14:34 -0800 Subject: [PATCH 19/48] fix output_average_image not taking user defined file name --- nipype/interfaces/ants/preprocess.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index d09db7f1b9..9bcf0215b7 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -1,12 +1,6 @@ # -*- coding: utf-8 -*- """The ants module provides basic functions for interfacing with ants functions. - - Change directory to provide relative paths for doctests - >>> import os - >>> filepath = os.path.dirname( os.path.realpath( __file__ ) ) - >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data')) - >>> os.chdir(datadir) """ import csv import math @@ -181,6 +175,8 @@ class AntsMotionCorr(ANTSCommand): Format and description of the affine motion correction parameters can be found in this PDF starting on page 555 section 3.9.16 AffineTransform: https://itk.org/ItkSoftwareGuide.pdf + + https://github.com/stnava/ANTs/blob/master/Scripts/antsMotionCorrExample ''' _cmd = 'antsMotionCorr' input_spec = AntsMotionCorrInputSpec @@ -188,11 +184,20 @@ class AntsMotionCorr(ANTSCommand): def _gen_filename(self, name): + ''' + If a fixed image is specified we are not going to be outputting + a newly created averaged image. The output flag for calls to + antsMotionCorr with a fixed image have a value set for an average + image. In all of the examples this value always matches the fixed + image name. + ''' if name == 'output_average_image': if _extant(self.inputs.fixed_image): return self.inputs.fixed_image if not _extant(self.inputs.average_image): raise ValueError("Either fixed_image or average_image must be defined") + if _extant(self.inputs.output_average_image): + return self.inputs.output_average_image pth, fname, ext = split_filename(self.inputs.average_image) new_fname = '{}{}{}'.format(fname, '_avg', ext) return os.path.join(pth, new_fname) From 5c4193b6904bd6e074ecb290175d96ee4294807e Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 2 Mar 2017 12:17:57 -0800 Subject: [PATCH 20/48] add examples of using AntsMotionCorr to take average of an image --- nipype/interfaces/ants/preprocess.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 9bcf0215b7..5634875c8d 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -149,6 +149,7 @@ class AntsMotionCorr(ANTSCommand): ''' Examples ------- + >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr >>> ants_mc = AntsMotionCorr() >>> ants_mc.inputs.metric_type = 'GC' @@ -172,6 +173,18 @@ class AntsMotionCorr(ANTSCommand): >>> print(ants_mc.cmdline) antsMotionCorr -d 3 -i 10 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1 -s 0 -t Affine[0.005] -u 1 -e 1 + >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr + >>> ants_avg = AntsMotionCorr() + >>> ants_avg.inputs.average_image = 'input.nii.gz' + >>> ants_avg.inputs.output_average_image = 'avg_out.nii.gz' + >>> print(ants_avg.cmdline) + antsMotionCorr -d 3 -a input.nii.gz -o avg_out.nii.gz + + >>> ants_avg = AntsMotionCorr() + >>> ants_avg.inputs.average_image = 'input.nii.gz' + >>> print(ants_avg.cmdline) + antsMotionCorr -d 3 -a input.nii.gz -o input_avg.nii.gz + Format and description of the affine motion correction parameters can be found in this PDF starting on page 555 section 3.9.16 AffineTransform: https://itk.org/ItkSoftwareGuide.pdf From a2ca34e9ad525c570790726273903d4227ab6785 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 2 Mar 2017 12:22:37 -0800 Subject: [PATCH 21/48] add note about lack of origin of rotation for motioncorrection conversion interface --- nipype/interfaces/ants/preprocess.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 5634875c8d..10027dca14 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -293,7 +293,10 @@ class AntsMatrixConversionOutputSpec(TraitedSpec): class AntsMatrixConversion(BaseInterface): - ''' Take antsMotionCorr motion output as input, convert to FSL style parameter files''' + ''' + Take antsMotionCorr motion output as input, convert to FSL style + parameter files. Currently does not output origin of rotation. + ''' input_spec = AntsMatrixConversionInputSpec output_spec = AntsMatrixConversionOutputSpec From 6935720869e7f83d2cea213b1e8111e9ef46dfd0 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 2 Mar 2017 12:25:22 -0800 Subject: [PATCH 22/48] remove test wat --- nipype/interfaces/ants/preprocess.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 10027dca14..816e0338c5 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -163,7 +163,6 @@ class AntsMotionCorr(ANTSCommand): >>> ants_mc.inputs.n_images = 10 >>> ants_mc.inputs.use_fixed_reference_image = True >>> ants_mc.inputs.use_scales_estimator = True - >>> ants_mc.inputs.output_average_image = 'wat' >>> ants_mc.inputs.output_warped_image = 'warped.nii.gz' >>> ants_mc.inputs.output_transform_prefix = 'motcorr' >>> ants_mc.inputs.transformation_model = 'Affine' From 24d1e4e7c85582c4fa76b97f8c642a7f8c27cf0d Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 2 Mar 2017 12:42:22 -0800 Subject: [PATCH 23/48] Raise error if unable to format trasnformation model argument --- nipype/interfaces/ants/preprocess.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 816e0338c5..2904dbddf7 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -198,9 +198,9 @@ class AntsMotionCorr(ANTSCommand): def _gen_filename(self, name): ''' If a fixed image is specified we are not going to be outputting - a newly created averaged image. The output flag for calls to - antsMotionCorr with a fixed image have a value set for an average - image. In all of the examples this value always matches the fixed + a newly created averaged image. The output flag for calls to + antsMotionCorr with a fixed image have a value set for an average + image. In all of the examples this value always matches the fixed image name. ''' if name == 'output_average_image': @@ -248,7 +248,7 @@ def _format_transform(self): and _extant(self.inputs.gradient_step_length)): return transform_str.format(self.inputs.transformation_model, self.inputs.gradient_step_length) - return " bad format " + raise ValueError("Unable to format transformation_model argument") def _format_output(self): if (_extant(self.inputs.output_transform_prefix) From 42f85df33f6658b30bdf3fbcfaeb4e1dc7984154 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 3 Mar 2017 13:25:59 -0800 Subject: [PATCH 24/48] add write frame displacements arguments to antsmotioncorr. Also updated iterations, sigmas, and shrink factors, to be lists' --- nipype/interfaces/ants/preprocess.py | 46 ++++++++++++++----- .../ants/tests/test_auto_AntsMotionCorr.py | 10 +++- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 2904dbddf7..ffa148db98 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -103,19 +103,20 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): gradient_step_length = traits.Float(requires=['transformation_model'], desc='') - iterations = traits.Int( - argstr="-i %d", - desc="Specify the number of iterations at each level." - ) - smoothing_sigmas = traits.Int( - argstr="-s %d", - desc="Specify the amount of smoothing at each level." - ) - shrink_factors = traits.Int( - argstr="-f %d", + iterations = traits.List(traits.Int, argstr='-i %d', sep='x', + desc="Specify the number of iterations at each level.") + + smoothing_sigmas = traits.List(traits.Float, argstr='-s %f', sep='x', + desc="Specify the amount of smoothing at each level.") + + shrink_factors = traits.List( + traits.Int, + argstr='-f %d', + sep='x', desc=("Specify the shrink factor for the virtual domain (typically " "the fixed image) at each level.") ) + n_images = traits.Int( argstr="-n %d", desc=("This option sets the number of images to use to construct the " @@ -135,6 +136,21 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): desc="use the scale estimator to control optimization." ) + use_estimate_learning_rate_once = traits.Bool( + False, + argstr="-l %d", + desc=("turn on the option that lets you estimate the learning rate " + "step size only at the beginning of each level. Useful as a " + "second stage of fine-scale registration.") + ) + + write_displacement = traits.Bool( + False, + argstr="-w %d", + desc="Write the low-dimensional 3D transforms to a 4D displacement field" + ) + + class AntsMotionCorrOutputSpec(TraitedSpec): '''Output spec for the antsMotionCorr command''' @@ -144,6 +160,8 @@ class AntsMotionCorrOutputSpec(TraitedSpec): warped_image = File(desc="Outputs warped image") inverse_warped_image = File(desc="Outputs the inverse of the warped image") save_state = File(desc="The saved registration state to be restored") + displacement_field = File(desc=("4D displacement field that captures the " + "affine induced motion at each voxel")) class AntsMotionCorr(ANTSCommand): ''' @@ -261,7 +279,8 @@ def _format_output(self): ) elif _extant(self.inputs.output_average_image): return "-o {}".format(self.inputs.output_average_image) - return "" + else: + raise ValueError("Unable to format output due to lack of inputs.") def _list_outputs(self): outputs = self._outputs().get() @@ -278,6 +297,11 @@ def _list_outputs(self): self.inputs.output_transform_prefix ) outputs['composite_transform'] = os.path.abspath(fname) + if (_extant(self.inputs.write_displacement) and + _extant(self.inputs.output_transform_prefix) and + self.inputs.write_displacement is True): + fname = '{}Warp.nii.gz'.format(self.inputs.output_transform_prefix) + outputs['displacement_field'] = os.path.abspath(fname) return outputs class AntsMatrixConversionInputSpec(BaseInterfaceInputSpec): diff --git a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py index d340a437bf..886992cecd 100644 --- a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py +++ b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py @@ -24,6 +24,7 @@ def test_AntsMotionCorr_inputs(): usedefault=True, ), iterations=dict(argstr='-i %d', + sep='x', ), metric_type=dict(argstr='%s', ), @@ -49,17 +50,23 @@ def test_AntsMotionCorr_inputs(): sampling_strategy=dict(requires=['metric_type'], ), shrink_factors=dict(argstr='-f %d', + sep='x', ), - smoothing_sigmas=dict(argstr='-s %d', + smoothing_sigmas=dict(argstr='-s %f', + sep='x', ), terminal_output=dict(nohash=True, ), transformation_model=dict(argstr='%s', ), + use_estimate_learning_rate_once=dict(argstr='-l %d', + ), use_fixed_reference_image=dict(argstr='-u %d', ), use_scales_estimator=dict(argstr='-e %d', ), + write_displacement=dict(argstr='-w %d', + ), ) inputs = AntsMotionCorr.input_spec() @@ -71,6 +78,7 @@ def test_AntsMotionCorr_inputs(): def test_AntsMotionCorr_outputs(): output_map = dict(average_image=dict(), composite_transform=dict(), + displacement_field=dict(), inverse_composite_transform=dict(), inverse_warped_image=dict(), save_state=dict(), From 23344890dce5b1d55ef896ca151d1595364f7b6d Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 3 Mar 2017 13:36:03 -0800 Subject: [PATCH 25/48] update list arguments to format as a string to properly account for specificed seperator --- nipype/interfaces/ants/preprocess.py | 6 +++--- nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index ffa148db98..74cc65528c 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -103,15 +103,15 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): gradient_step_length = traits.Float(requires=['transformation_model'], desc='') - iterations = traits.List(traits.Int, argstr='-i %d', sep='x', + iterations = traits.List(traits.Int, argstr='-i %s', sep='x', desc="Specify the number of iterations at each level.") - smoothing_sigmas = traits.List(traits.Float, argstr='-s %f', sep='x', + smoothing_sigmas = traits.List(traits.Float, argstr='-s %s', sep='x', desc="Specify the amount of smoothing at each level.") shrink_factors = traits.List( traits.Int, - argstr='-f %d', + argstr='-f %s', sep='x', desc=("Specify the shrink factor for the virtual domain (typically " "the fixed image) at each level.") diff --git a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py index 886992cecd..2d526179fb 100644 --- a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py +++ b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py @@ -23,7 +23,7 @@ def test_AntsMotionCorr_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - iterations=dict(argstr='-i %d', + iterations=dict(argstr='-i %s', sep='x', ), metric_type=dict(argstr='%s', @@ -49,10 +49,10 @@ def test_AntsMotionCorr_inputs(): ), sampling_strategy=dict(requires=['metric_type'], ), - shrink_factors=dict(argstr='-f %d', + shrink_factors=dict(argstr='-f %s', sep='x', ), - smoothing_sigmas=dict(argstr='-s %f', + smoothing_sigmas=dict(argstr='-s %s', sep='x', ), terminal_output=dict(nohash=True, From 644ce4c7beaef291e022416a196feeb8d3aab8ac Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 3 Mar 2017 14:08:35 -0800 Subject: [PATCH 26/48] udpate antsmotioncorr example --- nipype/interfaces/ants/preprocess.py | 9 +++--- .../ants/tests/test_spec_AntsMotionCorr.py | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 nipype/interfaces/ants/tests/test_spec_AntsMotionCorr.py diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 74cc65528c..33f6aa7c8a 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -175,12 +175,13 @@ class AntsMotionCorr(ANTSCommand): >>> ants_mc.inputs.radius_or_bins = 1 >>> ants_mc.inputs.sampling_strategy = "Random" >>> ants_mc.inputs.sampling_percentage = 0.05 - >>> ants_mc.inputs.iterations = 10 - >>> ants_mc.inputs.smoothing_sigmas = 0 - >>> ants_mc.inputs.shrink_factors = 1 + >>> ants_mc.inputs.iterations = [10,3] + >>> ants_mc.inputs.smoothing_sigmas = [0,0] + >>> ants_mc.inputs.shrink_factors = [1,1] >>> ants_mc.inputs.n_images = 10 >>> ants_mc.inputs.use_fixed_reference_image = True >>> ants_mc.inputs.use_scales_estimator = True + >>> ants_mc.inputs.output_average_image = 'wat' >>> ants_mc.inputs.output_warped_image = 'warped.nii.gz' >>> ants_mc.inputs.output_transform_prefix = 'motcorr' >>> ants_mc.inputs.transformation_model = 'Affine' @@ -188,7 +189,7 @@ class AntsMotionCorr(ANTSCommand): >>> ants_mc.inputs.fixed_image = "average_image.nii.gz" >>> ants_mc.inputs.moving_image = "input.nii.gz" >>> print(ants_mc.cmdline) - antsMotionCorr -d 3 -i 10 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1 -s 0 -t Affine[0.005] -u 1 -e 1 + antsMotionCorr -d 3 -i 10x3 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1x1 -s 0.0x0.0 -t Affine[0.005] -u 1 -e 1 >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr >>> ants_avg = AntsMotionCorr() diff --git a/nipype/interfaces/ants/tests/test_spec_AntsMotionCorr.py b/nipype/interfaces/ants/tests/test_spec_AntsMotionCorr.py new file mode 100644 index 0000000000..ae8119638d --- /dev/null +++ b/nipype/interfaces/ants/tests/test_spec_AntsMotionCorr.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import division +from nipype.interfaces.ants.preprocess import AntsMotionCorr + +def test_AntsMotionCorr_cmd(): + ants_mc = AntsMotionCorr() + ants_mc.inputs.metric_type = 'GC' + ants_mc.inputs.metric_weight = 1 + ants_mc.inputs.radius_or_bins = 1 + ants_mc.inputs.sampling_strategy = "Random" + ants_mc.inputs.sampling_percentage = 0.05 + ants_mc.inputs.iterations = [10, 3] + ants_mc.inputs.smoothing_sigmas = [0, 0] + ants_mc.inputs.shrink_factors = [1, 1] + ants_mc.inputs.n_images = 10 + ants_mc.inputs.use_fixed_reference_image = True + ants_mc.inputs.use_scales_estimator = True + ants_mc.inputs.output_average_image = 'wat' + ants_mc.inputs.output_warped_image = 'warped.nii.gz' + ants_mc.inputs.output_transform_prefix = 'motcorr' + ants_mc.inputs.transformation_model = 'Affine' + ants_mc.inputs.gradient_step_length = 0.005 + ants_mc.inputs.fixed_image = "average_image.nii.gz" + ants_mc.inputs.moving_image = "input.nii.gz" + + expected_command = ( + "antsMotionCorr -d 3 -i 10x3 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] " + "-n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1x1 -s 0.0x0.0 -t Affine[0.005] " + "-u 1 -e 1" + ) + assert ants_mc.cmdline == expected_command From 43f09135c7aa039e0e86bbc40b8ebf19fe7e01c6 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Mar 2017 15:20:06 -0800 Subject: [PATCH 27/48] remove ants prefix from class names --- nipype/interfaces/ants/preprocess.py | 42 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 33f6aa7c8a..203db5bc0f 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -15,7 +15,7 @@ def _extant(field): return (field is not None) and isdefined(field) -class AntsMotionCorrStatsInputSpec(ANTSCommandInputSpec): +class MotionCorrStatsInputSpec(ANTSCommandInputSpec): ''' Input spec for the antsMotionCorrStats command ''' mask = File(argstr="-x %s", mandatory=True, desc="compute displacements within specified mask.") @@ -30,17 +30,17 @@ class AntsMotionCorrStatsInputSpec(ANTSCommandInputSpec): output_spatial_map = File(argstr='-s %s', hash_files=False, desc="File to output displacement magnitude to.") -class AntsMotionCorrStatsOutputSpec(TraitedSpec): +class MotionCorrStatsOutputSpec(TraitedSpec): ''' Output spec for the antsMotionCorrStats command ''' spatial_map = File(desc="output image of displacement magnitude", exists=True) output = File(desc="CSV file containg motion correction statistics", exists=True) -class AntsMotionCorrStats(ANTSCommand): +class MotionCorrStats(ANTSCommand): ''' Interface for the antsMotionCorrStats command ''' _cmd = 'antsMotionCorrStats' - input_spec = AntsMotionCorrStatsInputSpec - output_spec = AntsMotionCorrStatsOutputSpec + input_spec = MotionCorrStatsInputSpec + output_spec = MotionCorrStatsOutputSpec def _gen_filename(self, name): if name == 'output': @@ -57,7 +57,7 @@ def _list_outputs(self): outputs['output'] = os.path.abspath(self.inputs.output) return outputs -class AntsMotionCorrInputSpec(ANTSCommandInputSpec): +class MotionCorrInputSpec(ANTSCommandInputSpec): '''Input spec for the antsMotionCorr command.''' dimension_desc = ( "This option forces the image to be treated as a " @@ -152,7 +152,7 @@ class AntsMotionCorrInputSpec(ANTSCommandInputSpec): -class AntsMotionCorrOutputSpec(TraitedSpec): +class MotionCorrOutputSpec(TraitedSpec): '''Output spec for the antsMotionCorr command''' average_image = File(exists=True, desc="Average of an image") composite_transform = File(desc="Composite transform file") @@ -163,13 +163,13 @@ class AntsMotionCorrOutputSpec(TraitedSpec): displacement_field = File(desc=("4D displacement field that captures the " "affine induced motion at each voxel")) -class AntsMotionCorr(ANTSCommand): +class MotionCorr(ANTSCommand): ''' Examples ------- - >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr - >>> ants_mc = AntsMotionCorr() + >>> from nipype.interfaces.ants.preprocess import MotionCorr + >>> ants_mc = MotionCorr() >>> ants_mc.inputs.metric_type = 'GC' >>> ants_mc.inputs.metric_weight = 1 >>> ants_mc.inputs.radius_or_bins = 1 @@ -191,14 +191,14 @@ class AntsMotionCorr(ANTSCommand): >>> print(ants_mc.cmdline) antsMotionCorr -d 3 -i 10x3 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1x1 -s 0.0x0.0 -t Affine[0.005] -u 1 -e 1 - >>> from nipype.interfaces.ants.preprocess import AntsMotionCorr - >>> ants_avg = AntsMotionCorr() + >>> from nipype.interfaces.ants.preprocess import MotionCorr + >>> ants_avg = MotionCorr() >>> ants_avg.inputs.average_image = 'input.nii.gz' >>> ants_avg.inputs.output_average_image = 'avg_out.nii.gz' >>> print(ants_avg.cmdline) antsMotionCorr -d 3 -a input.nii.gz -o avg_out.nii.gz - >>> ants_avg = AntsMotionCorr() + >>> ants_avg = MotionCorr() >>> ants_avg.inputs.average_image = 'input.nii.gz' >>> print(ants_avg.cmdline) antsMotionCorr -d 3 -a input.nii.gz -o input_avg.nii.gz @@ -210,8 +210,8 @@ class AntsMotionCorr(ANTSCommand): https://github.com/stnava/ANTs/blob/master/Scripts/antsMotionCorrExample ''' _cmd = 'antsMotionCorr' - input_spec = AntsMotionCorrInputSpec - output_spec = AntsMotionCorrOutputSpec + input_spec = MotionCorrInputSpec + output_spec = MotionCorrOutputSpec def _gen_filename(self, name): @@ -246,7 +246,7 @@ def _format_arg(self, opt, spec, val): if opt == 'output_average_image': self.inputs.output_average_image = self._gen_filename("output_average_image") return self._format_output() - return super(AntsMotionCorr, self)._format_arg(opt, spec, val) + return super(MotionCorr, self)._format_arg(opt, spec, val) def _format_metric(self): metric_str = ("-m {metric_type}[{fixed_image},{moving_image}," @@ -305,24 +305,24 @@ def _list_outputs(self): outputs['displacement_field'] = os.path.abspath(fname) return outputs -class AntsMatrixConversionInputSpec(BaseInterfaceInputSpec): +class Matrix2FSLParamsInputSpec(BaseInterfaceInputSpec): matrix = File( exists=True, desc='Motion crrection matrices to be converted into FSL style motion parameters', mandatory=True ) -class AntsMatrixConversionOutputSpec(TraitedSpec): +class Matrix2FSLParamsOutputSpec(TraitedSpec): parameters = File(exists=True, desc="parameters to be output") -class AntsMatrixConversion(BaseInterface): +class Matrix2FSLParamsConversion(BaseInterface): ''' Take antsMotionCorr motion output as input, convert to FSL style parameter files. Currently does not output origin of rotation. ''' - input_spec = AntsMatrixConversionInputSpec - output_spec = AntsMatrixConversionOutputSpec + input_spec = Matrix2FSLParamsInputSpec + output_spec = Matrix2FSLParamsOutputSpec def _run_interface(self, runtime): in_fp = open(self.inputs.matrix) From 2c3833de1219be92e730c5f06cb9c045710c0ee7 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Mar 2017 15:24:23 -0800 Subject: [PATCH 28/48] update spec test for new class name --- ...{test_spec_AntsMotionCorr.py => test_spec_MotionCorr.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename nipype/interfaces/ants/tests/{test_spec_AntsMotionCorr.py => test_spec_MotionCorr.py} (90%) diff --git a/nipype/interfaces/ants/tests/test_spec_AntsMotionCorr.py b/nipype/interfaces/ants/tests/test_spec_MotionCorr.py similarity index 90% rename from nipype/interfaces/ants/tests/test_spec_AntsMotionCorr.py rename to nipype/interfaces/ants/tests/test_spec_MotionCorr.py index ae8119638d..a5101eb934 100644 --- a/nipype/interfaces/ants/tests/test_spec_AntsMotionCorr.py +++ b/nipype/interfaces/ants/tests/test_spec_MotionCorr.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import division -from nipype.interfaces.ants.preprocess import AntsMotionCorr +from nipype.interfaces.ants.preprocess import MotionCorr -def test_AntsMotionCorr_cmd(): - ants_mc = AntsMotionCorr() +def test_MotionCorr_cmd(): + ants_mc = MotionCorr() ants_mc.inputs.metric_type = 'GC' ants_mc.inputs.metric_weight = 1 ants_mc.inputs.radius_or_bins = 1 From ebebf705cc2c24dbdf2329e041b5115392cf2e40 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Mar 2017 15:25:42 -0800 Subject: [PATCH 29/48] update specs for new class names --- .../test_auto_Matrix2FSLParamsConversion.py | 27 ++++++ .../ants/tests/test_auto_MotionCorr.py | 91 +++++++++++++++++++ .../ants/tests/test_auto_MotionCorrStats.py | 51 +++++++++++ 3 files changed, 169 insertions(+) create mode 100644 nipype/interfaces/ants/tests/test_auto_Matrix2FSLParamsConversion.py create mode 100644 nipype/interfaces/ants/tests/test_auto_MotionCorr.py create mode 100644 nipype/interfaces/ants/tests/test_auto_MotionCorrStats.py diff --git a/nipype/interfaces/ants/tests/test_auto_Matrix2FSLParamsConversion.py b/nipype/interfaces/ants/tests/test_auto_Matrix2FSLParamsConversion.py new file mode 100644 index 0000000000..ff649a2c02 --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_Matrix2FSLParamsConversion.py @@ -0,0 +1,27 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..preprocess import Matrix2FSLParamsConversion + + +def test_Matrix2FSLParamsConversion_inputs(): + input_map = dict(ignore_exception=dict(nohash=True, + usedefault=True, + ), + matrix=dict(mandatory=True, + ), + ) + inputs = Matrix2FSLParamsConversion.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_Matrix2FSLParamsConversion_outputs(): + output_map = dict(parameters=dict(), + ) + outputs = Matrix2FSLParamsConversion.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/ants/tests/test_auto_MotionCorr.py b/nipype/interfaces/ants/tests/test_auto_MotionCorr.py new file mode 100644 index 0000000000..1099338673 --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_MotionCorr.py @@ -0,0 +1,91 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..preprocess import MotionCorr + + +def test_MotionCorr_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + average_image=dict(argstr='-a %s', + position=1, + ), + dimensionality=dict(argstr='-d %d', + position=0, + usedefault=True, + ), + environ=dict(nohash=True, + usedefault=True, + ), + fixed_image=dict(requires=['metric_type'], + ), + gradient_step_length=dict(requires=['transformation_model'], + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + iterations=dict(argstr='-i %s', + sep='x', + ), + metric_type=dict(argstr='%s', + ), + metric_weight=dict(requires=['metric_type'], + ), + moving_image=dict(requires=['metric_type'], + ), + n_images=dict(argstr='-n %d', + ), + num_threads=dict(nohash=True, + usedefault=True, + ), + output_average_image=dict(argstr='%s', + genfile=True, + hash_files=False, + ), + output_transform_prefix=dict(), + output_warped_image=dict(), + radius_or_bins=dict(requires=['metric_type'], + ), + sampling_percentage=dict(requires=['metric_type'], + ), + sampling_strategy=dict(requires=['metric_type'], + ), + shrink_factors=dict(argstr='-f %s', + sep='x', + ), + smoothing_sigmas=dict(argstr='-s %s', + sep='x', + ), + terminal_output=dict(nohash=True, + ), + transformation_model=dict(argstr='%s', + ), + use_estimate_learning_rate_once=dict(argstr='-l %d', + ), + use_fixed_reference_image=dict(argstr='-u %d', + ), + use_scales_estimator=dict(argstr='-e %d', + ), + write_displacement=dict(argstr='-w %d', + ), + ) + inputs = MotionCorr.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_MotionCorr_outputs(): + output_map = dict(average_image=dict(), + composite_transform=dict(), + displacement_field=dict(), + inverse_composite_transform=dict(), + inverse_warped_image=dict(), + save_state=dict(), + warped_image=dict(), + ) + outputs = MotionCorr.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/ants/tests/test_auto_MotionCorrStats.py b/nipype/interfaces/ants/tests/test_auto_MotionCorrStats.py new file mode 100644 index 0000000000..ecfe1653fb --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_MotionCorrStats.py @@ -0,0 +1,51 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from __future__ import unicode_literals +from ..preprocess import MotionCorrStats + + +def test_MotionCorrStats_inputs(): + input_map = dict(args=dict(argstr='%s', + ), + environ=dict(nohash=True, + usedefault=True, + ), + framewise=dict(argstr='-f %d', + ), + ignore_exception=dict(nohash=True, + usedefault=True, + ), + mask=dict(argstr='-x %s', + mandatory=True, + ), + moco=dict(argstr='-m %s', + mandatory=True, + ), + num_threads=dict(nohash=True, + usedefault=True, + ), + output=dict(argstr='-o %s', + genfile=True, + hash_files=False, + ), + output_spatial_map=dict(argstr='-s %s', + hash_files=False, + ), + terminal_output=dict(nohash=True, + ), + ) + inputs = MotionCorrStats.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_MotionCorrStats_outputs(): + output_map = dict(output=dict(), + spatial_map=dict(), + ) + outputs = MotionCorrStats.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value From 8b9c20a9f57f487aeb33f610544c398a6d691be8 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Mar 2017 15:55:11 -0800 Subject: [PATCH 30/48] add test for Matrix2FSLParams --- nipype/interfaces/ants/preprocess.py | 2 +- .../ants/tests/test_Matrix2FSLParams.py | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 nipype/interfaces/ants/tests/test_Matrix2FSLParams.py diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 203db5bc0f..4e8e9c53e9 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -316,7 +316,7 @@ class Matrix2FSLParamsOutputSpec(TraitedSpec): parameters = File(exists=True, desc="parameters to be output") -class Matrix2FSLParamsConversion(BaseInterface): +class Matrix2FSLParams(BaseInterface): ''' Take antsMotionCorr motion output as input, convert to FSL style parameter files. Currently does not output origin of rotation. diff --git a/nipype/interfaces/ants/tests/test_Matrix2FSLParams.py b/nipype/interfaces/ants/tests/test_Matrix2FSLParams.py new file mode 100644 index 0000000000..4b327e3819 --- /dev/null +++ b/nipype/interfaces/ants/tests/test_Matrix2FSLParams.py @@ -0,0 +1,34 @@ +import csv +import os + +from nipype.interfaces.ants.preprocess import Matrix2FSLParams +from nipype.utils.tmpdirs import InTemporaryDirectory +from nipype.utils.filemanip import split_filename + +def test_Matrix2FSLParams(): + with InTemporaryDirectory(): + cwd = os.getcwd() + in_filename = os.path.join(cwd, 'in_file.csv') + fp = open(in_filename, 'w+') + fp.write("this line is ignored\n") + fp.write( + "0,-0.99918075422702,1.00028207678993,-7.41063731046199e-06," + "3.93289649449106e-05,1.24969530535555e-05,1.00021114616063," + "0.000233157514656132,-0.000195740079275366,-0.00033024846828514," + "0.999495881936253,-0.0168301940330171,-0.0549722774931478," + "0.202056708561567\n" + ) + fp.close() + # m2p - matrix 2 parameters + m2p = Matrix2FSLParams() + m2p.inputs.matrix = in_filename + m2p.run() + + pth, fname, _ = split_filename(in_filename) + params_fname = '{}{}'.format(fname, '.par') + params_fname = os.path.join(pth, params_fname) + + params_fp = open(params_fname) + params_data = csv.reader(params_fp, delimiter=' ') + line = next(params_data) + assert len(line) == 6 From ebd5350d475ce92b9958603dd5eb17c4741e3f3d Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Fri, 10 Mar 2017 15:56:39 -0800 Subject: [PATCH 31/48] remove old auto tests --- .../ants/tests/test_auto_AntsMotionCorr.py | 91 ------------------- .../tests/test_auto_AntsMotionCorrStats.py | 51 ----------- .../test_auto_Matrix2FSLParamsConversion.py | 27 ------ 3 files changed, 169 deletions(-) delete mode 100644 nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py delete mode 100644 nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py delete mode 100644 nipype/interfaces/ants/tests/test_auto_Matrix2FSLParamsConversion.py diff --git a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py deleted file mode 100644 index 2d526179fb..0000000000 --- a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorr.py +++ /dev/null @@ -1,91 +0,0 @@ -# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT -from __future__ import unicode_literals -from ..preprocess import AntsMotionCorr - - -def test_AntsMotionCorr_inputs(): - input_map = dict(args=dict(argstr='%s', - ), - average_image=dict(argstr='-a %s', - position=1, - ), - dimensionality=dict(argstr='-d %d', - position=0, - usedefault=True, - ), - environ=dict(nohash=True, - usedefault=True, - ), - fixed_image=dict(requires=['metric_type'], - ), - gradient_step_length=dict(requires=['transformation_model'], - ), - ignore_exception=dict(nohash=True, - usedefault=True, - ), - iterations=dict(argstr='-i %s', - sep='x', - ), - metric_type=dict(argstr='%s', - ), - metric_weight=dict(requires=['metric_type'], - ), - moving_image=dict(requires=['metric_type'], - ), - n_images=dict(argstr='-n %d', - ), - num_threads=dict(nohash=True, - usedefault=True, - ), - output_average_image=dict(argstr='%s', - genfile=True, - hash_files=False, - ), - output_transform_prefix=dict(), - output_warped_image=dict(), - radius_or_bins=dict(requires=['metric_type'], - ), - sampling_percentage=dict(requires=['metric_type'], - ), - sampling_strategy=dict(requires=['metric_type'], - ), - shrink_factors=dict(argstr='-f %s', - sep='x', - ), - smoothing_sigmas=dict(argstr='-s %s', - sep='x', - ), - terminal_output=dict(nohash=True, - ), - transformation_model=dict(argstr='%s', - ), - use_estimate_learning_rate_once=dict(argstr='-l %d', - ), - use_fixed_reference_image=dict(argstr='-u %d', - ), - use_scales_estimator=dict(argstr='-e %d', - ), - write_displacement=dict(argstr='-w %d', - ), - ) - inputs = AntsMotionCorr.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_AntsMotionCorr_outputs(): - output_map = dict(average_image=dict(), - composite_transform=dict(), - displacement_field=dict(), - inverse_composite_transform=dict(), - inverse_warped_image=dict(), - save_state=dict(), - warped_image=dict(), - ) - outputs = AntsMotionCorr.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/ants/tests/test_auto_AntsMotionCorrStats.py b/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py deleted file mode 100644 index e81fb4f95d..0000000000 --- a/nipype/interfaces/ants/tests/test_auto_AntsMotionCorrStats.py +++ /dev/null @@ -1,51 +0,0 @@ -# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT -from __future__ import unicode_literals -from ..preprocess import AntsMotionCorrStats - - -def test_AntsMotionCorrStats_inputs(): - input_map = dict(args=dict(argstr='%s', - ), - environ=dict(nohash=True, - usedefault=True, - ), - framewise=dict(argstr='-f %d', - ), - ignore_exception=dict(nohash=True, - usedefault=True, - ), - mask=dict(argstr='-x %s', - mandatory=True, - ), - moco=dict(argstr='-m %s', - mandatory=True, - ), - num_threads=dict(nohash=True, - usedefault=True, - ), - output=dict(argstr='-o %s', - genfile=True, - hash_files=False, - ), - output_spatial_map=dict(argstr='-s %s', - hash_files=False, - ), - terminal_output=dict(nohash=True, - ), - ) - inputs = AntsMotionCorrStats.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_AntsMotionCorrStats_outputs(): - output_map = dict(output=dict(), - spatial_map=dict(), - ) - outputs = AntsMotionCorrStats.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/ants/tests/test_auto_Matrix2FSLParamsConversion.py b/nipype/interfaces/ants/tests/test_auto_Matrix2FSLParamsConversion.py deleted file mode 100644 index ff649a2c02..0000000000 --- a/nipype/interfaces/ants/tests/test_auto_Matrix2FSLParamsConversion.py +++ /dev/null @@ -1,27 +0,0 @@ -# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT -from __future__ import unicode_literals -from ..preprocess import Matrix2FSLParamsConversion - - -def test_Matrix2FSLParamsConversion_inputs(): - input_map = dict(ignore_exception=dict(nohash=True, - usedefault=True, - ), - matrix=dict(mandatory=True, - ), - ) - inputs = Matrix2FSLParamsConversion.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_Matrix2FSLParamsConversion_outputs(): - output_map = dict(parameters=dict(), - ) - outputs = Matrix2FSLParamsConversion.output_spec() - - for key, metadata in list(output_map.items()): - for metakey, value in list(metadata.items()): - assert getattr(outputs.traits()[key], metakey) == value From 82877444c3bac8457f9801786d8ea58112ecd289 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Mar 2017 09:36:21 -0700 Subject: [PATCH 32/48] add preprocess functions to init --- nipype/interfaces/ants/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nipype/interfaces/ants/__init__.py b/nipype/interfaces/ants/__init__.py index 3bb83b3df9..faafcea2b3 100644 --- a/nipype/interfaces/ants/__init__.py +++ b/nipype/interfaces/ants/__init__.py @@ -3,6 +3,8 @@ # vi: set ft=python sts=4 ts=4 sw=4 et: """Top-level namespace for ants.""" +# Preprocessing Programs +from .preprocess import Matrix2FSLParams, MotionCorr, MotionCorrStats # Registraiton programs from .registration import ANTS, Registration From deba31ae286a878bc0b04b04225180b058d81073 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Mar 2017 09:37:35 -0700 Subject: [PATCH 33/48] remove unnecessary _extant function --- nipype/interfaces/ants/preprocess.py | 39 +++++++++++++--------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 4e8e9c53e9..0265287931 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -12,9 +12,6 @@ from ...utils.filemanip import split_filename -def _extant(field): - return (field is not None) and isdefined(field) - class MotionCorrStatsInputSpec(ANTSCommandInputSpec): ''' Input spec for the antsMotionCorrStats command ''' mask = File(argstr="-x %s", mandatory=True, @@ -49,11 +46,11 @@ def _gen_filename(self, name): def _list_outputs(self): outputs = self._outputs().get() - if _extant(self.inputs.output_spatial_map): + if isdefined(self.inputs.output_spatial_map): outputs['spatial_map'] = ( os.path.abspath(self.inputs.output_spatial_map) ) - if _extant(self.inputs.output): + if isdefined(self.inputs.output): outputs['output'] = os.path.abspath(self.inputs.output) return outputs @@ -223,16 +220,16 @@ def _gen_filename(self, name): image name. ''' if name == 'output_average_image': - if _extant(self.inputs.fixed_image): + if isdefined(self.inputs.fixed_image): return self.inputs.fixed_image - if not _extant(self.inputs.average_image): + if not isdefined(self.inputs.average_image): raise ValueError("Either fixed_image or average_image must be defined") - if _extant(self.inputs.output_average_image): + if isdefined(self.inputs.output_average_image): return self.inputs.output_average_image pth, fname, ext = split_filename(self.inputs.average_image) new_fname = '{}{}{}'.format(fname, '_avg', ext) return os.path.join(pth, new_fname) - if name == 'ouput_warped_image' and _extant(self.inputs.fixed_image): + if name == 'ouput_warped_image' and isdefined(self.inputs.fixed_image): pth, fname, ext = split_filename(self.inputs.fixed_image) new_fname = '{}{}{}'.format(fname, '_warped', ext) return os.path.join(pth, new_fname) @@ -257,49 +254,49 @@ def _format_metric(self): "metric_weight", "radius_or_bins", "sampling_strategy", "sampling_percentage"] for metric_arg in metric_args: - if _extant(getattr(self.inputs, metric_arg)): + if isdefined(getattr(self.inputs, metric_arg)): format_args[metric_arg] = getattr(self.inputs, metric_arg) return metric_str.format(**format_args) def _format_transform(self): transform_str = "-t {}[{}]" - if (_extant(self.inputs.transformation_model) - and _extant(self.inputs.gradient_step_length)): + if (isdefined(self.inputs.transformation_model) + and isdefined(self.inputs.gradient_step_length)): return transform_str.format(self.inputs.transformation_model, self.inputs.gradient_step_length) raise ValueError("Unable to format transformation_model argument") def _format_output(self): - if (_extant(self.inputs.output_transform_prefix) - and _extant(self.inputs.output_warped_image) - and _extant(self.inputs.output_average_image)): + if (isdefined(self.inputs.output_transform_prefix) + and isdefined(self.inputs.output_warped_image) + and isdefined(self.inputs.output_average_image)): return "-o [{},{},{}]".format( self.inputs.output_transform_prefix, self.inputs.output_warped_image, self.inputs.output_average_image ) - elif _extant(self.inputs.output_average_image): + elif isdefined(self.inputs.output_average_image): return "-o {}".format(self.inputs.output_average_image) else: raise ValueError("Unable to format output due to lack of inputs.") def _list_outputs(self): outputs = self._outputs().get() - if _extant(self.inputs.output_average_image): + if isdefined(self.inputs.output_average_image): outputs['average_image'] = ( os.path.abspath(self.inputs.output_average_image) ) - if _extant(self.inputs.output_warped_image): + if isdefined(self.inputs.output_warped_image): outputs['warped_image'] = ( os.path.abspath(self.inputs.output_warped_image) ) - if _extant(self.inputs.output_transform_prefix): + if isdefined(self.inputs.output_transform_prefix): fname = '{}MOCOparams.csv'.format( self.inputs.output_transform_prefix ) outputs['composite_transform'] = os.path.abspath(fname) - if (_extant(self.inputs.write_displacement) and - _extant(self.inputs.output_transform_prefix) and + if (isdefined(self.inputs.write_displacement) and + isdefined(self.inputs.output_transform_prefix) and self.inputs.write_displacement is True): fname = '{}Warp.nii.gz'.format(self.inputs.output_transform_prefix) outputs['displacement_field'] = os.path.abspath(fname) From 045e798cbe660ae4105ada8e2c8df9b0a94b3c74 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Mon, 13 Mar 2017 09:38:50 -0700 Subject: [PATCH 34/48] remove old auto test --- .../tests/test_auto_AntsMatrixConversion.py | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py diff --git a/nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py b/nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py deleted file mode 100644 index f7ecb64d2b..0000000000 --- a/nipype/interfaces/ants/tests/test_auto_AntsMatrixConversion.py +++ /dev/null @@ -1,27 +0,0 @@ -# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT -from __future__ import unicode_literals -from ..preprocess import AntsMatrixConversion - - -def test_AntsMatrixConversion_inputs(): - input_map = dict(ignore_exception=dict(nohash=True, - usedefault=True, - ), - matrix=dict(mandatory=True, - ), - ) - inputs = AntsMatrixConversion.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_AntsMatrixConversion_outputs(): - output_map = dict(parameters=dict(), - ) - outputs = AntsMatrixConversion.output_spec() - - for key, metadata in list(output_map.items()): - for metakey, value in list(metadata.items()): - assert getattr(outputs.traits()[key], metakey) == value From c5e90a191eb0bb6060d1cd6c1e21f0fde03dbfd1 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Mar 2017 10:56:45 -0700 Subject: [PATCH 35/48] switch matrix conversion to use nibabel --- nipype/interfaces/ants/preprocess.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 0265287931..666ecf64c0 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -3,9 +3,11 @@ functions. """ import csv -import math import os +from nibabel.eulerangles import mat2euler +import numpy + from ..base import (BaseInterface, BaseInterfaceInputSpec, TraitedSpec, File, traits, isdefined, Str) from .base import ANTSCommand, ANTSCommandInputSpec @@ -330,12 +332,13 @@ def _run_interface(self, runtime): next(in_data) for x in in_data: - t1 = math.atan2(float(x[7]), float(x[10])) - c2 = math.sqrt((float(x[2]) * float(x[2])) + (float(x[3]) * float(x[3]))) - t2 = math.atan2(-float(x[4]), c2) - t3 = math.atan2(float(x[3]), float(x[2])) + mat = numpy.zeroes((3, 3)) + mat[0] = [x[2], x[3], x[4]] + mat[1] = [x[5], x[6], x[7]] + mat[2] = [x[8], x[9], x[10]] + param_z, param_y, param_x = mat2euler(mat) parameters = "{:.8f} {:.8f} {:.8f} {:.8f} {:.8f} {:.8f}" - pars.append(parameters.format(t1, t2, t3, float(x[11]), float(x[12]), + pars.append(parameters.format(param_x, param_y, param_z, float(x[11]), float(x[12]), float(x[13]))) pth, fname, _ = split_filename(self.inputs.matrix) From 95313e14a0b541bf8de51c3746b5f978126e9154 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Mar 2017 12:16:16 -0700 Subject: [PATCH 36/48] update matrix conversion names, fix typo, make output file naming simpler --- nipype/interfaces/ants/preprocess.py | 36 ++++++++----------- ...Params.py => test_MotionCorr2FSLParams.py} | 8 ++--- 2 files changed, 19 insertions(+), 25 deletions(-) rename nipype/interfaces/ants/tests/{test_Matrix2FSLParams.py => test_MotionCorr2FSLParams.py} (85%) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 666ecf64c0..4aa3ff8ee4 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -304,27 +304,29 @@ def _list_outputs(self): outputs['displacement_field'] = os.path.abspath(fname) return outputs -class Matrix2FSLParamsInputSpec(BaseInterfaceInputSpec): - matrix = File( +class MotionCorr2FSLParamsInputSpec(BaseInterfaceInputSpec): + ants_matrix = File( exists=True, - desc='Motion crrection matrices to be converted into FSL style motion parameters', + desc='Motion correction matrices to be converted into FSL style motion parameters', mandatory=True ) + fsl_params = File(name_template='%s.par', name_source='ants_matrix', + desc='FSL parameter file') -class Matrix2FSLParamsOutputSpec(TraitedSpec): - parameters = File(exists=True, desc="parameters to be output") +class MotionCorr2FSLParamsOutputSpec(TraitedSpec): + fsl_params = File(exists=True, desc="FSL parameters file to be output") -class Matrix2FSLParams(BaseInterface): +class MotionCorr2FSLParams(BaseInterface): ''' - Take antsMotionCorr motion output as input, convert to FSL style + Take antsMotionCorr motion output as input, convert to FSL-style parameter files. Currently does not output origin of rotation. ''' - input_spec = Matrix2FSLParamsInputSpec - output_spec = Matrix2FSLParamsOutputSpec + input_spec = MotionCorr2FSLParamsInputSpec + output_spec = MotionCorr2FSLParamsOutputSpec def _run_interface(self, runtime): - in_fp = open(self.inputs.matrix) + in_fp = open(self.inputs.ants_matrix) in_data = csv.reader(in_fp) pars = [] @@ -341,18 +343,10 @@ def _run_interface(self, runtime): pars.append(parameters.format(param_x, param_y, param_z, float(x[11]), float(x[12]), float(x[13]))) - pth, fname, _ = split_filename(self.inputs.matrix) + pth, fname, _ = split_filename(self.inputs.ants_matrix) new_fname = '{}{}'.format(fname, '.par') - parameters = os.path.join(pth, new_fname) - with open(parameters, mode='wt') as out_fp: + fsl_params = os.path.join(pth, new_fname) + with open(fsl_params, mode='wt') as out_fp: out_fp.write('\n'.join(pars)) in_fp.close() return runtime - - def _list_outputs(self): - outputs = self._outputs().get() - pth, fname, _ = split_filename(self.inputs.matrix) - new_fname = '{}{}'.format(fname, '.par') - out_file = os.path.join(pth, new_fname) - outputs["parameters"] = out_file - return outputs diff --git a/nipype/interfaces/ants/tests/test_Matrix2FSLParams.py b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py similarity index 85% rename from nipype/interfaces/ants/tests/test_Matrix2FSLParams.py rename to nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py index 4b327e3819..e055542da0 100644 --- a/nipype/interfaces/ants/tests/test_Matrix2FSLParams.py +++ b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py @@ -1,11 +1,11 @@ import csv import os -from nipype.interfaces.ants.preprocess import Matrix2FSLParams +from nipype.interfaces.ants.preprocess import MotionCorr2FSLParams from nipype.utils.tmpdirs import InTemporaryDirectory from nipype.utils.filemanip import split_filename -def test_Matrix2FSLParams(): +def test_MotionCorr2FSLParams(): with InTemporaryDirectory(): cwd = os.getcwd() in_filename = os.path.join(cwd, 'in_file.csv') @@ -20,8 +20,8 @@ def test_Matrix2FSLParams(): ) fp.close() # m2p - matrix 2 parameters - m2p = Matrix2FSLParams() - m2p.inputs.matrix = in_filename + m2p = MotionCorr2FSLParams() + m2p.inputs.ants_matrix = in_filename m2p.run() pth, fname, _ = split_filename(in_filename) From 5c1e3f68193c5cffc4f50d6fbe603a56f6e3b8aa Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Mar 2017 12:59:38 -0700 Subject: [PATCH 37/48] update init to match new names in preprocess --- nipype/interfaces/ants/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/interfaces/ants/__init__.py b/nipype/interfaces/ants/__init__.py index faafcea2b3..ed7e7dbbce 100644 --- a/nipype/interfaces/ants/__init__.py +++ b/nipype/interfaces/ants/__init__.py @@ -4,7 +4,7 @@ """Top-level namespace for ants.""" # Preprocessing Programs -from .preprocess import Matrix2FSLParams, MotionCorr, MotionCorrStats +from .preprocess import MotionCorr2FSLParams, MotionCorr, MotionCorrStats # Registraiton programs from .registration import ANTS, Registration From 01bc07d7ccaff39d15a7d7f2c04058825d6d6176 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Mar 2017 13:05:57 -0700 Subject: [PATCH 38/48] fix spelling mistake --- nipype/interfaces/ants/preprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 4aa3ff8ee4..0646f21283 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -334,7 +334,7 @@ def _run_interface(self, runtime): next(in_data) for x in in_data: - mat = numpy.zeroes((3, 3)) + mat = numpy.zeros((3, 3)) mat[0] = [x[2], x[3], x[4]] mat[1] = [x[5], x[6], x[7]] mat[2] = [x[8], x[9], x[10]] From 32968cd8cda1e0def2bcb68dd878c56ca9c67827 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Mar 2017 13:11:33 -0700 Subject: [PATCH 39/48] still need _list_outputs --- nipype/interfaces/ants/preprocess.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 0646f21283..458f41f955 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -310,8 +310,6 @@ class MotionCorr2FSLParamsInputSpec(BaseInterfaceInputSpec): desc='Motion correction matrices to be converted into FSL style motion parameters', mandatory=True ) - fsl_params = File(name_template='%s.par', name_source='ants_matrix', - desc='FSL parameter file') class MotionCorr2FSLParamsOutputSpec(TraitedSpec): fsl_params = File(exists=True, desc="FSL parameters file to be output") @@ -350,3 +348,11 @@ def _run_interface(self, runtime): out_fp.write('\n'.join(pars)) in_fp.close() return runtime + + def _list_outputs(self): + outputs = self._outputs().get() + pth, fname, _ = split_filename(self.inputs.matrix) + new_fname = '{}{}'.format(fname, '.par') + out_file = os.path.join(pth, new_fname) + outputs["parameters"] = out_file + return outputs From ab74434b2a1879cf957e00c0fb185f864e031282 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Wed, 15 Mar 2017 13:14:51 -0700 Subject: [PATCH 40/48] correct input and output names in list_outputs --- nipype/interfaces/ants/preprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 458f41f955..aaa5f9efe0 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -351,8 +351,8 @@ def _run_interface(self, runtime): def _list_outputs(self): outputs = self._outputs().get() - pth, fname, _ = split_filename(self.inputs.matrix) + pth, fname, _ = split_filename(self.inputs.ants_matrix) new_fname = '{}{}'.format(fname, '.par') out_file = os.path.join(pth, new_fname) - outputs["parameters"] = out_file + outputs["fsl_params"] = out_file return outputs From 7401ad0e59a45abbbdb65c5a8aeb6fc801fdf932 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Mar 2017 13:35:51 -0700 Subject: [PATCH 41/48] update motioncorr2fslparams test to check to see if the original and converted parameters are close in value --- .../ants/tests/test_MotionCorr2FSLParams.py | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py index e055542da0..4e577d128b 100644 --- a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py +++ b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py @@ -1,34 +1,53 @@ -import csv import os +import numpy + from nipype.interfaces.ants.preprocess import MotionCorr2FSLParams +from nipype.interfaces.fsl.utils import AvScale from nipype.utils.tmpdirs import InTemporaryDirectory from nipype.utils.filemanip import split_filename def test_MotionCorr2FSLParams(): with InTemporaryDirectory(): cwd = os.getcwd() + fsl_mat_fname = "fsl_style.mat" + fp = open(fsl_mat_fname, 'w+') + fp.write( + "1.000000 0.000000 -0.000935 0.062539\n" + "0.000001 0.999999 0.001470 -0.162467\n" + "0.000935 -0.001470 0.999999 -0.279038\n" + "0.000000 0.000000 0.000000 1.000000\n" + ) + fp.close() + in_filename = os.path.join(cwd, 'in_file.csv') fp = open(in_filename, 'w+') fp.write("this line is ignored\n") fp.write( - "0,-0.99918075422702,1.00028207678993,-7.41063731046199e-06," - "3.93289649449106e-05,1.24969530535555e-05,1.00021114616063," - "0.000233157514656132,-0.000195740079275366,-0.00033024846828514," - "0.999495881936253,-0.0168301940330171,-0.0549722774931478," - "0.202056708561567\n" + "0,-0.99918075422702,1.000000,0.000000,-0.000935,0.000001," + "0.999999,0.001470,0.000935,-0.001470,0.999999,-0.279038,0.062539," + "-0.162467,-0.279038\n" ) fp.close() + # m2p - matrix 2 parameters m2p = MotionCorr2FSLParams() m2p.inputs.ants_matrix = in_filename m2p.run() pth, fname, _ = split_filename(in_filename) - params_fname = '{}{}'.format(fname, '.par') - params_fname = os.path.join(pth, params_fname) + conv_params_fname = '{}{}'.format(fname, '.par') + conv_params_fname = os.path.join(pth, conv_params_fname) + + avscale = AvScale() + avscale.inputs.all_param = True + avscale.inputs.mat_file = fsl_mat_fname + avscale.run() + avscale_out = avscale.aggregate_outputs() + orig_params = [] + orig_params.extend(avscale_out.rot_angles) + orig_params.extend(avscale_out.translations) + conv_params = numpy.genfromtext(conv_params_fname, delimeter=' ') + comp = numpy.isclose(conv_params, orig_params) - params_fp = open(params_fname) - params_data = csv.reader(params_fp, delimiter=' ') - line = next(params_data) - assert len(line) == 6 + assert(False not in comp) From 56ebd4a3c08e815c778ce16f49d3ff95cd342fce Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Mar 2017 15:01:29 -0700 Subject: [PATCH 42/48] switch file operatations to use numpy in test_MotionCorr2FSLParams --- .../ants/tests/test_MotionCorr2FSLParams.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py index 4e577d128b..9f7765aa2f 100644 --- a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py +++ b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py @@ -11,24 +11,24 @@ def test_MotionCorr2FSLParams(): with InTemporaryDirectory(): cwd = os.getcwd() fsl_mat_fname = "fsl_style.mat" - fp = open(fsl_mat_fname, 'w+') - fp.write( - "1.000000 0.000000 -0.000935 0.062539\n" - "0.000001 0.999999 0.001470 -0.162467\n" - "0.000935 -0.001470 0.999999 -0.279038\n" - "0.000000 0.000000 0.000000 1.000000\n" + fsl_mat = numpy.array( + [ + [1.000000, 0.000000, -0.000935, 0.062539], + [0.000001, 0.999999, 0.001470, -0.162467], + [0.000935, -0.001470, 0.999999, -0.279038], + [0.000000, 0.000000, 0.000000, 1.000000] + ] ) - fp.close() + numpy.savetxt(fsl_mat_fname, fsl_mat, delimiter=' ') in_filename = os.path.join(cwd, 'in_file.csv') - fp = open(in_filename, 'w+') - fp.write("this line is ignored\n") - fp.write( - "0,-0.99918075422702,1.000000,0.000000,-0.000935,0.000001," - "0.999999,0.001470,0.000935,-0.001470,0.999999,-0.279038,0.062539," - "-0.162467,-0.279038\n" - ) - fp.close() + ants_mat = numpy.array([ + 0, -0.99918075422702, 1.000000, 0.000000, -0.000935, 0.000001, + 0.999999, 0.001470, 0.000935, -0.001470, 0.999999, -0.279038, + 0.062539, -0.162467, -0.279038 + ]) + numpy.savetxt(in_filename, ants_mat, delimiter=',', + header="this line is ignored") # m2p - matrix 2 parameters m2p = MotionCorr2FSLParams() @@ -47,7 +47,7 @@ def test_MotionCorr2FSLParams(): orig_params = [] orig_params.extend(avscale_out.rot_angles) orig_params.extend(avscale_out.translations) - conv_params = numpy.genfromtext(conv_params_fname, delimeter=' ') + conv_params = numpy.genfromtxt(conv_params_fname, delimiter=' ') comp = numpy.isclose(conv_params, orig_params) assert(False not in comp) From a13a7dd1716704cb9ff5a555d07fbbdad8d9cfa0 Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Mar 2017 15:08:31 -0700 Subject: [PATCH 43/48] switch file operations in MotionCorr2FslParams to use numpy savetxt --- nipype/interfaces/ants/preprocess.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index aaa5f9efe0..cb70c25783 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -343,10 +343,9 @@ def _run_interface(self, runtime): pth, fname, _ = split_filename(self.inputs.ants_matrix) new_fname = '{}{}'.format(fname, '.par') - fsl_params = os.path.join(pth, new_fname) - with open(fsl_params, mode='wt') as out_fp: - out_fp.write('\n'.join(pars)) - in_fp.close() + fsl_params_fname = os.path.join(pth, new_fname) + fsl_params = numpy.array(pars) + numpy.savetxt(fsl_params_fname, fsl_params, delimiter=' ') return runtime def _list_outputs(self): From 696aa4a334860088d70255fe5bc6829c598d23bc Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Mar 2017 16:36:10 -0700 Subject: [PATCH 44/48] have newly generated parameters be saved as arrays not strings so that output via numpy works as expected --- nipype/interfaces/ants/preprocess.py | 5 +- .../ants/tests/test_MotionCorr2FSLParams.py | 87 ++++++++++--------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index cb70c25783..07c00e978d 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -337,9 +337,8 @@ def _run_interface(self, runtime): mat[1] = [x[5], x[6], x[7]] mat[2] = [x[8], x[9], x[10]] param_z, param_y, param_x = mat2euler(mat) - parameters = "{:.8f} {:.8f} {:.8f} {:.8f} {:.8f} {:.8f}" - pars.append(parameters.format(param_x, param_y, param_z, float(x[11]), float(x[12]), - float(x[13]))) + pars.append([param_x, param_y, param_z, float(x[11]), float(x[12]), + float(x[13])]) pth, fname, _ = split_filename(self.inputs.ants_matrix) new_fname = '{}{}'.format(fname, '.par') diff --git a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py index 9f7765aa2f..ce24522c6f 100644 --- a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py +++ b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py @@ -8,46 +8,47 @@ from nipype.utils.filemanip import split_filename def test_MotionCorr2FSLParams(): - with InTemporaryDirectory(): - cwd = os.getcwd() - fsl_mat_fname = "fsl_style.mat" - fsl_mat = numpy.array( - [ - [1.000000, 0.000000, -0.000935, 0.062539], - [0.000001, 0.999999, 0.001470, -0.162467], - [0.000935, -0.001470, 0.999999, -0.279038], - [0.000000, 0.000000, 0.000000, 1.000000] - ] - ) - numpy.savetxt(fsl_mat_fname, fsl_mat, delimiter=' ') - - in_filename = os.path.join(cwd, 'in_file.csv') - ants_mat = numpy.array([ - 0, -0.99918075422702, 1.000000, 0.000000, -0.000935, 0.000001, - 0.999999, 0.001470, 0.000935, -0.001470, 0.999999, -0.279038, - 0.062539, -0.162467, -0.279038 - ]) - numpy.savetxt(in_filename, ants_mat, delimiter=',', - header="this line is ignored") - - # m2p - matrix 2 parameters - m2p = MotionCorr2FSLParams() - m2p.inputs.ants_matrix = in_filename - m2p.run() - - pth, fname, _ = split_filename(in_filename) - conv_params_fname = '{}{}'.format(fname, '.par') - conv_params_fname = os.path.join(pth, conv_params_fname) - - avscale = AvScale() - avscale.inputs.all_param = True - avscale.inputs.mat_file = fsl_mat_fname - avscale.run() - avscale_out = avscale.aggregate_outputs() - orig_params = [] - orig_params.extend(avscale_out.rot_angles) - orig_params.extend(avscale_out.translations) - conv_params = numpy.genfromtxt(conv_params_fname, delimiter=' ') - comp = numpy.isclose(conv_params, orig_params) - - assert(False not in comp) + #with InTemporaryDirectory(): + cwd = os.getcwd() + fsl_mat_fname = "fsl_style.mat" + fsl_mat = numpy.array( + [ + [1.000000, 0.000000, -0.000935, 0.062539], + [0.000001, 0.999999, 0.001470, -0.162467], + [0.000935, -0.001470, 0.999999, -0.279038], + [0.000000, 0.000000, 0.000000, 1.000000] + ] + ) + numpy.savetxt(fsl_mat_fname, fsl_mat, delimiter=' ') + + in_filename = os.path.join(cwd, 'in_file.csv') + ants_mat = numpy.array([[ + 0, -0.99918075422702, 1.000000, 0.000000, -0.000935, 0.000001, + 0.999999, 0.001470, 0.000935, -0.001470, 0.999999, -0.279038, + 0.062539, -0.162467, -0.279038 + ]]) + numpy.savetxt(in_filename, ants_mat, delimiter=',', fmt='%.8f', + header='this line is ignored') + + + # m2p - matrix 2 parameters + m2p = MotionCorr2FSLParams() + m2p.inputs.ants_matrix = in_filename + m2p.run() + + pth, fname, _ = split_filename(in_filename) + conv_params_fname = '{}{}'.format(fname, '.par') + conv_params_fname = os.path.join(pth, conv_params_fname) + + avscale = AvScale() + avscale.inputs.all_param = True + avscale.inputs.mat_file = fsl_mat_fname + avscale.run() + avscale_out = avscale.aggregate_outputs() + orig_params = [] + orig_params.extend(avscale_out.rot_angles) + orig_params.extend(avscale_out.translations) + conv_params = numpy.genfromtxt(conv_params_fname, delimiter=' ') + comp = numpy.isclose(conv_params, orig_params) + + assert(False not in comp) From 77250b23ac0abd659feb29c893c10b88cdc8209f Mon Sep 17 00:00:00 2001 From: Ross Blair Date: Thu, 16 Mar 2017 17:02:32 -0700 Subject: [PATCH 45/48] add in c3d transformation - broken --- .../interfaces/ants/tests/test_MotionCorr2FSLParams.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py index ce24522c6f..87572ad6c7 100644 --- a/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py +++ b/nipype/interfaces/ants/tests/test_MotionCorr2FSLParams.py @@ -2,6 +2,7 @@ import numpy +from nipype.interfaces.c3 import C3dAffineTool from nipype.interfaces.ants.preprocess import MotionCorr2FSLParams from nipype.interfaces.fsl.utils import AvScale from nipype.utils.tmpdirs import InTemporaryDirectory @@ -21,6 +22,15 @@ def test_MotionCorr2FSLParams(): ) numpy.savetxt(fsl_mat_fname, fsl_mat, delimiter=' ') + c3 = C3dAffineTool() + c3.inputs.source_file = fsl_mat_fname + c3.inputs.itk_transform = 'affine.txt' + c3.inputs.fsl2ras = True + c3.run() + c3_out = c3.aggregate_outpus() + + # ants = numpy.fromfile('affine.txt') + in_filename = os.path.join(cwd, 'in_file.csv') ants_mat = numpy.array([[ 0, -0.99918075422702, 1.000000, 0.000000, -0.000935, 0.000001, From d0d03b0c6cdef3807f79daf614f92f4c3fca4446 Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 22 Mar 2017 11:46:33 -0700 Subject: [PATCH 46/48] use numpy to handle matrices --- nipype/interfaces/ants/preprocess.py | 47 ++++++++++++---------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index 07c00e978d..d6dcab8428 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -6,7 +6,7 @@ import os from nibabel.eulerangles import mat2euler -import numpy +import numpy as np from ..base import (BaseInterface, BaseInterfaceInputSpec, TraitedSpec, File, traits, isdefined, Str) @@ -304,8 +304,9 @@ def _list_outputs(self): outputs['displacement_field'] = os.path.abspath(fname) return outputs + class MotionCorr2FSLParamsInputSpec(BaseInterfaceInputSpec): - ants_matrix = File( + ants_moco = File( exists=True, desc='Motion correction matrices to be converted into FSL style motion parameters', mandatory=True @@ -323,34 +324,26 @@ class MotionCorr2FSLParams(BaseInterface): input_spec = MotionCorr2FSLParamsInputSpec output_spec = MotionCorr2FSLParamsOutputSpec + def __init__(self, **inputs): + self._results = {} + super(MotionCorr2FSLParams, self).__init__(**inputs) + + def _list_outputs(self): + return self._results + def _run_interface(self, runtime): - in_fp = open(self.inputs.ants_matrix) - in_data = csv.reader(in_fp) + # Ants motion correction output has a single line header that we ignore + in_data = np.loadtxt(self.inputs.ants_moco, + delimiter=',', skiprows=1, dtype=np.float32)[:, 2:] pars = [] + for row in in_data: + mat = row[:9].reshape(3, 3) + param_z, param_y, param_x = mat2euler(mat) + pars.append([param_x, param_y, param_z] + row[9:].tolist()) - # Ants motion correction output has a single line header that we ignore - next(in_data) + _, fname, _ = split_filename(self.inputs.ants_moco) + self._results['fsl_params'] = os.path.abspath('{}_fsl{}'.format(fname, '.par')) + np.savetxt(self._results['fsl_params'], pars, delimiter=' ') - for x in in_data: - mat = numpy.zeros((3, 3)) - mat[0] = [x[2], x[3], x[4]] - mat[1] = [x[5], x[6], x[7]] - mat[2] = [x[8], x[9], x[10]] - param_z, param_y, param_x = mat2euler(mat) - pars.append([param_x, param_y, param_z, float(x[11]), float(x[12]), - float(x[13])]) - - pth, fname, _ = split_filename(self.inputs.ants_matrix) - new_fname = '{}{}'.format(fname, '.par') - fsl_params_fname = os.path.join(pth, new_fname) - fsl_params = numpy.array(pars) - numpy.savetxt(fsl_params_fname, fsl_params, delimiter=' ') return runtime - def _list_outputs(self): - outputs = self._outputs().get() - pth, fname, _ = split_filename(self.inputs.ants_matrix) - new_fname = '{}{}'.format(fname, '.par') - out_file = os.path.join(pth, new_fname) - outputs["fsl_params"] = out_file - return outputs From d8786aa2bf294669e0d9914741670bd480f4159a Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 22 Mar 2017 19:03:03 -0700 Subject: [PATCH 47/48] add moco2itk --- nipype/interfaces/ants/preprocess.py | 56 +++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index d6dcab8428..ef62966e65 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -5,8 +5,9 @@ import csv import os -from nibabel.eulerangles import mat2euler import numpy as np +import nibabel as nb +from nibabel.eulerangles import mat2euler from ..base import (BaseInterface, BaseInterfaceInputSpec, TraitedSpec, File, traits, isdefined, Str) @@ -14,6 +15,14 @@ from ...utils.filemanip import split_filename +ITK_TFM_HEADER = "#Insight Transform File V1.0" +ITK_TFM_TPL = """\ +#Transform {tf_id} +Transform: {tf_type} +Parameters: {tf_params} +FixedParameters: {fixed_params}""".format + + class MotionCorrStatsInputSpec(ANTSCommandInputSpec): ''' Input spec for the antsMotionCorrStats command ''' mask = File(argstr="-x %s", mandatory=True, @@ -161,6 +170,7 @@ class MotionCorrOutputSpec(TraitedSpec): save_state = File(desc="The saved registration state to be restored") displacement_field = File(desc=("4D displacement field that captures the " "affine induced motion at each voxel")) + out_tfm = File(desc='write transforms in the Insight transform format') class MotionCorr(ANTSCommand): ''' @@ -212,6 +222,10 @@ class MotionCorr(ANTSCommand): input_spec = MotionCorrInputSpec output_spec = MotionCorrOutputSpec + def _run_interface(self, runtime): + runtime = super(MotionCorr, self)._run_interface(runtime) + + return runtime def _gen_filename(self, name): ''' @@ -347,3 +361,43 @@ def _run_interface(self, runtime): return runtime +def moco2itk(in_csv, in_reference, out_file=None): + """ + Generates an Insight Transform File V1.0 from the motion correction + CSV file. + + Would be replaced if finally `this issue `_ + is fixed. + + """ + from scipy.ndimage.measurements import center_of_mass + + movpar = np.loadtxt(in_csv, dtype=float, skiprows=1, + delimiter=',')[2:] + + nii = nb.load(in_reference) + + # Convert first to RAS, then to LPS + cmfixed = np.diag([-1, -1, 1, 1]).dot(nii.affine).dot( + list(center_of_mass(nii.get_data())) + [1]) + + tf_type = ('AffineTransform_double_3_3' if movpar.shape[1] == 12 + else 'Euler3DTransform_double_3_3') + + xfm_file = [ITK_TFM_HEADER] + for i, param in enumerate(movpar): + xfm_file.append(ITK_TFM_TPL( + tf_id=i, tf_type=tf_type, + tf_params=' '.join(['%.8f' % p for p in param]), + fixed_params=' '.join(['%.8f' % p for p in cmfixed[:3]]) + )) + xfm_file += [''] + + if out_file is None: + _, fname, _ = split_filename(in_reference) + out_file = os.path.abspath('{}_ants.tfm'.format(fname)) + + with open(out_file, 'w') as outfh: + outfh.write("\n".join(xfm_file)) + + return out_file From e87346bbd4a12124c05b4d204779e7767da5fe9b Mon Sep 17 00:00:00 2001 From: oesteban Date: Sat, 29 Apr 2017 18:29:57 -0700 Subject: [PATCH 48/48] add doctest, use name_source when possible --- nipype/interfaces/ants/preprocess.py | 100 ++++++++++++++------------- nipype/testing/data/ants_moco.csv | 0 2 files changed, 51 insertions(+), 49 deletions(-) create mode 100644 nipype/testing/data/ants_moco.csv diff --git a/nipype/interfaces/ants/preprocess.py b/nipype/interfaces/ants/preprocess.py index ef62966e65..c8c4943beb 100644 --- a/nipype/interfaces/ants/preprocess.py +++ b/nipype/interfaces/ants/preprocess.py @@ -31,7 +31,8 @@ class MotionCorrStatsInputSpec(ANTSCommandInputSpec): argstr="-m %s", mandatory=True, desc="motion correction parameter file to calculate statistics on" ) - output = File(argstr="-o %s", hash_files=False, genfile=True, + output = File(name_source=['moco'], name_template='%s_fd.csv', keep_ext=False, + argstr="-o %s", hash_files=False, desc="csv file to output calculated statistics into") framewise = traits.Bool(argstr="-f %d", desc="do framwise summarywise stats") @@ -44,36 +45,43 @@ class MotionCorrStatsOutputSpec(TraitedSpec): output = File(desc="CSV file containg motion correction statistics", exists=True) class MotionCorrStats(ANTSCommand): - ''' Interface for the antsMotionCorrStats command ''' + """ + Interface for the antsMotionCorrStats command + + Example + ------- + + >>> from nipype.interfaces.ants import MotionCorrStats + >>> stats = MotionCorrStats(framewise=True) + >>> stats.inputs.mask = 'brain_mask.nii' + >>> stats.inputs.moco = 'ants_moco.csv' + >>> stats.cmdline # doctest: +ALLOW_UNICODE + 'antsMotionCorrStats -f 1 -x brain_mask.nii -m ants_moco.csv -o ants_moco_fd.csv' + + """ _cmd = 'antsMotionCorrStats' input_spec = MotionCorrStatsInputSpec output_spec = MotionCorrStatsOutputSpec - def _gen_filename(self, name): - if name == 'output': - return "frame_displacement.csv" - return None - def _list_outputs(self): outputs = self._outputs().get() if isdefined(self.inputs.output_spatial_map): outputs['spatial_map'] = ( os.path.abspath(self.inputs.output_spatial_map) ) - if isdefined(self.inputs.output): - outputs['output'] = os.path.abspath(self.inputs.output) return outputs class MotionCorrInputSpec(ANTSCommandInputSpec): '''Input spec for the antsMotionCorr command.''' - dimension_desc = ( - "This option forces the image to be treated as a " - "specified-dimensional image. If not specified, N4 tries to infer " - "the dimensionality from the input image." - ) - dimensionality = traits.Enum(3, 2, argstr='-d %d', usedefault=True, - position=0, desc=dimension_desc) + + dimensionality = traits.Enum( + 3, 2, argstr='-d %d', usedefault=True, position=0, + desc=( + "This option forces the image to be treated as a " + "specified-dimensional image. If not specified, N4 tries to infer " + "the dimensionality from the input image." + )) average_image = File(argstr='-a %s', position=1, exists=False, desc="4D image to take an average of.") @@ -82,20 +90,18 @@ class MotionCorrInputSpec(ANTSCommandInputSpec): genfile=True, hash_files=False, argstr='%s') output_transform_prefix = Str( - desc="string to prepend to file containg the transformation parameters" + '', desc="string to prepend to file containg the transformation parameters" ) output_warped_image = Str(desc="Name to save motion corrected image as.") - - metric_type_desc = ( - "GC : global correlation, CC: ANTS neighborhood cross correlation, " - "MI: Mutual information, and Demons: Thirion's Demons " - "(modified mean-squares). Note that the metricWeight is currently not " - "used. Rather, it is a temporary place holder until multivariate " - "metrics are available for a single stage." - ) - - metric_type = traits.Enum("CC", "MeanSquares", "Demons", "GC", "MI", - "Mattes", argstr="%s", desc=metric_type_desc) + metric_type = traits.Enum( + "CC", "MeanSquares", "Demons", "GC", "MI", "Mattes", argstr="%s", + desc=( + "GC : global correlation, CC: ANTS neighborhood cross correlation, " + "MI: Mutual information, and Demons: Thirion's Demons " + "(modified mean-squares). Note that the metricWeight is currently not " + "used. Rather, it is a temporary place holder until multivariate " + "metrics are available for a single stage." + )) fixed_image = File(requires=['metric_type'], desc="Fixed image to do motion correction with respect to.") moving_image = File(requires=['metric_type'], @@ -158,6 +164,10 @@ class MotionCorrInputSpec(ANTSCommandInputSpec): desc="Write the low-dimensional 3D transforms to a 4D displacement field" ) + out_composite_transform = File( + name_source=['output_transform_prefix'], name_template='%sMOCOparams.csv', + output_name='composite_transform', desc='output file containing head-motion transforms') + class MotionCorrOutputSpec(TraitedSpec): @@ -174,6 +184,12 @@ class MotionCorrOutputSpec(TraitedSpec): class MotionCorr(ANTSCommand): ''' + Format and description of the affine motion correction parameters can be + found in this PDF starting on page 555 section 3.9.16 AffineTransform: + https://itk.org/ItkSoftwareGuide.pdf + + https://github.com/stnava/ANTs/blob/master/Scripts/antsMotionCorrExample + Examples ------- @@ -197,36 +213,27 @@ class MotionCorr(ANTSCommand): >>> ants_mc.inputs.gradient_step_length = 0.005 >>> ants_mc.inputs.fixed_image = "average_image.nii.gz" >>> ants_mc.inputs.moving_image = "input.nii.gz" - >>> print(ants_mc.cmdline) - antsMotionCorr -d 3 -i 10x3 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1x1 -s 0.0x0.0 -t Affine[0.005] -u 1 -e 1 + >>> ants_mc.cmdline # doctest: +ALLOW_UNICODE + 'antsMotionCorr -d 3 -i 10x3 -m GC[average_image.nii.gz,input.nii.gz,1.0,1,Random,0.05] -n\ + 10 -o [motcorr,warped.nii.gz,average_image.nii.gz] -f 1x1 -s 0.0x0.0 -t Affine[0.005] -u 1 -e 1' >>> from nipype.interfaces.ants.preprocess import MotionCorr >>> ants_avg = MotionCorr() >>> ants_avg.inputs.average_image = 'input.nii.gz' >>> ants_avg.inputs.output_average_image = 'avg_out.nii.gz' - >>> print(ants_avg.cmdline) - antsMotionCorr -d 3 -a input.nii.gz -o avg_out.nii.gz + >>> ants_mc.cmdline # doctest: +ALLOW_UNICODE + 'antsMotionCorr -d 3 -a input.nii.gz -o avg_out.nii.gz' >>> ants_avg = MotionCorr() >>> ants_avg.inputs.average_image = 'input.nii.gz' - >>> print(ants_avg.cmdline) - antsMotionCorr -d 3 -a input.nii.gz -o input_avg.nii.gz + >>> ants_mc.cmdline # doctest: +ALLOW_UNICODE + 'antsMotionCorr -d 3 -a input.nii.gz -o input_avg.nii.gz' - Format and description of the affine motion correction parameters can be - found in this PDF starting on page 555 section 3.9.16 AffineTransform: - https://itk.org/ItkSoftwareGuide.pdf - - https://github.com/stnava/ANTs/blob/master/Scripts/antsMotionCorrExample ''' _cmd = 'antsMotionCorr' input_spec = MotionCorrInputSpec output_spec = MotionCorrOutputSpec - def _run_interface(self, runtime): - runtime = super(MotionCorr, self)._run_interface(runtime) - - return runtime - def _gen_filename(self, name): ''' If a fixed image is specified we are not going to be outputting @@ -306,11 +313,6 @@ def _list_outputs(self): outputs['warped_image'] = ( os.path.abspath(self.inputs.output_warped_image) ) - if isdefined(self.inputs.output_transform_prefix): - fname = '{}MOCOparams.csv'.format( - self.inputs.output_transform_prefix - ) - outputs['composite_transform'] = os.path.abspath(fname) if (isdefined(self.inputs.write_displacement) and isdefined(self.inputs.output_transform_prefix) and self.inputs.write_displacement is True): diff --git a/nipype/testing/data/ants_moco.csv b/nipype/testing/data/ants_moco.csv new file mode 100644 index 0000000000..e69de29bb2