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
junifer/datagrabber/utils.py
DELETED
@@ -1,230 +0,0 @@
|
|
1
|
-
"""Provide utility functions for the datagrabber sub-package."""
|
2
|
-
|
3
|
-
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
|
-
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
|
-
# License: AGPL
|
6
|
-
|
7
|
-
from typing import Dict, List
|
8
|
-
|
9
|
-
from ..utils import logger, raise_error
|
10
|
-
|
11
|
-
|
12
|
-
# Define schema for pattern-based datagrabber's patterns
|
13
|
-
PATTERNS_SCHEMA = {
|
14
|
-
"T1w": {
|
15
|
-
"mandatory": ["pattern", "space"],
|
16
|
-
"optional": ["mask_item"],
|
17
|
-
},
|
18
|
-
"T1w_mask": {
|
19
|
-
"mandatory": ["pattern", "space"],
|
20
|
-
"optional": [],
|
21
|
-
},
|
22
|
-
"T2w": {
|
23
|
-
"mandatory": ["pattern", "space"],
|
24
|
-
"optional": ["mask_item"],
|
25
|
-
},
|
26
|
-
"T2w_mask": {
|
27
|
-
"mandatory": ["pattern", "space"],
|
28
|
-
"optional": [],
|
29
|
-
},
|
30
|
-
"BOLD": {
|
31
|
-
"mandatory": ["pattern", "space"],
|
32
|
-
"optional": ["mask_item"],
|
33
|
-
},
|
34
|
-
"BOLD_confounds": {
|
35
|
-
"mandatory": ["pattern", "format"],
|
36
|
-
"optional": [],
|
37
|
-
},
|
38
|
-
"BOLD_mask": {
|
39
|
-
"mandatory": ["pattern", "space"],
|
40
|
-
"optional": [],
|
41
|
-
},
|
42
|
-
"Warp": {
|
43
|
-
"mandatory": ["pattern", "src", "dst"],
|
44
|
-
"optional": [],
|
45
|
-
},
|
46
|
-
"VBM_GM": {
|
47
|
-
"mandatory": ["pattern", "space"],
|
48
|
-
"optional": [],
|
49
|
-
},
|
50
|
-
"VBM_WM": {
|
51
|
-
"mandatory": ["pattern", "space"],
|
52
|
-
"optional": [],
|
53
|
-
},
|
54
|
-
"VBM_CSF": {
|
55
|
-
"mandatory": ["pattern", "space"],
|
56
|
-
"optional": [],
|
57
|
-
},
|
58
|
-
"DWI": {
|
59
|
-
"mandatory": ["pattern"],
|
60
|
-
"optional": [],
|
61
|
-
},
|
62
|
-
}
|
63
|
-
|
64
|
-
|
65
|
-
def validate_types(types: List[str]) -> None:
|
66
|
-
"""Validate the types.
|
67
|
-
|
68
|
-
Parameters
|
69
|
-
----------
|
70
|
-
types : list of str
|
71
|
-
The object to validate.
|
72
|
-
|
73
|
-
Raises
|
74
|
-
------
|
75
|
-
TypeError
|
76
|
-
If ``types`` is not a list or if the values are not string.
|
77
|
-
|
78
|
-
"""
|
79
|
-
if not isinstance(types, list):
|
80
|
-
raise_error(msg="`types` must be a list", klass=TypeError)
|
81
|
-
if any(not isinstance(x, str) for x in types):
|
82
|
-
raise_error(msg="`types` must be a list of strings", klass=TypeError)
|
83
|
-
|
84
|
-
|
85
|
-
def validate_replacements(
|
86
|
-
replacements: List[str], patterns: Dict[str, Dict[str, str]]
|
87
|
-
) -> None:
|
88
|
-
"""Validate the replacements.
|
89
|
-
|
90
|
-
Parameters
|
91
|
-
----------
|
92
|
-
replacements : list of str
|
93
|
-
The object to validate.
|
94
|
-
patterns : dict
|
95
|
-
The patterns to validate against.
|
96
|
-
|
97
|
-
Raises
|
98
|
-
------
|
99
|
-
TypeError
|
100
|
-
If ``replacements`` is not a list or if the values are not string.
|
101
|
-
ValueError
|
102
|
-
If a value in ``replacements`` is not part of a data type pattern or
|
103
|
-
if no data type patterns contain all values in ``replacements``.
|
104
|
-
|
105
|
-
"""
|
106
|
-
if not isinstance(replacements, list):
|
107
|
-
raise_error(msg="`replacements` must be a list.", klass=TypeError)
|
108
|
-
|
109
|
-
if any(not isinstance(x, str) for x in replacements):
|
110
|
-
raise_error(
|
111
|
-
msg="`replacements` must be a list of strings.", klass=TypeError
|
112
|
-
)
|
113
|
-
|
114
|
-
for x in replacements:
|
115
|
-
if all(
|
116
|
-
x not in y
|
117
|
-
for y in [
|
118
|
-
data_type_val["pattern"] for data_type_val in patterns.values()
|
119
|
-
]
|
120
|
-
):
|
121
|
-
raise_error(msg=f"Replacement: {x} is not part of any pattern.")
|
122
|
-
|
123
|
-
# Check that at least one pattern has all the replacements
|
124
|
-
at_least_one = False
|
125
|
-
for data_type_val in patterns.values():
|
126
|
-
if all(x in data_type_val["pattern"] for x in replacements):
|
127
|
-
at_least_one = True
|
128
|
-
if at_least_one is False:
|
129
|
-
raise_error(msg="At least one pattern must contain all replacements.")
|
130
|
-
|
131
|
-
|
132
|
-
def validate_patterns(
|
133
|
-
types: List[str], patterns: Dict[str, Dict[str, str]]
|
134
|
-
) -> None:
|
135
|
-
"""Validate the patterns.
|
136
|
-
|
137
|
-
Parameters
|
138
|
-
----------
|
139
|
-
types : list of str
|
140
|
-
The types list.
|
141
|
-
patterns : dict
|
142
|
-
The object to validate.
|
143
|
-
|
144
|
-
Raises
|
145
|
-
------
|
146
|
-
KeyError
|
147
|
-
If any mandatory key is missing for a data type.
|
148
|
-
RuntimeError
|
149
|
-
If an unknown key is found for a data type.
|
150
|
-
TypeError
|
151
|
-
If ``patterns`` is not a dictionary.
|
152
|
-
ValueError
|
153
|
-
If length of ``types`` and ``patterns`` are different or
|
154
|
-
if ``patterns`` is missing entries from ``types`` or
|
155
|
-
if unknown data type is found in ``patterns`` or
|
156
|
-
if data type pattern key contains '*' as value.
|
157
|
-
|
158
|
-
"""
|
159
|
-
# Validate the types
|
160
|
-
validate_types(types)
|
161
|
-
if not isinstance(patterns, dict):
|
162
|
-
raise_error(msg="`patterns` must be a dict.", klass=TypeError)
|
163
|
-
# Unequal length of objects
|
164
|
-
if len(types) > len(patterns):
|
165
|
-
raise_error(
|
166
|
-
msg="Length of `types` more than that of `patterns`.",
|
167
|
-
klass=ValueError,
|
168
|
-
)
|
169
|
-
# Missing type in patterns
|
170
|
-
if any(x not in patterns for x in types):
|
171
|
-
raise_error(
|
172
|
-
msg="`patterns` must contain all `types`", klass=ValueError
|
173
|
-
)
|
174
|
-
# Check against schema
|
175
|
-
for data_type_key, data_type_val in patterns.items():
|
176
|
-
# Check if valid data type is provided
|
177
|
-
if data_type_key not in PATTERNS_SCHEMA:
|
178
|
-
raise_error(
|
179
|
-
f"Unknown data type: {data_type_key}, "
|
180
|
-
f"should be one of: {list(PATTERNS_SCHEMA.keys())}"
|
181
|
-
)
|
182
|
-
# Check mandatory keys for data type
|
183
|
-
for mandatory_key in PATTERNS_SCHEMA[data_type_key]["mandatory"]:
|
184
|
-
if mandatory_key not in data_type_val:
|
185
|
-
raise_error(
|
186
|
-
msg=(
|
187
|
-
f"Mandatory key: `{mandatory_key}` missing for "
|
188
|
-
f"{data_type_key}"
|
189
|
-
),
|
190
|
-
klass=KeyError,
|
191
|
-
)
|
192
|
-
else:
|
193
|
-
logger.debug(
|
194
|
-
f"Mandatory key: `{mandatory_key}` found for "
|
195
|
-
f"{data_type_key}"
|
196
|
-
)
|
197
|
-
# Check optional keys for data type
|
198
|
-
for optional_key in PATTERNS_SCHEMA[data_type_key]["optional"]:
|
199
|
-
if optional_key not in data_type_val:
|
200
|
-
logger.debug(
|
201
|
-
f"Optional key: `{optional_key}` missing for "
|
202
|
-
f"{data_type_key}"
|
203
|
-
)
|
204
|
-
else:
|
205
|
-
logger.debug(
|
206
|
-
f"Optional key: `{optional_key}` found for "
|
207
|
-
f"{data_type_key}"
|
208
|
-
)
|
209
|
-
# Check stray key for data type
|
210
|
-
for key in data_type_val.keys():
|
211
|
-
if key not in (
|
212
|
-
PATTERNS_SCHEMA[data_type_key]["mandatory"]
|
213
|
-
+ PATTERNS_SCHEMA[data_type_key]["optional"]
|
214
|
-
):
|
215
|
-
raise_error(
|
216
|
-
msg=(
|
217
|
-
f"Key: {key} not accepted for {data_type_key} "
|
218
|
-
"pattern, remove it to proceed"
|
219
|
-
),
|
220
|
-
klass=RuntimeError,
|
221
|
-
)
|
222
|
-
# Wildcard check in patterns
|
223
|
-
if "}*" in data_type_val["pattern"]:
|
224
|
-
raise_error(
|
225
|
-
msg=(
|
226
|
-
f"`{data_type_key}.pattern` must not contain `*` "
|
227
|
-
"following a replacement"
|
228
|
-
),
|
229
|
-
klass=ValueError,
|
230
|
-
)
|
@@ -1,185 +0,0 @@
|
|
1
|
-
"""Provide class for warping via ANTs antsApplyTransforms."""
|
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 _AntsApplyTransformsWarper:
|
30
|
-
"""Class for warping NIfTI images via ANTs antsApplyTransforms.
|
31
|
-
|
32
|
-
Warps ANTs ``antsApplyTransforms``.
|
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": "ants",
|
51
|
-
"commands": ["ResampleImage", "antsApplyTransforms"],
|
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_apply_transforms(
|
64
|
-
self,
|
65
|
-
input_data: Dict,
|
66
|
-
ref_path: Path,
|
67
|
-
warp_path: Path,
|
68
|
-
) -> Tuple["Nifti1Image", Path]:
|
69
|
-
"""Run ``antsApplyTransforms``.
|
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(
|
94
|
-
prefix="applytransforms"
|
95
|
-
)
|
96
|
-
|
97
|
-
# Create a tempfile for resampled reference output
|
98
|
-
resample_image_out_path = tempdir / "reference_resampled.nii.gz"
|
99
|
-
# Set ResampleImage command
|
100
|
-
resample_image_cmd = [
|
101
|
-
"ResampleImage",
|
102
|
-
"3", # image dimension
|
103
|
-
f"{ref_path.resolve()}",
|
104
|
-
f"{resample_image_out_path.resolve()}",
|
105
|
-
f"{resolution}x{resolution}x{resolution}",
|
106
|
-
"0", # option for spacing and not size
|
107
|
-
"3 3", # Lanczos windowed sinc
|
108
|
-
]
|
109
|
-
# Call ResampleImage
|
110
|
-
run_ext_cmd(name="ResampleImage", cmd=resample_image_cmd)
|
111
|
-
|
112
|
-
# Create a tempfile for warped output
|
113
|
-
apply_transforms_out_path = tempdir / "input_warped.nii.gz"
|
114
|
-
# Set antsApplyTransforms command
|
115
|
-
apply_transforms_cmd = [
|
116
|
-
"antsApplyTransforms",
|
117
|
-
"-d 3",
|
118
|
-
"-e 3",
|
119
|
-
"-n LanczosWindowedSinc",
|
120
|
-
f"-i {input_data['path'].resolve()}",
|
121
|
-
# use resampled reference
|
122
|
-
f"-r {resample_image_out_path.resolve()}",
|
123
|
-
f"-t {warp_path.resolve()}",
|
124
|
-
f"-o {apply_transforms_out_path.resolve()}",
|
125
|
-
]
|
126
|
-
# Call antsApplyTransforms
|
127
|
-
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
128
|
-
|
129
|
-
# Load nifti
|
130
|
-
output_img = nib.load(apply_transforms_out_path)
|
131
|
-
|
132
|
-
return output_img, resample_image_out_path # type: ignore
|
133
|
-
|
134
|
-
def preprocess(
|
135
|
-
self,
|
136
|
-
input: Dict[str, Any],
|
137
|
-
extra_input: Optional[Dict[str, Any]] = None,
|
138
|
-
) -> Tuple[str, Dict[str, Any]]:
|
139
|
-
"""Preprocess.
|
140
|
-
|
141
|
-
Parameters
|
142
|
-
----------
|
143
|
-
input : dict
|
144
|
-
A single input from the Junifer Data object in which to preprocess.
|
145
|
-
extra_input : dict, optional
|
146
|
-
The other fields in the Junifer Data object. Must include the
|
147
|
-
``Warp`` and ``ref`` value's keys.
|
148
|
-
|
149
|
-
Returns
|
150
|
-
-------
|
151
|
-
str
|
152
|
-
The key to store the output in the Junifer Data object.
|
153
|
-
dict
|
154
|
-
The computed result as dictionary. This will be stored in the
|
155
|
-
Junifer Data object under the key ``data`` of the data type.
|
156
|
-
|
157
|
-
Raises
|
158
|
-
------
|
159
|
-
ValueError
|
160
|
-
If ``extra_input`` is None.
|
161
|
-
|
162
|
-
"""
|
163
|
-
logger.debug("Warping via ANTs using antsApplyTransforms")
|
164
|
-
# Check for extra inputs
|
165
|
-
if extra_input is None:
|
166
|
-
raise_error(
|
167
|
-
f"No extra input provided, requires `Warp` and `{self.ref}` "
|
168
|
-
"data types in particular."
|
169
|
-
)
|
170
|
-
# Retrieve data type info to warp
|
171
|
-
to_warp_input = input
|
172
|
-
# Retrieve data type info to use as reference
|
173
|
-
ref_input = extra_input[self.ref]
|
174
|
-
# Retrieve Warp data
|
175
|
-
warp = extra_input["Warp"]
|
176
|
-
# Replace original data with warped data and add resampled reference
|
177
|
-
# path
|
178
|
-
input["data"], input["reference_path"] = self._run_apply_transforms(
|
179
|
-
input_data=to_warp_input,
|
180
|
-
ref_path=ref_input["path"],
|
181
|
-
warp_path=warp["path"],
|
182
|
-
)
|
183
|
-
# Use reference input's space as warped input's space
|
184
|
-
input["space"] = ref_input["space"]
|
185
|
-
return self.on, input
|
@@ -1,56 +0,0 @@
|
|
1
|
-
"""Provide tests for AntsApplyTransformsWarper."""
|
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 DMCC13Benchmark
|
11
|
-
from junifer.datareader import DefaultDataReader
|
12
|
-
from junifer.pipeline.utils import _check_ants
|
13
|
-
from junifer.preprocess.ants.ants_apply_transforms_warper import (
|
14
|
-
_AntsApplyTransformsWarper,
|
15
|
-
)
|
16
|
-
|
17
|
-
|
18
|
-
def test_AntsApplyTransformsWarper_init() -> None:
|
19
|
-
"""Test AntsApplyTransformsWarper init."""
|
20
|
-
ants_apply_transforms_warper = _AntsApplyTransformsWarper(
|
21
|
-
reference="T1w", on="BOLD"
|
22
|
-
)
|
23
|
-
assert ants_apply_transforms_warper.ref == "T1w"
|
24
|
-
assert ants_apply_transforms_warper.on == "BOLD"
|
25
|
-
|
26
|
-
|
27
|
-
@pytest.mark.skipif(
|
28
|
-
_check_ants() is False, reason="requires ANTs to be in PATH"
|
29
|
-
)
|
30
|
-
@pytest.mark.skipif(
|
31
|
-
socket.gethostname() != "juseless",
|
32
|
-
reason="only for juseless",
|
33
|
-
)
|
34
|
-
def test_AntsApplyTransformsWarper_preprocess() -> None:
|
35
|
-
"""Test AntsApplyTransformsWarper preprocess."""
|
36
|
-
with DMCC13Benchmark(
|
37
|
-
types=["BOLD", "T1w", "Warp"],
|
38
|
-
sessions=["ses-wave1bas"],
|
39
|
-
tasks=["Rest"],
|
40
|
-
phase_encodings=["AP"],
|
41
|
-
runs=["1"],
|
42
|
-
native_t1w=True,
|
43
|
-
) as dg:
|
44
|
-
# Read data
|
45
|
-
element_data = DefaultDataReader().fit_transform(
|
46
|
-
dg[("sub-f9057kp", "ses-wave1bas", "Rest", "AP", "1")]
|
47
|
-
)
|
48
|
-
# Preprocess data
|
49
|
-
data_type, data = _AntsApplyTransformsWarper(
|
50
|
-
reference="T1w", on="BOLD"
|
51
|
-
).preprocess(
|
52
|
-
input=element_data["BOLD"],
|
53
|
-
extra_input=element_data,
|
54
|
-
)
|
55
|
-
assert isinstance(data_type, str)
|
56
|
-
assert isinstance(data, dict)
|