junifer 0.0.4.dev829__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.
- junifer/__init__.py +17 -0
- junifer/_version.py +2 -2
- junifer/api/__init__.py +4 -1
- junifer/api/cli.py +91 -1
- junifer/api/decorators.py +9 -0
- junifer/api/functions.py +56 -10
- junifer/api/parser.py +3 -0
- junifer/api/queue_context/__init__.py +4 -1
- junifer/api/queue_context/gnu_parallel_local_adapter.py +16 -6
- junifer/api/queue_context/htcondor_adapter.py +16 -5
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +41 -12
- junifer/api/queue_context/tests/test_htcondor_adapter.py +48 -15
- junifer/api/res/afni/run_afni_docker.sh +1 -1
- junifer/api/res/ants/run_ants_docker.sh +1 -1
- junifer/api/res/freesurfer/mri_binarize +3 -0
- junifer/api/res/freesurfer/mri_mc +3 -0
- junifer/api/res/freesurfer/mri_pretess +3 -0
- junifer/api/res/freesurfer/mris_convert +3 -0
- junifer/api/res/freesurfer/run_freesurfer_docker.sh +61 -0
- junifer/api/res/fsl/run_fsl_docker.sh +1 -1
- junifer/api/res/{run_conda.sh → run_conda.bash} +1 -1
- junifer/api/res/run_conda.zsh +23 -0
- junifer/api/res/run_venv.bash +22 -0
- junifer/api/res/{run_venv.sh → run_venv.zsh} +1 -1
- junifer/api/tests/test_api_utils.py +4 -2
- junifer/api/tests/test_cli.py +83 -0
- junifer/api/tests/test_functions.py +27 -2
- junifer/configs/__init__.py +1 -1
- junifer/configs/juseless/__init__.py +4 -1
- junifer/configs/juseless/datagrabbers/__init__.py +10 -1
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +4 -3
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +3 -0
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +4 -3
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +1 -3
- junifer/configs/juseless/datagrabbers/ucla.py +12 -9
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +3 -0
- junifer/data/__init__.py +21 -1
- junifer/data/coordinates.py +10 -19
- junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz +0 -0
- junifer/data/masks.py +58 -87
- junifer/data/parcellations.py +14 -3
- junifer/data/template_spaces.py +4 -1
- junifer/data/tests/test_masks.py +26 -37
- junifer/data/utils.py +3 -0
- junifer/datagrabber/__init__.py +18 -1
- junifer/datagrabber/aomic/__init__.py +3 -0
- junifer/datagrabber/aomic/id1000.py +70 -37
- junifer/datagrabber/aomic/piop1.py +69 -36
- junifer/datagrabber/aomic/piop2.py +71 -38
- junifer/datagrabber/aomic/tests/test_id1000.py +44 -100
- junifer/datagrabber/aomic/tests/test_piop1.py +65 -108
- junifer/datagrabber/aomic/tests/test_piop2.py +45 -102
- junifer/datagrabber/base.py +13 -6
- junifer/datagrabber/datalad_base.py +13 -1
- junifer/datagrabber/dmcc13_benchmark.py +36 -53
- junifer/datagrabber/hcp1200/__init__.py +3 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -0
- junifer/datagrabber/hcp1200/hcp1200.py +4 -1
- junifer/datagrabber/multiple.py +45 -6
- junifer/datagrabber/pattern.py +170 -62
- junifer/datagrabber/pattern_datalad.py +25 -12
- junifer/datagrabber/pattern_validation_mixin.py +388 -0
- junifer/datagrabber/tests/test_datalad_base.py +4 -4
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +46 -19
- junifer/datagrabber/tests/test_multiple.py +161 -84
- junifer/datagrabber/tests/test_pattern.py +45 -0
- junifer/datagrabber/tests/test_pattern_datalad.py +4 -4
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +249 -0
- junifer/datareader/__init__.py +4 -1
- junifer/datareader/default.py +95 -43
- junifer/external/BrainPrint/brainprint/__init__.py +4 -0
- junifer/external/BrainPrint/brainprint/_version.py +3 -0
- junifer/external/BrainPrint/brainprint/asymmetry.py +91 -0
- junifer/external/BrainPrint/brainprint/brainprint.py +441 -0
- junifer/external/BrainPrint/brainprint/surfaces.py +258 -0
- junifer/external/BrainPrint/brainprint/utils/__init__.py +1 -0
- junifer/external/BrainPrint/brainprint/utils/_config.py +112 -0
- junifer/external/BrainPrint/brainprint/utils/utils.py +188 -0
- junifer/external/__init__.py +1 -1
- junifer/external/nilearn/__init__.py +5 -1
- junifer/external/nilearn/junifer_connectivity_measure.py +483 -0
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +23 -9
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +1089 -0
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +76 -1
- junifer/markers/__init__.py +23 -1
- junifer/markers/base.py +68 -28
- junifer/markers/brainprint.py +459 -0
- junifer/markers/collection.py +10 -2
- junifer/markers/complexity/__init__.py +10 -0
- junifer/markers/complexity/complexity_base.py +26 -43
- junifer/markers/complexity/hurst_exponent.py +3 -0
- junifer/markers/complexity/multiscale_entropy_auc.py +3 -0
- junifer/markers/complexity/perm_entropy.py +3 -0
- junifer/markers/complexity/range_entropy.py +3 -0
- junifer/markers/complexity/range_entropy_auc.py +3 -0
- junifer/markers/complexity/sample_entropy.py +3 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +11 -3
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +11 -3
- junifer/markers/complexity/tests/test_perm_entropy.py +11 -3
- junifer/markers/complexity/tests/test_range_entropy.py +11 -3
- junifer/markers/complexity/tests/test_range_entropy_auc.py +11 -3
- junifer/markers/complexity/tests/test_sample_entropy.py +11 -3
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +11 -3
- junifer/markers/complexity/weighted_perm_entropy.py +3 -0
- junifer/markers/ets_rss.py +27 -42
- junifer/markers/falff/__init__.py +3 -0
- junifer/markers/falff/_afni_falff.py +5 -2
- junifer/markers/falff/_junifer_falff.py +3 -0
- junifer/markers/falff/falff_base.py +20 -46
- junifer/markers/falff/falff_parcels.py +56 -27
- junifer/markers/falff/falff_spheres.py +60 -29
- junifer/markers/falff/tests/test_falff_parcels.py +39 -23
- junifer/markers/falff/tests/test_falff_spheres.py +39 -23
- junifer/markers/functional_connectivity/__init__.py +9 -0
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +63 -60
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +45 -32
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +49 -36
- junifer/markers/functional_connectivity/functional_connectivity_base.py +71 -70
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +34 -25
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +40 -30
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +11 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +27 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +28 -12
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +35 -11
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +36 -62
- junifer/markers/parcel_aggregation.py +47 -61
- junifer/markers/reho/__init__.py +3 -0
- junifer/markers/reho/_afni_reho.py +5 -2
- junifer/markers/reho/_junifer_reho.py +4 -1
- junifer/markers/reho/reho_base.py +8 -27
- junifer/markers/reho/reho_parcels.py +28 -17
- junifer/markers/reho/reho_spheres.py +27 -18
- junifer/markers/reho/tests/test_reho_parcels.py +8 -3
- junifer/markers/reho/tests/test_reho_spheres.py +8 -3
- junifer/markers/sphere_aggregation.py +43 -59
- junifer/markers/temporal_snr/__init__.py +3 -0
- junifer/markers/temporal_snr/temporal_snr_base.py +23 -32
- junifer/markers/temporal_snr/temporal_snr_parcels.py +9 -6
- junifer/markers/temporal_snr/temporal_snr_spheres.py +9 -6
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +6 -3
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +6 -3
- junifer/markers/tests/test_brainprint.py +58 -0
- junifer/markers/tests/test_collection.py +9 -8
- junifer/markers/tests/test_ets_rss.py +15 -9
- junifer/markers/tests/test_markers_base.py +17 -18
- junifer/markers/tests/test_parcel_aggregation.py +93 -32
- junifer/markers/tests/test_sphere_aggregation.py +72 -19
- junifer/onthefly/__init__.py +4 -1
- junifer/onthefly/read_transform.py +3 -0
- junifer/pipeline/__init__.py +9 -1
- junifer/pipeline/pipeline_step_mixin.py +21 -4
- junifer/pipeline/registry.py +3 -0
- junifer/pipeline/singleton.py +3 -0
- junifer/pipeline/tests/test_registry.py +1 -1
- junifer/pipeline/update_meta_mixin.py +3 -0
- junifer/pipeline/utils.py +67 -1
- junifer/pipeline/workdir_manager.py +3 -0
- junifer/preprocess/__init__.py +10 -2
- junifer/preprocess/base.py +6 -3
- junifer/preprocess/confounds/__init__.py +3 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +47 -60
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +72 -113
- junifer/preprocess/smoothing/__init__.py +9 -0
- junifer/preprocess/smoothing/_afni_smoothing.py +119 -0
- junifer/preprocess/smoothing/_fsl_smoothing.py +116 -0
- junifer/preprocess/smoothing/_nilearn_smoothing.py +69 -0
- junifer/preprocess/smoothing/smoothing.py +174 -0
- junifer/preprocess/smoothing/tests/test_smoothing.py +94 -0
- junifer/preprocess/warping/__init__.py +3 -0
- junifer/preprocess/warping/_ants_warper.py +3 -0
- junifer/preprocess/warping/_fsl_warper.py +3 -0
- junifer/stats.py +4 -1
- junifer/storage/__init__.py +9 -1
- junifer/storage/base.py +40 -1
- junifer/storage/hdf5.py +71 -9
- junifer/storage/pandas_base.py +3 -0
- junifer/storage/sqlite.py +3 -0
- junifer/storage/tests/test_hdf5.py +82 -10
- junifer/storage/utils.py +9 -0
- junifer/testing/__init__.py +4 -1
- junifer/testing/datagrabbers.py +13 -6
- junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
- junifer/testing/utils.py +3 -0
- junifer/utils/__init__.py +13 -2
- junifer/utils/fs.py +3 -0
- junifer/utils/helpers.py +32 -1
- junifer/utils/logging.py +33 -4
- junifer/utils/tests/test_logging.py +8 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/METADATA +17 -16
- junifer-0.0.5.dist-info/RECORD +275 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/WHEEL +1 -1
- junifer/datagrabber/tests/test_datagrabber_utils.py +0 -218
- junifer/datagrabber/utils.py +0 -230
- junifer/preprocess/ants/__init__.py +0 -4
- junifer/preprocess/ants/ants_apply_transforms_warper.py +0 -185
- junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +0 -56
- junifer/preprocess/bold_warper.py +0 -265
- junifer/preprocess/fsl/__init__.py +0 -4
- junifer/preprocess/fsl/apply_warper.py +0 -179
- junifer/preprocess/fsl/tests/test_apply_warper.py +0 -45
- junifer/preprocess/tests/test_bold_warper.py +0 -159
- junifer-0.0.4.dev829.dist-info/RECORD +0 -257
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.4.dev829.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,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"])
|