Repository: nipy/nipype

Issue:
Issues of interfacing with PETPVC modules
Hi @effigies @alexsavio, I have run it in a Node and the content of the file report.rst as following:

Node: petpvc
============

 Hierarchy : pvc
 Exec ID : pvc

Original Inputs
---------------

* alpha : <undefined>
* args : <undefined>
* debug : False
* environ : {}
* fwhm_x : 2.0
* fwhm_y : 2.0
* fwhm_z : 2.0
* ignore_exception : False
* in_file : C:\Users\135173\Desktop\pvc_test\PET_nii\x_WB_CTAC_Body.nii.gz
* mask_file : C:\Users\135173\Desktop\pvc_test\mask_nill\skin_mask.nii.gz
* n_deconv : <undefined>
* n_iter : <undefined>
* out_file : C:\Users\135173\Desktop\pvc_test\pet_pvc_rbv.nii.gz
* pvc : RBV
* stop_crit : <undefined>
* terminal_output : stream

Thanks.


Comments:

Patch:
diff --git a/.mailmap b/.mailmap
index af5a39bd6..f7d32274f 100644
--- a/.mailmap
+++ b/.mailmap
@@ -83,6 +83,7 @@ Michael Waskom <michael.l.waskom@gmail.com> Michael Waskom <mwaskom@mit.edu>
 Michael Waskom <michael.l.waskom@gmail.com> Michael Waskom <mwaskom@stanford.edu>
 Michael Waskom <michael.l.waskom@gmail.com> mwaskom <mwaskom@mit.edu>
 Michael Waskom <michael.l.waskom@gmail.com> mwaskom <mwaskom@stanford.edu>
+Michael Waskom <michael.l.waskom@gmail.com> mwaskom <mwaskom@nyu.edu>
 Oscar Esteban <code@oscaresteban.es> Oscar Esteban <code@oscaresteban>
 Oscar Esteban <code@oscaresteban.es> oesteban <code@oscaresteban.es>
 Russell Poldrack <poldrack@gmail.com> Russ Poldrack <poldrack@gmail.com>
diff --git a/nipype/interfaces/afni/__init__.py b/nipype/interfaces/afni/__init__.py
index 60076eefc..6fb20abf4 100644
--- a/nipype/interfaces/afni/__init__.py
+++ b/nipype/interfaces/afni/__init__.py
@@ -17,7 +17,7 @@ from .preprocess import (Allineate, Automask, AutoTcorrelate,
                          Seg, SkullStrip, TCorr1D, TCorrMap, TCorrelate,
                          TShift, Volreg, Warp, QwarpPlusMinus)
 from .svm import (SVMTest, SVMTrain)
