Repository: nipy/nipype

Issue:
antslegacy module
I notice that in the newest version the `nipype.interfaces.ants.legacy` module is missing, and I cannot find a class for `antsIntroduction()` anywhere in the package.

I know that using antsIntroduction is probably not the best idea, in fact I need it for an article recommending against it for preclinical imaging, but I need it for the benchmark. Is ti not coming back, and should I thus depend on older nipype for the article source code?


Discussion:
It seems to have been dropped here https://github.com/nipy/nipype/pull/2654/files#diff-a2e391882927e53f0bd8855c06af7009.

What's `antsIntroduction` for and, if its usage is not discouraged, maybe we should bring just that one back?
@oesteban it's a mini-workflow by the ANTs team which is useful if you don't want to use an external preprocessing pipeline. I actually try to convince people not to use it with preclinical data, but with human data it can be quite useful given the numerous good heuristic presets they figured out: https://github.com/ANTsX/ANTs/blob/master/Scripts/antsIntroduction.sh

It would be really great if you can reinstate it.
@oesteban seeing as there's no action on this, can I just go ahead and bring it back? Will you pull?
@TheChymera - i can't remember why it was in the legacy module in the first place. perhaps @hjmjohnson does.

if there is no good reason for it to have been discarded, we should bring it back in some standard location, not legacy. 
@TheChymera sure, I have no bandwidth to do it but I'd be happy to accept the PR. As Satra mentions, please do not restore the whole antslegacy module, just the interface you need somewhere more appropriate.

Resolving patch:
diff --git a/nipype/interfaces/ants/legacy.py b/nipype/interfaces/ants/legacy.py
new file mode 100644
--- /dev/null
+++ b/nipype/interfaces/ants/legacy.py
@@ -0,0 +1,372 @@
+# -*- coding: utf-8 -*-
+"""ANTS Legacy Interfaces
+
+These interfaces are for programs that have been deprecated by ANTs, but
+are preserved for backwards compatibility.
+"""
+
+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


Test patch:
diff --git a/nipype/interfaces/ants/tests/test_auto_GenWarpFields.py b/nipype/interfaces/ants/tests/test_auto_GenWarpFields.py
new file mode 100644
--- /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
--- /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
--- /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