junifer 0.0.5.dev240__py3-none-any.whl → 0.0.6__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 (279) hide show
  1. junifer/__init__.py +2 -31
  2. junifer/__init__.pyi +37 -0
  3. junifer/_version.py +9 -4
  4. junifer/api/__init__.py +3 -5
  5. junifer/api/__init__.pyi +4 -0
  6. junifer/api/decorators.py +14 -19
  7. junifer/api/functions.py +165 -109
  8. junifer/api/py.typed +0 -0
  9. junifer/api/queue_context/__init__.py +2 -4
  10. junifer/api/queue_context/__init__.pyi +5 -0
  11. junifer/api/queue_context/gnu_parallel_local_adapter.py +22 -6
  12. junifer/api/queue_context/htcondor_adapter.py +23 -6
  13. junifer/api/queue_context/py.typed +0 -0
  14. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +3 -3
  15. junifer/api/queue_context/tests/test_htcondor_adapter.py +3 -3
  16. junifer/api/tests/test_functions.py +168 -74
  17. junifer/cli/__init__.py +24 -0
  18. junifer/cli/__init__.pyi +3 -0
  19. junifer/{api → cli}/cli.py +141 -125
  20. junifer/cli/parser.py +235 -0
  21. junifer/cli/py.typed +0 -0
  22. junifer/{api → cli}/tests/test_cli.py +8 -8
  23. junifer/{api/tests/test_api_utils.py → cli/tests/test_cli_utils.py} +5 -4
  24. junifer/{api → cli}/tests/test_parser.py +2 -2
  25. junifer/{api → cli}/utils.py +6 -16
  26. junifer/configs/juseless/__init__.py +2 -2
  27. junifer/configs/juseless/__init__.pyi +3 -0
  28. junifer/configs/juseless/datagrabbers/__init__.py +2 -12
  29. junifer/configs/juseless/datagrabbers/__init__.pyi +13 -0
  30. junifer/configs/juseless/datagrabbers/ixi_vbm.py +2 -2
  31. junifer/configs/juseless/datagrabbers/py.typed +0 -0
  32. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -2
  33. junifer/configs/juseless/datagrabbers/ucla.py +4 -4
  34. junifer/configs/juseless/py.typed +0 -0
  35. junifer/conftest.py +25 -0
  36. junifer/data/__init__.py +2 -42
  37. junifer/data/__init__.pyi +29 -0
  38. junifer/data/_dispatch.py +248 -0
  39. junifer/data/coordinates/__init__.py +9 -0
  40. junifer/data/coordinates/__init__.pyi +5 -0
  41. junifer/data/coordinates/_ants_coordinates_warper.py +104 -0
  42. junifer/data/coordinates/_coordinates.py +385 -0
  43. junifer/data/coordinates/_fsl_coordinates_warper.py +81 -0
  44. junifer/data/{tests → coordinates/tests}/test_coordinates.py +26 -33
  45. junifer/data/masks/__init__.py +9 -0
  46. junifer/data/masks/__init__.pyi +6 -0
  47. junifer/data/masks/_ants_mask_warper.py +177 -0
  48. junifer/data/masks/_fsl_mask_warper.py +106 -0
  49. junifer/data/masks/_masks.py +802 -0
  50. junifer/data/{tests → masks/tests}/test_masks.py +67 -63
  51. junifer/data/parcellations/__init__.py +9 -0
  52. junifer/data/parcellations/__init__.pyi +6 -0
  53. junifer/data/parcellations/_ants_parcellation_warper.py +166 -0
  54. junifer/data/parcellations/_fsl_parcellation_warper.py +89 -0
  55. junifer/data/parcellations/_parcellations.py +1388 -0
  56. junifer/data/{tests → parcellations/tests}/test_parcellations.py +165 -295
  57. junifer/data/pipeline_data_registry_base.py +76 -0
  58. junifer/data/py.typed +0 -0
  59. junifer/data/template_spaces.py +44 -79
  60. junifer/data/tests/test_data_utils.py +1 -2
  61. junifer/data/tests/test_template_spaces.py +8 -4
  62. junifer/data/utils.py +109 -4
  63. junifer/datagrabber/__init__.py +2 -26
  64. junifer/datagrabber/__init__.pyi +27 -0
  65. junifer/datagrabber/aomic/__init__.py +2 -4
  66. junifer/datagrabber/aomic/__init__.pyi +5 -0
  67. junifer/datagrabber/aomic/id1000.py +81 -52
  68. junifer/datagrabber/aomic/piop1.py +83 -55
  69. junifer/datagrabber/aomic/piop2.py +85 -56
  70. junifer/datagrabber/aomic/py.typed +0 -0
  71. junifer/datagrabber/aomic/tests/test_id1000.py +19 -12
  72. junifer/datagrabber/aomic/tests/test_piop1.py +52 -18
  73. junifer/datagrabber/aomic/tests/test_piop2.py +50 -17
  74. junifer/datagrabber/base.py +22 -18
  75. junifer/datagrabber/datalad_base.py +71 -34
  76. junifer/datagrabber/dmcc13_benchmark.py +31 -18
  77. junifer/datagrabber/hcp1200/__init__.py +2 -3
  78. junifer/datagrabber/hcp1200/__init__.pyi +4 -0
  79. junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -3
  80. junifer/datagrabber/hcp1200/hcp1200.py +26 -15
  81. junifer/datagrabber/hcp1200/py.typed +0 -0
  82. junifer/datagrabber/hcp1200/tests/test_hcp1200.py +8 -2
  83. junifer/datagrabber/multiple.py +14 -9
  84. junifer/datagrabber/pattern.py +132 -96
  85. junifer/datagrabber/pattern_validation_mixin.py +206 -94
  86. junifer/datagrabber/py.typed +0 -0
  87. junifer/datagrabber/tests/test_datalad_base.py +27 -12
  88. junifer/datagrabber/tests/test_dmcc13_benchmark.py +28 -11
  89. junifer/datagrabber/tests/test_multiple.py +48 -2
  90. junifer/datagrabber/tests/test_pattern_datalad.py +1 -1
  91. junifer/datagrabber/tests/test_pattern_validation_mixin.py +6 -6
  92. junifer/datareader/__init__.py +2 -2
  93. junifer/datareader/__init__.pyi +3 -0
  94. junifer/datareader/default.py +6 -6
  95. junifer/datareader/py.typed +0 -0
  96. junifer/external/nilearn/__init__.py +2 -3
  97. junifer/external/nilearn/__init__.pyi +4 -0
  98. junifer/external/nilearn/junifer_connectivity_measure.py +25 -17
  99. junifer/external/nilearn/junifer_nifti_spheres_masker.py +4 -4
  100. junifer/external/nilearn/py.typed +0 -0
  101. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +17 -16
  102. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +2 -3
  103. junifer/markers/__init__.py +2 -38
  104. junifer/markers/__init__.pyi +37 -0
  105. junifer/markers/base.py +11 -14
  106. junifer/markers/brainprint.py +12 -14
  107. junifer/markers/complexity/__init__.py +2 -18
  108. junifer/markers/complexity/__init__.pyi +17 -0
  109. junifer/markers/complexity/complexity_base.py +9 -11
  110. junifer/markers/complexity/hurst_exponent.py +7 -7
  111. junifer/markers/complexity/multiscale_entropy_auc.py +7 -7
  112. junifer/markers/complexity/perm_entropy.py +7 -7
  113. junifer/markers/complexity/py.typed +0 -0
  114. junifer/markers/complexity/range_entropy.py +7 -7
  115. junifer/markers/complexity/range_entropy_auc.py +7 -7
  116. junifer/markers/complexity/sample_entropy.py +7 -7
  117. junifer/markers/complexity/tests/test_complexity_base.py +1 -1
  118. junifer/markers/complexity/tests/test_hurst_exponent.py +5 -5
  119. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +5 -5
  120. junifer/markers/complexity/tests/test_perm_entropy.py +5 -5
  121. junifer/markers/complexity/tests/test_range_entropy.py +5 -5
  122. junifer/markers/complexity/tests/test_range_entropy_auc.py +5 -5
  123. junifer/markers/complexity/tests/test_sample_entropy.py +5 -5
  124. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +5 -5
  125. junifer/markers/complexity/weighted_perm_entropy.py +7 -7
  126. junifer/markers/ets_rss.py +12 -11
  127. junifer/markers/falff/__init__.py +2 -3
  128. junifer/markers/falff/__init__.pyi +4 -0
  129. junifer/markers/falff/_afni_falff.py +38 -45
  130. junifer/markers/falff/_junifer_falff.py +16 -19
  131. junifer/markers/falff/falff_base.py +7 -11
  132. junifer/markers/falff/falff_parcels.py +9 -9
  133. junifer/markers/falff/falff_spheres.py +8 -8
  134. junifer/markers/falff/py.typed +0 -0
  135. junifer/markers/falff/tests/test_falff_spheres.py +3 -1
  136. junifer/markers/functional_connectivity/__init__.py +2 -12
  137. junifer/markers/functional_connectivity/__init__.pyi +13 -0
  138. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +9 -8
  139. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +8 -8
  140. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +7 -7
  141. junifer/markers/functional_connectivity/functional_connectivity_base.py +13 -12
  142. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +8 -8
  143. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +7 -7
  144. junifer/markers/functional_connectivity/py.typed +0 -0
  145. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +1 -2
  146. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +1 -2
  147. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +6 -6
  148. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +5 -5
  149. junifer/markers/parcel_aggregation.py +22 -17
  150. junifer/markers/py.typed +0 -0
  151. junifer/markers/reho/__init__.py +2 -3
  152. junifer/markers/reho/__init__.pyi +4 -0
  153. junifer/markers/reho/_afni_reho.py +29 -35
  154. junifer/markers/reho/_junifer_reho.py +13 -14
  155. junifer/markers/reho/py.typed +0 -0
  156. junifer/markers/reho/reho_base.py +7 -11
  157. junifer/markers/reho/reho_parcels.py +10 -10
  158. junifer/markers/reho/reho_spheres.py +9 -9
  159. junifer/markers/sphere_aggregation.py +22 -17
  160. junifer/markers/temporal_snr/__init__.py +2 -3
  161. junifer/markers/temporal_snr/__init__.pyi +4 -0
  162. junifer/markers/temporal_snr/py.typed +0 -0
  163. junifer/markers/temporal_snr/temporal_snr_base.py +11 -10
  164. junifer/markers/temporal_snr/temporal_snr_parcels.py +8 -8
  165. junifer/markers/temporal_snr/temporal_snr_spheres.py +7 -7
  166. junifer/markers/tests/test_ets_rss.py +3 -3
  167. junifer/markers/tests/test_parcel_aggregation.py +24 -24
  168. junifer/markers/tests/test_sphere_aggregation.py +6 -6
  169. junifer/markers/utils.py +3 -3
  170. junifer/onthefly/__init__.py +2 -1
  171. junifer/onthefly/_brainprint.py +138 -0
  172. junifer/onthefly/read_transform.py +5 -8
  173. junifer/pipeline/__init__.py +2 -10
  174. junifer/pipeline/__init__.pyi +13 -0
  175. junifer/{markers/collection.py → pipeline/marker_collection.py} +8 -14
  176. junifer/pipeline/pipeline_component_registry.py +294 -0
  177. junifer/pipeline/pipeline_step_mixin.py +15 -11
  178. junifer/pipeline/py.typed +0 -0
  179. junifer/{markers/tests/test_collection.py → pipeline/tests/test_marker_collection.py} +2 -3
  180. junifer/pipeline/tests/test_pipeline_component_registry.py +200 -0
  181. junifer/pipeline/tests/test_pipeline_step_mixin.py +36 -37
  182. junifer/pipeline/tests/test_update_meta_mixin.py +4 -4
  183. junifer/pipeline/tests/test_workdir_manager.py +43 -0
  184. junifer/pipeline/update_meta_mixin.py +21 -17
  185. junifer/pipeline/utils.py +6 -6
  186. junifer/pipeline/workdir_manager.py +19 -5
  187. junifer/preprocess/__init__.py +2 -10
  188. junifer/preprocess/__init__.pyi +11 -0
  189. junifer/preprocess/base.py +10 -10
  190. junifer/preprocess/confounds/__init__.py +2 -2
  191. junifer/preprocess/confounds/__init__.pyi +3 -0
  192. junifer/preprocess/confounds/fmriprep_confound_remover.py +243 -64
  193. junifer/preprocess/confounds/py.typed +0 -0
  194. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +121 -14
  195. junifer/preprocess/py.typed +0 -0
  196. junifer/preprocess/smoothing/__init__.py +2 -2
  197. junifer/preprocess/smoothing/__init__.pyi +3 -0
  198. junifer/preprocess/smoothing/_afni_smoothing.py +40 -40
  199. junifer/preprocess/smoothing/_fsl_smoothing.py +22 -32
  200. junifer/preprocess/smoothing/_nilearn_smoothing.py +35 -14
  201. junifer/preprocess/smoothing/py.typed +0 -0
  202. junifer/preprocess/smoothing/smoothing.py +11 -13
  203. junifer/preprocess/warping/__init__.py +2 -2
  204. junifer/preprocess/warping/__init__.pyi +3 -0
  205. junifer/preprocess/warping/_ants_warper.py +136 -32
  206. junifer/preprocess/warping/_fsl_warper.py +73 -22
  207. junifer/preprocess/warping/py.typed +0 -0
  208. junifer/preprocess/warping/space_warper.py +39 -11
  209. junifer/preprocess/warping/tests/test_space_warper.py +5 -9
  210. junifer/py.typed +0 -0
  211. junifer/stats.py +5 -5
  212. junifer/storage/__init__.py +2 -10
  213. junifer/storage/__init__.pyi +11 -0
  214. junifer/storage/base.py +47 -13
  215. junifer/storage/hdf5.py +95 -33
  216. junifer/storage/pandas_base.py +12 -11
  217. junifer/storage/py.typed +0 -0
  218. junifer/storage/sqlite.py +11 -11
  219. junifer/storage/tests/test_hdf5.py +86 -4
  220. junifer/storage/tests/test_sqlite.py +2 -2
  221. junifer/storage/tests/test_storage_base.py +5 -2
  222. junifer/storage/tests/test_utils.py +33 -7
  223. junifer/storage/utils.py +95 -9
  224. junifer/testing/__init__.py +2 -3
  225. junifer/testing/__init__.pyi +4 -0
  226. junifer/testing/datagrabbers.py +10 -11
  227. junifer/testing/py.typed +0 -0
  228. junifer/testing/registry.py +4 -7
  229. junifer/testing/tests/test_testing_registry.py +9 -17
  230. junifer/tests/test_stats.py +2 -2
  231. junifer/typing/__init__.py +9 -0
  232. junifer/typing/__init__.pyi +31 -0
  233. junifer/typing/_typing.py +68 -0
  234. junifer/utils/__init__.py +2 -12
  235. junifer/utils/__init__.pyi +18 -0
  236. junifer/utils/_config.py +110 -0
  237. junifer/utils/_yaml.py +16 -0
  238. junifer/utils/helpers.py +6 -6
  239. junifer/utils/logging.py +117 -8
  240. junifer/utils/py.typed +0 -0
  241. junifer/{pipeline → utils}/singleton.py +19 -14
  242. junifer/utils/tests/test_config.py +59 -0
  243. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/METADATA +43 -38
  244. junifer-0.0.6.dist-info/RECORD +350 -0
  245. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/WHEEL +1 -1
  246. junifer-0.0.6.dist-info/entry_points.txt +2 -0
  247. junifer/api/parser.py +0 -118
  248. junifer/data/coordinates.py +0 -408
  249. junifer/data/masks.py +0 -670
  250. junifer/data/parcellations.py +0 -1828
  251. junifer/pipeline/registry.py +0 -177
  252. junifer/pipeline/tests/test_registry.py +0 -150
  253. junifer-0.0.5.dev240.dist-info/RECORD +0 -275
  254. junifer-0.0.5.dev240.dist-info/entry_points.txt +0 -2
  255. /junifer/{api → cli}/tests/data/gmd_mean.yaml +0 -0
  256. /junifer/{api → cli}/tests/data/gmd_mean_htcondor.yaml +0 -0
  257. /junifer/{api → cli}/tests/data/partly_cloudy_agg_mean_tian.yml +0 -0
  258. /junifer/data/{VOIs → coordinates/VOIs}/meta/AutobiographicalMemory_VOIs.txt +0 -0
  259. /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAC_VOIs.txt +0 -0
  260. /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAR_VOIs.txt +0 -0
  261. /junifer/data/{VOIs → coordinates/VOIs}/meta/DMNBuckner_VOIs.txt +0 -0
  262. /junifer/data/{VOIs → coordinates/VOIs}/meta/Dosenbach2010_MNI_VOIs.txt +0 -0
  263. /junifer/data/{VOIs → coordinates/VOIs}/meta/Empathy_VOIs.txt +0 -0
  264. /junifer/data/{VOIs → coordinates/VOIs}/meta/Motor_VOIs.txt +0 -0
  265. /junifer/data/{VOIs → coordinates/VOIs}/meta/MultiTask_VOIs.txt +0 -0
  266. /junifer/data/{VOIs → coordinates/VOIs}/meta/PhysioStress_VOIs.txt +0 -0
  267. /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2011_MNI_VOIs.txt +0 -0
  268. /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2013_MNI_VOIs.tsv +0 -0
  269. /junifer/data/{VOIs → coordinates/VOIs}/meta/Rew_VOIs.txt +0 -0
  270. /junifer/data/{VOIs → coordinates/VOIs}/meta/Somatosensory_VOIs.txt +0 -0
  271. /junifer/data/{VOIs → coordinates/VOIs}/meta/ToM_VOIs.txt +0 -0
  272. /junifer/data/{VOIs → coordinates/VOIs}/meta/VigAtt_VOIs.txt +0 -0
  273. /junifer/data/{VOIs → coordinates/VOIs}/meta/WM_VOIs.txt +0 -0
  274. /junifer/data/{VOIs → coordinates/VOIs}/meta/eMDN_VOIs.txt +0 -0
  275. /junifer/data/{VOIs → coordinates/VOIs}/meta/eSAD_VOIs.txt +0 -0
  276. /junifer/data/{VOIs → coordinates/VOIs}/meta/extDMN_VOIs.txt +0 -0
  277. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/AUTHORS.rst +0 -0
  278. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/LICENSE.md +0 -0
  279. {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,76 @@
1
+ """Provide abstract base class for pipeline data registry."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ from abc import ABC, abstractmethod
7
+ from collections.abc import Mapping
8
+ from typing import Any
9
+
10
+ from ..utils import raise_error
11
+ from ..utils.singleton import ABCSingleton
12
+
13
+
14
+ __all__ = ["BasePipelineDataRegistry"]
15
+
16
+
17
+ class BasePipelineDataRegistry(ABC, metaclass=ABCSingleton):
18
+ """Abstract base class for pipeline data registry.
19
+
20
+ For every interface that is required, one needs to provide a concrete
21
+ implementation for this abstract class.
22
+
23
+ Attributes
24
+ ----------
25
+ registry : dict
26
+ Registry of available pipeline data.
27
+ list : dict
28
+ Keys of available pipeline data registry.
29
+
30
+ """
31
+
32
+ def __init__(self) -> None:
33
+ """Initialize the class."""
34
+ self._registry: Mapping[str, Any] = {}
35
+
36
+ @property
37
+ def data(self) -> Mapping[str, Any]:
38
+ """Get available pipeline data."""
39
+ return self._registry
40
+
41
+ @property
42
+ def list(self) -> list[str]:
43
+ """List available pipeline data keys."""
44
+ return sorted(self._registry.keys())
45
+
46
+ @abstractmethod
47
+ def register(self) -> None:
48
+ """Register data."""
49
+ raise_error(
50
+ msg="Concrete classes need to implement register().",
51
+ klass=NotImplementedError,
52
+ )
53
+
54
+ @abstractmethod
55
+ def deregister(self) -> None:
56
+ """De-register data."""
57
+ raise_error(
58
+ msg="Concrete classes need to implement deregister().",
59
+ klass=NotImplementedError,
60
+ )
61
+
62
+ @abstractmethod
63
+ def load(self) -> Any:
64
+ """Load data."""
65
+ raise_error(
66
+ msg="Concrete classes need to implement load().",
67
+ klass=NotImplementedError,
68
+ )
69
+
70
+ @abstractmethod
71
+ def get(self) -> Any:
72
+ """Get tailored data for a target."""
73
+ raise_error(
74
+ msg="Concrete classes need to implement get().",
75
+ klass=NotImplementedError,
76
+ )
junifer/data/py.typed ADDED
File without changes
@@ -4,23 +4,21 @@
4
4
  # License: AGPL
5
5
 
6
6
  from pathlib import Path
7
- from typing import Any, Dict, Optional, Union
7
+ from typing import Any, Optional, Union
8
8
 
9
- import httpx
10
9
  import nibabel as nib
11
10
  import numpy as np
11
+ from junifer_data import get
12
12
  from templateflow import api as tflow
13
13
 
14
14
  from ..utils import logger, raise_error
15
- from .utils import closest_resolution
15
+ from .utils import JUNIFER_DATA_PARAMS, closest_resolution, get_dataset_path
16
16
 
17
17
 
18
- __all__ = ["get_xfm", "get_template"]
18
+ __all__ = ["get_template", "get_xfm"]
19
19
 
20
20
 
21
- def get_xfm(
22
- src: str, dst: str, xfms_dir: Union[str, Path, None] = None
23
- ) -> Path: # pragma: no cover
21
+ def get_xfm(src: str, dst: str) -> Path: # pragma: no cover
24
22
  """Fetch warp files to convert from ``src`` to ``dst``.
25
23
 
26
24
  Parameters
@@ -29,80 +27,29 @@ def get_xfm(
29
27
  The template space to transform from.
30
28
  dst : str
31
29
  The template space to transform to.
32
- xfms_dir : str or pathlib.Path, optional
33
- Path where the retrieved transformation files are stored.
34
- The default location is "$HOME/junifer/data/xfms" (default None).
35
30
 
36
31
  Returns
37
32
  -------
38
33
  pathlib.Path
39
34
  The path to the transformation file.
40
35
 
41
- Raises
42
- ------
43
- RuntimeError
44
- If there is a problem fetching files.
45
-
46
36
  """
47
- if xfms_dir is None:
48
- xfms_dir = Path().home() / "junifer" / "data" / "xfms"
49
- logger.debug(f"Creating xfm directory at: {xfms_dir.resolve()}")
50
- # Create default junifer data directory if not present
51
- xfms_dir.mkdir(exist_ok=True, parents=True)
52
- # Convert str to Path
53
- elif not isinstance(xfms_dir, Path):
54
- xfms_dir = Path(xfms_dir)
55
-
56
- # Set local file prefix
57
- xfm_file_prefix = f"{src}_to_{dst}"
58
- # Set local file dir
59
- xfm_file_dir = xfms_dir / xfm_file_prefix
60
- # Create local directory if not present
61
- xfm_file_dir.mkdir(exist_ok=True, parents=True)
62
- # Set file name with extension
63
- xfm_file = f"{src}_to_{dst}_Composite.h5"
64
- # Set local file path
65
- xfm_file_path = xfm_file_dir / xfm_file
66
- # Check if the file exists
67
- if xfm_file_path.exists():
68
- logger.info(
69
- f"Found existing xfm file for {src} to {dst} at "
70
- f"{xfm_file_path.resolve()}"
71
- )
72
- return xfm_file_path
73
-
74
- # Set URL
75
- url = (
76
- "https://gin.g-node.org/juaml/human-template-xfms/raw/main/xfms/"
77
- f"{xfm_file_prefix}/{xfm_file}"
37
+ # Set file path to retrieve
38
+ xfm_file_path = Path(f"xfms/{src}_to_{dst}/{src}_to_{dst}_Composite.h5")
39
+ # Retrieve file
40
+ return get(
41
+ file_path=xfm_file_path,
42
+ dataset_path=get_dataset_path(),
43
+ **JUNIFER_DATA_PARAMS,
78
44
  )
79
- # Create the file before proceeding
80
- xfm_file_path.touch()
81
-
82
- logger.info(f"Downloading xfm file for {src} to {dst} from {url}")
83
- # Steam response
84
- with httpx.stream("GET", url) as resp:
85
- try:
86
- resp.raise_for_status()
87
- except httpx.HTTPError as exc:
88
- raise_error(
89
- f"Error response {exc.response.status_code} while "
90
- f"requesting {exc.request.url!r}",
91
- klass=RuntimeError,
92
- )
93
- else:
94
- with open(xfm_file_path, "ab") as f:
95
- for chunk in resp.iter_bytes():
96
- f.write(chunk)
97
-
98
- return xfm_file_path
99
45
 
100
46
 
101
47
  def get_template(
102
48
  space: str,
103
- target_data: Dict[str, Any],
104
- extra_input: Optional[Dict[str, Any]] = None,
49
+ target_img: nib.Nifti1Image,
50
+ extra_input: Optional[dict[str, Any]] = None,
105
51
  template_type: str = "T1w",
52
+ resolution: Optional[Union[int, "str"]] = None,
106
53
  ) -> nib.Nifti1Image:
107
54
  """Get template for the space, tailored for the target image.
108
55
 
@@ -110,14 +57,18 @@ def get_template(
110
57
  ----------
111
58
  space : str
112
59
  The name of the template space.
113
- target_data : dict
114
- The corresponding item of the data object for which the template space
115
- will be loaded.
60
+ target_img : Nifti1Image
61
+ The corresponding image for which the template space will be loaded.
62
+ This is used to obtain the best matching resolution.
116
63
  extra_input : dict, optional
117
64
  The other fields in the data object. Useful for accessing other data
118
65
  types (default None).
119
66
  template_type : {"T1w", "brain", "gm", "wm", "csf"}, optional
120
67
  The template type to retrieve (default "T1w").
68
+ resolution : int or "highest", optional
69
+ The resolution of the template to fetch. If None, the closest
70
+ resolution to the target image is used (default None). If "highest",
71
+ the highest resolution is used.
121
72
 
122
73
  Returns
123
74
  -------
@@ -127,7 +78,8 @@ def get_template(
127
78
  Raises
128
79
  ------
129
80
  ValueError
130
- If ``space`` or ``template_type`` is invalid.
81
+ If ``space`` or ``template_type`` is invalid or
82
+ if ``resolution`` is not at int or "highest".
131
83
  RuntimeError
132
84
  If required template is not found.
133
85
 
@@ -140,19 +92,30 @@ def get_template(
140
92
  if template_type not in ["T1w", "brain", "gm", "wm", "csf"]:
141
93
  raise_error(f"Unknown template type: {template_type}")
142
94
 
143
- # Get the min of the voxels sizes and use it as the resolution
144
- target_img = target_data["data"]
145
- resolution = np.min(target_img.header.get_zooms()[:3]).astype(int)
95
+ if isinstance(resolution, str) and resolution != "highest":
96
+ raise_error(
97
+ "Invalid resolution value. Must be an integer or 'highest'"
98
+ )
146
99
 
147
100
  # Fetch available resolutions for the template
148
101
  available_resolutions = [
149
102
  int(min(val["zooms"]))
150
103
  for val in tflow.get_metadata(space)["res"].values()
151
104
  ]
105
+
106
+ # Get the min of the voxels sizes and use it as the resolution
107
+ if resolution is None:
108
+ resolution = np.min(target_img.header.get_zooms()[:3]).astype(int)
109
+ elif resolution == "highest":
110
+ resolution = 0
111
+
152
112
  # Use the closest resolution if desired resolution is not found
153
113
  resolution = closest_resolution(resolution, available_resolutions)
154
114
 
155
- logger.info(f"Downloading template {space} in resolution {resolution}")
115
+ logger.info(
116
+ f"Downloading template {space} ({template_type} in "
117
+ f"resolution {resolution})"
118
+ )
156
119
  # Retrieve template
157
120
  try:
158
121
  suffix = None
@@ -185,9 +148,11 @@ def get_template(
185
148
  )
186
149
  except Exception: # noqa: BLE001
187
150
  raise_error(
188
- f"Template {space} ({template_type}) with resolution {resolution} "
189
- "not found",
151
+ msg=(
152
+ f"Template {space} ({template_type}) with resolution "
153
+ f"{resolution}) not found"
154
+ ),
190
155
  klass=RuntimeError,
191
156
  )
192
157
  else:
193
- return nib.load(template_path) # type: ignore
158
+ return nib.load(template_path)
@@ -3,7 +3,6 @@
3
3
  # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
4
  # License: AGPL
5
5
 
6
- from typing import List
7
6
 
8
7
  import numpy as np
9
8
  import pytest
@@ -24,7 +23,7 @@ from junifer.data.utils import closest_resolution
24
23
  ],
25
24
  )
26
25
  def test_closest_resolution(
27
- resolution: float, valid_resolutions: List[float], expected: float
26
+ resolution: float, valid_resolutions: list[float], expected: float
28
27
  ):
29
28
  """Test closest_resolution.
30
29
 
@@ -61,7 +61,9 @@ def test_get_template(template_type: str) -> None:
61
61
  bold = element_data["BOLD"]
62
62
  # Get tailored parcellation
63
63
  tailored_template = get_template(
64
- space=bold["space"], target_data=bold, template_type=template_type
64
+ space=bold["space"],
65
+ target_img=bold["data"],
66
+ template_type=template_type,
65
67
  )
66
68
  assert isinstance(tailored_template, nib.Nifti1Image)
67
69
 
@@ -74,7 +76,7 @@ def test_get_template_invalid_space() -> None:
74
76
  vbm_gm = element_data["VBM_GM"]
75
77
  # Get tailored parcellation
76
78
  with pytest.raises(ValueError, match="Unknown template space:"):
77
- get_template(space="andromeda", target_data=vbm_gm)
79
+ get_template(space="andromeda", target_img=vbm_gm["data"])
78
80
 
79
81
 
80
82
  def test_get_template_invalid_template_type() -> None:
@@ -87,7 +89,7 @@ def test_get_template_invalid_template_type() -> None:
87
89
  with pytest.raises(ValueError, match="Unknown template type:"):
88
90
  get_template(
89
91
  space=vbm_gm["space"],
90
- target_data=vbm_gm,
92
+ target_img=vbm_gm["data"],
91
93
  template_type="xenon",
92
94
  )
93
95
 
@@ -100,5 +102,7 @@ def test_get_template_closest_resolution() -> None:
100
102
  vbm_gm = element_data["VBM_GM"]
101
103
  # Change header resolution to fetch closest resolution
102
104
  element_data["VBM_GM"]["data"].header.set_zooms((3, 3, 3))
103
- template = get_template(space=vbm_gm["space"], target_data=vbm_gm)
105
+ template = get_template(
106
+ space=vbm_gm["space"], target_img=vbm_gm["data"]
107
+ )
104
108
  assert isinstance(template, nib.Nifti1Image)
junifer/data/utils.py CHANGED
@@ -1,18 +1,43 @@
1
1
  """Provide utilities for data module."""
2
2
 
3
- from typing import List, Optional, Union
3
+ # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
+ # Synchon Mandal <s.mandal@fz-juelich.de>
5
+ # License: AGPL
6
+
7
+ from collections.abc import MutableMapping
8
+ from pathlib import Path
9
+ from typing import Optional, Union
4
10
 
5
11
  import numpy as np
6
12
 
7
- from ..utils.logging import logger
13
+ from ..utils import config, logger, raise_error
14
+
15
+
16
+ __all__ = [
17
+ "JUNIFER_DATA_HEXSHA",
18
+ "JUNIFER_DATA_PARAMS",
19
+ "JUNIFER_DATA_VERSION",
20
+ "closest_resolution",
21
+ "get_dataset_path",
22
+ "get_native_warper",
23
+ ]
24
+
8
25
 
26
+ # junifer-data version constant
27
+ JUNIFER_DATA_VERSION = "2"
9
28
 
10
- __all__ = ["closest_resolution"]
29
+ # junifer-data hexsha constant
30
+ JUNIFER_DATA_HEXSHA = "015712689254c052fa64bca19f0f2da342e664ac"
31
+
32
+ JUNIFER_DATA_PARAMS = {
33
+ "tag": JUNIFER_DATA_VERSION,
34
+ "hexsha": JUNIFER_DATA_HEXSHA,
35
+ }
11
36
 
12
37
 
13
38
  def closest_resolution(
14
39
  resolution: Optional[Union[float, int]],
15
- valid_resolution: Union[List[float], List[int], np.ndarray],
40
+ valid_resolution: Union[list[float], list[int], np.ndarray],
16
41
  ) -> Union[float, int]:
17
42
  """Find the closest resolution.
18
43
 
@@ -45,3 +70,83 @@ def closest_resolution(
45
70
  closest = np.min(valid_resolution)
46
71
 
47
72
  return closest
73
+
74
+
75
+ def get_native_warper(
76
+ target_data: MutableMapping,
77
+ other_data: MutableMapping,
78
+ inverse: bool = False,
79
+ ) -> dict:
80
+ """Get correct warping specification for native space.
81
+
82
+ Parameters
83
+ ----------
84
+ target_data : dict
85
+ The target data from the pipeline data object.
86
+ other_data : dict
87
+ The other data in the pipeline data object.
88
+ inverse : bool, optional
89
+ Whether to get the inverse warping specification (default False).
90
+
91
+ Returns
92
+ -------
93
+ dict
94
+ The correct warping specification.
95
+
96
+ Raises
97
+ ------
98
+ RuntimeError
99
+ If no warper or multiple possible warpers are found.
100
+
101
+ """
102
+ # Get possible warpers
103
+ possible_warpers = []
104
+ for entry in other_data["Warp"]:
105
+ if not inverse:
106
+ if (
107
+ entry["src"] == target_data["prewarp_space"]
108
+ and entry["dst"] == "native"
109
+ ):
110
+ possible_warpers.append(entry)
111
+ else:
112
+ if (
113
+ entry["dst"] == target_data["prewarp_space"]
114
+ and entry["src"] == "native"
115
+ ):
116
+ possible_warpers.append(entry)
117
+
118
+ # Check for no warper
119
+ if not possible_warpers:
120
+ raise_error(
121
+ klass=RuntimeError,
122
+ msg="Could not find correct warping specification",
123
+ )
124
+
125
+ # Check for multiple possible warpers
126
+ if len(possible_warpers) > 1:
127
+ raise_error(
128
+ klass=RuntimeError,
129
+ msg=(
130
+ "Cannot proceed as multiple warping specification found, "
131
+ "adjust either the DataGrabber or the working space: "
132
+ f"{possible_warpers}"
133
+ ),
134
+ )
135
+
136
+ return possible_warpers[0]
137
+
138
+
139
+ def get_dataset_path() -> Optional[Path]:
140
+ """Get junifer-data dataset path.
141
+
142
+ Returns
143
+ -------
144
+ pathlib.Path or None
145
+ Path to the dataset or None.
146
+
147
+ """
148
+ return (
149
+ Path(config.get("data.location"))
150
+ if config.get("data.location") is not None
151
+ else None
152
+ )
@@ -5,31 +5,7 @@
5
5
  # Synchon Mandal <s.mandal@fz-juelich.de>
6
6
  # License: AGPL
7
7
 
8
+ import lazy_loader as lazy
8
9
 
9
- # These 4 need to be in this order, otherwise it is a circular import
10
- from .base import BaseDataGrabber
11
- from .datalad_base import DataladDataGrabber
12
- from .pattern import PatternDataGrabber
13
- from .pattern_datalad import PatternDataladDataGrabber
14
10
 
15
- from .aomic import DataladAOMICID1000, DataladAOMICPIOP1, DataladAOMICPIOP2
16
- from .hcp1200 import HCP1200, DataladHCP1200
17
- from .multiple import MultipleDataGrabber
18
- from .dmcc13_benchmark import DMCC13Benchmark
19
-
20
- from .pattern_validation_mixin import PatternValidationMixin
21
-
22
- __all__ = [
23
- "BaseDataGrabber",
24
- "DataladDataGrabber",
25
- "PatternDataGrabber",
26
- "PatternDataladDataGrabber",
27
- "DataladAOMICID1000",
28
- "DataladAOMICPIOP1",
29
- "DataladAOMICPIOP2",
30
- "HCP1200",
31
- "DataladHCP1200",
32
- "MultipleDataGrabber",
33
- "DMCC13Benchmark",
34
- "PatternValidationMixin",
35
- ]
11
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,27 @@
1
+ __all__ = [
2
+ "BaseDataGrabber",
3
+ "DataladDataGrabber",
4
+ "PatternDataGrabber",
5
+ "PatternDataladDataGrabber",
6
+ "DataladAOMICID1000",
7
+ "DataladAOMICPIOP1",
8
+ "DataladAOMICPIOP2",
9
+ "HCP1200",
10
+ "DataladHCP1200",
11
+ "MultipleDataGrabber",
12
+ "DMCC13Benchmark",
13
+ "PatternValidationMixin",
14
+ ]
15
+
16
+ # These 4 need to be in this order, otherwise it is a circular import
17
+ from .base import BaseDataGrabber
18
+ from .datalad_base import DataladDataGrabber
19
+ from .pattern import PatternDataGrabber
20
+ from .pattern_datalad import PatternDataladDataGrabber
21
+
22
+ from .aomic import DataladAOMICID1000, DataladAOMICPIOP1, DataladAOMICPIOP2
23
+ from .hcp1200 import HCP1200, DataladHCP1200
24
+ from .multiple import MultipleDataGrabber
25
+ from .dmcc13_benchmark import DMCC13Benchmark
26
+
27
+ from .pattern_validation_mixin import PatternValidationMixin
@@ -4,9 +4,7 @@
4
4
  # Leonard Sasse <l.sasse@fz-juelich.de>
5
5
  # License: AGPL
6
6
 
7
- from .id1000 import DataladAOMICID1000
8
- from .piop1 import DataladAOMICPIOP1
9
- from .piop2 import DataladAOMICPIOP2
7
+ import lazy_loader as lazy
10
8
 
11
9
 
12
- __all__ = ["DataladAOMICID1000", "DataladAOMICPIOP1", "DataladAOMICPIOP2"]
10
+ __getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
@@ -0,0 +1,5 @@
1
+ __all__ = ["DataladAOMICID1000", "DataladAOMICPIOP1", "DataladAOMICPIOP2"]
2
+
3
+ from .id1000 import DataladAOMICID1000
4
+ from .piop1 import DataladAOMICPIOP1
5
+ from .piop2 import DataladAOMICPIOP2