junifer 0.0.5.dev242__py3-none-any.whl → 0.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- junifer/__init__.py +2 -31
- junifer/__init__.pyi +37 -0
- junifer/_version.py +9 -4
- junifer/api/__init__.py +3 -5
- junifer/api/__init__.pyi +4 -0
- junifer/api/decorators.py +14 -19
- junifer/api/functions.py +165 -109
- junifer/api/py.typed +0 -0
- junifer/api/queue_context/__init__.py +2 -4
- junifer/api/queue_context/__init__.pyi +5 -0
- junifer/api/queue_context/gnu_parallel_local_adapter.py +22 -6
- junifer/api/queue_context/htcondor_adapter.py +23 -6
- junifer/api/queue_context/py.typed +0 -0
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +3 -3
- junifer/api/queue_context/tests/test_htcondor_adapter.py +3 -3
- junifer/api/tests/test_functions.py +168 -74
- junifer/cli/__init__.py +24 -0
- junifer/cli/__init__.pyi +3 -0
- junifer/{api → cli}/cli.py +141 -125
- junifer/cli/parser.py +235 -0
- junifer/cli/py.typed +0 -0
- junifer/{api → cli}/tests/test_cli.py +8 -8
- junifer/{api/tests/test_api_utils.py → cli/tests/test_cli_utils.py} +5 -4
- junifer/{api → cli}/tests/test_parser.py +2 -2
- junifer/{api → cli}/utils.py +6 -16
- junifer/configs/juseless/__init__.py +2 -2
- junifer/configs/juseless/__init__.pyi +3 -0
- junifer/configs/juseless/datagrabbers/__init__.py +2 -12
- junifer/configs/juseless/datagrabbers/__init__.pyi +13 -0
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +2 -2
- junifer/configs/juseless/datagrabbers/py.typed +0 -0
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -2
- junifer/configs/juseless/datagrabbers/ucla.py +4 -4
- junifer/configs/juseless/py.typed +0 -0
- junifer/conftest.py +25 -0
- junifer/data/__init__.py +2 -42
- junifer/data/__init__.pyi +29 -0
- junifer/data/_dispatch.py +248 -0
- junifer/data/coordinates/__init__.py +9 -0
- junifer/data/coordinates/__init__.pyi +5 -0
- junifer/data/coordinates/_ants_coordinates_warper.py +104 -0
- junifer/data/coordinates/_coordinates.py +385 -0
- junifer/data/coordinates/_fsl_coordinates_warper.py +81 -0
- junifer/data/{tests → coordinates/tests}/test_coordinates.py +26 -33
- junifer/data/masks/__init__.py +9 -0
- junifer/data/masks/__init__.pyi +6 -0
- junifer/data/masks/_ants_mask_warper.py +177 -0
- junifer/data/masks/_fsl_mask_warper.py +106 -0
- junifer/data/masks/_masks.py +802 -0
- junifer/data/{tests → masks/tests}/test_masks.py +67 -63
- junifer/data/parcellations/__init__.py +9 -0
- junifer/data/parcellations/__init__.pyi +6 -0
- junifer/data/parcellations/_ants_parcellation_warper.py +166 -0
- junifer/data/parcellations/_fsl_parcellation_warper.py +89 -0
- junifer/data/parcellations/_parcellations.py +1388 -0
- junifer/data/{tests → parcellations/tests}/test_parcellations.py +165 -295
- junifer/data/pipeline_data_registry_base.py +76 -0
- junifer/data/py.typed +0 -0
- junifer/data/template_spaces.py +44 -79
- junifer/data/tests/test_data_utils.py +1 -2
- junifer/data/tests/test_template_spaces.py +8 -4
- junifer/data/utils.py +109 -4
- junifer/datagrabber/__init__.py +2 -26
- junifer/datagrabber/__init__.pyi +27 -0
- junifer/datagrabber/aomic/__init__.py +2 -4
- junifer/datagrabber/aomic/__init__.pyi +5 -0
- junifer/datagrabber/aomic/id1000.py +81 -52
- junifer/datagrabber/aomic/piop1.py +83 -55
- junifer/datagrabber/aomic/piop2.py +85 -56
- junifer/datagrabber/aomic/py.typed +0 -0
- junifer/datagrabber/aomic/tests/test_id1000.py +19 -12
- junifer/datagrabber/aomic/tests/test_piop1.py +52 -18
- junifer/datagrabber/aomic/tests/test_piop2.py +50 -17
- junifer/datagrabber/base.py +22 -18
- junifer/datagrabber/datalad_base.py +71 -34
- junifer/datagrabber/dmcc13_benchmark.py +31 -18
- junifer/datagrabber/hcp1200/__init__.py +2 -3
- junifer/datagrabber/hcp1200/__init__.pyi +4 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -3
- junifer/datagrabber/hcp1200/hcp1200.py +26 -15
- junifer/datagrabber/hcp1200/py.typed +0 -0
- junifer/datagrabber/hcp1200/tests/test_hcp1200.py +8 -2
- junifer/datagrabber/multiple.py +14 -9
- junifer/datagrabber/pattern.py +132 -96
- junifer/datagrabber/pattern_validation_mixin.py +206 -94
- junifer/datagrabber/py.typed +0 -0
- junifer/datagrabber/tests/test_datalad_base.py +27 -12
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +28 -11
- junifer/datagrabber/tests/test_multiple.py +48 -2
- junifer/datagrabber/tests/test_pattern_datalad.py +1 -1
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +6 -6
- junifer/datareader/__init__.py +2 -2
- junifer/datareader/__init__.pyi +3 -0
- junifer/datareader/default.py +6 -6
- junifer/datareader/py.typed +0 -0
- junifer/external/nilearn/__init__.py +2 -3
- junifer/external/nilearn/__init__.pyi +4 -0
- junifer/external/nilearn/junifer_connectivity_measure.py +25 -17
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +4 -4
- junifer/external/nilearn/py.typed +0 -0
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +17 -16
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +2 -3
- junifer/markers/__init__.py +2 -38
- junifer/markers/__init__.pyi +37 -0
- junifer/markers/base.py +11 -14
- junifer/markers/brainprint.py +12 -14
- junifer/markers/complexity/__init__.py +2 -18
- junifer/markers/complexity/__init__.pyi +17 -0
- junifer/markers/complexity/complexity_base.py +9 -11
- junifer/markers/complexity/hurst_exponent.py +7 -7
- junifer/markers/complexity/multiscale_entropy_auc.py +7 -7
- junifer/markers/complexity/perm_entropy.py +7 -7
- junifer/markers/complexity/py.typed +0 -0
- junifer/markers/complexity/range_entropy.py +7 -7
- junifer/markers/complexity/range_entropy_auc.py +7 -7
- junifer/markers/complexity/sample_entropy.py +7 -7
- junifer/markers/complexity/tests/test_complexity_base.py +1 -1
- junifer/markers/complexity/tests/test_hurst_exponent.py +5 -5
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +5 -5
- junifer/markers/complexity/tests/test_perm_entropy.py +5 -5
- junifer/markers/complexity/tests/test_range_entropy.py +5 -5
- junifer/markers/complexity/tests/test_range_entropy_auc.py +5 -5
- junifer/markers/complexity/tests/test_sample_entropy.py +5 -5
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +5 -5
- junifer/markers/complexity/weighted_perm_entropy.py +7 -7
- junifer/markers/ets_rss.py +12 -11
- junifer/markers/falff/__init__.py +2 -3
- junifer/markers/falff/__init__.pyi +4 -0
- junifer/markers/falff/_afni_falff.py +38 -45
- junifer/markers/falff/_junifer_falff.py +16 -19
- junifer/markers/falff/falff_base.py +7 -11
- junifer/markers/falff/falff_parcels.py +9 -9
- junifer/markers/falff/falff_spheres.py +8 -8
- junifer/markers/falff/py.typed +0 -0
- junifer/markers/falff/tests/test_falff_spheres.py +3 -1
- junifer/markers/functional_connectivity/__init__.py +2 -12
- junifer/markers/functional_connectivity/__init__.pyi +13 -0
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +9 -8
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +8 -8
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +7 -7
- junifer/markers/functional_connectivity/functional_connectivity_base.py +13 -12
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +8 -8
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +7 -7
- junifer/markers/functional_connectivity/py.typed +0 -0
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +1 -2
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +1 -2
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +6 -6
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +5 -5
- junifer/markers/parcel_aggregation.py +22 -17
- junifer/markers/py.typed +0 -0
- junifer/markers/reho/__init__.py +2 -3
- junifer/markers/reho/__init__.pyi +4 -0
- junifer/markers/reho/_afni_reho.py +29 -35
- junifer/markers/reho/_junifer_reho.py +13 -14
- junifer/markers/reho/py.typed +0 -0
- junifer/markers/reho/reho_base.py +7 -11
- junifer/markers/reho/reho_parcels.py +10 -10
- junifer/markers/reho/reho_spheres.py +9 -9
- junifer/markers/sphere_aggregation.py +22 -17
- junifer/markers/temporal_snr/__init__.py +2 -3
- junifer/markers/temporal_snr/__init__.pyi +4 -0
- junifer/markers/temporal_snr/py.typed +0 -0
- junifer/markers/temporal_snr/temporal_snr_base.py +11 -10
- junifer/markers/temporal_snr/temporal_snr_parcels.py +8 -8
- junifer/markers/temporal_snr/temporal_snr_spheres.py +7 -7
- junifer/markers/tests/test_ets_rss.py +3 -3
- junifer/markers/tests/test_parcel_aggregation.py +24 -24
- junifer/markers/tests/test_sphere_aggregation.py +6 -6
- junifer/markers/utils.py +3 -3
- junifer/onthefly/__init__.py +2 -1
- junifer/onthefly/_brainprint.py +138 -0
- junifer/onthefly/read_transform.py +5 -8
- junifer/pipeline/__init__.py +2 -10
- junifer/pipeline/__init__.pyi +13 -0
- junifer/{markers/collection.py → pipeline/marker_collection.py} +8 -14
- junifer/pipeline/pipeline_component_registry.py +294 -0
- junifer/pipeline/pipeline_step_mixin.py +15 -11
- junifer/pipeline/py.typed +0 -0
- junifer/{markers/tests/test_collection.py → pipeline/tests/test_marker_collection.py} +2 -3
- junifer/pipeline/tests/test_pipeline_component_registry.py +200 -0
- junifer/pipeline/tests/test_pipeline_step_mixin.py +36 -37
- junifer/pipeline/tests/test_update_meta_mixin.py +4 -4
- junifer/pipeline/tests/test_workdir_manager.py +43 -0
- junifer/pipeline/update_meta_mixin.py +21 -17
- junifer/pipeline/utils.py +6 -6
- junifer/pipeline/workdir_manager.py +19 -5
- junifer/preprocess/__init__.py +2 -10
- junifer/preprocess/__init__.pyi +11 -0
- junifer/preprocess/base.py +10 -10
- junifer/preprocess/confounds/__init__.py +2 -2
- junifer/preprocess/confounds/__init__.pyi +3 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +243 -64
- junifer/preprocess/confounds/py.typed +0 -0
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +121 -14
- junifer/preprocess/py.typed +0 -0
- junifer/preprocess/smoothing/__init__.py +2 -2
- junifer/preprocess/smoothing/__init__.pyi +3 -0
- junifer/preprocess/smoothing/_afni_smoothing.py +40 -40
- junifer/preprocess/smoothing/_fsl_smoothing.py +22 -32
- junifer/preprocess/smoothing/_nilearn_smoothing.py +35 -14
- junifer/preprocess/smoothing/py.typed +0 -0
- junifer/preprocess/smoothing/smoothing.py +11 -13
- junifer/preprocess/warping/__init__.py +2 -2
- junifer/preprocess/warping/__init__.pyi +3 -0
- junifer/preprocess/warping/_ants_warper.py +136 -32
- junifer/preprocess/warping/_fsl_warper.py +73 -22
- junifer/preprocess/warping/py.typed +0 -0
- junifer/preprocess/warping/space_warper.py +39 -11
- junifer/preprocess/warping/tests/test_space_warper.py +5 -9
- junifer/py.typed +0 -0
- junifer/stats.py +5 -5
- junifer/storage/__init__.py +2 -10
- junifer/storage/__init__.pyi +11 -0
- junifer/storage/base.py +47 -13
- junifer/storage/hdf5.py +95 -33
- junifer/storage/pandas_base.py +12 -11
- junifer/storage/py.typed +0 -0
- junifer/storage/sqlite.py +11 -11
- junifer/storage/tests/test_hdf5.py +86 -4
- junifer/storage/tests/test_sqlite.py +2 -2
- junifer/storage/tests/test_storage_base.py +5 -2
- junifer/storage/tests/test_utils.py +33 -7
- junifer/storage/utils.py +95 -9
- junifer/testing/__init__.py +2 -3
- junifer/testing/__init__.pyi +4 -0
- junifer/testing/datagrabbers.py +10 -11
- junifer/testing/py.typed +0 -0
- junifer/testing/registry.py +4 -7
- junifer/testing/tests/test_testing_registry.py +9 -17
- junifer/tests/test_stats.py +2 -2
- junifer/typing/__init__.py +9 -0
- junifer/typing/__init__.pyi +31 -0
- junifer/typing/_typing.py +68 -0
- junifer/utils/__init__.py +2 -12
- junifer/utils/__init__.pyi +18 -0
- junifer/utils/_config.py +110 -0
- junifer/utils/_yaml.py +16 -0
- junifer/utils/helpers.py +6 -6
- junifer/utils/logging.py +117 -8
- junifer/utils/py.typed +0 -0
- junifer/{pipeline → utils}/singleton.py +19 -14
- junifer/utils/tests/test_config.py +59 -0
- {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info}/METADATA +43 -38
- junifer-0.0.6.dist-info/RECORD +350 -0
- {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info}/WHEEL +1 -1
- junifer-0.0.6.dist-info/entry_points.txt +2 -0
- junifer/api/parser.py +0 -118
- junifer/data/coordinates.py +0 -408
- junifer/data/masks.py +0 -670
- junifer/data/parcellations.py +0 -1828
- junifer/pipeline/registry.py +0 -177
- junifer/pipeline/tests/test_registry.py +0 -150
- junifer-0.0.5.dev242.dist-info/RECORD +0 -275
- junifer-0.0.5.dev242.dist-info/entry_points.txt +0 -2
- /junifer/{api → cli}/tests/data/gmd_mean.yaml +0 -0
- /junifer/{api → cli}/tests/data/gmd_mean_htcondor.yaml +0 -0
- /junifer/{api → cli}/tests/data/partly_cloudy_agg_mean_tian.yml +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/AutobiographicalMemory_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAC_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAR_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/DMNBuckner_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Dosenbach2010_MNI_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Empathy_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Motor_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/MultiTask_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/PhysioStress_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2011_MNI_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2013_MNI_VOIs.tsv +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Rew_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Somatosensory_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/ToM_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/VigAtt_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/WM_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/eMDN_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/eSAD_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/extDMN_VOIs.txt +0 -0
- {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info/licenses}/AUTHORS.rst +0 -0
- {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info/licenses}/LICENSE.md +0 -0
- {junifer-0.0.5.dev242.dist-info → junifer-0.0.6.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
import socket
|
9
9
|
from pathlib import Path
|
10
|
-
from typing import Callable,
|
10
|
+
from typing import Callable, Optional, Union
|
11
11
|
|
12
12
|
import nibabel as nib
|
13
13
|
import numpy as np
|
@@ -20,15 +20,11 @@ from nilearn.masking import (
|
|
20
20
|
)
|
21
21
|
from numpy.testing import assert_array_almost_equal, assert_array_equal
|
22
22
|
|
23
|
-
from junifer.data
|
24
|
-
|
23
|
+
from junifer.data import MaskRegistry
|
24
|
+
from junifer.data.masks import compute_brain_mask
|
25
|
+
from junifer.data.masks._masks import (
|
25
26
|
_load_ukb_mask,
|
26
27
|
_load_vickery_patil_mask,
|
27
|
-
compute_brain_mask,
|
28
|
-
get_mask,
|
29
|
-
list_masks,
|
30
|
-
load_mask,
|
31
|
-
register_mask,
|
32
28
|
)
|
33
29
|
from junifer.datagrabber import DMCC13Benchmark
|
34
30
|
from junifer.datareader import DefaultDataReader
|
@@ -68,10 +64,9 @@ def test_compute_brain_mask(mask_type: str, threshold: float) -> None:
|
|
68
64
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
69
65
|
mask = compute_brain_mask(
|
70
66
|
target_data=element_data["BOLD"],
|
71
|
-
extra_input=None,
|
72
67
|
mask_type=mask_type,
|
73
68
|
)
|
74
|
-
assert isinstance(mask, nib.Nifti1Image)
|
69
|
+
assert isinstance(mask, nib.nifti1.Nifti1Image)
|
75
70
|
|
76
71
|
|
77
72
|
@pytest.mark.skipif(
|
@@ -111,13 +106,13 @@ def test_compute_brain_mask_for_native(mask_type: str) -> None:
|
|
111
106
|
extra_input=None,
|
112
107
|
mask_type=mask_type,
|
113
108
|
)
|
114
|
-
assert isinstance(mask, nib.Nifti1Image)
|
109
|
+
assert isinstance(mask, nib.nifti1.Nifti1Image)
|
115
110
|
|
116
111
|
|
117
|
-
def
|
112
|
+
def test_register_built_in_check() -> None:
|
118
113
|
"""Test mask registration check for built-in masks."""
|
119
114
|
with pytest.raises(ValueError, match=r"built-in mask"):
|
120
|
-
|
115
|
+
MaskRegistry().register(
|
121
116
|
name="GM_prob0.2",
|
122
117
|
mask_path="testmask.nii.gz",
|
123
118
|
space="MNI",
|
@@ -125,39 +120,38 @@ def test_register_mask_built_in_check() -> None:
|
|
125
120
|
)
|
126
121
|
|
127
122
|
|
128
|
-
def
|
123
|
+
def test_list_incorrect() -> None:
|
129
124
|
"""Test incorrect information check for list masks."""
|
130
|
-
|
131
|
-
assert "testmask" not in masks
|
125
|
+
assert "testmask" not in MaskRegistry().list
|
132
126
|
|
133
127
|
|
134
|
-
def
|
128
|
+
def test_register_already_registered() -> None:
|
135
129
|
"""Test mask registration check for already registered."""
|
136
130
|
# Register custom mask
|
137
|
-
|
131
|
+
MaskRegistry().register(
|
138
132
|
name="testmask",
|
139
133
|
mask_path="testmask.nii.gz",
|
140
134
|
space="MNI",
|
141
135
|
)
|
142
|
-
out =
|
136
|
+
out = MaskRegistry().load("testmask", path_only=True)
|
143
137
|
assert out[1] is not None
|
144
138
|
assert out[1].name == "testmask.nii.gz"
|
145
139
|
|
146
140
|
# Try registering again
|
147
141
|
with pytest.raises(ValueError, match=r"already registered."):
|
148
|
-
|
142
|
+
MaskRegistry().register(
|
149
143
|
name="testmask",
|
150
144
|
mask_path="testmask.nii.gz",
|
151
145
|
space="MNI",
|
152
146
|
)
|
153
|
-
|
147
|
+
MaskRegistry().register(
|
154
148
|
name="testmask",
|
155
149
|
mask_path="testmask2.nii.gz",
|
156
150
|
space="MNI",
|
157
151
|
overwrite=True,
|
158
152
|
)
|
159
153
|
|
160
|
-
out =
|
154
|
+
out = MaskRegistry().load("testmask", path_only=True)
|
161
155
|
assert out[1] is not None
|
162
156
|
assert out[1].name == "testmask2.nii.gz"
|
163
157
|
|
@@ -170,7 +164,7 @@ def test_register_mask_already_registered() -> None:
|
|
170
164
|
("testmask_3", Path("testmask_3.nii.gz"), "MNI", True),
|
171
165
|
],
|
172
166
|
)
|
173
|
-
def
|
167
|
+
def test_register(
|
174
168
|
name: str,
|
175
169
|
mask_path: str,
|
176
170
|
space: str,
|
@@ -191,17 +185,16 @@ def test_register_mask(
|
|
191
185
|
|
192
186
|
"""
|
193
187
|
# Register custom mask
|
194
|
-
|
188
|
+
MaskRegistry().register(
|
195
189
|
name=name,
|
196
190
|
mask_path=mask_path,
|
197
191
|
space=space,
|
198
192
|
overwrite=overwrite,
|
199
193
|
)
|
200
194
|
# List available mask and check registration
|
201
|
-
|
202
|
-
assert name in masks
|
195
|
+
assert name in MaskRegistry().list
|
203
196
|
# Load registered mask
|
204
|
-
_, fname, mask_space =
|
197
|
+
_, fname, mask_space = MaskRegistry().load(name=name, path_only=True)
|
205
198
|
# Check values for registered mask
|
206
199
|
assert fname is not None
|
207
200
|
assert fname.name == f"{name}.nii.gz"
|
@@ -216,7 +209,7 @@ def test_register_mask(
|
|
216
209
|
"UKB_15K_GM",
|
217
210
|
],
|
218
211
|
)
|
219
|
-
def
|
212
|
+
def test_list_correct(mask_name: str) -> None:
|
220
213
|
"""Test correct information check for list masks.
|
221
214
|
|
222
215
|
Parameters
|
@@ -225,14 +218,13 @@ def test_list_masks_correct(mask_name: str) -> None:
|
|
225
218
|
The parametrized mask name.
|
226
219
|
|
227
220
|
"""
|
228
|
-
|
229
|
-
assert mask_name in masks
|
221
|
+
assert mask_name in MaskRegistry().list
|
230
222
|
|
231
223
|
|
232
|
-
def
|
224
|
+
def test_load_incorrect() -> None:
|
233
225
|
"""Test loading of invalid masks."""
|
234
226
|
with pytest.raises(ValueError, match=r"not found"):
|
235
|
-
|
227
|
+
MaskRegistry().load("wrongmask")
|
236
228
|
|
237
229
|
|
238
230
|
@pytest.mark.parametrize(
|
@@ -261,7 +253,7 @@ def test_load_mask_incorrect() -> None:
|
|
261
253
|
def test_vickery_patil(
|
262
254
|
name: str,
|
263
255
|
resolution: Optional[float],
|
264
|
-
pixdim:
|
256
|
+
pixdim: list[float],
|
265
257
|
fname: str,
|
266
258
|
) -> None:
|
267
259
|
"""Test Vickery-Patil mask.
|
@@ -278,7 +270,7 @@ def test_vickery_patil(
|
|
278
270
|
The parametrized name of the mask file.
|
279
271
|
|
280
272
|
"""
|
281
|
-
mask, mask_fname, space =
|
273
|
+
mask, mask_fname, space = MaskRegistry().load(name, resolution=resolution)
|
282
274
|
assert_array_almost_equal(
|
283
275
|
mask.header["pixdim"][1:4], pixdim # type: ignore
|
284
276
|
)
|
@@ -295,7 +287,7 @@ def test_vickery_patil_error() -> None:
|
|
295
287
|
|
296
288
|
def test_ukb() -> None:
|
297
289
|
"""Test UKB mask."""
|
298
|
-
mask, mask_fname, space =
|
290
|
+
mask, mask_fname, space = MaskRegistry().load("UKB_15K_GM", resolution=2.0)
|
299
291
|
assert_array_almost_equal(mask.header["pixdim"][1:4], 2.0) # type: ignore
|
300
292
|
assert space == "MNI152NLin6Asym"
|
301
293
|
assert mask_fname is not None
|
@@ -308,18 +300,20 @@ def test_ukb_error() -> None:
|
|
308
300
|
_load_ukb_mask(name="wrong")
|
309
301
|
|
310
302
|
|
311
|
-
def
|
312
|
-
"""Test
|
303
|
+
def test_get() -> None:
|
304
|
+
"""Test tailored mask fetch."""
|
313
305
|
with OasisVBMTestingDataGrabber() as dg:
|
314
306
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
315
307
|
vbm_gm = element_data["VBM_GM"]
|
316
308
|
vbm_gm_img = vbm_gm["data"]
|
317
|
-
mask =
|
309
|
+
mask = MaskRegistry().get(
|
310
|
+
masks="compute_brain_mask", target_data=vbm_gm
|
311
|
+
)
|
318
312
|
|
319
313
|
assert mask.shape == vbm_gm_img.shape
|
320
314
|
assert_array_equal(mask.affine, vbm_gm_img.affine)
|
321
315
|
|
322
|
-
raw_mask_callable, _, _ =
|
316
|
+
raw_mask_callable, _, _ = MaskRegistry().load(
|
323
317
|
"compute_brain_mask", resolution=1.5
|
324
318
|
)
|
325
319
|
raw_mask_img = raw_mask_callable(vbm_gm) # type: ignore
|
@@ -338,7 +332,7 @@ def test_mask_callable() -> None:
|
|
338
332
|
def ident(x):
|
339
333
|
return x
|
340
334
|
|
341
|
-
|
335
|
+
MaskRegistry()._registry["identity"] = {
|
342
336
|
"family": "Callable",
|
343
337
|
"func": ident,
|
344
338
|
"space": "MNI152Lin",
|
@@ -347,44 +341,48 @@ def test_mask_callable() -> None:
|
|
347
341
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
348
342
|
vbm_gm = element_data["VBM_GM"]
|
349
343
|
vbm_gm_img = vbm_gm["data"]
|
350
|
-
mask =
|
344
|
+
mask = MaskRegistry().get(masks="identity", target_data=vbm_gm)
|
351
345
|
|
352
346
|
assert_array_equal(mask.get_fdata(), vbm_gm_img.get_fdata())
|
353
347
|
|
354
|
-
del
|
348
|
+
del MaskRegistry()._registry["identity"]
|
355
349
|
|
356
350
|
|
357
|
-
def
|
358
|
-
"""Test passing wrong parameters to
|
351
|
+
def test_get_errors() -> None:
|
352
|
+
"""Test passing wrong parameters to fetch mask."""
|
359
353
|
with OasisVBMTestingDataGrabber() as dg:
|
360
354
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
361
355
|
vbm_gm = element_data["VBM_GM"]
|
362
356
|
# Test wrong masks definitions (more than one key per dict)
|
363
357
|
with pytest.raises(ValueError, match=r"only one key"):
|
364
|
-
|
358
|
+
MaskRegistry().get(
|
359
|
+
masks={"GM_prob0.2": {}, "Other": {}}, target_data=vbm_gm
|
360
|
+
)
|
365
361
|
|
366
362
|
# Test wrong masks definitions (pass paramaeters to non-callable mask)
|
367
363
|
with pytest.raises(ValueError, match=r"callable params"):
|
368
|
-
|
364
|
+
MaskRegistry().get(
|
365
|
+
masks={"GM_prob0.2": {"param": 1}}, target_data=vbm_gm
|
366
|
+
)
|
369
367
|
|
370
368
|
# Pass only parameters to the intersection function
|
371
369
|
with pytest.raises(
|
372
370
|
ValueError, match=r" At least one mask is required."
|
373
371
|
):
|
374
|
-
|
372
|
+
MaskRegistry().get(masks={"threshold": 1}, target_data=vbm_gm)
|
375
373
|
|
376
374
|
# Pass parameters to the intersection function when only one mask
|
377
375
|
with pytest.raises(
|
378
376
|
ValueError, match=r"parameters to the intersection"
|
379
377
|
):
|
380
|
-
|
378
|
+
MaskRegistry().get(
|
381
379
|
masks=["compute_brain_mask", {"threshold": 1}],
|
382
380
|
target_data=vbm_gm,
|
383
381
|
)
|
384
382
|
|
385
383
|
# Test "inherited" masks error
|
386
384
|
with pytest.raises(ValueError, match=r"provide `mask`"):
|
387
|
-
|
385
|
+
MaskRegistry().get(masks="inherit", target_data=vbm_gm)
|
388
386
|
|
389
387
|
|
390
388
|
@pytest.mark.parametrize(
|
@@ -397,7 +395,7 @@ def test_get_mask_errors() -> None:
|
|
397
395
|
def test_nilearn_compute_masks(
|
398
396
|
mask_name: str,
|
399
397
|
function: Callable,
|
400
|
-
params: Union[
|
398
|
+
params: Union[dict, None],
|
401
399
|
resample: bool,
|
402
400
|
) -> None:
|
403
401
|
"""Test using nilearn compute mask functions.
|
@@ -425,7 +423,7 @@ def test_nilearn_compute_masks(
|
|
425
423
|
else:
|
426
424
|
mask_spec = {mask_name: params}
|
427
425
|
|
428
|
-
mask =
|
426
|
+
mask = MaskRegistry().get(masks=mask_spec, target_data=bold)
|
429
427
|
|
430
428
|
assert_array_equal(mask.affine, bold_img.affine)
|
431
429
|
|
@@ -443,15 +441,15 @@ def test_nilearn_compute_masks(
|
|
443
441
|
assert_array_equal(mask.get_fdata(), ni_mask.get_fdata())
|
444
442
|
|
445
443
|
|
446
|
-
def
|
447
|
-
"""Test using the inherit mask functionality."""
|
444
|
+
def test_get_inherit() -> None:
|
445
|
+
"""Test mask fetch using the inherit mask functionality."""
|
448
446
|
with SPMAuditoryTestingDataGrabber() as dg:
|
449
447
|
element_data = DefaultDataReader().fit_transform(dg["sub001"])
|
450
448
|
# Compute brain mask using nilearn
|
451
449
|
gm_mask = compute_brain_mask(element_data["BOLD"], threshold=0.2)
|
452
450
|
|
453
451
|
# Get mask using the compute_brain_mask function
|
454
|
-
mask1 =
|
452
|
+
mask1 = MaskRegistry().get(
|
455
453
|
masks={"compute_brain_mask": {"threshold": 0.2}},
|
456
454
|
target_data=element_data["BOLD"],
|
457
455
|
)
|
@@ -463,7 +461,7 @@ def test_get_mask_inherit() -> None:
|
|
463
461
|
"data": gm_mask,
|
464
462
|
"space": element_data["BOLD"]["space"],
|
465
463
|
}
|
466
|
-
mask2 =
|
464
|
+
mask2 = MaskRegistry().get(
|
467
465
|
masks="inherit",
|
468
466
|
target_data=bold_dict,
|
469
467
|
)
|
@@ -479,8 +477,8 @@ def test_get_mask_inherit() -> None:
|
|
479
477
|
(["compute_brain_mask", "compute_epi_mask"], {}),
|
480
478
|
],
|
481
479
|
)
|
482
|
-
def
|
483
|
-
masks: Union[str,
|
480
|
+
def test_get_multiple(
|
481
|
+
masks: Union[str, dict, list[Union[dict, str]]], params: dict
|
484
482
|
) -> None:
|
485
483
|
"""Test getting multiple masks.
|
486
484
|
|
@@ -505,7 +503,7 @@ def test_get_mask_multiple(
|
|
505
503
|
target_img = element_data["BOLD"]["data"]
|
506
504
|
resolution = np.min(target_img.header.get_zooms()[:3])
|
507
505
|
|
508
|
-
computed =
|
506
|
+
computed = MaskRegistry().get(
|
509
507
|
masks=junifer_masks, target_data=element_data["BOLD"]
|
510
508
|
)
|
511
509
|
|
@@ -516,16 +514,18 @@ def test_get_mask_multiple(
|
|
516
514
|
mask_funcs = [
|
517
515
|
x
|
518
516
|
for x in masks_names
|
519
|
-
if
|
517
|
+
if MaskRegistry()._registry[x]["family"] == "Callable"
|
520
518
|
]
|
521
519
|
mask_files = [
|
522
520
|
x
|
523
521
|
for x in masks_names
|
524
|
-
if
|
522
|
+
if MaskRegistry()._registry[x]["family"] != "Callable"
|
525
523
|
]
|
526
524
|
|
527
525
|
mask_imgs = [
|
528
|
-
|
526
|
+
MaskRegistry().load(
|
527
|
+
t_mask, path_only=False, resolution=resolution
|
528
|
+
)[0]
|
529
529
|
for t_mask in mask_files
|
530
530
|
]
|
531
531
|
|
@@ -533,10 +533,14 @@ def test_get_mask_multiple(
|
|
533
533
|
# Bypass for custom mask
|
534
534
|
if t_func == "compute_brain_mask":
|
535
535
|
mask_imgs.append(
|
536
|
-
|
536
|
+
MaskRegistry()._registry[t_func]["func"](
|
537
|
+
element_data["BOLD"]
|
538
|
+
)
|
537
539
|
)
|
538
540
|
else:
|
539
|
-
mask_imgs.append(
|
541
|
+
mask_imgs.append(
|
542
|
+
MaskRegistry()._registry[t_func]["func"](target_img)
|
543
|
+
)
|
540
544
|
|
541
545
|
mask_imgs = [
|
542
546
|
resample_to_img(
|
@@ -0,0 +1,166 @@
|
|
1
|
+
"""Provide class for parcellation space warping via ANTs."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
import uuid
|
7
|
+
from typing import TYPE_CHECKING, Any, Optional
|
8
|
+
|
9
|
+
import nibabel as nib
|
10
|
+
|
11
|
+
from ...pipeline import WorkDirManager
|
12
|
+
from ...utils import logger, raise_error, run_ext_cmd
|
13
|
+
from ..template_spaces import get_template, get_xfm
|
14
|
+
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from nibabel.nifti1 import Nifti1Image
|
18
|
+
|
19
|
+
|
20
|
+
__all__ = ["ANTsParcellationWarper"]
|
21
|
+
|
22
|
+
|
23
|
+
class ANTsParcellationWarper:
|
24
|
+
"""Class for parcellation space warping via ANTs.
|
25
|
+
|
26
|
+
This class uses ANTs ``antsApplyTransforms`` for transformation.
|
27
|
+
|
28
|
+
"""
|
29
|
+
|
30
|
+
def warp(
|
31
|
+
self,
|
32
|
+
parcellation_name: str,
|
33
|
+
parcellation_img: "Nifti1Image",
|
34
|
+
src: str,
|
35
|
+
dst: str,
|
36
|
+
target_data: dict[str, Any],
|
37
|
+
warp_data: Optional[dict[str, Any]],
|
38
|
+
) -> "Nifti1Image":
|
39
|
+
"""Warp ``parcellation_img`` to correct space.
|
40
|
+
|
41
|
+
Parameters
|
42
|
+
----------
|
43
|
+
parcellation_name : str
|
44
|
+
The name of the parcellation.
|
45
|
+
parcellation_img : nibabel.nifti1.Nifti1Image
|
46
|
+
The parcellation image to transform.
|
47
|
+
src : str
|
48
|
+
The data type or template space to warp from.
|
49
|
+
It should be empty string if ``dst="T1w"``.
|
50
|
+
dst : str
|
51
|
+
The data type or template space to warp to.
|
52
|
+
`"T1w"` is the only allowed data type and it uses the resampled T1w
|
53
|
+
found in ``target_data.reference``. The ``"reference"``
|
54
|
+
key is added if the :class:`.SpaceWarper` is used or if the
|
55
|
+
data is provided in native space.
|
56
|
+
target_data : dict
|
57
|
+
The corresponding item of the data object to which the parcellation
|
58
|
+
will be applied.
|
59
|
+
warp_data : dict or None
|
60
|
+
The warp data item of the data object. The value is unused if
|
61
|
+
``dst!="T1w"``.
|
62
|
+
|
63
|
+
Returns
|
64
|
+
-------
|
65
|
+
nibabel.nifti1.Nifti1Image
|
66
|
+
The transformed parcellation image.
|
67
|
+
|
68
|
+
Raises
|
69
|
+
------
|
70
|
+
ValueError
|
71
|
+
If ``warp_data`` is None when ``dst="T1w"``.
|
72
|
+
|
73
|
+
"""
|
74
|
+
# Create element-scoped tempdir so that warped parcellation is
|
75
|
+
# available later as nibabel stores file path reference for
|
76
|
+
# loading on computation
|
77
|
+
prefix = (
|
78
|
+
f"ants_parcellation_warper_{parcellation_name}"
|
79
|
+
f"{'' if not src else f'_from_{src}'}_to_{dst}_"
|
80
|
+
f"{uuid.uuid1()}"
|
81
|
+
)
|
82
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
83
|
+
prefix=prefix,
|
84
|
+
)
|
85
|
+
|
86
|
+
# Native space warping
|
87
|
+
if dst == "native":
|
88
|
+
# Warp data check
|
89
|
+
if warp_data is None:
|
90
|
+
raise_error("No `warp_data` provided")
|
91
|
+
if "reference" not in target_data:
|
92
|
+
raise_error("No `reference` provided")
|
93
|
+
if "path" not in target_data["reference"]:
|
94
|
+
raise_error("No `path` provided in `reference`")
|
95
|
+
|
96
|
+
logger.debug("Using ANTs for parcellation transformation")
|
97
|
+
|
98
|
+
# Save existing parcellation image to a tempfile
|
99
|
+
prewarp_parcellation_path = (
|
100
|
+
element_tempdir / "prewarp_parcellation.nii.gz"
|
101
|
+
)
|
102
|
+
nib.save(parcellation_img, prewarp_parcellation_path)
|
103
|
+
|
104
|
+
# Create a tempfile for warped output
|
105
|
+
warped_parcellation_path = (
|
106
|
+
element_tempdir / "parcellation_warped.nii.gz"
|
107
|
+
)
|
108
|
+
# Set antsApplyTransforms command
|
109
|
+
apply_transforms_cmd = [
|
110
|
+
"antsApplyTransforms",
|
111
|
+
"-d 3",
|
112
|
+
"-e 3",
|
113
|
+
"-n 'GenericLabel[NearestNeighbor]'",
|
114
|
+
f"-i {prewarp_parcellation_path.resolve()}",
|
115
|
+
# use resampled reference
|
116
|
+
f"-r {target_data['reference']['path'].resolve()}",
|
117
|
+
f"-t {warp_data['path'].resolve()}",
|
118
|
+
f"-o {warped_parcellation_path.resolve()}",
|
119
|
+
]
|
120
|
+
# Call antsApplyTransforms
|
121
|
+
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
122
|
+
|
123
|
+
# Template space warping
|
124
|
+
else:
|
125
|
+
logger.debug(
|
126
|
+
f"Using ANTs to warp parcellation from {src} to {dst}"
|
127
|
+
)
|
128
|
+
|
129
|
+
# Get xfm file
|
130
|
+
xfm_file_path = get_xfm(src=src, dst=dst)
|
131
|
+
# Get template space image
|
132
|
+
template_space_img = get_template(
|
133
|
+
space=dst,
|
134
|
+
target_img=parcellation_img,
|
135
|
+
extra_input=None,
|
136
|
+
)
|
137
|
+
# Save template to a tempfile
|
138
|
+
template_space_img_path = element_tempdir / f"{dst}_T1w.nii.gz"
|
139
|
+
nib.save(template_space_img, template_space_img_path)
|
140
|
+
|
141
|
+
# Save existing parcellation image to a tempfile
|
142
|
+
prewarp_parcellation_path = (
|
143
|
+
element_tempdir / "prewarp_parcellation.nii.gz"
|
144
|
+
)
|
145
|
+
nib.save(parcellation_img, prewarp_parcellation_path)
|
146
|
+
|
147
|
+
# Create a tempfile for warped output
|
148
|
+
warped_parcellation_path = (
|
149
|
+
element_tempdir / "parcellation_warped.nii.gz"
|
150
|
+
)
|
151
|
+
# Set antsApplyTransforms command
|
152
|
+
apply_transforms_cmd = [
|
153
|
+
"antsApplyTransforms",
|
154
|
+
"-d 3",
|
155
|
+
"-e 3",
|
156
|
+
"-n 'GenericLabel[NearestNeighbor]'",
|
157
|
+
f"-i {prewarp_parcellation_path.resolve()}",
|
158
|
+
f"-r {template_space_img_path.resolve()}",
|
159
|
+
f"-t {xfm_file_path.resolve()}",
|
160
|
+
f"-o {warped_parcellation_path.resolve()}",
|
161
|
+
]
|
162
|
+
# Call antsApplyTransforms
|
163
|
+
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
164
|
+
|
165
|
+
# Load nifti
|
166
|
+
return nib.load(warped_parcellation_path)
|
@@ -0,0 +1,89 @@
|
|
1
|
+
"""Provide class for parcellation space warping via FSL FLIRT."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
import uuid
|
7
|
+
from typing import TYPE_CHECKING, Any
|
8
|
+
|
9
|
+
import nibabel as nib
|
10
|
+
|
11
|
+
from ...pipeline import WorkDirManager
|
12
|
+
from ...utils import logger, run_ext_cmd
|
13
|
+
|
14
|
+
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
from nibabel.nifti1 import Nifti1Image
|
17
|
+
|
18
|
+
|
19
|
+
__all__ = ["FSLParcellationWarper"]
|
20
|
+
|
21
|
+
|
22
|
+
class FSLParcellationWarper:
|
23
|
+
"""Class for parcellation space warping via FSL FLIRT.
|
24
|
+
|
25
|
+
This class uses FSL FLIRT's ``applywarp`` for transformation.
|
26
|
+
|
27
|
+
"""
|
28
|
+
|
29
|
+
def warp(
|
30
|
+
self,
|
31
|
+
parcellation_name: str,
|
32
|
+
parcellation_img: "Nifti1Image",
|
33
|
+
target_data: dict[str, Any],
|
34
|
+
warp_data: dict[str, Any],
|
35
|
+
) -> "Nifti1Image":
|
36
|
+
"""Warp ``parcellation_img`` to correct space.
|
37
|
+
|
38
|
+
Parameters
|
39
|
+
----------
|
40
|
+
parcellation_name : str
|
41
|
+
The name of the parcellation.
|
42
|
+
parcellation_img : nibabel.nifti1.Nifti1Image
|
43
|
+
The parcellation image to transform.
|
44
|
+
target_data : dict
|
45
|
+
The corresponding item of the data object to which the parcellation
|
46
|
+
will be applied.
|
47
|
+
warp_data : dict
|
48
|
+
The warp data item of the data object.
|
49
|
+
|
50
|
+
Returns
|
51
|
+
-------
|
52
|
+
nibabel.nifti1.Nifti1Image
|
53
|
+
The transformed parcellation image.
|
54
|
+
|
55
|
+
"""
|
56
|
+
logger.debug("Using FSL for parcellation transformation")
|
57
|
+
|
58
|
+
# Create element-scoped tempdir so that warped parcellation is
|
59
|
+
# available later as nibabel stores file path reference for
|
60
|
+
# loading on computation
|
61
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
62
|
+
prefix=f"fsl_parcellation_warper_{parcellation_name}_{uuid.uuid1()}"
|
63
|
+
)
|
64
|
+
|
65
|
+
# Save existing parcellation image to a tempfile
|
66
|
+
prewarp_parcellation_path = (
|
67
|
+
element_tempdir / "prewarp_parcellation.nii.gz"
|
68
|
+
)
|
69
|
+
nib.save(parcellation_img, prewarp_parcellation_path)
|
70
|
+
|
71
|
+
# Create a tempfile for warped output
|
72
|
+
warped_parcellation_path = (
|
73
|
+
element_tempdir / "parcellation_warped.nii.gz"
|
74
|
+
)
|
75
|
+
# Set applywarp command
|
76
|
+
applywarp_cmd = [
|
77
|
+
"applywarp",
|
78
|
+
"--interp=nn",
|
79
|
+
f"-i {prewarp_parcellation_path.resolve()}",
|
80
|
+
# use resampled reference
|
81
|
+
f"-r {target_data['reference']['path'].resolve()}",
|
82
|
+
f"-w {warp_data['path'].resolve()}",
|
83
|
+
f"-o {warped_parcellation_path.resolve()}",
|
84
|
+
]
|
85
|
+
# Call applywarp
|
86
|
+
run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
|
87
|
+
|
88
|
+
# Load nifti
|
89
|
+
return nib.load(warped_parcellation_path)
|