junifer 0.0.4.dev831__py3-none-any.whl → 0.0.5__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 (206) hide show
  1. junifer/__init__.py +17 -0
  2. junifer/_version.py +2 -2
  3. junifer/api/__init__.py +4 -1
  4. junifer/api/cli.py +91 -1
  5. junifer/api/decorators.py +9 -0
  6. junifer/api/functions.py +56 -10
  7. junifer/api/parser.py +3 -0
  8. junifer/api/queue_context/__init__.py +4 -1
  9. junifer/api/queue_context/gnu_parallel_local_adapter.py +16 -6
  10. junifer/api/queue_context/htcondor_adapter.py +16 -5
  11. junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +41 -12
  12. junifer/api/queue_context/tests/test_htcondor_adapter.py +48 -15
  13. junifer/api/res/afni/run_afni_docker.sh +1 -1
  14. junifer/api/res/ants/run_ants_docker.sh +1 -1
  15. junifer/api/res/freesurfer/mri_binarize +3 -0
  16. junifer/api/res/freesurfer/mri_mc +3 -0
  17. junifer/api/res/freesurfer/mri_pretess +3 -0
  18. junifer/api/res/freesurfer/mris_convert +3 -0
  19. junifer/api/res/freesurfer/run_freesurfer_docker.sh +61 -0
  20. junifer/api/res/fsl/run_fsl_docker.sh +1 -1
  21. junifer/api/res/{run_conda.sh → run_conda.bash} +1 -1
  22. junifer/api/res/run_conda.zsh +23 -0
  23. junifer/api/res/run_venv.bash +22 -0
  24. junifer/api/res/{run_venv.sh → run_venv.zsh} +1 -1
  25. junifer/api/tests/test_api_utils.py +4 -2
  26. junifer/api/tests/test_cli.py +83 -0
  27. junifer/api/tests/test_functions.py +27 -2
  28. junifer/configs/__init__.py +1 -1
  29. junifer/configs/juseless/__init__.py +4 -1
  30. junifer/configs/juseless/datagrabbers/__init__.py +10 -1
  31. junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +4 -3
  32. junifer/configs/juseless/datagrabbers/camcan_vbm.py +3 -0
  33. junifer/configs/juseless/datagrabbers/ixi_vbm.py +4 -3
  34. junifer/configs/juseless/datagrabbers/tests/test_ucla.py +1 -3
  35. junifer/configs/juseless/datagrabbers/ucla.py +12 -9
  36. junifer/configs/juseless/datagrabbers/ukb_vbm.py +3 -0
  37. junifer/data/__init__.py +21 -1
  38. junifer/data/coordinates.py +10 -19
  39. junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz +0 -0
  40. junifer/data/masks.py +58 -87
  41. junifer/data/parcellations.py +14 -3
  42. junifer/data/template_spaces.py +4 -1
  43. junifer/data/tests/test_masks.py +26 -37
  44. junifer/data/utils.py +3 -0
  45. junifer/datagrabber/__init__.py +18 -1
  46. junifer/datagrabber/aomic/__init__.py +3 -0
  47. junifer/datagrabber/aomic/id1000.py +70 -37
  48. junifer/datagrabber/aomic/piop1.py +69 -36
  49. junifer/datagrabber/aomic/piop2.py +71 -38
  50. junifer/datagrabber/aomic/tests/test_id1000.py +44 -100
  51. junifer/datagrabber/aomic/tests/test_piop1.py +65 -108
  52. junifer/datagrabber/aomic/tests/test_piop2.py +45 -102
  53. junifer/datagrabber/base.py +13 -6
  54. junifer/datagrabber/datalad_base.py +13 -1
  55. junifer/datagrabber/dmcc13_benchmark.py +36 -53
  56. junifer/datagrabber/hcp1200/__init__.py +3 -0
  57. junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -0
  58. junifer/datagrabber/hcp1200/hcp1200.py +4 -1
  59. junifer/datagrabber/multiple.py +45 -6
  60. junifer/datagrabber/pattern.py +170 -62
  61. junifer/datagrabber/pattern_datalad.py +25 -12
  62. junifer/datagrabber/pattern_validation_mixin.py +388 -0
  63. junifer/datagrabber/tests/test_datalad_base.py +4 -4
  64. junifer/datagrabber/tests/test_dmcc13_benchmark.py +46 -19
  65. junifer/datagrabber/tests/test_multiple.py +161 -84
  66. junifer/datagrabber/tests/test_pattern.py +45 -0
  67. junifer/datagrabber/tests/test_pattern_datalad.py +4 -4
  68. junifer/datagrabber/tests/test_pattern_validation_mixin.py +249 -0
  69. junifer/datareader/__init__.py +4 -1
  70. junifer/datareader/default.py +95 -43
  71. junifer/external/BrainPrint/brainprint/__init__.py +4 -0
  72. junifer/external/BrainPrint/brainprint/_version.py +3 -0
  73. junifer/external/BrainPrint/brainprint/asymmetry.py +91 -0
  74. junifer/external/BrainPrint/brainprint/brainprint.py +441 -0
  75. junifer/external/BrainPrint/brainprint/surfaces.py +258 -0
  76. junifer/external/BrainPrint/brainprint/utils/__init__.py +1 -0
  77. junifer/external/BrainPrint/brainprint/utils/_config.py +112 -0
  78. junifer/external/BrainPrint/brainprint/utils/utils.py +188 -0
  79. junifer/external/__init__.py +1 -1
  80. junifer/external/nilearn/__init__.py +5 -1
  81. junifer/external/nilearn/junifer_connectivity_measure.py +483 -0
  82. junifer/external/nilearn/junifer_nifti_spheres_masker.py +23 -9
  83. junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +1089 -0
  84. junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +76 -1
  85. junifer/markers/__init__.py +23 -1
  86. junifer/markers/base.py +68 -28
  87. junifer/markers/brainprint.py +459 -0
  88. junifer/markers/collection.py +10 -2
  89. junifer/markers/complexity/__init__.py +10 -0
  90. junifer/markers/complexity/complexity_base.py +26 -43
  91. junifer/markers/complexity/hurst_exponent.py +3 -0
  92. junifer/markers/complexity/multiscale_entropy_auc.py +3 -0
  93. junifer/markers/complexity/perm_entropy.py +3 -0
  94. junifer/markers/complexity/range_entropy.py +3 -0
  95. junifer/markers/complexity/range_entropy_auc.py +3 -0
  96. junifer/markers/complexity/sample_entropy.py +3 -0
  97. junifer/markers/complexity/tests/test_hurst_exponent.py +11 -3
  98. junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +11 -3
  99. junifer/markers/complexity/tests/test_perm_entropy.py +11 -3
  100. junifer/markers/complexity/tests/test_range_entropy.py +11 -3
  101. junifer/markers/complexity/tests/test_range_entropy_auc.py +11 -3
  102. junifer/markers/complexity/tests/test_sample_entropy.py +11 -3
  103. junifer/markers/complexity/tests/test_weighted_perm_entropy.py +11 -3
  104. junifer/markers/complexity/weighted_perm_entropy.py +3 -0
  105. junifer/markers/ets_rss.py +27 -42
  106. junifer/markers/falff/__init__.py +3 -0
  107. junifer/markers/falff/_afni_falff.py +5 -2
  108. junifer/markers/falff/_junifer_falff.py +3 -0
  109. junifer/markers/falff/falff_base.py +20 -46
  110. junifer/markers/falff/falff_parcels.py +56 -27
  111. junifer/markers/falff/falff_spheres.py +60 -29
  112. junifer/markers/falff/tests/test_falff_parcels.py +39 -23
  113. junifer/markers/falff/tests/test_falff_spheres.py +39 -23
  114. junifer/markers/functional_connectivity/__init__.py +9 -0
  115. junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +63 -60
  116. junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +45 -32
  117. junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +49 -36
  118. junifer/markers/functional_connectivity/functional_connectivity_base.py +71 -70
  119. junifer/markers/functional_connectivity/functional_connectivity_parcels.py +34 -25
  120. junifer/markers/functional_connectivity/functional_connectivity_spheres.py +40 -30
  121. junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +11 -7
  122. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +27 -7
  123. junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +28 -12
  124. junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +35 -11
  125. junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +36 -62
  126. junifer/markers/parcel_aggregation.py +47 -61
  127. junifer/markers/reho/__init__.py +3 -0
  128. junifer/markers/reho/_afni_reho.py +5 -2
  129. junifer/markers/reho/_junifer_reho.py +4 -1
  130. junifer/markers/reho/reho_base.py +8 -27
  131. junifer/markers/reho/reho_parcels.py +28 -17
  132. junifer/markers/reho/reho_spheres.py +27 -18
  133. junifer/markers/reho/tests/test_reho_parcels.py +8 -3
  134. junifer/markers/reho/tests/test_reho_spheres.py +8 -3
  135. junifer/markers/sphere_aggregation.py +43 -59
  136. junifer/markers/temporal_snr/__init__.py +3 -0
  137. junifer/markers/temporal_snr/temporal_snr_base.py +23 -32
  138. junifer/markers/temporal_snr/temporal_snr_parcels.py +9 -6
  139. junifer/markers/temporal_snr/temporal_snr_spheres.py +9 -6
  140. junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +6 -3
  141. junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +6 -3
  142. junifer/markers/tests/test_brainprint.py +58 -0
  143. junifer/markers/tests/test_collection.py +9 -8
  144. junifer/markers/tests/test_ets_rss.py +15 -9
  145. junifer/markers/tests/test_markers_base.py +17 -18
  146. junifer/markers/tests/test_parcel_aggregation.py +93 -32
  147. junifer/markers/tests/test_sphere_aggregation.py +72 -19
  148. junifer/onthefly/__init__.py +4 -1
  149. junifer/onthefly/read_transform.py +3 -0
  150. junifer/pipeline/__init__.py +9 -1
  151. junifer/pipeline/pipeline_step_mixin.py +21 -4
  152. junifer/pipeline/registry.py +3 -0
  153. junifer/pipeline/singleton.py +3 -0
  154. junifer/pipeline/tests/test_registry.py +1 -1
  155. junifer/pipeline/update_meta_mixin.py +3 -0
  156. junifer/pipeline/utils.py +67 -1
  157. junifer/pipeline/workdir_manager.py +3 -0
  158. junifer/preprocess/__init__.py +10 -2
  159. junifer/preprocess/base.py +6 -3
  160. junifer/preprocess/confounds/__init__.py +3 -0
  161. junifer/preprocess/confounds/fmriprep_confound_remover.py +47 -60
  162. junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +72 -113
  163. junifer/preprocess/smoothing/__init__.py +9 -0
  164. junifer/preprocess/smoothing/_afni_smoothing.py +119 -0
  165. junifer/preprocess/smoothing/_fsl_smoothing.py +116 -0
  166. junifer/preprocess/smoothing/_nilearn_smoothing.py +69 -0
  167. junifer/preprocess/smoothing/smoothing.py +174 -0
  168. junifer/preprocess/smoothing/tests/test_smoothing.py +94 -0
  169. junifer/preprocess/warping/__init__.py +3 -0
  170. junifer/preprocess/warping/_ants_warper.py +3 -0
  171. junifer/preprocess/warping/_fsl_warper.py +3 -0
  172. junifer/stats.py +4 -1
  173. junifer/storage/__init__.py +9 -1
  174. junifer/storage/base.py +40 -1
  175. junifer/storage/hdf5.py +71 -9
  176. junifer/storage/pandas_base.py +3 -0
  177. junifer/storage/sqlite.py +3 -0
  178. junifer/storage/tests/test_hdf5.py +82 -10
  179. junifer/storage/utils.py +9 -0
  180. junifer/testing/__init__.py +4 -1
  181. junifer/testing/datagrabbers.py +13 -6
  182. junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
  183. junifer/testing/utils.py +3 -0
  184. junifer/utils/__init__.py +13 -2
  185. junifer/utils/fs.py +3 -0
  186. junifer/utils/helpers.py +32 -1
  187. junifer/utils/logging.py +33 -4
  188. junifer/utils/tests/test_logging.py +8 -0
  189. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/METADATA +17 -16
  190. junifer-0.0.5.dist-info/RECORD +275 -0
  191. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/WHEEL +1 -1
  192. junifer/datagrabber/tests/test_datagrabber_utils.py +0 -218
  193. junifer/datagrabber/utils.py +0 -230
  194. junifer/preprocess/ants/__init__.py +0 -4
  195. junifer/preprocess/ants/ants_apply_transforms_warper.py +0 -185
  196. junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +0 -56
  197. junifer/preprocess/bold_warper.py +0 -265
  198. junifer/preprocess/fsl/__init__.py +0 -4
  199. junifer/preprocess/fsl/apply_warper.py +0 -179
  200. junifer/preprocess/fsl/tests/test_apply_warper.py +0 -45
  201. junifer/preprocess/tests/test_bold_warper.py +0 -159
  202. junifer-0.0.4.dev831.dist-info/RECORD +0 -257
  203. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/AUTHORS.rst +0 -0
  204. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/LICENSE.md +0 -0
  205. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/entry_points.txt +0 -0
  206. {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/top_level.txt +0 -0
@@ -1,265 +0,0 @@
1
- """Provide class for warping BOLD to other template spaces."""
2
-
3
- # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
- # License: AGPL
5
-
6
- from typing import (
7
- Any,
8
- ClassVar,
9
- Dict,
10
- List,
11
- Optional,
12
- Tuple,
13
- Type,
14
- Union,
15
- )
16
-
17
- import nibabel as nib
18
- from templateflow import api as tflow
19
-
20
- from ..api.decorators import register_preprocessor
21
- from ..data import get_template, get_xfm
22
- from ..pipeline import WorkDirManager
23
- from ..utils import logger, raise_error, run_ext_cmd
24
- from .ants.ants_apply_transforms_warper import _AntsApplyTransformsWarper
25
- from .base import BasePreprocessor
26
- from .fsl.apply_warper import _ApplyWarper
27
-
28
-
29
- @register_preprocessor
30
- class BOLDWarper(BasePreprocessor):
31
- """Class for warping BOLD NIfTI images.
32
-
33
- .. deprecated:: 0.0.3
34
- `BOLDWarper` will be removed in v0.0.4, it is replaced by
35
- `SpaceWarper` because the latter works also with T1w data.
36
-
37
- Parameters
38
- ----------
39
- using : {"fsl", "ants"}
40
- Implementation to use for warping:
41
-
42
- * "fsl" : Use FSL's ``applywarp``
43
- * "afni" : Use ANTs' ``antsApplyTransforms``
44
-
45
- reference : str
46
- The data type to use as reference for warping, can be either a data
47
- type like "T1w" or a template space like "MNI152NLin2009cAsym".
48
-
49
- Raises
50
- ------
51
- ValueError
52
- If ``using`` is invalid or
53
- if ``reference`` is invalid.
54
-
55
- Notes
56
- -----
57
- If you are setting ``reference`` to a template space like
58
- "MNI152NLin2009cAsym", make sure ANTs is available for the
59
- transformation else it will fail during runtime. It is tricky to validate
60
- this beforehand and difficult to enforce this as a requirement, hence the
61
- heads-up.
62
-
63
- """
64
-
65
- _CONDITIONAL_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, Type]]]] = [
66
- {
67
- "using": "fsl",
68
- "depends_on": _ApplyWarper,
69
- },
70
- {
71
- "using": "ants",
72
- "depends_on": _AntsApplyTransformsWarper,
73
- },
74
- ]
75
-
76
- def __init__(self, using: str, reference: str) -> None:
77
- """Initialize the class."""
78
- # Validate `using` parameter
79
- valid_using = [dep["using"] for dep in self._CONDITIONAL_DEPENDENCIES]
80
- if using not in valid_using:
81
- raise_error(
82
- f"Invalid value for `using`, should be one of: {valid_using}"
83
- )
84
- self.using = using
85
- self.ref = reference
86
- # Initialize superclass based on reference
87
- if self.ref == "T1w":
88
- super().__init__(
89
- on="BOLD", required_data_types=["BOLD", self.ref, "Warp"]
90
- )
91
- elif self.ref in tflow.templates():
92
- super().__init__(on="BOLD", required_data_types=["BOLD"])
93
- else:
94
- raise_error(f"Unknown reference: {self.ref}")
95
-
96
- def get_valid_inputs(self) -> List[str]:
97
- """Get valid data types for input.
98
-
99
- Returns
100
- -------
101
- list of str
102
- The list of data types that can be used as input for this
103
- preprocessor.
104
-
105
- """
106
- return ["BOLD"]
107
-
108
- def get_output_type(self, input_type: str) -> str:
109
- """Get output type.
110
-
111
- Parameters
112
- ----------
113
- input_type : str
114
- The data type input to the preprocessor.
115
-
116
- Returns
117
- -------
118
- str
119
- The data type output by the preprocessor.
120
-
121
- """
122
- # Does not add any new keys
123
- return input_type
124
-
125
- def preprocess(
126
- self,
127
- input: Dict[str, Any],
128
- extra_input: Optional[Dict[str, Any]] = None,
129
- ) -> Tuple[Dict[str, Any], Optional[Dict[str, Dict[str, Any]]]]:
130
- """Preprocess.
131
-
132
- Parameters
133
- ----------
134
- input : dict
135
- The BOLD input from the Junifer Data object.
136
- extra_input : dict, optional
137
- The other fields in the Junifer Data object. Must include the
138
- ``Warp`` and ``ref`` value's keys if native space transformation is
139
- needed.
140
-
141
- Returns
142
- -------
143
- dict
144
- The computed result as dictionary.
145
- None
146
- Extra "helper" data types as dictionary to add to the Junifer Data
147
- object.
148
-
149
- Raises
150
- ------
151
- ValueError
152
- If ``extra_input`` is None when transforming to native space
153
- i.e., using "T1w" as reference.
154
- RuntimeError
155
- If warp / transformation file extension is not ".mat" or ".h5"
156
- when transforming to native space or
157
- if the BOLD data is in the correct space and does not require
158
- warping.
159
-
160
- """
161
- logger.info(f"Warping BOLD to {self.ref} space using BOLDWarper")
162
- # Transform to native space
163
- if self.ref == "T1w":
164
- # Check for extra inputs
165
- if extra_input is None:
166
- raise_error(
167
- "No extra input provided, requires `Warp` and "
168
- f"`{self.ref}` data types in particular."
169
- )
170
- # Check for warp file type to use correct tool
171
- warp_file_ext = extra_input["Warp"]["path"].suffix
172
- if warp_file_ext == ".mat":
173
- logger.debug("Using FSL with BOLDWarper")
174
- # Initialize ApplyWarper for computation
175
- apply_warper = _ApplyWarper(reference=self.ref, on="BOLD")
176
- # Replace original BOLD data with warped BOLD data
177
- _, input = apply_warper.preprocess(
178
- input=input,
179
- extra_input=extra_input,
180
- )
181
- elif warp_file_ext == ".h5":
182
- logger.debug("Using ANTs with BOLDWarper")
183
- # Initialize AntsApplyTransformsWarper for computation
184
- ants_apply_transforms_warper = _AntsApplyTransformsWarper(
185
- reference=self.ref, on="BOLD"
186
- )
187
- # Replace original BOLD data with warped BOLD data
188
- _, input = ants_apply_transforms_warper.preprocess(
189
- input=input,
190
- extra_input=extra_input,
191
- )
192
- else:
193
- raise_error(
194
- msg=(
195
- "Unknown warp / transformation file extension: "
196
- f"{warp_file_ext}"
197
- ),
198
- klass=RuntimeError,
199
- )
200
- # Transform to template space
201
- else:
202
- # Check pre-requirements for space manipulation
203
- if self.ref == input["space"]:
204
- raise_error(
205
- (
206
- f"Skipped warping as the BOLD data is in {self.ref} "
207
- "space which would mean that you can remove the "
208
- "BOLDWarper from the preprocess step."
209
- ),
210
- klass=RuntimeError,
211
- )
212
- else:
213
- # Get xfm file
214
- xfm_file_path = get_xfm(src=input["space"], dst=self.ref)
215
- # Get template space image
216
- template_space_img = get_template(
217
- space=self.ref,
218
- target_data=input,
219
- extra_input=None,
220
- )
221
-
222
- # Create component-scoped tempdir
223
- tempdir = WorkDirManager().get_tempdir(prefix="boldwarper")
224
- # Create element-scoped tempdir so that warped BOLD is
225
- # available later as nibabel stores file path reference for
226
- # loading on computation
227
- element_tempdir = WorkDirManager().get_element_tempdir(
228
- prefix="boldwarper"
229
- )
230
-
231
- # Save template
232
- template_space_img_path = tempdir / f"{self.ref}_T1w.nii.gz"
233
- nib.save(template_space_img, template_space_img_path)
234
-
235
- # Create a tempfile for warped output
236
- warped_bold_path = (
237
- element_tempdir
238
- / f"bold_warped_from_{input['space']}_to_{self.ref}.nii.gz"
239
- )
240
-
241
- logger.debug(
242
- f"Using ANTs to warp BOLD "
243
- f"from {input['space']} to {self.ref}"
244
- )
245
- # Set antsApplyTransforms command
246
- apply_transforms_cmd = [
247
- "antsApplyTransforms",
248
- "-d 3",
249
- "-e 3",
250
- "-n LanczosWindowedSinc",
251
- f"-i {input['path'].resolve()}",
252
- f"-r {template_space_img_path.resolve()}",
253
- f"-t {xfm_file_path.resolve()}",
254
- f"-o {warped_bold_path.resolve()}",
255
- ]
256
- # Call antsApplyTransforms
257
- run_ext_cmd(
258
- name="antsApplyTransforms", cmd=apply_transforms_cmd
259
- )
260
-
261
- # Modify target data
262
- input["data"] = nib.load(warped_bold_path)
263
- input["space"] = self.ref
264
-
265
- return input, None
@@ -1,4 +0,0 @@
1
- """Provide imports for fsl sub-package."""
2
-
3
- # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
- # License: AGPL
@@ -1,179 +0,0 @@
1
- """Provide class for warping via FSL FLIRT."""
2
-
3
- # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
- # License: AGPL
5
-
6
- from pathlib import Path
7
- from typing import (
8
- TYPE_CHECKING,
9
- Any,
10
- ClassVar,
11
- Dict,
12
- List,
13
- Optional,
14
- Tuple,
15
- Union,
16
- )
17
-
18
- import nibabel as nib
19
- import numpy as np
20
-
21
- from ...pipeline import WorkDirManager
22
- from ...utils import logger, raise_error, run_ext_cmd
23
-
24
-
25
- if TYPE_CHECKING:
26
- from nibabel import Nifti1Image
27
-
28
-
29
- class _ApplyWarper:
30
- """Class for warping NIfTI images via FSL FLIRT.
31
-
32
- Wraps FSL FLIRT ``applywarp``.
33
-
34
- Parameters
35
- ----------
36
- reference : str
37
- The data type to use as reference for warping.
38
- on : str
39
- The data type to use for warping.
40
-
41
- Raises
42
- ------
43
- ValueError
44
- If a list was passed for ``on``.
45
-
46
- """
47
-
48
- _EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
49
- {
50
- "name": "fsl",
51
- "commands": ["flirt", "applywarp"],
52
- },
53
- ]
54
-
55
- def __init__(self, reference: str, on: str) -> None:
56
- """Initialize the class."""
57
- self.ref = reference
58
- # Check only single data type is passed
59
- if isinstance(on, list):
60
- raise_error("Can only work on single data type, list was passed.")
61
- self.on = on
62
-
63
- def _run_applywarp(
64
- self,
65
- input_data: Dict,
66
- ref_path: Path,
67
- warp_path: Path,
68
- ) -> Tuple["Nifti1Image", Path]:
69
- """Run ``applywarp``.
70
-
71
- Parameters
72
- ----------
73
- input_data : dict
74
- The input data.
75
- ref_path : pathlib.Path
76
- The path to the reference file.
77
- warp_path : pathlib.Path
78
- The path to the warp file.
79
-
80
- Returns
81
- -------
82
- Niimg-like object
83
- The warped input image.
84
- pathlib.Path
85
- The path to the resampled reference image.
86
-
87
- """
88
- # Get the min of the voxel sizes from input and use it as the
89
- # resolution
90
- resolution = np.min(input_data["data"].header.get_zooms()[:3])
91
-
92
- # Create element-specific tempdir for storing post-warping assets
93
- tempdir = WorkDirManager().get_element_tempdir(prefix="applywarp")
94
-
95
- # Create a tempfile for resampled reference output
96
- flirt_out_path = tempdir / "reference_resampled.nii.gz"
97
- # Set flirt command
98
- flirt_cmd = [
99
- "flirt",
100
- "-interp spline",
101
- f"-in {ref_path.resolve()}",
102
- f"-ref {ref_path.resolve()}",
103
- f"-applyisoxfm {resolution}",
104
- f"-out {flirt_out_path.resolve()}",
105
- ]
106
- # Call flirt
107
- run_ext_cmd(name="flirt", cmd=flirt_cmd)
108
-
109
- # Create a tempfile for warped output
110
- applywarp_out_path = tempdir / "input_warped.nii.gz"
111
- # Set applywarp command
112
- applywarp_cmd = [
113
- "applywarp",
114
- "--interp=spline",
115
- f"-i {input_data['path'].resolve()}",
116
- f"-r {flirt_out_path.resolve()}", # use resampled reference
117
- f"-w {warp_path.resolve()}",
118
- f"-o {applywarp_out_path.resolve()}",
119
- ]
120
- # Call applywarp
121
- run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
122
-
123
- # Load nifti
124
- output_img = nib.load(applywarp_out_path)
125
-
126
- return output_img, flirt_out_path # type: ignore
127
-
128
- def preprocess(
129
- self,
130
- input: Dict[str, Any],
131
- extra_input: Optional[Dict[str, Any]] = None,
132
- ) -> Tuple[str, Dict[str, Any]]:
133
- """Preprocess.
134
-
135
- Parameters
136
- ----------
137
- input : dict
138
- A single input from the Junifer Data object in which to preprocess.
139
- extra_input : dict, optional
140
- The other fields in the Junifer Data object. Must include the
141
- ``Warp`` and ``ref`` value's keys.
142
-
143
- Returns
144
- -------
145
- str
146
- The key to store the output in the Junifer Data object.
147
- dict
148
- The computed result as dictionary. This will be stored in the
149
- Junifer Data object under the key ``data`` of the data type.
150
-
151
- Raises
152
- ------
153
- ValueError
154
- If ``extra_input`` is None.
155
-
156
- """
157
- logger.debug("Warping via FSL using ApplyWarper")
158
- # Check for extra inputs
159
- if extra_input is None:
160
- raise_error(
161
- f"No extra input provided, requires `Warp` and `{self.ref}` "
162
- "data types in particular."
163
- )
164
- # Retrieve data type info to warp
165
- to_warp_input = input
166
- # Retrieve data type info to use as reference
167
- ref_input = extra_input[self.ref]
168
- # Retrieve Warp data
169
- warp = extra_input["Warp"]
170
- # Replace original data with warped data and add resampled reference
171
- # path
172
- input["data"], input["reference_path"] = self._run_applywarp(
173
- input_data=to_warp_input,
174
- ref_path=ref_input["path"],
175
- warp_path=warp["path"],
176
- )
177
- # Use reference input's space as warped input's space
178
- input["space"] = ref_input["space"]
179
- return self.on, input
@@ -1,45 +0,0 @@
1
- """Provide tests for ApplyWarper."""
2
-
3
- # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
- # License: AGPL
5
-
6
- import socket
7
-
8
- import pytest
9
-
10
- from junifer.datagrabber import DataladHCP1200
11
- from junifer.datareader import DefaultDataReader
12
- from junifer.pipeline.utils import _check_fsl
13
- from junifer.preprocess.fsl.apply_warper import _ApplyWarper
14
-
15
-
16
- def test_ApplyWarper_init() -> None:
17
- """Test ApplyWarper init."""
18
- apply_warper = _ApplyWarper(reference="T1w", on="BOLD")
19
- assert apply_warper.ref == "T1w"
20
- assert apply_warper.on == "BOLD"
21
-
22
-
23
- @pytest.mark.skipif(_check_fsl() is False, reason="requires FSL to be in PATH")
24
- @pytest.mark.skipif(
25
- socket.gethostname() != "juseless",
26
- reason="only for juseless",
27
- )
28
- def test_ApplyWarper_preprocess() -> None:
29
- """Test ApplyWarper preprocess."""
30
- with DataladHCP1200(
31
- tasks=["REST1"],
32
- phase_encodings=["LR"],
33
- ica_fix=True,
34
- ) as dg:
35
- # Read data
36
- element_data = DefaultDataReader().fit_transform(
37
- dg[("100206", "REST1", "LR")]
38
- )
39
- # Preprocess data
40
- data_type, data = _ApplyWarper(reference="T1w", on="BOLD").preprocess(
41
- input=element_data["BOLD"],
42
- extra_input=element_data,
43
- )
44
- assert isinstance(data_type, str)
45
- assert isinstance(data, dict)
@@ -1,159 +0,0 @@
1
- """Provide tests for BOLDWarper."""
2
-
3
- # Authors: Synchon Mandal <s.mandal@fz-juelich.de>
4
- # License: AGPL
5
-
6
- import socket
7
- from typing import TYPE_CHECKING, Tuple
8
-
9
- import pytest
10
- from numpy.testing import assert_array_equal, assert_raises
11
-
12
- from junifer.datagrabber import DataladHCP1200, DMCC13Benchmark
13
- from junifer.datareader import DefaultDataReader
14
- from junifer.pipeline.utils import _check_ants, _check_fsl
15
- from junifer.preprocess import BOLDWarper
16
-
17
-
18
- if TYPE_CHECKING:
19
- from junifer.datagrabber import BaseDataGrabber
20
-
21
-
22
- def test_BOLDWarper_init() -> None:
23
- """Test BOLDWarper init."""
24
- bold_warper = BOLDWarper(using="ants", reference="T1w")
25
- assert bold_warper._on == ["BOLD"]
26
-
27
-
28
- def test_BOLDWarper_get_valid_inputs() -> None:
29
- """Test BOLDWarper get_valid_inputs."""
30
- bold_warper = BOLDWarper(using="ants", reference="T1w")
31
- assert bold_warper.get_valid_inputs() == ["BOLD"]
32
-
33
-
34
- def test_BOLDWarper_get_output_type() -> None:
35
- """Test BOLDWarper get_output_type."""
36
- bold_warper = BOLDWarper(using="ants", reference="T1w")
37
- assert bold_warper.get_output_type("BOLD") == "BOLD"
38
-
39
-
40
- @pytest.mark.parametrize(
41
- "datagrabber, element",
42
- [
43
- [
44
- DMCC13Benchmark(
45
- types=["BOLD", "T1w", "Warp"],
46
- sessions=["ses-wave1bas"],
47
- tasks=["Rest"],
48
- phase_encodings=["AP"],
49
- runs=["1"],
50
- native_t1w=True,
51
- ),
52
- ("sub-f9057kp", "ses-wave1bas", "Rest", "AP", "1"),
53
- ],
54
- [
55
- DataladHCP1200(
56
- tasks=["REST1"],
57
- phase_encodings=["LR"],
58
- ica_fix=True,
59
- ),
60
- ("100206", "REST1", "LR"),
61
- ],
62
- ],
63
- )
64
- @pytest.mark.skipif(_check_fsl() is False, reason="requires FSL to be in PATH")
65
- @pytest.mark.skipif(
66
- _check_ants() is False, reason="requires ANTs to be in PATH"
67
- )
68
- @pytest.mark.skipif(
69
- socket.gethostname() != "juseless",
70
- reason="only for juseless",
71
- )
72
- def test_BOLDWarper_preprocess_to_native(
73
- datagrabber: "BaseDataGrabber", element: Tuple[str, ...]
74
- ) -> None:
75
- """Test BOLDWarper preprocess.
76
-
77
- Parameters
78
- ----------
79
- datagrabber : DataGrabber-like object
80
- The parametrized DataGrabber objects.
81
- element : tuple of str
82
- The parametrized elements.
83
-
84
- """
85
- with datagrabber as dg:
86
- # Read data
87
- element_data = DefaultDataReader().fit_transform(dg[element])
88
- # Preprocess data
89
- data, _ = BOLDWarper(reference="T1w").preprocess(
90
- input=element_data["BOLD"],
91
- extra_input=element_data,
92
- )
93
- assert isinstance(data, dict)
94
-
95
-
96
- @pytest.mark.parametrize(
97
- "datagrabber, element, space",
98
- [
99
- [
100
- DMCC13Benchmark(
101
- types=["BOLD"],
102
- sessions=["ses-wave1bas"],
103
- tasks=["Rest"],
104
- phase_encodings=["AP"],
105
- runs=["1"],
106
- native_t1w=False,
107
- ),
108
- ("sub-f9057kp", "ses-wave1bas", "Rest", "AP", "1"),
109
- "MNI152NLin2009aAsym",
110
- ],
111
- [
112
- DMCC13Benchmark(
113
- types=["BOLD"],
114
- sessions=["ses-wave1bas"],
115
- tasks=["Rest"],
116
- phase_encodings=["AP"],
117
- runs=["1"],
118
- native_t1w=False,
119
- ),
120
- ("sub-f9057kp", "ses-wave1bas", "Rest", "AP", "1"),
121
- "MNI152NLin6Asym",
122
- ],
123
- ],
124
- )
125
- @pytest.mark.skipif(
126
- _check_ants() is False, reason="requires ANTs to be in PATH"
127
- )
128
- @pytest.mark.skipif(
129
- socket.gethostname() != "juseless",
130
- reason="only for juseless",
131
- )
132
- def test_BOLDWarper_preprocess_to_multi_mni(
133
- datagrabber: "BaseDataGrabber", element: Tuple[str, ...], space: str
134
- ) -> None:
135
- """Test BOLDWarper preprocess.
136
-
137
- Parameters
138
- ----------
139
- datagrabber : DataGrabber-like object
140
- The parametrized DataGrabber objects.
141
- element : tuple of str
142
- The parametrized elements.
143
- space : str
144
- The parametrized template space to transform to.
145
-
146
- """
147
- with datagrabber as dg:
148
- # Read data
149
- element_data = DefaultDataReader().fit_transform(dg[element])
150
- pre_xfm_data = element_data["BOLD"]["data"].get_fdata().copy()
151
- # Preprocess data
152
- data, _ = BOLDWarper(reference=space).preprocess(
153
- input=element_data["BOLD"],
154
- extra_input=element_data,
155
- )
156
- assert isinstance(data, dict)
157
- assert data["space"] == space
158
- with assert_raises(AssertionError):
159
- assert_array_equal(pre_xfm_data, data["data"])