junifer 0.0.7.dev332__py3-none-any.whl → 0.0.8__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 (293) hide show
  1. junifer/_version.py +9 -19
  2. junifer/api/__init__.pyi +17 -2
  3. junifer/api/functions.py +222 -39
  4. junifer/api/queue_context/__init__.pyi +11 -3
  5. junifer/api/queue_context/gnu_parallel_local_adapter.py +81 -120
  6. junifer/api/queue_context/htcondor_adapter.py +118 -181
  7. junifer/api/queue_context/queue_context_adapter.py +51 -7
  8. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +10 -35
  9. junifer/api/queue_context/tests/test_htcondor_adapter.py +13 -50
  10. junifer/api/res/fsl/run_fsl_docker.sh +4 -0
  11. junifer/api/tests/test_functions.py +863 -36
  12. junifer/cli/__init__.py +1 -7
  13. junifer/cli/cli.py +41 -22
  14. junifer/cli/parser.py +23 -147
  15. junifer/cli/tests/data/gmd_mean.yaml +6 -6
  16. junifer/cli/tests/data/gmd_mean_htcondor.yaml +6 -5
  17. junifer/cli/tests/data/partly_cloudy_agg_mean_tian.yml +1 -0
  18. junifer/cli/tests/test_cli.py +120 -4
  19. junifer/cli/tests/test_cli_utils.py +23 -6
  20. junifer/cli/utils.py +14 -9
  21. junifer/configs/__init__.py +5 -0
  22. junifer/configs/__init__.pyi +0 -0
  23. junifer/configs/juseless/datagrabbers/__init__.pyi +4 -2
  24. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +18 -24
  25. junifer/configs/juseless/datagrabbers/camcan_vbm.py +20 -27
  26. junifer/configs/juseless/datagrabbers/ixi_vbm.py +44 -52
  27. junifer/configs/juseless/datagrabbers/tests/test_ixi_vbm.py +0 -7
  28. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -19
  29. junifer/configs/juseless/datagrabbers/ucla.py +111 -104
  30. junifer/configs/juseless/datagrabbers/ukb_vbm.py +19 -25
  31. junifer/configs/py.typed +0 -0
  32. junifer/conftest.py +63 -24
  33. junifer/data/_dispatch.py +9 -19
  34. junifer/data/coordinates/_ants_coordinates_warper.py +4 -1
  35. junifer/data/coordinates/_coordinates.py +51 -6
  36. junifer/data/coordinates/_fsl_coordinates_warper.py +4 -1
  37. junifer/data/coordinates/tests/test_coordinates.py +15 -3
  38. junifer/data/coordinates/tests/test_coordinates_names.py +37 -0
  39. junifer/data/maps/_ants_maps_warper.py +18 -12
  40. junifer/data/maps/_fsl_maps_warper.py +7 -3
  41. junifer/data/maps/_maps.py +12 -8
  42. junifer/data/maps/tests/test_maps.py +13 -21
  43. junifer/data/masks/_ants_mask_warper.py +18 -12
  44. junifer/data/masks/_fsl_mask_warper.py +7 -3
  45. junifer/data/masks/_masks.py +15 -12
  46. junifer/data/masks/tests/test_masks.py +20 -17
  47. junifer/data/parcellations/_ants_parcellation_warper.py +18 -12
  48. junifer/data/parcellations/_fsl_parcellation_warper.py +7 -5
  49. junifer/data/parcellations/_parcellations.py +125 -91
  50. junifer/data/parcellations/tests/test_parcellations.py +122 -1
  51. junifer/data/template_spaces.py +117 -50
  52. junifer/data/tests/test_template_spaces.py +10 -7
  53. junifer/data/utils.py +10 -8
  54. junifer/datagrabber/__init__.pyi +38 -5
  55. junifer/datagrabber/aomic/__init__.pyi +8 -1
  56. junifer/datagrabber/aomic/_types.py +22 -0
  57. junifer/datagrabber/aomic/id1000.py +193 -183
  58. junifer/datagrabber/aomic/piop1.py +215 -259
  59. junifer/datagrabber/aomic/piop2.py +211 -244
  60. junifer/datagrabber/aomic/tests/test_id1000.py +70 -53
  61. junifer/datagrabber/aomic/tests/test_piop1.py +98 -102
  62. junifer/datagrabber/aomic/tests/test_piop2.py +88 -85
  63. junifer/datagrabber/base.py +253 -79
  64. junifer/datagrabber/datalad_base.py +127 -76
  65. junifer/datagrabber/dmcc13_benchmark.py +204 -289
  66. junifer/datagrabber/hcp1200/__init__.pyi +7 -2
  67. junifer/datagrabber/hcp1200/datalad_hcp1200.py +61 -40
  68. junifer/datagrabber/hcp1200/hcp1200.py +151 -164
  69. junifer/datagrabber/hcp1200/tests/test_hcp1200.py +116 -128
  70. junifer/datagrabber/multiple.py +183 -63
  71. junifer/datagrabber/pattern.py +358 -227
  72. junifer/datagrabber/pattern_datalad.py +84 -124
  73. junifer/datagrabber/pattern_validation_mixin.py +22 -50
  74. junifer/datagrabber/tests/test_base.py +17 -4
  75. junifer/datagrabber/tests/test_datalad_base.py +20 -27
  76. junifer/datagrabber/tests/test_dmcc13_benchmark.py +110 -122
  77. junifer/datagrabber/tests/test_multiple.py +293 -31
  78. junifer/datagrabber/tests/test_pattern.py +577 -18
  79. junifer/datagrabber/tests/test_pattern_datalad.py +177 -107
  80. junifer/datagrabber/tests/test_pattern_validation_mixin.py +9 -51
  81. junifer/datareader/default.py +8 -5
  82. junifer/datareader/tests/test_default_reader.py +1 -1
  83. junifer/external/BrainPrint/.codecov.yml +2 -0
  84. junifer/external/BrainPrint/.codespellignore +2 -0
  85. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/bug-report.md +45 -0
  86. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/documentation.md +12 -0
  87. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/feature-request.md +24 -0
  88. junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/questions-help-support.md +28 -0
  89. junifer/external/BrainPrint/.github/dependabot.yml +6 -0
  90. junifer/external/BrainPrint/.github/workflows/build.yml +53 -0
  91. junifer/external/BrainPrint/.github/workflows/code-style.yml +44 -0
  92. junifer/external/BrainPrint/.github/workflows/doc.yml +65 -0
  93. junifer/external/BrainPrint/.github/workflows/publish.yml +31 -0
  94. junifer/external/BrainPrint/.github/workflows/pytest.yml +54 -0
  95. junifer/external/BrainPrint/.gitignore +123 -0
  96. junifer/external/BrainPrint/LICENSE +21 -0
  97. junifer/external/BrainPrint/README.md +108 -0
  98. junifer/external/BrainPrint/brainprint/cli/__init__.py +11 -0
  99. junifer/external/BrainPrint/brainprint/cli/help_text.py +169 -0
  100. junifer/external/BrainPrint/brainprint/cli/parser.py +150 -0
  101. junifer/external/BrainPrint/brainprint/cli/utils.py +14 -0
  102. junifer/external/BrainPrint/brainprint/commands/__init__.py +0 -0
  103. junifer/external/BrainPrint/brainprint/commands/sys_info.py +18 -0
  104. junifer/external/BrainPrint/brainprint/utils/tests/__init__.py +0 -0
  105. junifer/external/BrainPrint/brainprint/utils/tests/test_config.py +33 -0
  106. junifer/external/BrainPrint/doc/Makefile +20 -0
  107. junifer/external/BrainPrint/doc/_static/.gitkeep +0 -0
  108. junifer/external/BrainPrint/doc/_templates/autosummary/class.rst +10 -0
  109. junifer/external/BrainPrint/doc/_templates/autosummary/function.rst +8 -0
  110. junifer/external/BrainPrint/doc/_templates/autosummary/module.rst +6 -0
  111. junifer/external/BrainPrint/doc/api/index.rst +11 -0
  112. junifer/external/BrainPrint/doc/conf.py +272 -0
  113. junifer/external/BrainPrint/doc/index.rst +140 -0
  114. junifer/external/BrainPrint/doc/links.inc +36 -0
  115. junifer/external/BrainPrint/doc/make.bat +35 -0
  116. junifer/external/BrainPrint/doc/references.bib +23 -0
  117. junifer/external/BrainPrint/pyproject.toml +170 -0
  118. junifer/external/BrainPrint/setup.py +3 -0
  119. junifer/external/__init__.py +5 -0
  120. junifer/external/__init__.pyi +0 -0
  121. junifer/external/nilearn/junifer_connectivity_measure.py +95 -39
  122. junifer/external/nilearn/junifer_nifti_spheres_masker.py +226 -67
  123. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +39 -27
  124. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +231 -21
  125. junifer/external/py.typed +0 -0
  126. junifer/markers/__init__.pyi +6 -3
  127. junifer/markers/base.py +126 -76
  128. junifer/markers/brainprint.py +30 -39
  129. junifer/markers/complexity/__init__.py +20 -3
  130. junifer/markers/complexity/complexity_base.py +40 -41
  131. junifer/markers/complexity/hurst_exponent.py +22 -35
  132. junifer/markers/complexity/multiscale_entropy_auc.py +53 -41
  133. junifer/markers/complexity/perm_entropy.py +22 -36
  134. junifer/markers/complexity/range_entropy.py +22 -35
  135. junifer/markers/complexity/range_entropy_auc.py +27 -36
  136. junifer/markers/complexity/sample_entropy.py +22 -36
  137. junifer/markers/complexity/tests/test_complexity_registry.py +48 -0
  138. junifer/markers/complexity/tests/test_hurst_exponent.py +47 -36
  139. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +50 -36
  140. junifer/markers/complexity/tests/test_perm_entropy.py +48 -36
  141. junifer/markers/complexity/tests/test_range_entropy.py +47 -36
  142. junifer/markers/complexity/tests/test_range_entropy_auc.py +47 -36
  143. junifer/markers/complexity/tests/test_sample_entropy.py +48 -36
  144. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +50 -36
  145. junifer/markers/complexity/weighted_perm_entropy.py +22 -36
  146. junifer/markers/ets_rss.py +35 -31
  147. junifer/markers/falff/__init__.pyi +7 -1
  148. junifer/markers/falff/_afni_falff.py +22 -9
  149. junifer/markers/falff/_junifer_falff.py +47 -31
  150. junifer/markers/falff/falff_base.py +80 -60
  151. junifer/markers/falff/falff_maps.py +17 -41
  152. junifer/markers/falff/falff_parcels.py +26 -51
  153. junifer/markers/falff/falff_spheres.py +34 -61
  154. junifer/markers/falff/tests/test_falff_maps.py +20 -23
  155. junifer/markers/falff/tests/test_falff_mask.py +99 -0
  156. junifer/markers/falff/tests/test_falff_parcels.py +14 -10
  157. junifer/markers/falff/tests/test_falff_spheres.py +14 -10
  158. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +32 -34
  159. junifer/markers/functional_connectivity/edge_functional_connectivity_maps.py +12 -25
  160. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +19 -34
  161. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +24 -44
  162. junifer/markers/functional_connectivity/functional_connectivity_base.py +50 -40
  163. junifer/markers/functional_connectivity/functional_connectivity_maps.py +12 -25
  164. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +19 -34
  165. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +24 -45
  166. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +21 -22
  167. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_maps.py +8 -7
  168. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +8 -4
  169. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +8 -4
  170. junifer/markers/functional_connectivity/tests/test_functional_connectivity_maps.py +8 -7
  171. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +9 -5
  172. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +8 -12
  173. junifer/markers/maps_aggregation.py +77 -55
  174. junifer/markers/parcel_aggregation.py +91 -70
  175. junifer/markers/reho/__init__.pyi +7 -1
  176. junifer/markers/reho/_afni_reho.py +23 -14
  177. junifer/markers/reho/_junifer_reho.py +112 -123
  178. junifer/markers/reho/reho_base.py +115 -37
  179. junifer/markers/reho/reho_maps.py +18 -32
  180. junifer/markers/reho/reho_parcels.py +26 -41
  181. junifer/markers/reho/reho_spheres.py +33 -54
  182. junifer/markers/reho/tests/test_junifer_reho.py +178 -0
  183. junifer/markers/reho/tests/test_reho_maps.py +27 -25
  184. junifer/markers/reho/tests/test_reho_parcels.py +28 -22
  185. junifer/markers/reho/tests/test_reho_spheres.py +16 -12
  186. junifer/markers/sphere_aggregation.py +88 -72
  187. junifer/markers/temporal_snr/temporal_snr_base.py +32 -30
  188. junifer/markers/temporal_snr/temporal_snr_maps.py +9 -18
  189. junifer/markers/temporal_snr/temporal_snr_parcels.py +19 -27
  190. junifer/markers/temporal_snr/temporal_snr_spheres.py +27 -41
  191. junifer/markers/temporal_snr/tests/test_temporal_snr_base.py +2 -2
  192. junifer/markers/temporal_snr/tests/test_temporal_snr_maps.py +17 -22
  193. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +11 -3
  194. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +9 -9
  195. junifer/markers/tests/test_brainprint.py +10 -9
  196. junifer/markers/tests/test_ets_rss.py +7 -3
  197. junifer/markers/tests/test_lost_regions.py +279 -0
  198. junifer/markers/tests/test_maps_aggregation.py +67 -114
  199. junifer/markers/tests/test_marker_utils.py +40 -1
  200. junifer/markers/tests/test_markers_base.py +275 -6
  201. junifer/markers/tests/test_parcel_aggregation.py +91 -53
  202. junifer/markers/tests/test_sphere_aggregation.py +53 -47
  203. junifer/markers/utils.py +267 -5
  204. junifer/onthefly/__init__.py +2 -3
  205. junifer/onthefly/__init__.pyi +4 -0
  206. junifer/onthefly/_brainprint.py +3 -5
  207. junifer/onthefly/py.typed +0 -0
  208. junifer/onthefly/read_transform.py +11 -9
  209. junifer/onthefly/tests/test_read_transform.py +7 -7
  210. junifer/pipeline/__init__.pyi +4 -2
  211. junifer/pipeline/marker_collection.py +14 -10
  212. junifer/pipeline/pipeline_component_registry.py +25 -6
  213. junifer/pipeline/pipeline_step_mixin.py +5 -12
  214. junifer/pipeline/tests/test_data_object_dumper.py +10 -10
  215. junifer/pipeline/tests/test_marker_collection.py +145 -168
  216. junifer/pipeline/tests/test_pipeline_component_registry.py +52 -8
  217. junifer/pipeline/tests/test_pipeline_step_mixin.py +12 -26
  218. junifer/pipeline/tests/test_update_meta_mixin.py +4 -5
  219. junifer/pipeline/tests/test_workdir_manager.py +39 -0
  220. junifer/pipeline/update_meta_mixin.py +13 -10
  221. junifer/pipeline/utils.py +22 -17
  222. junifer/pipeline/workdir_manager.py +41 -32
  223. junifer/preprocess/__init__.pyi +9 -4
  224. junifer/preprocess/_temporal_filter.py +34 -33
  225. junifer/preprocess/_temporal_slicer.py +17 -30
  226. junifer/preprocess/base.py +99 -55
  227. junifer/preprocess/confounds/__init__.pyi +10 -2
  228. junifer/preprocess/confounds/fmriprep_confound_remover.py +87 -109
  229. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +64 -45
  230. junifer/preprocess/smoothing/__init__.pyi +2 -2
  231. junifer/preprocess/smoothing/_afni_smoothing.py +8 -7
  232. junifer/preprocess/smoothing/_fsl_smoothing.py +6 -4
  233. junifer/preprocess/smoothing/_nilearn_smoothing.py +7 -5
  234. junifer/preprocess/smoothing/smoothing.py +59 -40
  235. junifer/preprocess/smoothing/tests/test_smoothing.py +28 -8
  236. junifer/preprocess/tests/test_preprocess_base.py +17 -9
  237. junifer/preprocess/tests/test_temporal_filter.py +37 -16
  238. junifer/preprocess/tests/test_temporal_slicer.py +12 -12
  239. junifer/preprocess/warping/__init__.pyi +2 -2
  240. junifer/preprocess/warping/_ants_warper.py +56 -40
  241. junifer/preprocess/warping/_fsl_warper.py +49 -36
  242. junifer/preprocess/warping/_utils.py +106 -0
  243. junifer/preprocess/warping/space_warper.py +99 -98
  244. junifer/preprocess/warping/tests/test_space_warper.py +46 -41
  245. junifer/preprocess/warping/tests/test_warping_utils.py +163 -0
  246. junifer/stats.py +46 -34
  247. junifer/storage/__init__.pyi +8 -4
  248. junifer/storage/base.py +129 -64
  249. junifer/storage/hdf5.py +307 -199
  250. junifer/storage/pandas_base.py +46 -24
  251. junifer/storage/sqlite.py +99 -62
  252. junifer/storage/tests/data/collected_h5io_fork.hdf5 +0 -0
  253. junifer/storage/tests/test_hdf5.py +147 -12
  254. junifer/storage/tests/test_repeated_storage.py +293 -0
  255. junifer/storage/tests/test_sqlite.py +9 -20
  256. junifer/storage/tests/test_storage_base.py +18 -21
  257. junifer/storage/tests/test_utils.py +83 -22
  258. junifer/storage/utils.py +58 -38
  259. junifer/testing/__init__.pyi +2 -2
  260. junifer/testing/datagrabbers.py +480 -44
  261. junifer/testing/registry.py +12 -0
  262. junifer/testing/tests/test_adhdtesting_datagrabber.py +24 -0
  263. junifer/testing/tests/test_oasisvmbtesting_datagrabber.py +5 -0
  264. junifer/testing/tests/test_partlycloudytesting_datagrabber.py +52 -0
  265. junifer/testing/tests/test_spmauditory_datagrabber.py +16 -0
  266. junifer/testing/tests/test_testing_registry.py +31 -0
  267. junifer/testing/tests/test_testing_utils.py +35 -0
  268. junifer/testing/tests/test_uclacnpvbmtesting_datagrabber.py +81 -0
  269. junifer/testing/utils.py +33 -1
  270. junifer/tests/test_stats.py +1 -5
  271. junifer/typing/__init__.pyi +2 -0
  272. junifer/typing/_typing.py +34 -19
  273. junifer/typing/py.typed +0 -0
  274. junifer/utils/__init__.pyi +10 -1
  275. junifer/utils/_config.py +1 -2
  276. junifer/utils/helpers.py +68 -2
  277. junifer/utils/logging.py +31 -7
  278. junifer/utils/tests/test_helpers.py +36 -1
  279. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/METADATA +43 -35
  280. junifer-0.0.8.dist-info/RECORD +402 -0
  281. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/WHEEL +1 -1
  282. junifer/cli/tests/test_parser.py +0 -280
  283. junifer/external/h5io/h5io/__init__.py +0 -15
  284. junifer/external/h5io/h5io/_h5io.py +0 -767
  285. junifer/external/h5io/h5io/_version.py +0 -1
  286. junifer/external/h5io/h5io/chunked_array.py +0 -94
  287. junifer/external/h5io/h5io/chunked_list.py +0 -65
  288. junifer/markers/complexity/__init__.pyi +0 -17
  289. junifer-0.0.7.dev332.dist-info/RECORD +0 -353
  290. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/entry_points.txt +0 -0
  291. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/AUTHORS.rst +0 -0
  292. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
  293. {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/top_level.txt +0 -0
@@ -12,9 +12,11 @@ import nibabel as nib
12
12
  import numpy as np
13
13
 
14
14
  from ...data import get_template, get_xfm
15
- from ...pipeline import WorkDirManager
16
- from ...typing import Dependencies, ExternalDependencies
17
- from ...utils import logger, raise_error, run_ext_cmd
15
+ from ...pipeline import ExtDep, WorkDirManager
16
+ from ...typing import Dependencies, ExternalDependencies, SpaceLike
17
+ from ...utils import raise_error, run_ext_cmd
18
+ from ..base import logger
19
+ from ._utils import warp_in_chunks
18
20
 
19
21
 
20
22
  __all__ = ["ANTsWarper"]
@@ -30,7 +32,7 @@ class ANTsWarper:
30
32
 
31
33
  _EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
32
34
  {
33
- "name": "ants",
35
+ "name": ExtDep.ANTs,
34
36
  "commands": ["ResampleImage", "antsApplyTransforms"],
35
37
  },
36
38
  ]
