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
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import nibabel as nib
|
|
3
|
+
import dipy.io.stateful_surface
|
|
4
|
+
|
|
5
|
+
from dipy.io.stateful_tractogram import StatefulTractogram
|
|
6
|
+
from dipy.io.utils import Space
|
|
7
|
+
from dipy.io.streamline import load_tractogram, save_tractogram
|
|
8
|
+
from dipy.tracking.utils import target, density_map
|
|
9
|
+
|
|
10
|
+
def get_tract(
|
|
11
|
+
tract_path,
|
|
12
|
+
reference_path = None
|
|
13
|
+
):
|
|
14
|
+
"""Load a tractogram with the possibility of assigning a new
|
|
15
|
+
NIfTI reference. The reference should be in the same world space
|
|
16
|
+
as the tractogram.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
tract_path : Path
|
|
21
|
+
Path to the tractogram file.
|
|
22
|
+
reference_path : Path | None, optional
|
|
23
|
+
Path to a NIfTI image to use as the tractogram reference.
|
|
24
|
+
If provided, the tractogram is reconstructed in RASMM space
|
|
25
|
+
using this reference.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
StatefulTractogram
|
|
30
|
+
Loaded tractogram, optionally associated with the provided
|
|
31
|
+
reference image.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
tract = load_tractogram(filename=tract_path,
|
|
35
|
+
reference="same",
|
|
36
|
+
bbox_valid_check=False)
|
|
37
|
+
if reference_path:
|
|
38
|
+
reference = nib.nifti1.load(reference_path)
|
|
39
|
+
tract = StatefulTractogram(tract.streamlines,
|
|
40
|
+
reference=reference,
|
|
41
|
+
space=Space.RASMM)
|
|
42
|
+
return tract
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_aggregated_tracts(
|
|
46
|
+
tracts_pathlist,
|
|
47
|
+
reference_path = None,
|
|
48
|
+
out_path = None
|
|
49
|
+
):
|
|
50
|
+
"""Aggregate multiple tractograms into a single tractogram.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
tracts_pathlist : list[Path]
|
|
55
|
+
Paths to the tractogram files to aggregate.
|
|
56
|
+
reference_path : Path | None, optional
|
|
57
|
+
Path to a NIfTI image to use as the reference for all tractograms.
|
|
58
|
+
If not provided, the first loaded tractogram is used as the reference.
|
|
59
|
+
out_path : Path | None, optional
|
|
60
|
+
Path where the aggregated tractogram should be saved. If None,
|
|
61
|
+
the tractogram is not saved.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
StatefulTractogram
|
|
66
|
+
Aggregated tractogram containing the streamlines from all input
|
|
67
|
+
tractograms, clipped to the reference image grid.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
streamlines = []
|
|
71
|
+
|
|
72
|
+
if reference_path:
|
|
73
|
+
reference = nib.nifti1.load(reference_path)
|
|
74
|
+
else:
|
|
75
|
+
reference = None
|
|
76
|
+
|
|
77
|
+
for tract_path in tracts_pathlist:
|
|
78
|
+
tract = get_tract(tract_path, reference)
|
|
79
|
+
streamlines.extend(tract.streamlines)
|
|
80
|
+
|
|
81
|
+
if reference is None:
|
|
82
|
+
reference = tract
|
|
83
|
+
aggregated_tracts = StatefulTractogram(streamlines,
|
|
84
|
+
reference,
|
|
85
|
+
space=Space.RASMM)
|
|
86
|
+
|
|
87
|
+
aggregated_tracts = clip_tractogram_to_grid(aggregated_tracts)
|
|
88
|
+
|
|
89
|
+
if out_path:
|
|
90
|
+
save_tractogram(aggregated_tracts,
|
|
91
|
+
out_path,
|
|
92
|
+
bbox_valid_check=False,)
|
|
93
|
+
|
|
94
|
+
return aggregated_tracts
|
|
95
|
+
|
|
96
|
+
def clip_streamline_to_grid(
|
|
97
|
+
streamline,
|
|
98
|
+
inv_affine,
|
|
99
|
+
shape
|
|
100
|
+
):
|
|
101
|
+
"""Clip a streamline to the voxel grid of a reference image.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
streamline : ndarray
|
|
106
|
+
Streamline coordinates in world space.
|
|
107
|
+
inv_affine : ndarray
|
|
108
|
+
Inverse affine transformation from world space to voxel space.
|
|
109
|
+
shape : tuple
|
|
110
|
+
Shape of the reference image grid.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
ndarray
|
|
115
|
+
Streamline containing only the points that fall within the voxel grid.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
voxel = np.column_stack((
|
|
119
|
+
streamline,
|
|
120
|
+
np.ones(len(streamline))
|
|
121
|
+
)) @ inv_affine.T
|
|
122
|
+
|
|
123
|
+
voxel = voxel[:, :3]
|
|
124
|
+
|
|
125
|
+
valid = np.all(
|
|
126
|
+
(voxel >= 0) &
|
|
127
|
+
(voxel < shape),
|
|
128
|
+
axis=1
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return streamline[valid]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def clip_tractogram_to_grid(tract):
|
|
135
|
+
"""Clip a tractogram to the voxel grid of its reference space.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
tract : StatefulTractogram
|
|
140
|
+
Tractogram to clip. Its streamlines are expected to be in the
|
|
141
|
+
same world space as its reference image.
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
StatefulTractogram
|
|
146
|
+
Tractogram containing only streamline points within the reference
|
|
147
|
+
voxel grid. Streamlines with fewer than two remaining points are
|
|
148
|
+
removed.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
inv_affine = np.linalg.inv(tract.affine)
|
|
152
|
+
shape = np.asarray(tract.space_attributes[1])
|
|
153
|
+
|
|
154
|
+
clipped_streamlines = [
|
|
155
|
+
clip_streamline_to_grid(
|
|
156
|
+
streamline,
|
|
157
|
+
inv_affine,
|
|
158
|
+
shape
|
|
159
|
+
)
|
|
160
|
+
for streamline in tract.streamlines
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
clipped_streamlines = [
|
|
164
|
+
streamline
|
|
165
|
+
for streamline in clipped_streamlines
|
|
166
|
+
if len(streamline) >= 2
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
return StatefulTractogram.from_sft(clipped_streamlines, tract)
|
|
170
|
+
|
|
171
|
+
def get_streamlines_disconnection(
|
|
172
|
+
tract,
|
|
173
|
+
lesion,
|
|
174
|
+
out_path=None
|
|
175
|
+
):
|
|
176
|
+
"""Return the streamlines intersecting a lesion and compute the proportion
|
|
177
|
+
of streamlines intersecting this lesion.
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
tract : dipy.io.stateful_tractogram.StatefulTractogram
|
|
182
|
+
Tractogram from which to identify intersecting streamlines.
|
|
183
|
+
lesion : nib.Nifti1Image
|
|
184
|
+
NIfTI lesion. Converted to binary mask where voxels with values
|
|
185
|
+
greater than zero are considered part of the lesion.
|
|
186
|
+
out_path : Path | None, optional
|
|
187
|
+
Path where the disconnected streamlines should be saved. If None,
|
|
188
|
+
the tractogram is not saved.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
dipy.io.stateful_tractogram.StatefulTractogram
|
|
193
|
+
Tractogram of the intersecting streamlines.
|
|
194
|
+
float
|
|
195
|
+
Proportion of streamlines that intersect the lesion, between 0 and 1.
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
lesion_mask = lesion.get_fdata() > 0
|
|
199
|
+
|
|
200
|
+
disconnected_streamlines = list(
|
|
201
|
+
target(tract.streamlines, lesion.affine, lesion_mask)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
disconnected_tract = StatefulTractogram.from_sft(
|
|
205
|
+
disconnected_streamlines,
|
|
206
|
+
tract
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if out_path:
|
|
210
|
+
save_tractogram(
|
|
211
|
+
disconnected_tract,
|
|
212
|
+
out_path,
|
|
213
|
+
bbox_valid_check=False
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
return disconnected_tract, len(disconnected_streamlines) / len(tract.streamlines)
|
|
217
|
+
|
|
218
|
+
def get_tdi(
|
|
219
|
+
tract,
|
|
220
|
+
out_path = None
|
|
221
|
+
):
|
|
222
|
+
"""Compute a track density image (TDI) from a tractogram.
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
tract : StatefulTractogram
|
|
227
|
+
Tractogram from which to compute the track density image.
|
|
228
|
+
out_path : Path | None, optional
|
|
229
|
+
Path where the TDI NIfTI image should be saved. If None, the image
|
|
230
|
+
is not saved.
|
|
231
|
+
|
|
232
|
+
Returns
|
|
233
|
+
-------
|
|
234
|
+
nib.Nifti1Image
|
|
235
|
+
NIfTI image containing the number of streamlines passing through
|
|
236
|
+
each voxel.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
tdi = density_map(tract.streamlines, tract.affine, tract.space_attributes[1])
|
|
240
|
+
tdi = nib.Nifti1Image(tdi.astype("uint32"), tract.affine)
|
|
241
|
+
if out_path:
|
|
242
|
+
nib.nifti1.save(tdi, out_path)
|
|
243
|
+
|
|
244
|
+
return tdi
|
pyLQT_cli.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from lesion_metrics.white_matter.metrics import get_tracts_disconnections_severity, get_tracts_disconnections_map
|
|
2
|
+
from lesion_metrics.white_matter.utils import get_streamlines_disconnection, get_aggregated_tracts, clip_tractogram_to_grid
|
|
3
|
+
from lesion_metrics.network.metrics import get_sspl_increase, get_parcel_wise_disconnection_severity
|
|
4
|
+
from lesion_metrics.gray_matter.metrics import get_parcel_damage
|
|
5
|
+
from lesion_metrics.utils.configs import atlas_cfg_read, tracts_cfg_read, lesion_cfg_read
|
|
6
|
+
from lesion_metrics.utils.downloads import default_download, create_tract_list, uncompress_trk_gz, get_ref_space_tf
|
|
7
|
+
from lesion_metrics.utils.registration import register_affine, register_syn
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import nibabel as nib
|
|
11
|
+
import shutil
|
|
12
|
+
import tempfile
|
|
13
|
+
import argparse
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_pyLQT():
|
|
17
|
+
"""Run the pyLQT lesion quantification pipeline.
|
|
18
|
+
|
|
19
|
+
Parses the command-line arguments, loads the lesion, atlas, and
|
|
20
|
+
tractography configurations, performs the required registrations,
|
|
21
|
+
and computes the different lesion quantification measures.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
parser = argparse.ArgumentParser()
|
|
25
|
+
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--lesion_cfg",
|
|
28
|
+
required=True,
|
|
29
|
+
help='[Path] Path to lesion config'
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--atlas_cfg",
|
|
34
|
+
required=True,
|
|
35
|
+
help='[Path] Path to atlas config'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"--tracts_cfg",
|
|
40
|
+
default=None,
|
|
41
|
+
help='[Path] Path to tracts config'
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"--out_folder",
|
|
46
|
+
required=True,
|
|
47
|
+
help='[Path] Output folder path'
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--registration_type",
|
|
52
|
+
choices=['affine', 'syn'],
|
|
53
|
+
default='syn',
|
|
54
|
+
help='[str, default=syn] Registration type, from lesion space and atlas space to tract space'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--spared_threshold",
|
|
59
|
+
default=0.5,
|
|
60
|
+
help='[float (0,1), default=0.5] Defines threshold for spared parcel-to-parcel connectivity'
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--delete_tmp",
|
|
65
|
+
default=False,
|
|
66
|
+
help='[bool, default=False] Delete temporary folder'
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
args = parser.parse_args()
|
|
70
|
+
|
|
71
|
+
out_folder = Path(args.out_folder)
|
|
72
|
+
out_folder.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
tmp_folder = Path(tempfile.mkdtemp(prefix="LQT_tmp_", dir=out_folder))
|
|
74
|
+
|
|
75
|
+
lesion_cfg_path = Path(args.lesion_cfg)
|
|
76
|
+
atlas_cfg_path = Path(args.atlas_cfg)
|
|
77
|
+
if not args.tracts_cfg:
|
|
78
|
+
tracts_cfg_path = default_download(out_folder)
|
|
79
|
+
else:
|
|
80
|
+
tracts_cfg_path = Path(args.tracts_cfg)
|
|
81
|
+
|
|
82
|
+
lesion_cohort_name, lesion_folder, lesion_space = lesion_cfg_read(lesion_cfg_path)
|
|
83
|
+
atlas_name, atlas, atlas_labels, atlas_space = atlas_cfg_read(atlas_cfg_path)
|
|
84
|
+
tracts_name, tracts_folder, references_folder, tracts_space = tracts_cfg_read(tracts_cfg_path)
|
|
85
|
+
|
|
86
|
+
if lesion_space.endswith(".nii.gz") or lesion_space.endswith(".nii"):
|
|
87
|
+
lesion_ref_space = nib.nifti1.load(lesion_space)
|
|
88
|
+
else: lesion_ref_space = get_ref_space_tf(lesion_space)
|
|
89
|
+
if atlas_space.endswith(".nii.gz") or atlas_space.endswith(".nii"):
|
|
90
|
+
atlas_ref_space = nib.nifti1.load(atlas_space)
|
|
91
|
+
else: atlas_ref_space = get_ref_space_tf(atlas_space)
|
|
92
|
+
if tracts_space.endswith(".nii.gz") or tracts_space.endswith(".nii"):
|
|
93
|
+
tracts_ref_space = nib.nifti1.load(tracts_space)
|
|
94
|
+
else: tracts_ref_space = get_ref_space_tf(tracts_space)
|
|
95
|
+
|
|
96
|
+
if args.registration_type == "affine":
|
|
97
|
+
registrator = register_affine
|
|
98
|
+
else:
|
|
99
|
+
registrator = register_syn
|
|
100
|
+
lesion_tracts_mapping = registrator(lesion_ref_space, tracts_ref_space)
|
|
101
|
+
atlas_tracts_mapping = registrator(atlas_ref_space, tracts_ref_space)
|
|
102
|
+
|
|
103
|
+
atlas_trans_volume = atlas_tracts_mapping.transform(atlas.get_fdata(), interpolation='nearest')
|
|
104
|
+
atlas_trans = nib.Nifti1Image(atlas_trans_volume, tracts_ref_space.affine, tracts_ref_space.header)
|
|
105
|
+
nib.nifti1.save(atlas_trans, tmp_folder / "atlas_trans.nii.gz")
|
|
106
|
+
|
|
107
|
+
spared_threshold = float(args.spared_threshold)
|
|
108
|
+
tracts_pathlist_gz, references_pathlist = create_tract_list(references_folder=references_folder,
|
|
109
|
+
tracts_folder=tracts_folder)
|
|
110
|
+
|
|
111
|
+
tracts_pathlist = []
|
|
112
|
+
for tract_path in tracts_pathlist_gz:
|
|
113
|
+
relative_path = tract_path.relative_to(tracts_folder)
|
|
114
|
+
out_path = tmp_folder / relative_path.with_suffix("")
|
|
115
|
+
|
|
116
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
117
|
+
|
|
118
|
+
tracts_pathlist.append(
|
|
119
|
+
uncompress_trk_gz(tract_path, out_path)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
aggregated_tracts_path = tmp_folder / "aggregated_tracts.trk"
|
|
123
|
+
aggregated_tracts = get_aggregated_tracts(tracts_pathlist=tracts_pathlist,
|
|
124
|
+
out_path=aggregated_tracts_path)
|
|
125
|
+
|
|
126
|
+
for lesion_path in lesion_folder.glob("*.nii*"):
|
|
127
|
+
|
|
128
|
+
lesion_name = lesion_path.name.removesuffix(".nii.gz").removesuffix(".nii")
|
|
129
|
+
out_dir = out_folder / lesion_name
|
|
130
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
131
|
+
tmp_dir = tmp_folder / lesion_name
|
|
132
|
+
tmp_dir.mkdir(parents=True, exist_ok=True)
|
|
133
|
+
|
|
134
|
+
lesion = nib.nifti1.load(lesion_path)
|
|
135
|
+
lesion_trans_volume = lesion_tracts_mapping.transform(lesion.get_fdata(), interpolation='linear')
|
|
136
|
+
lesion_trans = nib.Nifti1Image(lesion_trans_volume, tracts_ref_space.affine, tracts_ref_space.header)
|
|
137
|
+
nib.nifti1.save(lesion_trans, tmp_dir / "lesion_trans.nii.gz")
|
|
138
|
+
|
|
139
|
+
aggregated_tracts_disconnected_path = tmp_dir / f"aggregated_disconnected.trk"
|
|
140
|
+
aggregated_tracts_disconnected, _ = get_streamlines_disconnection(tract=aggregated_tracts,
|
|
141
|
+
lesion=lesion_trans,
|
|
142
|
+
out_path=aggregated_tracts_disconnected_path)
|
|
143
|
+
aggregated_tracts_disconnected = clip_tractogram_to_grid(aggregated_tracts_disconnected)
|
|
144
|
+
|
|
145
|
+
get_parcel_damage(lesion=lesion_trans,
|
|
146
|
+
atlas=atlas_trans,
|
|
147
|
+
atlas_labels=atlas_labels,
|
|
148
|
+
parcel_column="Parcel",
|
|
149
|
+
out_path=out_dir / "parcel_damage")
|
|
150
|
+
|
|
151
|
+
get_tracts_disconnections_severity(tracts_path=tracts_pathlist,
|
|
152
|
+
references_path=references_pathlist,
|
|
153
|
+
lesion=lesion_trans,
|
|
154
|
+
disc_tracts_out_path=tmp_dir / "wm_disconnections_severity",
|
|
155
|
+
disc_frame_out_path=out_dir / "wm_disconnections_severity")
|
|
156
|
+
|
|
157
|
+
get_tracts_disconnections_map(tracts=aggregated_tracts,
|
|
158
|
+
tracts_disconnected=aggregated_tracts_disconnected,
|
|
159
|
+
out_path=out_dir / "wm_disconnections_map")
|
|
160
|
+
|
|
161
|
+
structural_connectivity, _, _, per_spared_connections = (
|
|
162
|
+
get_parcel_wise_disconnection_severity(
|
|
163
|
+
aggregated_tracts=aggregated_tracts,
|
|
164
|
+
aggregated_tracts_disconnected=aggregated_tracts_disconnected,
|
|
165
|
+
atlas=atlas_trans,
|
|
166
|
+
out_path=out_dir / "parcel_wise_disconnection_severity",
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
get_sspl_increase(structural_connectivity=structural_connectivity,
|
|
171
|
+
per_spared_connections=per_spared_connections,
|
|
172
|
+
out_path=out_dir / "sspl_increase",
|
|
173
|
+
spared_threshold=spared_threshold)
|
|
174
|
+
|
|
175
|
+
if args.delete_tmp == True:
|
|
176
|
+
shutil.rmtree(tmp_folder)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
run_pyLQT()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyLQT
|
|
3
|
+
Version: 1.0.dev1
|
|
4
|
+
Summary: Python version of LQT from Griffis et al. 2021 (https://www.sciencedirect.com/science/article/pii/S2213158221000838)
|
|
5
|
+
Author-email: Antonin HUDRY <hudryantoninm@gmail.com>
|
|
6
|
+
License-Expression: GPL-3.0-only
|
|
7
|
+
Keywords: LQT,lqt,lesion,brain,
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: numpy<2.0
|
|
16
|
+
Requires-Dist: nibabel>5.0
|
|
17
|
+
Requires-Dist: pandas>3.0
|
|
18
|
+
Requires-Dist: scipy>1.15
|
|
19
|
+
Requires-Dist: requests>2.0
|
|
20
|
+
Requires-Dist: dipy>1.10
|
|
21
|
+
Requires-Dist: templateflow
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# pyLQT
|
|
25
|
+
Python library to revamp the work on LQT, since it is no longer actively maintained.
|
|
26
|
+
|
|
27
|
+
# install
|
|
28
|
+
|
|
29
|
+
Make sure to update your python environment with the `requirements.txt`: `pip install -r requirements.txt`
|
|
30
|
+
|
|
31
|
+
# Running pyLQT
|
|
32
|
+
|
|
33
|
+
The main pyLQT pipeline can be run using the `full_pipeline.py` file.
|
|
34
|
+
|
|
35
|
+
### Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python full_pipeline.py \
|
|
39
|
+
--lesion_cfg <path/to/lesion_config> \
|
|
40
|
+
--atlas_cfg <path/to/atlas_config> \
|
|
41
|
+
--tracts_cfg <path/to/tracts_config> \
|
|
42
|
+
--out_folder <path/to/output> \
|
|
43
|
+
--spared_threshold
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| Argument | Required | Default | Description |
|
|
47
|
+
| -------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
48
|
+
| `--lesion_cfg` | Yes | — | Path to the lesion configuration file (`.json`). |
|
|
49
|
+
| `--atlas_cfg` | Yes | — | Path to the atlas configuration file (`.json`). |
|
|
50
|
+
| `--tracts_cfg` | No | `None` | Path to the tracts configuration file (`.json`). |
|
|
51
|
+
| `--out_folder` | Yes | — | Path to the output folder. |
|
|
52
|
+
| `--spared_threshold` | No | `0.5` | Threshold between 0 and 1 used to define spared parcel-to-parcel connectivity. |
|
|
53
|
+
| `--delete_tmp` | No | `True` | Whether to delete the temporary files generated during processing. |
|
|
54
|
+
|
|
55
|
+
### Configuration files
|
|
56
|
+
|
|
57
|
+
The CLI version of pyLQT relies on 3 configuration files, one for the lesions, one for the atlas and one for the tracts:
|
|
58
|
+
|
|
59
|
+
- Lesions: "cohort_name": name of the cohort of lesion used mainly for the naming of subsequent files; "lesion_folder": relative folder (should be next the config file) containing all the lesion niftis. The folder is scanned integrally so that all niftis in the folder and subfolders are processed; "space": world space in which the lesion are registered, can be a nifti directly given as relative path (with extensions) or a standard space name that will be fetched via templateflow (i.e. MNI152NLin2009cAsym).
|
|
60
|
+
- Atlas: "name": name of the atlas used mainly for naming subsequent files; "atlas": relative path to nifti file corresponding to atlas; "atlas_labels": relative path to `.csv` file containing the labels of the atlas. Format should contain columns "Parcel" and "Label", corresponding to the parcel number inside the nifti and the name of the parcel respectively; "space": world space in which the lesion are registered, can be a nifti directly given as relative path (with extensions) or a standard space name that will be fetched via templateflow (i.e. MNI152NLin2009cAsym).
|
|
61
|
+
|
|
62
|
+
Note: niftis can be given as raw or gzipped niftis (`.nii` or `.nii.gz`)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
pyLQT_cli.py,sha256=FlTLPGcoq-xwPeGwVUfe_W_dg8yMpYV1X1jPBf-EefA,7937
|
|
2
|
+
lesion_metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
lesion_metrics/gray_matter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
lesion_metrics/gray_matter/metrics.py,sha256=RzUS9glbin9Xn5ZldB7fGg2XiPS1P21e_7l02ZErk-I,2617
|
|
5
|
+
lesion_metrics/gray_matter/utils.py,sha256=czxyAESP0neVCue424L0hon-5Tdbsl0pN0zJHWG5HiQ,840
|
|
6
|
+
lesion_metrics/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
lesion_metrics/network/metrics.py,sha256=Ka3G8_dIq-8H3b8mvRR96QM7p_gR2yFLKUxQOLYMWM4,8281
|
|
8
|
+
lesion_metrics/network/utils.py,sha256=fcakfKSMortNIE3TfJP8zwanzBiT1McsYnHcTgwbxE4,3283
|
|
9
|
+
lesion_metrics/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
lesion_metrics/utils/configs.py,sha256=FFur04Sh48IlxUTmz4kRkyLKWffi7bvzyw8B2GclpRU,4961
|
|
11
|
+
lesion_metrics/utils/downloads.py,sha256=yu-rcuIDcgsDa7gxoLGyIewsk2DF2WGgCYe5bCOso0M,8031
|
|
12
|
+
lesion_metrics/utils/registration.py,sha256=rB4vEMBrlshofOxaKrSqyE3Wvgq2_o3y5T3dCS_9sM0,3174
|
|
13
|
+
lesion_metrics/white_matter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
lesion_metrics/white_matter/metrics.py,sha256=sujuHry19XpVWFNo52Ntq7h3JkWEcgsZ1-rNFr7E940,6155
|
|
15
|
+
lesion_metrics/white_matter/utils.py,sha256=oo492yzxsPACMvHEyYhoYVqgVRrv8mpqP7FOf5OU_Ms,7087
|
|
16
|
+
pylqt-1.0.dev1.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
17
|
+
pylqt-1.0.dev1.dist-info/METADATA,sha256=gMSuT2w08jR1QSTH_C9TRsLVsJBEnCh98jSDH4_eCGs,3990
|
|
18
|
+
pylqt-1.0.dev1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
19
|
+
pylqt-1.0.dev1.dist-info/entry_points.txt,sha256=1mhG9fCXeDnL9VEBbsIN34GU90953e85325pG_wo6l8,46
|
|
20
|
+
pylqt-1.0.dev1.dist-info/top_level.txt,sha256=KT_xuwXScSnV5QoZAXnCzeTOq23xQ_V9ESEh4jrfxBM,25
|
|
21
|
+
pylqt-1.0.dev1.dist-info/RECORD,,
|