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,12 +4,13 @@
|
|
|
4
4
|
# License: AGPL
|
|
5
5
|
|
|
6
6
|
import uuid
|
|
7
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
9
|
import nibabel as nib
|
|
10
10
|
|
|
11
11
|
from ...pipeline import WorkDirManager
|
|
12
|
-
from ...
|
|
12
|
+
from ...typing import SpaceLike
|
|
13
|
+
from ...utils import raise_error, run_ext_cmd
|
|
13
14
|
from ..template_spaces import get_template, get_xfm
|
|
14
15
|
|
|
15
16
|
|
|
@@ -31,10 +32,10 @@ class ANTsParcellationWarper:
|
|
|
31
32
|
self,
|
|
32
33
|
parcellation_name: str,
|
|
33
34
|
parcellation_img: "Nifti1Image",
|
|
34
|
-
src:
|
|
35
|
-
dst:
|
|
35
|
+
src: SpaceLike,
|
|
36
|
+
dst: SpaceLike,
|
|
36
37
|
target_data: dict[str, Any],
|
|
37
|
-
warp_data:
|
|
38
|
+
warp_data: dict[str, Any] | None,
|
|
38
39
|
) -> "Nifti1Image":
|
|
39
40
|
"""Warp ``parcellation_img`` to correct space.
|
|
40
41
|
|
|
@@ -44,10 +45,10 @@ class ANTsParcellationWarper:
|
|
|
44
45
|
The name of the parcellation.
|
|
45
46
|
parcellation_img : nibabel.nifti1.Nifti1Image
|
|
46
47
|
The parcellation image to transform.
|
|
47
|
-
src : str
|
|
48
|
+
src : str or Enum
|
|
48
49
|
The data type or template space to warp from.
|
|
49
50
|
It should be empty string if ``dst="T1w"``.
|
|
50
|
-
dst : str
|
|
51
|
+
dst : str or Enum
|
|
51
52
|
The data type or template space to warp to.
|
|
52
53
|
`"T1w"` is the only allowed data type and it uses the resampled T1w
|
|
53
54
|
found in ``target_data.reference``. The ``"reference"``
|
|
@@ -71,6 +72,9 @@ class ANTsParcellationWarper:
|
|
|
71
72
|
If ``warp_data`` is None when ``dst="T1w"``.
|
|
72
73
|
|
|
73
74
|
"""
|
|
75
|
+
# Imported here to avoid circular import
|
|
76
|
+
from ._parcellations import logger
|
|
77
|
+
|
|
74
78
|
# Create element-scoped tempdir so that warped parcellation is
|
|
75
79
|
# available later as nibabel stores file path reference for
|
|
76
80
|
# loading on computation
|
|
@@ -97,19 +101,20 @@ class ANTsParcellationWarper:
|
|
|
97
101
|
|
|
98
102
|
# Save existing parcellation image to a tempfile
|
|
99
103
|
prewarp_parcellation_path = (
|
|
100
|
-
element_tempdir / "prewarp_parcellation.nii
|
|
104
|
+
element_tempdir / "prewarp_parcellation.nii"
|
|
101
105
|
)
|
|
102
106
|
nib.save(parcellation_img, prewarp_parcellation_path)
|
|
103
107
|
|
|
104
108
|
# Create a tempfile for warped output
|
|
105
109
|
warped_parcellation_path = (
|
|
106
|
-
element_tempdir / "parcellation_warped.nii
|
|
110
|
+
element_tempdir / "parcellation_warped.nii"
|
|
107
111
|
)
|
|
108
112
|
# Set antsApplyTransforms command
|
|
109
113
|
apply_transforms_cmd = [
|
|
110
114
|
"antsApplyTransforms",
|
|
111
115
|
"-d 3",
|
|
112
116
|
"-e 3",
|
|
117
|
+
"--float 1",
|
|
113
118
|
"-n 'GenericLabel[NearestNeighbor]'",
|
|
114
119
|
f"-i {prewarp_parcellation_path.resolve()}",
|
|
115
120
|
# use resampled reference
|
|
@@ -135,24 +140,25 @@ class ANTsParcellationWarper:
|
|
|
135
140
|
extra_input=None,
|
|
136
141
|
)
|
|
137
142
|
# Save template to a tempfile
|
|
138
|
-
template_space_img_path = element_tempdir / f"{dst}_T1w.nii
|
|
143
|
+
template_space_img_path = element_tempdir / f"{dst}_T1w.nii"
|
|
139
144
|
nib.save(template_space_img, template_space_img_path)
|
|
140
145
|
|
|
141
146
|
# Save existing parcellation image to a tempfile
|
|
142
147
|
prewarp_parcellation_path = (
|
|
143
|
-
element_tempdir / "prewarp_parcellation.nii
|
|
148
|
+
element_tempdir / "prewarp_parcellation.nii"
|
|
144
149
|
)
|
|
145
150
|
nib.save(parcellation_img, prewarp_parcellation_path)
|
|
146
151
|
|
|
147
152
|
# Create a tempfile for warped output
|
|
148
153
|
warped_parcellation_path = (
|
|
149
|
-
element_tempdir / "parcellation_warped.nii
|
|
154
|
+
element_tempdir / "parcellation_warped.nii"
|
|
150
155
|
)
|
|
151
156
|
# Set antsApplyTransforms command
|
|
152
157
|
apply_transforms_cmd = [
|
|
153
158
|
"antsApplyTransforms",
|
|
154
159
|
"-d 3",
|
|
155
160
|
"-e 3",
|
|
161
|
+
"--float 1",
|
|
156
162
|
"-n 'GenericLabel[NearestNeighbor]'",
|
|
157
163
|
f"-i {prewarp_parcellation_path.resolve()}",
|
|
158
164
|
f"-r {template_space_img_path.resolve()}",
|
|
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
9
9
|
import nibabel as nib
|
|
10
10
|
|
|
11
11
|
from ...pipeline import WorkDirManager
|
|
12
|
-
from ...utils import
|
|
12
|
+
from ...utils import run_ext_cmd
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
@@ -53,6 +53,9 @@ class FSLParcellationWarper:
|
|
|
53
53
|
The transformed parcellation image.
|
|
54
54
|
|
|
55
55
|
"""
|
|
56
|
+
# Imported here to avoid circular import
|
|
57
|
+
from ._parcellations import logger
|
|
58
|
+
|
|
56
59
|
logger.debug("Using FSL for parcellation transformation")
|
|
57
60
|
|
|
58
61
|
# Create element-scoped tempdir so that warped parcellation is
|
|
@@ -64,16 +67,15 @@ class FSLParcellationWarper:
|
|
|
64
67
|
|
|
65
68
|
# Save existing parcellation image to a tempfile
|
|
66
69
|
prewarp_parcellation_path = (
|
|
67
|
-
element_tempdir / "prewarp_parcellation.nii
|
|
70
|
+
element_tempdir / "prewarp_parcellation.nii"
|
|
68
71
|
)
|
|
69
72
|
nib.save(parcellation_img, prewarp_parcellation_path)
|
|
70
73
|
|
|
71
74
|
# Create a tempfile for warped output
|
|
72
|
-
warped_parcellation_path =
|
|
73
|
-
element_tempdir / "parcellation_warped.nii.gz"
|
|
74
|
-
)
|
|
75
|
+
warped_parcellation_path = element_tempdir / "parcellation_warped.nii"
|
|
75
76
|
# Set applywarp command
|
|
76
77
|
applywarp_cmd = [
|
|
78
|
+
"FSLOUTPUTTYPE=NIFTI",
|
|
77
79
|
"applywarp",
|
|
78
80
|
"--interp=nn",
|
|
79
81
|
f"-i {prewarp_parcellation_path.resolve()}",
|
|
@@ -5,17 +5,19 @@
|
|
|
5
5
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
|
6
6
|
# License: AGPL
|
|
7
7
|
|
|
8
|
+
from collections import Counter
|
|
8
9
|
from itertools import product
|
|
9
10
|
from pathlib import Path
|
|
10
|
-
from typing import TYPE_CHECKING, Any, Optional
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
11
12
|
|
|
12
13
|
import nibabel as nib
|
|
13
14
|
import nilearn.image as nimg
|
|
14
15
|
import numpy as np
|
|
15
16
|
import pandas as pd
|
|
17
|
+
import structlog
|
|
16
18
|
from junifer_data import get
|
|
17
19
|
|
|
18
|
-
from ...utils import
|
|
20
|
+
from ...utils import raise_error, warn_with_log
|
|
19
21
|
from ..pipeline_data_registry_base import BasePipelineDataRegistry
|
|
20
22
|
from ..utils import (
|
|
21
23
|
JUNIFER_DATA_PARAMS,
|
|
@@ -36,6 +38,9 @@ __all__ = [
|
|
|
36
38
|
"merge_parcellations",
|
|
37
39
|
]
|
|
38
40
|
|
|
41
|
+
_log = structlog.get_logger("junifer")
|
|
42
|
+
logger = _log.bind(pkg="data")
|
|
43
|
+
|
|
39
44
|
|
|
40
45
|
class ParcellationRegistry(BasePipelineDataRegistry):
|
|
41
46
|
"""Class for parcellation data registry.
|
|
@@ -58,8 +63,12 @@ class ParcellationRegistry(BasePipelineDataRegistry):
|
|
|
58
63
|
# The built-in parcellations are files that are shipped with the
|
|
59
64
|
# junifer-data dataset.
|
|
60
65
|
# Make built-in and external dictionaries for validation later
|
|
61
|
-
self._builtin
|
|
62
|
-
|
|
66
|
+
self._builtin: dict[
|
|
67
|
+
str, dict[str, str | int | Path | list[str] | list[int]]
|
|
68
|
+
] = {}
|
|
69
|
+
self._external: dict[
|
|
70
|
+
str, dict[str, str | int | Path | list[str] | list[int]]
|
|
71
|
+
] = {}
|
|
63
72
|
|
|
64
73
|
# Add SUIT
|
|
65
74
|
self._builtin.update(
|
|
@@ -226,7 +235,7 @@ class ParcellationRegistry(BasePipelineDataRegistry):
|
|
|
226
235
|
def register(
|
|
227
236
|
self,
|
|
228
237
|
name: str,
|
|
229
|
-
parcellation_path:
|
|
238
|
+
parcellation_path: str | Path,
|
|
230
239
|
parcels_labels: list[str],
|
|
231
240
|
space: str,
|
|
232
241
|
overwrite: bool = False,
|
|
@@ -309,7 +318,7 @@ class ParcellationRegistry(BasePipelineDataRegistry):
|
|
|
309
318
|
self,
|
|
310
319
|
name: str,
|
|
311
320
|
target_space: str,
|
|
312
|
-
resolution:
|
|
321
|
+
resolution: float | None = None,
|
|
313
322
|
path_only: bool = False,
|
|
314
323
|
) -> tuple[Optional["Nifti1Image"], list[str], Path, str]:
|
|
315
324
|
"""Load parcellation and labels.
|
|
@@ -463,9 +472,9 @@ class ParcellationRegistry(BasePipelineDataRegistry):
|
|
|
463
472
|
|
|
464
473
|
def get(
|
|
465
474
|
self,
|
|
466
|
-
parcellations:
|
|
475
|
+
parcellations: str | list[str],
|
|
467
476
|
target_data: dict[str, Any],
|
|
468
|
-
extra_input:
|
|
477
|
+
extra_input: dict[str, Any] | None = None,
|
|
469
478
|
) -> tuple["Nifti1Image", dict[int, str]]:
|
|
470
479
|
"""Get parcellation, tailored for the target image.
|
|
471
480
|
|
|
@@ -539,6 +548,17 @@ class ParcellationRegistry(BasePipelineDataRegistry):
|
|
|
539
548
|
resolution=resolution,
|
|
540
549
|
target_space=target_space,
|
|
541
550
|
)
|
|
551
|
+
# Map the labels to the values before resampling or warping,
|
|
552
|
+
# as small regions can be lost in the process
|
|
553
|
+
value_labels = dict(
|
|
554
|
+
zip(
|
|
555
|
+
np.trim_zeros(
|
|
556
|
+
np.unique(np.asarray(img.dataobj).astype(int))
|
|
557
|
+
).tolist(),
|
|
558
|
+
labels,
|
|
559
|
+
strict=True,
|
|
560
|
+
)
|
|
561
|
+
)
|
|
542
562
|
|
|
543
563
|
# Convert parcellation spaces if required;
|
|
544
564
|
# cannot be "native" due to earlier check
|
|
@@ -594,22 +614,27 @@ class ParcellationRegistry(BasePipelineDataRegistry):
|
|
|
594
614
|
warp_data=warper_spec,
|
|
595
615
|
)
|
|
596
616
|
|
|
617
|
+
# Keep the lost regions, so all elements have the same regions
|
|
618
|
+
present = set(
|
|
619
|
+
np.unique(np.asarray(img.dataobj).astype(int)).tolist()
|
|
620
|
+
)
|
|
621
|
+
lost = [
|
|
622
|
+
label
|
|
623
|
+
for value, label in value_labels.items()
|
|
624
|
+
if value not in present
|
|
625
|
+
]
|
|
626
|
+
if lost:
|
|
627
|
+
warn_with_log(
|
|
628
|
+
f"{len(lost)} region(s) of {name} have no voxels in the "
|
|
629
|
+
f"target image, so they will have no data: {lost}"
|
|
630
|
+
)
|
|
597
631
|
all_parcellations.append(img)
|
|
598
|
-
all_labels.append(
|
|
632
|
+
all_labels.append(value_labels)
|
|
599
633
|
|
|
600
634
|
# Avoid merging if there is only one parcellation
|
|
601
635
|
if len(all_parcellations) == 1:
|
|
602
636
|
resampled_parcellation_img = all_parcellations[0]
|
|
603
|
-
labels =
|
|
604
|
-
zip(
|
|
605
|
-
np.trim_zeros(
|
|
606
|
-
np.unique(
|
|
607
|
-
resampled_parcellation_img.get_fdata().astype(int)
|
|
608
|
-
)
|
|
609
|
-
),
|
|
610
|
-
all_labels[0],
|
|
611
|
-
)
|
|
612
|
-
)
|
|
637
|
+
labels = all_labels[0]
|
|
613
638
|
# Parcellations are already transformed to target standard space
|
|
614
639
|
else:
|
|
615
640
|
logger.debug("Merging parcellations.")
|
|
@@ -623,8 +648,8 @@ class ParcellationRegistry(BasePipelineDataRegistry):
|
|
|
623
648
|
|
|
624
649
|
|
|
625
650
|
def _retrieve_schaefer(
|
|
626
|
-
resolution:
|
|
627
|
-
n_rois:
|
|
651
|
+
resolution: float | None = None,
|
|
652
|
+
n_rois: int | None = None,
|
|
628
653
|
yeo_networks: int = 7,
|
|
629
654
|
) -> tuple[Path, list[str]]:
|
|
630
655
|
"""Retrieve Schaefer parcellation.
|
|
@@ -707,8 +732,8 @@ def _retrieve_schaefer(
|
|
|
707
732
|
|
|
708
733
|
|
|
709
734
|
def _retrieve_tian(
|
|
710
|
-
resolution:
|
|
711
|
-
scale:
|
|
735
|
+
resolution: float | None = None,
|
|
736
|
+
scale: int | None = None,
|
|
712
737
|
space: str = "MNI152NLin6Asym",
|
|
713
738
|
magneticfield: str = "3T",
|
|
714
739
|
) -> tuple[Path, list[str]]:
|
|
@@ -847,7 +872,7 @@ def _retrieve_tian(
|
|
|
847
872
|
|
|
848
873
|
|
|
849
874
|
def _retrieve_suit(
|
|
850
|
-
resolution:
|
|
875
|
+
resolution: float | None,
|
|
851
876
|
space: str = "MNI152NLin6Asym",
|
|
852
877
|
) -> tuple[Path, list[str]]:
|
|
853
878
|
"""Retrieve SUIT parcellation.
|
|
@@ -919,7 +944,7 @@ def _retrieve_suit(
|
|
|
919
944
|
|
|
920
945
|
|
|
921
946
|
def _retrieve_aicha(
|
|
922
|
-
resolution:
|
|
947
|
+
resolution: float | None = None,
|
|
923
948
|
version: int = 2,
|
|
924
949
|
) -> tuple[Path, list[str]]:
|
|
925
950
|
"""Retrieve AICHA parcellation.
|
|
@@ -1013,7 +1038,7 @@ def _retrieve_aicha(
|
|
|
1013
1038
|
|
|
1014
1039
|
|
|
1015
1040
|
def _retrieve_shen(
|
|
1016
|
-
resolution:
|
|
1041
|
+
resolution: float | None = None,
|
|
1017
1042
|
year: int = 2015,
|
|
1018
1043
|
n_rois: int = 268,
|
|
1019
1044
|
) -> tuple[Path, list[str]]:
|
|
@@ -1109,16 +1134,29 @@ def _retrieve_shen(
|
|
|
1109
1134
|
dataset_path=get_dataset_path(),
|
|
1110
1135
|
**JUNIFER_DATA_PARAMS,
|
|
1111
1136
|
)
|
|
1112
|
-
|
|
1137
|
+
# The rows are sorted by name, so order them by the parcel value
|
|
1138
|
+
# (first column) to match the parcellation image
|
|
1139
|
+
names = (
|
|
1113
1140
|
pd.read_csv(
|
|
1114
1141
|
parcellation_label_path,
|
|
1115
1142
|
sep=",",
|
|
1116
1143
|
header=None,
|
|
1117
1144
|
skiprows=[0],
|
|
1118
|
-
)
|
|
1145
|
+
)
|
|
1146
|
+
.sort_values(0)[1]
|
|
1119
1147
|
.map(lambda x: x.strip()) # fix formatting
|
|
1120
1148
|
.to_list()
|
|
1121
1149
|
)
|
|
1150
|
+
# Regions outside Brodmann areas share a name ("No_BA_region"), so
|
|
1151
|
+
# number every occurrence of repeated names
|
|
1152
|
+
counts = Counter(names)
|
|
1153
|
+
seen: Counter = Counter()
|
|
1154
|
+
labels = []
|
|
1155
|
+
for name in names:
|
|
1156
|
+
if counts[name] > 1:
|
|
1157
|
+
seen[name] += 1
|
|
1158
|
+
name = f"{name}-{seen[name]}"
|
|
1159
|
+
labels.append(name)
|
|
1122
1160
|
elif year == 2015:
|
|
1123
1161
|
parcellation_img_path = get(
|
|
1124
1162
|
file_path=path_prefix
|
|
@@ -1139,10 +1177,10 @@ def _retrieve_shen(
|
|
|
1139
1177
|
|
|
1140
1178
|
|
|
1141
1179
|
def _retrieve_yan(
|
|
1142
|
-
resolution:
|
|
1143
|
-
n_rois:
|
|
1144
|
-
yeo_networks:
|
|
1145
|
-
kong_networks:
|
|
1180
|
+
resolution: float | None = None,
|
|
1181
|
+
n_rois: int | None = None,
|
|
1182
|
+
yeo_networks: int | None = None,
|
|
1183
|
+
kong_networks: int | None = None,
|
|
1146
1184
|
) -> tuple[Path, list[str]]:
|
|
1147
1185
|
"""Retrieve Yan parcellation.
|
|
1148
1186
|
|
|
@@ -1255,8 +1293,9 @@ def _retrieve_yan(
|
|
|
1255
1293
|
**JUNIFER_DATA_PARAMS,
|
|
1256
1294
|
)
|
|
1257
1295
|
|
|
1258
|
-
# Load label file
|
|
1259
|
-
|
|
1296
|
+
# Load label file; the index is right-aligned with spaces, so split on
|
|
1297
|
+
# any whitespace
|
|
1298
|
+
labels = pd.read_csv(parcellation_label_path, sep=r"\s+", header=None)[
|
|
1260
1299
|
1
|
|
1261
1300
|
].to_list()
|
|
1262
1301
|
|
|
@@ -1264,8 +1303,8 @@ def _retrieve_yan(
|
|
|
1264
1303
|
|
|
1265
1304
|
|
|
1266
1305
|
def _retrieve_brainnetome(
|
|
1267
|
-
resolution:
|
|
1268
|
-
threshold:
|
|
1306
|
+
resolution: float | None = None,
|
|
1307
|
+
threshold: int | None = None,
|
|
1269
1308
|
) -> tuple[Path, list[str]]:
|
|
1270
1309
|
"""Retrieve Brainnetome parcellation.
|
|
1271
1310
|
|
|
@@ -1322,40 +1361,23 @@ def _retrieve_brainnetome(
|
|
|
1322
1361
|
**JUNIFER_DATA_PARAMS,
|
|
1323
1362
|
)
|
|
1324
1363
|
|
|
1325
|
-
# Load labels
|
|
1364
|
+
# Load labels (one row per label, ordered by label value)
|
|
1365
|
+
parcellation_label_path = get(
|
|
1366
|
+
file_path=Path("parcellations/Brainnetome/labels.csv"),
|
|
1367
|
+
dataset_path=get_dataset_path(),
|
|
1368
|
+
**JUNIFER_DATA_PARAMS,
|
|
1369
|
+
)
|
|
1326
1370
|
labels = (
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
+ sorted([f"OrG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
|
1331
|
-
+ sorted([f"PrG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
|
1332
|
-
+ sorted([f"PCL_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
|
1333
|
-
+ sorted([f"STG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
|
1334
|
-
+ sorted([f"MTG_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
|
1335
|
-
+ sorted([f"ITG_L(R)_7_{i}" for i in range(1, 8)] * 2)
|
|
1336
|
-
+ sorted([f"FuG_L(R)_3_{i}" for i in range(1, 4)] * 2)
|
|
1337
|
-
+ sorted([f"PhG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
|
1338
|
-
+ sorted([f"pSTS_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
|
1339
|
-
+ sorted([f"SPL_L(R)_5_{i}" for i in range(1, 6)] * 2)
|
|
1340
|
-
+ sorted([f"IPL_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
|
1341
|
-
+ sorted([f"PCun_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
|
1342
|
-
+ sorted([f"PoG_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
|
1343
|
-
+ sorted([f"INS_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
|
1344
|
-
+ sorted([f"CG_L(R)_7_{i}" for i in range(1, 8)] * 2)
|
|
1345
|
-
+ sorted([f"MVOcC _L(R)_5_{i}" for i in range(1, 6)] * 2)
|
|
1346
|
-
+ sorted([f"LOcC_L(R)_4_{i}" for i in range(1, 5)] * 2)
|
|
1347
|
-
+ sorted([f"LOcC_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
|
1348
|
-
+ sorted([f"Amyg_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
|
1349
|
-
+ sorted([f"Hipp_L(R)_2_{i}" for i in range(1, 3)] * 2)
|
|
1350
|
-
+ sorted([f"BG_L(R)_6_{i}" for i in range(1, 7)] * 2)
|
|
1351
|
-
+ sorted([f"Tha_L(R)_8_{i}" for i in range(1, 9)] * 2)
|
|
1371
|
+
pd.read_csv(parcellation_label_path, sep=",")
|
|
1372
|
+
.sort_values("idx")["label"]
|
|
1373
|
+
.to_list()
|
|
1352
1374
|
)
|
|
1353
1375
|
|
|
1354
1376
|
return parcellation_img_path, labels
|
|
1355
1377
|
|
|
1356
1378
|
|
|
1357
1379
|
def _retrieve_aseg(
|
|
1358
|
-
resolution:
|
|
1380
|
+
resolution: float | None = None,
|
|
1359
1381
|
) -> tuple[Path, list[str]]:
|
|
1360
1382
|
"""Retrieve aseg generated from FreeSurfer 7.4.1 .
|
|
1361
1383
|
|
|
@@ -1455,7 +1477,7 @@ def _retrieve_aseg(
|
|
|
1455
1477
|
|
|
1456
1478
|
|
|
1457
1479
|
def _retrieve_glasser(
|
|
1458
|
-
resolution:
|
|
1480
|
+
resolution: float | None = None,
|
|
1459
1481
|
) -> tuple[Path, list[str]]:
|
|
1460
1482
|
"""Retrieve Glasser v1.0 .
|
|
1461
1483
|
|
|
@@ -1499,7 +1521,7 @@ def _retrieve_glasser(
|
|
|
1499
1521
|
|
|
1500
1522
|
|
|
1501
1523
|
def _retrieve_julich_brain(
|
|
1502
|
-
resolution:
|
|
1524
|
+
resolution: float | None = None,
|
|
1503
1525
|
version: str = "v3_1",
|
|
1504
1526
|
) -> tuple[Path, list[str]]:
|
|
1505
1527
|
"""Retrieve Julich-Brain labelled parcellations.
|
|
@@ -1554,7 +1576,12 @@ def _retrieve_julich_brain(
|
|
|
1554
1576
|
dataset_path=get_dataset_path(),
|
|
1555
1577
|
**JUNIFER_DATA_PARAMS,
|
|
1556
1578
|
)
|
|
1557
|
-
|
|
1579
|
+
# The "label" column is the value in the image and "region" the name
|
|
1580
|
+
labels = (
|
|
1581
|
+
pd.read_csv(parcellation_label_path, sep=",")
|
|
1582
|
+
.sort_values("label")["region"]
|
|
1583
|
+
.to_list()
|
|
1584
|
+
)
|
|
1558
1585
|
|
|
1559
1586
|
return parcellation_img_path, labels
|
|
1560
1587
|
|
|
@@ -1562,7 +1589,7 @@ def _retrieve_julich_brain(
|
|
|
1562
1589
|
def merge_parcellations(
|
|
1563
1590
|
parcellations_list: list["Nifti1Image"],
|
|
1564
1591
|
parcellations_names: list[str],
|
|
1565
|
-
labels_lists: list[list[str]],
|
|
1592
|
+
labels_lists: list[list[str] | dict[int, str]],
|
|
1566
1593
|
) -> tuple["Nifti1Image", dict[int, str]]:
|
|
1567
1594
|
"""Merge multiple parcellations.
|
|
1568
1595
|
|
|
@@ -1572,9 +1599,12 @@ def merge_parcellations(
|
|
|
1572
1599
|
List of parcellations to merge.
|
|
1573
1600
|
parcellations_names: list of str
|
|
1574
1601
|
List of names for parcellations at the corresponding indices.
|
|
1575
|
-
labels_lists : list of list of str
|
|
1576
|
-
|
|
1577
|
-
|
|
1602
|
+
labels_lists : list of list of str or dict
|
|
1603
|
+
The labels for the parcellation at the corresponding index, either
|
|
1604
|
+
as a mapping of value to label or as a list of labels for the
|
|
1605
|
+
(sorted) values in the parcellation. Use the mapping if regions
|
|
1606
|
+
might be missing from the parcellation (e.g., after resampling), so
|
|
1607
|
+
they keep their label.
|
|
1578
1608
|
|
|
1579
1609
|
Returns
|
|
1580
1610
|
-------
|
|
@@ -1585,39 +1615,42 @@ def merge_parcellations(
|
|
|
1585
1615
|
Merged parcellation value to label mappings.
|
|
1586
1616
|
|
|
1587
1617
|
"""
|
|
1618
|
+
# Get the value to label mapping of each parcellation; lists are
|
|
1619
|
+
# matched to the values in the parcellation
|
|
1620
|
+
label_maps = []
|
|
1621
|
+
for parc, labels in zip(parcellations_list, labels_lists, strict=True):
|
|
1622
|
+
if isinstance(labels, dict):
|
|
1623
|
+
label_maps.append(dict(labels))
|
|
1624
|
+
else:
|
|
1625
|
+
values = np.trim_zeros(np.unique(parc.get_fdata().astype(int)))
|
|
1626
|
+
label_maps.append(dict(zip(values.tolist(), labels, strict=True)))
|
|
1627
|
+
|
|
1588
1628
|
# Check for duplicated labels
|
|
1589
|
-
|
|
1590
|
-
if len(
|
|
1629
|
+
labels_flat = [label for m in label_maps for label in m.values()]
|
|
1630
|
+
if len(labels_flat) != len(set(labels_flat)):
|
|
1591
1631
|
warn_with_log(
|
|
1592
1632
|
"The parcellations have duplicated labels. "
|
|
1593
1633
|
"Each label will be prefixed with the parcellation name."
|
|
1594
1634
|
)
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
]
|
|
1635
|
+
label_maps = [
|
|
1636
|
+
{value: f"{name}_{label}" for value, label in m.items()}
|
|
1637
|
+
for name, m in zip(parcellations_names, label_maps, strict=True)
|
|
1638
|
+
]
|
|
1600
1639
|
overlapping_voxels = False
|
|
1601
1640
|
ref_parc = parcellations_list[0]
|
|
1602
1641
|
ref_parc_data = ref_parc.get_fdata()
|
|
1603
1642
|
|
|
1604
|
-
# Get max value for the parcellation ROIs
|
|
1605
|
-
# increment
|
|
1606
|
-
max_val =
|
|
1607
|
-
np.
|
|
1608
|
-
|
|
1609
|
-
)
|
|
1643
|
+
# Get max value for the parcellation ROIs (including the ones that might
|
|
1644
|
+
# not be in the images) to get a reference for ROI value increment
|
|
1645
|
+
max_val = max(
|
|
1646
|
+
max(np.max(p.get_fdata().astype(int)) for p in parcellations_list),
|
|
1647
|
+
max(max(m, default=0) for m in label_maps),
|
|
1610
1648
|
)
|
|
1611
1649
|
# Setup parcellation value to label mapping
|
|
1612
|
-
val_label_map = dict(
|
|
1613
|
-
zip(
|
|
1614
|
-
np.trim_zeros(np.unique(ref_parc_data.astype(int))),
|
|
1615
|
-
labels_lists[0],
|
|
1616
|
-
),
|
|
1617
|
-
)
|
|
1650
|
+
val_label_map = dict(label_maps[0])
|
|
1618
1651
|
|
|
1619
1652
|
for idx, (parc, labs) in enumerate(
|
|
1620
|
-
zip(parcellations_list[1:],
|
|
1653
|
+
zip(parcellations_list[1:], label_maps[1:], strict=True)
|
|
1621
1654
|
):
|
|
1622
1655
|
# Resample to reference (1st in the list) parcellation
|
|
1623
1656
|
if parc.shape != ref_parc.shape:
|
|
@@ -1636,8 +1669,9 @@ def merge_parcellations(
|
|
|
1636
1669
|
# Increase the values of each ROI to match the labels
|
|
1637
1670
|
# and update label mapping
|
|
1638
1671
|
parc_data[parc_data != 0] += (idx + 1) * max_val
|
|
1672
|
+
offset = (idx + 1) * max_val
|
|
1639
1673
|
val_label_map.update(
|
|
1640
|
-
|
|
1674
|
+
{value + offset: label for value, label in labs.items()}
|
|
1641
1675
|
)
|
|
1642
1676
|
# Only set new values for the voxels that are 0
|
|
1643
1677
|
# This makes sure that the voxels that are in multiple
|