junifer 0.0.3.dev188__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.dev188.dist-info → junifer-0.0.4.dist-info}/METADATA +59 -28
- junifer-0.0.4.dist-info/RECORD +257 -0
- {junifer-0.0.3.dev188.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.dev188.dist-info/RECORD +0 -199
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/top_level.txt +0 -0
junifer/data/coordinates.py
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
"""Provide functions for list of coordinates."""
|
2
2
|
|
3
3
|
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
|
+
# Synchon Mandal <s.mandal@fz-juelich.de>
|
4
5
|
# License: AGPL
|
5
6
|
|
6
7
|
import typing
|
7
8
|
from pathlib import Path
|
8
|
-
from typing import Dict, List, Optional, Tuple, Union
|
9
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
9
10
|
|
10
11
|
import numpy as np
|
11
12
|
import pandas as pd
|
12
13
|
from numpy.typing import ArrayLike
|
13
14
|
|
14
|
-
from ..
|
15
|
+
from ..pipeline import WorkDirManager
|
16
|
+
from ..utils import logger, raise_error, run_ext_cmd, warn_with_log
|
15
17
|
|
16
18
|
|
17
19
|
# Path to the VOIs
|
@@ -23,29 +25,95 @@ _vois_meta_path = _vois_path / "meta"
|
|
23
25
|
# A dictionary containing all supported coordinates and their respective file
|
24
26
|
# or data.
|
25
27
|
|
28
|
+
# Each entry is a dictionary that must contain at least the following keys:
|
29
|
+
# * 'space': the coordinates' space (e.g., 'MNI')
|
30
|
+
|
26
31
|
# The built-in coordinates are files that are shipped with the package in the
|
27
32
|
# data/VOIs directory. The user can also register their own coordinates, which
|
28
33
|
# will be stored as numpy arrays in the dictionary.
|
29
34
|
_available_coordinates: Dict[
|
30
|
-
str,
|
35
|
+
str, Dict[str, Union[Path, ArrayLike, List[str]]]
|
31
36
|
] = {
|
32
|
-
"CogAC":
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
"
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
"
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
"
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
"
|
37
|
+
"CogAC": {
|
38
|
+
"path": _vois_meta_path / "CogAC_VOIs.txt",
|
39
|
+
"space": "MNI",
|
40
|
+
},
|
41
|
+
"CogAR": {
|
42
|
+
"path": _vois_meta_path / "CogAR_VOIs.txt",
|
43
|
+
"space": "MNI",
|
44
|
+
},
|
45
|
+
"DMNBuckner": {
|
46
|
+
"path": _vois_meta_path / "DMNBuckner_VOIs.txt",
|
47
|
+
"space": "MNI",
|
48
|
+
},
|
49
|
+
"eMDN": {
|
50
|
+
"path": _vois_meta_path / "eMDN_VOIs.txt",
|
51
|
+
"space": "MNI",
|
52
|
+
},
|
53
|
+
"Empathy": {
|
54
|
+
"path": _vois_meta_path / "Empathy_VOIs.txt",
|
55
|
+
"space": "MNI",
|
56
|
+
},
|
57
|
+
"eSAD": {
|
58
|
+
"path": _vois_meta_path / "eSAD_VOIs.txt",
|
59
|
+
"space": "MNI",
|
60
|
+
},
|
61
|
+
"extDMN": {
|
62
|
+
"path": _vois_meta_path / "extDMN_VOIs.txt",
|
63
|
+
"space": "MNI",
|
64
|
+
},
|
65
|
+
"Motor": {
|
66
|
+
"path": _vois_meta_path / "Motor_VOIs.txt",
|
67
|
+
"space": "MNI",
|
68
|
+
},
|
69
|
+
"MultiTask": {
|
70
|
+
"path": _vois_meta_path / "MultiTask_VOIs.txt",
|
71
|
+
"space": "MNI",
|
72
|
+
},
|
73
|
+
"PhysioStress": {
|
74
|
+
"path": _vois_meta_path / "PhysioStress_VOIs.txt",
|
75
|
+
"space": "MNI",
|
76
|
+
},
|
77
|
+
"Rew": {
|
78
|
+
"path": _vois_meta_path / "Rew_VOIs.txt",
|
79
|
+
"space": "MNI",
|
80
|
+
},
|
81
|
+
"Somatosensory": {
|
82
|
+
"path": _vois_meta_path / "Somatosensory_VOIs.txt",
|
83
|
+
"space": "MNI",
|
84
|
+
},
|
85
|
+
"ToM": {
|
86
|
+
"path": _vois_meta_path / "ToM_VOIs.txt",
|
87
|
+
"space": "MNI",
|
88
|
+
},
|
89
|
+
"VigAtt": {
|
90
|
+
"path": _vois_meta_path / "VigAtt_VOIs.txt",
|
91
|
+
"space": "MNI",
|
92
|
+
},
|
93
|
+
"WM": {
|
94
|
+
"path": _vois_meta_path / "WM_VOIs.txt",
|
95
|
+
"space": "MNI",
|
96
|
+
},
|
97
|
+
"Power": {
|
98
|
+
"path": _vois_meta_path / "Power2011_MNI_VOIs.txt",
|
99
|
+
"space": "MNI",
|
100
|
+
},
|
101
|
+
"Power2011": {
|
102
|
+
"path": _vois_meta_path / "Power2011_MNI_VOIs.txt",
|
103
|
+
"space": "MNI",
|
104
|
+
},
|
105
|
+
"Dosenbach": {
|
106
|
+
"path": _vois_meta_path / "Dosenbach2010_MNI_VOIs.txt",
|
107
|
+
"space": "MNI",
|
108
|
+
},
|
109
|
+
"Power2013": {
|
110
|
+
"path": _vois_meta_path / "Power2013_MNI_VOIs.tsv",
|
111
|
+
"space": "MNI",
|
112
|
+
},
|
113
|
+
"AutobiographicalMemory": {
|
114
|
+
"path": _vois_meta_path / "AutobiographicalMemory_VOIs.txt",
|
115
|
+
"space": "MNI",
|
116
|
+
},
|
49
117
|
}
|
50
118
|
|
51
119
|
|
@@ -53,9 +121,10 @@ def register_coordinates(
|
|
53
121
|
name: str,
|
54
122
|
coordinates: ArrayLike,
|
55
123
|
voi_names: List[str],
|
124
|
+
space: str,
|
56
125
|
overwrite: Optional[bool] = False,
|
57
126
|
) -> None:
|
58
|
-
"""Register coordinates.
|
127
|
+
"""Register a custom user coordinates.
|
59
128
|
|
60
129
|
Parameters
|
61
130
|
----------
|
@@ -68,12 +137,26 @@ def register_coordinates(
|
|
68
137
|
z-coordinates).
|
69
138
|
voi_names : list of str
|
70
139
|
The names of the VOIs.
|
140
|
+
space : str
|
141
|
+
The space of the coordinates, for e.g., "MNI".
|
71
142
|
overwrite : bool, optional
|
72
143
|
If True, overwrite an existing list of coordinates with the same name.
|
73
144
|
Does not apply to built-in coordinates (default False).
|
145
|
+
|
146
|
+
Raises
|
147
|
+
------
|
148
|
+
ValueError
|
149
|
+
If the coordinates name is already registered and overwrite is set to
|
150
|
+
False or if the coordinates name is a built-in coordinates or if the
|
151
|
+
``coordinates`` is not a 2D array or if coordinate value does not have
|
152
|
+
3 components or if the ``voi_names`` shape does not match the
|
153
|
+
``coordinates`` shape.
|
154
|
+
TypeError
|
155
|
+
If ``coordinates`` is not a ``numpy.ndarray``.
|
156
|
+
|
74
157
|
"""
|
75
158
|
if name in _available_coordinates:
|
76
|
-
if isinstance(_available_coordinates[name], Path):
|
159
|
+
if isinstance(_available_coordinates[name].get("path"), Path):
|
77
160
|
raise_error(
|
78
161
|
f"Coordinates {name} already registered as built-in "
|
79
162
|
"coordinates."
|
@@ -88,7 +171,8 @@ def register_coordinates(
|
|
88
171
|
|
89
172
|
if not isinstance(coordinates, np.ndarray):
|
90
173
|
raise_error(
|
91
|
-
f"Coordinates must be a numpy.ndarray
|
174
|
+
f"Coordinates must be a `numpy.ndarray`, not {type(coordinates)}.",
|
175
|
+
klass=TypeError,
|
92
176
|
)
|
93
177
|
if coordinates.ndim != 2:
|
94
178
|
raise_error(
|
@@ -100,27 +184,178 @@ def register_coordinates(
|
|
100
184
|
)
|
101
185
|
if len(voi_names) != coordinates.shape[0]:
|
102
186
|
raise_error(
|
103
|
-
f"Length of voi_names ({len(voi_names)}) does not match the "
|
104
|
-
f"number of coordinates ({coordinates.shape[0]})."
|
187
|
+
f"Length of `voi_names` ({len(voi_names)}) does not match the "
|
188
|
+
f"number of `coordinates` ({coordinates.shape[0]})."
|
105
189
|
)
|
106
190
|
_available_coordinates[name] = {
|
107
191
|
"coords": coordinates,
|
108
192
|
"voi_names": voi_names,
|
193
|
+
"space": space,
|
109
194
|
}
|
110
195
|
|
111
196
|
|
112
197
|
def list_coordinates() -> List[str]:
|
113
|
-
"""List all the available coordinates
|
198
|
+
"""List all the available coordinates (VOIs).
|
114
199
|
|
115
200
|
Returns
|
116
201
|
-------
|
117
202
|
list of str
|
118
203
|
A list with all available coordinates names.
|
204
|
+
|
119
205
|
"""
|
120
206
|
return sorted(_available_coordinates.keys())
|
121
207
|
|
122
208
|
|
123
|
-
def
|
209
|
+
def get_coordinates(
|
210
|
+
coords: str,
|
211
|
+
target_data: Dict[str, Any],
|
212
|
+
extra_input: Optional[Dict[str, Any]] = None,
|
213
|
+
) -> Tuple[ArrayLike, List[str]]:
|
214
|
+
"""Get coordinates, tailored for the target image.
|
215
|
+
|
216
|
+
Parameters
|
217
|
+
----------
|
218
|
+
coords : str
|
219
|
+
The name of the coordinates.
|
220
|
+
target_data : dict
|
221
|
+
The corresponding item of the data object to which the coordinates
|
222
|
+
will be applied.
|
223
|
+
extra_input : dict, optional
|
224
|
+
The other fields in the data object. Useful for accessing other data
|
225
|
+
kinds that needs to be used in the computation of coordinates
|
226
|
+
(default None).
|
227
|
+
|
228
|
+
Returns
|
229
|
+
-------
|
230
|
+
numpy.ndarray
|
231
|
+
The coordinates.
|
232
|
+
list of str
|
233
|
+
The names of the VOIs.
|
234
|
+
|
235
|
+
Raises
|
236
|
+
------
|
237
|
+
RuntimeError
|
238
|
+
If warp / transformation file extension is not ".mat" or ".h5".
|
239
|
+
ValueError
|
240
|
+
If ``extra_input`` is None when ``target_data``'s space is native.
|
241
|
+
|
242
|
+
"""
|
243
|
+
# Load the coordinates
|
244
|
+
seeds, labels, _ = load_coordinates(name=coords)
|
245
|
+
|
246
|
+
# Transform coordinate if target data is native
|
247
|
+
if target_data["space"] == "native":
|
248
|
+
# Check for extra inputs
|
249
|
+
if extra_input is None:
|
250
|
+
raise_error(
|
251
|
+
"No extra input provided, requires `Warp` and `T1w` "
|
252
|
+
"data types in particular for transformation to "
|
253
|
+
f"{target_data['space']} space for further computation."
|
254
|
+
)
|
255
|
+
|
256
|
+
# Create component-scoped tempdir
|
257
|
+
tempdir = WorkDirManager().get_tempdir(prefix="coordinates")
|
258
|
+
|
259
|
+
# Create element-scoped tempdir so that transformed coordinates is
|
260
|
+
# available later as numpy stores file path reference for
|
261
|
+
# loading on computation
|
262
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
263
|
+
prefix="coordinates"
|
264
|
+
)
|
265
|
+
|
266
|
+
# Check for warp file type to use correct tool
|
267
|
+
warp_file_ext = extra_input["Warp"]["path"].suffix
|
268
|
+
if warp_file_ext == ".mat":
|
269
|
+
# Save existing coordinates to a component-scoped tempfile
|
270
|
+
pretransform_coordinates_path = (
|
271
|
+
tempdir / "pretransform_coordinates.txt"
|
272
|
+
)
|
273
|
+
np.savetxt(pretransform_coordinates_path, seeds)
|
274
|
+
|
275
|
+
# Create an element-scoped tempfile for transformed coordinates
|
276
|
+
# output
|
277
|
+
transformed_coords_path = (
|
278
|
+
element_tempdir / "coordinates_transformed.txt"
|
279
|
+
)
|
280
|
+
|
281
|
+
logger.debug("Using FSL for coordinates transformation")
|
282
|
+
# Set img2imgcoord command
|
283
|
+
img2imgcoord_cmd = [
|
284
|
+
"cat",
|
285
|
+
f"{pretransform_coordinates_path.resolve()}",
|
286
|
+
"| img2imgcoord -mm",
|
287
|
+
f"-src {target_data['path'].resolve()}",
|
288
|
+
f"-dest {target_data['reference_path'].resolve()}",
|
289
|
+
f"-warp {extra_input['Warp']['path'].resolve()}",
|
290
|
+
f"> {transformed_coords_path.resolve()};",
|
291
|
+
f"sed -i 1d {transformed_coords_path.resolve()}",
|
292
|
+
]
|
293
|
+
# Call img2imgcoord
|
294
|
+
run_ext_cmd(name="img2imgcoord", cmd=img2imgcoord_cmd)
|
295
|
+
|
296
|
+
# Load coordinates
|
297
|
+
seeds = np.loadtxt(transformed_coords_path)
|
298
|
+
|
299
|
+
elif warp_file_ext == ".h5":
|
300
|
+
# Save existing coordinates to a component-scoped tempfile
|
301
|
+
pretransform_coordinates_path = (
|
302
|
+
tempdir / "pretransform_coordinates.csv"
|
303
|
+
)
|
304
|
+
np.savetxt(
|
305
|
+
pretransform_coordinates_path,
|
306
|
+
seeds,
|
307
|
+
delimiter=",",
|
308
|
+
# Add header while saving to make ANTs work
|
309
|
+
header="x,y,z",
|
310
|
+
)
|
311
|
+
|
312
|
+
# Create an element-scoped tempfile for transformed coordinates
|
313
|
+
# output
|
314
|
+
transformed_coords_path = (
|
315
|
+
element_tempdir / "coordinates_transformed.csv"
|
316
|
+
)
|
317
|
+
|
318
|
+
logger.debug("Using ANTs for coordinates transformation")
|
319
|
+
# Set antsApplyTransformsToPoints command
|
320
|
+
apply_transforms_to_points_cmd = [
|
321
|
+
"antsApplyTransformsToPoints",
|
322
|
+
"-d 3",
|
323
|
+
"-p 1",
|
324
|
+
"-f 0",
|
325
|
+
f"-i {pretransform_coordinates_path.resolve()}",
|
326
|
+
f"-o {transformed_coords_path.resolve()}",
|
327
|
+
f"-t {extra_input['Warp']['path'].resolve()};",
|
328
|
+
]
|
329
|
+
# Call antsApplyTransformsToPoints
|
330
|
+
run_ext_cmd(
|
331
|
+
name="antsApplyTransformsToPoints",
|
332
|
+
cmd=apply_transforms_to_points_cmd,
|
333
|
+
)
|
334
|
+
|
335
|
+
# Load coordinates
|
336
|
+
seeds = np.loadtxt(
|
337
|
+
# Skip header when reading
|
338
|
+
transformed_coords_path,
|
339
|
+
delimiter=",",
|
340
|
+
skiprows=1,
|
341
|
+
)
|
342
|
+
|
343
|
+
else:
|
344
|
+
raise_error(
|
345
|
+
msg=(
|
346
|
+
"Unknown warp / transformation file extension: "
|
347
|
+
f"{warp_file_ext}"
|
348
|
+
),
|
349
|
+
klass=RuntimeError,
|
350
|
+
)
|
351
|
+
|
352
|
+
# Delete tempdir
|
353
|
+
WorkDirManager().delete_tempdir(tempdir)
|
354
|
+
|
355
|
+
return seeds, labels
|
356
|
+
|
357
|
+
|
358
|
+
def load_coordinates(name: str) -> Tuple[ArrayLike, List[str], str]:
|
124
359
|
"""Load coordinates.
|
125
360
|
|
126
361
|
Parameters
|
@@ -134,12 +369,43 @@ def load_coordinates(name: str) -> Tuple[ArrayLike, List[str]]:
|
|
134
369
|
The coordinates.
|
135
370
|
list of str
|
136
371
|
The names of the VOIs.
|
372
|
+
str
|
373
|
+
The space of the coordinates.
|
374
|
+
|
375
|
+
Raises
|
376
|
+
------
|
377
|
+
ValueError
|
378
|
+
If ``name`` is invalid.
|
379
|
+
|
380
|
+
Warns
|
381
|
+
-----
|
382
|
+
DeprecationWarning
|
383
|
+
If ``Power`` is provided as the ``name``.
|
384
|
+
|
137
385
|
"""
|
386
|
+
# Check for valid coordinates name
|
138
387
|
if name not in _available_coordinates:
|
139
|
-
raise_error(
|
388
|
+
raise_error(
|
389
|
+
f"Coordinates {name} not found. "
|
390
|
+
f"Valid options are: {list_coordinates()}"
|
391
|
+
)
|
392
|
+
|
393
|
+
# Put up deprecation notice
|
394
|
+
if name == "Power":
|
395
|
+
warn_with_log(
|
396
|
+
msg=(
|
397
|
+
"`Power` has been replaced with `Power2011` and will be "
|
398
|
+
"removed in the next release. For now, it's available for "
|
399
|
+
"backward compatibility."
|
400
|
+
),
|
401
|
+
category=DeprecationWarning,
|
402
|
+
)
|
403
|
+
|
404
|
+
# Load coordinates
|
140
405
|
t_coord = _available_coordinates[name]
|
141
|
-
if isinstance(t_coord, Path):
|
142
|
-
|
406
|
+
if isinstance(t_coord.get("path"), Path):
|
407
|
+
# Load via pandas
|
408
|
+
df_coords = pd.read_csv(t_coord["path"], sep="\t", header=None)
|
143
409
|
coords = df_coords.iloc[:, [0, 1, 2]].to_numpy()
|
144
410
|
names = list(df_coords.iloc[:, [3]].values[:, 0])
|
145
411
|
else:
|
@@ -147,4 +413,5 @@ def load_coordinates(name: str) -> Tuple[ArrayLike, List[str]]:
|
|
147
413
|
coords = typing.cast(ArrayLike, coords)
|
148
414
|
names = t_coord["voi_names"]
|
149
415
|
names = typing.cast(List[str], names)
|
150
|
-
|
416
|
+
|
417
|
+
return coords, names, t_coord["space"]
|