From eae3598397a465be7e0d4c1449a3aa34a8cde6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Alexandre=20C=C3=B4t=C3=A9?= Date: Tue, 6 Mar 2018 21:41:00 -0500 Subject: [PATCH 1/2] ENH: adding TRK <=> TCK streamlines conversion scripts --- bin/nib-tck2trk | 64 ++++++++++++++++++++++++ bin/nib-trk2tck | 47 ++++++++++++++++++ nibabel/tests/test_scripts.py | 93 ++++++++++++++++++++++++++++++++++- setup.py | 2 + 4 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 bin/nib-tck2trk create mode 100644 bin/nib-trk2tck diff --git a/bin/nib-tck2trk b/bin/nib-tck2trk new file mode 100644 index 0000000000..fee3ebed8a --- /dev/null +++ b/bin/nib-tck2trk @@ -0,0 +1,64 @@ +#!python +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +""" +Convert tractograms (TCK -> TRK). +""" +import os +import argparse + +import nibabel as nib + +from nibabel.streamlines import Field +from nibabel.orientations import aff2axcodes + + +def parse_args(): + DESCRIPTION = "Convert tractograms (TCK -> TRK)." + parser = argparse.ArgumentParser(description=DESCRIPTION) + parser.add_argument("anatomy", + help="reference anatomical image (.nii|.nii.gz.") + parser.add_argument("tractograms", metavar="tractogram", nargs="+", + help="list of tractograms (.tck).") + parser.add_argument("-f", "--force", action="store_true", + help="overwrite existing output files.") + + args = parser.parse_args() + return args, parser + + +def main(): + args, parser = parse_args() + + try: + nii = nib.load(args.anatomy) + except Exception: + parser.error("Expecting anatomical image as first agument.") + + for tractogram in args.tractograms: + tractogram_format = nib.streamlines.detect_format(tractogram) + if tractogram_format is not nib.streamlines.TckFile: + print("Skipping non TCK file: '{}'".format(tractogram)) + continue + + filename, _ = os.path.splitext(tractogram) + output_filename = filename + '.trk' + if os.path.isfile(output_filename) and not args.force: + msg = "Skipping existing file: '{}'. Use -f to overwrite." + print(msg.format(output_filename)) + continue + + # Build header using infos from the anatomical image. + header = {} + header[Field.VOXEL_TO_RASMM] = nii.affine.copy() + header[Field.VOXEL_SIZES] = nii.header.get_zooms()[:3] + header[Field.DIMENSIONS] = nii.shape[:3] + header[Field.VOXEL_ORDER] = "".join(aff2axcodes(nii.affine)) + + tck = nib.streamlines.load(tractogram) + nib.streamlines.save(tck.tractogram, output_filename, header=header) + + +if __name__ == '__main__': + main() diff --git a/bin/nib-trk2tck b/bin/nib-trk2tck new file mode 100644 index 0000000000..2e0848f67d --- /dev/null +++ b/bin/nib-trk2tck @@ -0,0 +1,47 @@ +#!python +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: + +""" +Convert tractograms (TRK -> TCK). +""" + +import os +import argparse + +import nibabel as nib + + +def parse_args(): + DESCRIPTION = "Convert tractograms (TRK -> TCK)." + parser = argparse.ArgumentParser(description=DESCRIPTION) + parser.add_argument("tractograms", metavar="tractogram", nargs="+", + help="list of tractograms (.trk).") + parser.add_argument("-f", "--force", action="store_true", + help="overwrite existing output files.") + + args = parser.parse_args() + return args, parser + + +def main(): + args, parser = parse_args() + for tractogram in args.tractograms: + tractogram_format = nib.streamlines.detect_format(tractogram) + if tractogram_format is not nib.streamlines.TrkFile: + print("Skipping non TRK file: '{}'".format(tractogram)) + continue + + filename, _ = os.path.splitext(tractogram) + output_filename = filename + '.tck' + if os.path.isfile(output_filename) and not args.force: + msg = "Skipping existing file: '{}'. Use -f to overwrite." + print(msg.format(output_filename)) + continue + + trk = nib.streamlines.load(tractogram) + nib.streamlines.save(trk.tractogram, output_filename) + + +if __name__ == '__main__': + main() diff --git a/nibabel/tests/test_scripts.py b/nibabel/tests/test_scripts.py index 941e2271b0..eab8d35164 100644 --- a/nibabel/tests/test_scripts.py +++ b/nibabel/tests/test_scripts.py @@ -8,6 +8,7 @@ import sys import os +import shutil from os.path import (dirname, join as pjoin, abspath, splitext, basename, exists) import csv @@ -15,6 +16,7 @@ import numpy as np +import nibabel as nib from ..tmpdirs import InTemporaryDirectory from ..loadsave import load from ..orientations import flip_axis, aff2axcodes, inv_ornt_aff @@ -22,7 +24,7 @@ from nose.tools import assert_true, assert_false, assert_equal from nose import SkipTest -from numpy.testing import assert_almost_equal +from numpy.testing import assert_almost_equal, assert_array_equal from .scriptrunner import ScriptRunner from .nibabel_data import needs_nibabel_data @@ -357,3 +359,92 @@ def test_parrec2nii_with_data(): assert_equal(sorted(csv_keys), ['diffusion b value number', 'gradient orientation number']) assert_equal(nlines, 8) # 8 volumes present in DTI.PAR + + +@script_test +def test_nib_trk2tck(): + simple_trk = pjoin(DATA_PATH, "simple.trk") + standard_trk = pjoin(DATA_PATH, "standard.trk") + + with InTemporaryDirectory() as tmpdir: + # Copy input files to convert. + shutil.copy(simple_trk, tmpdir) + shutil.copy(standard_trk, tmpdir) + simple_trk = pjoin(tmpdir, "simple.trk") + standard_trk = pjoin(tmpdir, "standard.trk") + simple_tck = pjoin(tmpdir, "simple.tck") + standard_tck = pjoin(tmpdir, "standard.tck") + + # Convert one file. + cmd = ["nib-trk2tck", simple_trk] + code, stdout, stderr = run_command(cmd) + assert_equal(len(stdout), 0) + assert_true(os.path.isfile(simple_tck)) + trk = nib.streamlines.load(simple_trk) + tck = nib.streamlines.load(simple_tck) + assert_array_equal(tck.streamlines.data, trk.streamlines.data) + assert_true(isinstance(tck, nib.streamlines.TckFile)) + + # Skip non TRK files. + cmd = ["nib-trk2tck", simple_tck] + code, stdout, stderr = run_command(cmd) + assert_true("Skipping non TRK file" in stdout) + + # By default, refuse to overwrite existing output files. + cmd = ["nib-trk2tck", simple_trk] + code, stdout, stderr = run_command(cmd) + assert_true("Skipping existing file" in stdout) + + # Convert multiple files and with --force. + cmd = ["nib-trk2tck", "--force", simple_trk, standard_trk] + code, stdout, stderr = run_command(cmd) + assert_equal(len(stdout), 0) + trk = nib.streamlines.load(standard_trk) + tck = nib.streamlines.load(standard_tck) + assert_array_equal(tck.streamlines.data, trk.streamlines.data) + + +@script_test +def test_nib_tck2trk(): + anat = pjoin(DATA_PATH, "standard.nii.gz") + standard_tck = pjoin(DATA_PATH, "standard.tck") + + with InTemporaryDirectory() as tmpdir: + # Copy input file to convert. + shutil.copy(standard_tck, tmpdir) + standard_trk = pjoin(tmpdir, "standard.trk") + standard_tck = pjoin(tmpdir, "standard.tck") + + # Anatomical image not found as first argument. + cmd = ["nib-tck2trk", standard_tck, anat] + code, stdout, stderr = run_command(cmd, check_code=False) + assert_equal(code, 2) # Parser error. + assert_true("Expecting anatomical image as first agument" in stderr) + + # Convert one file. + cmd = ["nib-tck2trk", anat, standard_tck] + code, stdout, stderr = run_command(cmd) + assert_equal(len(stdout), 0) + assert_true(os.path.isfile(standard_trk)) + tck = nib.streamlines.load(standard_tck) + trk = nib.streamlines.load(standard_trk) + assert_array_equal(trk.streamlines.data, tck.streamlines.data) + assert_true(isinstance(trk, nib.streamlines.TrkFile)) + + # Skip non TCK files. + cmd = ["nib-tck2trk", anat, standard_trk] + code, stdout, stderr = run_command(cmd) + assert_true("Skipping non TCK file" in stdout) + + # By default, refuse to overwrite existing output files. + cmd = ["nib-tck2trk", anat, standard_tck] + code, stdout, stderr = run_command(cmd) + assert_true("Skipping existing file" in stdout) + + # Convert multiple files and with --force. + cmd = ["nib-tck2trk", "--force", anat, standard_tck, standard_tck] + code, stdout, stderr = run_command(cmd) + assert_equal(len(stdout), 0) + tck = nib.streamlines.load(standard_tck) + trk = nib.streamlines.load(standard_trk) + assert_array_equal(tck.streamlines.data, trk.streamlines.data) diff --git a/setup.py b/setup.py index 009969a3c5..b0f5bc093c 100755 --- a/setup.py +++ b/setup.py @@ -117,6 +117,8 @@ def main(**extra_args): pjoin('bin', 'nib-ls'), pjoin('bin', 'nib-dicomfs'), pjoin('bin', 'nib-nifti-dx'), + pjoin('bin', 'nib-tck2trk'), + pjoin('bin', 'nib-trk2tck'), ], cmdclass = cmdclass, **extra_args From de686353ba9c932207a5b2d43a5f264e5d82bf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Alexandre=20C=C3=B4t=C3=A9?= Date: Thu, 12 Apr 2018 20:53:21 -0400 Subject: [PATCH 2/2] Move code for conversion scripts into nibabel.cmdline --- bin/nib-tck2trk | 60 +++++--------------------------------- bin/nib-trk2tck | 43 +++++---------------------- nibabel/cmdline/tck2trk.py | 56 +++++++++++++++++++++++++++++++++++ nibabel/cmdline/trk2tck.py | 39 +++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 89 deletions(-) create mode 100644 nibabel/cmdline/tck2trk.py create mode 100644 nibabel/cmdline/trk2tck.py diff --git a/bin/nib-tck2trk b/bin/nib-tck2trk index fee3ebed8a..896e67a5d1 100644 --- a/bin/nib-tck2trk +++ b/bin/nib-tck2trk @@ -1,63 +1,17 @@ #!python # emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: - +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Convert tractograms (TCK -> TRK). """ -import os -import argparse - -import nibabel as nib - -from nibabel.streamlines import Field -from nibabel.orientations import aff2axcodes - - -def parse_args(): - DESCRIPTION = "Convert tractograms (TCK -> TRK)." - parser = argparse.ArgumentParser(description=DESCRIPTION) - parser.add_argument("anatomy", - help="reference anatomical image (.nii|.nii.gz.") - parser.add_argument("tractograms", metavar="tractogram", nargs="+", - help="list of tractograms (.tck).") - parser.add_argument("-f", "--force", action="store_true", - help="overwrite existing output files.") - - args = parser.parse_args() - return args, parser - - -def main(): - args, parser = parse_args() - - try: - nii = nib.load(args.anatomy) - except Exception: - parser.error("Expecting anatomical image as first agument.") - - for tractogram in args.tractograms: - tractogram_format = nib.streamlines.detect_format(tractogram) - if tractogram_format is not nib.streamlines.TckFile: - print("Skipping non TCK file: '{}'".format(tractogram)) - continue - - filename, _ = os.path.splitext(tractogram) - output_filename = filename + '.trk' - if os.path.isfile(output_filename) and not args.force: - msg = "Skipping existing file: '{}'. Use -f to overwrite." - print(msg.format(output_filename)) - continue - - # Build header using infos from the anatomical image. - header = {} - header[Field.VOXEL_TO_RASMM] = nii.affine.copy() - header[Field.VOXEL_SIZES] = nii.header.get_zooms()[:3] - header[Field.DIMENSIONS] = nii.shape[:3] - header[Field.VOXEL_ORDER] = "".join(aff2axcodes(nii.affine)) - tck = nib.streamlines.load(tractogram) - nib.streamlines.save(tck.tractogram, output_filename, header=header) +from nibabel.cmdline.tck2trk import main if __name__ == '__main__': diff --git a/bin/nib-trk2tck b/bin/nib-trk2tck index 2e0848f67d..85509e7447 100644 --- a/bin/nib-trk2tck +++ b/bin/nib-trk2tck @@ -1,46 +1,17 @@ #!python # emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: - +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## """ Convert tractograms (TRK -> TCK). """ -import os -import argparse - -import nibabel as nib - - -def parse_args(): - DESCRIPTION = "Convert tractograms (TRK -> TCK)." - parser = argparse.ArgumentParser(description=DESCRIPTION) - parser.add_argument("tractograms", metavar="tractogram", nargs="+", - help="list of tractograms (.trk).") - parser.add_argument("-f", "--force", action="store_true", - help="overwrite existing output files.") - - args = parser.parse_args() - return args, parser - - -def main(): - args, parser = parse_args() - for tractogram in args.tractograms: - tractogram_format = nib.streamlines.detect_format(tractogram) - if tractogram_format is not nib.streamlines.TrkFile: - print("Skipping non TRK file: '{}'".format(tractogram)) - continue - - filename, _ = os.path.splitext(tractogram) - output_filename = filename + '.tck' - if os.path.isfile(output_filename) and not args.force: - msg = "Skipping existing file: '{}'. Use -f to overwrite." - print(msg.format(output_filename)) - continue - - trk = nib.streamlines.load(tractogram) - nib.streamlines.save(trk.tractogram, output_filename) +from nibabel.cmdline.trk2tck import main if __name__ == '__main__': diff --git a/nibabel/cmdline/tck2trk.py b/nibabel/cmdline/tck2trk.py new file mode 100644 index 0000000000..deb3adcd5f --- /dev/null +++ b/nibabel/cmdline/tck2trk.py @@ -0,0 +1,56 @@ +""" +Convert tractograms (TCK -> TRK). +""" +import os +import argparse + +import nibabel as nib + +from nibabel.streamlines import Field +from nibabel.orientations import aff2axcodes + + +def parse_args(): + DESCRIPTION = "Convert tractograms (TCK -> TRK)." + parser = argparse.ArgumentParser(description=DESCRIPTION) + parser.add_argument("anatomy", + help="reference anatomical image (.nii|.nii.gz.") + parser.add_argument("tractograms", metavar="tractogram", nargs="+", + help="list of tractograms (.tck).") + parser.add_argument("-f", "--force", action="store_true", + help="overwrite existing output files.") + + args = parser.parse_args() + return args, parser + + +def main(): + args, parser = parse_args() + + try: + nii = nib.load(args.anatomy) + except Exception: + parser.error("Expecting anatomical image as first agument.") + + for tractogram in args.tractograms: + tractogram_format = nib.streamlines.detect_format(tractogram) + if tractogram_format is not nib.streamlines.TckFile: + print("Skipping non TCK file: '{}'".format(tractogram)) + continue + + filename, _ = os.path.splitext(tractogram) + output_filename = filename + '.trk' + if os.path.isfile(output_filename) and not args.force: + msg = "Skipping existing file: '{}'. Use -f to overwrite." + print(msg.format(output_filename)) + continue + + # Build header using infos from the anatomical image. + header = {} + header[Field.VOXEL_TO_RASMM] = nii.affine.copy() + header[Field.VOXEL_SIZES] = nii.header.get_zooms()[:3] + header[Field.DIMENSIONS] = nii.shape[:3] + header[Field.VOXEL_ORDER] = "".join(aff2axcodes(nii.affine)) + + tck = nib.streamlines.load(tractogram) + nib.streamlines.save(tck.tractogram, output_filename, header=header) diff --git a/nibabel/cmdline/trk2tck.py b/nibabel/cmdline/trk2tck.py new file mode 100644 index 0000000000..a55f7e95af --- /dev/null +++ b/nibabel/cmdline/trk2tck.py @@ -0,0 +1,39 @@ +""" +Convert tractograms (TRK -> TCK). +""" + +import os +import argparse + +import nibabel as nib + + +def parse_args(): + DESCRIPTION = "Convert tractograms (TRK -> TCK)." + parser = argparse.ArgumentParser(description=DESCRIPTION) + parser.add_argument("tractograms", metavar="tractogram", nargs="+", + help="list of tractograms (.trk).") + parser.add_argument("-f", "--force", action="store_true", + help="overwrite existing output files.") + + args = parser.parse_args() + return args, parser + + +def main(): + args, parser = parse_args() + for tractogram in args.tractograms: + tractogram_format = nib.streamlines.detect_format(tractogram) + if tractogram_format is not nib.streamlines.TrkFile: + print("Skipping non TRK file: '{}'".format(tractogram)) + continue + + filename, _ = os.path.splitext(tractogram) + output_filename = filename + '.tck' + if os.path.isfile(output_filename) and not args.force: + msg = "Skipping existing file: '{}'. Use -f to overwrite." + print(msg.format(output_filename)) + continue + + trk = nib.streamlines.load(tractogram) + nib.streamlines.save(trk.tractogram, output_filename)