pyLQT 1.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lesion_metrics/__init__.py +0 -0
- lesion_metrics/gray_matter/__init__.py +0 -0
- lesion_metrics/gray_matter/metrics.py +78 -0
- lesion_metrics/gray_matter/utils.py +30 -0
- lesion_metrics/network/__init__.py +0 -0
- lesion_metrics/network/metrics.py +190 -0
- lesion_metrics/network/utils.py +104 -0
- lesion_metrics/utils/__init__.py +0 -0
- lesion_metrics/utils/configs.py +137 -0
- lesion_metrics/utils/downloads.py +250 -0
- lesion_metrics/utils/registration.py +106 -0
- lesion_metrics/white_matter/__init__.py +0 -0
- lesion_metrics/white_matter/metrics.py +160 -0
- lesion_metrics/white_matter/utils.py +244 -0
- pyLQT_cli.py +180 -0
- pylqt-1.0.dev1.dist-info/METADATA +62 -0
- pylqt-1.0.dev1.dist-info/RECORD +21 -0
- pylqt-1.0.dev1.dist-info/WHEEL +5 -0
- pylqt-1.0.dev1.dist-info/entry_points.txt +2 -0
- pylqt-1.0.dev1.dist-info/licenses/LICENSE +674 -0
- pylqt-1.0.dev1.dist-info/top_level.txt +2 -0
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import nibabel as nib
|
|
3
|
+
|
|
4
|
+
from nibabel.processing import resample_from_to
|
|
5
|
+
from .utils import compute_parcel_damage
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_parcel_damage(
|
|
9
|
+
lesion,
|
|
10
|
+
atlas,
|
|
11
|
+
atlas_labels,
|
|
12
|
+
parcel_column,
|
|
13
|
+
out_path = None
|
|
14
|
+
):
|
|
15
|
+
"""Compute lesion damage for each parcel of an atlas.
|
|
16
|
+
|
|
17
|
+
The lesion is resampled to the atlas space, and the proportion of
|
|
18
|
+
each parcel overlapping with the lesion is computed. A parcel damage
|
|
19
|
+
map is created, where each parcel voxel is assigned the damage proportion
|
|
20
|
+
of its parcel.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
lesion : nibabel.Nifti1Image
|
|
25
|
+
NIfTI lesion mask. Voxels with values greater than zero are
|
|
26
|
+
considered lesioned.
|
|
27
|
+
atlas : nibabel.Nifti1Image
|
|
28
|
+
NIfTI parcellation atlas containing integer parcel labels.
|
|
29
|
+
atlas_labels : pandas.DataFrame
|
|
30
|
+
DataFrame containing the parcel labels corresponding to the
|
|
31
|
+
values in ``parcel_column``.
|
|
32
|
+
parcel_column : str
|
|
33
|
+
Name of the column in ``atlas_labels`` containing the parcel
|
|
34
|
+
labels used in the atlas.
|
|
35
|
+
out_path : pathlib.Path | None, optional
|
|
36
|
+
Directory where the parcel damage map and damage DataFrame are
|
|
37
|
+
saved. If None, no files are saved.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
pandas.DataFrame
|
|
42
|
+
Copy of ``atlas_labels`` with an additional ``PercentDamage``
|
|
43
|
+
column containing the proportion of each parcel affected by the
|
|
44
|
+
lesion.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
lesion_resampled = resample_from_to(lesion,
|
|
48
|
+
atlas,
|
|
49
|
+
order=1)
|
|
50
|
+
|
|
51
|
+
lesion_volume = lesion_resampled.get_fdata()
|
|
52
|
+
atlas_volume = atlas.get_fdata().astype(np.int32)
|
|
53
|
+
|
|
54
|
+
parcel_damages = []
|
|
55
|
+
parcel_damage_map = np.zeros_like(atlas_volume, dtype=np.float32)
|
|
56
|
+
|
|
57
|
+
for parcel in atlas_labels[parcel_column]:
|
|
58
|
+
parcel_volume = atlas_volume == parcel
|
|
59
|
+
parcel_damage = compute_parcel_damage(parcel_volume, lesion_volume)
|
|
60
|
+
|
|
61
|
+
parcel_damages.append(parcel_damage)
|
|
62
|
+
parcel_damage_map[parcel_volume] = np.round(parcel_damage, 5)
|
|
63
|
+
|
|
64
|
+
if out_path:
|
|
65
|
+
out_path.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
|
|
67
|
+
nib.save(
|
|
68
|
+
nib.Nifti1Image(parcel_damage_map, atlas.affine, atlas.header),
|
|
69
|
+
out_path / "parcel_damage_map.nii.gz"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
damage_frame = atlas_labels.copy(deep=True)
|
|
73
|
+
damage_frame["PercentDamage"] = parcel_damages
|
|
74
|
+
if out_path:
|
|
75
|
+
damage_frame.to_csv(out_path / 'parcel_damage_frame.csv', index=False)
|
|
76
|
+
|
|
77
|
+
return damage_frame
|
|
78
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def compute_parcel_damage(
|
|
4
|
+
parcel_volume,
|
|
5
|
+
lesion_volume
|
|
6
|
+
):
|
|
7
|
+
"""Compute the percentage of overlap of a lesion on a parcel.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
parcel_volume : numpy.ndarray
|
|
12
|
+
Binary or boolean mask representing the voxels belonging to the
|
|
13
|
+
parcel.
|
|
14
|
+
lesion_volume : numpy.ndarray
|
|
15
|
+
Lesion mask. Voxels with values greater than zero are considered
|
|
16
|
+
lesioned.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
float
|
|
21
|
+
Proportion of parcel voxels overlapping with the lesion, between
|
|
22
|
+
0 and 1. Returns 0 if the parcel contains no voxels.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
parcel_size = np.count_nonzero(parcel_volume)
|
|
26
|
+
if parcel_size == 0:
|
|
27
|
+
return 0
|
|
28
|
+
overlap = np.count_nonzero(parcel_volume & (lesion_volume > 0))
|
|
29
|
+
|
|
30
|
+
return overlap / parcel_size
|
|
File without changes
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import networkx as nx
|
|
3
|
+
import pickle
|
|
4
|
+
|
|
5
|
+
from scipy.sparse import csr_matrix
|
|
6
|
+
from scipy.sparse.csgraph import shortest_path
|
|
7
|
+
from nibabel.processing import resample_from_to
|
|
8
|
+
from .utils import create_structural_connectivity
|
|
9
|
+
|
|
10
|
+
def get_parcel_wise_disconnection_severity(
|
|
11
|
+
aggregated_tracts,
|
|
12
|
+
aggregated_tracts_disconnected,
|
|
13
|
+
atlas,
|
|
14
|
+
out_path = None
|
|
15
|
+
):
|
|
16
|
+
"""Compute parcel-wise structural disconnection severity.
|
|
17
|
+
|
|
18
|
+
Structural connectivity is computed for all streamlines and for
|
|
19
|
+
disconnected streamlines separately. The disconnection severity is
|
|
20
|
+
then calculated as the proportion of connections that are disconnected
|
|
21
|
+
for each pair of atlas parcels.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
aggregated_tracts : StatefulTractogram
|
|
26
|
+
Tractogram containing all aggregated streamlines.
|
|
27
|
+
aggregated_tracts_disconnected : StatefulTractogram
|
|
28
|
+
Tractogram containing the subset of aggregated streamlines that
|
|
29
|
+
are disconnected.
|
|
30
|
+
atlas : nib.Nifti1Image
|
|
31
|
+
Parcellation atlas. The atlas is resampled to the tractogram
|
|
32
|
+
reference grid using nearest-neighbor interpolation.
|
|
33
|
+
out_path : Path | None, optional
|
|
34
|
+
Directory where the structural connectivity matrices and
|
|
35
|
+
disconnection measures are saved. If None, no files are saved.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]
|
|
40
|
+
A tuple containing:
|
|
41
|
+
|
|
42
|
+
- structural_connectivity : numpy.ndarray
|
|
43
|
+
Structural connectivity matrix computed from all streamlines.
|
|
44
|
+
- structural_connectivity_disconnected : numpy.ndarray
|
|
45
|
+
Structural connectivity matrix computed from disconnected
|
|
46
|
+
streamlines.
|
|
47
|
+
- disconnections_severity : numpy.ndarray
|
|
48
|
+
Proportion of connections that are disconnected for each
|
|
49
|
+
pair of parcels.
|
|
50
|
+
- per_spared_connections : numpy.ndarray
|
|
51
|
+
Proportion of connections that remain spared for each pair
|
|
52
|
+
of parcels.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
if out_path:
|
|
56
|
+
out_path.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
sc_path = out_path / "structural_connectivity"
|
|
58
|
+
sc_disconnected_path = out_path / "structural_connectivity_disconnected"
|
|
59
|
+
else:
|
|
60
|
+
sc_path = None
|
|
61
|
+
sc_disconnected_path = None
|
|
62
|
+
|
|
63
|
+
atlas_resampled = resample_from_to(atlas,
|
|
64
|
+
aggregated_tracts.space_attributes[1::-1],
|
|
65
|
+
order=0)
|
|
66
|
+
|
|
67
|
+
structural_connectivity = create_structural_connectivity(tract=aggregated_tracts,
|
|
68
|
+
atlas_resampled=atlas_resampled,
|
|
69
|
+
out_path=sc_path)
|
|
70
|
+
structural_connectivity_disconnected = create_structural_connectivity(tract=aggregated_tracts_disconnected,
|
|
71
|
+
atlas_resampled=atlas_resampled,
|
|
72
|
+
out_path=sc_disconnected_path)
|
|
73
|
+
|
|
74
|
+
disconnections_severity = np.divide(structural_connectivity_disconnected,
|
|
75
|
+
structural_connectivity,
|
|
76
|
+
out=np.zeros_like(structural_connectivity, dtype=float),
|
|
77
|
+
where=structural_connectivity != 0
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
per_spared_connections = np.divide(
|
|
81
|
+
structural_connectivity-structural_connectivity_disconnected,
|
|
82
|
+
structural_connectivity,
|
|
83
|
+
out=np.zeros_like(structural_connectivity, dtype=float),
|
|
84
|
+
where=structural_connectivity != 0
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if out_path:
|
|
88
|
+
np.save((out_path / "structural_connectivity.npy"), structural_connectivity)
|
|
89
|
+
np.save((out_path / "structural_connectivity_disconnected.npy"), structural_connectivity_disconnected)
|
|
90
|
+
np.save((out_path / "disconnections_severity.npy"), disconnections_severity)
|
|
91
|
+
np.save((out_path / "per_spared_connections.npy"), per_spared_connections)
|
|
92
|
+
|
|
93
|
+
return (structural_connectivity,
|
|
94
|
+
structural_connectivity_disconnected,
|
|
95
|
+
disconnections_severity,
|
|
96
|
+
per_spared_connections)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_sspl_increase(
|
|
100
|
+
structural_connectivity,
|
|
101
|
+
per_spared_connections,
|
|
102
|
+
out_path = None,
|
|
103
|
+
spared_threshold = 0.5
|
|
104
|
+
):
|
|
105
|
+
|
|
106
|
+
"""Compute the increase in shortest structural path length after damage.
|
|
107
|
+
|
|
108
|
+
The structural connectivity matrix is converted to a binary graph.
|
|
109
|
+
Connections are considered spared when their proportion of
|
|
110
|
+
remaining streamlines exceeds ``spared_threshold``. Shortest path
|
|
111
|
+
lengths are computed for the full and spared connectivity graphs,
|
|
112
|
+
and their difference is used to quantify the increase in shortest
|
|
113
|
+
structural path length.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
structural_connectivity : numpy.ndarray
|
|
118
|
+
Structural connectivity matrix representing the intact network.
|
|
119
|
+
Non-zero entries are treated as existing connections.
|
|
120
|
+
per_spared_connections : numpy.ndarray
|
|
121
|
+
Matrix containing the proportion of streamlines spared for each
|
|
122
|
+
connection.
|
|
123
|
+
out_path : Path | None, optional
|
|
124
|
+
Directory where the shortest path matrices and their differences
|
|
125
|
+
are saved. If None, no files are saved.
|
|
126
|
+
spared_threshold : float, optional
|
|
127
|
+
Minimum proportion of spared streamlines required for a connection
|
|
128
|
+
to be considered present in the spared network. Defaults to 0.5.
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]
|
|
133
|
+
A tuple containing:
|
|
134
|
+
|
|
135
|
+
- SSPL_full_connections : numpy.ndarray
|
|
136
|
+
Shortest structural path lengths in the intact connectivity
|
|
137
|
+
network.
|
|
138
|
+
- SSPL_spared_connections : numpy.ndarray
|
|
139
|
+
Shortest structural path lengths in the spared connectivity
|
|
140
|
+
network.
|
|
141
|
+
- SSPL_increase : numpy.ndarray
|
|
142
|
+
Increase in shortest structural path length between the intact
|
|
143
|
+
and spared networks.
|
|
144
|
+
- SSPL_indirect_increase : numpy.ndarray
|
|
145
|
+
SSPL increase for indirect connections only. Direct connections
|
|
146
|
+
are set to zero.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# fix inf values, maybe set them to max+1 as it is done for pathway that completely disconnects
|
|
151
|
+
|
|
152
|
+
binary_structural_connectivity = structural_connectivity > 0
|
|
153
|
+
binary_spared_connections = per_spared_connections > spared_threshold
|
|
154
|
+
|
|
155
|
+
structural_connectivity_graph = csr_matrix(binary_structural_connectivity)
|
|
156
|
+
spared_connections_graph = csr_matrix(binary_spared_connections)
|
|
157
|
+
|
|
158
|
+
# selects automatically the correct algorithm (Dijkstra, Floyd-Warshall, Bellman-Ford, Johnson).
|
|
159
|
+
# Only Dijkstra or Floyd-Warshall will be used since the graph should be only positive
|
|
160
|
+
SSPL_full_connections = shortest_path(
|
|
161
|
+
structural_connectivity_graph,
|
|
162
|
+
directed=False,
|
|
163
|
+
unweighted=True)
|
|
164
|
+
max_SSPL = np.max(SSPL_full_connections[np.isfinite(SSPL_full_connections)])
|
|
165
|
+
SSPL_full_connections[
|
|
166
|
+
np.isinf(SSPL_full_connections)
|
|
167
|
+
] = max_SSPL + 1
|
|
168
|
+
|
|
169
|
+
direct_connections = (SSPL_full_connections == 1)
|
|
170
|
+
|
|
171
|
+
SSPL_spared_connections = shortest_path(
|
|
172
|
+
spared_connections_graph,
|
|
173
|
+
directed=False,
|
|
174
|
+
unweighted=True)
|
|
175
|
+
SSPL_spared_connections[
|
|
176
|
+
np.isinf(SSPL_spared_connections)
|
|
177
|
+
] = max_SSPL + 1
|
|
178
|
+
|
|
179
|
+
SSPL_increase = SSPL_spared_connections - SSPL_full_connections
|
|
180
|
+
SSPL_indirect_increase = SSPL_increase.copy()
|
|
181
|
+
SSPL_indirect_increase[~direct_connections] = 0.0
|
|
182
|
+
|
|
183
|
+
if out_path:
|
|
184
|
+
out_path.mkdir(parents=True, exist_ok=True)
|
|
185
|
+
np.save((out_path / "SSPL_full_connections.npy"), SSPL_full_connections)
|
|
186
|
+
np.save((out_path / "SSPL_spared_connections.npy"), SSPL_spared_connections)
|
|
187
|
+
np.save((out_path / "SSPL_increase.npy"), SSPL_increase)
|
|
188
|
+
np.save((out_path / "SSPL_indirect_increase.npy"), SSPL_indirect_increase)
|
|
189
|
+
|
|
190
|
+
return SSPL_full_connections, SSPL_spared_connections, SSPL_increase, SSPL_indirect_increase
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import networkx as nx
|
|
3
|
+
import dipy.io.stateful_surface
|
|
4
|
+
|
|
5
|
+
from scipy.spatial.distance import cdist
|
|
6
|
+
from scipy import ndimage
|
|
7
|
+
from dipy.io.stateful_tractogram import StatefulTractogram
|
|
8
|
+
|
|
9
|
+
def create_structural_connectivity(
|
|
10
|
+
tract,
|
|
11
|
+
atlas_resampled,
|
|
12
|
+
threshold=5.0,
|
|
13
|
+
out_path=None
|
|
14
|
+
):
|
|
15
|
+
"""Compute a structural connectivity matrix from a tractogram and atlas.
|
|
16
|
+
|
|
17
|
+
Streamline endpoints are assigned to the first parcel crossed when
|
|
18
|
+
following the streamline from the endpoint inward. Streamlines are
|
|
19
|
+
only included if both endpoints are within ``threshold`` of a parcel.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
tract : StatefulTractogram
|
|
24
|
+
Tractogram containing the streamlines used to estimate structural
|
|
25
|
+
connectivity.
|
|
26
|
+
atlas_resampled : nibabel.Nifti1Image
|
|
27
|
+
Parcellation atlas resampled to the same space and grid as the
|
|
28
|
+
tractogram.
|
|
29
|
+
threshold : float | None, optional
|
|
30
|
+
Maximum distance along the streamline from each endpoint to the
|
|
31
|
+
first crossed parcel. If None, no distance threshold is applied.
|
|
32
|
+
out_path : pathlib.Path | None, optional
|
|
33
|
+
Path where the structural connectivity matrix should be saved
|
|
34
|
+
as a NumPy array. If None, the matrix is not saved.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
numpy.ndarray
|
|
39
|
+
Symmetric structural connectivity matrix with the background
|
|
40
|
+
label removed.
|
|
41
|
+
"""
|
|
42
|
+
atlas = atlas_resampled.get_fdata().astype(int)
|
|
43
|
+
atlas_affine_inv = np.linalg.inv(atlas_resampled.affine)
|
|
44
|
+
|
|
45
|
+
n_parcels = int(np.max(atlas))
|
|
46
|
+
|
|
47
|
+
sc = np.zeros((n_parcels, n_parcels), dtype=np.int32)
|
|
48
|
+
|
|
49
|
+
for streamline in tract.streamlines:
|
|
50
|
+
|
|
51
|
+
points = np.asarray(streamline)
|
|
52
|
+
points_h = np.column_stack((points, np.ones(len(points))))
|
|
53
|
+
|
|
54
|
+
voxel_coords = (atlas_affine_inv @ points_h.T).T[:, :3]
|
|
55
|
+
voxel_coords = np.rint(voxel_coords).astype(int)
|
|
56
|
+
|
|
57
|
+
# Keep only points inside the atlas volume
|
|
58
|
+
valid = np.all((voxel_coords >= 0) & (voxel_coords < np.array(atlas.shape)), axis=1)
|
|
59
|
+
|
|
60
|
+
parcel_labels = np.zeros(len(points), dtype=int)
|
|
61
|
+
parcel_labels[valid] = atlas[
|
|
62
|
+
voxel_coords[valid, 0],
|
|
63
|
+
voxel_coords[valid, 1],
|
|
64
|
+
voxel_coords[valid, 2]
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
endpoints = []
|
|
68
|
+
|
|
69
|
+
for indices in (range(len(points)), range(len(points) - 1, -1, -1)):
|
|
70
|
+
|
|
71
|
+
distance = 0.0
|
|
72
|
+
parcel = 0
|
|
73
|
+
previous = None
|
|
74
|
+
|
|
75
|
+
for i in indices:
|
|
76
|
+
if previous is not None:
|
|
77
|
+
distance += np.linalg.norm(
|
|
78
|
+
points[i] - points[previous]
|
|
79
|
+
)
|
|
80
|
+
if parcel_labels[i] > 0:
|
|
81
|
+
parcel = parcel_labels[i]
|
|
82
|
+
break
|
|
83
|
+
previous = i
|
|
84
|
+
endpoints.append((parcel, distance))
|
|
85
|
+
|
|
86
|
+
p1, d1 = endpoints[0]
|
|
87
|
+
p2, d2 = endpoints[1]
|
|
88
|
+
|
|
89
|
+
if p1 == 0 or p2 == 0:
|
|
90
|
+
continue
|
|
91
|
+
if threshold is not None and (d1 > threshold or d2 > threshold):
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
p1 -= 1
|
|
95
|
+
p2 -= 1
|
|
96
|
+
sc[p1, p2] += 1
|
|
97
|
+
if p1 != p2:
|
|
98
|
+
sc[p2, p1] += 1
|
|
99
|
+
|
|
100
|
+
if out_path:
|
|
101
|
+
np.save(out_path, sc)
|
|
102
|
+
|
|
103
|
+
return sc
|
|
104
|
+
|
|
File without changes
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import nibabel as nib
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
def atlas_cfg_read(atlas_cfg_path):
|
|
8
|
+
"""Read an atlas configuration file and load the associated data.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
atlas_cfg_path : Path
|
|
13
|
+
Path to the .json atlas configuration file. The configuration must
|
|
14
|
+
contain the following fields:
|
|
15
|
+
|
|
16
|
+
- 'name':
|
|
17
|
+
a string naming the atlas
|
|
18
|
+
- 'atlas':
|
|
19
|
+
path to the nifti atlas file, relative to the folder
|
|
20
|
+
containing the config file
|
|
21
|
+
- 'labels':
|
|
22
|
+
path to the csv containing the labels and their associated
|
|
23
|
+
parcel name, relative to the folder containing the config file
|
|
24
|
+
- 'space':
|
|
25
|
+
standard space name available in templateflow or path
|
|
26
|
+
to reference nifti relative to the folder containing the config file
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
tuple[str, nibabel.Nifti1Image, pandas.DataFrame, str]
|
|
31
|
+
A tuple containing:
|
|
32
|
+
|
|
33
|
+
- atlas_name : str
|
|
34
|
+
Name of the atlas.
|
|
35
|
+
- atlas : nibabel.Nifti1Image
|
|
36
|
+
Loaded atlas image.
|
|
37
|
+
- atlas_labels : pandas.DataFrame
|
|
38
|
+
DataFrame containing the atlas parcel labels.
|
|
39
|
+
- atlas_space : str
|
|
40
|
+
Space in which the atlas is defined.
|
|
41
|
+
"""
|
|
42
|
+
with atlas_cfg_path.open("r") as f:
|
|
43
|
+
atlas_cfg = json.load(f)
|
|
44
|
+
|
|
45
|
+
atlas_name = atlas_cfg.get("name")
|
|
46
|
+
atlas_path = atlas_cfg_path.parent / atlas_cfg.get("atlas")
|
|
47
|
+
atlas_labels_path = atlas_cfg_path.parent / atlas_cfg.get("labels")
|
|
48
|
+
atlas_space = atlas_cfg.get("space")
|
|
49
|
+
|
|
50
|
+
atlas = nib.nifti1.load(atlas_path)
|
|
51
|
+
atlas_labels = pd.read_csv(atlas_labels_path)
|
|
52
|
+
|
|
53
|
+
return atlas_name, atlas, atlas_labels, atlas_space
|
|
54
|
+
|
|
55
|
+
def tracts_cfg_read(tracts_cfg_path):
|
|
56
|
+
"""Read a tracts configuration file and load the associated data.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
tracts_cfg_path : Path
|
|
61
|
+
Path to the .json tracts configuration file. The configuration must
|
|
62
|
+
contain the following fields:
|
|
63
|
+
|
|
64
|
+
- 'name':
|
|
65
|
+
a string naming the tracts
|
|
66
|
+
- 'tracts_folder':
|
|
67
|
+
path to the folder containing .trk files, relative to
|
|
68
|
+
the folder containing the config file
|
|
69
|
+
- 'references_folder':
|
|
70
|
+
path to the folder containing nifti reference files
|
|
71
|
+
of the tracts, relative to the folder containing the config file
|
|
72
|
+
- 'space':
|
|
73
|
+
standard space name available in templateflow or path
|
|
74
|
+
to reference nifti relative to the folder containing the config file
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
tuple[str, pathlib.Path, pathlib.Path, str]
|
|
79
|
+
A tuple containing:
|
|
80
|
+
|
|
81
|
+
- tracts_name : str
|
|
82
|
+
Name of the tracts.
|
|
83
|
+
- tracts_folder : pathlib.Path
|
|
84
|
+
Path to folder containing .trk tract files.
|
|
85
|
+
- references_folder : pathlib.Path
|
|
86
|
+
Path to folder containing .nii tract reference files.
|
|
87
|
+
- tracts_space : str
|
|
88
|
+
Space in which the tracts are defined.
|
|
89
|
+
"""
|
|
90
|
+
with tracts_cfg_path.open("r") as f:
|
|
91
|
+
tracts_cfg = json.load(f)
|
|
92
|
+
|
|
93
|
+
tracts_name = tracts_cfg.get("name")
|
|
94
|
+
tracts_folder = Path(tracts_cfg_path.parent / tracts_cfg.get("tracts"))
|
|
95
|
+
references_folder = Path(tracts_cfg_path.parent / tracts_cfg.get("references"))
|
|
96
|
+
tracts_space = tracts_cfg.get("space")
|
|
97
|
+
|
|
98
|
+
return tracts_name, tracts_folder, references_folder, tracts_space
|
|
99
|
+
|
|
100
|
+
def lesion_cfg_read(lesion_cfg_path: Path):
|
|
101
|
+
"""Read a tracts configuration file and load the associated data.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
tracts_cfg_path : Path
|
|
106
|
+
Path to the .json tracts configuration file. The configuration must
|
|
107
|
+
contain the following fields:
|
|
108
|
+
|
|
109
|
+
- 'cohort_name':
|
|
110
|
+
a string naming the tracts
|
|
111
|
+
- 'tracts_folder':
|
|
112
|
+
path to the folder containing segemented lesion files,
|
|
113
|
+
relative to the folder containing the config file
|
|
114
|
+
- 'space':
|
|
115
|
+
standard space name available in templateflow or path
|
|
116
|
+
to reference nifti relative to the folder containing the config file
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
tuple[str, pathlib.Path, pathlib.Path, str]
|
|
121
|
+
A tuple containing:
|
|
122
|
+
|
|
123
|
+
- lesion_cohort_name : str
|
|
124
|
+
Name of the lesion cohort.
|
|
125
|
+
- lesion_folder : pathlib.Path
|
|
126
|
+
Path to folder containing nifti segmented lesion files.
|
|
127
|
+
- lesion_space : str
|
|
128
|
+
Space in which the lesions are defined.
|
|
129
|
+
"""
|
|
130
|
+
with lesion_cfg_path.open("r") as f:
|
|
131
|
+
lesion_cfg = json.load(f)
|
|
132
|
+
|
|
133
|
+
lesion_cohort_name = lesion_cfg.get("cohort_name")
|
|
134
|
+
lesion_folder = Path(lesion_cfg_path.parent / lesion_cfg.get("lesion_folder"))
|
|
135
|
+
lesion_space = lesion_cfg.get("space")
|
|
136
|
+
|
|
137
|
+
return lesion_cohort_name, lesion_folder, lesion_space
|