From 37c1a0e85f540d2a0585d6b9577c18ea66e09775 Mon Sep 17 00:00:00 2001 From: basile Date: Fri, 5 Aug 2022 16:48:02 -0400 Subject: [PATCH 01/21] add gradunwarp interface and workflow --- niworkflows/interfaces/gradunwarp.py | 79 ++++++++++++++++++++++++++++ niworkflows/workflows/gradunwarp.py | 76 ++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 niworkflows/interfaces/gradunwarp.py create mode 100644 niworkflows/workflows/gradunwarp.py diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py new file mode 100644 index 00000000000..15178ccc891 --- /dev/null +++ b/niworkflows/interfaces/gradunwarp.py @@ -0,0 +1,79 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +# +# Copyright 2021 The NiPreps Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# We support and encourage derived works from this project, please read +# about our expectations at +# +# https://www.nipreps.org/community/licensing/ +# +"""GradUnwarp interface.""" +import numpy as np +from nipype import logging +from nipype.utils.filemanip import fname_presuffix +from nipype.interfaces.base import ( + traits, + TraitedSpec, + BaseInterfaceInputSpec, + File, + SimpleInterface, + OutputMultiObject, + InputMultiObject, +) + + +class _GradUnwarpInputSpec(BaseInterfaceInputSpec): + infile = File(exists=True, mandatory=True, desc="input image to be corrected") + gradfile = File(exists=True, default=None, desc="gradient file") + coeffile = File(exists=True, default=None, desc="coefficients file") + outfile = File(desc="output corrected image") + vendor = traits.Enum("siemens", "ge", usedefault=True, desc="scanner vendor") + warp = traits.Bool(desc="warp a volume (as opposed to unwarping)") + nojac = traits.Bool(desc="Do not perform Jacobian intensity correction") + + fovmin = traits.Float(desc="the minimum extent of harmonics evaluation grid in meters") + fovmax = traits.Float(desc="the maximum extent of harmonics evaluation grid in meters") + order = traits.Int(min=1, max=4, usedefault=True, desc="the order of interpolation(1..4) where 1 is linear - default") + +class _GradUnwarpOutputSpec(TraitedSpec): + corrected_file = File(desc="input images corrected") + warp_file = File(desc="absolute warp file") + + +class GradUnwarp(SimpleInterface): + input_spec = _GradUnwarpInputSpec + output_spec = _GradUnwarpOutputSpec + + def _run_interface(self, runtime): + + from gradunwarp.core.gradient_unwarp import GradientUnwarpRunner + if not self.inputs.outfile: + self.inputs.outfile = fname_presuffix( + self.inputs.infile, + suffix='_gradunwarped', + newpath=runtime.cwd + ) + gur = GradientUnwarpRunner(self.inputs) + gur.run() + gur.write() + del gur + + self._results["corrected_file"] = self.inputs.outfile + self._results["warp_file"] = fname_presuffix( + "fullWarp_abs.nii.gz", + newpath=runtime.cwd + ) + return runtime diff --git a/niworkflows/workflows/gradunwarp.py b/niworkflows/workflows/gradunwarp.py new file mode 100644 index 00000000000..b792645e500 --- /dev/null +++ b/niworkflows/workflows/gradunwarp.py @@ -0,0 +1,76 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +# +# Copyright 2021 The NiPreps Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# We support and encourage derived works from this project, please read +# about our expectations at +# +# https://www.nipreps.org/community/licensing/ +# +"""Workflow for the unwarping .""" + +from nipype.pipeline import engine as pe +from nipype.interfaces import utility as niu + +from ..engine.workflows import LiterateWorkflow as Workflow + +def init_gradunwarp_wf( + name="gradunwarp_wf", +): + from nipype.interfaces.fsl import ConvertWarp + from ..interfaces.gradunwarp import GradUnwarp + + wf = Workflow(name=name) + + inputnode = pe.Node( + niu.IdentityInterface(fields=["in_file", "coeff_file", "grad_file"]), name="inputnode" + ) + outputnode = pe.Node( + niu.IdentityInterface( + fields=[ + "warp_file", + "corrected_file" + ] + ), + name="outputnode", + ) + + gradient_unwarp = pe.Node(GradUnwarp(), name="grad_unwarp") + convert_warp = pe.Node( + ConvertWarp( + abswarp=True, + out_relwarp=True, + ), + name='convert_warp_abs2rel' + ) + + # fmt:off + wf.connect([ + (inputnode, gradient_unwarp, [ + ("in_file", "infile"), + ("coeff_file", "coeffile"), + ("grad_file", "gradfile"), + ]), + (inputnode, convert_warp, [ + ("in_file", "reference"), + ]), + (gradient_unwarp, convert_warp, [ + ("warp_file", "warp1"), + ]), + ]) + # fmt:on + + return wf From 4e4d74593aa0239eea3b640dbb10a84e8f74d763 Mon Sep 17 00:00:00 2001 From: basile Date: Fri, 12 Aug 2022 09:20:35 -0400 Subject: [PATCH 02/21] finish gradunwarp wf --- niworkflows/workflows/gradunwarp.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/niworkflows/workflows/gradunwarp.py b/niworkflows/workflows/gradunwarp.py index b792645e500..1c3f51ed7fb 100644 --- a/niworkflows/workflows/gradunwarp.py +++ b/niworkflows/workflows/gradunwarp.py @@ -36,7 +36,7 @@ def init_gradunwarp_wf( wf = Workflow(name=name) inputnode = pe.Node( - niu.IdentityInterface(fields=["in_file", "coeff_file", "grad_file"]), name="inputnode" + niu.IdentityInterface(fields=["input_file", "coeff_file", "grad_file"]), name="inputnode" ) outputnode = pe.Node( niu.IdentityInterface( @@ -48,8 +48,8 @@ def init_gradunwarp_wf( name="outputnode", ) - gradient_unwarp = pe.Node(GradUnwarp(), name="grad_unwarp") - convert_warp = pe.Node( + gradient_unwarp = pe.MapNode(GradUnwarp(), name="grad_unwarp") + convert_warp = pe.MapNode( ConvertWarp( abswarp=True, out_relwarp=True, @@ -60,7 +60,7 @@ def init_gradunwarp_wf( # fmt:off wf.connect([ (inputnode, gradient_unwarp, [ - ("in_file", "infile"), + ("input_file", "infile"), ("coeff_file", "coeffile"), ("grad_file", "gradfile"), ]), @@ -70,6 +70,9 @@ def init_gradunwarp_wf( (gradient_unwarp, convert_warp, [ ("warp_file", "warp1"), ]), + (gradient_unwarp, output_node, [ + ("corrected_file", "corrected_file") + ]) ]) # fmt:on From 9855c77161e0520e32efbeae20b18c4f88be2e32 Mon Sep 17 00:00:00 2001 From: cneuromod_bot Date: Tue, 1 Aug 2023 15:54:01 -0400 Subject: [PATCH 03/21] wip: integrating gradunwarp in the pipelines --- niworkflows/interfaces/gradunwarp.py | 23 ++++--- niworkflows/testing.py | 2 + niworkflows/tests/data/gradunwarp_coeffs.grad | 65 +++++++++++++++++++ niworkflows/workflows/gradunwarp.py | 26 ++++---- niworkflows/workflows/tests/__init__.py | 0 .../workflows/tests/test_gradunwarp.py | 45 +++++++++++++ 6 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 niworkflows/tests/data/gradunwarp_coeffs.grad create mode 100644 niworkflows/workflows/tests/__init__.py create mode 100644 niworkflows/workflows/tests/test_gradunwarp.py diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 15178ccc891..546b4957340 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -21,19 +21,21 @@ # https://www.nipreps.org/community/licensing/ # """GradUnwarp interface.""" -import numpy as np -from nipype import logging from nipype.utils.filemanip import fname_presuffix from nipype.interfaces.base import ( traits, TraitedSpec, BaseInterfaceInputSpec, File, - SimpleInterface, - OutputMultiObject, - InputMultiObject, + SimpleInterface ) +try: + import gradunwarp + has_gradunwarp = True +except ImportError: + has_gradunwarp = False + class _GradUnwarpInputSpec(BaseInterfaceInputSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") @@ -46,7 +48,11 @@ class _GradUnwarpInputSpec(BaseInterfaceInputSpec): fovmin = traits.Float(desc="the minimum extent of harmonics evaluation grid in meters") fovmax = traits.Float(desc="the maximum extent of harmonics evaluation grid in meters") - order = traits.Int(min=1, max=4, usedefault=True, desc="the order of interpolation(1..4) where 1 is linear - default") + order = traits.Int( + min=1, max=4, + usedefault=True, + desc="the order of interpolation(1..4) where 1 is linear - default") + class _GradUnwarpOutputSpec(TraitedSpec): corrected_file = File(desc="input images corrected") @@ -65,7 +71,8 @@ def _run_interface(self, runtime): self.inputs.infile, suffix='_gradunwarped', newpath=runtime.cwd - ) + ) + print(self.inputs) gur = GradientUnwarpRunner(self.inputs) gur.run() gur.write() @@ -75,5 +82,5 @@ def _run_interface(self, runtime): self._results["warp_file"] = fname_presuffix( "fullWarp_abs.nii.gz", newpath=runtime.cwd - ) + ) return runtime diff --git a/niworkflows/testing.py b/niworkflows/testing.py index 837f7b5817e..37ee9b09fed 100644 --- a/niworkflows/testing.py +++ b/niworkflows/testing.py @@ -3,6 +3,7 @@ import os from pathlib import Path from nipype.interfaces import fsl, freesurfer as fs, afni +from .interfaces import gradunwarp test_data_env = os.getenv( "TEST_DATA_HOME", str(Path.home() / ".cache" / "stanford-crn") @@ -41,3 +42,4 @@ def wrapper(*args, **kwargs): has_fsl = fsl.Info.version() is not None has_freesurfer = fs.Info.version() is not None has_afni = afni.Info.version() is not None +has_gradunwarp = gradunwarp.has_gradunwarp diff --git a/niworkflows/tests/data/gradunwarp_coeffs.grad b/niworkflows/tests/data/gradunwarp_coeffs.grad new file mode 100644 index 00000000000..443644521f0 --- /dev/null +++ b/niworkflows/tests/data/gradunwarp_coeffs.grad @@ -0,0 +1,65 @@ +#*[ Script ****************************************************************\ +# +# Name : dummy.grad +# +# Date : 0970-01-01 +# +# Author : Mario Bros +# +# Language : +# +# Description : Defines Legendre coefficients in spherical harmonics for +# dummy Gradient Coil +# +#****************************************************************************/ + +#*] END: */ +dummy, dummy , Gx,y,z = 80/80 mT/m +win_low = 0, win_high = 0, win_algo = 0, win_dummy = 2; +0.25 m = R0, lnorm = 4? A(1,0) = B(1,1) = A(1,1) = 0; +0 = CoSyMode, +NO. TYPE SPECTRUM AXIS + +1 A( 3, 0) -0.0510000 z + 2 A( 5, 0) -0.11000000 z + 3 A( 7, 0) 0.04600000 z + 4 A( 9, 0) -0.00600000 z + 5 A(11, 0) -0.00400000 z + 6 A(13, 0) 0.00400000 z + 7 A(15, 0) -0.00200000 z + 8 A(17, 0) 0.00100000 z + 9 A(19, 0) -0.00000000 z +101 A( 3, 1) -0.02500000 x +102 A( 3, 3) -0.00300000 x +103 A( 5, 1) -0.09400000 x +104 A( 5, 3) -0.00300000 x +105 A( 5, 5) -0.00200000 x +106 A( 7, 1) 0.01400000 x +107 A( 7, 3) 0.00700000 x +108 A( 7, 5) -0.00100000 x +109 A( 7, 7) 0.00300000 x +110 A( 9, 1) 0.00800000 x +111 A( 9, 3) -0.00400000 x +112 A(11, 1) -0.00600000 x +113 A(11, 3) 0.00200000 x +114 A(13, 1) 0.00200000 x +115 A(13, 3) -0.00100000 x +116 A(15, 1) -0.00000000 x +117 A(15, 3) 0.00000000 x +201 B( 3, 1) -0.03000000 y +202 B( 3, 3) 0.00700000 y +203 B( 5, 1) -0.08800000 y +204 B( 5, 3) -0.00400000 y +205 B( 5, 5) -0.00100000 y +206 B( 7, 1) 0.01900000 y +207 B( 7, 3) -0.00300000 y +208 B( 7, 5) -0.00100000 y +209 B( 7, 7) 0.00100000 y +210 B( 9, 1) 0.00200000 y +211 B( 9, 3) 0.00300000 y +212 B( 9, 5) 0.00000000 y +213 B(11, 1) -0.00300000 y +214 B(11, 3) -0.00100000 y +215 B(13, 1) 0.00100000 y +216 B(13, 3) 0.00000000 y +217 B(15, 1) -0.00000000 y diff --git a/niworkflows/workflows/gradunwarp.py b/niworkflows/workflows/gradunwarp.py index 1c3f51ed7fb..ed0e12d7233 100644 --- a/niworkflows/workflows/gradunwarp.py +++ b/niworkflows/workflows/gradunwarp.py @@ -27,6 +27,7 @@ from ..engine.workflows import LiterateWorkflow as Workflow + def init_gradunwarp_wf( name="gradunwarp_wf", ): @@ -48,14 +49,20 @@ def init_gradunwarp_wf( name="outputnode", ) - gradient_unwarp = pe.MapNode(GradUnwarp(), name="grad_unwarp") + gradient_unwarp = pe.MapNode( + GradUnwarp(), + name="grad_unwarp", + iterfield=["infile"] + ) + convert_warp = pe.MapNode( ConvertWarp( abswarp=True, out_relwarp=True, ), - name='convert_warp_abs2rel' - ) + name='convert_warp_abs2rel', + iterfield=["reference", "warp1"] + ) # fmt:off wf.connect([ @@ -64,15 +71,10 @@ def init_gradunwarp_wf( ("coeff_file", "coeffile"), ("grad_file", "gradfile"), ]), - (inputnode, convert_warp, [ - ("in_file", "reference"), - ]), - (gradient_unwarp, convert_warp, [ - ("warp_file", "warp1"), - ]), - (gradient_unwarp, output_node, [ - ("corrected_file", "corrected_file") - ]) + (inputnode, convert_warp, [("input_file", "reference")]), + (gradient_unwarp, convert_warp, [("warp_file", "warp1")]), + (gradient_unwarp, outputnode, [("corrected_file", "corrected_file")]), + (convert_warp, outputnode, [("out_file", "warp_file")]) ]) # fmt:on diff --git a/niworkflows/workflows/tests/__init__.py b/niworkflows/workflows/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/niworkflows/workflows/tests/test_gradunwarp.py b/niworkflows/workflows/tests/test_gradunwarp.py new file mode 100644 index 00000000000..919b009ad29 --- /dev/null +++ b/niworkflows/workflows/tests/test_gradunwarp.py @@ -0,0 +1,45 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +# +# Copyright 2021 The NiPreps Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# We support and encourage derived works from this project, please read +# about our expectations at +# +# https://www.nipreps.org/community/licensing/ +# +"""Check the refmap module.""" +import unittest +from ..gradunwarp import init_gradunwarp_wf +from ...testing import has_gradunwarp +from niworkflows.tests.data import load_test_data + + +@unittest.skipUnless(has_gradunwarp, "Needs Gradunwarp") +def test_gradunwarp(tmpdir, ds000030_dir, workdir, outdir): + """Exercise the Gradunwarp workflow.""" + tmpdir.chdir() + + wf = init_gradunwarp_wf() + if workdir: + wf.base_dir = str(workdir) + + print(ds000030_dir) + wf.inputs.inputnode.input_file = [ + str(f) for f in (ds000030_dir / "sub-10228" / "anat").glob("*_T1w.nii.gz") + ][0] + wf.inputs.inputnode.grad_file = load_test_data('gradunwarp_coeffs.grad') + + wf.run() From 9226dbb7b6c7df4205a6061af61fe2b77134ea77 Mon Sep 17 00:00:00 2001 From: bpinsard Date: Fri, 4 Aug 2023 10:50:00 -0400 Subject: [PATCH 04/21] xform warp from fsl to itk --- niworkflows/workflows/gradunwarp.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/niworkflows/workflows/gradunwarp.py b/niworkflows/workflows/gradunwarp.py index ed0e12d7233..29e8830d342 100644 --- a/niworkflows/workflows/gradunwarp.py +++ b/niworkflows/workflows/gradunwarp.py @@ -64,6 +64,29 @@ def init_gradunwarp_wf( iterfield=["reference", "warp1"] ) + def _warp_fsl2itk(in_warp): + import os + import nitransforms.io + from nipype.utils.filemanip import fname_presuffix + fsl_warp = nitransforms.io.fsl.FSLDisplacementsField.from_filename(in_warp) + itk_warp_data = fsl_warp.get_fdata().reshape(fsl_warp.shape[:3]+(1,3)) + itk_warp_data[...,(0,1)] *= -1 + itk_warp = fsl_warp.__class__(itk_warp_data, fsl_warp.affine) + itk_warp.header.set_intent("vector") + out_fname = fname_presuffix(in_warp, suffix="_itk", newpath=os.getcwd()) + itk_warp.to_filename(out_fname) + return out_fname + + warp_fsl2itk = pe.MapNode( + niu.Function( + function=_warp_fsl2itk, + output_names=["itk_warp"], + input_names=["in_warp"] + ), + iterfield=['in_warp'], + name="warp_fsl2itk" + ) + # fmt:off wf.connect([ (inputnode, gradient_unwarp, [ @@ -74,7 +97,8 @@ def init_gradunwarp_wf( (inputnode, convert_warp, [("input_file", "reference")]), (gradient_unwarp, convert_warp, [("warp_file", "warp1")]), (gradient_unwarp, outputnode, [("corrected_file", "corrected_file")]), - (convert_warp, outputnode, [("out_file", "warp_file")]) + (convert_warp, warp_fsl2itk, [("out_file", "in_warp")]), + (warp_fsl2itk, outputnode, [("itk_warp", "warp_file")]) ]) # fmt:on From 03f7e48a9cf1a1fd462a05caf1f17e1a589c86b1 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:38:14 -0400 Subject: [PATCH 05/21] Update niworkflows/interfaces/gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/interfaces/gradunwarp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 546b4957340..425dbad5fcf 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -41,7 +41,7 @@ class _GradUnwarpInputSpec(BaseInterfaceInputSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") gradfile = File(exists=True, default=None, desc="gradient file") coeffile = File(exists=True, default=None, desc="coefficients file") - outfile = File(desc="output corrected image") + outfile = File(name_source=['in_file'], name_template=['%s_gradunwarped', keep_extension=True, desc="output corrected image") vendor = traits.Enum("siemens", "ge", usedefault=True, desc="scanner vendor") warp = traits.Bool(desc="warp a volume (as opposed to unwarping)") nojac = traits.Bool(desc="Do not perform Jacobian intensity correction") From 545c38b3d576ab94f9b6d7bcca19d26a04aaa669 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:39:33 -0400 Subject: [PATCH 06/21] Update niworkflows/interfaces/gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/interfaces/gradunwarp.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 425dbad5fcf..4b37604e02a 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -78,9 +78,7 @@ def _run_interface(self, runtime): gur.write() del gur - self._results["corrected_file"] = self.inputs.outfile - self._results["warp_file"] = fname_presuffix( - "fullWarp_abs.nii.gz", - newpath=runtime.cwd - ) + cwd = Path(runtime.cwd) + self._results["corrected_file"] = str(cwd / self.inputs.outfile) + self._results["warp_file"] = str(cwd / "fullWarp_abs.nii.gz") return runtime From 364ee76eb86d8d3f8e1318f6362d2b372a4ef203 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:39:54 -0400 Subject: [PATCH 07/21] Update niworkflows/interfaces/gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/interfaces/gradunwarp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 4b37604e02a..7744f8ff9ad 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -21,7 +21,7 @@ # https://www.nipreps.org/community/licensing/ # """GradUnwarp interface.""" -from nipype.utils.filemanip import fname_presuffix +from pathlib import Path from nipype.interfaces.base import ( traits, TraitedSpec, From 47985779947b49caac2670931060a2e752f1a757 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:40:16 -0400 Subject: [PATCH 08/21] Update niworkflows/interfaces/gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/interfaces/gradunwarp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 7744f8ff9ad..d9b5b840ff3 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -25,7 +25,6 @@ from nipype.interfaces.base import ( traits, TraitedSpec, - BaseInterfaceInputSpec, File, SimpleInterface ) From baf8e453dcf087c4baa3108a9e29b316fcec53a6 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:40:53 -0400 Subject: [PATCH 09/21] Update niworkflows/interfaces/gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/interfaces/gradunwarp.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index d9b5b840ff3..ccc7ed957ec 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -65,17 +65,9 @@ class GradUnwarp(SimpleInterface): def _run_interface(self, runtime): from gradunwarp.core.gradient_unwarp import GradientUnwarpRunner - if not self.inputs.outfile: - self.inputs.outfile = fname_presuffix( - self.inputs.infile, - suffix='_gradunwarped', - newpath=runtime.cwd - ) - print(self.inputs) gur = GradientUnwarpRunner(self.inputs) gur.run() gur.write() - del gur cwd = Path(runtime.cwd) self._results["corrected_file"] = str(cwd / self.inputs.outfile) From 8e85701ace4e6d767763fcc8b7edca8d2d759d0d Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:41:15 -0400 Subject: [PATCH 10/21] Update niworkflows/workflows/tests/test_gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/workflows/tests/test_gradunwarp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/niworkflows/workflows/tests/test_gradunwarp.py b/niworkflows/workflows/tests/test_gradunwarp.py index 919b009ad29..807cc709189 100644 --- a/niworkflows/workflows/tests/test_gradunwarp.py +++ b/niworkflows/workflows/tests/test_gradunwarp.py @@ -37,9 +37,7 @@ def test_gradunwarp(tmpdir, ds000030_dir, workdir, outdir): wf.base_dir = str(workdir) print(ds000030_dir) - wf.inputs.inputnode.input_file = [ - str(f) for f in (ds000030_dir / "sub-10228" / "anat").glob("*_T1w.nii.gz") - ][0] + wf.inputs.inputnode.input_file = str(next(ds000030_dir.glob("sub-10228/anat/*_T1w.nii.gz"))) wf.inputs.inputnode.grad_file = load_test_data('gradunwarp_coeffs.grad') wf.run() From 96e829f17e052185b3dcc36f905bf366abf5f6b2 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:41:30 -0400 Subject: [PATCH 11/21] Update niworkflows/workflows/tests/test_gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/workflows/tests/test_gradunwarp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niworkflows/workflows/tests/test_gradunwarp.py b/niworkflows/workflows/tests/test_gradunwarp.py index 807cc709189..d38556c9e91 100644 --- a/niworkflows/workflows/tests/test_gradunwarp.py +++ b/niworkflows/workflows/tests/test_gradunwarp.py @@ -38,6 +38,6 @@ def test_gradunwarp(tmpdir, ds000030_dir, workdir, outdir): print(ds000030_dir) wf.inputs.inputnode.input_file = str(next(ds000030_dir.glob("sub-10228/anat/*_T1w.nii.gz"))) - wf.inputs.inputnode.grad_file = load_test_data('gradunwarp_coeffs.grad') + wf.inputs.inputnode.grad_file = str(load_test_data('gradunwarp_coeffs.grad')) wf.run() From 29f706d8b7a5b9e3a3ff49241e11ee3fc3a97ec0 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:42:19 -0400 Subject: [PATCH 12/21] Update niworkflows/interfaces/gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/interfaces/gradunwarp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index ccc7ed957ec..f981760c9cc 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -36,7 +36,7 @@ has_gradunwarp = False -class _GradUnwarpInputSpec(BaseInterfaceInputSpec): +class _GradUnwarpInputSpec(TraitedSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") gradfile = File(exists=True, default=None, desc="gradient file") coeffile = File(exists=True, default=None, desc="coefficients file") From 2e371618bb53e226e7043549bd2c01ce4deb9033 Mon Sep 17 00:00:00 2001 From: Basile Date: Tue, 15 Aug 2023 09:42:49 -0400 Subject: [PATCH 13/21] Update niworkflows/interfaces/gradunwarp.py Co-authored-by: Chris Markiewicz --- niworkflows/interfaces/gradunwarp.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index f981760c9cc..90104f2c780 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -29,12 +29,6 @@ SimpleInterface ) -try: - import gradunwarp - has_gradunwarp = True -except ImportError: - has_gradunwarp = False - class _GradUnwarpInputSpec(TraitedSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") From 7bbf5c546f7c80e4352ff24d4848685a76c91014 Mon Sep 17 00:00:00 2001 From: bpinsard Date: Tue, 15 Aug 2023 10:14:02 -0400 Subject: [PATCH 14/21] fix typo --- niworkflows/interfaces/gradunwarp.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 90104f2c780..61652be208b 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -34,7 +34,12 @@ class _GradUnwarpInputSpec(TraitedSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") gradfile = File(exists=True, default=None, desc="gradient file") coeffile = File(exists=True, default=None, desc="coefficients file") - outfile = File(name_source=['in_file'], name_template=['%s_gradunwarped', keep_extension=True, desc="output corrected image") + outfile = File( + name_source=['in_file'], + name_template=['%s_gradunwarped'], + keep_extension=True, + desc="output corrected image" + ) vendor = traits.Enum("siemens", "ge", usedefault=True, desc="scanner vendor") warp = traits.Bool(desc="warp a volume (as opposed to unwarping)") nojac = traits.Bool(desc="Do not perform Jacobian intensity correction") From 6d9f77e08abb14f9174aeb0a9a38eb759ac30d99 Mon Sep 17 00:00:00 2001 From: bpinsard Date: Tue, 15 Aug 2023 10:17:38 -0400 Subject: [PATCH 15/21] use nitransforms output for DisplacementField --- niworkflows/workflows/gradunwarp.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/niworkflows/workflows/gradunwarp.py b/niworkflows/workflows/gradunwarp.py index 29e8830d342..d182acefdae 100644 --- a/niworkflows/workflows/gradunwarp.py +++ b/niworkflows/workflows/gradunwarp.py @@ -69,12 +69,8 @@ def _warp_fsl2itk(in_warp): import nitransforms.io from nipype.utils.filemanip import fname_presuffix fsl_warp = nitransforms.io.fsl.FSLDisplacementsField.from_filename(in_warp) - itk_warp_data = fsl_warp.get_fdata().reshape(fsl_warp.shape[:3]+(1,3)) - itk_warp_data[...,(0,1)] *= -1 - itk_warp = fsl_warp.__class__(itk_warp_data, fsl_warp.affine) - itk_warp.header.set_intent("vector") out_fname = fname_presuffix(in_warp, suffix="_itk", newpath=os.getcwd()) - itk_warp.to_filename(out_fname) + nitransforms.io.itk.ITKDisplacementsField.to_filename(fsl_warp, out_fname) return out_fname warp_fsl2itk = pe.MapNode( From 9c0c3c50b6311a63d4113d47fa6f2e7243028b6a Mon Sep 17 00:00:00 2001 From: bpinsard Date: Tue, 15 Aug 2023 10:18:59 -0400 Subject: [PATCH 16/21] add dependencies, use temporary nitransforms dev branch --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4f37a3c1118..5394af62b82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,8 @@ dependencies = [ "nibabel >= 3.0", "nilearn >= 0.5.2", "nipype >= 1.8.5", - "nitransforms >= 21.0.0", +# "nitransforms >= 21.0.0", + "nitransforms@https://github.com/bpinsard/nitransforms/archive/refs/heads/enh/output_displacement_field.zip", "numpy", "packaging", "pandas", @@ -45,6 +46,7 @@ dependencies = [ "templateflow >= 0.7.2", "traits < 6.4", "transforms3d", + "gradunwarp@https://github.com/bpinsard/gradunwarp/archive/refs/heads/master.zip", ] [project.optional-dependencies] From 6d3d3e4b685687c79f0b9debc1774d64fbbb6a33 Mon Sep 17 00:00:00 2001 From: bpinsard Date: Tue, 15 Aug 2023 12:26:04 -0400 Subject: [PATCH 17/21] fix flake8 tests --- niworkflows/interfaces/tests/test_bids.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/niworkflows/interfaces/tests/test_bids.py b/niworkflows/interfaces/tests/test_bids.py index 365a62356a8..007c943d60e 100644 --- a/niworkflows/interfaces/tests/test_bids.py +++ b/niworkflows/interfaces/tests/test_bids.py @@ -300,7 +300,11 @@ def test_DerivativesDataSink_build_path( **entities, ) +<<<<<<< HEAD if isinstance(expectation, type): +======= + if type(expectation) is type(Exception): +>>>>>>> 601d1d1f (fix flake8 tests) with pytest.raises(expectation): dds.run() return From fb04e5d26b73c25bad830ac73b4ef6589776adc1 Mon Sep 17 00:00:00 2001 From: bpinsard Date: Wed, 15 May 2024 14:05:53 -0400 Subject: [PATCH 18/21] fix rebase mess --- niworkflows/interfaces/gradunwarp.py | 11 ++++++++++- niworkflows/interfaces/tests/test_bids.py | 4 ---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 61652be208b..553c8d5e6eb 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -29,6 +29,13 @@ SimpleInterface ) +has_gradunwarp = False +try: + from gradunwarp.core.gradient_unwarp import GradientUnwarpRunner + has_gradunwarp = True +except ImportError: + pass + class _GradUnwarpInputSpec(TraitedSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") @@ -63,7 +70,9 @@ class GradUnwarp(SimpleInterface): def _run_interface(self, runtime): - from gradunwarp.core.gradient_unwarp import GradientUnwarpRunner + if not has_gradunwarp: + raise RuntimeError('missing gradunwarp dependency') + gur = GradientUnwarpRunner(self.inputs) gur.run() gur.write() diff --git a/niworkflows/interfaces/tests/test_bids.py b/niworkflows/interfaces/tests/test_bids.py index 007c943d60e..365a62356a8 100644 --- a/niworkflows/interfaces/tests/test_bids.py +++ b/niworkflows/interfaces/tests/test_bids.py @@ -300,11 +300,7 @@ def test_DerivativesDataSink_build_path( **entities, ) -<<<<<<< HEAD if isinstance(expectation, type): -======= - if type(expectation) is type(Exception): ->>>>>>> 601d1d1f (fix flake8 tests) with pytest.raises(expectation): dds.run() return From 9e3ae7e9963619bc2f1fb2feb4f4ce03519d492a Mon Sep 17 00:00:00 2001 From: bpinsard Date: Thu, 16 May 2024 16:26:15 -0400 Subject: [PATCH 19/21] fix interface --- niworkflows/interfaces/gradunwarp.py | 26 ++++++++++---------------- niworkflows/testing.py | 2 +- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 553c8d5e6eb..6bb3fd263c3 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -29,24 +29,11 @@ SimpleInterface ) -has_gradunwarp = False -try: - from gradunwarp.core.gradient_unwarp import GradientUnwarpRunner - has_gradunwarp = True -except ImportError: - pass - - class _GradUnwarpInputSpec(TraitedSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") gradfile = File(exists=True, default=None, desc="gradient file") coeffile = File(exists=True, default=None, desc="coefficients file") - outfile = File( - name_source=['in_file'], - name_template=['%s_gradunwarped'], - keep_extension=True, - desc="output corrected image" - ) + outfile = File("gradunwarped.nii.gz", mandatory=True, usedefault=True, desc="output corrected image") vendor = traits.Enum("siemens", "ge", usedefault=True, desc="scanner vendor") warp = traits.Bool(desc="warp a volume (as opposed to unwarping)") nojac = traits.Bool(desc="Do not perform Jacobian intensity correction") @@ -68,11 +55,18 @@ class GradUnwarp(SimpleInterface): input_spec = _GradUnwarpInputSpec output_spec = _GradUnwarpOutputSpec + def version(): + try: + import gradunwarp + except ImportError: + return + return gradunwarp.__version__ + def _run_interface(self, runtime): - if not has_gradunwarp: + if not GradUnwarp.version(): raise RuntimeError('missing gradunwarp dependency') - + from gradunwarp.core.gradient_unwarp import GradientUnwarpRunner gur = GradientUnwarpRunner(self.inputs) gur.run() gur.write() diff --git a/niworkflows/testing.py b/niworkflows/testing.py index 37ee9b09fed..78340b442dc 100644 --- a/niworkflows/testing.py +++ b/niworkflows/testing.py @@ -42,4 +42,4 @@ def wrapper(*args, **kwargs): has_fsl = fsl.Info.version() is not None has_freesurfer = fs.Info.version() is not None has_afni = afni.Info.version() is not None -has_gradunwarp = gradunwarp.has_gradunwarp +has_gradunwarp = gradunwarp.GradUnwarp.version() From f55b38716f998c868935fed7ee1e39ca4b7fb83a Mon Sep 17 00:00:00 2001 From: bpinsard Date: Fri, 17 May 2024 09:37:26 -0400 Subject: [PATCH 20/21] move to nitransforms master branch until release --- niworkflows/interfaces/gradunwarp.py | 8 +++++++- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/niworkflows/interfaces/gradunwarp.py b/niworkflows/interfaces/gradunwarp.py index 6bb3fd263c3..6a3df020ca9 100644 --- a/niworkflows/interfaces/gradunwarp.py +++ b/niworkflows/interfaces/gradunwarp.py @@ -29,11 +29,17 @@ SimpleInterface ) + class _GradUnwarpInputSpec(TraitedSpec): infile = File(exists=True, mandatory=True, desc="input image to be corrected") gradfile = File(exists=True, default=None, desc="gradient file") coeffile = File(exists=True, default=None, desc="coefficients file") - outfile = File("gradunwarped.nii.gz", mandatory=True, usedefault=True, desc="output corrected image") + outfile = File( + "gradunwarped.nii.gz", + mandatory=True, + usedefault=True, + desc="output corrected image" + ) vendor = traits.Enum("siemens", "ge", usedefault=True, desc="scanner vendor") warp = traits.Bool(desc="warp a volume (as opposed to unwarping)") nojac = traits.Bool(desc="Do not perform Jacobian intensity correction") diff --git a/pyproject.toml b/pyproject.toml index 5394af62b82..a7d79f6cc27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "nilearn >= 0.5.2", "nipype >= 1.8.5", # "nitransforms >= 21.0.0", - "nitransforms@https://github.com/bpinsard/nitransforms/archive/refs/heads/enh/output_displacement_field.zip", + "nitransforms@https://github.com/nipy/nitransforms/archive/refs/heads/master.zip", "numpy", "packaging", "pandas", From 2c1507a1c37e9b269542ae3c4d74d8d724cd002e Mon Sep 17 00:00:00 2001 From: bpinsard Date: Fri, 17 May 2024 10:11:59 -0400 Subject: [PATCH 21/21] install nipreps gradunwarp fork --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a7d79f6cc27..b545f642ea5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "templateflow >= 0.7.2", "traits < 6.4", "transforms3d", - "gradunwarp@https://github.com/bpinsard/gradunwarp/archive/refs/heads/master.zip", + "gradunwarp@https://github.com/nipreps/gradunwarp/archive/refs/heads/master.zip", ] [project.optional-dependencies]