@@ -41,7 +43,7 @@ class ANTsWarper:
41
43
  self,
42
44
  input: dict[str, Any],
43
45
  extra_input: dict[str, Any],
44
- reference: str,
46
+ reference: SpaceLike,
45
47
  ) -> dict[str, Any]:
46
48
  """Preprocess using ANTs.
47
49
 
@@ -52,7 +54,7 @@ class ANTsWarper:
52
54
  extra_input : dict
53
55
  The other fields in the Junifer Data object. Should have ``T1w``
54
56
  and ``Warp`` data types.
55
- reference : str
57
+ reference : str or Enum
56
58
  The data type or template space to use as reference for warping.
57
59
 
58
60
  Returns
@@ -92,7 +94,7 @@ class ANTsWarper:
92
94
 
93
95
  # Create a tempfile for resampled reference output
94
96
  resample_image_out_path = (
95
- element_tempdir / "resampled_reference.nii.gz"
97
+ element_tempdir / "resampled_reference.nii"
96
98
  )
97
99
  # Set ResampleImage command
98
100
  resample_image_cmd = [
@@ -108,21 +110,27 @@ class ANTsWarper:
108
110
  run_ext_cmd(name="ResampleImage", cmd=resample_image_cmd)
109
111
 
110
112
  # Create a tempfile for warped output
111
- apply_transforms_out_path = element_tempdir / "warped_data.nii.gz"
112
- # Set antsApplyTransforms command
113
- apply_transforms_cmd = [
114
- "antsApplyTransforms",
115
- "-d 3",
116
- "-e 3",
117
- "-n LanczosWindowedSinc",
118
- f"-i {input['path'].resolve()}",
119
- # use resampled reference
120
- f"-r {resample_image_out_path.resolve()}",
121
- f"-t {warp_file_path.resolve()}",
122
- f"-o {apply_transforms_out_path.resolve()}",
123
- ]
124
- # Call antsApplyTransforms
125
- run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
113
+ apply_transforms_out_path = element_tempdir / "warped_data.nii"
114
+ # Call antsApplyTransforms in single precision, in chunks of
115
+ # volumes
116
+ warp_in_chunks(
117
+ name="antsApplyTransforms",
118
+ input_path=input["path"].resolve(),
119
+ output_path=apply_transforms_out_path.resolve(),
120
+ cmd=lambda in_path, out_path: [
121
+ "antsApplyTransforms",
122
+ "-d 3",
123
+ "-e 3",
124
+ "-n LanczosWindowedSinc",
125
+ "--float 1",
126
+ f"-i {in_path}",
127
+ # use resampled reference
128
+ f"-r {resample_image_out_path.resolve()}",
129
+ f"-t {warp_file_path.resolve()}",
130
+ f"-o {out_path}",
131
+ ],
132
+ tempdir=element_tempdir,
133
+ )
126
134
 
127
135
  logger.debug("Updating warped data")
128
136
  input.update(
@@ -144,7 +152,7 @@ class ANTsWarper:
144
152
  if input.get("mask") is not None:
145
153
  # Create a tempfile for warped mask output
146
154
  apply_transforms_mask_out_path = (
147
- element_tempdir / "warped_mask.nii.gz"
155
+ element_tempdir / "warped_mask.nii"
148
156
  )
149
157
  # Set antsApplyTransforms command
150
158
  apply_transforms_mask_cmd = [
@@ -152,6 +160,7 @@ class ANTsWarper:
152
160
  "-d 3",
153
161
  "-e 3",
154
162
  "-n 'GenericLabel[NearestNeighbor]'",
163
+ "--float 1",
155
164
  f"-i {input['mask']['path'].resolve()}",
156
165
  # use resampled reference
157
166
  f"-r {input['reference']['path'].resolve()}",
@@ -211,7 +220,7 @@ class ANTsWarper:
211
220
  # Create a tempfile for resampled reference output
212
221
  ref_path = (
213
222
  element_tempdir
214
- / f"resampled_reference-{reference}.nii.gz"
223
+ / f"resampled_reference-{reference}.nii"
215
224
  )
216
225
  # Set ResampleImage command
217
226
  resample_image_cmd = [
@@ -242,27 +251,33 @@ class ANTsWarper:
242
251
  extra_input=None,
243
252
  )
244
253
  # Save template
245
- ref_path = element_tempdir / f"{reference}_T1w.nii.gz"
254
+ ref_path = element_tempdir / f"{reference}_T1w.nii"
246
255
  nib.save(template_space_img, ref_path)
247
256
 
248
257
  # Create a tempfile for warped output
249
258
  warped_output_path = element_tempdir / (
250
- f"warped_data_from_{input_space}_to_{reference}.nii.gz"
259
+ f"warped_data_from_{input_space}_to_{reference}.nii"
251
260
  )
252
261
 
253
- # Set antsApplyTransforms command
254
- apply_transforms_cmd = [
255
- "antsApplyTransforms",
256
- "-d 3",
257
- "-e 3",
258
- "-n LanczosWindowedSinc",
259
- f"-i {input['path'].resolve()}",
260
- f"-r {ref_path.resolve()}",
261
- f"-t {xfm_file_path.resolve()}",
262
- f"-o {warped_output_path.resolve()}",
263
- ]
264
- # Call antsApplyTransforms
265
- run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
262
+ # Call antsApplyTransforms in single precision, in chunks of
263
+ # volumes
264
+ warp_in_chunks(
265
+ name="antsApplyTransforms",
266
+ input_path=input["path"].resolve(),
267
+ output_path=warped_output_path.resolve(),
268
+ cmd=lambda in_path, out_path: [
269
+ "antsApplyTransforms",
270
+ "-d 3",
271
+ "-e 3",
272
+ "-n LanczosWindowedSinc",
273
+ "--float 1",
274
+ f"-i {in_path}",
275
+ f"-r {ref_path.resolve()}",
276
+ f"-t {xfm_file_path.resolve()}",
277
+ f"-o {out_path}",
278
+ ],
279
+ tempdir=element_tempdir,
280
+ )
266
281
 
267
282
  logger.debug("Updating warped data")
268
283
  input.update(
@@ -286,7 +301,7 @@ class ANTsWarper:
286
301
  logger.debug("Warping associated mask")
287
302
  # Create a tempfile for warped mask output
288
303
  apply_transforms_mask_out_path = element_tempdir / (
289
- f"warped_mask_from_{input_space}_to_{reference}.nii.gz"
304
+ f"warped_mask_from_{input_space}_to_{reference}.nii"
290
305
  )
291
306
  # Set antsApplyTransforms command
292
307
  apply_transforms_mask_cmd = [
@@ -294,6 +309,7 @@ class ANTsWarper:
294
309
  "-d 3",
295
310
  "-e 3",
296
311
  "-n 'GenericLabel[NearestNeighbor]'",
312
+ "--float 1",
297
313
  f"-i {input['mask']['path'].resolve()}",
298
314
  # use resampled reference or original
299
315
  f"-r {ref_path.resolve()}",
@@ -11,9 +11,11 @@ from typing import (
11
11
  import nibabel as nib
12
12
  import numpy as np
13
13
 
14
- from ...pipeline import WorkDirManager
14
+ from ...pipeline import ExtDep, WorkDirManager
15
15
  from ...typing import Dependencies, ExternalDependencies
16
- from ...utils import logger, raise_error, run_ext_cmd
16
+ from ...utils import raise_error, run_ext_cmd
17
+ from ..base import logger
18
+ from ._utils import warp_in_chunks
17
19
 
18
20
 
19
21
  __all__ = ["FSLWarper"]
@@ -29,7 +31,7 @@ class FSLWarper:
29
31
 
30
32
  _EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
31
33
  {
32
- "name": "fsl",
34
+ "name": ExtDep.FSL,
33
35
  "commands": ["flirt", "applywarp"],
34
36
  },
35
37
  ]
@@ -92,9 +94,10 @@ class FSLWarper:
92
94
  )
93
95
 
94
96
  # Create a tempfile for resampled reference output
95
- flirt_out_path = element_tempdir / "resampled_reference.nii.gz"
97
+ flirt_out_path = element_tempdir / "resampled_reference.nii"
96
98
  # Set flirt command
97
99
  flirt_cmd = [
100
+ "FSLOUTPUTTYPE=NIFTI",
98
101
  "flirt",
99
102
  "-interp spline",
100
103
  f"-in {extra_input['T1w']['path'].resolve()}",
@@ -106,19 +109,24 @@ class FSLWarper:
106
109
  run_ext_cmd(name="flirt", cmd=flirt_cmd)
107
110
 
108
111
  # Create a tempfile for warped output
109
- applywarp_out_path = element_tempdir / "warped_data.nii.gz"
110
- # Set applywarp command
111
- applywarp_cmd = [
112
- "applywarp",
113
- "--interp=spline",
114
- f"-i {input['path'].resolve()}",
115
- # use resampled reference
116
- f"-r {flirt_out_path.resolve()}",
117
- f"-w {warp_file_path.resolve()}",
118
- f"-o {applywarp_out_path.resolve()}",
119
- ]
120
- # Call applywarp
121
- run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
112
+ applywarp_out_path = element_tempdir / "warped_data.nii"
113
+ # Call applywarp in chunks of volumes
114
+ warp_in_chunks(
115
+ name="applywarp",
116
+ input_path=input["path"].resolve(),
117
+ output_path=applywarp_out_path.resolve(),
118
+ cmd=lambda in_path, out_path: [
119
+ "FSLOUTPUTTYPE=NIFTI",
120
+ "applywarp",
121
+ "--interp=spline",
122
+ f"-i {in_path}",
123
+ # use resampled reference
124
+ f"-r {flirt_out_path.resolve()}",
125
+ f"-w {warp_file_path.resolve()}",
126
+ f"-o {out_path}",
127
+ ],
128
+ tempdir=element_tempdir,
129
+ )
122
130
 
123
131
  logger.debug("Updating warped data")
124
132
  input.update(
@@ -139,11 +147,10 @@ class FSLWarper:
139
147
  # Check for data type's mask and warp if found
140
148
  if input.get("mask") is not None:
141
149
  # Create a tempfile for warped mask output
142
- applywarp_mask_out_path = (
143
- element_tempdir / "warped_mask.nii.gz"
144
- )
150
+ applywarp_mask_out_path = element_tempdir / "warped_mask.nii"
145
151
  # Set applywarp command
146
152
  applywarp_mask_cmd = [
153
+ "FSLOUTPUTTYPE=NIFTI",
147
154
  "applywarp",
148
155
  "--interp=nn",
149
156
  f"-i {input['mask']['path'].resolve()}",
@@ -200,10 +207,11 @@ class FSLWarper:
200
207
  logger.debug("Resampling reference to match input resolution")
201
208
  # Create a tempfile for resampled reference output
202
209
  ref_path = (
203
- element_tempdir / f"resampled_reference-{reference}.nii.gz"
210
+ element_tempdir / f"resampled_reference-{reference}.nii"
204
211
  )
205
212
  # Set flirt command
206
213
  flirt_cmd = [
214
+ "FSLOUTPUTTYPE=NIFTI",
207
215
  "flirt",
208
216
  "-interp spline",
209
217
  f"-in {input['reference']['path'].resolve()}",
@@ -219,21 +227,25 @@ class FSLWarper:
219
227
 
220
228
  # Create a tempfile for warped output
221
229
  applywarp_out_path = (
222
- element_tempdir
223
- / f"warped_data_from_native_to_{reference}.nii.gz"
230
+ element_tempdir / f"warped_data_from_native_to_{reference}.nii"
231
+ )
232
+ # Call applywarp in chunks of volumes
233
+ warp_in_chunks(
234
+ name="applywarp",
235
+ input_path=input["path"].resolve(),
236
+ output_path=applywarp_out_path.resolve(),
237
+ cmd=lambda in_path, out_path: [
238
+ "FSLOUTPUTTYPE=NIFTI",
239
+ "applywarp",
240
+ "--interp=spline",
241
+ f"-i {in_path}",
242
+ # use resampled reference or original
243
+ f"-r {ref_path.resolve()}",
244
+ f"-w {warp_file_path.resolve()}",
245
+ f"-o {out_path}",
246
+ ],
247
+ tempdir=element_tempdir,
224
248
  )
225
- # Set applywarp command
226
- applywarp_cmd = [
227
- "applywarp",
228
- "--interp=spline",
229
- f"-i {input['path'].resolve()}",
230
- # use resampled reference or original
231
- f"-r {ref_path.resolve()}",
232
- f"-w {warp_file_path.resolve()}",
233
- f"-o {applywarp_out_path.resolve()}",
234
- ]
235
- # Call applywarp
236
- run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
237
249
 
238
250
  logger.debug("Updating warped data")
239
251
  input.update(
@@ -257,10 +269,11 @@ class FSLWarper:
257
269
  # Create a tempfile for warped mask output
258
270
  applywarp_mask_out_path = (
259
271
  element_tempdir
260
- / f"warped_mask_from_native_to_{reference}.nii.gz"
272
+ / f"warped_mask_from_native_to_{reference}.nii"
261
273
  )
262
274
  # Set applywarp command
263
275
  applywarp_mask_cmd = [
276
+ "FSLOUTPUTTYPE=NIFTI",
264
277
  "applywarp",
265
278
  "--interp=nn",
266
279
  f"-i {input['mask']['path'].resolve()}",
@@ -0,0 +1,106 @@
1
+ """Provide utilities for warping."""
2
+
3
+ # Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
4
+ # License: AGPL
5
+
6
+ from collections.abc import Callable
7
+ from pathlib import Path
8
+
9
+ import nibabel as nib
10
+ import numpy as np
11
+ from tqdm import tqdm
12
+
13
+ from ...utils import config, raise_error, run_ext_cmd
14
+ from ..base import logger
15
+
16
+
17
+ __all__ = ["warp_in_chunks"]
18
+
19
+
20
+ def warp_in_chunks(
21
+ name: str,
22
+ input_path: Path,
23
+ output_path: Path,
24
+ cmd: Callable[[Path, Path], list[str]],
25
+ tempdir: Path,
26
+ ) -> None:
27
+ """Warp an image, in chunks of volumes if it is 4D.
28
+
29
+ The memory used by the warping commands grows with the number of volumes,
30
+ so the volumes of a 4D image are warped in chunks of
31
+ ``preprocessing.warping.chunksize`` volumes (default 100), which are then
32
+ concatenated. The volumes are warped independently, so the result is the
33
+ same as warping the whole image.
34
+
35
+ Parameters
36
+ ----------
37
+ name : str
38
+ The name of the warping command.
39
+ input_path : pathlib.Path
40
+ The path of the image to warp.
41
+ output_path : pathlib.Path
42
+ The path of the warped image.
43
+ cmd : callable
44
+ A function that returns the command to warp an image, given the paths
45
+ of the image to warp and of the warped image.
46
+ tempdir : pathlib.Path
47
+ The directory for the chunks.
48
+
49
+ Raises
50
+ ------
51
+ ValueError
52
+ If ``preprocessing.warping.chunksize`` is not a positive integer.
53
+
54
+ """
55
+ chunk_size = config.get("preprocessing.warping.chunksize", 100)
56
+ if (
57
+ isinstance(chunk_size, bool)
58
+ or not isinstance(chunk_size, int)
59
+ or chunk_size < 1
60
+ ):
61
+ raise_error(
62
+ "`preprocessing.warping.chunksize` must be a positive integer, "
63
+ f"but is {chunk_size}"
64
+ )
65
+ img = nib.load(input_path)
66
+ if img.ndim != 4 or img.shape[3] <= chunk_size:
67
+ run_ext_cmd(name=name, cmd=cmd(input_path, output_path))
68
+ return
69
+
70
+ n_volumes = img.shape[3]
71
+ starts = range(0, n_volumes, chunk_size)
72
+ logger.info(
73
+ f"Warping {n_volumes} volumes in {len(starts)} chunks of up to "
74
+ f"{chunk_size} volumes"
75
+ )
76
+ warped_chunks = []
77
+ for i, start in enumerate(tqdm(starts, desc=f"{name} chunks")):
78
+ # Uncompressed, as they are deleted right after being read
79
+ chunk_path = tempdir / f"chunk{i}_input_{output_path.stem}.nii"
80
+ warped_chunk_path = tempdir / f"chunk{i}_{output_path.stem}.nii"
81
+ chunk = img.slicer[..., start : start + chunk_size]
82
+ # The data is read scaled, so save it as float instead of scaling it
83
+ # again (with a different scaling) in its data type
84
+ if img.dataobj.slope != 1 or img.dataobj.inter != 0:
85
+ chunk.set_data_dtype(np.float32)
86
+ nib.save(chunk, chunk_path)
87
+ run_ext_cmd(name=name, cmd=cmd(chunk_path, warped_chunk_path))
88
+ chunk_path.unlink()
89
+ warped_chunks.append(warped_chunk_path)
90
+
91
+ # Fill the data of the warped image with the warped chunks (instead of
92
+ # concatenating them, which needs twice the memory)
93
+ data = None
94
+ for start, warped_chunk_path in zip(starts, warped_chunks, strict=True):
95
+ warped_chunk = nib.load(warped_chunk_path)
96
+ chunk_data = np.asanyarray(warped_chunk.dataobj)
97
+ if data is None:
98
+ first = warped_chunk
99
+ data = np.empty(
100
+ (*chunk_data.shape[:3], n_volumes), dtype=chunk_data.dtype
101
+ )
102
+ data[..., start : start + chunk_data.shape[3]] = chunk_data
103
+ warped_chunk_path.unlink()
104
+ warped = nib.Nifti1Image(data, first.affine, first.header)
105
+ warped.set_data_dtype(data.dtype)
106
+ nib.save(warped, output_path)