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.
Files changed (178) hide show
  1. junifer/_version.py +14 -2
  2. junifer/api/cli.py +162 -17
  3. junifer/api/functions.py +87 -419
  4. junifer/api/parser.py +24 -0
  5. junifer/api/queue_context/__init__.py +8 -0
  6. junifer/api/queue_context/gnu_parallel_local_adapter.py +258 -0
  7. junifer/api/queue_context/htcondor_adapter.py +365 -0
  8. junifer/api/queue_context/queue_context_adapter.py +60 -0
  9. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +192 -0
  10. junifer/api/queue_context/tests/test_htcondor_adapter.py +257 -0
  11. junifer/api/res/afni/run_afni_docker.sh +6 -6
  12. junifer/api/res/ants/ResampleImage +3 -0
  13. junifer/api/res/ants/antsApplyTransforms +3 -0
  14. junifer/api/res/ants/antsApplyTransformsToPoints +3 -0
  15. junifer/api/res/ants/run_ants_docker.sh +39 -0
  16. junifer/api/res/fsl/applywarp +3 -0
  17. junifer/api/res/fsl/flirt +3 -0
  18. junifer/api/res/fsl/img2imgcoord +3 -0
  19. junifer/api/res/fsl/run_fsl_docker.sh +39 -0
  20. junifer/api/res/fsl/std2imgcoord +3 -0
  21. junifer/api/res/run_conda.sh +4 -4
  22. junifer/api/res/run_venv.sh +22 -0
  23. junifer/api/tests/data/partly_cloudy_agg_mean_tian.yml +16 -0
  24. junifer/api/tests/test_api_utils.py +21 -3
  25. junifer/api/tests/test_cli.py +232 -9
  26. junifer/api/tests/test_functions.py +211 -439
  27. junifer/api/tests/test_parser.py +1 -1
  28. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +6 -1
  29. junifer/configs/juseless/datagrabbers/camcan_vbm.py +6 -1
  30. junifer/configs/juseless/datagrabbers/ixi_vbm.py +6 -1
  31. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +8 -8
  32. junifer/configs/juseless/datagrabbers/ucla.py +44 -26
  33. junifer/configs/juseless/datagrabbers/ukb_vbm.py +6 -1
  34. junifer/data/VOIs/meta/AutobiographicalMemory_VOIs.txt +23 -0
  35. junifer/data/VOIs/meta/Power2013_MNI_VOIs.tsv +264 -0
  36. junifer/data/__init__.py +4 -0
  37. junifer/data/coordinates.py +298 -31
  38. junifer/data/masks.py +360 -28
  39. junifer/data/parcellations.py +621 -188
  40. junifer/data/template_spaces.py +190 -0
  41. junifer/data/tests/test_coordinates.py +34 -3
  42. junifer/data/tests/test_data_utils.py +1 -0
  43. junifer/data/tests/test_masks.py +202 -86
  44. junifer/data/tests/test_parcellations.py +266 -55
  45. junifer/data/tests/test_template_spaces.py +104 -0
  46. junifer/data/utils.py +4 -2
  47. junifer/datagrabber/__init__.py +1 -0
  48. junifer/datagrabber/aomic/id1000.py +111 -70
  49. junifer/datagrabber/aomic/piop1.py +116 -53
  50. junifer/datagrabber/aomic/piop2.py +116 -53
  51. junifer/datagrabber/aomic/tests/test_id1000.py +27 -27
  52. junifer/datagrabber/aomic/tests/test_piop1.py +27 -27
  53. junifer/datagrabber/aomic/tests/test_piop2.py +27 -27
  54. junifer/datagrabber/base.py +62 -10
  55. junifer/datagrabber/datalad_base.py +0 -2
  56. junifer/datagrabber/dmcc13_benchmark.py +372 -0
  57. junifer/datagrabber/hcp1200/datalad_hcp1200.py +5 -0
  58. junifer/datagrabber/hcp1200/hcp1200.py +30 -13
  59. junifer/datagrabber/pattern.py +133 -27
  60. junifer/datagrabber/pattern_datalad.py +111 -13
  61. junifer/datagrabber/tests/test_base.py +57 -6
  62. junifer/datagrabber/tests/test_datagrabber_utils.py +204 -76
  63. junifer/datagrabber/tests/test_datalad_base.py +0 -6
  64. junifer/datagrabber/tests/test_dmcc13_benchmark.py +256 -0
  65. junifer/datagrabber/tests/test_multiple.py +43 -10
  66. junifer/datagrabber/tests/test_pattern.py +125 -178
  67. junifer/datagrabber/tests/test_pattern_datalad.py +44 -25
  68. junifer/datagrabber/utils.py +151 -16
  69. junifer/datareader/default.py +36 -10
  70. junifer/external/nilearn/junifer_nifti_spheres_masker.py +6 -0
  71. junifer/markers/base.py +25 -16
  72. junifer/markers/collection.py +35 -16
  73. junifer/markers/complexity/__init__.py +27 -0
  74. junifer/markers/complexity/complexity_base.py +149 -0
  75. junifer/markers/complexity/hurst_exponent.py +136 -0
  76. junifer/markers/complexity/multiscale_entropy_auc.py +140 -0
  77. junifer/markers/complexity/perm_entropy.py +132 -0
  78. junifer/markers/complexity/range_entropy.py +136 -0
  79. junifer/markers/complexity/range_entropy_auc.py +145 -0
  80. junifer/markers/complexity/sample_entropy.py +134 -0
  81. junifer/markers/complexity/tests/test_complexity_base.py +19 -0
  82. junifer/markers/complexity/tests/test_hurst_exponent.py +69 -0
  83. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +68 -0
  84. junifer/markers/complexity/tests/test_perm_entropy.py +68 -0
  85. junifer/markers/complexity/tests/test_range_entropy.py +69 -0
  86. junifer/markers/complexity/tests/test_range_entropy_auc.py +69 -0
  87. junifer/markers/complexity/tests/test_sample_entropy.py +68 -0
  88. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +68 -0
  89. junifer/markers/complexity/weighted_perm_entropy.py +133 -0
  90. junifer/markers/falff/_afni_falff.py +153 -0
  91. junifer/markers/falff/_junifer_falff.py +142 -0
  92. junifer/markers/falff/falff_base.py +91 -84
  93. junifer/markers/falff/falff_parcels.py +61 -45
  94. junifer/markers/falff/falff_spheres.py +64 -48
  95. junifer/markers/falff/tests/test_falff_parcels.py +89 -121
  96. junifer/markers/falff/tests/test_falff_spheres.py +92 -127
  97. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +1 -0
  98. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +1 -0
  99. junifer/markers/functional_connectivity/functional_connectivity_base.py +1 -0
  100. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +46 -44
  101. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +34 -39
  102. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +40 -52
  103. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +62 -70
  104. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +99 -85
  105. junifer/markers/parcel_aggregation.py +60 -38
  106. junifer/markers/reho/_afni_reho.py +192 -0
  107. junifer/markers/reho/_junifer_reho.py +281 -0
  108. junifer/markers/reho/reho_base.py +69 -34
  109. junifer/markers/reho/reho_parcels.py +26 -16
  110. junifer/markers/reho/reho_spheres.py +23 -9
  111. junifer/markers/reho/tests/test_reho_parcels.py +93 -92
  112. junifer/markers/reho/tests/test_reho_spheres.py +88 -86
  113. junifer/markers/sphere_aggregation.py +54 -9
  114. junifer/markers/temporal_snr/temporal_snr_base.py +1 -0
  115. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +38 -37
  116. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +34 -38
  117. junifer/markers/tests/test_collection.py +43 -42
  118. junifer/markers/tests/test_ets_rss.py +29 -37
  119. junifer/markers/tests/test_parcel_aggregation.py +587 -468
  120. junifer/markers/tests/test_sphere_aggregation.py +209 -157
  121. junifer/markers/utils.py +2 -40
  122. junifer/onthefly/read_transform.py +13 -6
  123. junifer/pipeline/__init__.py +1 -0
  124. junifer/pipeline/pipeline_step_mixin.py +105 -41
  125. junifer/pipeline/registry.py +17 -0
  126. junifer/pipeline/singleton.py +45 -0
  127. junifer/pipeline/tests/test_pipeline_step_mixin.py +139 -51
  128. junifer/pipeline/tests/test_update_meta_mixin.py +1 -0
  129. junifer/pipeline/tests/test_workdir_manager.py +104 -0
  130. junifer/pipeline/update_meta_mixin.py +8 -2
  131. junifer/pipeline/utils.py +154 -15
  132. junifer/pipeline/workdir_manager.py +246 -0
  133. junifer/preprocess/__init__.py +3 -0
  134. junifer/preprocess/ants/__init__.py +4 -0
  135. junifer/preprocess/ants/ants_apply_transforms_warper.py +185 -0
  136. junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +56 -0
  137. junifer/preprocess/base.py +96 -69
  138. junifer/preprocess/bold_warper.py +265 -0
  139. junifer/preprocess/confounds/fmriprep_confound_remover.py +91 -134
  140. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +106 -111
  141. junifer/preprocess/fsl/__init__.py +4 -0
  142. junifer/preprocess/fsl/apply_warper.py +179 -0
  143. junifer/preprocess/fsl/tests/test_apply_warper.py +45 -0
  144. junifer/preprocess/tests/test_bold_warper.py +159 -0
  145. junifer/preprocess/tests/test_preprocess_base.py +6 -6
  146. junifer/preprocess/warping/__init__.py +6 -0
  147. junifer/preprocess/warping/_ants_warper.py +167 -0
  148. junifer/preprocess/warping/_fsl_warper.py +109 -0
  149. junifer/preprocess/warping/space_warper.py +213 -0
  150. junifer/preprocess/warping/tests/test_space_warper.py +198 -0
  151. junifer/stats.py +18 -4
  152. junifer/storage/base.py +9 -1
  153. junifer/storage/hdf5.py +8 -3
  154. junifer/storage/pandas_base.py +2 -1
  155. junifer/storage/sqlite.py +1 -0
  156. junifer/storage/tests/test_hdf5.py +2 -1
  157. junifer/storage/tests/test_sqlite.py +8 -8
  158. junifer/storage/tests/test_utils.py +6 -6
  159. junifer/storage/utils.py +1 -0
  160. junifer/testing/datagrabbers.py +11 -7
  161. junifer/testing/utils.py +1 -0
  162. junifer/tests/test_stats.py +2 -0
  163. junifer/utils/__init__.py +1 -0
  164. junifer/utils/helpers.py +53 -0
  165. junifer/utils/logging.py +14 -3
  166. junifer/utils/tests/test_helpers.py +35 -0
  167. {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/METADATA +59 -28
  168. junifer-0.0.4.dist-info/RECORD +257 -0
  169. {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/WHEEL +1 -1
  170. junifer/markers/falff/falff_estimator.py +0 -334
  171. junifer/markers/falff/tests/test_falff_estimator.py +0 -238
  172. junifer/markers/reho/reho_estimator.py +0 -515
  173. junifer/markers/reho/tests/test_reho_estimator.py +0 -260
  174. junifer-0.0.3.dev188.dist-info/RECORD +0 -199
  175. {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/AUTHORS.rst +0 -0
  176. {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/LICENSE.md +0 -0
  177. {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/entry_points.txt +0 -0
  178. {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/top_level.txt +0 -0
@@ -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 ..utils.logging import logger, raise_error
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, Union[Path, Dict[str, Union[ArrayLike, List[str]]]]
35
+ str, Dict[str, Union[Path, ArrayLike, List[str]]]
31
36
  ] = {
32
- "CogAC": _vois_meta_path / "CogAC_VOIs.txt",
33
- "CogAR": _vois_meta_path / "CogAR_VOIs.txt",
34
- "DMNBuckner": _vois_meta_path / "DMNBuckner_VOIs.txt",
35
- "eMDN": _vois_meta_path / "eMDN_VOIs.txt",
36
- "Empathy": _vois_meta_path / "Empathy_VOIs.txt",
37
- "eSAD": _vois_meta_path / "eSAD_VOIs.txt",
38
- "extDMN": _vois_meta_path / "extDMN_VOIs.txt",
39
- "Motor": _vois_meta_path / "Motor_VOIs.txt",
40
- "MultiTask": _vois_meta_path / "MultiTask_VOIs.txt",
41
- "PhysioStress": _vois_meta_path / "PhysioStress_VOIs.txt",
42
- "Rew": _vois_meta_path / "Rew_VOIs.txt",
43
- "Somatosensory": _vois_meta_path / "Somatosensory_VOIs.txt",
44
- "ToM": _vois_meta_path / "ToM_VOIs.txt",
45
- "VigAtt": _vois_meta_path / "VigAtt_VOIs.txt",
46
- "WM": _vois_meta_path / "WM_VOIs.txt",
47
- "Power": _vois_meta_path / "Power2011_MNI_VOIs.txt",
48
- "Dosenbach": _vois_meta_path / "Dosenbach2010_MNI_VOIs.txt",
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, not {type(coordinates)}."
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 lists (VOIs).
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 load_coordinates(name: str) -> Tuple[ArrayLike, List[str]]:
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(f"Coordinates {name} not found.")
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
- df_coords = pd.read_csv(t_coord, sep="\t", header=None)
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
- return coords, names
416
+
417
+ return coords, names, t_coord["space"]