Repository: nipy/nipype

Issue:
FIX: DTITK Interface
Fixes #2462 

Changes proposed in this pull request
- This is addressing the code review from @satra and @effigies  #2463 

Closes #2463.



Discussion:
THEY PASSED!!! Thank you so much for your help, @satra and @effigies !!!
https://github.com/kesshijordan/shared_notebooks/blob/master/Kesh_Test_DTITK_Interface_Registration_Utils.ipynb
Just checking in... is this in my court to review, or do you have things you're planning to do first?
Great, thanks for making those changes. Since this is so big, I'll have another read through in a couple days, but I suspect this is just about done. Thanks a lot for your patience.
Thanks for all the time you have put into this review! 
@effigies I haven't found descriptions for a few of the flags in the documentation. I may see if I can get ahold of the author and ask if he can look this over.
Here's a mixin you can put in `dtitk/base.py`

```Python
class DTITKRenameMixin(object):
    def __init__(self, *args, **kwargs):
        classes = [cls.__name__ for cls in self.__class__.mro()]
        dep_name = classes[0]
        rename_idx = classes.index('DTITKRenameMixin')
        new_name = classes[rename_idx + 1]
        warnings.warn('The {} interface has been renamed to {}\n'
                      'Please see the documentation for DTI-TK '
                      'interfaces, as some inputs have been '
                      'added or renamed for clarity.'
                      ''.format(dep_name, new_name),
                      DeprecationWarning)
        super(DTITKRenameMixin, self).__init__(*args, **kwargs)
```

And then for classes that have been renamed, we can just have a big sequence of dummy classes like so:

`dtitk/registration.py`:
```Python
class RigidTask(DTITKRenameMixin, Rigid):
    pass
...
class diffeoScalarVolTask(DTITKRenameMixin, DiffeoScalarVol):
    pass
```

`dtitk/utils.py`:
```Python
class TVAdjustVoxSpTask(DTITKRenameMixin, TVAdjustVoxSp):
    pass
...
class BinThreshTask(DTITKRenameMixin, BinThresh):
    pass
```

@satra @mgxd @djarecka I suspect you might have preferences for how deprecations are handled. Feel free to suggest any modifications (e.g. do we want to set a version that these will be removed by?).

Resolving patch:
diff --git a/nipype/interfaces/dcm2nii.py b/nipype/interfaces/dcm2nii.py
--- a/nipype/interfaces/dcm2nii.py
+++ b/nipype/interfaces/dcm2nii.py
@@ -264,7 +264,7 @@ class Dcm2niixInputSpec(CommandLineInputSpec):
         position=-1,
         copyfile=False,
         mandatory=True,
