From 81b9c83ab022cc499325cdb5675fcce42581622f Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Tue, 2 Jun 2020 16:38:20 -0400 Subject: [PATCH 1/3] RESTORE ants.legacy, removed in 4a84a0275 --- nipype/interfaces/ants/legacy.py | 373 +++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 nipype/interfaces/ants/legacy.py diff --git a/nipype/interfaces/ants/legacy.py b/nipype/interfaces/ants/legacy.py new file mode 100644 index 0000000000..bdeaa02279 --- /dev/null +++ b/nipype/interfaces/ants/legacy.py @@ -0,0 +1,373 @@ +# -*- coding: utf-8 -*- +# NOTE: This implementation has been superceeded buy the antsApplyTransform +# implmeentation that more closely follows the strucutre and capabilities +# of the antsApplyTransform program. This implementation is here +# for backwards compatibility. +"""ANTS Apply Transforms interface +""" + +from builtins import range + +import os +from glob import glob + +from .base import ANTSCommand, ANTSCommandInputSpec +from ..base import TraitedSpec, File, traits, isdefined, OutputMultiPath +from ...utils.filemanip import split_filename + + +class antsIntroductionInputSpec(ANTSCommandInputSpec): + dimension = traits.Enum( + 3, + 2, + argstr="-d %d", + usedefault=True, + desc="image dimension (2 or 3)", + position=1, + ) + reference_image = File( + exists=True, + argstr="-r %s", + desc="template file to warp to", + mandatory=True, + copyfile=True, + ) + input_image = File( + exists=True, + argstr="-i %s", + desc="input image to warp to template", + mandatory=True, + copyfile=False, + ) + force_proceed = traits.Bool( + argstr="-f 1", + desc=("force script to proceed even if headers " "may be incompatible"), + ) + inverse_warp_template_labels = traits.Bool( + argstr="-l", + desc=( + "Applies inverse warp to the template labels " + "to estimate label positions in target space (use " + "for template-based segmentation)" + ), + ) + max_iterations = traits.List( + traits.Int, + argstr="-m %s", + sep="x", + desc=( + "maximum number of iterations (must be " + "list of integers in the form [J,K,L...]: " + "J = coarsest resolution iterations, K = " + "middle resolution interations, L = fine " + "resolution iterations" + ), + ) + bias_field_correction = traits.Bool( + argstr="-n 1", desc=("Applies bias field correction to moving " "image") + ) + similarity_metric = traits.Enum( + "PR", + "CC", + "MI", + "MSQ", + argstr="-s %s", + desc=( + "Type of similartiy metric used for registration " + "(CC = cross correlation, MI = mutual information, " + "PR = probability mapping, MSQ = mean square difference)" + ), + ) + transformation_model = traits.Enum( + "GR", + "EL", + "SY", + "S2", + "EX", + "DD", + "RI", + "RA", + argstr="-t %s", + usedefault=True, + desc=( + "Type of transofmration model used for registration " + "(EL = elastic transformation model, SY = SyN with time, " + "arbitrary number of time points, S2 = SyN with time " + "optimized for 2 time points, GR = greedy SyN, EX = " + "exponential, DD = diffeomorphic demons style exponential " + "mapping, RI = purely rigid, RA = affine rigid" + ), + ) + out_prefix = traits.Str( + "ants_", + argstr="-o %s", + usedefault=True, + desc=("Prefix that is prepended to all output " "files (default = ants_)"), + ) + quality_check = traits.Bool( + argstr="-q 1", desc="Perform a quality check of the result" + ) + + +class antsIntroductionOutputSpec(TraitedSpec): + affine_transformation = File(exists=True, desc="affine (prefix_Affine.txt)") + warp_field = File(exists=True, desc="warp field (prefix_Warp.nii)") + inverse_warp_field = File( + exists=True, desc="inverse warp field (prefix_InverseWarp.nii)" + ) + input_file = File(exists=True, desc="input image (prefix_repaired.nii)") + output_file = File(exists=True, desc="output image (prefix_deformed.nii)") + + +class antsIntroduction(ANTSCommand): + """Uses ANTS to generate matrices to warp data from one space to another. + + Examples + -------- + + >>> from nipype.interfaces.ants.legacy import antsIntroduction + >>> warp = antsIntroduction() + >>> warp.inputs.reference_image = 'Template_6.nii' + >>> warp.inputs.input_image = 'structural.nii' + >>> warp.inputs.max_iterations = [30,90,20] + >>> warp.cmdline + 'antsIntroduction.sh -d 3 -i structural.nii -m 30x90x20 -o ants_ -r Template_6.nii -t GR' + + """ + + _cmd = "antsIntroduction.sh" + input_spec = antsIntroductionInputSpec + output_spec = antsIntroductionOutputSpec + + def _list_outputs(self): + outputs = self._outputs().get() + transmodel = self.inputs.transformation_model + + # When transform is set as 'RI'/'RA', wrap fields should not be expected + # The default transformation is GR, which outputs the wrap fields + if not isdefined(transmodel) or ( + isdefined(transmodel) and transmodel not in ["RI", "RA"] + ): + outputs["warp_field"] = os.path.join( + os.getcwd(), self.inputs.out_prefix + "Warp.nii.gz" + ) + outputs["inverse_warp_field"] = os.path.join( + os.getcwd(), self.inputs.out_prefix + "InverseWarp.nii.gz" + ) + + outputs["affine_transformation"] = os.path.join( + os.getcwd(), self.inputs.out_prefix + "Affine.txt" + ) + outputs["input_file"] = os.path.join( + os.getcwd(), self.inputs.out_prefix + "repaired.nii.gz" + ) + outputs["output_file"] = os.path.join( + os.getcwd(), self.inputs.out_prefix + "deformed.nii.gz" + ) + + return outputs + + +# How do we make a pass through so that GenWarpFields is just an alias for antsIntroduction ? + + +class GenWarpFields(antsIntroduction): + pass + + +class buildtemplateparallelInputSpec(ANTSCommandInputSpec): + dimension = traits.Enum( + 3, + 2, + 4, + argstr="-d %d", + usedefault=True, + desc="image dimension (2, 3 or 4)", + position=1, + ) + out_prefix = traits.Str( + "antsTMPL_", + argstr="-o %s", + usedefault=True, + desc=("Prefix that is prepended to all output " "files (default = antsTMPL_)"), + ) + in_files = traits.List( + File(exists=True), + mandatory=True, + desc="list of images to generate template from", + argstr="%s", + position=-1, + ) + parallelization = traits.Enum( + 0, + 1, + 2, + argstr="-c %d", + usedefault=True, + desc=( + "control for parallel processing (0 = " + "serial, 1 = use PBS, 2 = use PEXEC, 3 = " + "use Apple XGrid" + ), + ) + gradient_step_size = traits.Float( + argstr="-g %f", + desc=("smaller magnitude results in " "more cautious steps (default = " ".25)"), + ) + iteration_limit = traits.Int( + 4, argstr="-i %d", usedefault=True, desc="iterations of template construction" + ) + num_cores = traits.Int( + argstr="-j %d", + requires=["parallelization"], + desc=( + "Requires parallelization = 2 (PEXEC). " "Sets number of cpu cores to use" + ), + ) + max_iterations = traits.List( + traits.Int, + argstr="-m %s", + sep="x", + desc=( + "maximum number of iterations (must be " + "list of integers in the form [J,K,L...]: " + "J = coarsest resolution iterations, K = " + "middle resolution interations, L = fine " + "resolution iterations" + ), + ) + bias_field_correction = traits.Bool( + argstr="-n 1", desc=("Applies bias field correction to moving " "image") + ) + rigid_body_registration = traits.Bool( + argstr="-r 1", + desc=( + "registers inputs before creating template " + "(useful if no initial template available)" + ), + ) + similarity_metric = traits.Enum( + "PR", + "CC", + "MI", + "MSQ", + argstr="-s %s", + desc=( + "Type of similartiy metric used for registration " + "(CC = cross correlation, MI = mutual information, " + "PR = probability mapping, MSQ = mean square difference)" + ), + ) + transformation_model = traits.Enum( + "GR", + "EL", + "SY", + "S2", + "EX", + "DD", + argstr="-t %s", + usedefault=True, + desc=( + "Type of transofmration model used for registration " + "(EL = elastic transformation model, SY = SyN with time, " + "arbitrary number of time points, S2 = SyN with time " + "optimized for 2 time points, GR = greedy SyN, EX = " + "exponential, DD = diffeomorphic demons style exponential " + "mapping" + ), + ) + use_first_as_target = traits.Bool( + desc=( + "uses first volume as target of " + "all inputs. When not used, an " + "unbiased average image is used " + "to start." + ) + ) + + +class buildtemplateparallelOutputSpec(TraitedSpec): + final_template_file = File(exists=True, desc="final ANTS template") + template_files = OutputMultiPath( + File(exists=True), desc="Templates from different stages of iteration" + ) + subject_outfiles = OutputMultiPath( + File(exists=True), + desc=( + "Outputs for each input image. Includes warp " + "field, inverse warp, Affine, original image " + "(repaired) and warped image (deformed)" + ), + ) + + +class buildtemplateparallel(ANTSCommand): + """Generate a optimal average template + + .. warning:: + + This can take a VERY long time to complete + + Examples + -------- + + >>> from nipype.interfaces.ants.legacy import buildtemplateparallel + >>> tmpl = buildtemplateparallel() + >>> tmpl.inputs.in_files = ['T1.nii', 'structural.nii'] + >>> tmpl.inputs.max_iterations = [30, 90, 20] + >>> tmpl.cmdline + 'buildtemplateparallel.sh -d 3 -i 4 -m 30x90x20 -o antsTMPL_ -c 0 -t GR T1.nii structural.nii' + + """ + + _cmd = "buildtemplateparallel.sh" + input_spec = buildtemplateparallelInputSpec + output_spec = buildtemplateparallelOutputSpec + + def _format_arg(self, opt, spec, val): + if opt == "num_cores": + if self.inputs.parallelization == 2: + return "-j " + str(val) + else: + return "" + if opt == "in_files": + if self.inputs.use_first_as_target: + start = "-z " + else: + start = "" + return start + " ".join(name for name in val) + return super(buildtemplateparallel, self)._format_arg(opt, spec, val) + + def _list_outputs(self): + outputs = self._outputs().get() + outputs["template_files"] = [] + for i in range(len(glob(os.path.realpath("*iteration*")))): + temp = os.path.realpath( + "%s_iteration_%d/%stemplate.nii.gz" + % (self.inputs.transformation_model, i, self.inputs.out_prefix) + ) + os.rename( + temp, + os.path.realpath( + "%s_iteration_%d/%stemplate_i%d.nii.gz" + % (self.inputs.transformation_model, i, self.inputs.out_prefix, i) + ), + ) + file_ = "%s_iteration_%d/%stemplate_i%d.nii.gz" % ( + self.inputs.transformation_model, + i, + self.inputs.out_prefix, + i, + ) + + outputs["template_files"].append(os.path.realpath(file_)) + outputs["final_template_file"] = os.path.realpath( + "%stemplate.nii.gz" % self.inputs.out_prefix + ) + outputs["subject_outfiles"] = [] + for filename in self.inputs.in_files: + _, base, _ = split_filename(filename) + temp = glob(os.path.realpath("%s%s*" % (self.inputs.out_prefix, base))) + for file_ in temp: + outputs["subject_outfiles"].append(file_) + return outputs From 124d4ef7668eaece4236b2c5a814148bb3c838f1 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Tue, 2 Jun 2020 16:42:09 -0400 Subject: [PATCH 2/3] make specs --- .../ants/tests/test_auto_GenWarpFields.py | 45 +++++++++++++++++++ .../ants/tests/test_auto_antsIntroduction.py | 45 +++++++++++++++++++ .../tests/test_auto_buildtemplateparallel.py | 41 +++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 nipype/interfaces/ants/tests/test_auto_GenWarpFields.py create mode 100644 nipype/interfaces/ants/tests/test_auto_antsIntroduction.py create mode 100644 nipype/interfaces/ants/tests/test_auto_buildtemplateparallel.py diff --git a/nipype/interfaces/ants/tests/test_auto_GenWarpFields.py b/nipype/interfaces/ants/tests/test_auto_GenWarpFields.py new file mode 100644 index 0000000000..11fbe56ccd --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_GenWarpFields.py @@ -0,0 +1,45 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..legacy import GenWarpFields + + +def test_GenWarpFields_inputs(): + input_map = dict( + args=dict(argstr="%s",), + bias_field_correction=dict(argstr="-n 1",), + dimension=dict(argstr="-d %d", position=1, usedefault=True,), + environ=dict(nohash=True, usedefault=True,), + force_proceed=dict(argstr="-f 1",), + input_image=dict( + argstr="-i %s", copyfile=False, extensions=None, mandatory=True, + ), + inverse_warp_template_labels=dict(argstr="-l",), + max_iterations=dict(argstr="-m %s", sep="x",), + num_threads=dict(nohash=True, usedefault=True,), + out_prefix=dict(argstr="-o %s", usedefault=True,), + quality_check=dict(argstr="-q 1",), + reference_image=dict( + argstr="-r %s", copyfile=True, extensions=None, mandatory=True, + ), + similarity_metric=dict(argstr="-s %s",), + transformation_model=dict(argstr="-t %s", usedefault=True,), + ) + inputs = GenWarpFields.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_GenWarpFields_outputs(): + output_map = dict( + affine_transformation=dict(extensions=None,), + input_file=dict(extensions=None,), + inverse_warp_field=dict(extensions=None,), + output_file=dict(extensions=None,), + warp_field=dict(extensions=None,), + ) + outputs = GenWarpFields.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_antsIntroduction.py b/nipype/interfaces/ants/tests/test_auto_antsIntroduction.py new file mode 100644 index 0000000000..d7950f38b5 --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_antsIntroduction.py @@ -0,0 +1,45 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..legacy import antsIntroduction + + +def test_antsIntroduction_inputs(): + input_map = dict( + args=dict(argstr="%s",), + bias_field_correction=dict(argstr="-n 1",), + dimension=dict(argstr="-d %d", position=1, usedefault=True,), + environ=dict(nohash=True, usedefault=True,), + force_proceed=dict(argstr="-f 1",), + input_image=dict( + argstr="-i %s", copyfile=False, extensions=None, mandatory=True, + ), + inverse_warp_template_labels=dict(argstr="-l",), + max_iterations=dict(argstr="-m %s", sep="x",), + num_threads=dict(nohash=True, usedefault=True,), + out_prefix=dict(argstr="-o %s", usedefault=True,), + quality_check=dict(argstr="-q 1",), + reference_image=dict( + argstr="-r %s", copyfile=True, extensions=None, mandatory=True, + ), + similarity_metric=dict(argstr="-s %s",), + transformation_model=dict(argstr="-t %s", usedefault=True,), + ) + inputs = antsIntroduction.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_antsIntroduction_outputs(): + output_map = dict( + affine_transformation=dict(extensions=None,), + input_file=dict(extensions=None,), + inverse_warp_field=dict(extensions=None,), + output_file=dict(extensions=None,), + warp_field=dict(extensions=None,), + ) + outputs = antsIntroduction.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_buildtemplateparallel.py b/nipype/interfaces/ants/tests/test_auto_buildtemplateparallel.py new file mode 100644 index 0000000000..35b225949b --- /dev/null +++ b/nipype/interfaces/ants/tests/test_auto_buildtemplateparallel.py @@ -0,0 +1,41 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..legacy import buildtemplateparallel + + +def test_buildtemplateparallel_inputs(): + input_map = dict( + args=dict(argstr="%s",), + bias_field_correction=dict(argstr="-n 1",), + dimension=dict(argstr="-d %d", position=1, usedefault=True,), + environ=dict(nohash=True, usedefault=True,), + gradient_step_size=dict(argstr="-g %f",), + in_files=dict(argstr="%s", mandatory=True, position=-1,), + iteration_limit=dict(argstr="-i %d", usedefault=True,), + max_iterations=dict(argstr="-m %s", sep="x",), + num_cores=dict(argstr="-j %d", requires=["parallelization"],), + num_threads=dict(nohash=True, usedefault=True,), + out_prefix=dict(argstr="-o %s", usedefault=True,), + parallelization=dict(argstr="-c %d", usedefault=True,), + rigid_body_registration=dict(argstr="-r 1",), + similarity_metric=dict(argstr="-s %s",), + transformation_model=dict(argstr="-t %s", usedefault=True,), + use_first_as_target=dict(), + ) + inputs = buildtemplateparallel.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_buildtemplateparallel_outputs(): + output_map = dict( + final_template_file=dict(extensions=None,), + subject_outfiles=dict(), + template_files=dict(), + ) + outputs = buildtemplateparallel.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 81e8f9ed6ff3636e7e2ec3916249b4084a066af7 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Tue, 2 Jun 2020 16:45:25 -0400 Subject: [PATCH 3/3] Update module description --- nipype/interfaces/ants/legacy.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nipype/interfaces/ants/legacy.py b/nipype/interfaces/ants/legacy.py index bdeaa02279..0cc8b7e864 100644 --- a/nipype/interfaces/ants/legacy.py +++ b/nipype/interfaces/ants/legacy.py @@ -1,9 +1,8 @@ # -*- coding: utf-8 -*- -# NOTE: This implementation has been superceeded buy the antsApplyTransform -# implmeentation that more closely follows the strucutre and capabilities -# of the antsApplyTransform program. This implementation is here -# for backwards compatibility. -"""ANTS Apply Transforms interface +"""ANTS Legacy Interfaces + +These interfaces are for programs that have been deprecated by ANTs, but +are preserved for backwards compatibility. """ from builtins import range