From 68d67a4aeb6fa3a7ed1c22a75f7b6687b8d99ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 11:21:51 +0100 Subject: [PATCH 01/11] Added robex segment interface --- nipype/interfaces/robex/__init__.py | 0 nipype/interfaces/robex/preprocess.py | 70 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 nipype/interfaces/robex/__init__.py create mode 100644 nipype/interfaces/robex/preprocess.py diff --git a/nipype/interfaces/robex/__init__.py b/nipype/interfaces/robex/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py new file mode 100644 index 0000000000..d947bc9da0 --- /dev/null +++ b/nipype/interfaces/robex/preprocess.py @@ -0,0 +1,70 @@ +import os +from pathlib import Path + +from nipype.interfaces.base import ( + TraitedSpec, + CommandLineInputSpec, + CommandLine, + File, + traits, isdefined +) +from nipype.utils.filemanip import split_filename + + +class RobexInputSpec(CommandLineInputSpec): + in_file = File(desc="Input volume", exists=True, + mandatory=True, position=0, argstr="%s") + out_file = File(desc="Output volume", position=1, argstr="%s", + hash_files=False, name_template='%s_brain', keep_extension=True) + out_mask = File(desc="Output mask", position=2, argstr="%s", + hash_files=False, name_template='%s_brainmask', keep_extension=True) + seed = traits.Int(desc="Seed for random number generator", position=3, argstr="%i") + + +class RobexOutputSpec(TraitedSpec): + out_file = File(desc="Output volume", exists=True) + out_mask = File(desc="Output mask", exists=True) + + +class RobexSegment(CommandLine): + """ + + ROBEX is an automatic whole-brain extraction tool for T1-weighted MRI data (commonly known as skull stripping). + ROBEX aims for robust skull-stripping across datasets with no parameter settings. It fits a triangular mesh, + constrained by a shape model, to the probabilistic output of a supervised brain boundary classifier. + Because the shape model cannot perfectly accommodate unseen cases, a small free deformation is subsequently allowed. + The deformation is optimized using graph cuts. + The method ROBEX is based on was published in IEEE Transactions on Medical Imaging; + please visit the website http://www.jeiglesias.com to download the paper. + + Examples + -------- + >>> path_mr = 'structural.nii' + >>> robex = RobexSegment(in_file=path_mr) + >>> robex.run() # doctest: +SKIP + + """ + + input_spec = RobexInputSpec + output_spec = RobexOutputSpec + _cmd = 'runROBEX.sh' + + def _format_arg(self, name, trait_spec, value): + if name == "out_file" or name == "out_mask": + if Path(value).name == value: + value = os.path.join(os.getcwd(), Path(value).name) + setattr(self.inputs, name, value) + return super(RobexSegment, self)._format_arg(name, trait_spec, value) + + def _list_outputs(self): + outputs = self._outputs().get() + outputs["out_file"] = self.inputs.out_file + if isdefined(self.inputs.out_mask): + outputs["out_mask"] = self.inputs.out_mask + return outputs + + def run(self, cwd=None, ignore_exception=None, **inputs): + if not isdefined(self.inputs.out_file): + _, base, extension = split_filename(self.inputs.in_file) + self.inputs.out_file = base + "_brain_robex_" + extension + return super(RobexSegment, self).run(cwd, ignore_exception, **inputs) From 7c211caedd0afb506204d372bcd784bea101629d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 11:28:22 +0100 Subject: [PATCH 02/11] Added ROBEX test. Updated name on contributions. --- .zenodo.json | 2 +- .../robex/tests/test_auto_RobexSegment.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 nipype/interfaces/robex/tests/test_auto_RobexSegment.py diff --git a/.zenodo.json b/.zenodo.json index 19909579b2..44159ebd58 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -263,7 +263,7 @@ }, { "affiliation": "CIBIT, UC", - "name": "Machado, F\u00e1tima", + "name": "Dias, Maria de Fatima", "orcid": "0000-0001-8878-1750" }, { diff --git a/nipype/interfaces/robex/tests/test_auto_RobexSegment.py b/nipype/interfaces/robex/tests/test_auto_RobexSegment.py new file mode 100644 index 0000000000..89a8b928d7 --- /dev/null +++ b/nipype/interfaces/robex/tests/test_auto_RobexSegment.py @@ -0,0 +1,61 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..preprocess import RobexSegment + + +def test_RobexSegment_inputs(): + input_map = dict( + args=dict( + argstr="%s", + ), + environ=dict( + nohash=True, + usedefault=True, + ), + in_file=dict( + argstr="%s", + extensions=None, + mandatory=True, + position=0, + ), + out_file=dict( + argstr="%s", + extensions=None, + hash_files=False, + keep_extension=True, + name_template="%s_brain", + position=1, + ), + out_mask=dict( + argstr="%s", + extensions=None, + hash_files=False, + keep_extension=True, + name_template="%s_brainmask", + position=2, + ), + seed=dict( + argstr="%i", + position=3, + ), + ) + inputs = RobexSegment.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_RobexSegment_outputs(): + output_map = dict( + out_file=dict( + extensions=None, + ), + out_mask=dict( + extensions=None, + ), + ) + outputs = RobexSegment.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value From b45a2206e563bf8843fc0aabaa261095b347465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 12:28:22 +0100 Subject: [PATCH 03/11] Formatted code. --- nipype/interfaces/robex/preprocess.py | 30 +++++++++++++++++------ nipype/interfaces/robex/tests/__init__.py | 0 2 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 nipype/interfaces/robex/tests/__init__.py diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index d947bc9da0..7422e79770 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -6,18 +6,32 @@ CommandLineInputSpec, CommandLine, File, - traits, isdefined + traits, + isdefined, ) from nipype.utils.filemanip import split_filename class RobexInputSpec(CommandLineInputSpec): - in_file = File(desc="Input volume", exists=True, - mandatory=True, position=0, argstr="%s") - out_file = File(desc="Output volume", position=1, argstr="%s", - hash_files=False, name_template='%s_brain', keep_extension=True) - out_mask = File(desc="Output mask", position=2, argstr="%s", - hash_files=False, name_template='%s_brainmask', keep_extension=True) + in_file = File( + desc="Input volume", exists=True, mandatory=True, position=0, argstr="%s" + ) + out_file = File( + desc="Output volume", + position=1, + argstr="%s", + hash_files=False, + name_template='%s_brain', + keep_extension=True, + ) + out_mask = File( + desc="Output mask", + position=2, + argstr="%s", + hash_files=False, + name_template='%s_brainmask', + keep_extension=True, + ) seed = traits.Int(desc="Seed for random number generator", position=3, argstr="%i") @@ -37,7 +51,7 @@ class RobexSegment(CommandLine): The method ROBEX is based on was published in IEEE Transactions on Medical Imaging; please visit the website http://www.jeiglesias.com to download the paper. - Examples + Examples -------- >>> path_mr = 'structural.nii' >>> robex = RobexSegment(in_file=path_mr) diff --git a/nipype/interfaces/robex/tests/__init__.py b/nipype/interfaces/robex/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 92b0d930ec7acecd796d6b94a11969cce0b04a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 12:49:17 +0100 Subject: [PATCH 04/11] Minor fix. --- nipype/interfaces/robex/preprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index 7422e79770..19b3884ebd 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -80,5 +80,5 @@ def _list_outputs(self): def run(self, cwd=None, ignore_exception=None, **inputs): if not isdefined(self.inputs.out_file): _, base, extension = split_filename(self.inputs.in_file) - self.inputs.out_file = base + "_brain_robex_" + extension + self.inputs.out_file = base + "_brain_robex" + extension return super(RobexSegment, self).run(cwd, ignore_exception, **inputs) From 35ccaa921c20feb4c8e5e981d12cd2b57927820f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 16:02:53 +0100 Subject: [PATCH 05/11] Update nipype/interfaces/robex/preprocess.py Co-authored-by: Chris Markiewicz --- nipype/interfaces/robex/preprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index 19b3884ebd..07a9fad595 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -36,8 +36,8 @@ class RobexInputSpec(CommandLineInputSpec): class RobexOutputSpec(TraitedSpec): - out_file = File(desc="Output volume", exists=True) - out_mask = File(desc="Output mask", exists=True) + out_file = File(desc="Output volume") + out_mask = File(desc="Output mask") class RobexSegment(CommandLine): From 90e6b19784c4ecd55b055f74ab7c2b5890b1fe18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 16:02:59 +0100 Subject: [PATCH 06/11] Update nipype/interfaces/robex/preprocess.py Co-authored-by: Chris Markiewicz --- nipype/interfaces/robex/preprocess.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index 07a9fad595..800f4680a6 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -54,7 +54,8 @@ class RobexSegment(CommandLine): Examples -------- >>> path_mr = 'structural.nii' - >>> robex = RobexSegment(in_file=path_mr) + >>> robex.cmdline + 'runROBEX.sh structural.nii structural_brain.nii structural_mask.nii' >>> robex.run() # doctest: +SKIP """ From e44e2d2f1a32af3855a930e3ee84ca0ceea9da4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 16:07:36 +0100 Subject: [PATCH 07/11] Improved example ROBEX. --- .mailmap | 1 + nipype/interfaces/robex/preprocess.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index 0509365e65..99dc087a60 100644 --- a/.mailmap +++ b/.mailmap @@ -121,6 +121,7 @@ Kshitij Chawla Leonie Lampe Lukas Snoek Marcel Falkiewicz +Maria de Fatima Dias Martin Perez-Guevara Mathias Goncalves Mathias Goncalves diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index 800f4680a6..c37963822b 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -53,7 +53,8 @@ class RobexSegment(CommandLine): Examples -------- - >>> path_mr = 'structural.nii' + >>> path_mr, path_out, path_mask = 'structural.nii', 'structural_brain.nii', 'structural_mask.nii' + >>> robex = RobexSegment(in_file=path_mr, out_file=path_out, out_mask=path_mask) >>> robex.cmdline 'runROBEX.sh structural.nii structural_brain.nii structural_mask.nii' >>> robex.run() # doctest: +SKIP From 868d8c76c759713ab299076d1ffa82cdcf982665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 16:24:10 +0100 Subject: [PATCH 08/11] Improved example ROBEX. --- nipype/interfaces/robex/preprocess.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index c37963822b..8e268fbc10 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -53,9 +53,12 @@ class RobexSegment(CommandLine): Examples -------- - >>> path_mr, path_out, path_mask = 'structural.nii', 'structural_brain.nii', 'structural_mask.nii' - >>> robex = RobexSegment(in_file=path_mr, out_file=path_out, out_mask=path_mask) - >>> robex.cmdline + >>> from nipype.interfaces.robex.preprocess import RobexSegment + >>> robex = RobexSegment() + >>> robex.inputs.in_file = 'structural.nii' + >>> robex.inputs.out_file = 'structural_brain.nii' + >>> robex.inputs.out_mask = 'structural_mask.nii' + >>> robex.cmdline # doctest: +SKIP 'runROBEX.sh structural.nii structural_brain.nii structural_mask.nii' >>> robex.run() # doctest: +SKIP From e0eddc8e966c06559d62163944a90ad58cdf5605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 17:46:23 +0100 Subject: [PATCH 09/11] Update nipype/interfaces/robex/preprocess.py Co-authored-by: Chris Markiewicz --- nipype/interfaces/robex/preprocess.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index 8e268fbc10..dcfba319e8 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -22,6 +22,7 @@ class RobexInputSpec(CommandLineInputSpec): argstr="%s", hash_files=False, name_template='%s_brain', + name_source=["in_file"], keep_extension=True, ) out_mask = File( @@ -30,6 +31,7 @@ class RobexInputSpec(CommandLineInputSpec): argstr="%s", hash_files=False, name_template='%s_brainmask', + name_source=["in_file"], keep_extension=True, ) seed = traits.Int(desc="Seed for random number generator", position=3, argstr="%i") From 8f29d8b7ec6ed67caa999c7814c21d3d84d840eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1tima=20Machado?= Date: Fri, 1 Apr 2022 17:51:35 +0100 Subject: [PATCH 10/11] Replaced Robex test. --- nipype/interfaces/robex/preprocess.py | 20 ------------------- .../robex/tests/test_auto_RobexSegment.py | 2 ++ 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index dcfba319e8..32bf4e9b63 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -69,23 +69,3 @@ class RobexSegment(CommandLine): input_spec = RobexInputSpec output_spec = RobexOutputSpec _cmd = 'runROBEX.sh' - - def _format_arg(self, name, trait_spec, value): - if name == "out_file" or name == "out_mask": - if Path(value).name == value: - value = os.path.join(os.getcwd(), Path(value).name) - setattr(self.inputs, name, value) - return super(RobexSegment, self)._format_arg(name, trait_spec, value) - - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = self.inputs.out_file - if isdefined(self.inputs.out_mask): - outputs["out_mask"] = self.inputs.out_mask - return outputs - - def run(self, cwd=None, ignore_exception=None, **inputs): - if not isdefined(self.inputs.out_file): - _, base, extension = split_filename(self.inputs.in_file) - self.inputs.out_file = base + "_brain_robex" + extension - return super(RobexSegment, self).run(cwd, ignore_exception, **inputs) diff --git a/nipype/interfaces/robex/tests/test_auto_RobexSegment.py b/nipype/interfaces/robex/tests/test_auto_RobexSegment.py index 89a8b928d7..caccd469e3 100644 --- a/nipype/interfaces/robex/tests/test_auto_RobexSegment.py +++ b/nipype/interfaces/robex/tests/test_auto_RobexSegment.py @@ -22,6 +22,7 @@ def test_RobexSegment_inputs(): extensions=None, hash_files=False, keep_extension=True, + name_source=["in_file"], name_template="%s_brain", position=1, ), @@ -30,6 +31,7 @@ def test_RobexSegment_inputs(): extensions=None, hash_files=False, keep_extension=True, + name_source=["in_file"], name_template="%s_brainmask", position=2, ), From a62dd8cceb3521b2321dd8d8f210530cf33c7af4 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Sat, 2 Apr 2022 12:12:26 -0400 Subject: [PATCH 11/11] Update nipype/interfaces/robex/preprocess.py --- nipype/interfaces/robex/preprocess.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nipype/interfaces/robex/preprocess.py b/nipype/interfaces/robex/preprocess.py index 32bf4e9b63..85660f8211 100644 --- a/nipype/interfaces/robex/preprocess.py +++ b/nipype/interfaces/robex/preprocess.py @@ -58,10 +58,8 @@ class RobexSegment(CommandLine): >>> from nipype.interfaces.robex.preprocess import RobexSegment >>> robex = RobexSegment() >>> robex.inputs.in_file = 'structural.nii' - >>> robex.inputs.out_file = 'structural_brain.nii' - >>> robex.inputs.out_mask = 'structural_mask.nii' - >>> robex.cmdline # doctest: +SKIP - 'runROBEX.sh structural.nii structural_brain.nii structural_mask.nii' + >>> robex.cmdline + 'runROBEX.sh structural.nii structural_brain.nii structural_brainmask.nii' >>> robex.run() # doctest: +SKIP """