Skip to content

Commit f109163

Browse files
committed
Added interfaces to extract measures from the surface using CAT12.
1 parent eb80b27 commit f109163

File tree

1 file changed

+234
-0
lines changed

1 file changed

+234
-0
lines changed

nipype/interfaces/cat12/surface.py

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

0 commit comments

Comments
 (0)