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,177 @@
1
+ """Provide class for mask space warping via ANTs."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ import uuid
7
+ from typing import TYPE_CHECKING, Any, Optional
8
+
9
+ import nibabel as nib
10
+ import numpy as np
11
+
12
+ from ...pipeline import WorkDirManager
13
+ from ...utils import logger, raise_error, run_ext_cmd
14
+ from ..template_spaces import get_template, get_xfm
15
+
16
+
17
+ if TYPE_CHECKING:
18
+ from nibabel.nifti1 import Nifti1Image
19
+
20
+
21
+ __all__ = ["ANTsMaskWarper"]
22
+
23
+
24
+ def _get_interpolation_method(img: "Nifti1Image") -> str:
25
+ """Get correct interpolation method for `img`.
26
+
27
+ Parameters
28
+ ----------
29
+ img : nibabel.nifti1.Nifti1Image
30
+ The image.
31
+
32
+ Returns
33
+ -------
34
+ str
35
+ The interpolation method.
36
+
37
+ """
38
+ if np.array_equal(np.unique(img.get_fdata()), [0, 1]):
39
+ return "'GenericLabel[NearestNeighbor]'"
40
+ else:
41
+ return "LanczosWindowedSinc"
42
+
43
+
44
+ class ANTsMaskWarper:
45
+ """Class for mask space warping via ANTs.
46
+
47
+ This class uses ANTs ``antsApplyTransforms`` for transformation.
48
+
49
+ """
50
+
51
+ def warp(
52
+ self,
53
+ mask_name: str,
54
+ mask_img: "Nifti1Image",
55
+ src: str,
56
+ dst: str,
57
+ target_data: dict[str, Any],
58
+ warp_data: Optional[dict[str, Any]],
59
+ ) -> "Nifti1Image":
60
+ """Warp ``mask_img`` to correct space.
61
+
62
+ Parameters
63
+ ----------
64
+ mask_name : str
65
+ The name of the mask.
66
+ mask_img : nibabel.nifti1.Nifti1Image
67
+ The mask image to transform.
68
+ src : str
69
+ The data type or template space to warp from.
70
+ It should be empty string if ``dst="native"``.
71
+ dst : str
72
+ The data type or template space to warp to.
73
+ `"native"` is the only allowed data type and it uses the resampled
74
+ T1w found in ``target_data.reference``. The
75
+ ``"reference"`` key is added when :class:`.SpaceWarper` is
76
+ used or if the data is provided native space.
77
+ target_data : dict
78
+ The corresponding item of the data object to which the mask
79
+ will be applied.
80
+ warp_data : dict or None
81
+ The warp data item of the data object. The value is unused if
82
+ ``dst!="native"``.
83
+
84
+ Returns
85
+ -------
86
+ nibabel.nifti1.Nifti1Image
87
+ The transformed mask image.
88
+
89
+ Raises
90
+ ------
91
+ RuntimeError
92
+ If ``warp_data`` is None when ``dst="T1w"``.
93
+
94
+ """
95
+ # Create element-scoped tempdir so that warped mask is
96
+ # available later as nibabel stores file path reference for
97
+ # loading on computation
98
+ prefix = (
99
+ f"ants_mask_warper_{mask_name}"
100
+ f"{'' if not src else f'_from_{src}'}_to_{dst}_"
101
+ f"{uuid.uuid1()}"
102
+ )
103
+ element_tempdir = WorkDirManager().get_element_tempdir(
104
+ prefix=prefix,
105
+ )
106
+
107
+ # Native space warping
108
+ if dst == "native":
109
+ # Warp data check
110
+ if warp_data is None:
111
+ raise_error("No `warp_data` provided")
112
+ if "reference" not in target_data:
113
+ raise_error("No `reference` provided")
114
+ if "path" not in target_data["reference"]:
115
+ raise_error("No `path` provided in `reference`")
116
+
117
+ logger.debug("Using ANTs for mask transformation")
118
+
119
+ # Save existing mask image to a tempfile
120
+ prewarp_mask_path = element_tempdir / "prewarp_mask.nii.gz"
121
+ nib.save(mask_img, prewarp_mask_path)
122
+
123
+ # Create a tempfile for warped output
124
+ warped_mask_path = element_tempdir / "mask_warped.nii.gz"
125
+ # Set antsApplyTransforms command
126
+ apply_transforms_cmd = [
127
+ "antsApplyTransforms",
128
+ "-d 3",
129
+ "-e 3",
130
+ "-n 'GenericLabel[NearestNeighbor]'",
131
+ f"-i {prewarp_mask_path.resolve()}",
132
+ # use resampled reference
133
+ f"-r {target_data['reference']['path'].resolve()}",
134
+ f"-t {warp_data['path'].resolve()}",
135
+ f"-o {warped_mask_path.resolve()}",
136
+ ]
137
+ # Call antsApplyTransforms
138
+ run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
139
+
140
+ # Template space warping
141
+ else:
142
+ logger.debug(f"Using ANTs to warp mask from {src} to {dst}")
143
+
144
+ # Get xfm file
145
+ xfm_file_path = get_xfm(src=src, dst=dst)
146
+ # Get template space image
147
+ template_space_img = get_template(
148
+ space=dst,
149
+ target_img=mask_img,
150
+ extra_input=None,
151
+ )
152
+ # Save template to a tempfile
153
+ template_space_img_path = element_tempdir / f"{dst}_T1w.nii.gz"
154
+ nib.save(template_space_img, template_space_img_path)
155
+
156
+ # Save existing mask image to a tempfile
157
+ prewarp_mask_path = element_tempdir / "prewarp_mask.nii.gz"
158
+ nib.save(mask_img, prewarp_mask_path)
159
+
160
+ # Create a tempfile for warped output
161
+ warped_mask_path = element_tempdir / "mask_warped.nii.gz"
162
+ # Set antsApplyTransforms command
163
+ apply_transforms_cmd = [
164
+ "antsApplyTransforms",
165
+ "-d 3",
166
+ "-e 3",
167
+ f"-n {_get_interpolation_method(mask_img)}",
168
+ f"-i {prewarp_mask_path.resolve()}",
169
+ f"-r {template_space_img_path.resolve()}",
170
+ f"-t {xfm_file_path.resolve()}",
171
+ f"-o {warped_mask_path.resolve()}",
172
+ ]
173
+ # Call antsApplyTransforms
174
+ run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
175
+
176
+ # Load nifti
177
+ return nib.load(warped_mask_path)
@@ -0,0 +1,106 @@
1
+ """Provide class for mask space warping via FSL FLIRT."""
2
+
3
+ # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ import uuid
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ import nibabel as nib
10
+ import numpy as np
11
+
12
+ from ...pipeline import WorkDirManager
13
+ from ...utils import logger, run_ext_cmd
14
+
15
+
16
+ if TYPE_CHECKING:
17
+ from nibabel.nifti1 import Nifti1Image
18
+
19
+
20
+ __all__ = ["FSLMaskWarper"]
21
+
22
+
23
+ def _get_interpolation_method(img: "Nifti1Image") -> str:
24
+ """Get correct interpolation method for `img`.
25
+
26
+ Parameters
27
+ ----------
28
+ img : nibabel.nifti1.Nifti1Image
29
+ The image.
30
+
31
+ Returns
32
+ -------
33
+ str
34
+ The interpolation method.
35
+
36
+ """
37
+ if np.array_equal(np.unique(img.get_fdata()), [0, 1]):
38
+ return "nn"
39
+ else:
40
+ return "spline"
41
+
42
+
43
+ class FSLMaskWarper:
44
+ """Class for mask space warping via FSL FLIRT.
45
+
46
+ This class uses FSL FLIRT's ``applywarp`` for transformation.
47
+
48
+ """
49
+
50
+ def warp(
51
+ self,
52
+ mask_name: str,
53
+ mask_img: "Nifti1Image",
54
+ target_data: dict[str, Any],
55
+ warp_data: dict[str, Any],
56
+ ) -> "Nifti1Image":
57
+ """Warp ``mask_img`` to correct space.
58
+
59
+ Parameters
60
+ ----------
61
+ mask_name : str
62
+ The name of the mask.
63
+ mask_img : nibabel.nifti1.Nifti1Image
64
+ The mask image to transform.
65
+ target_data : dict
66
+ The corresponding item of the data object to which the mask
67
+ will be applied.
68
+ warp_data : dict
69
+ The warp data item of the data object.
70
+
71
+ Returns
72
+ -------
73
+ nibabel.nifti1.Nifti1Image
74
+ The transformed mask image.
75
+
76
+ """
77
+ logger.debug("Using FSL for mask transformation")
78
+
79
+ # Create element-scoped tempdir so that warped mask is
80
+ # available later as nibabel stores file path reference for
81
+ # loading on computation
82
+ element_tempdir = WorkDirManager().get_element_tempdir(
83
+ prefix=f"fsl_mask_warper_{mask_name}_{uuid.uuid1()}"
84
+ )
85
+
86
+ # Save existing mask image to a tempfile
87
+ prewarp_mask_path = element_tempdir / "prewarp_mask.nii.gz"
88
+ nib.save(mask_img, prewarp_mask_path)
89
+
90
+ # Create a tempfile for warped output
91
+ warped_mask_path = element_tempdir / "mask_warped.nii.gz"
92
+ # Set applywarp command
93
+ applywarp_cmd = [
94
+ "applywarp",
95
+ f"--interp={_get_interpolation_method(mask_img)}",
96
+ f"-i {prewarp_mask_path.resolve()}",
97
+ # use resampled reference
98
+ f"-r {target_data['reference']['path'].resolve()}",
99
+ f"-w {warp_data['path'].resolve()}",
100
+ f"-o {warped_mask_path.resolve()}",
101
+ ]
102
+ # Call applywarp
103
+ run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
104
+
105
+ # Load nifti
106
+ return nib.load(warped_mask_path)