-        desc=('A set of filenames to be converted. Note that the current ' 
+        desc=('A set of filenames to be converted. Note that the current '
               'version (1.0.20180328) of dcm2niix converts any files in the '
               'directory. To only convert specific files they should be in an '
               'isolated directory'),
diff --git a/nipype/workflows/dmri/dtitk/tensor_registration.py b/nipype/workflows/dmri/dtitk/tensor_registration.py
--- a/nipype/workflows/dmri/dtitk/tensor_registration.py
+++ b/nipype/workflows/dmri/dtitk/tensor_registration.py
@@ -52,10 +52,11 @@ def diffeomorphic_tensor_pipeline(name='DiffeoTen',
                                   params={'array_size': (128, 128, 64)}):
     """
     Workflow that performs a diffeomorphic registration
-    (Rigid and Affine follwed by Diffeomorphic)
+    (Rigid and Affine followed by Diffeomorphic)
     Note: the requirements for a diffeomorphic registration specify that
     the dimension 0 is a power of 2 so images are resliced prior to
-    registration
+    registration. Remember to move origin and reslice prior to applying xfm to
+    another file!
 
     Example
     -------
@@ -75,7 +76,9 @@ def diffeomorphic_tensor_pipeline(name='DiffeoTen',
                          fields=['out_file', 'out_file_xfm',
                                  'fixed_resliced', 'moving_resliced']),
                          name='outputnode')
-
+    origin_node_fixed = pe.Node(dtitk.TVAdjustVoxSp(origin=(0, 0, 0)),
+                                name='origin_node_fixed')
+    origin_node_moving = origin_node_fixed.clone(name='origin_node_moving')
     reslice_node_pow2 = pe.Node(dtitk.TVResample(
                                 origin=(0, 0, 0),
                                 array_size=params['array_size']),
@@ -92,14 +95,23 @@ def diffeomorphic_tensor_pipeline(name='DiffeoTen',
     compose_xfm_node = pe.Node(dtitk.ComposeXfm(), name='compose_xfm_node')
     apply_xfm_node = pe.Node(dtitk.DiffeoSymTensor3DVol(),
                              name='apply_xfm_node')
+    adjust_vs_node_to_input = pe.Node(dtitk.TVAdjustVoxSp(),
+                                      name='adjust_vs_node_to_input')
+    reslice_node_to_input = pe.Node(dtitk.TVResample(),
+                                    name='reslice_node_to_input')
+    input_fa = pe.Node(dtitk.TVtool(in_flag='fa'), name='input_fa')
 
     wf = pe.Workflow(name=name)
 
+    # calculate input FA image for origin reference
+    wf.connect(inputnode, 'fixed_file', input_fa, 'in_file')
     # Reslice input images
-    wf.connect(inputnode, 'fixed_file', reslice_node_pow2, 'in_file')
+    wf.connect(inputnode, 'fixed_file', origin_node_fixed, 'in_file')
+    wf.connect(origin_node_fixed, 'out_file', reslice_node_pow2, 'in_file')
     wf.connect(reslice_node_pow2, 'out_file',
                reslice_node_moving, 'target_file')
-    wf.connect(inputnode, 'moving_file', reslice_node_moving, 'in_file')
+    wf.connect(inputnode, 'moving_file', origin_node_moving, 'in_file')
+    wf.connect(origin_node_moving, 'out_file', reslice_node_moving, 'in_file')
     # Rigid registration
     wf.connect(reslice_node_pow2, 'out_file', rigid_node, 'fixed_file')
     wf.connect(reslice_node_moving, 'out_file', rigid_node, 'moving_file')
@@ -118,8 +130,13 @@ def diffeomorphic_tensor_pipeline(name='DiffeoTen',
     # Apply transform
     wf.connect(reslice_node_moving, 'out_file', apply_xfm_node, 'in_file')
     wf.connect(compose_xfm_node, 'out_file', apply_xfm_node, 'transform')
+    # Move origin and reslice to match original fixed input image
+    wf.connect(apply_xfm_node, 'out_file', adjust_vs_node_to_input, 'in_file')
+    wf.connect(input_fa, 'out_file', adjust_vs_node_to_input, 'target_file')
+    wf.connect(adjust_vs_node_to_input, 'out_file', reslice_node_to_input, 'in_file')
+    wf.connect(input_fa, 'out_file', reslice_node_to_input, 'target_file')
     # Send to output
-    wf.connect(apply_xfm_node, 'out_file', outputnode, 'out_file')
+    wf.connect(reslice_node_to_input, 'out_file', outputnode, 'out_file')
     wf.connect(compose_xfm_node, 'out_file', outputnode, 'out_file_xfm')
     wf.connect(reslice_node_pow2, 'out_file', outputnode, 'fixed_resliced')
     wf.connect(reslice_node_moving, 'out_file', outputnode, 'moving_resliced')
diff --git a/tools/checkspecs.py b/tools/checkspecs.py
--- a/tools/checkspecs.py
+++ b/tools/checkspecs.py
@@ -287,7 +287,7 @@ def test_specs(self, uri):
                         and "requires" not in trait.__dict__\
                         and "xor" not in trait.__dict__:
                     if trait.trait_type.__class__.__name__ is "Range"\
-                            and trait.default == trait.trait_type._low: 
+                            and trait.default == trait.trait_type._low:
                         continue
                     bad_specs.append(
                         [uri, c, 'Inputs', traitname, 'default value is set, no value for usedefault'])


Test patch:
diff --git a/nipype/tests/test_nipype.py b/nipype/tests/test_nipype.py
--- a/nipype/tests/test_nipype.py
+++ b/nipype/tests/test_nipype.py
@@ -12,7 +12,7 @@ def test_nipype_info():
     assert exception_not_raised
 
 
-@pytest.mark.skipif(not get_nipype_gitversion(), 
+@pytest.mark.skipif(not get_nipype_gitversion(),
                     reason="not able to get version from get_nipype_gitversion")
 def test_git_hash():
     # removing the first "g" from gitversion