From 309da9517e0cb534aa88f44e0e658e68c84c1e42 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauldmccarthy@gmail.com>
Date: Sun, 21 Jul 2019 16:31:20 +0100
Subject: [PATCH] ENH: New fsl_apply_x5 script, to apply a X5 transformation to
 an image

---
 fsl/scripts/fsl_apply_x5.py | 135 ++++++++++++++++++++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 fsl/scripts/fsl_apply_x5.py

diff --git a/fsl/scripts/fsl_apply_x5.py b/fsl/scripts/fsl_apply_x5.py
new file mode 100644
index 000000000..8128ec348
--- /dev/null
+++ b/fsl/scripts/fsl_apply_x5.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+#
+# fsl_apply_x5.py - Apply an X5 transformation to an image.
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+#
+"""The ``fsl_apply_x5`` script can be used to apply an X5 transformation file
+to resample an image.
+"""
+
+
+import functools as ft
+import              sys
+import              argparse
+
+import fsl.transform.x5         as x5
+import fsl.transform.nonlinear  as nonlinear
+import fsl.utils.parse_data     as parse_data
+import fsl.utils.image.resample as resample
+import fsl.data.image           as fslimage
+
+
+def parseArgs(args=None):
+    """Parses command-line arguments.
+
+    :arg args: Sequence of command-line arguments. If ``None``, ``sys.argv``
+               is used
+    :returns:  An ``argparse.Namespace`` object containing parsed arguments
+    """
+
+    parser = argparse.ArgumentParser('fsl_apply_x5')
+    flags  = {
+        'input'  : ('input',),
+        'xform'  : ('xform',),
+        'output' : ('output',),
+        'interp' : ('-i', '--interp'),
+        'ref'    : ('-r', '--ref'),
+    }
+
+    helps  = {
+        'input'  : 'Input image',
+        'xform'  : 'X5 transformation file',
+        'output' : 'Output image',
+        'interp' : 'Interpolation (default: linear)',
+        'ref'    : 'Alternate reference image (default: '
+                   'reference specified in X5 file)',
+    }
+    opts = {
+        'input'  : dict(helps['input'],
+                        type=parse_data.Image),
+        'xform'  : dict(helps['xform']),
+        'output' : dict(helps['output'],
+                        type=parse_data.ImageOut),
+        'interp' : dict(helps['interp'],
+                        choices=('nearest', 'linear', 'cubic'),
+                        default='linear'),
+        'ref'    : dict(helps['ref'],
+                        type=ft.partial(parse_data.Image, loadData=False)),
+    }
+
+    parser.add_argument(*flags['input'],  **opts['input'])
+    parser.add_argument(*flags['xform'],  **opts['xform'])
+    parser.add_argument(*flags['output'], **opts['output'])
+    parser.add_argument(*flags['interp'], **opts['interp'])
+    parser.add_argument(*flags['ref'],    **opts['ref'])
+
+    args = parser.parse_args(args)
+
+    if   args.interp == 'nearest': args.interp = 0
+    elif args.interp == 'linear':  args.interp = 1
+    elif args.interp == 'cubic':   args.interp = 3
+
+    return args
+
+
+def applyLinear(args):
+    """Applies a linear X5 transformation file to the input.
+
+    :arg args: ``argparse.Namespace`` object
+    :returns:  The transformed input as an :class:`.Image` object
+    """
+
+    input           = args.input
+    xform, src, ref = x5.readLinearX5(args.xform)
+
+    if args.ref is not None:
+        ref = args.ref
+
+    res, xform = resample.resampleToReference(input,
+                                              ref,
+                                              matrix=xform,
+                                              order=args.interp)
+
+    return fslimage.Image(res, xform=xform, header=ref.header)
+
+
+def applyNonlinear(args):
+    """Applies a non-linear X5 transformation file to the input.
+
+    :arg args: ``argparse.Namespace`` object
+    :returns:  The transformed input as an :class:`.Image` object
+    """
+
+    field = x5.readNonLinearX5(args.xform)
+
+    if args.ref is None: ref = field.ref
+    else:                ref = args.ref
+
+    result = nonlinear.applyDeformation(args.input,
+                                        field,
+                                        ref=ref,
+                                        order=args.interp,
+                                        mode='constant')
+
+    return fslimage.Image(result, header=ref.header)
+
+
+def main(args=None):
+    """Entry point. Parse command-line arguments, then calls
+    :func:`applyLinear` or :func:`applyNonlinear` depending on the x5 file
+    type.
+    """
+
+    args = parseArgs(args)
+
+    if x5.inferType(args.xform) == 'linear':
+        result = applyLinear(args)
+    else:
+        result = applyNonlinear(args)
+
+    result.save(args.output)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
-- 
GitLab