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
junifer/testing/datagrabbers.py
CHANGED
|
@@ -5,21 +5,48 @@
|
|
|
5
5
|
# License: AGPL
|
|
6
6
|
|
|
7
7
|
import tempfile
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from enum import Enum, StrEnum
|
|
8
10
|
from pathlib import Path
|
|
11
|
+
from typing import Any, ClassVar
|
|
12
|
+
from uuid import uuid4
|
|
9
13
|
|
|
10
14
|
import nibabel as nib
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pandas as pd
|
|
11
17
|
from nilearn import datasets, image
|
|
12
18
|
|
|
13
|
-
from ..datagrabber
|
|
19
|
+
from ..datagrabber import BaseDataGrabber, DataType
|
|
14
20
|
|
|
15
21
|
|
|
16
22
|
__all__ = [
|
|
23
|
+
"ADHDTestingDataGrabber",
|
|
17
24
|
"OasisVBMTestingDataGrabber",
|
|
25
|
+
"PartlyCloudyAgeGroup",
|
|
18
26
|
"PartlyCloudyTestingDataGrabber",
|
|
19
27
|
"SPMAuditoryTestingDataGrabber",
|
|
28
|
+
"UCLACNPVBMTestingDataGrabber",
|
|
20
29
|
]
|
|
21
30
|
|
|
22
31
|
|
|
32
|
+
def _save_atomic(out_path: Path, save: Callable[[Path], None]) -> None:
|
|
33
|
+
"""Save a file by writing to a temporary file and renaming it.
|
|
34
|
+
|
|
35
|
+
Concurrent test workers never read a partially written file.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
out_path : pathlib.Path
|
|
40
|
+
The path to save to.
|
|
41
|
+
save : callable
|
|
42
|
+
Function that writes the file to the path it is given.
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
tmp_path = out_path.with_name(f".{uuid4().hex}.{out_path.name}")
|
|
46
|
+
save(tmp_path)
|
|
47
|
+
tmp_path.replace(out_path)
|
|
48
|
+
|
|
49
|
+
|
|
23
50
|
class OasisVBMTestingDataGrabber(BaseDataGrabber):
|
|
24
51
|
"""DataGrabber for Oasis VBM testing data.
|
|
25
52
|
|
|
@@ -27,12 +54,9 @@ class OasisVBMTestingDataGrabber(BaseDataGrabber):
|
|
|
27
54
|
|
|
28
55
|
"""
|
|
29
56
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
# Define types
|
|
34
|
-
types = ["VBM_GM"]
|
|
35
|
-
super().__init__(types=types, datadir=datadir)
|
|
57
|
+
types: list[DataType] = [DataType.VBM_GM] # noqa: RUF012
|
|
58
|
+
datadir: Path = Path(tempfile.mkdtemp())
|
|
59
|
+
_dataset: Any = None
|
|
36
60
|
|
|
37
61
|
def get_element_keys(self) -> list[str]:
|
|
38
62
|
"""Get element keys.
|
|
@@ -91,19 +115,198 @@ class OasisVBMTestingDataGrabber(BaseDataGrabber):
|
|
|
91
115
|
return [f"sub-{x:02d}" for x in list(range(1, 11))]
|
|
92
116
|
|
|
93
117
|
|
|
118
|
+
class UCLACNPVBMTestingDataGrabber(BaseDataGrabber):
|
|
119
|
+
"""DataGrabber for UCLA CNP VBM testing data.
|
|
120
|
+
|
|
121
|
+
Grey matter probability maps (computed with fMRIPrep) of 10 control
|
|
122
|
+
subjects of the UCLA Consortium for Neuropsychiatric Phenomics LA5c
|
|
123
|
+
study (OpenNeuro ds000030, release R1.0.4), downloaded with
|
|
124
|
+
:func:`nilearn.datasets.fetch_openneuro_dataset`.
|
|
125
|
+
|
|
126
|
+
The maps are in MNI152NLin2009cAsym at 1mm. Other resolutions are
|
|
127
|
+
resampled (linear interpolation) to the corresponding templateflow grid,
|
|
128
|
+
so templates and masks at that resolution do not need resampling. They
|
|
129
|
+
are cached (uncompressed) next to the original files in the nilearn data
|
|
130
|
+
directory.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
resolution : float, optional
|
|
135
|
+
The resolution (in mm) of the maps (default 2.0).
|
|
136
|
+
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
types: list[DataType] = [DataType.VBM_GM] # noqa: RUF012
|
|
140
|
+
datadir: Path = Path(tempfile.mkdtemp())
|
|
141
|
+
resolution: float = 2.0
|
|
142
|
+
|
|
143
|
+
_BASE_URL: ClassVar[str] = (
|
|
144
|
+
"https://s3.amazonaws.com/openneuro/ds000030/ds000030_R1.0.4/"
|
|
145
|
+
"uncompressed"
|
|
146
|
+
)
|
|
147
|
+
# Controls spread across the age range (21 to 50 years)
|
|
148
|
+
_SUBJECTS: ClassVar[list[str]] = [
|
|
149
|
+
"sub-10206",
|
|
150
|
+
"sub-10339",
|
|
151
|
+
"sub-10388",
|
|
152
|
+
"sub-10429",
|
|
153
|
+
"sub-10460",
|
|
154
|
+
"sub-10471",
|
|
155
|
+
"sub-10527",
|
|
156
|
+
"sub-10696",
|
|
157
|
+
"sub-11059",
|
|
158
|
+
"sub-11105",
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
def __enter__(self) -> "UCLACNPVBMTestingDataGrabber":
|
|
162
|
+
"""Implement context entry.
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
UCLACNPVBMTestingDataGrabber
|
|
167
|
+
|
|
168
|
+
"""
|
|
169
|
+
urls = [f"{self._BASE_URL}/participants.tsv"] + [
|
|
170
|
+
f"{self._BASE_URL}/derivatives/fmriprep/{sub}/anat/"
|
|
171
|
+
f"{sub}_T1w_space-MNI152NLin2009cAsym_class-GM_probtissue.nii.gz"
|
|
172
|
+
for sub in self._SUBJECTS
|
|
173
|
+
]
|
|
174
|
+
_, files = datasets.fetch_openneuro_dataset(urls=urls, verbose=0)
|
|
175
|
+
files = {Path(f).name: Path(f) for f in files}
|
|
176
|
+
self._participants_path = files["participants.tsv"]
|
|
177
|
+
self._paths = {
|
|
178
|
+
sub: next(f for name, f in files.items() if name.startswith(sub))
|
|
179
|
+
for sub in self._SUBJECTS
|
|
180
|
+
}
|
|
181
|
+
return self
|
|
182
|
+
|
|
183
|
+
def get_element_keys(self) -> list[str]:
|
|
184
|
+
"""Get element keys.
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
list of str
|
|
189
|
+
The element keys.
|
|
190
|
+
|
|
191
|
+
"""
|
|
192
|
+
return ["subject"]
|
|
193
|
+
|
|
194
|
+
def get_elements(self) -> list[str]:
|
|
195
|
+
"""Get elements.
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
list of str
|
|
200
|
+
List of elements that can be grabbed.
|
|
201
|
+
|
|
202
|
+
"""
|
|
203
|
+
return list(self._SUBJECTS)
|
|
204
|
+
|
|
205
|
+
def get_participants(self) -> pd.DataFrame:
|
|
206
|
+
"""Get the participants information.
|
|
207
|
+
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
pandas.DataFrame
|
|
211
|
+
The participants information (e.g. age and gender), indexed by
|
|
212
|
+
subject.
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
participants = pd.read_csv(self._participants_path, sep="\t")
|
|
216
|
+
participants = participants.set_index("participant_id")
|
|
217
|
+
return participants.loc[self._SUBJECTS]
|
|
218
|
+
|
|
219
|
+
def get_item(self, subject: str) -> dict[str, dict]:
|
|
220
|
+
"""Implement indexing support.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
subject : str
|
|
225
|
+
The subject to retrieve.
|
|
226
|
+
|
|
227
|
+
Returns
|
|
228
|
+
-------
|
|
229
|
+
dict
|
|
230
|
+
The data along with the metadata.
|
|
231
|
+
|
|
232
|
+
"""
|
|
233
|
+
path = self._paths[subject]
|
|
234
|
+
return {
|
|
235
|
+
"VBM_GM": {
|
|
236
|
+
"path": self._get_resampled(path),
|
|
237
|
+
"space": "MNI152NLin2009cAsym",
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
def _get_resampled(self, path: Path) -> Path:
|
|
242
|
+
"""Get the map resampled to the requested resolution.
|
|
243
|
+
|
|
244
|
+
The resampled map is cached next to the original one, so it is
|
|
245
|
+
computed only once.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
path : pathlib.Path
|
|
250
|
+
The path to the original map.
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
pathlib.Path
|
|
255
|
+
The path to the resampled map.
|
|
256
|
+
|
|
257
|
+
"""
|
|
258
|
+
res = self.resolution
|
|
259
|
+
# The data is on the 1mm templateflow grid; the grids at other
|
|
260
|
+
# resolutions keep the same bounding box
|
|
261
|
+
target_affine = np.array(
|
|
262
|
+
[
|
|
263
|
+
[res, 0, 0, -96 - (res - 1) / 2],
|
|
264
|
+
[0, res, 0, -132 - (res - 1) / 2],
|
|
265
|
+
[0, 0, res, -78 - (res - 1) / 2],
|
|
266
|
+
[0, 0, 0, 1],
|
|
267
|
+
]
|
|
268
|
+
)
|
|
269
|
+
target_shape = tuple(int(np.ceil(n / res)) for n in (193, 229, 193))
|
|
270
|
+
img = nib.load(path)
|
|
271
|
+
# Original data is already in the requested grid
|
|
272
|
+
if img.shape[:3] == target_shape and np.allclose(
|
|
273
|
+
img.affine, target_affine
|
|
274
|
+
):
|
|
275
|
+
return path
|
|
276
|
+
|
|
277
|
+
# Store uncompressed so it can be memory-mapped
|
|
278
|
+
out_path = path.with_name(
|
|
279
|
+
path.name.replace(".nii.gz", f"_res-{res:g}mm.nii")
|
|
280
|
+
)
|
|
281
|
+
if not out_path.exists():
|
|
282
|
+
img = image.resample_img(
|
|
283
|
+
img,
|
|
284
|
+
target_affine=target_affine,
|
|
285
|
+
target_shape=target_shape,
|
|
286
|
+
interpolation="linear",
|
|
287
|
+
)
|
|
288
|
+
img.set_data_dtype(np.float32)
|
|
289
|
+
_save_atomic(out_path, lambda x: nib.save(img, x))
|
|
290
|
+
return out_path
|
|
291
|
+
|
|
292
|
+
|
|
94
293
|
class SPMAuditoryTestingDataGrabber(BaseDataGrabber):
|
|
95
294
|
"""DataGrabber for SPM Auditory dataset.
|
|
96
295
|
|
|
97
|
-
Wrapper for :func:`nilearn.datasets.fetch_spm_auditory`.
|
|
296
|
+
Wrapper for :func:`nilearn.datasets.fetch_spm_auditory`. The dataset has
|
|
297
|
+
a single subject, which is provided for each of the elements.
|
|
298
|
+
|
|
299
|
+
Parameters
|
|
300
|
+
----------
|
|
301
|
+
n_timepoints : int or None, optional
|
|
302
|
+
The number of BOLD timepoints to keep. If None, all the 84
|
|
303
|
+
timepoints are kept (default None).
|
|
98
304
|
|
|
99
305
|
"""
|
|
100
306
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# Define types
|
|
105
|
-
types = ["BOLD", "T1w"] # TODO: Check that they are T1w
|
|
106
|
-
super().__init__(types=types, datadir=datadir)
|
|
307
|
+
types: list[DataType] = [DataType.BOLD, DataType.T1w] # noqa: RUF012
|
|
308
|
+
datadir: Path = Path(tempfile.mkdtemp())
|
|
309
|
+
n_timepoints: int | None = None
|
|
107
310
|
|
|
108
311
|
def get_element_keys(self) -> list[str]:
|
|
109
312
|
"""Get element keys.
|
|
@@ -142,19 +345,42 @@ class SPMAuditoryTestingDataGrabber(BaseDataGrabber):
|
|
|
142
345
|
|
|
143
346
|
"""
|
|
144
347
|
out = {}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
fmri_fname = self.datadir / f"{subject}_bold.nii.gz"
|
|
348
|
+
tp_suffix = (
|
|
349
|
+
f"_tp-{self.n_timepoints}" if self.n_timepoints is not None else ""
|
|
350
|
+
)
|
|
351
|
+
fmri_fname = self.datadir / f"{subject}{tp_suffix}_bold.nii.gz"
|
|
150
352
|
anat_fname = self.datadir / f"{subject}_T1w.nii.gz"
|
|
151
|
-
|
|
152
|
-
|
|
353
|
+
# Files only depend on the parameters, so reuse them if already there
|
|
354
|
+
if not fmri_fname.exists() or not anat_fname.exists():
|
|
355
|
+
nilearn_data = datasets.fetch_spm_auditory(verbose=0)
|
|
356
|
+
if not fmri_fname.exists():
|
|
357
|
+
# Keep only the timepoints needed
|
|
358
|
+
fmri_img = image.index_img(
|
|
359
|
+
nilearn_data.func[0], slice(None, self.n_timepoints)
|
|
360
|
+
)
|
|
361
|
+
_save_atomic(fmri_fname, lambda x: nib.save(fmri_img, x))
|
|
362
|
+
if not anat_fname.exists():
|
|
363
|
+
anat_img = nib.load(nilearn_data.anat)
|
|
364
|
+
_save_atomic(anat_fname, lambda x: nib.save(anat_img, x))
|
|
153
365
|
out["BOLD"] = {"path": fmri_fname, "space": "MNI152Lin"}
|
|
154
366
|
out["T1w"] = {"path": anat_fname, "space": "native"}
|
|
155
367
|
return out
|
|
156
368
|
|
|
157
369
|
|
|
370
|
+
class PartlyCloudyAgeGroup(StrEnum):
|
|
371
|
+
"""Age group to fetch.
|
|
372
|
+
|
|
373
|
+
* ``Adult`` : fetch adults only (n=33, ages 18-39)
|
|
374
|
+
* ``Child`` : fetch children only (n=122, ages 3-12)
|
|
375
|
+
* ``Both`` : fetch full sample (n=155)
|
|
376
|
+
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
Adult = "adult"
|
|
380
|
+
Child = "child"
|
|
381
|
+
Both = "both"
|
|
382
|
+
|
|
383
|
+
|
|
158
384
|
class PartlyCloudyTestingDataGrabber(BaseDataGrabber):
|
|
159
385
|
"""DataGrabber for Partly Cloudy dataset.
|
|
160
386
|
|
|
@@ -169,28 +395,23 @@ class PartlyCloudyTestingDataGrabber(BaseDataGrabber):
|
|
|
169
395
|
purpose of having realistic examples. Depending on your research
|
|
170
396
|
question, other confounds might be more appropriate.
|
|
171
397
|
If False, returns all :term:`fMRIPrep` confounds (default True).
|
|
172
|
-
|
|
173
398
|
age_group : {"adult", "child", "both"}, optional
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
(default
|
|
399
|
+
Age group to fetch (default ``PartlyCloudyAgeGroup.Both``).
|
|
400
|
+
n_subjects : int, optional
|
|
401
|
+
The number of subjects to use (default 10).
|
|
402
|
+
n_timepoints : int or None, optional
|
|
403
|
+
The number of timepoints to keep. The truncated BOLD images and
|
|
404
|
+
confounds are cached next to the original files in the nilearn data
|
|
405
|
+
directory. If None, all the 168 timepoints are kept (default None).
|
|
181
406
|
|
|
182
407
|
"""
|
|
183
408
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
types = ["BOLD"]
|
|
191
|
-
self.reduce_confounds = reduce_confounds
|
|
192
|
-
self.age_group = age_group
|
|
193
|
-
super().__init__(types=types, datadir=datadir)
|
|
409
|
+
types: list[DataType] = [DataType.BOLD] # noqa: RUF012
|
|
410
|
+
datadir: Path = Path(tempfile.mkdtemp())
|
|
411
|
+
reduce_confounds: bool = True
|
|
412
|
+
age_group: PartlyCloudyAgeGroup = PartlyCloudyAgeGroup.Both
|
|
413
|
+
n_subjects: int = 10
|
|
414
|
+
n_timepoints: int | None = None
|
|
194
415
|
|
|
195
416
|
def __enter__(self) -> "PartlyCloudyTestingDataGrabber":
|
|
196
417
|
"""Implement context entry.
|
|
@@ -201,9 +422,11 @@ class PartlyCloudyTestingDataGrabber(BaseDataGrabber):
|
|
|
201
422
|
|
|
202
423
|
"""
|
|
203
424
|
self._dataset = datasets.fetch_development_fmri(
|
|
204
|
-
n_subjects=
|
|
425
|
+
n_subjects=self.n_subjects,
|
|
205
426
|
reduce_confounds=self.reduce_confounds,
|
|
206
|
-
age_group=self.age_group
|
|
427
|
+
age_group=self.age_group.value
|
|
428
|
+
if isinstance(self.age_group, Enum)
|
|
429
|
+
else self.age_group,
|
|
207
430
|
)
|
|
208
431
|
return self
|
|
209
432
|
|
|
@@ -227,7 +450,7 @@ class PartlyCloudyTestingDataGrabber(BaseDataGrabber):
|
|
|
227
450
|
List of elements that can be grabbed.
|
|
228
451
|
|
|
229
452
|
"""
|
|
230
|
-
return [f"sub-{x:02d}" for x in
|
|
453
|
+
return [f"sub-{x:02d}" for x in range(1, self.n_subjects + 1)]
|
|
231
454
|
|
|
232
455
|
def get_item(self, subject: str) -> dict[str, dict]:
|
|
233
456
|
"""Implement indexing support.
|
|
@@ -245,13 +468,226 @@ class PartlyCloudyTestingDataGrabber(BaseDataGrabber):
|
|
|
245
468
|
"""
|
|
246
469
|
out = {}
|
|
247
470
|
i_sub = int(subject.split("-")[1]) - 1
|
|
471
|
+
bold_path = Path(self._dataset["func"][i_sub])
|
|
472
|
+
confounds_path = Path(self._dataset["confounds"][i_sub])
|
|
473
|
+
if self.n_timepoints is not None:
|
|
474
|
+
bold_path, confounds_path = self._truncate(
|
|
475
|
+
bold_path, confounds_path
|
|
476
|
+
)
|
|
248
477
|
out["BOLD"] = {
|
|
249
|
-
"path":
|
|
478
|
+
"path": bold_path,
|
|
250
479
|
"space": "MNI152NLin2009cAsym",
|
|
251
480
|
"confounds": {
|
|
252
|
-
"path":
|
|
481
|
+
"path": confounds_path,
|
|
253
482
|
"format": "fmriprep",
|
|
254
483
|
},
|
|
255
484
|
}
|
|
256
485
|
|
|
257
486
|
return out
|
|
487
|
+
|
|
488
|
+
def _truncate(
|
|
489
|
+
self, bold_path: Path, confounds_path: Path
|
|
490
|
+
) -> tuple[Path, Path]:
|
|
491
|
+
"""Truncate the BOLD image and confounds to ``n_timepoints``.
|
|
492
|
+
|
|
493
|
+
The truncated files are cached next to the original ones, so they
|
|
494
|
+
are computed only once.
|
|
495
|
+
|
|
496
|
+
Parameters
|
|
497
|
+
----------
|
|
498
|
+
bold_path : pathlib.Path
|
|
499
|
+
The path to the original BOLD image.
|
|
500
|
+
confounds_path : pathlib.Path
|
|
501
|
+
The path to the original confounds file.
|
|
502
|
+
|
|
503
|
+
Returns
|
|
504
|
+
-------
|
|
505
|
+
pathlib.Path
|
|
506
|
+
The path to the truncated BOLD image.
|
|
507
|
+
pathlib.Path
|
|
508
|
+
The path to the truncated confounds file.
|
|
509
|
+
|
|
510
|
+
"""
|
|
511
|
+
n_tp = self.n_timepoints
|
|
512
|
+
img = nib.load(bold_path)
|
|
513
|
+
if n_tp >= img.shape[3]:
|
|
514
|
+
return bold_path, confounds_path
|
|
515
|
+
|
|
516
|
+
# Store uncompressed so it can be memory-mapped
|
|
517
|
+
out_bold = bold_path.with_name(
|
|
518
|
+
bold_path.name.replace("_bold.nii.gz", f"_tp-{n_tp}_bold.nii")
|
|
519
|
+
)
|
|
520
|
+
if not out_bold.exists():
|
|
521
|
+
# Keep the raw values and the original scaling; letting nibabel
|
|
522
|
+
# rescale the subset would re-quantize the data
|
|
523
|
+
truncated = nib.Nifti1Image(
|
|
524
|
+
np.asanyarray(img.dataobj.get_unscaled())[..., :n_tp],
|
|
525
|
+
img.affine,
|
|
526
|
+
img.header,
|
|
527
|
+
)
|
|
528
|
+
truncated.header.set_slope_inter(
|
|
529
|
+
img.dataobj.slope, img.dataobj.inter
|
|
530
|
+
)
|
|
531
|
+
_save_atomic(out_bold, lambda x: nib.save(truncated, x))
|
|
532
|
+
|
|
533
|
+
out_confounds = confounds_path.with_name(
|
|
534
|
+
confounds_path.name.replace(
|
|
535
|
+
"_regressors.tsv", f"_tp-{n_tp}_regressors.tsv"
|
|
536
|
+
)
|
|
537
|
+
)
|
|
538
|
+
if not out_confounds.exists():
|
|
539
|
+
# Copy the header and the first rows verbatim
|
|
540
|
+
lines = confounds_path.read_text().splitlines(keepends=True)
|
|
541
|
+
_save_atomic(
|
|
542
|
+
out_confounds,
|
|
543
|
+
lambda x: x.write_text("".join(lines[: n_tp + 1])),
|
|
544
|
+
)
|
|
545
|
+
return out_bold, out_confounds
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
class ADHDTestingDataGrabber(BaseDataGrabber):
|
|
549
|
+
"""DataGrabber for ADHD dataset.
|
|
550
|
+
|
|
551
|
+
Wrapper for :func:`nilearn.datasets.fetch_adhd`. The BOLD images are
|
|
552
|
+
truncated to the first ``n_timepoints``, resampled to the requested
|
|
553
|
+
resolution on the MNI152NLin6Asym (FSL) grid and cached (uncompressed)
|
|
554
|
+
next to the original files in the nilearn data directory.
|
|
555
|
+
|
|
556
|
+
Parameters
|
|
557
|
+
----------
|
|
558
|
+
resolution : float, optional
|
|
559
|
+
The resolution (in mm) of the BOLD images. The original data is in
|
|
560
|
+
3mm, so other resolutions are resampled (default 3.0).
|
|
561
|
+
n_timepoints : int or None, optional
|
|
562
|
+
The number of timepoints to keep. If None, all the 176 timepoints
|
|
563
|
+
are kept (default 50).
|
|
564
|
+
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
types: list[DataType] = [DataType.BOLD] # noqa: RUF012
|
|
568
|
+
datadir: Path = Path(tempfile.mkdtemp())
|
|
569
|
+
resolution: float = 3.0
|
|
570
|
+
n_timepoints: int | None = 50
|
|
571
|
+
|
|
572
|
+
def __enter__(self) -> "ADHDTestingDataGrabber":
|
|
573
|
+
"""Implement context entry.
|
|
574
|
+
|
|
575
|
+
Returns
|
|
576
|
+
-------
|
|
577
|
+
ADHDTestingDataGrabber
|
|
578
|
+
|
|
579
|
+
"""
|
|
580
|
+
self._dataset = datasets.fetch_adhd(n_subjects=10)
|
|
581
|
+
return self
|
|
582
|
+
|
|
583
|
+
def get_element_keys(self) -> list[str]:
|
|
584
|
+
"""Get element keys.
|
|
585
|
+
|
|
586
|
+
Returns
|
|
587
|
+
-------
|
|
588
|
+
list of str
|
|
589
|
+
The element keys.
|
|
590
|
+
|
|
591
|
+
"""
|
|
592
|
+
return ["subject"]
|
|
593
|
+
|
|
594
|
+
def get_elements(self) -> list[str]:
|
|
595
|
+
"""Get elements.
|
|
596
|
+
|
|
597
|
+
Returns
|
|
598
|
+
-------
|
|
599
|
+
list of str
|
|
600
|
+
List of elements that can be grabbed.
|
|
601
|
+
|
|
602
|
+
"""
|
|
603
|
+
return [f"sub-{x:02d}" for x in list(range(1, 11))]
|
|
604
|
+
|
|
605
|
+
def get_item(self, subject: str) -> dict[str, dict]:
|
|
606
|
+
"""Implement indexing support.
|
|
607
|
+
|
|
608
|
+
Parameters
|
|
609
|
+
----------
|
|
610
|
+
subject : str
|
|
611
|
+
The subject to retrieve.
|
|
612
|
+
|
|
613
|
+
Returns
|
|
614
|
+
-------
|
|
615
|
+
dict
|
|
616
|
+
The data along with the metadata.
|
|
617
|
+
|
|
618
|
+
"""
|
|
619
|
+
out = {}
|
|
620
|
+
i_sub = int(subject.split("-")[1]) - 1
|
|
621
|
+
out["BOLD"] = {
|
|
622
|
+
"path": self._get_bold(Path(self._dataset["func"][i_sub])),
|
|
623
|
+
"space": "MNI152NLin6Asym",
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return out
|
|
627
|
+
|
|
628
|
+
def _get_bold(self, path: Path) -> Path:
|
|
629
|
+
"""Get the BOLD image truncated and resampled as requested.
|
|
630
|
+
|
|
631
|
+
The resulting image is cached next to the original one, so it is
|
|
632
|
+
computed only once.
|
|
633
|
+
|
|
634
|
+
Parameters
|
|
635
|
+
----------
|
|
636
|
+
path : pathlib.Path
|
|
637
|
+
The path to the original BOLD image.
|
|
638
|
+
|
|
639
|
+
Returns
|
|
640
|
+
-------
|
|
641
|
+
pathlib.Path
|
|
642
|
+
The path to the BOLD image.
|
|
643
|
+
|
|
644
|
+
"""
|
|
645
|
+
res = self.resolution
|
|
646
|
+
n_tp = self.n_timepoints
|
|
647
|
+
# FSL MNI152 grid: x is flipped and the bounding box is
|
|
648
|
+
# 180 x 216 x 180 mm, with the origin at (90, -126, -72)
|
|
649
|
+
target_affine = np.array(
|
|
650
|
+
[
|
|
651
|
+
[-res, 0, 0, 90],
|
|
652
|
+
[0, res, 0, -126],
|
|
653
|
+
[0, 0, res, -72],
|
|
654
|
+
[0, 0, 0, 1],
|
|
655
|
+
]
|
|
656
|
+
)
|
|
657
|
+
target_shape = (
|
|
658
|
+
round(180 / res) + 1,
|
|
659
|
+
round(216 / res) + 1,
|
|
660
|
+
round(180 / res) + 1,
|
|
661
|
+
)
|
|
662
|
+
img = nib.load(path)
|
|
663
|
+
needs_resample = img.shape[:3] != target_shape or not np.allclose(
|
|
664
|
+
img.affine, target_affine
|
|
665
|
+
)
|
|
666
|
+
needs_truncate = n_tp is not None and n_tp < img.shape[3]
|
|
667
|
+
# Original data is already as requested
|
|
668
|
+
if not needs_resample and not needs_truncate:
|
|
669
|
+
return path
|
|
670
|
+
|
|
671
|
+
# Store uncompressed so it can be memory-mapped; otherwise every
|
|
672
|
+
# load decompresses the whole file again
|
|
673
|
+
tp_suffix = f"_tp-{n_tp}" if needs_truncate else ""
|
|
674
|
+
out_path = path.with_name(
|
|
675
|
+
path.name.replace(".nii.gz", f"_res-{res:g}mm{tp_suffix}.nii")
|
|
676
|
+
)
|
|
677
|
+
if not out_path.exists():
|
|
678
|
+
if needs_truncate:
|
|
679
|
+
img = img.slicer[..., :n_tp]
|
|
680
|
+
if needs_resample:
|
|
681
|
+
tr = img.header.get_zooms()[3]
|
|
682
|
+
img = image.resample_img(
|
|
683
|
+
img,
|
|
684
|
+
target_affine=target_affine,
|
|
685
|
+
target_shape=target_shape,
|
|
686
|
+
interpolation="continuous",
|
|
687
|
+
)
|
|
688
|
+
# Resampling creates a new header, so restore the TR
|
|
689
|
+
img.header.set_zooms((res, res, res, tr))
|
|
690
|
+
img.header.set_xyzt_units(xyz="mm", t="sec")
|
|
691
|
+
img.set_data_dtype(np.float32)
|
|
692
|
+
_save_atomic(out_path, lambda x: nib.save(img, x))
|
|
693
|
+
return out_path
|
junifer/testing/registry.py
CHANGED
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
from ..pipeline import PipelineComponentRegistry
|
|
8
8
|
from .datagrabbers import (
|
|
9
|
+
ADHDTestingDataGrabber,
|
|
9
10
|
OasisVBMTestingDataGrabber,
|
|
10
11
|
PartlyCloudyTestingDataGrabber,
|
|
11
12
|
SPMAuditoryTestingDataGrabber,
|
|
13
|
+
UCLACNPVBMTestingDataGrabber,
|
|
12
14
|
)
|
|
13
15
|
|
|
14
16
|
|
|
@@ -27,3 +29,13 @@ PipelineComponentRegistry().register(
|
|
|
27
29
|
step="datagrabber",
|
|
28
30
|
klass=PartlyCloudyTestingDataGrabber,
|
|
29
31
|
)
|
|
32
|
+
|
|
33
|
+
PipelineComponentRegistry().register(
|
|
34
|
+
step="datagrabber",
|
|
35
|
+
klass=ADHDTestingDataGrabber,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
PipelineComponentRegistry().register(
|
|
39
|
+
step="datagrabber",
|
|
40
|
+
klass=UCLACNPVBMTestingDataGrabber,
|
|
41
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Provide tests for ADHDTestingDataGrabber."""
|
|
2
|
+
|
|
3
|
+
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
|
4
|
+
# License: AGPL
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from junifer.testing.datagrabbers import ADHDTestingDataGrabber
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
pytestmark = pytest.mark.external
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_ADHDTestingDataGrabber() -> None:
|
|
15
|
+
"""Test ADHDTestingDataGrabber."""
|
|
16
|
+
expected_elements = [f"sub-{x:02d}" for x in range(1, 11)]
|
|
17
|
+
with ADHDTestingDataGrabber() as dg:
|
|
18
|
+
all_elements = dg.get_elements()
|
|
19
|
+
assert set(all_elements) == set(expected_elements)
|
|
20
|
+
out = dg["sub-01"]
|
|
21
|
+
assert "BOLD" in out
|
|
22
|
+
assert out["BOLD"]["path"].exists()
|
|
23
|
+
assert out["BOLD"]["path"].is_file()
|
|
24
|
+
assert out["BOLD"]["space"] == "MNI152NLin6Asym"
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
|
4
4
|
# License: AGPL
|
|
5
5
|
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
6
8
|
from junifer.testing.datagrabbers import OasisVBMTestingDataGrabber
|
|
7
9
|
|
|
8
10
|
|
|
11
|
+
pytestmark = pytest.mark.external
|
|
12
|
+
|
|
13
|
+
|
|
9
14
|
def test_OasisVBMTestingDataGrabber() -> None:
|
|
10
15
|
"""Test OasisVBMTestingDataGrabber."""
|
|
11
16
|
expected_elements = [
|