junifer 0.0.3.dev186__py3-none-any.whl → 0.0.4__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.
- junifer/_version.py +14 -2
- junifer/api/cli.py +162 -17
- junifer/api/functions.py +87 -419
- junifer/api/parser.py +24 -0
- junifer/api/queue_context/__init__.py +8 -0
- junifer/api/queue_context/gnu_parallel_local_adapter.py +258 -0
- junifer/api/queue_context/htcondor_adapter.py +365 -0
- junifer/api/queue_context/queue_context_adapter.py +60 -0
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +192 -0
- junifer/api/queue_context/tests/test_htcondor_adapter.py +257 -0
- junifer/api/res/afni/run_afni_docker.sh +6 -6
- junifer/api/res/ants/ResampleImage +3 -0
- junifer/api/res/ants/antsApplyTransforms +3 -0
- junifer/api/res/ants/antsApplyTransformsToPoints +3 -0
- junifer/api/res/ants/run_ants_docker.sh +39 -0
- junifer/api/res/fsl/applywarp +3 -0
- junifer/api/res/fsl/flirt +3 -0
- junifer/api/res/fsl/img2imgcoord +3 -0
- junifer/api/res/fsl/run_fsl_docker.sh +39 -0
- junifer/api/res/fsl/std2imgcoord +3 -0
- junifer/api/res/run_conda.sh +4 -4
- junifer/api/res/run_venv.sh +22 -0
- junifer/api/tests/data/partly_cloudy_agg_mean_tian.yml +16 -0
- junifer/api/tests/test_api_utils.py +21 -3
- junifer/api/tests/test_cli.py +232 -9
- junifer/api/tests/test_functions.py +211 -439
- junifer/api/tests/test_parser.py +1 -1
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +8 -8
- junifer/configs/juseless/datagrabbers/ucla.py +44 -26
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +6 -1
- junifer/data/VOIs/meta/AutobiographicalMemory_VOIs.txt +23 -0
- junifer/data/VOIs/meta/Power2013_MNI_VOIs.tsv +264 -0
- junifer/data/__init__.py +4 -0
- junifer/data/coordinates.py +298 -31
- junifer/data/masks.py +360 -28
- junifer/data/parcellations.py +621 -188
- junifer/data/template_spaces.py +190 -0
- junifer/data/tests/test_coordinates.py +34 -3
- junifer/data/tests/test_data_utils.py +1 -0
- junifer/data/tests/test_masks.py +202 -86
- junifer/data/tests/test_parcellations.py +266 -55
- junifer/data/tests/test_template_spaces.py +104 -0
- junifer/data/utils.py +4 -2
- junifer/datagrabber/__init__.py +1 -0
- junifer/datagrabber/aomic/id1000.py +111 -70
- junifer/datagrabber/aomic/piop1.py +116 -53
- junifer/datagrabber/aomic/piop2.py +116 -53
- junifer/datagrabber/aomic/tests/test_id1000.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop1.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop2.py +27 -27
- junifer/datagrabber/base.py +62 -10
- junifer/datagrabber/datalad_base.py +0 -2
- junifer/datagrabber/dmcc13_benchmark.py +372 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +5 -0
- junifer/datagrabber/hcp1200/hcp1200.py +30 -13
- junifer/datagrabber/pattern.py +133 -27
- junifer/datagrabber/pattern_datalad.py +111 -13
- junifer/datagrabber/tests/test_base.py +57 -6
- junifer/datagrabber/tests/test_datagrabber_utils.py +204 -76
- junifer/datagrabber/tests/test_datalad_base.py +0 -6
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +256 -0
- junifer/datagrabber/tests/test_multiple.py +43 -10
- junifer/datagrabber/tests/test_pattern.py +125 -178
- junifer/datagrabber/tests/test_pattern_datalad.py +44 -25
- junifer/datagrabber/utils.py +151 -16
- junifer/datareader/default.py +36 -10
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +6 -0
- junifer/markers/base.py +25 -16
- junifer/markers/collection.py +35 -16
- junifer/markers/complexity/__init__.py +27 -0
- junifer/markers/complexity/complexity_base.py +149 -0
- junifer/markers/complexity/hurst_exponent.py +136 -0
- junifer/markers/complexity/multiscale_entropy_auc.py +140 -0
- junifer/markers/complexity/perm_entropy.py +132 -0
- junifer/markers/complexity/range_entropy.py +136 -0
- junifer/markers/complexity/range_entropy_auc.py +145 -0
- junifer/markers/complexity/sample_entropy.py +134 -0
- junifer/markers/complexity/tests/test_complexity_base.py +19 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +69 -0
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +68 -0
- junifer/markers/complexity/tests/test_perm_entropy.py +68 -0
- junifer/markers/complexity/tests/test_range_entropy.py +69 -0
- junifer/markers/complexity/tests/test_range_entropy_auc.py +69 -0
- junifer/markers/complexity/tests/test_sample_entropy.py +68 -0
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +68 -0
- junifer/markers/complexity/weighted_perm_entropy.py +133 -0
- junifer/markers/falff/_afni_falff.py +153 -0
- junifer/markers/falff/_junifer_falff.py +142 -0
- junifer/markers/falff/falff_base.py +91 -84
- junifer/markers/falff/falff_parcels.py +61 -45
- junifer/markers/falff/falff_spheres.py +64 -48
- junifer/markers/falff/tests/test_falff_parcels.py +89 -121
- junifer/markers/falff/tests/test_falff_spheres.py +92 -127
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +1 -0
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +1 -0
- junifer/markers/functional_connectivity/functional_connectivity_base.py +1 -0
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +46 -44
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +34 -39
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +40 -52
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +62 -70
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +99 -85
- junifer/markers/parcel_aggregation.py +60 -38
- junifer/markers/reho/_afni_reho.py +192 -0
- junifer/markers/reho/_junifer_reho.py +281 -0
- junifer/markers/reho/reho_base.py +69 -34
- junifer/markers/reho/reho_parcels.py +26 -16
- junifer/markers/reho/reho_spheres.py +23 -9
- junifer/markers/reho/tests/test_reho_parcels.py +93 -92
- junifer/markers/reho/tests/test_reho_spheres.py +88 -86
- junifer/markers/sphere_aggregation.py +54 -9
- junifer/markers/temporal_snr/temporal_snr_base.py +1 -0
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +38 -37
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +34 -38
- junifer/markers/tests/test_collection.py +43 -42
- junifer/markers/tests/test_ets_rss.py +29 -37
- junifer/markers/tests/test_parcel_aggregation.py +587 -468
- junifer/markers/tests/test_sphere_aggregation.py +209 -157
- junifer/markers/utils.py +2 -40
- junifer/onthefly/read_transform.py +13 -6
- junifer/pipeline/__init__.py +1 -0
- junifer/pipeline/pipeline_step_mixin.py +105 -41
- junifer/pipeline/registry.py +17 -0
- junifer/pipeline/singleton.py +45 -0
- junifer/pipeline/tests/test_pipeline_step_mixin.py +139 -51
- junifer/pipeline/tests/test_update_meta_mixin.py +1 -0
- junifer/pipeline/tests/test_workdir_manager.py +104 -0
- junifer/pipeline/update_meta_mixin.py +8 -2
- junifer/pipeline/utils.py +154 -15
- junifer/pipeline/workdir_manager.py +246 -0
- junifer/preprocess/__init__.py +3 -0
- junifer/preprocess/ants/__init__.py +4 -0
- junifer/preprocess/ants/ants_apply_transforms_warper.py +185 -0
- junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +56 -0
- junifer/preprocess/base.py +96 -69
- junifer/preprocess/bold_warper.py +265 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +91 -134
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +106 -111
- junifer/preprocess/fsl/__init__.py +4 -0
- junifer/preprocess/fsl/apply_warper.py +179 -0
- junifer/preprocess/fsl/tests/test_apply_warper.py +45 -0
- junifer/preprocess/tests/test_bold_warper.py +159 -0
- junifer/preprocess/tests/test_preprocess_base.py +6 -6
- junifer/preprocess/warping/__init__.py +6 -0
- junifer/preprocess/warping/_ants_warper.py +167 -0
- junifer/preprocess/warping/_fsl_warper.py +109 -0
- junifer/preprocess/warping/space_warper.py +213 -0
- junifer/preprocess/warping/tests/test_space_warper.py +198 -0
- junifer/stats.py +18 -4
- junifer/storage/base.py +9 -1
- junifer/storage/hdf5.py +8 -3
- junifer/storage/pandas_base.py +2 -1
- junifer/storage/sqlite.py +1 -0
- junifer/storage/tests/test_hdf5.py +2 -1
- junifer/storage/tests/test_sqlite.py +8 -8
- junifer/storage/tests/test_utils.py +6 -6
- junifer/storage/utils.py +1 -0
- junifer/testing/datagrabbers.py +11 -7
- junifer/testing/utils.py +1 -0
- junifer/tests/test_stats.py +2 -0
- junifer/utils/__init__.py +1 -0
- junifer/utils/helpers.py +53 -0
- junifer/utils/logging.py +14 -3
- junifer/utils/tests/test_helpers.py +35 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/METADATA +59 -28
- junifer-0.0.4.dist-info/RECORD +257 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/WHEEL +1 -1
- junifer/markers/falff/falff_estimator.py +0 -334
- junifer/markers/falff/tests/test_falff_estimator.py +0 -238
- junifer/markers/reho/reho_estimator.py +0 -515
- junifer/markers/reho/tests/test_reho_estimator.py +0 -260
- junifer-0.0.3.dev186.dist-info/RECORD +0 -199
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/top_level.txt +0 -0
junifer/data/parcellations.py
CHANGED
@@ -9,18 +9,20 @@ import io
|
|
9
9
|
import shutil
|
10
10
|
import tarfile
|
11
11
|
import tempfile
|
12
|
+
import typing
|
12
13
|
import zipfile
|
13
14
|
from pathlib import Path
|
14
15
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
15
16
|
|
17
|
+
import httpx
|
16
18
|
import nibabel as nib
|
17
19
|
import numpy as np
|
18
20
|
import pandas as pd
|
19
|
-
import requests
|
20
21
|
from nilearn import datasets, image
|
21
|
-
from requests.exceptions import ConnectionError, HTTPError, ReadTimeout
|
22
22
|
|
23
|
-
from ..
|
23
|
+
from ..pipeline import WorkDirManager
|
24
|
+
from ..utils import logger, raise_error, run_ext_cmd, warn_with_log
|
25
|
+
from .template_spaces import get_template, get_xfm
|
24
26
|
from .utils import closest_resolution
|
25
27
|
|
26
28
|
|
@@ -33,6 +35,7 @@ if TYPE_CHECKING:
|
|
33
35
|
|
34
36
|
# Each entry is a dictionary that must contain at least the following keys:
|
35
37
|
# * 'family': the parcellation's family name (e.g. 'Schaefer', 'SUIT')
|
38
|
+
# * 'space': the parcellation's space (e.g., 'MNI', 'SUIT')
|
36
39
|
|
37
40
|
# Optional keys:
|
38
41
|
# * 'valid_resolutions': a list of valid resolutions for the parcellation
|
@@ -41,7 +44,7 @@ if TYPE_CHECKING:
|
|
41
44
|
# TODO: have separate dictionary for built-in
|
42
45
|
_available_parcellations: Dict[str, Dict[Any, Any]] = {
|
43
46
|
"SUITxSUIT": {"family": "SUIT", "space": "SUIT"},
|
44
|
-
"SUITxMNI": {"family": "SUIT", "space": "
|
47
|
+
"SUITxMNI": {"family": "SUIT", "space": "MNI152NLin6Asym"},
|
45
48
|
}
|
46
49
|
|
47
50
|
# Add Schaefer parcellation info
|
@@ -52,6 +55,7 @@ for n_rois in range(100, 1001, 100):
|
|
52
55
|
"family": "Schaefer",
|
53
56
|
"n_rois": n_rois,
|
54
57
|
"yeo_networks": t_net,
|
58
|
+
"space": "MNI152NLin6Asym",
|
55
59
|
}
|
56
60
|
# Add Tian parcellation info
|
57
61
|
for scale in range(1, 5):
|
@@ -60,27 +64,28 @@ for scale in range(1, 5):
|
|
60
64
|
"family": "Tian",
|
61
65
|
"scale": scale,
|
62
66
|
"magneticfield": "7T",
|
63
|
-
"space": "
|
67
|
+
"space": "MNI152NLin6Asym",
|
64
68
|
}
|
65
69
|
t_name = f"TianxS{scale}x3TxMNI6thgeneration"
|
66
70
|
_available_parcellations[t_name] = {
|
67
71
|
"family": "Tian",
|
68
72
|
"scale": scale,
|
69
73
|
"magneticfield": "3T",
|
70
|
-
"space": "
|
74
|
+
"space": "MNI152NLin6Asym",
|
71
75
|
}
|
72
76
|
t_name = f"TianxS{scale}x3TxMNInonlinear2009cAsym"
|
73
77
|
_available_parcellations[t_name] = {
|
74
78
|
"family": "Tian",
|
75
79
|
"scale": scale,
|
76
80
|
"magneticfield": "3T",
|
77
|
-
"space": "
|
81
|
+
"space": "MNI152NLin2009cAsym",
|
78
82
|
}
|
79
83
|
# Add AICHA parcellation info
|
80
84
|
for version in (1, 2):
|
81
85
|
_available_parcellations[f"AICHA_v{version}"] = {
|
82
86
|
"family": "AICHA",
|
83
87
|
"version": version,
|
88
|
+
"space": "IXI549Space",
|
84
89
|
}
|
85
90
|
# Add Shen parcellation info
|
86
91
|
for year in (2013, 2015, 2019):
|
@@ -90,18 +95,21 @@ for year in (2013, 2015, 2019):
|
|
90
95
|
"family": "Shen",
|
91
96
|
"year": 2013,
|
92
97
|
"n_rois": n_rois,
|
98
|
+
"space": "MNI152NLin2009cAsym",
|
93
99
|
}
|
94
100
|
elif year == 2015:
|
95
101
|
_available_parcellations["Shen_2015_268"] = {
|
96
102
|
"family": "Shen",
|
97
103
|
"year": 2015,
|
98
104
|
"n_rois": 268,
|
105
|
+
"space": "MNI152NLin2009cAsym",
|
99
106
|
}
|
100
107
|
elif year == 2019:
|
101
108
|
_available_parcellations["Shen_2019_368"] = {
|
102
109
|
"family": "Shen",
|
103
110
|
"year": 2019,
|
104
111
|
"n_rois": 368,
|
112
|
+
"space": "MNI152NLin2009cAsym",
|
105
113
|
}
|
106
114
|
# Add Yan parcellation info
|
107
115
|
for n_rois in range(100, 1001, 100):
|
@@ -111,12 +119,21 @@ for n_rois in range(100, 1001, 100):
|
|
111
119
|
"family": "Yan",
|
112
120
|
"n_rois": n_rois,
|
113
121
|
"yeo_networks": yeo_network,
|
122
|
+
"space": "MNI152NLin6Asym",
|
114
123
|
}
|
115
124
|
# Add Kong networks
|
116
125
|
_available_parcellations[f"Yan{n_rois}xKong17"] = {
|
117
126
|
"family": "Yan",
|
118
127
|
"n_rois": n_rois,
|
119
128
|
"kong_networks": 17,
|
129
|
+
"space": "MNI152NLin6Asym",
|
130
|
+
}
|
131
|
+
# Add Brainnetome parcellation info
|
132
|
+
for threshold in [0, 25, 50]:
|
133
|
+
_available_parcellations[f"Brainnetome_thr{threshold}"] = {
|
134
|
+
"family": "Brainnetome",
|
135
|
+
"threshold": threshold,
|
136
|
+
"space": "MNI152NLin6Asym",
|
120
137
|
}
|
121
138
|
|
122
139
|
|
@@ -124,6 +141,7 @@ def register_parcellation(
|
|
124
141
|
name: str,
|
125
142
|
parcellation_path: Union[str, Path],
|
126
143
|
parcels_labels: List[str],
|
144
|
+
space: str,
|
127
145
|
overwrite: bool = False,
|
128
146
|
) -> None:
|
129
147
|
"""Register a custom user parcellation.
|
@@ -136,6 +154,8 @@ def register_parcellation(
|
|
136
154
|
The path to the parcellation file.
|
137
155
|
parcels_labels : list of str
|
138
156
|
The list of labels for the parcellation.
|
157
|
+
space : str
|
158
|
+
The template space of the parcellation, for e.g., "MNI152NLin6Asym".
|
139
159
|
overwrite : bool, optional
|
140
160
|
If True, overwrite an existing parcellation with the same name.
|
141
161
|
Does not apply to built-in parcellations (default False).
|
@@ -161,8 +181,8 @@ def register_parcellation(
|
|
161
181
|
)
|
162
182
|
else:
|
163
183
|
raise_error(
|
164
|
-
f"Parcellation {name} already registered. Set
|
165
|
-
"to update its value."
|
184
|
+
f"Parcellation {name} already registered. Set "
|
185
|
+
"`overwrite=True` to update its value."
|
166
186
|
)
|
167
187
|
# Convert str to Path
|
168
188
|
if not isinstance(parcellation_path, Path):
|
@@ -172,6 +192,7 @@ def register_parcellation(
|
|
172
192
|
"path": str(parcellation_path.absolute()),
|
173
193
|
"labels": parcels_labels,
|
174
194
|
"family": "CustomUserParcellation",
|
195
|
+
"space": space,
|
175
196
|
}
|
176
197
|
|
177
198
|
|
@@ -187,12 +208,210 @@ def list_parcellations() -> List[str]:
|
|
187
208
|
return sorted(_available_parcellations.keys())
|
188
209
|
|
189
210
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
211
|
+
def get_parcellation(
|
212
|
+
parcellation: List[str],
|
213
|
+
target_data: Dict[str, Any],
|
214
|
+
extra_input: Optional[Dict[str, Any]] = None,
|
215
|
+
) -> Tuple["Nifti1Image", List[str]]:
|
216
|
+
"""Get parcellation, tailored for the target image.
|
217
|
+
|
218
|
+
Parameters
|
219
|
+
----------
|
220
|
+
parcellation : list of str
|
221
|
+
The name(s) of the parcellation(s).
|
222
|
+
target_data : dict
|
223
|
+
The corresponding item of the data object to which the parcellation
|
224
|
+
will be applied.
|
225
|
+
extra_input : dict, optional
|
226
|
+
The other fields in the data object. Useful for accessing other data
|
227
|
+
kinds that needs to be used in the computation of parcellations
|
228
|
+
(default None).
|
229
|
+
|
230
|
+
Returns
|
231
|
+
-------
|
232
|
+
Nifti1Image
|
233
|
+
The parcellation image.
|
234
|
+
list of str
|
235
|
+
Parcellation labels.
|
236
|
+
|
237
|
+
Raises
|
238
|
+
------
|
239
|
+
RuntimeError
|
240
|
+
If warp / transformation file extension is not ".mat" or ".h5".
|
241
|
+
ValueError
|
242
|
+
If ``extra_input`` is None when ``target_data``'s space is native.
|
243
|
+
|
244
|
+
"""
|
245
|
+
# Check pre-requirements for space manipulation
|
246
|
+
target_space = target_data["space"]
|
247
|
+
# Set target standard space to target space
|
248
|
+
target_std_space = target_space
|
249
|
+
# Extra data type requirement check if target space is native
|
250
|
+
if target_space == "native":
|
251
|
+
# Check for extra inputs
|
252
|
+
if extra_input is None:
|
253
|
+
raise_error(
|
254
|
+
"No extra input provided, requires `Warp` and `T1w` "
|
255
|
+
"data types in particular for transformation to "
|
256
|
+
f"{target_data['space']} space for further computation."
|
257
|
+
)
|
258
|
+
# Set target standard space to warp file space source
|
259
|
+
target_std_space = extra_input["Warp"]["src"]
|
260
|
+
|
261
|
+
# Get the min of the voxels sizes and use it as the resolution
|
262
|
+
target_img = target_data["data"]
|
263
|
+
resolution = np.min(target_img.header.get_zooms()[:3])
|
264
|
+
|
265
|
+
# Create component-scoped tempdir
|
266
|
+
tempdir = WorkDirManager().get_tempdir(prefix="parcellations")
|
267
|
+
# Create element-scoped tempdir so that warped parcellation is
|
268
|
+
# available later as nibabel stores file path reference for
|
269
|
+
# loading on computation
|
270
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
271
|
+
prefix="parcellations"
|
272
|
+
)
|
273
|
+
|
274
|
+
# Load the parcellations
|
275
|
+
all_parcellations = []
|
276
|
+
all_labels = []
|
277
|
+
for name in parcellation:
|
278
|
+
img, labels, _, space = load_parcellation(
|
279
|
+
name=name,
|
280
|
+
resolution=resolution,
|
281
|
+
)
|
282
|
+
|
283
|
+
# Convert parcellation spaces if required
|
284
|
+
if space != target_std_space:
|
285
|
+
# Get xfm file
|
286
|
+
xfm_file_path = get_xfm(src=space, dst=target_std_space)
|
287
|
+
# Get target standard space template
|
288
|
+
target_std_space_template_img = get_template(
|
289
|
+
space=target_std_space,
|
290
|
+
target_data=target_data,
|
291
|
+
extra_input=extra_input,
|
292
|
+
)
|
293
|
+
|
294
|
+
# Save parcellation image to a component-scoped tempfile
|
295
|
+
parcellation_path = tempdir / f"{name}.nii.gz"
|
296
|
+
nib.save(img, parcellation_path)
|
297
|
+
|
298
|
+
# Save template
|
299
|
+
target_std_space_template_path = (
|
300
|
+
tempdir / f"{target_std_space}_T1w_{resolution}.nii.gz"
|
301
|
+
)
|
302
|
+
nib.save(
|
303
|
+
target_std_space_template_img, target_std_space_template_path
|
304
|
+
)
|
305
|
+
|
306
|
+
# Set warped parcellation path
|
307
|
+
warped_parcellation_path = element_tempdir / (
|
308
|
+
f"{name}_warped_from_{space}_to_" f"{target_std_space}.nii.gz"
|
309
|
+
)
|
310
|
+
|
311
|
+
logger.debug(
|
312
|
+
f"Using ANTs to warp {name} "
|
313
|
+
f"from {space} to {target_std_space}"
|
314
|
+
)
|
315
|
+
# Set antsApplyTransforms command
|
316
|
+
apply_transforms_cmd = [
|
317
|
+
"antsApplyTransforms",
|
318
|
+
"-d 3",
|
319
|
+
"-e 3",
|
320
|
+
"-n 'GenericLabel[NearestNeighbor]'",
|
321
|
+
f"-i {parcellation_path.resolve()}",
|
322
|
+
f"-r {target_std_space_template_path.resolve()}",
|
323
|
+
f"-t {xfm_file_path.resolve()}",
|
324
|
+
f"-o {warped_parcellation_path.resolve()}",
|
325
|
+
]
|
326
|
+
# Call antsApplyTransforms
|
327
|
+
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
328
|
+
|
329
|
+
img = nib.load(warped_parcellation_path)
|
330
|
+
|
331
|
+
# Resample parcellation to target image
|
332
|
+
img_to_merge = image.resample_to_img(
|
333
|
+
source_img=img,
|
334
|
+
target_img=target_img,
|
335
|
+
interpolation="nearest",
|
336
|
+
copy=True,
|
337
|
+
)
|
338
|
+
|
339
|
+
all_parcellations.append(img_to_merge)
|
340
|
+
all_labels.append(labels)
|
341
|
+
|
342
|
+
# Avoid merging if there is only one parcellation
|
343
|
+
if len(all_parcellations) == 1:
|
344
|
+
resampled_parcellation_img = all_parcellations[0]
|
345
|
+
labels = all_labels[0]
|
346
|
+
# Parcellations are already transformed to target standard space
|
347
|
+
else:
|
348
|
+
resampled_parcellation_img, labels = merge_parcellations(
|
349
|
+
parcellations_list=all_parcellations,
|
350
|
+
parcellations_names=parcellation,
|
351
|
+
labels_lists=all_labels,
|
352
|
+
)
|
353
|
+
|
354
|
+
# Warp parcellation if target space is native
|
355
|
+
if target_space == "native":
|
356
|
+
# Save parcellation image to a component-scoped tempfile
|
357
|
+
prewarp_parcellation_path = tempdir / "prewarp_parcellation.nii.gz"
|
358
|
+
nib.save(resampled_parcellation_img, prewarp_parcellation_path)
|
359
|
+
|
360
|
+
# Create an element-scoped tempfile for warped output
|
361
|
+
warped_parcellation_path = (
|
362
|
+
element_tempdir / "parcellation_warped.nii.gz"
|
363
|
+
)
|
364
|
+
|
365
|
+
# Check for warp file type to use correct tool
|
366
|
+
warp_file_ext = extra_input["Warp"]["path"].suffix
|
367
|
+
if warp_file_ext == ".mat":
|
368
|
+
logger.debug("Using FSL for parcellation warping")
|
369
|
+
# Set applywarp command
|
370
|
+
applywarp_cmd = [
|
371
|
+
"applywarp",
|
372
|
+
"--interp=nn",
|
373
|
+
f"-i {prewarp_parcellation_path.resolve()}",
|
374
|
+
# use resampled reference
|
375
|
+
f"-r {target_data['reference_path'].resolve()}",
|
376
|
+
f"-w {extra_input['Warp']['path'].resolve()}",
|
377
|
+
f"-o {warped_parcellation_path.resolve()}",
|
378
|
+
]
|
379
|
+
# Call applywarp
|
380
|
+
run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
|
381
|
+
|
382
|
+
elif warp_file_ext == ".h5":
|
383
|
+
logger.debug("Using ANTs for parcellation warping")
|
384
|
+
# Set antsApplyTransforms command
|
385
|
+
apply_transforms_cmd = [
|
386
|
+
"antsApplyTransforms",
|
387
|
+
"-d 3",
|
388
|
+
"-e 3",
|
389
|
+
"-n 'GenericLabel[NearestNeighbor]'",
|
390
|
+
f"-i {prewarp_parcellation_path.resolve()}",
|
391
|
+
# use resampled reference
|
392
|
+
f"-r {target_data['reference_path'].resolve()}",
|
393
|
+
f"-t {extra_input['Warp']['path'].resolve()}",
|
394
|
+
f"-o {warped_parcellation_path.resolve()}",
|
395
|
+
]
|
396
|
+
# Call antsApplyTransforms
|
397
|
+
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
398
|
+
|
399
|
+
else:
|
400
|
+
raise_error(
|
401
|
+
msg=(
|
402
|
+
"Unknown warp / transformation file extension: "
|
403
|
+
f"{warp_file_ext}"
|
404
|
+
),
|
405
|
+
klass=RuntimeError,
|
406
|
+
)
|
407
|
+
|
408
|
+
# Load nifti
|
409
|
+
resampled_parcellation_img = nib.load(warped_parcellation_path)
|
410
|
+
|
411
|
+
# Delete tempdir
|
412
|
+
WorkDirManager().delete_tempdir(tempdir)
|
413
|
+
|
414
|
+
return resampled_parcellation_img, labels # type: ignore
|
196
415
|
|
197
416
|
|
198
417
|
def load_parcellation(
|
@@ -200,11 +419,11 @@ def load_parcellation(
|
|
200
419
|
parcellations_dir: Union[str, Path, None] = None,
|
201
420
|
resolution: Optional[float] = None,
|
202
421
|
path_only: bool = False,
|
203
|
-
) -> Tuple[Optional["Nifti1Image"], List[str], Path]:
|
422
|
+
) -> Tuple[Optional["Nifti1Image"], List[str], Path, str]:
|
204
423
|
"""Load a brain parcellation (including a label file).
|
205
424
|
|
206
|
-
If it is a built-in
|
207
|
-
|
425
|
+
If it is a built-in parcellation and the file is not present in the
|
426
|
+
``parcellations_dir`` directory, it will be downloaded.
|
208
427
|
|
209
428
|
Parameters
|
210
429
|
----------
|
@@ -230,18 +449,33 @@ def load_parcellation(
|
|
230
449
|
Parcellation labels.
|
231
450
|
pathlib.Path
|
232
451
|
File path to the parcellation image.
|
452
|
+
str
|
453
|
+
The space of the parcellation.
|
454
|
+
|
455
|
+
Raises
|
456
|
+
------
|
457
|
+
ValueError
|
458
|
+
If ``name`` is invalid or if the parcellation values and labels
|
459
|
+
don't have equal dimension or if the value range is invalid.
|
233
460
|
|
234
461
|
"""
|
235
|
-
#
|
462
|
+
# Check for valid parcellation name
|
236
463
|
if name not in _available_parcellations:
|
237
464
|
raise_error(
|
238
465
|
f"Parcellation {name} not found. "
|
239
466
|
f"Valid options are: {list_parcellations()}"
|
240
467
|
)
|
241
468
|
|
469
|
+
# Copy parcellation definition to avoid edits in original object
|
242
470
|
parcellation_definition = _available_parcellations[name].copy()
|
243
471
|
t_family = parcellation_definition.pop("family")
|
472
|
+
# Remove space conditionally
|
473
|
+
if t_family not in ["SUIT", "Tian"]:
|
474
|
+
space = parcellation_definition.pop("space")
|
475
|
+
else:
|
476
|
+
space = parcellation_definition["space"]
|
244
477
|
|
478
|
+
# Check if the parcellation family is custom or built-in
|
245
479
|
if t_family == "CustomUserParcellation":
|
246
480
|
parcellation_fname = Path(parcellation_definition["path"])
|
247
481
|
parcellation_labels = parcellation_definition["labels"]
|
@@ -253,25 +487,32 @@ def load_parcellation(
|
|
253
487
|
**parcellation_definition,
|
254
488
|
)
|
255
489
|
|
490
|
+
# Load parcellation image and values
|
256
491
|
logger.info(f"Loading parcellation {parcellation_fname.absolute()!s}")
|
257
|
-
|
258
492
|
parcellation_img = None
|
259
493
|
if path_only is False:
|
494
|
+
# Load image via nibabel
|
260
495
|
parcellation_img = nib.load(parcellation_fname)
|
496
|
+
# Get unique values
|
261
497
|
parcel_values = np.unique(parcellation_img.get_fdata())
|
498
|
+
# Check for dimension
|
262
499
|
if len(parcel_values) - 1 != len(parcellation_labels):
|
263
500
|
raise_error(
|
264
501
|
f"Parcellation {name} has {len(parcel_values) - 1} parcels "
|
265
502
|
f"but {len(parcellation_labels)} labels."
|
266
503
|
)
|
504
|
+
# Sort values
|
267
505
|
parcel_values.sort()
|
506
|
+
# Check if value range is invalid
|
268
507
|
if np.any(np.diff(parcel_values) != 1):
|
269
508
|
raise_error(
|
270
509
|
f"Parcellation {name} must have all the values in the range "
|
271
510
|
f"[0, {len(parcel_values)}]."
|
272
511
|
)
|
273
512
|
|
274
|
-
|
513
|
+
# Type-cast to remove errors
|
514
|
+
parcellation_img = typing.cast("Nifti1Image", parcellation_img)
|
515
|
+
return parcellation_img, parcellation_labels, parcellation_fname, space
|
275
516
|
|
276
517
|
|
277
518
|
def _retrieve_parcellation(
|
@@ -312,13 +553,13 @@ def _retrieve_parcellation(
|
|
312
553
|
* Tian :
|
313
554
|
``scale`` : {1, 2, 3, 4}
|
314
555
|
Scale of parcellation (defines granularity).
|
315
|
-
``space`` : {"
|
316
|
-
Space of parcellation (default "
|
556
|
+
``space`` : {"MNI152NLin6Asym", "MNI152NLin2009cAsym"}, optional
|
557
|
+
Space of parcellation (default "MNI152NLin6Asym"). (For more
|
317
558
|
information see https://github.com/yetianmed/subcortex)
|
318
559
|
``magneticfield`` : {"3T", "7T"}, optional
|
319
560
|
Magnetic field (default "3T").
|
320
561
|
* SUIT :
|
321
|
-
``space`` : {"
|
562
|
+
``space`` : {"MNI152NLin6Asym", "SUIT"}, optional
|
322
563
|
Space of parcellation (default "MNI"). (For more information
|
323
564
|
see http://www.diedrichsenlab.org/imaging/suit.htm).
|
324
565
|
* AICHA :
|
@@ -338,6 +579,9 @@ def _retrieve_parcellation(
|
|
338
579
|
Number of Yeo networks to use (default None).
|
339
580
|
``kong_networks`` : {17}, optional
|
340
581
|
Number of Kong networks to use (default None).
|
582
|
+
* Brainnetome :
|
583
|
+
``threshold`` : {0, 25, 50}
|
584
|
+
Threshold for the probabilistic maps of subregion.
|
341
585
|
|
342
586
|
Returns
|
343
587
|
-------
|
@@ -401,6 +645,12 @@ def _retrieve_parcellation(
|
|
401
645
|
resolution=resolution,
|
402
646
|
**kwargs,
|
403
647
|
)
|
648
|
+
elif family == "Brainnetome":
|
649
|
+
parcellation_fname, parcellation_labels = _retrieve_brainnetome(
|
650
|
+
parcellations_dir=parcellations_dir,
|
651
|
+
resolution=resolution,
|
652
|
+
**kwargs,
|
653
|
+
)
|
404
654
|
else:
|
405
655
|
raise_error(
|
406
656
|
f"The provided parcellation name {family} cannot be retrieved."
|
@@ -451,24 +701,27 @@ def _retrieve_schaefer(
|
|
451
701
|
logger.info(f"\tn_rois: {n_rois}")
|
452
702
|
logger.info(f"\tyeo_networks: {yeo_networks}")
|
453
703
|
|
704
|
+
# Check n_rois value
|
454
705
|
_valid_n_rois = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]
|
455
|
-
_valid_networks = [7, 17]
|
456
|
-
_valid_resolutions = [1, 2]
|
457
|
-
|
458
706
|
if n_rois not in _valid_n_rois:
|
459
707
|
raise_error(
|
460
708
|
f"The parameter `n_rois` ({n_rois}) needs to be one of the "
|
461
709
|
f"following: {_valid_n_rois}"
|
462
710
|
)
|
711
|
+
|
712
|
+
# Check networks
|
713
|
+
_valid_networks = [7, 17]
|
463
714
|
if yeo_networks not in _valid_networks:
|
464
715
|
raise_error(
|
465
716
|
f"The parameter `yeo_networks` ({yeo_networks}) needs to be one "
|
466
717
|
f"of the following: {_valid_networks}"
|
467
718
|
)
|
468
719
|
|
720
|
+
# Check resolution
|
721
|
+
_valid_resolutions = [1, 2]
|
469
722
|
resolution = closest_resolution(resolution, _valid_resolutions)
|
470
723
|
|
471
|
-
#
|
724
|
+
# Define parcellation and label file names
|
472
725
|
parcellation_fname = (
|
473
726
|
parcellations_dir
|
474
727
|
/ "schaefer_2018"
|
@@ -483,7 +736,7 @@ def _retrieve_schaefer(
|
|
483
736
|
/ (f"Schaefer2018_{n_rois}Parcels_{yeo_networks}Networks_order.txt")
|
484
737
|
)
|
485
738
|
|
486
|
-
#
|
739
|
+
# Check existence of parcellation
|
487
740
|
if not (parcellation_fname.exists() and parcellation_lname.exists()):
|
488
741
|
logger.info(
|
489
742
|
"At least one of the parcellation files are missing. "
|
@@ -493,14 +746,9 @@ def _retrieve_schaefer(
|
|
493
746
|
n_rois=n_rois,
|
494
747
|
yeo_networks=yeo_networks,
|
495
748
|
resolution_mm=resolution, # type: ignore we know it's 1 or 2
|
496
|
-
data_dir=
|
749
|
+
data_dir=parcellations_dir.resolve(),
|
497
750
|
)
|
498
751
|
|
499
|
-
if not (
|
500
|
-
parcellation_fname.exists() and parcellation_lname.exists()
|
501
|
-
): # pragma: no cover
|
502
|
-
raise_error("There was a problem fetching the parcellations.")
|
503
|
-
|
504
752
|
# Load labels
|
505
753
|
labels = [
|
506
754
|
"_".join(x.split("_")[1:])
|
@@ -516,7 +764,7 @@ def _retrieve_tian(
|
|
516
764
|
parcellations_dir: Path,
|
517
765
|
resolution: Optional[float] = None,
|
518
766
|
scale: Optional[int] = None,
|
519
|
-
space: str = "
|
767
|
+
space: str = "MNI152NLin6Asym",
|
520
768
|
magneticfield: str = "3T",
|
521
769
|
) -> Tuple[Path, List[str]]:
|
522
770
|
"""Retrieve Tian parcellation.
|
@@ -533,8 +781,8 @@ def _retrieve_tian(
|
|
533
781
|
parcellation depend on the space and magnetic field.
|
534
782
|
scale : {1, 2, 3, 4}, optional
|
535
783
|
Scale of parcellation (defines granularity) (default None).
|
536
|
-
space : {"
|
537
|
-
Space of parcellation (default "
|
784
|
+
space : {"MNI152NLin6Asym", "MNI152NLin2009cAsym"}, optional
|
785
|
+
Space of parcellation (default "MNI152NLin6Asym"). (For more
|
538
786
|
information see https://github.com/yetianmed/subcortex)
|
539
787
|
magneticfield : {"3T", "7T"}, optional
|
540
788
|
Magnetic field (default "3T").
|
@@ -548,19 +796,20 @@ def _retrieve_tian(
|
|
548
796
|
|
549
797
|
Raises
|
550
798
|
------
|
799
|
+
RuntimeError
|
800
|
+
If there is a problem fetching files.
|
551
801
|
ValueError
|
552
802
|
If invalid value is provided for ``scale`` or ``magneticfield`` or
|
553
|
-
``space
|
803
|
+
``space``.
|
554
804
|
|
555
805
|
"""
|
556
|
-
# show parameters to user
|
557
806
|
logger.info("Parcellation parameters:")
|
558
807
|
logger.info(f"\tresolution: {resolution}")
|
559
808
|
logger.info(f"\tscale: {scale}")
|
560
809
|
logger.info(f"\tspace: {space}")
|
561
810
|
logger.info(f"\tmagneticfield: {magneticfield}")
|
562
811
|
|
563
|
-
#
|
812
|
+
# Check scale
|
564
813
|
_valid_scales = [1, 2, 3, 4]
|
565
814
|
if scale not in _valid_scales:
|
566
815
|
raise_error(
|
@@ -568,12 +817,13 @@ def _retrieve_tian(
|
|
568
817
|
f"following: {_valid_scales}"
|
569
818
|
)
|
570
819
|
|
820
|
+
# Check resolution
|
571
821
|
_valid_resolutions = [] # avoid pylance error
|
572
822
|
if magneticfield == "3T":
|
573
|
-
_valid_spaces = ["
|
574
|
-
if space == "
|
823
|
+
_valid_spaces = ["MNI152NLin6Asym", "MNI152NLin2009cAsym"]
|
824
|
+
if space == "MNI152NLin6Asym":
|
575
825
|
_valid_resolutions = [1, 2]
|
576
|
-
elif space == "
|
826
|
+
elif space == "MNI152NLin2009cAsym":
|
577
827
|
_valid_resolutions = [2]
|
578
828
|
else:
|
579
829
|
raise_error(
|
@@ -582,20 +832,19 @@ def _retrieve_tian(
|
|
582
832
|
)
|
583
833
|
elif magneticfield == "7T":
|
584
834
|
_valid_resolutions = [1.6]
|
585
|
-
if space != "
|
835
|
+
if space != "MNI152NLin6Asym":
|
586
836
|
raise_error(
|
587
837
|
f"The parameter `space` ({space}) for 7T needs to be "
|
588
|
-
f"
|
838
|
+
f"MNI152NLin6Asym"
|
589
839
|
)
|
590
840
|
else:
|
591
841
|
raise_error(
|
592
842
|
f"The parameter `magneticfield` ({magneticfield}) needs to be "
|
593
843
|
f"one of the following: 3T or 7T"
|
594
844
|
)
|
595
|
-
|
596
845
|
resolution = closest_resolution(resolution, _valid_resolutions)
|
597
846
|
|
598
|
-
#
|
847
|
+
# Define parcellation and label file names
|
599
848
|
if magneticfield == "3T":
|
600
849
|
parcellation_fname_base_3T = (
|
601
850
|
parcellations_dir / "Tian2020MSA_v1.1" / "3T" / "Subcortex-Only"
|
@@ -603,7 +852,7 @@ def _retrieve_tian(
|
|
603
852
|
parcellation_lname = parcellation_fname_base_3T / (
|
604
853
|
f"Tian_Subcortex_S{scale}_3T_label.txt"
|
605
854
|
)
|
606
|
-
if space == "
|
855
|
+
if space == "MNI152NLin6Asym":
|
607
856
|
parcellation_fname = parcellation_fname_base_3T / (
|
608
857
|
f"Tian_Subcortex_S{scale}_{magneticfield}.nii.gz"
|
609
858
|
)
|
@@ -612,7 +861,7 @@ def _retrieve_tian(
|
|
612
861
|
parcellation_fname_base_3T
|
613
862
|
/ f"Tian_Subcortex_S{scale}_{magneticfield}_1mm.nii.gz"
|
614
863
|
)
|
615
|
-
elif space == "
|
864
|
+
elif space == "MNI152NLin2009cAsym":
|
616
865
|
space = "2009cAsym"
|
617
866
|
parcellation_fname = parcellation_fname_base_3T / (
|
618
867
|
f"Tian_Subcortex_S{scale}_{magneticfield}_{space}.nii.gz"
|
@@ -644,45 +893,53 @@ def _retrieve_tian(
|
|
644
893
|
"parcellation. A simple numbering scheme for distinction was "
|
645
894
|
"therefore used."
|
646
895
|
)
|
647
|
-
else: # pragma: no cover
|
648
|
-
raise_error("This should not happen. Please report this error.")
|
649
896
|
|
650
|
-
#
|
897
|
+
# Check existence of parcellation
|
651
898
|
if not (parcellation_fname.exists() and parcellation_lname.exists()):
|
652
899
|
logger.info(
|
653
900
|
"At least one of the parcellation files are missing, fetching."
|
654
901
|
)
|
655
|
-
|
656
|
-
|
902
|
+
# Set URL
|
903
|
+
url = (
|
657
904
|
"https://www.nitrc.org/frs/download.php/12012/Tian2020MSA_v1.1.zip"
|
658
905
|
)
|
659
906
|
|
660
|
-
logger.info(f"Downloading TIAN from {
|
907
|
+
logger.info(f"Downloading TIAN from {url}")
|
908
|
+
# Store initial download in a tempdir
|
661
909
|
with tempfile.TemporaryDirectory() as tmpdir:
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
910
|
+
# Make HTTP request
|
911
|
+
try:
|
912
|
+
resp = httpx.get(url)
|
913
|
+
resp.raise_for_status()
|
914
|
+
except httpx.HTTPError as exc:
|
915
|
+
raise_error(
|
916
|
+
f"Error response {exc.response.status_code} while "
|
917
|
+
f"requesting {exc.request.url!r}",
|
918
|
+
klass=RuntimeError,
|
919
|
+
)
|
920
|
+
else:
|
921
|
+
# Set tempfile for storing initial content and unzipping
|
922
|
+
zip_fname = Path(tmpdir) / "Tian2020MSA_v1.1.zip"
|
923
|
+
# Open tempfile and write content
|
924
|
+
with open(zip_fname, "wb") as f:
|
925
|
+
f.write(resp.content)
|
926
|
+
# Unzip tempfile
|
927
|
+
with zipfile.ZipFile(zip_fname, "r") as zip_ref:
|
928
|
+
zip_ref.extractall(parcellations_dir.as_posix())
|
929
|
+
# Clean after unzipping
|
930
|
+
if (parcellations_dir / "__MACOSX").exists():
|
931
|
+
shutil.rmtree((parcellations_dir / "__MACOSX").as_posix())
|
678
932
|
|
933
|
+
# Load labels
|
679
934
|
labels = pd.read_csv(parcellation_lname, sep=" ", header=None)[0].to_list()
|
680
935
|
|
681
936
|
return parcellation_fname, labels
|
682
937
|
|
683
938
|
|
684
939
|
def _retrieve_suit(
|
685
|
-
parcellations_dir: Path,
|
940
|
+
parcellations_dir: Path,
|
941
|
+
resolution: Optional[float],
|
942
|
+
space: str = "MNI152NLin6Asym",
|
686
943
|
) -> Tuple[Path, List[str]]:
|
687
944
|
"""Retrieve SUIT parcellation.
|
688
945
|
|
@@ -696,9 +953,9 @@ def _retrieve_suit(
|
|
696
953
|
resolution higher than the desired one. By default, will load the
|
697
954
|
highest one (default None). Available resolutions for this parcellation
|
698
955
|
are 1mm and 2mm.
|
699
|
-
space : {"
|
700
|
-
Space of parcellation (default "
|
701
|
-
see http://www.diedrichsenlab.org/imaging/suit.htm).
|
956
|
+
space : {"MNI152NLin6Asym", "SUIT"}, optional
|
957
|
+
Space of parcellation (default "MNI152NLin6Asym"). (For more
|
958
|
+
information see http://www.diedrichsenlab.org/imaging/suit.htm).
|
702
959
|
|
703
960
|
Returns
|
704
961
|
-------
|
@@ -709,30 +966,33 @@ def _retrieve_suit(
|
|
709
966
|
|
710
967
|
Raises
|
711
968
|
------
|
969
|
+
RuntimeError
|
970
|
+
If there is a problem fetching files.
|
712
971
|
ValueError
|
713
|
-
If invalid value is provided for ``space
|
714
|
-
fetching the parcellation.
|
972
|
+
If invalid value is provided for ``space``.
|
715
973
|
|
716
974
|
"""
|
717
975
|
logger.info("Parcellation parameters:")
|
718
976
|
logger.info(f"\tresolution: {resolution}")
|
719
977
|
logger.info(f"\tspace: {space}")
|
720
978
|
|
721
|
-
|
722
|
-
|
723
|
-
# check validity of parameters
|
979
|
+
# Check space
|
980
|
+
_valid_spaces = ["MNI152NLin6Asym", "SUIT"]
|
724
981
|
if space not in _valid_spaces:
|
725
982
|
raise_error(
|
726
983
|
f"The parameter `space` ({space}) needs to be one of the "
|
727
984
|
f"following: {_valid_spaces}"
|
728
985
|
)
|
729
986
|
|
730
|
-
#
|
987
|
+
# Check resolution
|
731
988
|
_valid_resolutions = [1]
|
732
|
-
|
733
989
|
resolution = closest_resolution(resolution, _valid_resolutions)
|
734
990
|
|
735
|
-
#
|
991
|
+
# Format space if MNI; required for the file name
|
992
|
+
if space == "MNI152NLin6Asym":
|
993
|
+
space = "MNI"
|
994
|
+
|
995
|
+
# Define parcellation and label file names
|
736
996
|
parcellation_fname = (
|
737
997
|
parcellations_dir / "SUIT" / (f"SUIT_{space}Space_{resolution}mm.nii")
|
738
998
|
)
|
@@ -740,45 +1000,61 @@ def _retrieve_suit(
|
|
740
1000
|
parcellations_dir / "SUIT" / (f"SUIT_{space}Space_{resolution}mm.tsv")
|
741
1001
|
)
|
742
1002
|
|
743
|
-
#
|
1003
|
+
# Check existence of parcellation
|
744
1004
|
if not (parcellation_fname.exists() and parcellation_lname.exists()):
|
745
|
-
parcellation_fname.parent.mkdir(exist_ok=True, parents=True)
|
746
1005
|
logger.info(
|
747
1006
|
"At least one of the parcellation files is missing, fetching."
|
748
1007
|
)
|
749
|
-
|
1008
|
+
# Create local directory if not present
|
1009
|
+
parcellation_fname.parent.mkdir(exist_ok=True, parents=True)
|
1010
|
+
# Set URL
|
750
1011
|
url_basis = (
|
751
1012
|
"https://github.com/DiedrichsenLab/cerebellar_atlases/raw"
|
752
|
-
"/master/Diedrichsen_2009
|
1013
|
+
"/master/Diedrichsen_2009"
|
753
1014
|
)
|
754
|
-
url_MNI = url_basis + "atl-Anatom_space-MNI_dseg.nii"
|
755
|
-
url_SUIT = url_basis + "atl-Anatom_space-SUIT_dseg.nii"
|
756
|
-
url_labels = url_basis + "atl-Anatom.tsv"
|
757
|
-
|
758
1015
|
if space == "MNI":
|
759
|
-
|
760
|
-
parcellation_download = requests.get(url_MNI)
|
761
|
-
with open(parcellation_fname, "wb") as f:
|
762
|
-
f.write(parcellation_download.content)
|
1016
|
+
url = f"{url_basis}/atl-Anatom_space-MNI_dseg.nii"
|
763
1017
|
else: # if not MNI, then SUIT
|
764
|
-
|
765
|
-
|
766
|
-
with open(parcellation_fname, "wb") as f:
|
767
|
-
f.write(parcellation_download.content)
|
768
|
-
|
769
|
-
labels_download = requests.get(url_labels)
|
770
|
-
labels = pd.read_csv(
|
771
|
-
io.StringIO(labels_download.content.decode("utf-8")),
|
772
|
-
sep="\t",
|
773
|
-
usecols=["name"],
|
774
|
-
)
|
1018
|
+
url = f"{url_basis}/atl-Anatom_space-SUIT_dseg.nii"
|
1019
|
+
url_labels = f"{url_basis}/atl-Anatom.tsv"
|
775
1020
|
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
1021
|
+
# Make HTTP requests
|
1022
|
+
with httpx.Client(follow_redirects=True) as client:
|
1023
|
+
# Download parcellation file
|
1024
|
+
logger.info(f"Downloading SUIT parcellation from {url}")
|
1025
|
+
try:
|
1026
|
+
img_resp = client.get(url)
|
1027
|
+
img_resp.raise_for_status()
|
1028
|
+
except httpx.HTTPError as exc:
|
1029
|
+
raise_error(
|
1030
|
+
f"Error response {exc.response.status_code} while "
|
1031
|
+
f"requesting {exc.request.url!r}",
|
1032
|
+
klass=RuntimeError,
|
1033
|
+
)
|
1034
|
+
else:
|
1035
|
+
with open(parcellation_fname, "wb") as f:
|
1036
|
+
f.write(img_resp.content)
|
1037
|
+
# Download label file
|
1038
|
+
logger.info(f"Downloading SUIT labels from {url_labels}")
|
1039
|
+
try:
|
1040
|
+
label_resp = client.get(url_labels)
|
1041
|
+
label_resp.raise_for_status()
|
1042
|
+
except httpx.HTTPError as exc:
|
1043
|
+
raise_error(
|
1044
|
+
f"Error response {exc.response.status_code} while "
|
1045
|
+
f"requesting {exc.request.url!r}",
|
1046
|
+
klass=RuntimeError,
|
1047
|
+
)
|
1048
|
+
else:
|
1049
|
+
# Load labels
|
1050
|
+
labels = pd.read_csv(
|
1051
|
+
io.StringIO(label_resp.content.decode("utf-8")),
|
1052
|
+
sep="\t",
|
1053
|
+
usecols=["name"],
|
1054
|
+
)
|
1055
|
+
labels.to_csv(parcellation_lname, sep="\t", index=False)
|
781
1056
|
|
1057
|
+
# Load labels
|
782
1058
|
labels = pd.read_csv(parcellation_lname, sep="\t", usecols=["name"])[
|
783
1059
|
"name"
|
784
1060
|
].to_list()
|
@@ -815,9 +1091,15 @@ def _retrieve_aicha(
|
|
815
1091
|
|
816
1092
|
Raises
|
817
1093
|
------
|
1094
|
+
RuntimeError
|
1095
|
+
If there is a problem fetching files.
|
818
1096
|
ValueError
|
819
|
-
If invalid value is provided for ``version
|
820
|
-
|
1097
|
+
If invalid value is provided for ``version``.
|
1098
|
+
|
1099
|
+
Warns
|
1100
|
+
-----
|
1101
|
+
RuntimeWarning
|
1102
|
+
Until the authors confirm the space, the warning will be issued.
|
821
1103
|
|
822
1104
|
Notes
|
823
1105
|
-----
|
@@ -825,28 +1107,33 @@ def _retrieve_aicha(
|
|
825
1107
|
1mm, it is only for display purpose as noted in the release document.
|
826
1108
|
|
827
1109
|
"""
|
828
|
-
#
|
1110
|
+
# Issue warning until space is confirmed by authors
|
1111
|
+
warn_with_log(
|
1112
|
+
"The current space for AICHA parcellations are IXI549Space, but are "
|
1113
|
+
"not confirmed by authors, until that this warning will be issued."
|
1114
|
+
)
|
1115
|
+
|
829
1116
|
logger.info("Parcellation parameters:")
|
830
1117
|
logger.info(f"\tresolution: {resolution}")
|
831
1118
|
logger.info(f"\tversion: {version}")
|
832
1119
|
|
833
|
-
# Check version
|
834
|
-
_valid_version =
|
1120
|
+
# Check version
|
1121
|
+
_valid_version = [1, 2]
|
835
1122
|
if version not in _valid_version:
|
836
1123
|
raise_error(
|
837
1124
|
f"The parameter `version` ({version}) needs to be one of the "
|
838
1125
|
f"following: {_valid_version}"
|
839
1126
|
)
|
840
1127
|
|
1128
|
+
# Check resolution
|
841
1129
|
_valid_resolutions = [1]
|
842
1130
|
resolution = closest_resolution(resolution, _valid_resolutions)
|
843
1131
|
|
844
|
-
# Define
|
1132
|
+
# Define parcellation and label file names
|
845
1133
|
parcellation_fname = (
|
846
1134
|
parcellations_dir / f"AICHA_v{version}" / "AICHA" / "AICHA.nii"
|
847
1135
|
)
|
848
|
-
|
849
|
-
# Define label file name according to version
|
1136
|
+
parcellation_lname = Path()
|
850
1137
|
if version == 1:
|
851
1138
|
parcellation_lname = (
|
852
1139
|
parcellations_dir
|
@@ -867,54 +1154,60 @@ def _retrieve_aicha(
|
|
867
1154
|
logger.info(
|
868
1155
|
"At least one of the parcellation files are missing, fetching."
|
869
1156
|
)
|
870
|
-
|
871
1157
|
# Set file name on server according to version
|
1158
|
+
server_filename = ""
|
872
1159
|
if version == 1:
|
873
1160
|
server_filename = "aicha_v1.zip"
|
874
1161
|
elif version == 2:
|
875
1162
|
server_filename = "AICHA_v2.tar.zip"
|
876
|
-
|
877
1163
|
# Set URL
|
878
1164
|
url = f"http://www.gin.cnrs.fr/wp-content/uploads/{server_filename}"
|
879
1165
|
|
880
1166
|
logger.info(f"Downloading AICHA v{version} from {url}")
|
1167
|
+
# Store initial download in a tempdir
|
881
1168
|
with tempfile.TemporaryDirectory() as tmpdir:
|
882
1169
|
# Make HTTP request
|
883
1170
|
try:
|
884
|
-
resp =
|
1171
|
+
resp = httpx.get(url, follow_redirects=True)
|
885
1172
|
resp.raise_for_status()
|
886
|
-
except
|
1173
|
+
except httpx.HTTPError as exc:
|
887
1174
|
raise_error(
|
888
|
-
f"
|
1175
|
+
f"Error response {exc.response.status_code} while "
|
1176
|
+
f"requesting {exc.request.url!r}",
|
1177
|
+
klass=RuntimeError,
|
889
1178
|
)
|
890
1179
|
else:
|
1180
|
+
# Set tempfile for storing initial content and unzipping
|
891
1181
|
parcellation_zip_path = Path(tmpdir) / server_filename
|
1182
|
+
# Open tempfile and write content
|
892
1183
|
with open(parcellation_zip_path, "wb") as f:
|
893
1184
|
f.write(resp.content)
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
(parcellations_dir / "AICHA_v1").as_posix()
|
900
|
-
)
|
901
|
-
elif version == 2:
|
902
|
-
zip_ref.extractall(Path(tmpdir).as_posix())
|
903
|
-
# Extract tarfile for v2
|
904
|
-
with tarfile.TarFile(
|
905
|
-
Path(tmpdir) / "aicha_v2.tar", "r"
|
906
|
-
) as tar_ref:
|
907
|
-
tar_ref.extractall(
|
908
|
-
(parcellations_dir / "AICHA_v2").as_posix()
|
1185
|
+
# Unzip tempfile
|
1186
|
+
with zipfile.ZipFile(parcellation_zip_path, "r") as zip_ref:
|
1187
|
+
if version == 1:
|
1188
|
+
zip_ref.extractall(
|
1189
|
+
(parcellations_dir / "AICHA_v1").as_posix()
|
909
1190
|
)
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
1191
|
+
elif version == 2:
|
1192
|
+
zip_ref.extractall(Path(tmpdir).as_posix())
|
1193
|
+
# Extract tarfile for v2
|
1194
|
+
with tarfile.TarFile(
|
1195
|
+
Path(tmpdir) / "aicha_v2.tar", "r"
|
1196
|
+
) as tar_ref:
|
1197
|
+
tar_ref.extractall(
|
1198
|
+
(parcellations_dir / "AICHA_v2").as_posix()
|
1199
|
+
)
|
1200
|
+
# Cleanup after unzipping
|
1201
|
+
if (
|
1202
|
+
parcellations_dir / f"AICHA_v{version}" / "__MACOSX"
|
1203
|
+
).exists():
|
1204
|
+
shutil.rmtree(
|
1205
|
+
(
|
1206
|
+
parcellations_dir
|
1207
|
+
/ f"AICHA_v{version}"
|
1208
|
+
/ "__MACOSX"
|
1209
|
+
).as_posix()
|
1210
|
+
)
|
918
1211
|
|
919
1212
|
# Load labels
|
920
1213
|
labels = pd.read_csv(
|
@@ -959,12 +1252,13 @@ def _retrieve_shen( # noqa: C901
|
|
959
1252
|
|
960
1253
|
Raises
|
961
1254
|
------
|
1255
|
+
RuntimeError
|
1256
|
+
If there is a problem fetching files.
|
962
1257
|
ValueError
|
963
|
-
If invalid value or combination is provided for ``year`` and
|
964
|
-
|
1258
|
+
If invalid value or combination is provided for ``year`` and
|
1259
|
+
``n_rois``.
|
965
1260
|
|
966
1261
|
"""
|
967
|
-
# show parameters to user
|
968
1262
|
logger.info("Parcellation parameters:")
|
969
1263
|
logger.info(f"\tresolution: {resolution}")
|
970
1264
|
logger.info(f"\tyear: {year}")
|
@@ -1012,7 +1306,7 @@ def _retrieve_shen( # noqa: C901
|
|
1012
1306
|
f"`year = {year}` is invalid"
|
1013
1307
|
)
|
1014
1308
|
|
1015
|
-
# Define
|
1309
|
+
# Define parcellation and label file names
|
1016
1310
|
if year == 2013:
|
1017
1311
|
parcellation_fname = (
|
1018
1312
|
parcellations_dir
|
@@ -1059,27 +1353,31 @@ def _retrieve_shen( # noqa: C901
|
|
1059
1353
|
url = "https://www.nitrc.org/frs/download.php/11629/shen_368.zip"
|
1060
1354
|
|
1061
1355
|
logger.info(f"Downloading Shen {year} from {url}")
|
1356
|
+
# Store initial download in a tempdir
|
1062
1357
|
with tempfile.TemporaryDirectory() as tmpdir:
|
1063
1358
|
# Make HTTP request
|
1064
1359
|
try:
|
1065
|
-
resp =
|
1360
|
+
resp = httpx.get(url)
|
1066
1361
|
resp.raise_for_status()
|
1067
|
-
except
|
1068
|
-
raise_error(
|
1362
|
+
except httpx.HTTPError as exc:
|
1363
|
+
raise_error(
|
1364
|
+
f"Error response {exc.response.status_code} while "
|
1365
|
+
f"requesting {exc.request.url!r}",
|
1366
|
+
klass=RuntimeError,
|
1367
|
+
)
|
1069
1368
|
else:
|
1070
1369
|
if year in (2013, 2019):
|
1071
1370
|
parcellation_zip_path = Path(tmpdir) / f"Shen{year}.zip"
|
1371
|
+
# Open tempfile and write content
|
1072
1372
|
with open(parcellation_zip_path, "wb") as f:
|
1073
1373
|
f.write(resp.content)
|
1074
|
-
|
1075
|
-
# Extract zipfile
|
1374
|
+
# Unzip tempfile
|
1076
1375
|
with zipfile.ZipFile(
|
1077
1376
|
parcellation_zip_path, "r"
|
1078
1377
|
) as zip_ref:
|
1079
1378
|
zip_ref.extractall(
|
1080
1379
|
(parcellations_dir / f"Shen_{year}").as_posix()
|
1081
1380
|
)
|
1082
|
-
|
1083
1381
|
# Cleanup after unzipping
|
1084
1382
|
if (
|
1085
1383
|
parcellations_dir / f"Shen_{year}" / "__MACOSX"
|
@@ -1089,16 +1387,18 @@ def _retrieve_shen( # noqa: C901
|
|
1089
1387
|
parcellations_dir / f"Shen_{year}" / "__MACOSX"
|
1090
1388
|
).as_posix()
|
1091
1389
|
)
|
1092
|
-
|
1093
1390
|
elif year == 2015:
|
1094
1391
|
img_dir_path = parcellations_dir / "Shen_2015"
|
1392
|
+
# Create local directory if not present
|
1095
1393
|
img_dir_path.mkdir(parents=True, exist_ok=True)
|
1096
1394
|
img_path = (
|
1097
1395
|
img_dir_path
|
1098
1396
|
/ f"shen_{resolution}mm_268_parcellation.nii.gz"
|
1099
1397
|
)
|
1398
|
+
# Create local file if not present
|
1100
1399
|
img_path.touch(exist_ok=True)
|
1101
|
-
|
1400
|
+
# Open tempfile and write content
|
1401
|
+
with open(img_path, "wb") as f:
|
1102
1402
|
f.write(resp.content)
|
1103
1403
|
|
1104
1404
|
# Load labels based on year
|
@@ -1156,9 +1456,11 @@ def _retrieve_yan(
|
|
1156
1456
|
|
1157
1457
|
Raises
|
1158
1458
|
------
|
1459
|
+
RuntimeError
|
1460
|
+
If there is a problem fetching files.
|
1159
1461
|
ValueError
|
1160
1462
|
If invalid value is provided for ``n_rois``, ``yeo_networks`` or
|
1161
|
-
``kong_networks
|
1463
|
+
``kong_networks``.
|
1162
1464
|
|
1163
1465
|
"""
|
1164
1466
|
logger.info("Parcellation parameters:")
|
@@ -1188,6 +1490,8 @@ def _retrieve_yan(
|
|
1188
1490
|
f"following: {_valid_n_rois}"
|
1189
1491
|
)
|
1190
1492
|
|
1493
|
+
parcellation_fname = Path()
|
1494
|
+
parcellation_lname = Path()
|
1191
1495
|
if yeo_networks:
|
1192
1496
|
# Check yeo_networks value
|
1193
1497
|
_valid_yeo_networks = [7, 17]
|
@@ -1240,6 +1544,8 @@ def _retrieve_yan(
|
|
1240
1544
|
)
|
1241
1545
|
|
1242
1546
|
# Set URL based on network
|
1547
|
+
img_url = ""
|
1548
|
+
label_url = ""
|
1243
1549
|
if yeo_networks:
|
1244
1550
|
img_url = (
|
1245
1551
|
"https://raw.githubusercontent.com/ThomasYeoLab/CBIG/"
|
@@ -1267,38 +1573,165 @@ def _retrieve_yan(
|
|
1267
1573
|
"Kong2022_17Networks_LUT.txt"
|
1268
1574
|
)
|
1269
1575
|
|
1270
|
-
#
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1576
|
+
# Make HTTP requests
|
1577
|
+
with httpx.Client() as client:
|
1578
|
+
# Download parcellation file
|
1579
|
+
logger.info(f"Downloading Yan 2023 parcellation from {img_url}")
|
1580
|
+
try:
|
1581
|
+
img_resp = client.get(img_url)
|
1582
|
+
img_resp.raise_for_status()
|
1583
|
+
except httpx.HTTPError as exc:
|
1584
|
+
raise_error(
|
1585
|
+
f"Error response {exc.response.status_code} while "
|
1586
|
+
f"requesting {exc.request.url!r}",
|
1587
|
+
klass=RuntimeError,
|
1588
|
+
)
|
1589
|
+
else:
|
1590
|
+
parcellation_img_path = Path(parcellation_fname)
|
1591
|
+
# Create local directory if not present
|
1592
|
+
parcellation_img_path.parent.mkdir(parents=True, exist_ok=True)
|
1593
|
+
# Create local file if not present
|
1594
|
+
parcellation_img_path.touch(exist_ok=True)
|
1595
|
+
# Open file and write content
|
1596
|
+
with open(parcellation_img_path, "wb") as f:
|
1597
|
+
f.write(img_resp.content)
|
1598
|
+
# Download label file
|
1599
|
+
logger.info(f"Downloading Yan 2023 labels from {label_url}")
|
1600
|
+
try:
|
1601
|
+
label_resp = client.get(label_url)
|
1602
|
+
label_resp.raise_for_status()
|
1603
|
+
except httpx.HTTPError as exc:
|
1604
|
+
raise_error(
|
1605
|
+
f"Error response {exc.response.status_code} while "
|
1606
|
+
f"requesting {exc.request.url!r}",
|
1607
|
+
klass=RuntimeError,
|
1608
|
+
)
|
1609
|
+
else:
|
1610
|
+
parcellation_labels_path = Path(parcellation_lname)
|
1611
|
+
# Create local file if not present
|
1612
|
+
parcellation_labels_path.touch(exist_ok=True)
|
1613
|
+
# Open file and write content
|
1614
|
+
with open(parcellation_labels_path, "wb") as f:
|
1615
|
+
f.write(label_resp.content)
|
1616
|
+
|
1617
|
+
# Load label file
|
1618
|
+
labels = pd.read_csv(parcellation_lname, sep=" ", header=None)[1].to_list()
|
1619
|
+
|
1620
|
+
return parcellation_fname, labels
|
1621
|
+
|
1622
|
+
|
1623
|
+
def _retrieve_brainnetome(
|
1624
|
+
parcellations_dir: Path,
|
1625
|
+
resolution: Optional[float] = None,
|
1626
|
+
threshold: Optional[int] = None,
|
1627
|
+
) -> Tuple[Path, List[str]]:
|
1628
|
+
"""Retrieve Brainnetome parcellation.
|
1629
|
+
|
1630
|
+
Parameters
|
1631
|
+
----------
|
1632
|
+
parcellations_dir : pathlib.Path
|
1633
|
+
The path to the parcellation data directory.
|
1634
|
+
resolution : {1.0, 1.25, 2.0}, optional
|
1635
|
+
The desired resolution of the parcellation to load. If it is not
|
1636
|
+
available, the closest resolution will be loaded. Preferably, use a
|
1637
|
+
resolution higher than the desired one. By default, will load the
|
1638
|
+
highest one (default None). Available resolutions for this
|
1639
|
+
parcellation are 1mm, 1.25mm and 2mm.
|
1640
|
+
threshold : {0, 25, 50}, optional
|
1641
|
+
The threshold for the probabilistic maps of subregion (default None).
|
1642
|
+
|
1643
|
+
Returns
|
1644
|
+
-------
|
1645
|
+
pathlib.Path
|
1646
|
+
File path to the parcellation image.
|
1647
|
+
list of str
|
1648
|
+
Parcellation labels.
|
1649
|
+
|
1650
|
+
Raises
|
1651
|
+
------
|
1652
|
+
RuntimeError
|
1653
|
+
If there is a problem fetching files.
|
1654
|
+
ValueError
|
1655
|
+
If invalid value is provided for ``threshold``.
|
1656
|
+
|
1657
|
+
"""
|
1658
|
+
logger.info("Parcellation parameters:")
|
1659
|
+
logger.info(f"\tresolution: {resolution}")
|
1660
|
+
logger.info(f"\tthreshold: {threshold}")
|
1661
|
+
|
1662
|
+
# Check resolution
|
1663
|
+
_valid_resolutions = [1.0, 1.25, 2.0]
|
1664
|
+
resolution = closest_resolution(resolution, _valid_resolutions)
|
1665
|
+
|
1666
|
+
# Check threshold value
|
1667
|
+
_valid_threshold = [0, 25, 50]
|
1668
|
+
if threshold not in _valid_threshold:
|
1669
|
+
raise_error(
|
1670
|
+
f"The parameter `threshold` ({threshold}) needs to be one of the "
|
1671
|
+
f"following: {_valid_threshold}"
|
1672
|
+
)
|
1673
|
+
# Correct resolution for further stuff
|
1674
|
+
if resolution in [1.0, 2.0]:
|
1675
|
+
resolution = int(resolution)
|
1676
|
+
|
1677
|
+
parcellation_fname = (
|
1678
|
+
parcellations_dir
|
1679
|
+
/ "BNA246"
|
1680
|
+
/ f"BNA-maxprob-thr{threshold}-{resolution}mm.nii.gz"
|
1681
|
+
)
|
1682
|
+
|
1683
|
+
# Check for existence of parcellation
|
1684
|
+
if not parcellation_fname.exists():
|
1685
|
+
# Set URL
|
1686
|
+
url = f"http://neurovault.org/media/images/1625/BNA-maxprob-thr{threshold}-{resolution}mm.nii.gz"
|
1687
|
+
|
1688
|
+
logger.info(f"Downloading Brainnetome from {url}")
|
1689
|
+
# Make HTTP request
|
1274
1690
|
try:
|
1275
|
-
|
1276
|
-
|
1277
|
-
except
|
1691
|
+
resp = httpx.get(url, follow_redirects=True)
|
1692
|
+
resp.raise_for_status()
|
1693
|
+
except httpx.HTTPError as exc:
|
1278
1694
|
raise_error(
|
1279
|
-
f"
|
1695
|
+
f"Error response {exc.response.status_code} while "
|
1696
|
+
f"requesting {exc.request.url!r}",
|
1697
|
+
klass=RuntimeError,
|
1280
1698
|
)
|
1281
1699
|
else:
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
logger.info(f"Downloading Yan 2023 labels from {label_url}")
|
1289
|
-
try:
|
1290
|
-
label_resp = session.get(label_url)
|
1291
|
-
label_resp.raise_for_status()
|
1292
|
-
except (ConnectionError, ReadTimeout, HTTPError) as err:
|
1293
|
-
raise_error(f"Failed to download Yan 2023 labels due to: {err}")
|
1294
|
-
else:
|
1295
|
-
parcellation_labels_path = Path(parcellation_lname)
|
1296
|
-
parcellation_labels_path.touch(exist_ok=True)
|
1297
|
-
with open(parcellation_labels_path, "wb") as f:
|
1298
|
-
f.write(label_resp.content)
|
1700
|
+
# Create local directory if not present
|
1701
|
+
parcellation_fname.parent.mkdir(parents=True, exist_ok=True)
|
1702
|
+
# Create file if not present
|
1703
|
+
parcellation_fname.touch(exist_ok=True)
|
1704
|
+
# Open file and write bytes
|
1705
|
+
parcellation_fname.write_bytes(resp.content)
|
1299
1706
|
|
1300
|
-
# Load
|
1301
|
-
labels =
|
1707
|
+
# Load labels
|
1708
|
+
labels = (
|
1709
|
+
sorted([f"SFG_L(R)_7_{i}" for i in range(1, 8)] * 2)
|
1710
|
+
+ sorted([f"MFG_L(R)_7_{i}" for i in range(1, 8)] * 2)
|
1711
|
+
+ sorted([f"IFG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1712
|
+
+ sorted([f"OrG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1713
|
+
+ sorted([f"PrG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1714
|
+
+ sorted([f"PCL_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
1715
|
+
+ sorted([f"STG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1716
|
+
+ sorted([f"MTG_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
1717
|
+
+ sorted([f"ITG_L(R)_7_{i}" for i in range(1, 8)] * 2)
|
1718
|
+
+ sorted([f"FuG_L(R)_3_{i}" for i in range(1, 4)] * 2)
|
1719
|
+
+ sorted([f"PhG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1720
|
+
+ sorted([f"pSTS_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
1721
|
+
+ sorted([f"SPL_L(R)_5_{i}" for i in range(1, 6)] * 2)
|
1722
|
+
+ sorted([f"IPL_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1723
|
+
+ sorted([f"PCun_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
1724
|
+
+ sorted([f"PoG_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
1725
|
+
+ sorted([f"INS_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1726
|
+
+ sorted([f"CG_L(R)_7_{i}" for i in range(1, 8)] * 2)
|
1727
|
+
+ sorted([f"MVOcC _L(R)_5_{i}" for i in range(1, 6)] * 2)
|
1728
|
+
+ sorted([f"LOcC_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
1729
|
+
+ sorted([f"LOcC_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
1730
|
+
+ sorted([f"Amyg_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
1731
|
+
+ sorted([f"Hipp_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
1732
|
+
+ sorted([f"BG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
1733
|
+
+ sorted([f"Tha_L(R)_8_{i}" for i in range(1, 9)] * 2)
|
1734
|
+
)
|
1302
1735
|
|
1303
1736
|
return parcellation_fname, labels
|
1304
1737
|
|