From c6a3b71aeb5498b72b312f507011165ef0216bef Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Sat, 18 Jun 2022 11:34:03 +0000 Subject: [PATCH 1/6] Added ApplyXFM interface and tests --- README.md | 2 +- pydra/tasks/fsl/preprocess/applyxfm.py | 75 +++++++++++++++++++ .../fsl/preprocess/tests/test_run_applyxfm.py | 37 +++++++++ .../preprocess/tests/test_spec_applyxfm.py | 32 ++++++++ pydra/tasks/fsl/tests/data/transform.mat | 0 specs/fsl_preprocess_param.yml | 20 +++++ 6 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 pydra/tasks/fsl/preprocess/applyxfm.py create mode 100644 pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py create mode 100644 pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py create mode 100644 pydra/tasks/fsl/tests/data/transform.mat diff --git a/README.md b/README.md index 77962ed..09242ce 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Below is a list of all planned interfaces, with completed interfaces checked. Th ### Preprocess - [x] ApplyWarp (`applywarp`) -- [ ] ApplyXFM (`flirt`) +- [~] ApplyXFM (`flirt`) - [x] BET (`bet`) - [x] FAST (`fast`) - [x] FIRST (`first`) diff --git a/pydra/tasks/fsl/preprocess/applyxfm.py b/pydra/tasks/fsl/preprocess/applyxfm.py new file mode 100644 index 0000000..d4d891c --- /dev/null +++ b/pydra/tasks/fsl/preprocess/applyxfm.py @@ -0,0 +1,75 @@ +from pydra.engine import specs +from pydra.tasks.fsl.preprocess.flirt import FLIRT + +input_fields = [ + ( + "in_file", + specs.File, + { + "help_string": "input file", + "argstr": "-in {in_file}", + "mandatory": True, + "position": 0, + }, + ), + ( + "reference", + specs.File, + { + "help_string": "reference file", + "argstr": "-ref {reference}", + "mandatory": True, + "position": 1, + }, + ), + ( + "apply_xfm", + bool, + True, + { + "help_string": "apply transformation supplied by in_matrix_file or uses_qform to use the affine matrix stored in the reference header", + "argstr": "-applyxfm", + } + ), + ( + "in_matrix_file", + str, + { + "help_string": "input 4x4 affine matrix", + "argstr": "-init {in_matrix_file}", + "mandatory" : True + }, + ), + ( + "out_file", + str, + { + "help_string": "registered output file", + "argstr": "-out {out_file}", + "position": 2, + "output_file_template": "{in_file}_flirt", + }, + ) +] + +ApplyXFM_input_spec = specs.SpecInfo(name="Input", fields=input_fields, bases=(specs.ShellSpec,)) + +class ApplyXFM(FLIRT): + """ + Example + ------- + >>> task = ApplyXFM() + >>> task.inputs.in_file = 'test.nii.gz' + >>> task.inputs.in_matrix_file = 'transform.mat' + >>> task.inputs.reference = 'dest.nii.gz' + >>> task.cmdline # doctest: +ELLIPSIS + 'flirt -in test.nii.gz -ref dest.nii.gz -out .../test_flirt.nii.gz -applyxfm -init transform.mat' + + # using a custom outfile + >>> task.inputs.out_file = 'custom_outfile.nii.gz' + >>> task.cmdline + 'flirt -in test.nii.gz -ref dest.nii.gz -out custom_outfile.nii.gz -applyxfm -init transform.mat' + """ + + input_spec = ApplyXFM_input_spec + diff --git a/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py b/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py new file mode 100644 index 0000000..106c968 --- /dev/null +++ b/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py @@ -0,0 +1,37 @@ +import os, pytest +from pathlib import Path +from pydra.tasks.fsl.preprocess.applyxfm import ApplyXFM + + +@pytest.mark.xfail("FSLDIR" not in os.environ, reason="no FSL found", raises=FileNotFoundError) +@pytest.mark.parametrize("inputs, outputs", []) +def test_ApplyXFM(test_data, inputs, outputs): + in_file = Path(test_data) / "test.nii.gz" + if inputs is None: + inputs = {} + for key, val in inputs.items(): + try: + inputs[key] = eval(val) + except: + pass + task = ApplyXFM(in_file=in_file, **inputs) + assert set(task.generated_output_names) == set(["return_code", "stdout", "stderr"] + outputs) + res = task() + print("RESULT: ", res) + for out_nm in outputs: + assert getattr(res.output, out_nm).exists() + + +@pytest.mark.parametrize("inputs, error", [(None, "AttributeError")]) +def test_ApplyXFM_exception(test_data, inputs, error): + in_file = Path(test_data) / "test.nii.gz" + if inputs is None: + inputs = {} + for key, val in inputs.items(): + try: + inputs[key] = eval(val) + except: + pass + task = ApplyXFM(in_file=in_file, **inputs) + with pytest.raises(eval(error)): + task.generated_output_names diff --git a/pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py b/pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py new file mode 100644 index 0000000..0d48c15 --- /dev/null +++ b/pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py @@ -0,0 +1,32 @@ +import os, pytest +from pathlib import Path +from pydra.tasks.fsl.preprocess.applyxfm import ApplyXFM + + +@pytest.mark.parametrize("inputs, outputs", []) +def test_ApplyXFM(test_data, inputs, outputs): + in_file = Path(test_data) / "test.nii.gz" + if inputs is None: + inputs = {} + for key, val in inputs.items(): + try: + inputs[key] = eval(val) + except: + pass + task = ApplyXFM(in_file=in_file, **inputs) + assert set(task.generated_output_names) == set(["return_code", "stdout", "stderr"] + outputs) + + +@pytest.mark.parametrize("inputs, error", [(None, "AttributeError")]) +def test_ApplyXFM_exception(test_data, inputs, error): + in_file = Path(test_data) / "test.nii.gz" + if inputs is None: + inputs = {} + for key, val in inputs.items(): + try: + inputs[key] = eval(val) + except: + pass + task = ApplyXFM(in_file=in_file, **inputs) + with pytest.raises(eval(error)): + task.generated_output_names diff --git a/pydra/tasks/fsl/tests/data/transform.mat b/pydra/tasks/fsl/tests/data/transform.mat new file mode 100644 index 0000000..e69de29 diff --git a/specs/fsl_preprocess_param.yml b/specs/fsl_preprocess_param.yml index 458fc42..0d955a7 100644 --- a/specs/fsl_preprocess_param.yml +++ b/specs/fsl_preprocess_param.yml @@ -163,6 +163,26 @@ ApplyWarp: - out_file +ApplyXFM: + output_requirements: + out_file: [in_file, reference, in_matrix_file] + + output_templates: + out_file: "{in_file}_flirt" + + tests_inputs: + - + - + in_file: True + reference: True + in_matrix_file: True + + tests_outputs: + - AttributeError + - out_file + + + SliceTimer: output_requirements: slice_time_corrected_file: [out_file] From dbb94f7764ac4214a4e6f3b2d6aad45830d2b7be Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Sat, 18 Jun 2022 12:08:38 +0000 Subject: [PATCH 2/6] Added ApplyXFM and removed unnecessary spec file --- README.md | 2 +- .../preprocess/tests/test_spec_applyxfm.py | 32 ------------------- 2 files changed, 1 insertion(+), 33 deletions(-) delete mode 100644 pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py diff --git a/README.md b/README.md index 09242ce..0eec56d 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Below is a list of all planned interfaces, with completed interfaces checked. Th ### Preprocess - [x] ApplyWarp (`applywarp`) -- [~] ApplyXFM (`flirt`) +- [X] ApplyXFM (`flirt`) - [x] BET (`bet`) - [x] FAST (`fast`) - [x] FIRST (`first`) diff --git a/pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py b/pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py deleted file mode 100644 index 0d48c15..0000000 --- a/pydra/tasks/fsl/preprocess/tests/test_spec_applyxfm.py +++ /dev/null @@ -1,32 +0,0 @@ -import os, pytest -from pathlib import Path -from pydra.tasks.fsl.preprocess.applyxfm import ApplyXFM - - -@pytest.mark.parametrize("inputs, outputs", []) -def test_ApplyXFM(test_data, inputs, outputs): - in_file = Path(test_data) / "test.nii.gz" - if inputs is None: - inputs = {} - for key, val in inputs.items(): - try: - inputs[key] = eval(val) - except: - pass - task = ApplyXFM(in_file=in_file, **inputs) - assert set(task.generated_output_names) == set(["return_code", "stdout", "stderr"] + outputs) - - -@pytest.mark.parametrize("inputs, error", [(None, "AttributeError")]) -def test_ApplyXFM_exception(test_data, inputs, error): - in_file = Path(test_data) / "test.nii.gz" - if inputs is None: - inputs = {} - for key, val in inputs.items(): - try: - inputs[key] = eval(val) - except: - pass - task = ApplyXFM(in_file=in_file, **inputs) - with pytest.raises(eval(error)): - task.generated_output_names From 42d64ce86e9ca95965e7c0891fe7445bb72490b1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 18 Jun 2022 12:12:28 +0000 Subject: [PATCH 3/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pydra/tasks/fsl/preprocess/applyxfm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pydra/tasks/fsl/preprocess/applyxfm.py b/pydra/tasks/fsl/preprocess/applyxfm.py index d4d891c..747ccc4 100644 --- a/pydra/tasks/fsl/preprocess/applyxfm.py +++ b/pydra/tasks/fsl/preprocess/applyxfm.py @@ -29,15 +29,15 @@ { "help_string": "apply transformation supplied by in_matrix_file or uses_qform to use the affine matrix stored in the reference header", "argstr": "-applyxfm", - } + }, ), ( "in_matrix_file", str, - { + { "help_string": "input 4x4 affine matrix", "argstr": "-init {in_matrix_file}", - "mandatory" : True + "mandatory": True, }, ), ( @@ -49,11 +49,12 @@ "position": 2, "output_file_template": "{in_file}_flirt", }, - ) + ), ] ApplyXFM_input_spec = specs.SpecInfo(name="Input", fields=input_fields, bases=(specs.ShellSpec,)) + class ApplyXFM(FLIRT): """ Example @@ -72,4 +73,3 @@ class ApplyXFM(FLIRT): """ input_spec = ApplyXFM_input_spec - From ec425b5d635f15771b28707be8e28b146928ceac Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Sat, 18 Jun 2022 15:22:42 +0000 Subject: [PATCH 4/6] Fixed tests for ApplyXFM --- pydra/tasks/fsl/preprocess/applyxfm.py | 2 +- .../fsl/preprocess/tests/test_run_applyxfm.py | 16 ++++++++++----- pydra/tasks/fsl/tests/data/transform.mat | 4 ++++ specs/fsl_preprocess_param.yml | 20 ------------------- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/pydra/tasks/fsl/preprocess/applyxfm.py b/pydra/tasks/fsl/preprocess/applyxfm.py index d4d891c..ebcb3c1 100644 --- a/pydra/tasks/fsl/preprocess/applyxfm.py +++ b/pydra/tasks/fsl/preprocess/applyxfm.py @@ -28,7 +28,7 @@ True, { "help_string": "apply transformation supplied by in_matrix_file or uses_qform to use the affine matrix stored in the reference header", - "argstr": "-applyxfm", + "argstr": "-applyxfm" } ), ( diff --git a/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py b/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py index 106c968..9a648f5 100644 --- a/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py +++ b/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py @@ -1,12 +1,19 @@ import os, pytest from pathlib import Path -from pydra.tasks.fsl.preprocess.applyxfm import ApplyXFM +from ..applyxfm import ApplyXFM @pytest.mark.xfail("FSLDIR" not in os.environ, reason="no FSL found", raises=FileNotFoundError) -@pytest.mark.parametrize("inputs, outputs", []) +@pytest.mark.parametrize( + "inputs, outputs", + [ + (None, ["out_file"]) + ] +) def test_ApplyXFM(test_data, inputs, outputs): in_file = Path(test_data) / "test.nii.gz" + reference = Path(test_data) / "test.nii.gz" + in_matrix_file = Path(test_data) / "transform.mat" if inputs is None: inputs = {} for key, val in inputs.items(): @@ -14,7 +21,7 @@ def test_ApplyXFM(test_data, inputs, outputs): inputs[key] = eval(val) except: pass - task = ApplyXFM(in_file=in_file, **inputs) + task = ApplyXFM(in_file=in_file, reference=reference, in_matrix_file=in_matrix_file, **inputs) assert set(task.generated_output_names) == set(["return_code", "stdout", "stderr"] + outputs) res = task() print("RESULT: ", res) @@ -24,7 +31,6 @@ def test_ApplyXFM(test_data, inputs, outputs): @pytest.mark.parametrize("inputs, error", [(None, "AttributeError")]) def test_ApplyXFM_exception(test_data, inputs, error): - in_file = Path(test_data) / "test.nii.gz" if inputs is None: inputs = {} for key, val in inputs.items(): @@ -32,6 +38,6 @@ def test_ApplyXFM_exception(test_data, inputs, error): inputs[key] = eval(val) except: pass - task = ApplyXFM(in_file=in_file, **inputs) + task = ApplyXFM(**inputs) with pytest.raises(eval(error)): task.generated_output_names diff --git a/pydra/tasks/fsl/tests/data/transform.mat b/pydra/tasks/fsl/tests/data/transform.mat index e69de29..7983453 100644 --- a/pydra/tasks/fsl/tests/data/transform.mat +++ b/pydra/tasks/fsl/tests/data/transform.mat @@ -0,0 +1,4 @@ +1 0.0001371992403 0 -0.01404274377 +0 1 0.0003819660051 -0.04808253666 +0 0 1 0 +0 0 0 1 diff --git a/specs/fsl_preprocess_param.yml b/specs/fsl_preprocess_param.yml index 0d955a7..458fc42 100644 --- a/specs/fsl_preprocess_param.yml +++ b/specs/fsl_preprocess_param.yml @@ -163,26 +163,6 @@ ApplyWarp: - out_file -ApplyXFM: - output_requirements: - out_file: [in_file, reference, in_matrix_file] - - output_templates: - out_file: "{in_file}_flirt" - - tests_inputs: - - - - - in_file: True - reference: True - in_matrix_file: True - - tests_outputs: - - AttributeError - - out_file - - - SliceTimer: output_requirements: slice_time_corrected_file: [out_file] From 2a0731fb1d951a0a3a381de4da6ddaf5e8c58e6a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 18 Jun 2022 15:25:32 +0000 Subject: [PATCH 5/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pydra/tasks/fsl/preprocess/applyxfm.py | 4 ++-- pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py | 7 +------ pydra/tasks/fsl/tests/data/transform.mat | 8 ++++---- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/pydra/tasks/fsl/preprocess/applyxfm.py b/pydra/tasks/fsl/preprocess/applyxfm.py index 24fd615..747ccc4 100644 --- a/pydra/tasks/fsl/preprocess/applyxfm.py +++ b/pydra/tasks/fsl/preprocess/applyxfm.py @@ -28,8 +28,8 @@ True, { "help_string": "apply transformation supplied by in_matrix_file or uses_qform to use the affine matrix stored in the reference header", - "argstr": "-applyxfm" - } + "argstr": "-applyxfm", + }, ), ( "in_matrix_file", diff --git a/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py b/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py index 9a648f5..f4d41b6 100644 --- a/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py +++ b/pydra/tasks/fsl/preprocess/tests/test_run_applyxfm.py @@ -4,12 +4,7 @@ @pytest.mark.xfail("FSLDIR" not in os.environ, reason="no FSL found", raises=FileNotFoundError) -@pytest.mark.parametrize( - "inputs, outputs", - [ - (None, ["out_file"]) - ] -) +@pytest.mark.parametrize("inputs, outputs", [(None, ["out_file"])]) def test_ApplyXFM(test_data, inputs, outputs): in_file = Path(test_data) / "test.nii.gz" reference = Path(test_data) / "test.nii.gz" diff --git a/pydra/tasks/fsl/tests/data/transform.mat b/pydra/tasks/fsl/tests/data/transform.mat index 7983453..7b34788 100644 --- a/pydra/tasks/fsl/tests/data/transform.mat +++ b/pydra/tasks/fsl/tests/data/transform.mat @@ -1,4 +1,4 @@ -1 0.0001371992403 0 -0.01404274377 -0 1 0.0003819660051 -0.04808253666 -0 0 1 0 -0 0 0 1 +1 0.0001371992403 0 -0.01404274377 +0 1 0.0003819660051 -0.04808253666 +0 0 1 0 +0 0 0 1 From 4cdd1ec4d2f0555b6b8ee872cf79a77c9408f34e Mon Sep 17 00:00:00 2001 From: Ashley Stewart Date: Sat, 18 Jun 2022 15:31:43 +0000 Subject: [PATCH 6/6] import fix --- pydra/tasks/fsl/preprocess/applyxfm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydra/tasks/fsl/preprocess/applyxfm.py b/pydra/tasks/fsl/preprocess/applyxfm.py index 747ccc4..2aaf749 100644 --- a/pydra/tasks/fsl/preprocess/applyxfm.py +++ b/pydra/tasks/fsl/preprocess/applyxfm.py @@ -1,5 +1,5 @@ from pydra.engine import specs -from pydra.tasks.fsl.preprocess.flirt import FLIRT +from .flirt import FLIRT input_fields = [ (