Skip to content

Commit 337bbc9

Browse files
authored
Merge pull request #3310 from mfmachado/interface/cat12
ENH: Add CAT12 interfaces
2 parents 12d06fd + 16f62fc commit 337bbc9

21 files changed

+1308
-0
lines changed

.zenodo.json

+5
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,11 @@
810810
"affiliation": "MIT, HMS",
811811
"name": "Ghosh, Satrajit",
812812
"orcid": "0000-0002-5312-6729"
813+
},
814+
{
815+
"affiliation": "CIBIT, UC",
816+
"name": "Machado, Fátima",
817+
"orcid": "0000-0001-8878-1750"
813818
}
814819
],
815820
"keywords": [

doc/interfaces.rst

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ Nipype provides interfaces for the following **third-party** tools:
5454
is an open-source software toolkit for diffusion MRI processing.
5555
* `Camino-TrackVis <api/generated/nipype.interfaces.camino2trackvis.html>`__
5656
allows interoperability between Camino and TrackVis.
57+
* `CAT12 <api/generated/nipype.interfaces.cat12.html>`__
58+
(Computational Anatomy Toolbox) extends SPM12 to provide computational
59+
anatomy.
5760
* `Connectome Mapper (CMP) <api/generated/nipype.interfaces.cmtk.html>`__
5861
implements a full processing pipeline for creating multi-variate and
5962
multi-resolution connectomes with dMRI data.

nipype/interfaces/cat12/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .preprocess import CAT12Segment
2+
from .surface import (
3+
ExtractAdditionalSurfaceParameters,
4+
ExtractROIBasedSurfaceMeasures,
5+
)

nipype/interfaces/cat12/base.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Cell:
2+
def __init__(self, arg):
3+
self.arg = arg
4+
5+
def to_string(self):
6+
if isinstance(self.arg, list):
7+
v = "\n".join([f"'{el}'" for el in self.arg])
8+
else:
9+
v = self.arg
10+
return v
11+
12+
13+
class NestedCell(Cell):
14+
def __str__(self):
15+
return "{{%s}}" % self.to_string()

nipype/interfaces/cat12/preprocess.py

+599
Large diffs are not rendered by default.

nipype/interfaces/cat12/surface.py

+276
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import os
2+
from pathlib import Path
3+
4+
from nipype.interfaces.base import File, InputMultiPath, TraitedSpec, traits, isdefined
5+
from nipype.interfaces.cat12.base import NestedCell, Cell
6+
from nipype.interfaces.spm import SPMCommand
7+
from nipype.interfaces.spm.base import SPMCommandInputSpec
8+
from nipype.utils.filemanip import split_filename
9+
10+
11+
class ExtractAdditionalSurfaceParametersInputSpec(SPMCommandInputSpec):
12+
left_central_surfaces = InputMultiPath(
13+
File(exists=True),
14+
field="data_surf",
15+
desc="Left and central surfaces files",
16+
mandatory=True,
17+
copyfile=False,
18+
)
19+
surface_files = InputMultiPath(
20+
File(exists=True), desc="All surface files", mandatory=False, copyfile=False
21+
)
22+
23+
gyrification = traits.Bool(
24+
True,
25+
field="GI",
26+
usedefault=True,
27+
desc="Extract gyrification index (GI) based on absolute mean curvature. The"
28+
" method is described in Luders et al. Neuroimage, 29:1224-1230, 2006",
29+
)
30+
gmv = traits.Bool(True, field="gmv", usedefault=True, desc="Extract volume")
31+
area = traits.Bool(True, field="area", usedefault=True, desc="Extract area surface")
32+
depth = traits.Bool(
33+
False,
34+
field="SD",
35+
usedefault=True,
36+
desc="Extract sulcus depth based on euclidian distance between the central "
37+
"surface anf its convex hull.",
38+
)
39+
fractal_dimension = traits.Bool(
40+
False,
41+
field="FD",
42+
usedefault=True,
43+
desc="Extract cortical complexity (fractal dimension) which is "
44+
"described in Yotter ar al. Neuroimage, 56(3): 961-973, 2011",
45+
)
46+
47+
48+
class ExtractAdditionalSurfaceParametersOutputSpec(TraitedSpec):
49+
lh_extracted_files = traits.List(
50+
File(exists=True), desc="Files of left Hemisphere extracted measures"
51+
)
52+
rh_extracted_files = traits.List(
53+
File(exists=True), desc="Files of right Hemisphere extracted measures"
54+
)
55+
56+
lh_gyrification = traits.List(
57+
File(exists=True), desc="Gyrification of left Hemisphere"
58+
)
59+
rh_gyrification = traits.List(
60+
File(exists=True), desc="Gyrification of right Hemisphere"
61+
)
62+
63+
lh_gmv = traits.List(
64+
File(exists=True), desc="Grey matter volume of left Hemisphere"
65+
)
66+
rh_gmv = traits.List(
67+
File(exists=True), desc="Grey matter volume of right Hemisphere"
68+
)
69+
70+
lh_area = traits.List(File(exists=True), desc="Area of left Hemisphere")
71+
rh_area = traits.List(File(exists=True), desc="Area of right Hemisphere")
72+
73+
lh_depth = traits.List(File(exists=True), desc="Depth of left Hemisphere")
74+
rh_depth = traits.List(File(exists=True), desc="Depth of right Hemisphere")
75+
76+
lh_fractaldimension = traits.List(
77+
File(exists=True), desc="Fractal Dimension of left Hemisphere"
78+
)
79+
rh_fractaldimension = traits.List(
80+
File(exists=True), desc="Fractal Dimension of right Hemisphere"
81+
)
82+
83+
84+
class ExtractAdditionalSurfaceParameters(SPMCommand):
85+
"""
86+
Additional surface parameters can be extracted that can be used for statistical analysis, such as:
87+
88+
* Central surfaces
89+
* Surface area
90+
* Surface GM volume
91+
* Gyrification Index
92+
* Sulcus depth
93+
* Toro's gyrification index
94+
* Shaer's local gyrification index
95+
* Laplacian gyrification indeces
96+
* Addicional surfaces
97+
* Measure normalization
98+
* Lazy processing
99+
100+
http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=53
101+
102+
Examples
103+
--------
104+
>>> # Set the left surface files, both will be processed
105+
>>> lh_path_central = 'lh.central.structural.gii'
106+
>>> # Put here all surface files generated by CAT12 Segment, this is only required if the this approach is putted in
107+
>>> surf_files = ['lh.sphere.reg.structural.gii', 'rh.sphere.reg.structural.gii', 'lh.sphere.structural.gii', 'rh.sphere.structural.gii', 'rh.central.structural.gii', 'lh.pbt.structural', 'rh.pbt.structural']
108+
>>> extract_additional_measures = ExtractAdditionalSurfaceParameters(left_central_surfaces=lh_path_central, surface_files=surf_files)
109+
>>> extract_additional_measures.run() # doctest: +SKIP
110+
111+
"""
112+
113+
input_spec = ExtractAdditionalSurfaceParametersInputSpec
114+
output_spec = ExtractAdditionalSurfaceParametersOutputSpec
115+
116+
def __init__(self, **inputs):
117+
_local_version = SPMCommand().version
118+
if _local_version and "12." in _local_version:
119+
self._jobtype = "tools"
120+
self._jobname = "cat.stools.surfextract"
121+
122+
super().__init__(**inputs)
123+
124+
def _list_outputs(self):
125+
outputs = self._outputs().get()
126+
127+
names_outputs = [
128+
(self.inputs.gyrification, "gyrification"),
129+
(self.inputs.gmv, "gmv"),
130+
(self.inputs.area, "area"),
131+
(self.inputs.depth, "depth"),
132+
(self.inputs.fractal_dimension, "fractaldimension"),
133+
]
134+
135+
for filename in self.inputs.left_central_surfaces:
136+
pth, base, ext = split_filename(filename)
137+
# The first part of the filename is rh.central or lh.central
138+
original_filename = base.split(".", 2)[-1]
139+
for extracted_parameter, parameter_name in names_outputs:
140+
if extracted_parameter:
141+
for hemisphere in ["rh", "lh"]:
142+
all_files_hemisphere = hemisphere + "_extracted_files"
143+
name_hemisphere = hemisphere + "_" + parameter_name
144+
if not isdefined(outputs[name_hemisphere]):
145+
outputs[name_hemisphere] = []
146+
if not isdefined(outputs[all_files_hemisphere]):
147+
outputs[all_files_hemisphere] = []
148+
generated_filename = ".".join(
149+
[hemisphere, parameter_name, original_filename]
150+
)
151+
outputs[name_hemisphere].append(
152+
os.path.join(pth, generated_filename)
153+
)
154+
155+
# Add all hemisphere files into one list, this is important because only the left hemisphere
156+
# files are used as input in the Surface ROI Tools, fpr instance.
157+
outputs[all_files_hemisphere].append(
158+
os.path.join(pth, generated_filename)
159+
)
160+
161+
return outputs
162+
163+
def _format_arg(self, opt, spec, val):
164+
if opt == "left_central_surfaces":
165+
return Cell2Str(val)
166+
return super(ExtractAdditionalSurfaceParameters, self)._format_arg(
167+
opt, spec, val
168+
)
169+
170+
171+
class ExtractROIBasedSurfaceMeasuresInputSpec(SPMCommandInputSpec):
172+
# Only these files are given as input, yet the right hemisphere (rh) files should also be on the processing
173+
# directory.
174+
175+
surface_files = InputMultiPath(
176+
File(exists=True),
177+
desc="Surface data files. This variable should be a list " "with all",
178+
mandatory=False,
179+
copyfile=False,
180+
)
181+
lh_roi_atlas = InputMultiPath(
182+
File(exists=True),
183+
field="rdata",
184+
desc="(Left) ROI Atlas. These are the ROI's ",
185+
mandatory=True,
186+
copyfile=False,
187+
)
188+
189+
rh_roi_atlas = InputMultiPath(
190+
File(exists=True),
191+
desc="(Right) ROI Atlas. These are the ROI's ",
192+
mandatory=False,
193+
copyfile=False,
194+
)
195+
196+
lh_surface_measure = InputMultiPath(
197+
File(exists=True),
198+
field="cdata",
199+
desc="(Left) Surface data files. ",
200+
mandatory=True,
201+
copyfile=False,
202+
)
203+
rh_surface_measure = InputMultiPath(
204+
File(exists=True),
205+
desc="(Right) Surface data files.",
206+
mandatory=False,
207+
copyfile=False,
208+
)
209+
210+
211+
class ExtractROIBasedSurfaceMeasuresOutputSpec(TraitedSpec):
212+
label_files = traits.List(
213+
File(exists=True), desc="Files with the measures extracted for ROIs."
214+
)
215+
216+
217+
class ExtractROIBasedSurfaceMeasures(SPMCommand):
218+
"""
219+
Extract ROI-based surface values
220+
While ROI-based values for VBM (volume) data are automatically saved in the ``label`` folder as XML file it is
221+
necessary to additionally extract these values for surface data (except for thickness which is automatically
222+
extracted during segmentation). This has to be done after preprocessing the data and creating cortical surfaces.
223+
224+
You can extract ROI-based values for cortical thickness but also for any other surface parameter that was extracted
225+
using the Extract Additional Surface Parameters such as volume, area, depth, gyrification and fractal dimension.
226+
227+
228+
http://www.neuro.uni-jena.de/cat12/CAT12-Manual.pdf#page=53
229+
230+
Examples
231+
--------
232+
>>> # Template surface files
233+
>>> lh_atlas = 'lh.aparc_a2009s.freesurfer.annot'
234+
>>> rh_atlas = 'rh.aparc_a2009s.freesurfer.annot'
235+
>>> surf_files = ['lh.sphere.reg.structural.gii', 'rh.sphere.reg.structural.gii', 'lh.sphere.structural.gii', 'rh.sphere.structural.gii', 'lh.central.structural.gii', 'rh.central.structural.gii', 'lh.pbt.structural', 'rh.pbt.structural']
236+
>>> lh_measure = 'lh.area.structural'
237+
>>> extract_additional_measures = ExtractROIBasedSurfaceMeasures(surface_files=surf_files, lh_surface_measure=lh_measure, lh_roi_atlas=lh_atlas, rh_roi_atlas=rh_atlas)
238+
>>> extract_additional_measures.run() # doctest: +SKIP
239+
240+
241+
"""
242+
243+
input_spec = ExtractROIBasedSurfaceMeasuresInputSpec
244+
output_spec = ExtractROIBasedSurfaceMeasuresOutputSpec
245+
246+
def __init__(self, **inputs):
247+
_local_version = SPMCommand().version
248+
if _local_version and "12." in _local_version:
249+
self._jobtype = "tools"
250+
self._jobname = "cat.stools.surf2roi"
251+
252+
SPMCommand.__init__(self, **inputs)
253+
254+
def _format_arg(self, opt, spec, val):
255+
if opt == "lh_surface_measure":
256+
return NestedCell(val)
257+
elif opt == "lh_roi_atlas":
258+
return Cell2Str(val)
259+
260+
return super(ExtractROIBasedSurfaceMeasures, self)._format_arg(opt, spec, val)
261+
262+
def _list_outputs(self):
263+
outputs = self._outputs().get()
264+
265+
pth, base, ext = split_filename(self.inputs.lh_surface_measure[0])
266+
267+
outputs["label_files"] = [
268+
str(label) for label in Path(pth).glob("label/*") if label.is_file()
269+
]
270+
return outputs
271+
272+
273+
class Cell2Str(Cell):
274+
def __str__(self):
275+
"""Convert input to appropriate format for cat12"""
276+
return "{%s}" % self.to_string()

nipype/interfaces/cat12/tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)