-
Notifications
You must be signed in to change notification settings - Fork 532
ENH: Add CAT12 interfaces #3310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…cat12 # Conflicts: # nipype/interfaces/cat12/surface.py
Codecov Report
@@ Coverage Diff @@
## master #3310 +/- ##
==========================================
+ Coverage 65.01% 65.05% +0.03%
==========================================
Files 302 306 +4
Lines 40035 40279 +244
Branches 5291 5320 +29
==========================================
+ Hits 26029 26202 +173
- Misses 12935 13006 +71
Partials 1071 1071
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a look through preprocess.py, but haven't done the other yet. Don't forget to make specs
.
import traits | ||
from nipype.interfaces.base import InputMultiPath, TraitedSpec, isdefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use nipype.interfaces.base
to override some aspects of traits. I would suggest the following:
import traits | |
from nipype.interfaces.base import InputMultiPath, TraitedSpec, isdefined | |
from nipype.interfaces.base import InputMultiPath, TraitedSpec, isdefined, traits, File, Str |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @effigies sorry for the late reply, I only had the time to correct you reviews today. I do not known what you mean by make specs...
from traits.trait_types import Int, File | ||
from traits.trait_types import List |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from traits.trait_types import Int, File | |
from traits.trait_types import List |
""" | ||
CAT12: Segmentation | ||
This toolbox is an extension to the default segmentation in SPM12, but uses a completely different segmentation | ||
approach. | ||
The segmentation approach is based on an Adaptive Maximum A Posterior (MAP) technique without the need for a priori | ||
information about tissue probabilities. That is, the Tissue Probability Maps (TPM) are not used constantly in the | ||
sense of the classical Unified Segmentation approach (Ashburner et. al. 2005), but just for spatial normalization. | ||
The following AMAP estimation is adaptive in the sense that local variations of the parameters (i.e., means and | ||
variance) are modeled as slowly varying spatial functions (Rajapakse et al. 1997). This not only accounts for | ||
intensity inhomogeneities but also for other local variations of intensity. | ||
Additionally, the segmentation approach uses a Partial Volume Estimation (PVE) with a simplified mixed model of at | ||
most two tissue types (Tohka et al. 2004). We start with an initial segmentation into three pure classes: gray | ||
matter (GM), white matter (WM), and cerebrospinal fluid (CSF) based on the above described AMAP estimation. The | ||
initial segmentation is followed by a PVE of two additional mixed classes: GM-WM and GM-CSF. This results in an | ||
estimation of the amount (or fraction) of each pure tissue type present in every voxel (as single voxels - given by | ||
Another important extension to the SPM12 segmentation is the integration of the Dartel or Geodesic Shooting | ||
registration into the toolbox by an already existing Dartel/Shooting template in MNI space. This template was | ||
derived from 555 healthy control subjects of the IXI-database (http://www.brain-development.org) and provides the | ||
several Dartel or Shooting iterations. Thus, for the majority of studies the creation of sample-specific templates | ||
is not necessary anymore and is mainly recommended for children data.'}; | ||
|
||
http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=15 | ||
|
||
Examples | ||
-------- | ||
>>> path_mr = 'structural.nii' | ||
>>> cat = CAT12Segment(in_files=path_mr) | ||
>>> cat.run() # doctest: +SKIP | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This docstring should go on CAT12Segment
, not CAT12SegmentInputSpec
.
help_tpm = 'Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability ' \ | ||
'classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain soft ' \ | ||
'tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation.' | ||
tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc=help_tpm, mandatory=False, | ||
copyfile=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For variables that are not traits, we use _
as a prefix to mark them as private. This will help declutter tab-completion in IPython or similar environments.
help_tpm = 'Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability ' \ | |
'classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain soft ' \ | |
'tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation.' | |
tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc=help_tpm, mandatory=False, | |
copyfile=False) | |
_help_tpm = 'Tissue Probability Map. Select the tissue probability image that includes 6 tissue probability ' \ | |
'classes for (1) grey matter, (2) white matter, (3) cerebrospinal fluid, (4) bone, (5) non-brain soft ' \ | |
'tissue, and (6) the background. CAT uses the TPM only for the initial SPM segmentation.' | |
tpm = InputMultiPath(ImageFileSPM(exists=True), field="tpm", desc=_help_tpm, mandatory=False, | |
copyfile=False) |
And so on for other traits.
n_jobs = traits.trait_types.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") | ||
use_prior = traits.trait_types.Str(field="useprior", usedefault=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on changed imports:
n_jobs = traits.trait_types.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") | |
use_prior = traits.trait_types.Str(field="useprior", usedefault=True) | |
n_jobs = traits.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") | |
use_prior = Str(field="useprior", usedefault=True) |
And can you add a description to use_prior
?
else: | ||
outputs[outfield] = fname_presuffix(f, prefix=prefix, suffix="_rigid") | ||
|
||
if isdefined(self.inputs.save_bias_corrected) and self.inputs.save_bias_corrected: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if isdefined(self.inputs.save_bias_corrected) and self.inputs.save_bias_corrected: | |
if self.inputs.save_bias_corrected: |
outputs["surface_files"] = [os.path.join(os.path.join(pth, "surf"), f) for f in | ||
os.listdir(os.path.join(pth, "surf")) | ||
if os.path.isfile(os.path.join(os.path.join(pth, "surf"), f))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outputs["surface_files"] = [os.path.join(os.path.join(pth, "surf"), f) for f in | |
os.listdir(os.path.join(pth, "surf")) | |
if os.path.isfile(os.path.join(os.path.join(pth, "surf"), f))] | |
outputs["surface_files"] = [str(surf) for surf in Path(pth).glob("surf/*") if surf.is_file()] |
for tidx, hemisphere in enumerate(["rh", "lh"]): | ||
for idx, suffix in enumerate(["central", "sphere"]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused indices.
for tidx, hemisphere in enumerate(["rh", "lh"]): | |
for idx, suffix in enumerate(["central", "sphere"]): | |
for hemisphere in ("rh", "lh"): | |
for suffix in ("central", "sphere"): |
outputs["report_files"] = [os.path.join(os.path.join(pth, "report"), f) for f in | ||
os.listdir(os.path.join(pth, "report")) | ||
if os.path.isfile(os.path.join(os.path.join(pth, "report"), f))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outputs["report_files"] = [os.path.join(os.path.join(pth, "report"), f) for f in | |
os.listdir(os.path.join(pth, "report")) | |
if os.path.isfile(os.path.join(os.path.join(pth, "report"), f))] | |
outputs["report_files"] = [str(report) for report in Path(pth).glob("report/*") if report.is_file()] |
outputs["label_files"] = [os.path.join(os.path.join(pth, "label"), f) for f in | ||
os.listdir(os.path.join(pth, "label")) | ||
if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outputs["label_files"] = [os.path.join(os.path.join(pth, "label"), f) for f in | |
os.listdir(os.path.join(pth, "label")) | |
if os.path.isfile(os.path.join(os.path.join(pth, "label"), f))] | |
outputs["label_files"] = [str(label) for label in Path(pth).glob("label/*") if label.is_file()] |
Update nipype/interfaces/cat12/surface.py
copyfile=False) | ||
|
||
n_jobs = traits.Int(1, usedefault=True, mandatory=True, field="nproc", desc="Number of threads") | ||
use_prior = Str(field="useprior", usedefault=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing descriptions for:
- use_prior
- surface_measures
- neuromorphometrics
- lpba40
- cobra
- hammers
- save_bias_corrected
Unclear descriptions:
- own_atlas
- gm_output_*
- wm_output_*
- csf_output_*
- label_* / output_labelnative
- las_*
There's a Makefile with a Lines 71 to 73 in 4e8f54d
If you run |
@mfmachado I went ahead and merged |
37facad
to
8a44b46
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically good to go. Some small comments, only.
|
||
def _format_arg(self, opt, spec, val): | ||
"""Convert input to appropriate format for spm""" | ||
if opt in ["in_files"]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if opt in ["in_files"]: | |
if opt == "in_files": |
for idx, (image, prefix) in enumerate( | ||
[("modulated", "mw"), ("dartel", "r"), ("native", "")] | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
idx
is unused.
for idx, (image, prefix) in enumerate( | |
[("modulated", "mw"), ("dartel", "r"), ("native", "")] | |
): | |
for image, prefix in (("modulated", "mw"), ("dartel", "r"), ("native", "")): |
if isdefined(getattr(self.inputs, outtype)) and getattr( | ||
self.inputs, outtype | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undefined
is treated as False
, so this can be simplified:
if isdefined(getattr(self.inputs, outtype)) and getattr( | |
self.inputs, outtype | |
): | |
if getattr(self.inputs, outtype): |
outputs[f"report"] = fname_presuffix( | ||
f, prefix=os.path.join("report", f"cat_"), suffix=".xml", use_ext=False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing to interpolate with f-strings.
outputs[f"report"] = fname_presuffix( | |
f, prefix=os.path.join("report", f"cat_"), suffix=".xml", use_ext=False | |
outputs["report"] = fname_presuffix( | |
f, prefix=os.path.join("report", "cat_"), suffix=".xml", use_ext=False |
] | ||
|
||
outputs["label_rois"] = fname_presuffix( | ||
f, prefix=os.path.join("label", f"catROIs_"), suffix=".xml", use_ext=False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f, prefix=os.path.join("label", f"catROIs_"), suffix=".xml", use_ext=False | |
f, prefix=os.path.join("label", "catROIs_"), suffix=".xml", use_ext=False |
f, prefix=os.path.join("label", f"catROIs_"), suffix=".xml", use_ext=False | ||
) | ||
outputs["label_roi"] = fname_presuffix( | ||
f, prefix=os.path.join("label", f"catROI_"), suffix=".xml", use_ext=False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f, prefix=os.path.join("label", f"catROI_"), suffix=".xml", use_ext=False | |
f, prefix=os.path.join("label", "catROI_"), suffix=".xml", use_ext=False |
_dartel_help = ( | ||
"This option is to export data into a form that can be used with DARTEL. The SPM default is to " | ||
"only apply rigid body transformation. However, a more appropriate option is to apply affine " | ||
"transformation, because the additional scaling of the images requires less deformations to " | ||
"non-linearly register brains to the template." | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused.
_dartel_help = ( | |
"This option is to export data into a form that can be used with DARTEL. The SPM default is to " | |
"only apply rigid body transformation. However, a more appropriate option is to apply affine " | |
"transformation, because the additional scaling of the images requires less deformations to " | |
"non-linearly register brains to the template." | |
) |
nipype/interfaces/cat12/surface.py
Outdated
pth, base, ext = split_filename(filename) | ||
# The first part of the filename is rh.central or lh.central | ||
original_filename = base.split(".", 2)[-1] | ||
for i, (extracted_parameter, parameter_name) in enumerate(names_outputs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused index i
.
for i, (extracted_parameter, parameter_name) in enumerate(names_outputs): | |
for extracted_parameter, parameter_name in names_outputs: |
nipype/interfaces/cat12/surface.py
Outdated
from traits.trait_base import _Undefined | ||
|
||
from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have the isdefined
function.
from traits.trait_base import _Undefined | |
from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits | |
from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits, isdefined |
nipype/interfaces/cat12/surface.py
Outdated
name_hemisphere = hemisphere + "_" + parameter_name | ||
if isinstance(outputs[name_hemisphere], _Undefined): | ||
outputs[name_hemisphere] = [] | ||
if isinstance(outputs[all_files_hemisphere], _Undefined): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if isinstance(outputs[all_files_hemisphere], _Undefined): | |
if not isdefined(outputs[all_files_hemisphere]): |
@mfmachado I'm going to try to make a release in the next couple days. If you want to try to finish this off, I'd love to include it! |
Hi @effigies, I have been super busy in the last couple of weeks, when are you going to do the release? I can try to do it this weekend. |
Happy to wait on this PR if you expect to get it in soon. If you're very busy, we can also release and wait on this until you have more time. |
Improved code.
Hi @effigies thanks for you input :) |
Hi @effigies all testes passed :D |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I did a few minor cleanups just to polish this off, rather than put you through another round of review. If you want to have a quick look, let me know if there's anything you disagree with. Otherwise, I'll plan to merge tomorrow.
hi, yes I agree with your changes :) |
Summary
Fixes #2783 .
List of changes proposed in this PR (pull-request)
Acknowledgment