-from .utils import (AFNItoNIFTI, Autobox, BrickStat, Calc, Copy,
+from .utils import (AFNItoNIFTI, Autobox, BrickStat, Calc, Copy, Edge3,
                     Eval, FWHMx,
                     MaskTool, Merge, Notes, Refit, Resample, TCat, TStat, To3D,
                     Unifize, ZCutUp, GCOR,)
diff --git a/nipype/interfaces/afni/utils.py b/nipype/interfaces/afni/utils.py
index 0e6455496..8500f998d 100644
--- a/nipype/interfaces/afni/utils.py
+++ b/nipype/interfaces/afni/utils.py
@@ -394,6 +394,99 @@ class Copy(AFNICommand):
     output_spec = AFNICommandOutputSpec
 
 
+class Edge3InputSpec(AFNICommandInputSpec):
+    in_file = File(
+        desc='input file to 3dedge3',
+        argstr='-input %s',
+        position=0,
+        mandatory=True,
+        exists=True,
+        copyfile=False)
+    out_file = File(
+        desc='output image file name',
+        position=-1,
+        argstr='-prefix %s')
+    datum = traits.Enum(
+        'byte','short','float',
+        argstr='-datum %s',
+        desc='specify data type for output. Valid types are \'byte\', '
+             '\'short\' and \'float\'.')
+    fscale = traits.Bool(
+        desc='Force scaling of the output to the maximum integer range.',
+        argstr='-fscale',
+        xor=['gscale', 'nscale', 'scale_floats'])
+    gscale = traits.Bool(
+        desc='Same as \'-fscale\', but also forces each output sub-brick to '
+             'to get the same scaling factor.',
+        argstr='-gscale',
+        xor=['fscale', 'nscale', 'scale_floats'])
+    nscale = traits.Bool(
+        desc='Don\'t do any scaling on output to byte or short datasets.',
+        argstr='-nscale',
+        xor=['fscale', 'gscale', 'scale_floats'])
+    scale_floats = traits.Float(
+        desc='Multiply input by VAL, but only if the input datum is '
+             'float. This is needed when the input dataset '
+             'has a small range, like 0 to 2.0 for instance. '
+             'With such a range, very few edges are detected due to '
+             'what I suspect to be truncation problems. '
+             'Multiplying such a dataset by 10000 fixes the problem '
+             'and the scaling is undone at the output.',
+        argstr='-scale_floats %f',
+        xor=['fscale', 'gscale', 'nscale'])
+    verbose = traits.Bool(
+        desc='Print out some information along the way.',
+        argstr='-verbose')
+
+
+class Edge3(AFNICommand):
+    """Does 3D Edge detection using the library 3DEdge
+    by Gregoire Malandain (gregoire.malandain@sophia.inria.fr).
+
+    For complete details, see the `3dedge3 Documentation.
+    <https://afni.nimh.nih.gov/pub/dist/doc/program_help/3dedge3.html>`_
+
+    references_ = [{'entry': BibTeX('@article{Deriche1987,'
+                                    'author={R. Deriche},'
+                                    'title={Optimal edge detection using recursive filtering},'
+                                    'journal={International Journal of Computer Vision},'
+                                    'volume={2},',
+                                    'pages={167-187},'
+                                    'year={1987},'
+                                    '}'),
+                    'tags': ['method'],
+                    },
+                   {'entry': BibTeX('@article{MongaDericheMalandainCocquerez1991,'
+                                    'author={O. Monga, R. Deriche, G. Malandain, J.P. Cocquerez},'
+                                    'title={Recursive filtering and edge tracking: two primary tools for 3D edge detection},'
+                                    'journal={Image and vision computing},'
+                                    'volume={9},',
+                                    'pages={203-214},'
+                                    'year={1991},'
+                                    '}'),
+                    'tags': ['method'],
+                    },
+                   ]
+
+    Examples
+    ========
+
+    >>> from nipype.interfaces import afni
+    >>> edge3 = afni.Edge3()
+    >>> edge3.inputs.in_file = 'functional.nii'
+    >>> edge3.inputs.out_file = 'edges.nii'
+    >>> edge3.inputs.datum = 'byte'
+    >>> edge3.cmdline  # doctest: +ALLOW_UNICODE
+    '3dedge3 -input functional.nii -datum byte -prefix edges.nii'
+    >>> res = edge3.run()  # doctest: +SKIP
+
+    """
+
+    _cmd = '3dedge3'
+    input_spec = Edge3InputSpec
+    output_spec = AFNICommandOutputSpec
+
+
 class EvalInputSpec(AFNICommandInputSpec):
     in_file_a = File(
         desc='input file to 1deval',
diff --git a/nipype/interfaces/base.py b/nipype/interfaces/base.py
index 8d7c53cde..2f8b1bf0e 100644
--- a/nipype/interfaces/base.py
+++ b/nipype/interfaces/base.py
@@ -1381,6 +1381,35 @@ def _get_ram_mb(pid, pyfunc=False):
     return mem_mb
 
 
+def _canonicalize_env(env):
+    """Windows requires that environment be dicts with bytes as keys and values
+    This function converts any unicode entries for Windows only, returning the
+    dictionary untouched in other environments.
+
+    Parameters
+    ----------
+    env : dict
+        environment dictionary with unicode or bytes keys and values
+
+    Returns
+    -------
+    env : dict
+        Windows: environment dictionary with bytes keys and values
+        Other: untouched input ``env``
+    """
+    if os.name != 'nt':
+        return env
+
+    out_env = {}
+    for key, val in env:
+        if not isinstance(key, bytes):
+            key = key.encode('utf-8')
+        if not isinstance(val, bytes):
+            val = key.encode('utf-8')
+        out_env[key] = val
+    return out_env
+
+
 # Get max resources used for process
 def get_max_resources_used(pid, mem_mb, num_threads, pyfunc=False):
     """Function to get the RAM and threads usage of a process
@@ -1435,6 +1464,8 @@ def run_command(runtime, output=None, timeout=0.01, redirect_x=False):
             raise RuntimeError('Xvfb was not found, X redirection aborted')
         cmdline = 'xvfb-run -a ' + cmdline
 
+    env = _canonicalize_env(runtime.environ)
+
     default_encoding = locale.getdefaultlocale()[1]
     if default_encoding is None:
         default_encoding = 'UTF-8'
@@ -1449,14 +1480,14 @@ def run_command(runtime, output=None, timeout=0.01, redirect_x=False):
                                 stderr=stderr,
                                 shell=True,
                                 cwd=runtime.cwd,
-                                env=runtime.environ)
+                                env=env)
     else:
         proc = subprocess.Popen(cmdline,
                                 stdout=PIPE,
                                 stderr=PIPE,
                                 shell=True,
                                 cwd=runtime.cwd,
-                                env=runtime.environ)
+                                env=env)
     result = {}
     errfile = os.path.join(runtime.cwd, 'stderr.nipype')
     outfile = os.path.join(runtime.cwd, 'stdout.nipype')
diff --git a/nipype/interfaces/fsl/epi.py b/nipype/interfaces/fsl/epi.py
index 1f4a7ded1..38c65efee 100644
--- a/nipype/interfaces/fsl/epi.py
+++ b/nipype/interfaces/fsl/epi.py
@@ -143,6 +143,9 @@ class TOPUPInputSpec(FSLCommandInputSpec):
     out_warp_prefix = traits.Str("warpfield", argstr='--dfout=%s', hash_files=False,
                                  desc='prefix for the warpfield images (in mm)',
                                  usedefault=True)
+    out_mat_prefix = traits.Str("xfm", argstr='--rbmout=%s', hash_files=False,
+                                desc='prefix for the realignment matrices',
+                                usedefault=True)
     out_jac_prefix = traits.Str("jac", argstr='--jacout=%s',
                                  hash_files=False,
                                  desc='prefix for the warpfield images',
@@ -221,6 +224,7 @@ class TOPUPOutputSpec(TraitedSpec):
     out_field = File(desc='name of image file with field (Hz)')
     out_warps = traits.List(File(exists=True), desc='warpfield images')
     out_jacs = traits.List(File(exists=True), desc='Jacobian images')
+    out_mats = traits.List(File(exists=True), desc='realignment matrices')
     out_corrected = File(desc='name of 4D image file with unwarped images')
     out_logfile = File(desc='name of log-file')
 
@@ -247,7 +251,7 @@ class TOPUP(FSLCommand):
     'topup --config=b02b0.cnf --datain=topup_encoding.txt \
 --imain=b0_b0rev.nii --out=b0_b0rev_base --iout=b0_b0rev_corrected.nii.gz \
 --fout=b0_b0rev_field.nii.gz --jacout=jac --logout=b0_b0rev_topup.log \
---dfout=warpfield'
+--rbmout=xfm --dfout=warpfield'
     >>> res = topup.run() # doctest: +SKIP
 
     """
@@ -289,6 +293,9 @@ class TOPUP(FSLCommand):
         outputs['out_jacs'] = [
             fmt(prefix=self.inputs.out_jac_prefix, i=i, ext=ext)
             for i in range(1, n_vols + 1)]
+        outputs['out_mats'] = [
+            fmt(prefix=self.inputs.out_mat_prefix, i=i, ext=".mat")
+            for i in range(1, n_vols + 1)]
 
         if isdefined(self.inputs.encoding_direction):
             outputs['out_enc_file'] = self._get_encfilename()


Test patch:
diff --git a/nipype/interfaces/afni/tests/test_auto_Edge3.py b/nipype/interfaces/afni/tests/test_auto_Edge3.py
new file mode 100644
index 000000000..51a4dc865
--- /dev/null
+++ b/nipype/interfaces/afni/tests/test_auto_Edge3.py
@@ -0,0 +1,57 @@
+# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
+from __future__ import unicode_literals
+from ..utils import Edge3
+
+
+def test_Edge3_inputs():
+    input_map = dict(args=dict(argstr='%s',
+    ),
+    datum=dict(argstr='-datum %s',
+    ),
+    environ=dict(nohash=True,
+    usedefault=True,
+    ),
+    fscale=dict(argstr='-fscale',
+    xor=['gscale', 'nscale', 'scale_floats'],
+    ),
+    gscale=dict(argstr='-gscale',
+    xor=['fscale', 'nscale', 'scale_floats'],
+    ),
+    ignore_exception=dict(nohash=True,
+    usedefault=True,
+    ),
+    in_file=dict(argstr='-input %s',
+    copyfile=False,
+    mandatory=True,
+    position=0,
+    ),
+    nscale=dict(argstr='-nscale',
+    xor=['fscale', 'gscale', 'scale_floats'],
+    ),
+    out_file=dict(argstr='-prefix %s',
+    position=-1,
+    ),
+    outputtype=dict(),
+    scale_floats=dict(argstr='-scale_floats %f',
+    xor=['fscale', 'gscale', 'nscale'],
+    ),
+    terminal_output=dict(nohash=True,
+    ),
+    verbose=dict(argstr='-verbose',
+    ),
+    )
+    inputs = Edge3.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_Edge3_outputs():
+    output_map = dict(out_file=dict(),
+    )
+    outputs = Edge3.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/fsl/tests/test_auto_TOPUP.py b/nipype/interfaces/fsl/tests/test_auto_TOPUP.py
index 28083c6dc..88f11a77d 100644
--- a/nipype/interfaces/fsl/tests/test_auto_TOPUP.py
+++ b/nipype/interfaces/fsl/tests/test_auto_TOPUP.py
@@ -64,6 +64,10 @@ def test_TOPUP_inputs():
     name_source=['in_file'],
     name_template='%s_topup.log',
     ),
+    out_mat_prefix=dict(argstr='--rbmout=%s',
+    hash_files=False,
+    usedefault=True,
+    ),
     out_warp_prefix=dict(argstr='--dfout=%s',
     hash_files=False,
     usedefault=True,