junifer 0.0.7.dev332__py3-none-any.whl → 0.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- junifer/_version.py +9 -19
- junifer/api/__init__.pyi +17 -2
- junifer/api/functions.py +222 -39
- junifer/api/queue_context/__init__.pyi +11 -3
- junifer/api/queue_context/gnu_parallel_local_adapter.py +81 -120
- junifer/api/queue_context/htcondor_adapter.py +118 -181
- junifer/api/queue_context/queue_context_adapter.py +51 -7
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +10 -35
- junifer/api/queue_context/tests/test_htcondor_adapter.py +13 -50
- junifer/api/res/fsl/run_fsl_docker.sh +4 -0
- junifer/api/tests/test_functions.py +863 -36
- junifer/cli/__init__.py +1 -7
- junifer/cli/cli.py +41 -22
- junifer/cli/parser.py +23 -147
- junifer/cli/tests/data/gmd_mean.yaml +6 -6
- junifer/cli/tests/data/gmd_mean_htcondor.yaml +6 -5
- junifer/cli/tests/data/partly_cloudy_agg_mean_tian.yml +1 -0
- junifer/cli/tests/test_cli.py +120 -4
- junifer/cli/tests/test_cli_utils.py +23 -6
- junifer/cli/utils.py +14 -9
- junifer/configs/__init__.py +5 -0
- junifer/configs/__init__.pyi +0 -0
- junifer/configs/juseless/datagrabbers/__init__.pyi +4 -2
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +18 -24
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +20 -27
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +44 -52
- junifer/configs/juseless/datagrabbers/tests/test_ixi_vbm.py +0 -7
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -19
- junifer/configs/juseless/datagrabbers/ucla.py +111 -104
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +19 -25
- junifer/configs/py.typed +0 -0
- junifer/conftest.py +63 -24
- junifer/data/_dispatch.py +9 -19
- junifer/data/coordinates/_ants_coordinates_warper.py +4 -1
- junifer/data/coordinates/_coordinates.py +51 -6
- junifer/data/coordinates/_fsl_coordinates_warper.py +4 -1
- junifer/data/coordinates/tests/test_coordinates.py +15 -3
- junifer/data/coordinates/tests/test_coordinates_names.py +37 -0
- junifer/data/maps/_ants_maps_warper.py +18 -12
- junifer/data/maps/_fsl_maps_warper.py +7 -3
- junifer/data/maps/_maps.py +12 -8
- junifer/data/maps/tests/test_maps.py +13 -21
- junifer/data/masks/_ants_mask_warper.py +18 -12
- junifer/data/masks/_fsl_mask_warper.py +7 -3
- junifer/data/masks/_masks.py +15 -12
- junifer/data/masks/tests/test_masks.py +20 -17
- junifer/data/parcellations/_ants_parcellation_warper.py +18 -12
- junifer/data/parcellations/_fsl_parcellation_warper.py +7 -5
- junifer/data/parcellations/_parcellations.py +125 -91
- junifer/data/parcellations/tests/test_parcellations.py +122 -1
- junifer/data/template_spaces.py +117 -50
- junifer/data/tests/test_template_spaces.py +10 -7
- junifer/data/utils.py +10 -8
- junifer/datagrabber/__init__.pyi +38 -5
- junifer/datagrabber/aomic/__init__.pyi +8 -1
- junifer/datagrabber/aomic/_types.py +22 -0
- junifer/datagrabber/aomic/id1000.py +193 -183
- junifer/datagrabber/aomic/piop1.py +215 -259
- junifer/datagrabber/aomic/piop2.py +211 -244
- junifer/datagrabber/aomic/tests/test_id1000.py +70 -53
- junifer/datagrabber/aomic/tests/test_piop1.py +98 -102
- junifer/datagrabber/aomic/tests/test_piop2.py +88 -85
- junifer/datagrabber/base.py +253 -79
- junifer/datagrabber/datalad_base.py +127 -76
- junifer/datagrabber/dmcc13_benchmark.py +204 -289
- junifer/datagrabber/hcp1200/__init__.pyi +7 -2
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +61 -40
- junifer/datagrabber/hcp1200/hcp1200.py +151 -164
- junifer/datagrabber/hcp1200/tests/test_hcp1200.py +116 -128
- junifer/datagrabber/multiple.py +183 -63
- junifer/datagrabber/pattern.py +358 -227
- junifer/datagrabber/pattern_datalad.py +84 -124
- junifer/datagrabber/pattern_validation_mixin.py +22 -50
- junifer/datagrabber/tests/test_base.py +17 -4
- junifer/datagrabber/tests/test_datalad_base.py +20 -27
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +110 -122
- junifer/datagrabber/tests/test_multiple.py +293 -31
- junifer/datagrabber/tests/test_pattern.py +577 -18
- junifer/datagrabber/tests/test_pattern_datalad.py +177 -107
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +9 -51
- junifer/datareader/default.py +8 -5
- junifer/datareader/tests/test_default_reader.py +1 -1
- junifer/external/BrainPrint/.codecov.yml +2 -0
- junifer/external/BrainPrint/.codespellignore +2 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/bug-report.md +45 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/documentation.md +12 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/feature-request.md +24 -0
- junifer/external/BrainPrint/.github/ISSUE_TEMPLATE/questions-help-support.md +28 -0
- junifer/external/BrainPrint/.github/dependabot.yml +6 -0
- junifer/external/BrainPrint/.github/workflows/build.yml +53 -0
- junifer/external/BrainPrint/.github/workflows/code-style.yml +44 -0
- junifer/external/BrainPrint/.github/workflows/doc.yml +65 -0
- junifer/external/BrainPrint/.github/workflows/publish.yml +31 -0
- junifer/external/BrainPrint/.github/workflows/pytest.yml +54 -0
- junifer/external/BrainPrint/.gitignore +123 -0
- junifer/external/BrainPrint/LICENSE +21 -0
- junifer/external/BrainPrint/README.md +108 -0
- junifer/external/BrainPrint/brainprint/cli/__init__.py +11 -0
- junifer/external/BrainPrint/brainprint/cli/help_text.py +169 -0
- junifer/external/BrainPrint/brainprint/cli/parser.py +150 -0
- junifer/external/BrainPrint/brainprint/cli/utils.py +14 -0
- junifer/external/BrainPrint/brainprint/commands/__init__.py +0 -0
- junifer/external/BrainPrint/brainprint/commands/sys_info.py +18 -0
- junifer/external/BrainPrint/brainprint/utils/tests/__init__.py +0 -0
- junifer/external/BrainPrint/brainprint/utils/tests/test_config.py +33 -0
- junifer/external/BrainPrint/doc/Makefile +20 -0
- junifer/external/BrainPrint/doc/_static/.gitkeep +0 -0
- junifer/external/BrainPrint/doc/_templates/autosummary/class.rst +10 -0
- junifer/external/BrainPrint/doc/_templates/autosummary/function.rst +8 -0
- junifer/external/BrainPrint/doc/_templates/autosummary/module.rst +6 -0
- junifer/external/BrainPrint/doc/api/index.rst +11 -0
- junifer/external/BrainPrint/doc/conf.py +272 -0
- junifer/external/BrainPrint/doc/index.rst +140 -0
- junifer/external/BrainPrint/doc/links.inc +36 -0
- junifer/external/BrainPrint/doc/make.bat +35 -0
- junifer/external/BrainPrint/doc/references.bib +23 -0
- junifer/external/BrainPrint/pyproject.toml +170 -0
- junifer/external/BrainPrint/setup.py +3 -0
- junifer/external/__init__.py +5 -0
- junifer/external/__init__.pyi +0 -0
- junifer/external/nilearn/junifer_connectivity_measure.py +95 -39
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +226 -67
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +39 -27
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +231 -21
- junifer/external/py.typed +0 -0
- junifer/markers/__init__.pyi +6 -3
- junifer/markers/base.py +126 -76
- junifer/markers/brainprint.py +30 -39
- junifer/markers/complexity/__init__.py +20 -3
- junifer/markers/complexity/complexity_base.py +40 -41
- junifer/markers/complexity/hurst_exponent.py +22 -35
- junifer/markers/complexity/multiscale_entropy_auc.py +53 -41
- junifer/markers/complexity/perm_entropy.py +22 -36
- junifer/markers/complexity/range_entropy.py +22 -35
- junifer/markers/complexity/range_entropy_auc.py +27 -36
- junifer/markers/complexity/sample_entropy.py +22 -36
- junifer/markers/complexity/tests/test_complexity_registry.py +48 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +47 -36
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +50 -36
- junifer/markers/complexity/tests/test_perm_entropy.py +48 -36
- junifer/markers/complexity/tests/test_range_entropy.py +47 -36
- junifer/markers/complexity/tests/test_range_entropy_auc.py +47 -36
- junifer/markers/complexity/tests/test_sample_entropy.py +48 -36
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +50 -36
- junifer/markers/complexity/weighted_perm_entropy.py +22 -36
- junifer/markers/ets_rss.py +35 -31
- junifer/markers/falff/__init__.pyi +7 -1
- junifer/markers/falff/_afni_falff.py +22 -9
- junifer/markers/falff/_junifer_falff.py +47 -31
- junifer/markers/falff/falff_base.py +80 -60
- junifer/markers/falff/falff_maps.py +17 -41
- junifer/markers/falff/falff_parcels.py +26 -51
- junifer/markers/falff/falff_spheres.py +34 -61
- junifer/markers/falff/tests/test_falff_maps.py +20 -23
- junifer/markers/falff/tests/test_falff_mask.py +99 -0
- junifer/markers/falff/tests/test_falff_parcels.py +14 -10
- junifer/markers/falff/tests/test_falff_spheres.py +14 -10
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +32 -34
- junifer/markers/functional_connectivity/edge_functional_connectivity_maps.py +12 -25
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +19 -34
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +24 -44
- junifer/markers/functional_connectivity/functional_connectivity_base.py +50 -40
- junifer/markers/functional_connectivity/functional_connectivity_maps.py +12 -25
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +19 -34
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +24 -45
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +21 -22
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_maps.py +8 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +8 -4
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +8 -4
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_maps.py +8 -7
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +9 -5
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +8 -12
- junifer/markers/maps_aggregation.py +77 -55
- junifer/markers/parcel_aggregation.py +91 -70
- junifer/markers/reho/__init__.pyi +7 -1
- junifer/markers/reho/_afni_reho.py +23 -14
- junifer/markers/reho/_junifer_reho.py +112 -123
- junifer/markers/reho/reho_base.py +115 -37
- junifer/markers/reho/reho_maps.py +18 -32
- junifer/markers/reho/reho_parcels.py +26 -41
- junifer/markers/reho/reho_spheres.py +33 -54
- junifer/markers/reho/tests/test_junifer_reho.py +178 -0
- junifer/markers/reho/tests/test_reho_maps.py +27 -25
- junifer/markers/reho/tests/test_reho_parcels.py +28 -22
- junifer/markers/reho/tests/test_reho_spheres.py +16 -12
- junifer/markers/sphere_aggregation.py +88 -72
- junifer/markers/temporal_snr/temporal_snr_base.py +32 -30
- junifer/markers/temporal_snr/temporal_snr_maps.py +9 -18
- junifer/markers/temporal_snr/temporal_snr_parcels.py +19 -27
- junifer/markers/temporal_snr/temporal_snr_spheres.py +27 -41
- junifer/markers/temporal_snr/tests/test_temporal_snr_base.py +2 -2
- junifer/markers/temporal_snr/tests/test_temporal_snr_maps.py +17 -22
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +11 -3
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +9 -9
- junifer/markers/tests/test_brainprint.py +10 -9
- junifer/markers/tests/test_ets_rss.py +7 -3
- junifer/markers/tests/test_lost_regions.py +279 -0
- junifer/markers/tests/test_maps_aggregation.py +67 -114
- junifer/markers/tests/test_marker_utils.py +40 -1
- junifer/markers/tests/test_markers_base.py +275 -6
- junifer/markers/tests/test_parcel_aggregation.py +91 -53
- junifer/markers/tests/test_sphere_aggregation.py +53 -47
- junifer/markers/utils.py +267 -5
- junifer/onthefly/__init__.py +2 -3
- junifer/onthefly/__init__.pyi +4 -0
- junifer/onthefly/_brainprint.py +3 -5
- junifer/onthefly/py.typed +0 -0
- junifer/onthefly/read_transform.py +11 -9
- junifer/onthefly/tests/test_read_transform.py +7 -7
- junifer/pipeline/__init__.pyi +4 -2
- junifer/pipeline/marker_collection.py +14 -10
- junifer/pipeline/pipeline_component_registry.py +25 -6
- junifer/pipeline/pipeline_step_mixin.py +5 -12
- junifer/pipeline/tests/test_data_object_dumper.py +10 -10
- junifer/pipeline/tests/test_marker_collection.py +145 -168
- junifer/pipeline/tests/test_pipeline_component_registry.py +52 -8
- junifer/pipeline/tests/test_pipeline_step_mixin.py +12 -26
- junifer/pipeline/tests/test_update_meta_mixin.py +4 -5
- junifer/pipeline/tests/test_workdir_manager.py +39 -0
- junifer/pipeline/update_meta_mixin.py +13 -10
- junifer/pipeline/utils.py +22 -17
- junifer/pipeline/workdir_manager.py +41 -32
- junifer/preprocess/__init__.pyi +9 -4
- junifer/preprocess/_temporal_filter.py +34 -33
- junifer/preprocess/_temporal_slicer.py +17 -30
- junifer/preprocess/base.py +99 -55
- junifer/preprocess/confounds/__init__.pyi +10 -2
- junifer/preprocess/confounds/fmriprep_confound_remover.py +87 -109
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +64 -45
- junifer/preprocess/smoothing/__init__.pyi +2 -2
- junifer/preprocess/smoothing/_afni_smoothing.py +8 -7
- junifer/preprocess/smoothing/_fsl_smoothing.py +6 -4
- junifer/preprocess/smoothing/_nilearn_smoothing.py +7 -5
- junifer/preprocess/smoothing/smoothing.py +59 -40
- junifer/preprocess/smoothing/tests/test_smoothing.py +28 -8
- junifer/preprocess/tests/test_preprocess_base.py +17 -9
- junifer/preprocess/tests/test_temporal_filter.py +37 -16
- junifer/preprocess/tests/test_temporal_slicer.py +12 -12
- junifer/preprocess/warping/__init__.pyi +2 -2
- junifer/preprocess/warping/_ants_warper.py +56 -40
- junifer/preprocess/warping/_fsl_warper.py +49 -36
- junifer/preprocess/warping/_utils.py +106 -0
- junifer/preprocess/warping/space_warper.py +99 -98
- junifer/preprocess/warping/tests/test_space_warper.py +46 -41
- junifer/preprocess/warping/tests/test_warping_utils.py +163 -0
- junifer/stats.py +46 -34
- junifer/storage/__init__.pyi +8 -4
- junifer/storage/base.py +129 -64
- junifer/storage/hdf5.py +307 -199
- junifer/storage/pandas_base.py +46 -24
- junifer/storage/sqlite.py +99 -62
- junifer/storage/tests/data/collected_h5io_fork.hdf5 +0 -0
- junifer/storage/tests/test_hdf5.py +147 -12
- junifer/storage/tests/test_repeated_storage.py +293 -0
- junifer/storage/tests/test_sqlite.py +9 -20
- junifer/storage/tests/test_storage_base.py +18 -21
- junifer/storage/tests/test_utils.py +83 -22
- junifer/storage/utils.py +58 -38
- junifer/testing/__init__.pyi +2 -2
- junifer/testing/datagrabbers.py +480 -44
- junifer/testing/registry.py +12 -0
- junifer/testing/tests/test_adhdtesting_datagrabber.py +24 -0
- junifer/testing/tests/test_oasisvmbtesting_datagrabber.py +5 -0
- junifer/testing/tests/test_partlycloudytesting_datagrabber.py +52 -0
- junifer/testing/tests/test_spmauditory_datagrabber.py +16 -0
- junifer/testing/tests/test_testing_registry.py +31 -0
- junifer/testing/tests/test_testing_utils.py +35 -0
- junifer/testing/tests/test_uclacnpvbmtesting_datagrabber.py +81 -0
- junifer/testing/utils.py +33 -1
- junifer/tests/test_stats.py +1 -5
- junifer/typing/__init__.pyi +2 -0
- junifer/typing/_typing.py +34 -19
- junifer/typing/py.typed +0 -0
- junifer/utils/__init__.pyi +10 -1
- junifer/utils/_config.py +1 -2
- junifer/utils/helpers.py +68 -2
- junifer/utils/logging.py +31 -7
- junifer/utils/tests/test_helpers.py +36 -1
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/METADATA +43 -35
- junifer-0.0.8.dist-info/RECORD +402 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/WHEEL +1 -1
- junifer/cli/tests/test_parser.py +0 -280
- junifer/external/h5io/h5io/__init__.py +0 -15
- junifer/external/h5io/h5io/_h5io.py +0 -767
- junifer/external/h5io/h5io/_version.py +0 -1
- junifer/external/h5io/h5io/chunked_array.py +0 -94
- junifer/external/h5io/h5io/chunked_list.py +0 -65
- junifer/markers/complexity/__init__.pyi +0 -17
- junifer-0.0.7.dev332.dist-info/RECORD +0 -353
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/AUTHORS.rst +0 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
- {junifer-0.0.7.dev332.dist-info → junifer-0.0.8.dist-info}/top_level.txt +0 -0
|
@@ -4,19 +4,49 @@
|
|
|
4
4
|
# License: AGPL
|
|
5
5
|
|
|
6
6
|
from collections.abc import Sequence
|
|
7
|
-
from
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import Annotated, Any, ClassVar, Literal
|
|
8
9
|
|
|
9
|
-
from
|
|
10
|
+
from pydantic import BeforeValidator
|
|
10
11
|
|
|
11
12
|
from ...api.decorators import register_preprocessor
|
|
13
|
+
from ...data.template_spaces import _get_templateflow_templates
|
|
14
|
+
from ...datagrabber import DataType
|
|
12
15
|
from ...typing import ConditionalDependencies
|
|
13
|
-
from ...utils import
|
|
14
|
-
from ..base import BasePreprocessor
|
|
16
|
+
from ...utils import ensure_list_or_none, raise_error
|
|
17
|
+
from ..base import BasePreprocessor, logger
|
|
15
18
|
from ._ants_warper import ANTsWarper
|
|
16
19
|
from ._fsl_warper import FSLWarper
|
|
17
20
|
|
|
18
21
|
|
|
19
|
-
__all__ = ["SpaceWarper"]
|
|
22
|
+
__all__ = ["SpaceWarper", "SpaceWarpingImpl"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SpaceWarpingImpl(StrEnum):
|
|
26
|
+
"""Accepted space warping implementations.
|
|
27
|
+
|
|
28
|
+
* ``fsl`` : FSL's ``applywarp``
|
|
29
|
+
* ``ants`` : ANTs' ``antsApplyTransforms``
|
|
30
|
+
* ``auto`` : Auto-select tool when ``reference="T1w"``
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
fsl = "fsl"
|
|
35
|
+
ants = "ants"
|
|
36
|
+
auto = "auto"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_on = Literal[
|
|
40
|
+
DataType.T1w,
|
|
41
|
+
DataType.T2w,
|
|
42
|
+
DataType.BOLD,
|
|
43
|
+
DataType.VBM_GM,
|
|
44
|
+
DataType.VBM_WM,
|
|
45
|
+
DataType.VBM_CSF,
|
|
46
|
+
DataType.FALFF,
|
|
47
|
+
DataType.GCOR,
|
|
48
|
+
DataType.LCOR,
|
|
49
|
+
]
|
|
20
50
|
|
|
21
51
|
|
|
22
52
|
@register_preprocessor
|
|
@@ -25,91 +55,67 @@ class SpaceWarper(BasePreprocessor):
|
|
|
25
55
|
|
|
26
56
|
Parameters
|
|
27
57
|
----------
|
|
28
|
-
using :
|
|
29
|
-
Implementation to use for warping:
|
|
30
|
-
|
|
31
|
-
* "fsl" : Use FSL's ``applywarp``
|
|
32
|
-
* "ants" : Use ANTs' ``antsApplyTransforms``
|
|
33
|
-
* "auto" : Auto-select tool when ``reference="T1w"``
|
|
34
|
-
|
|
58
|
+
using : :enum:`.SpaceWarpingImpl`
|
|
35
59
|
reference : str
|
|
36
60
|
The data type to use as reference for warping, can be either a data
|
|
37
61
|
type like ``"T1w"`` or a template space like ``"MNI152NLin2009cAsym"``.
|
|
38
62
|
Use ``"T1w"`` for native space warping and named templates for
|
|
39
63
|
template space warping.
|
|
40
|
-
on : {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
------
|
|
46
|
-
ValueError
|
|
47
|
-
If ``using`` is invalid or
|
|
48
|
-
if ``reference`` is invalid.
|
|
64
|
+
on : {``DataType.T1w``, ``DataType.T2w``, ``DataType.BOLD``, \
|
|
65
|
+
``DataType.VBM_GM``, ``DataType.VBM_WM``, ``DataType.VBM_CSF``, \
|
|
66
|
+
``DataType.FALFF``, ``DataType.GCOR``, ``DataType.LCOR``} or \
|
|
67
|
+
list of them
|
|
68
|
+
The data type(s) to warp.
|
|
49
69
|
|
|
50
70
|
"""
|
|
51
71
|
|
|
52
72
|
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
|
|
53
73
|
{
|
|
54
|
-
"using":
|
|
55
|
-
"depends_on": FSLWarper,
|
|
74
|
+
"using": SpaceWarpingImpl.fsl,
|
|
75
|
+
"depends_on": [FSLWarper],
|
|
56
76
|
},
|
|
57
77
|
{
|
|
58
|
-
"using":
|
|
59
|
-
"depends_on": ANTsWarper,
|
|
78
|
+
"using": SpaceWarpingImpl.ants,
|
|
79
|
+
"depends_on": [ANTsWarper],
|
|
60
80
|
},
|
|
61
81
|
{
|
|
62
|
-
"using":
|
|
82
|
+
"using": SpaceWarpingImpl.auto,
|
|
63
83
|
"depends_on": [FSLWarper, ANTsWarper],
|
|
64
84
|
},
|
|
65
85
|
]
|
|
66
|
-
_VALID_DATA_TYPES: ClassVar[Sequence[
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
86
|
+
_VALID_DATA_TYPES: ClassVar[Sequence[DataType]] = [
|
|
87
|
+
DataType.T1w,
|
|
88
|
+
DataType.T2w,
|
|
89
|
+
DataType.BOLD,
|
|
90
|
+
DataType.VBM_GM,
|
|
91
|
+
DataType.VBM_WM,
|
|
92
|
+
DataType.VBM_CSF,
|
|
93
|
+
DataType.FALFF,
|
|
94
|
+
DataType.GCOR,
|
|
95
|
+
DataType.LCOR,
|
|
76
96
|
]
|
|
77
97
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if using not in valid_using:
|
|
85
|
-
raise_error(
|
|
86
|
-
f"Invalid value for `using`, should be one of: {valid_using}"
|
|
87
|
-
)
|
|
88
|
-
self.using = using
|
|
89
|
-
self.reference = reference
|
|
90
|
-
# Set required data types based on reference and
|
|
91
|
-
# initialize superclass
|
|
92
|
-
if self.reference == "T1w": # pragma: no cover
|
|
93
|
-
required_data_types = [self.reference, "Warp"]
|
|
94
|
-
# Listify on
|
|
95
|
-
if not isinstance(on, list):
|
|
96
|
-
on = [on]
|
|
97
|
-
# Extend required data types
|
|
98
|
-
required_data_types.extend(on)
|
|
98
|
+
using: SpaceWarpingImpl
|
|
99
|
+
reference: str
|
|
100
|
+
on: Annotated[
|
|
101
|
+
_on | list[_on],
|
|
102
|
+
BeforeValidator(ensure_list_or_none),
|
|
103
|
+
]
|
|
99
104
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
def validate_preprocessor_params(self) -> None:
|
|
106
|
+
"""Run extra logical validation for preprocessor."""
|
|
107
|
+
# Set required data types based on reference
|
|
108
|
+
if self.reference == "T1w": # pragma: no cover
|
|
109
|
+
# Update required data types
|
|
110
|
+
self.required_data_types = [DataType.T1w, DataType.Warp]
|
|
111
|
+
self.required_data_types.extend(self.on)
|
|
112
|
+
elif self.reference not in _get_templateflow_templates():
|
|
107
113
|
raise_error(f"Unknown reference: {self.reference}")
|
|
108
114
|
|
|
109
115
|
def preprocess( # noqa: C901
|
|
110
116
|
self,
|
|
111
117
|
input: dict[str, Any],
|
|
112
|
-
extra_input:
|
|
118
|
+
extra_input: dict[str, Any] | None = None,
|
|
113
119
|
) -> dict[str, Any]:
|
|
114
120
|
"""Preprocess.
|
|
115
121
|
|
|
@@ -129,20 +135,21 @@ class SpaceWarper(BasePreprocessor):
|
|
|
129
135
|
------
|
|
130
136
|
ValueError
|
|
131
137
|
If ``extra_input`` is None when transforming to native space
|
|
132
|
-
i.e., using ``"T1w"`` as reference
|
|
138
|
+
i.e., using ``"T1w"`` as reference or converting from native to
|
|
139
|
+
template space or
|
|
140
|
+
if the ``reference`` key is missing from ``input`` when converting
|
|
141
|
+
from native to template space.
|
|
133
142
|
RuntimeError
|
|
134
143
|
If warper could not be found in ``extra_input`` when
|
|
135
144
|
``using="auto"`` or converting from native space or
|
|
136
145
|
if the data is in the correct space and does not require
|
|
137
146
|
warping or
|
|
138
|
-
if FSL is used when ``reference
|
|
147
|
+
if FSL or "auto" is used when ``reference!="T1w"``.
|
|
139
148
|
|
|
140
149
|
"""
|
|
141
150
|
logger.info(f"Warping to {self.reference} space using SpaceWarper")
|
|
142
151
|
# Transform to native space
|
|
143
|
-
if
|
|
144
|
-
self.using in ["fsl", "ants", "auto"] and self.reference == "T1w"
|
|
145
|
-
): # pragma: no cover
|
|
152
|
+
if self.reference == "T1w": # pragma: no cover
|
|
146
153
|
# Check for extra inputs
|
|
147
154
|
if extra_input is None:
|
|
148
155
|
raise_error(
|
|
@@ -150,41 +157,30 @@ class SpaceWarper(BasePreprocessor):
|
|
|
150
157
|
f"`{self.reference}` data types in particular."
|
|
151
158
|
)
|
|
152
159
|
# Conditional preprocessor
|
|
153
|
-
|
|
160
|
+
warper = None
|
|
161
|
+
if self.using == "auto":
|
|
162
|
+
for entry in extra_input["Warp"]:
|
|
163
|
+
if entry["dst"] == "native":
|
|
164
|
+
warper = entry["warper"]
|
|
165
|
+
if warper is None:
|
|
166
|
+
raise_error(
|
|
167
|
+
klass=RuntimeError, msg="Could not find correct warper"
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
warper = self.using
|
|
171
|
+
if warper == "fsl":
|
|
154
172
|
input = FSLWarper().preprocess(
|
|
155
173
|
input=input,
|
|
156
174
|
extra_input=extra_input,
|
|
157
175
|
reference=self.reference,
|
|
158
176
|
)
|
|
159
|
-
elif
|
|
177
|
+
elif warper == "ants":
|
|
160
178
|
input = ANTsWarper().preprocess(
|
|
161
179
|
input=input,
|
|
162
180
|
extra_input=extra_input,
|
|
163
181
|
reference=self.reference,
|
|
164
182
|
)
|
|
165
|
-
|
|
166
|
-
warper = None
|
|
167
|
-
for entry in extra_input["Warp"]:
|
|
168
|
-
if entry["dst"] == "native":
|
|
169
|
-
warper = entry["warper"]
|
|
170
|
-
if warper is None:
|
|
171
|
-
raise_error(
|
|
172
|
-
klass=RuntimeError, msg="Could not find correct warper"
|
|
173
|
-
)
|
|
174
|
-
if warper == "fsl":
|
|
175
|
-
input = FSLWarper().preprocess(
|
|
176
|
-
input=input,
|
|
177
|
-
extra_input=extra_input,
|
|
178
|
-
reference=self.reference,
|
|
179
|
-
)
|
|
180
|
-
elif warper == "ants":
|
|
181
|
-
input = ANTsWarper().preprocess(
|
|
182
|
-
input=input,
|
|
183
|
-
extra_input=extra_input,
|
|
184
|
-
reference=self.reference,
|
|
185
|
-
)
|
|
186
|
-
# Transform to template space
|
|
187
|
-
if self.using in ["fsl", "ants"] and self.reference != "T1w":
|
|
183
|
+
else:
|
|
188
184
|
input_space = input["space"]
|
|
189
185
|
# Check pre-requirements for space manipulation
|
|
190
186
|
if self.using == "ants" and self.reference == input_space:
|
|
@@ -241,12 +237,12 @@ class SpaceWarper(BasePreprocessor):
|
|
|
241
237
|
)
|
|
242
238
|
else:
|
|
243
239
|
# Transform from MNI to MNI template space not possible
|
|
244
|
-
if self.using
|
|
240
|
+
if self.using in ["fsl", "auto"]:
|
|
245
241
|
raise_error(
|
|
246
242
|
(
|
|
247
243
|
f"Warping from {input_space} space to "
|
|
248
244
|
f"{self.reference} space not possible with "
|
|
249
|
-
"
|
|
245
|
+
f"{self.using}, use ANTs instead."
|
|
250
246
|
),
|
|
251
247
|
klass=RuntimeError,
|
|
252
248
|
)
|
|
@@ -257,5 +253,10 @@ class SpaceWarper(BasePreprocessor):
|
|
|
257
253
|
extra_input={},
|
|
258
254
|
reference=self.reference,
|
|
259
255
|
)
|
|
260
|
-
|
|
256
|
+
logger.debug("Completed warping step")
|
|
257
|
+
logger.debug("Warped data types: ")
|
|
258
|
+
for k, v in input.items():
|
|
259
|
+
if k in ["data", "meta"]:
|
|
260
|
+
continue
|
|
261
|
+
logger.debug(f"\t{k}: {v}")
|
|
261
262
|
return input
|
|
@@ -8,25 +8,36 @@ import socket
|
|
|
8
8
|
import pytest
|
|
9
9
|
from numpy.testing import assert_array_equal, assert_raises
|
|
10
10
|
|
|
11
|
-
from junifer.datagrabber import
|
|
11
|
+
from junifer.datagrabber import (
|
|
12
|
+
DataladHCP1200,
|
|
13
|
+
DataType,
|
|
14
|
+
DMCC13Benchmark,
|
|
15
|
+
DMCCPhaseEncoding,
|
|
16
|
+
DMCCRun,
|
|
17
|
+
DMCCSession,
|
|
18
|
+
DMCCTask,
|
|
19
|
+
HCP1200PhaseEncoding,
|
|
20
|
+
HCP1200Task,
|
|
21
|
+
)
|
|
12
22
|
from junifer.datareader import DefaultDataReader
|
|
13
23
|
from junifer.pipeline.utils import _check_ants, _check_fsl
|
|
14
|
-
from junifer.preprocess import SpaceWarper
|
|
24
|
+
from junifer.preprocess import SpaceWarper, SpaceWarpingImpl
|
|
15
25
|
from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
|
|
16
26
|
from junifer.typing import DataGrabberLike
|
|
17
27
|
|
|
18
28
|
|
|
29
|
+
pytestmark = pytest.mark.external
|
|
30
|
+
|
|
31
|
+
|
|
19
32
|
@pytest.mark.parametrize(
|
|
20
33
|
"using, reference, error_type, error_msg",
|
|
21
34
|
[
|
|
22
|
-
(
|
|
23
|
-
(
|
|
24
|
-
("ants", "MNI152NLin2009cAsym", RuntimeError, "remove"),
|
|
25
|
-
("fsl", "MNI152NLin2009cAsym", RuntimeError, "ANTs"),
|
|
35
|
+
(SpaceWarpingImpl.ants, "MNI152NLin2009cAsym", RuntimeError, "remove"),
|
|
36
|
+
(SpaceWarpingImpl.fsl, "MNI152NLin2009cAsym", RuntimeError, "ANTs"),
|
|
26
37
|
],
|
|
27
38
|
)
|
|
28
39
|
def test_SpaceWarper_errors(
|
|
29
|
-
using:
|
|
40
|
+
using: SpaceWarpingImpl,
|
|
30
41
|
reference: str,
|
|
31
42
|
error_type: type[Exception],
|
|
32
43
|
error_msg: str,
|
|
@@ -35,7 +46,7 @@ def test_SpaceWarper_errors(
|
|
|
35
46
|
|
|
36
47
|
Parameters
|
|
37
48
|
----------
|
|
38
|
-
using :
|
|
49
|
+
using : SpaceWarpingImpl
|
|
39
50
|
The parametrized implementation method.
|
|
40
51
|
reference : str
|
|
41
52
|
The parametrized reference to use.
|
|
@@ -46,14 +57,12 @@ def test_SpaceWarper_errors(
|
|
|
46
57
|
|
|
47
58
|
"""
|
|
48
59
|
with PartlyCloudyTestingDataGrabber() as dg:
|
|
49
|
-
# Read data
|
|
50
60
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
|
51
|
-
# Preprocess data
|
|
52
61
|
with pytest.raises(error_type, match=error_msg):
|
|
53
62
|
SpaceWarper(
|
|
54
63
|
using=using,
|
|
55
64
|
reference=reference,
|
|
56
|
-
on=
|
|
65
|
+
on=DataType.BOLD,
|
|
57
66
|
).preprocess(
|
|
58
67
|
input=element_data["BOLD"],
|
|
59
68
|
extra_input=element_data,
|
|
@@ -65,24 +74,24 @@ def test_SpaceWarper_errors(
|
|
|
65
74
|
[
|
|
66
75
|
[
|
|
67
76
|
DMCC13Benchmark(
|
|
68
|
-
types=[
|
|
69
|
-
sessions=
|
|
70
|
-
tasks=
|
|
71
|
-
phase_encodings=
|
|
72
|
-
runs=
|
|
77
|
+
types=[DataType.BOLD, DataType.T1w, DataType.Warp],
|
|
78
|
+
sessions=DMCCSession.Wave1Bas,
|
|
79
|
+
tasks=DMCCTask.Rest,
|
|
80
|
+
phase_encodings=DMCCPhaseEncoding.AP,
|
|
81
|
+
runs=DMCCRun.One,
|
|
73
82
|
native_t1w=True,
|
|
74
83
|
),
|
|
75
84
|
("sub-f9057kp", "ses-wave1bas", "Rest", "AP", "1"),
|
|
76
|
-
|
|
85
|
+
SpaceWarpingImpl.ants,
|
|
77
86
|
],
|
|
78
87
|
[
|
|
79
88
|
DataladHCP1200(
|
|
80
|
-
tasks=
|
|
81
|
-
phase_encodings=
|
|
89
|
+
tasks=HCP1200Task.REST1,
|
|
90
|
+
phase_encodings=HCP1200PhaseEncoding.LR,
|
|
82
91
|
ica_fix=True,
|
|
83
92
|
),
|
|
84
93
|
("100206", "REST1", "LR"),
|
|
85
|
-
|
|
94
|
+
SpaceWarpingImpl.fsl,
|
|
86
95
|
],
|
|
87
96
|
],
|
|
88
97
|
)
|
|
@@ -95,7 +104,9 @@ def test_SpaceWarper_errors(
|
|
|
95
104
|
reason="only for juseless",
|
|
96
105
|
)
|
|
97
106
|
def test_SpaceWarper_native(
|
|
98
|
-
datagrabber: DataGrabberLike,
|
|
107
|
+
datagrabber: DataGrabberLike,
|
|
108
|
+
element: tuple[str, ...],
|
|
109
|
+
using: SpaceWarpingImpl,
|
|
99
110
|
) -> None:
|
|
100
111
|
"""Test SpaceWarper for native space warping.
|
|
101
112
|
|
|
@@ -105,23 +116,20 @@ def test_SpaceWarper_native(
|
|
|
105
116
|
The parametrized DataGrabber objects.
|
|
106
117
|
element : tuple of str
|
|
107
118
|
The parametrized elements.
|
|
108
|
-
using :
|
|
119
|
+
using : SpaceWarpingImpl
|
|
109
120
|
The parametrized implementation method.
|
|
110
121
|
|
|
111
122
|
"""
|
|
112
123
|
with datagrabber as dg:
|
|
113
|
-
# Read data
|
|
114
124
|
element_data = DefaultDataReader().fit_transform(dg[element])
|
|
115
|
-
# Preprocess data
|
|
116
125
|
output = SpaceWarper(
|
|
117
126
|
using=using,
|
|
118
127
|
reference="T1w",
|
|
119
|
-
on=
|
|
128
|
+
on=DataType.BOLD,
|
|
120
129
|
).preprocess(
|
|
121
130
|
input=element_data["BOLD"],
|
|
122
131
|
extra_input=element_data,
|
|
123
132
|
)
|
|
124
|
-
# Check
|
|
125
133
|
assert isinstance(output, dict)
|
|
126
134
|
|
|
127
135
|
|
|
@@ -130,11 +138,11 @@ def test_SpaceWarper_native(
|
|
|
130
138
|
[
|
|
131
139
|
[
|
|
132
140
|
DMCC13Benchmark(
|
|
133
|
-
types=
|
|
134
|
-
sessions=
|
|
135
|
-
tasks=
|
|
136
|
-
phase_encodings=
|
|
137
|
-
runs=
|
|
141
|
+
types=DataType.T1w,
|
|
142
|
+
sessions=DMCCSession.Wave1Bas,
|
|
143
|
+
tasks=DMCCTask.Rest,
|
|
144
|
+
phase_encodings=DMCCPhaseEncoding.AP,
|
|
145
|
+
runs=DMCCRun.One,
|
|
138
146
|
native_t1w=False,
|
|
139
147
|
),
|
|
140
148
|
("sub-f9057kp", "ses-wave1bas", "Rest", "AP", "1"),
|
|
@@ -142,11 +150,11 @@ def test_SpaceWarper_native(
|
|
|
142
150
|
],
|
|
143
151
|
[
|
|
144
152
|
DMCC13Benchmark(
|
|
145
|
-
types=
|
|
146
|
-
sessions=
|
|
147
|
-
tasks=
|
|
148
|
-
phase_encodings=
|
|
149
|
-
runs=
|
|
153
|
+
types=DataType.T1w,
|
|
154
|
+
sessions=DMCCSession.Wave1Bas,
|
|
155
|
+
tasks=DMCCTask.Rest,
|
|
156
|
+
phase_encodings=DMCCPhaseEncoding.AP,
|
|
157
|
+
runs=DMCCRun.One,
|
|
150
158
|
native_t1w=False,
|
|
151
159
|
),
|
|
152
160
|
("sub-f9057kp", "ses-wave1bas", "Rest", "AP", "1"),
|
|
@@ -175,19 +183,16 @@ def test_SpaceWarper_multi_mni(
|
|
|
175
183
|
|
|
176
184
|
"""
|
|
177
185
|
with datagrabber as dg:
|
|
178
|
-
# Read data
|
|
179
186
|
element_data = DefaultDataReader().fit_transform(dg[element])
|
|
180
187
|
pre_xfm_data = element_data["T1w"]["data"].get_fdata().copy()
|
|
181
|
-
# Preprocess data
|
|
182
188
|
output = SpaceWarper(
|
|
183
|
-
using=
|
|
189
|
+
using=SpaceWarpingImpl.ants,
|
|
184
190
|
reference=space,
|
|
185
|
-
on=
|
|
191
|
+
on=DataType.T1w,
|
|
186
192
|
).preprocess(
|
|
187
193
|
input=element_data["T1w"],
|
|
188
194
|
extra_input=element_data,
|
|
189
195
|
)
|
|
190
|
-
# Checks
|
|
191
196
|
assert isinstance(output, dict)
|
|
192
197
|
assert output["space"] == space
|
|
193
198
|
with assert_raises(AssertionError):
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Provide tests for the warping utilities."""
|
|
2
|
+
|
|
3
|
+
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
|
4
|
+
# License: AGPL
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import nibabel as nib
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pytest
|
|
11
|
+
from numpy.testing import assert_array_equal
|
|
12
|
+
|
|
13
|
+
from junifer.datagrabber import DataType
|
|
14
|
+
from junifer.datareader import DefaultDataReader
|
|
15
|
+
from junifer.pipeline.utils import _check_ants
|
|
16
|
+
from junifer.preprocess import SpaceWarper, SpaceWarpingImpl
|
|
17
|
+
from junifer.preprocess.warping._utils import warp_in_chunks
|
|
18
|
+
from junifer.testing import config_override
|
|
19
|
+
from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _copy(in_path: Path, out_path: Path) -> list[str]:
|
|
23
|
+
"""Return a command copying the image, to test the chunks."""
|
|
24
|
+
return ["cp", str(in_path), str(out_path)]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.mark.parametrize("suffix", [".nii", ".nii.gz"])
|
|
28
|
+
@pytest.mark.parametrize(
|
|
29
|
+
"n_volumes, chunk_size", [(10, 3), (10, 5), (10, 10), (10, 100), (1, 1)]
|
|
30
|
+
)
|
|
31
|
+
@pytest.mark.parametrize("dtype", [np.float32, np.float64, np.int16])
|
|
32
|
+
def test_warp_in_chunks(
|
|
33
|
+
tmp_path: Path,
|
|
34
|
+
suffix: str,
|
|
35
|
+
n_volumes: int,
|
|
36
|
+
chunk_size: int,
|
|
37
|
+
dtype: type,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Test warping in chunks gives the same image.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
tmp_path : pathlib.Path
|
|
44
|
+
The path to the test directory.
|
|
45
|
+
suffix : str
|
|
46
|
+
The parametrized file extension.
|
|
47
|
+
n_volumes : int
|
|
48
|
+
The parametrized number of volumes.
|
|
49
|
+
chunk_size : int
|
|
50
|
+
The parametrized number of volumes of the chunks.
|
|
51
|
+
dtype : type
|
|
52
|
+
The parametrized data type of the image.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
rng = np.random.default_rng(0)
|
|
56
|
+
data = rng.normal(size=(4, 5, 6, n_volumes)) * 100
|
|
57
|
+
img = nib.Nifti1Image(data.astype(dtype), np.diag([2, 2, 2, 1]))
|
|
58
|
+
if dtype is np.int16:
|
|
59
|
+
# Scaled data
|
|
60
|
+
img.header.set_slope_inter(0.5, 10)
|
|
61
|
+
in_path = tmp_path / f"input{suffix}"
|
|
62
|
+
out_path = tmp_path / f"output{suffix}"
|
|
63
|
+
nib.save(img, in_path)
|
|
64
|
+
with config_override("preprocessing.warping.chunksize", chunk_size):
|
|
65
|
+
warp_in_chunks(
|
|
66
|
+
name="cp",
|
|
67
|
+
input_path=in_path,
|
|
68
|
+
output_path=out_path,
|
|
69
|
+
cmd=_copy,
|
|
70
|
+
tempdir=tmp_path,
|
|
71
|
+
)
|
|
72
|
+
out = nib.load(out_path)
|
|
73
|
+
expected = nib.load(in_path)
|
|
74
|
+
assert out.shape == expected.shape
|
|
75
|
+
if dtype is not np.int16:
|
|
76
|
+
assert out.get_data_dtype() == expected.get_data_dtype()
|
|
77
|
+
assert_array_equal(out.affine, expected.affine)
|
|
78
|
+
assert_array_equal(out.get_fdata(), expected.get_fdata())
|
|
79
|
+
# Only the input and the output are left
|
|
80
|
+
assert sorted(p.name for p in tmp_path.iterdir()) == sorted(
|
|
81
|
+
[in_path.name, out_path.name]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_warp_in_chunks_3d(tmp_path: Path) -> None:
|
|
86
|
+
"""Test warping a 3D image is not done in chunks.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
tmp_path : pathlib.Path
|
|
91
|
+
The path to the test directory.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
img = nib.Nifti1Image(np.ones((4, 5, 6), dtype=np.float32), np.eye(4))
|
|
95
|
+
in_path = tmp_path / "input.nii.gz"
|
|
96
|
+
out_path = tmp_path / "output.nii.gz"
|
|
97
|
+
nib.save(img, in_path)
|
|
98
|
+
calls = []
|
|
99
|
+
|
|
100
|
+
def cmd(in_path: Path, out_path: Path) -> list[str]:
|
|
101
|
+
calls.append(in_path)
|
|
102
|
+
return _copy(in_path, out_path)
|
|
103
|
+
|
|
104
|
+
with config_override("preprocessing.warping.chunksize", 1):
|
|
105
|
+
warp_in_chunks(
|
|
106
|
+
name="cp",
|
|
107
|
+
input_path=in_path,
|
|
108
|
+
output_path=out_path,
|
|
109
|
+
cmd=cmd,
|
|
110
|
+
tempdir=tmp_path,
|
|
111
|
+
)
|
|
112
|
+
assert calls == [in_path]
|
|
113
|
+
assert_array_equal(nib.load(out_path).get_fdata(), img.get_fdata())
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.mark.parametrize("chunk_size", [0, -1, 2.5, "10"])
|
|
117
|
+
def test_warp_in_chunks_error(tmp_path: Path, chunk_size: object) -> None:
|
|
118
|
+
"""Test an invalid chunk size.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
tmp_path : pathlib.Path
|
|
123
|
+
The path to the test directory.
|
|
124
|
+
chunk_size : object
|
|
125
|
+
The parametrized invalid chunk size.
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
with config_override("preprocessing.warping.chunksize", chunk_size):
|
|
129
|
+
with pytest.raises(ValueError, match="must be a positive integer"):
|
|
130
|
+
warp_in_chunks(
|
|
131
|
+
name="cp",
|
|
132
|
+
input_path=tmp_path / "input.nii.gz",
|
|
133
|
+
output_path=tmp_path / "output.nii.gz",
|
|
134
|
+
cmd=_copy,
|
|
135
|
+
tempdir=tmp_path,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.mark.external
|
|
140
|
+
@pytest.mark.skipif(
|
|
141
|
+
_check_ants() is False, reason="requires ANTs to be in PATH"
|
|
142
|
+
)
|
|
143
|
+
def test_SpaceWarper_ants_chunks() -> None:
|
|
144
|
+
"""Test warping with ANTs in chunks gives the same data."""
|
|
145
|
+
outputs = []
|
|
146
|
+
for chunk_size in (100, 3):
|
|
147
|
+
with PartlyCloudyTestingDataGrabber(n_timepoints=8) as dg:
|
|
148
|
+
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
|
149
|
+
with config_override(
|
|
150
|
+
"preprocessing.warping.chunksize", chunk_size
|
|
151
|
+
):
|
|
152
|
+
output = SpaceWarper(
|
|
153
|
+
using=SpaceWarpingImpl.ants,
|
|
154
|
+
reference="MNI152NLin6Asym",
|
|
155
|
+
on=DataType.BOLD,
|
|
156
|
+
).preprocess(
|
|
157
|
+
input=element_data["BOLD"],
|
|
158
|
+
extra_input=element_data,
|
|
159
|
+
)
|
|
160
|
+
assert output["data"].get_data_dtype() == np.float32
|
|
161
|
+
outputs.append(np.asarray(output["data"].dataobj))
|
|
162
|
+
assert outputs[0].shape[-1] == 8
|
|
163
|
+
assert_array_equal(outputs[0], outputs[1])
|