junifer 0.0.5.dev240__py3-none-any.whl → 0.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- junifer/__init__.py +2 -31
- junifer/__init__.pyi +37 -0
- junifer/_version.py +9 -4
- junifer/api/__init__.py +3 -5
- junifer/api/__init__.pyi +4 -0
- junifer/api/decorators.py +14 -19
- junifer/api/functions.py +165 -109
- junifer/api/py.typed +0 -0
- junifer/api/queue_context/__init__.py +2 -4
- junifer/api/queue_context/__init__.pyi +5 -0
- junifer/api/queue_context/gnu_parallel_local_adapter.py +22 -6
- junifer/api/queue_context/htcondor_adapter.py +23 -6
- junifer/api/queue_context/py.typed +0 -0
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +3 -3
- junifer/api/queue_context/tests/test_htcondor_adapter.py +3 -3
- junifer/api/tests/test_functions.py +168 -74
- junifer/cli/__init__.py +24 -0
- junifer/cli/__init__.pyi +3 -0
- junifer/{api → cli}/cli.py +141 -125
- junifer/cli/parser.py +235 -0
- junifer/cli/py.typed +0 -0
- junifer/{api → cli}/tests/test_cli.py +8 -8
- junifer/{api/tests/test_api_utils.py → cli/tests/test_cli_utils.py} +5 -4
- junifer/{api → cli}/tests/test_parser.py +2 -2
- junifer/{api → cli}/utils.py +6 -16
- junifer/configs/juseless/__init__.py +2 -2
- junifer/configs/juseless/__init__.pyi +3 -0
- junifer/configs/juseless/datagrabbers/__init__.py +2 -12
- junifer/configs/juseless/datagrabbers/__init__.pyi +13 -0
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +2 -2
- junifer/configs/juseless/datagrabbers/py.typed +0 -0
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +2 -2
- junifer/configs/juseless/datagrabbers/ucla.py +4 -4
- junifer/configs/juseless/py.typed +0 -0
- junifer/conftest.py +25 -0
- junifer/data/__init__.py +2 -42
- junifer/data/__init__.pyi +29 -0
- junifer/data/_dispatch.py +248 -0
- junifer/data/coordinates/__init__.py +9 -0
- junifer/data/coordinates/__init__.pyi +5 -0
- junifer/data/coordinates/_ants_coordinates_warper.py +104 -0
- junifer/data/coordinates/_coordinates.py +385 -0
- junifer/data/coordinates/_fsl_coordinates_warper.py +81 -0
- junifer/data/{tests → coordinates/tests}/test_coordinates.py +26 -33
- junifer/data/masks/__init__.py +9 -0
- junifer/data/masks/__init__.pyi +6 -0
- junifer/data/masks/_ants_mask_warper.py +177 -0
- junifer/data/masks/_fsl_mask_warper.py +106 -0
- junifer/data/masks/_masks.py +802 -0
- junifer/data/{tests → masks/tests}/test_masks.py +67 -63
- junifer/data/parcellations/__init__.py +9 -0
- junifer/data/parcellations/__init__.pyi +6 -0
- junifer/data/parcellations/_ants_parcellation_warper.py +166 -0
- junifer/data/parcellations/_fsl_parcellation_warper.py +89 -0
- junifer/data/parcellations/_parcellations.py +1388 -0
- junifer/data/{tests → parcellations/tests}/test_parcellations.py +165 -295
- junifer/data/pipeline_data_registry_base.py +76 -0
- junifer/data/py.typed +0 -0
- junifer/data/template_spaces.py +44 -79
- junifer/data/tests/test_data_utils.py +1 -2
- junifer/data/tests/test_template_spaces.py +8 -4
- junifer/data/utils.py +109 -4
- junifer/datagrabber/__init__.py +2 -26
- junifer/datagrabber/__init__.pyi +27 -0
- junifer/datagrabber/aomic/__init__.py +2 -4
- junifer/datagrabber/aomic/__init__.pyi +5 -0
- junifer/datagrabber/aomic/id1000.py +81 -52
- junifer/datagrabber/aomic/piop1.py +83 -55
- junifer/datagrabber/aomic/piop2.py +85 -56
- junifer/datagrabber/aomic/py.typed +0 -0
- junifer/datagrabber/aomic/tests/test_id1000.py +19 -12
- junifer/datagrabber/aomic/tests/test_piop1.py +52 -18
- junifer/datagrabber/aomic/tests/test_piop2.py +50 -17
- junifer/datagrabber/base.py +22 -18
- junifer/datagrabber/datalad_base.py +71 -34
- junifer/datagrabber/dmcc13_benchmark.py +31 -18
- junifer/datagrabber/hcp1200/__init__.py +2 -3
- junifer/datagrabber/hcp1200/__init__.pyi +4 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -3
- junifer/datagrabber/hcp1200/hcp1200.py +26 -15
- junifer/datagrabber/hcp1200/py.typed +0 -0
- junifer/datagrabber/hcp1200/tests/test_hcp1200.py +8 -2
- junifer/datagrabber/multiple.py +14 -9
- junifer/datagrabber/pattern.py +132 -96
- junifer/datagrabber/pattern_validation_mixin.py +206 -94
- junifer/datagrabber/py.typed +0 -0
- junifer/datagrabber/tests/test_datalad_base.py +27 -12
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +28 -11
- junifer/datagrabber/tests/test_multiple.py +48 -2
- junifer/datagrabber/tests/test_pattern_datalad.py +1 -1
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +6 -6
- junifer/datareader/__init__.py +2 -2
- junifer/datareader/__init__.pyi +3 -0
- junifer/datareader/default.py +6 -6
- junifer/datareader/py.typed +0 -0
- junifer/external/nilearn/__init__.py +2 -3
- junifer/external/nilearn/__init__.pyi +4 -0
- junifer/external/nilearn/junifer_connectivity_measure.py +25 -17
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +4 -4
- junifer/external/nilearn/py.typed +0 -0
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +17 -16
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +2 -3
- junifer/markers/__init__.py +2 -38
- junifer/markers/__init__.pyi +37 -0
- junifer/markers/base.py +11 -14
- junifer/markers/brainprint.py +12 -14
- junifer/markers/complexity/__init__.py +2 -18
- junifer/markers/complexity/__init__.pyi +17 -0
- junifer/markers/complexity/complexity_base.py +9 -11
- junifer/markers/complexity/hurst_exponent.py +7 -7
- junifer/markers/complexity/multiscale_entropy_auc.py +7 -7
- junifer/markers/complexity/perm_entropy.py +7 -7
- junifer/markers/complexity/py.typed +0 -0
- junifer/markers/complexity/range_entropy.py +7 -7
- junifer/markers/complexity/range_entropy_auc.py +7 -7
- junifer/markers/complexity/sample_entropy.py +7 -7
- junifer/markers/complexity/tests/test_complexity_base.py +1 -1
- junifer/markers/complexity/tests/test_hurst_exponent.py +5 -5
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +5 -5
- junifer/markers/complexity/tests/test_perm_entropy.py +5 -5
- junifer/markers/complexity/tests/test_range_entropy.py +5 -5
- junifer/markers/complexity/tests/test_range_entropy_auc.py +5 -5
- junifer/markers/complexity/tests/test_sample_entropy.py +5 -5
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +5 -5
- junifer/markers/complexity/weighted_perm_entropy.py +7 -7
- junifer/markers/ets_rss.py +12 -11
- junifer/markers/falff/__init__.py +2 -3
- junifer/markers/falff/__init__.pyi +4 -0
- junifer/markers/falff/_afni_falff.py +38 -45
- junifer/markers/falff/_junifer_falff.py +16 -19
- junifer/markers/falff/falff_base.py +7 -11
- junifer/markers/falff/falff_parcels.py +9 -9
- junifer/markers/falff/falff_spheres.py +8 -8
- junifer/markers/falff/py.typed +0 -0
- junifer/markers/falff/tests/test_falff_spheres.py +3 -1
- junifer/markers/functional_connectivity/__init__.py +2 -12
- junifer/markers/functional_connectivity/__init__.pyi +13 -0
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +9 -8
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +8 -8
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +7 -7
- junifer/markers/functional_connectivity/functional_connectivity_base.py +13 -12
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +8 -8
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +7 -7
- junifer/markers/functional_connectivity/py.typed +0 -0
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +1 -2
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +1 -2
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +6 -6
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +5 -5
- junifer/markers/parcel_aggregation.py +22 -17
- junifer/markers/py.typed +0 -0
- junifer/markers/reho/__init__.py +2 -3
- junifer/markers/reho/__init__.pyi +4 -0
- junifer/markers/reho/_afni_reho.py +29 -35
- junifer/markers/reho/_junifer_reho.py +13 -14
- junifer/markers/reho/py.typed +0 -0
- junifer/markers/reho/reho_base.py +7 -11
- junifer/markers/reho/reho_parcels.py +10 -10
- junifer/markers/reho/reho_spheres.py +9 -9
- junifer/markers/sphere_aggregation.py +22 -17
- junifer/markers/temporal_snr/__init__.py +2 -3
- junifer/markers/temporal_snr/__init__.pyi +4 -0
- junifer/markers/temporal_snr/py.typed +0 -0
- junifer/markers/temporal_snr/temporal_snr_base.py +11 -10
- junifer/markers/temporal_snr/temporal_snr_parcels.py +8 -8
- junifer/markers/temporal_snr/temporal_snr_spheres.py +7 -7
- junifer/markers/tests/test_ets_rss.py +3 -3
- junifer/markers/tests/test_parcel_aggregation.py +24 -24
- junifer/markers/tests/test_sphere_aggregation.py +6 -6
- junifer/markers/utils.py +3 -3
- junifer/onthefly/__init__.py +2 -1
- junifer/onthefly/_brainprint.py +138 -0
- junifer/onthefly/read_transform.py +5 -8
- junifer/pipeline/__init__.py +2 -10
- junifer/pipeline/__init__.pyi +13 -0
- junifer/{markers/collection.py → pipeline/marker_collection.py} +8 -14
- junifer/pipeline/pipeline_component_registry.py +294 -0
- junifer/pipeline/pipeline_step_mixin.py +15 -11
- junifer/pipeline/py.typed +0 -0
- junifer/{markers/tests/test_collection.py → pipeline/tests/test_marker_collection.py} +2 -3
- junifer/pipeline/tests/test_pipeline_component_registry.py +200 -0
- junifer/pipeline/tests/test_pipeline_step_mixin.py +36 -37
- junifer/pipeline/tests/test_update_meta_mixin.py +4 -4
- junifer/pipeline/tests/test_workdir_manager.py +43 -0
- junifer/pipeline/update_meta_mixin.py +21 -17
- junifer/pipeline/utils.py +6 -6
- junifer/pipeline/workdir_manager.py +19 -5
- junifer/preprocess/__init__.py +2 -10
- junifer/preprocess/__init__.pyi +11 -0
- junifer/preprocess/base.py +10 -10
- junifer/preprocess/confounds/__init__.py +2 -2
- junifer/preprocess/confounds/__init__.pyi +3 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +243 -64
- junifer/preprocess/confounds/py.typed +0 -0
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +121 -14
- junifer/preprocess/py.typed +0 -0
- junifer/preprocess/smoothing/__init__.py +2 -2
- junifer/preprocess/smoothing/__init__.pyi +3 -0
- junifer/preprocess/smoothing/_afni_smoothing.py +40 -40
- junifer/preprocess/smoothing/_fsl_smoothing.py +22 -32
- junifer/preprocess/smoothing/_nilearn_smoothing.py +35 -14
- junifer/preprocess/smoothing/py.typed +0 -0
- junifer/preprocess/smoothing/smoothing.py +11 -13
- junifer/preprocess/warping/__init__.py +2 -2
- junifer/preprocess/warping/__init__.pyi +3 -0
- junifer/preprocess/warping/_ants_warper.py +136 -32
- junifer/preprocess/warping/_fsl_warper.py +73 -22
- junifer/preprocess/warping/py.typed +0 -0
- junifer/preprocess/warping/space_warper.py +39 -11
- junifer/preprocess/warping/tests/test_space_warper.py +5 -9
- junifer/py.typed +0 -0
- junifer/stats.py +5 -5
- junifer/storage/__init__.py +2 -10
- junifer/storage/__init__.pyi +11 -0
- junifer/storage/base.py +47 -13
- junifer/storage/hdf5.py +95 -33
- junifer/storage/pandas_base.py +12 -11
- junifer/storage/py.typed +0 -0
- junifer/storage/sqlite.py +11 -11
- junifer/storage/tests/test_hdf5.py +86 -4
- junifer/storage/tests/test_sqlite.py +2 -2
- junifer/storage/tests/test_storage_base.py +5 -2
- junifer/storage/tests/test_utils.py +33 -7
- junifer/storage/utils.py +95 -9
- junifer/testing/__init__.py +2 -3
- junifer/testing/__init__.pyi +4 -0
- junifer/testing/datagrabbers.py +10 -11
- junifer/testing/py.typed +0 -0
- junifer/testing/registry.py +4 -7
- junifer/testing/tests/test_testing_registry.py +9 -17
- junifer/tests/test_stats.py +2 -2
- junifer/typing/__init__.py +9 -0
- junifer/typing/__init__.pyi +31 -0
- junifer/typing/_typing.py +68 -0
- junifer/utils/__init__.py +2 -12
- junifer/utils/__init__.pyi +18 -0
- junifer/utils/_config.py +110 -0
- junifer/utils/_yaml.py +16 -0
- junifer/utils/helpers.py +6 -6
- junifer/utils/logging.py +117 -8
- junifer/utils/py.typed +0 -0
- junifer/{pipeline → utils}/singleton.py +19 -14
- junifer/utils/tests/test_config.py +59 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/METADATA +43 -38
- junifer-0.0.6.dist-info/RECORD +350 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/WHEEL +1 -1
- junifer-0.0.6.dist-info/entry_points.txt +2 -0
- junifer/api/parser.py +0 -118
- junifer/data/coordinates.py +0 -408
- junifer/data/masks.py +0 -670
- junifer/data/parcellations.py +0 -1828
- junifer/pipeline/registry.py +0 -177
- junifer/pipeline/tests/test_registry.py +0 -150
- junifer-0.0.5.dev240.dist-info/RECORD +0 -275
- junifer-0.0.5.dev240.dist-info/entry_points.txt +0 -2
- /junifer/{api → cli}/tests/data/gmd_mean.yaml +0 -0
- /junifer/{api → cli}/tests/data/gmd_mean_htcondor.yaml +0 -0
- /junifer/{api → cli}/tests/data/partly_cloudy_agg_mean_tian.yml +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/AutobiographicalMemory_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAC_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/CogAR_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/DMNBuckner_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Dosenbach2010_MNI_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Empathy_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Motor_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/MultiTask_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/PhysioStress_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2011_MNI_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Power2013_MNI_VOIs.tsv +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Rew_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/Somatosensory_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/ToM_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/VigAtt_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/WM_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/eMDN_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/eSAD_VOIs.txt +0 -0
- /junifer/data/{VOIs → coordinates/VOIs}/meta/extDMN_VOIs.txt +0 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/AUTHORS.rst +0 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info/licenses}/LICENSE.md +0 -0
- {junifer-0.0.5.dev240.dist-info → junifer-0.0.6.dist-info}/top_level.txt +0 -0
@@ -6,10 +6,6 @@
|
|
6
6
|
from typing import (
|
7
7
|
Any,
|
8
8
|
ClassVar,
|
9
|
-
Dict,
|
10
|
-
List,
|
11
|
-
Set,
|
12
|
-
Union,
|
13
9
|
)
|
14
10
|
|
15
11
|
import nibabel as nib
|
@@ -17,7 +13,8 @@ import numpy as np
|
|
17
13
|
|
18
14
|
from ...data import get_template, get_xfm
|
19
15
|
from ...pipeline import WorkDirManager
|
20
|
-
from ...
|
16
|
+
from ...typing import Dependencies, ExternalDependencies
|
17
|
+
from ...utils import logger, raise_error, run_ext_cmd
|
21
18
|
|
22
19
|
|
23
20
|
__all__ = ["ANTsWarper"]
|
@@ -31,21 +28,21 @@ class ANTsWarper:
|
|
31
28
|
|
32
29
|
"""
|
33
30
|
|
34
|
-
_EXT_DEPENDENCIES: ClassVar[
|
31
|
+
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
|
35
32
|
{
|
36
33
|
"name": "ants",
|
37
34
|
"commands": ["ResampleImage", "antsApplyTransforms"],
|
38
35
|
},
|
39
36
|
]
|
40
37
|
|
41
|
-
_DEPENDENCIES: ClassVar[
|
38
|
+
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nibabel"}
|
42
39
|
|
43
40
|
def preprocess(
|
44
41
|
self,
|
45
|
-
input:
|
46
|
-
extra_input:
|
42
|
+
input: dict[str, Any],
|
43
|
+
extra_input: dict[str, Any],
|
47
44
|
reference: str,
|
48
|
-
) ->
|
45
|
+
) -> dict[str, Any]:
|
49
46
|
"""Preprocess using ANTs.
|
50
47
|
|
51
48
|
Parameters
|
@@ -61,9 +58,12 @@ class ANTsWarper:
|
|
61
58
|
Returns
|
62
59
|
-------
|
63
60
|
dict
|
64
|
-
The ``input`` dictionary with
|
65
|
-
|
66
|
-
|
61
|
+
The ``input`` dictionary with updated values.
|
62
|
+
|
63
|
+
Raises
|
64
|
+
------
|
65
|
+
RuntimeError
|
66
|
+
If warp file path could not be found in ``extra_input``.
|
67
67
|
|
68
68
|
"""
|
69
69
|
# Create element-specific tempdir for storing post-warping assets
|
@@ -79,6 +79,17 @@ class ANTsWarper:
|
|
79
79
|
# resolution
|
80
80
|
resolution = np.min(input["data"].header.get_zooms()[:3])
|
81
81
|
|
82
|
+
# Get warp file path
|
83
|
+
warp_file_path = None
|
84
|
+
for entry in extra_input["Warp"]:
|
85
|
+
if entry["dst"] == "native":
|
86
|
+
warp_file_path = entry["path"]
|
87
|
+
if warp_file_path is None:
|
88
|
+
raise_error(
|
89
|
+
klass=RuntimeError,
|
90
|
+
msg="Could not find correct warp file path",
|
91
|
+
)
|
92
|
+
|
82
93
|
# Create a tempfile for resampled reference output
|
83
94
|
resample_image_out_path = (
|
84
95
|
element_tempdir / "resampled_reference.nii.gz"
|
@@ -97,7 +108,7 @@ class ANTsWarper:
|
|
97
108
|
run_ext_cmd(name="ResampleImage", cmd=resample_image_cmd)
|
98
109
|
|
99
110
|
# Create a tempfile for warped output
|
100
|
-
apply_transforms_out_path = element_tempdir / "
|
111
|
+
apply_transforms_out_path = element_tempdir / "warped_data.nii.gz"
|
101
112
|
# Set antsApplyTransforms command
|
102
113
|
apply_transforms_cmd = [
|
103
114
|
"antsApplyTransforms",
|
@@ -107,18 +118,65 @@ class ANTsWarper:
|
|
107
118
|
f"-i {input['path'].resolve()}",
|
108
119
|
# use resampled reference
|
109
120
|
f"-r {resample_image_out_path.resolve()}",
|
110
|
-
f"-t {
|
121
|
+
f"-t {warp_file_path.resolve()}",
|
111
122
|
f"-o {apply_transforms_out_path.resolve()}",
|
112
123
|
]
|
113
124
|
# Call antsApplyTransforms
|
114
125
|
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
115
126
|
|
116
|
-
|
117
|
-
input
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
127
|
+
logger.debug("Updating warped data")
|
128
|
+
input.update(
|
129
|
+
{
|
130
|
+
# Update path to sync with "data"
|
131
|
+
"path": apply_transforms_out_path,
|
132
|
+
# Load nifti
|
133
|
+
"data": nib.load(apply_transforms_out_path),
|
134
|
+
# Use reference input's space as warped input's space
|
135
|
+
"space": extra_input["T1w"]["space"],
|
136
|
+
# Save resampled reference path
|
137
|
+
"reference": {"path": resample_image_out_path},
|
138
|
+
# Keep pre-warp space for further operations
|
139
|
+
"prewarp_space": input["space"],
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
143
|
+
# Check for data type's mask and warp if found
|
144
|
+
if input.get("mask") is not None:
|
145
|
+
# Create a tempfile for warped mask output
|
146
|
+
apply_transforms_mask_out_path = (
|
147
|
+
element_tempdir / "warped_mask.nii.gz"
|
148
|
+
)
|
149
|
+
# Set antsApplyTransforms command
|
150
|
+
apply_transforms_mask_cmd = [
|
151
|
+
"antsApplyTransforms",
|
152
|
+
"-d 3",
|
153
|
+
"-e 3",
|
154
|
+
"-n 'GenericLabel[NearestNeighbor]'",
|
155
|
+
f"-i {input['mask']['path'].resolve()}",
|
156
|
+
# use resampled reference
|
157
|
+
f"-r {input['reference']['path'].resolve()}",
|
158
|
+
f"-t {warp_file_path.resolve()}",
|
159
|
+
f"-o {apply_transforms_mask_out_path.resolve()}",
|
160
|
+
]
|
161
|
+
# Call antsApplyTransforms
|
162
|
+
run_ext_cmd(
|
163
|
+
name="antsApplyTransforms", cmd=apply_transforms_mask_cmd
|
164
|
+
)
|
165
|
+
|
166
|
+
logger.debug("Updating warped mask data")
|
167
|
+
input.update(
|
168
|
+
{
|
169
|
+
"mask": {
|
170
|
+
# Update path to sync with "data"
|
171
|
+
"path": apply_transforms_mask_out_path,
|
172
|
+
# Load nifti
|
173
|
+
"data": nib.load(apply_transforms_mask_out_path),
|
174
|
+
# Use reference input's space as warped input
|
175
|
+
# mask's space
|
176
|
+
"space": extra_input["T1w"]["space"],
|
177
|
+
}
|
178
|
+
}
|
179
|
+
)
|
122
180
|
|
123
181
|
# Template space warping
|
124
182
|
else:
|
@@ -131,19 +189,18 @@ class ANTsWarper:
|
|
131
189
|
# Get template space image
|
132
190
|
template_space_img = get_template(
|
133
191
|
space=reference,
|
134
|
-
|
192
|
+
target_img=input["data"],
|
135
193
|
extra_input=None,
|
136
194
|
)
|
137
|
-
|
138
|
-
# Create component-scoped tempdir
|
139
|
-
tempdir = WorkDirManager().get_tempdir(prefix="ants_warper")
|
140
195
|
# Save template
|
141
|
-
template_space_img_path =
|
196
|
+
template_space_img_path = (
|
197
|
+
element_tempdir / f"{reference}_T1w.nii.gz"
|
198
|
+
)
|
142
199
|
nib.save(template_space_img, template_space_img_path)
|
143
200
|
|
144
201
|
# Create a tempfile for warped output
|
145
202
|
warped_output_path = element_tempdir / (
|
146
|
-
f"
|
203
|
+
f"warped_data_from_{input['space']}_to_{reference}.nii.gz"
|
147
204
|
)
|
148
205
|
|
149
206
|
# Set antsApplyTransforms command
|
@@ -160,11 +217,58 @@ class ANTsWarper:
|
|
160
217
|
# Call antsApplyTransforms
|
161
218
|
run_ext_cmd(name="antsApplyTransforms", cmd=apply_transforms_cmd)
|
162
219
|
|
163
|
-
|
164
|
-
|
220
|
+
logger.debug("Updating warped data")
|
221
|
+
input.update(
|
222
|
+
{
|
223
|
+
# Update path to sync with "data"
|
224
|
+
"path": warped_output_path,
|
225
|
+
# Load nifti
|
226
|
+
"data": nib.load(warped_output_path),
|
227
|
+
# Update warped input's space
|
228
|
+
"space": reference,
|
229
|
+
# Save reference path
|
230
|
+
"reference": {"path": template_space_img_path},
|
231
|
+
# Keep pre-warp space for further operations
|
232
|
+
"prewarp_space": input["space"],
|
233
|
+
}
|
234
|
+
)
|
235
|
+
|
236
|
+
# Check for data type's mask and warp if found
|
237
|
+
if input.get("mask") is not None:
|
238
|
+
# Create a tempfile for warped mask output
|
239
|
+
apply_transforms_mask_out_path = element_tempdir / (
|
240
|
+
f"warped_mask_from_{input['space']}_to_"
|
241
|
+
f"{reference}.nii.gz"
|
242
|
+
)
|
243
|
+
# Set antsApplyTransforms command
|
244
|
+
apply_transforms_mask_cmd = [
|
245
|
+
"antsApplyTransforms",
|
246
|
+
"-d 3",
|
247
|
+
"-e 3",
|
248
|
+
"-n 'GenericLabel[NearestNeighbor]'",
|
249
|
+
f"-i {input['mask']['path'].resolve()}",
|
250
|
+
# use resampled reference
|
251
|
+
f"-r {input['reference']['path'].resolve()}",
|
252
|
+
f"-t {xfm_file_path.resolve()}",
|
253
|
+
f"-o {apply_transforms_mask_out_path.resolve()}",
|
254
|
+
]
|
255
|
+
# Call antsApplyTransforms
|
256
|
+
run_ext_cmd(
|
257
|
+
name="antsApplyTransforms", cmd=apply_transforms_mask_cmd
|
258
|
+
)
|
165
259
|
|
166
|
-
|
167
|
-
|
168
|
-
|
260
|
+
logger.debug("Updating warped mask data")
|
261
|
+
input.update(
|
262
|
+
{
|
263
|
+
"mask": {
|
264
|
+
# Update path to sync with "data"
|
265
|
+
"path": apply_transforms_mask_out_path,
|
266
|
+
# Load nifti
|
267
|
+
"data": nib.load(apply_transforms_mask_out_path),
|
268
|
+
# Update warped input mask's space
|
269
|
+
"space": reference,
|
270
|
+
}
|
271
|
+
}
|
272
|
+
)
|
169
273
|
|
170
274
|
return input
|
@@ -6,17 +6,14 @@
|
|
6
6
|
from typing import (
|
7
7
|
Any,
|
8
8
|
ClassVar,
|
9
|
-
Dict,
|
10
|
-
List,
|
11
|
-
Set,
|
12
|
-
Union,
|
13
9
|
)
|
14
10
|
|
15
11
|
import nibabel as nib
|
16
12
|
import numpy as np
|
17
13
|
|
18
14
|
from ...pipeline import WorkDirManager
|
19
|
-
from ...
|
15
|
+
from ...typing import Dependencies, ExternalDependencies
|
16
|
+
from ...utils import logger, raise_error, run_ext_cmd
|
20
17
|
|
21
18
|
|
22
19
|
__all__ = ["FSLWarper"]
|
@@ -30,20 +27,20 @@ class FSLWarper:
|
|
30
27
|
|
31
28
|
"""
|
32
29
|
|
33
|
-
_EXT_DEPENDENCIES: ClassVar[
|
30
|
+
_EXT_DEPENDENCIES: ClassVar[ExternalDependencies] = [
|
34
31
|
{
|
35
32
|
"name": "fsl",
|
36
33
|
"commands": ["flirt", "applywarp"],
|
37
34
|
},
|
38
35
|
]
|
39
36
|
|
40
|
-
_DEPENDENCIES: ClassVar[
|
37
|
+
_DEPENDENCIES: ClassVar[Dependencies] = {"numpy", "nibabel"}
|
41
38
|
|
42
39
|
def preprocess(
|
43
40
|
self,
|
44
|
-
input:
|
45
|
-
extra_input:
|
46
|
-
) ->
|
41
|
+
input: dict[str, Any],
|
42
|
+
extra_input: dict[str, Any],
|
43
|
+
) -> dict[str, Any]:
|
47
44
|
"""Preprocess using FSL.
|
48
45
|
|
49
46
|
Parameters
|
@@ -57,9 +54,12 @@ class FSLWarper:
|
|
57
54
|
Returns
|
58
55
|
-------
|
59
56
|
dict
|
60
|
-
The ``input`` dictionary with
|
61
|
-
|
62
|
-
|
57
|
+
The ``input`` dictionary with updated values.
|
58
|
+
|
59
|
+
Raises
|
60
|
+
------
|
61
|
+
RuntimeError
|
62
|
+
If warp file path could not be found in ``extra_input``.
|
63
63
|
|
64
64
|
"""
|
65
65
|
logger.debug("Using FSL for space warping")
|
@@ -68,6 +68,16 @@ class FSLWarper:
|
|
68
68
|
# resolution
|
69
69
|
resolution = np.min(input["data"].header.get_zooms()[:3])
|
70
70
|
|
71
|
+
# Get warp file path
|
72
|
+
warp_file_path = None
|
73
|
+
for entry in extra_input["Warp"]:
|
74
|
+
if entry["dst"] == "native":
|
75
|
+
warp_file_path = entry["path"]
|
76
|
+
if warp_file_path is None:
|
77
|
+
raise_error(
|
78
|
+
klass=RuntimeError, msg="Could not find correct warp file path"
|
79
|
+
)
|
80
|
+
|
71
81
|
# Create element-specific tempdir for storing post-warping assets
|
72
82
|
element_tempdir = WorkDirManager().get_element_tempdir(
|
73
83
|
prefix="fsl_warper"
|
@@ -88,25 +98,66 @@ class FSLWarper:
|
|
88
98
|
run_ext_cmd(name="flirt", cmd=flirt_cmd)
|
89
99
|
|
90
100
|
# Create a tempfile for warped output
|
91
|
-
applywarp_out_path = element_tempdir / "
|
101
|
+
applywarp_out_path = element_tempdir / "warped_data.nii.gz"
|
92
102
|
# Set applywarp command
|
93
103
|
applywarp_cmd = [
|
94
104
|
"applywarp",
|
95
105
|
"--interp=spline",
|
96
106
|
f"-i {input['path'].resolve()}",
|
97
|
-
|
98
|
-
f"-
|
107
|
+
# use resampled reference
|
108
|
+
f"-r {flirt_out_path.resolve()}",
|
109
|
+
f"-w {warp_file_path.resolve()}",
|
99
110
|
f"-o {applywarp_out_path.resolve()}",
|
100
111
|
]
|
101
112
|
# Call applywarp
|
102
113
|
run_ext_cmd(name="applywarp", cmd=applywarp_cmd)
|
103
114
|
|
104
|
-
|
105
|
-
input
|
106
|
-
|
107
|
-
|
115
|
+
logger.debug("Updating warped data")
|
116
|
+
input.update(
|
117
|
+
{
|
118
|
+
# Update path to sync with "data"
|
119
|
+
"path": applywarp_out_path,
|
120
|
+
# Load nifti
|
121
|
+
"data": nib.load(applywarp_out_path),
|
122
|
+
# Use reference input's space as warped input's space
|
123
|
+
"space": extra_input["T1w"]["space"],
|
124
|
+
# Save resampled reference path
|
125
|
+
"reference": {"path": flirt_out_path},
|
126
|
+
# Keep pre-warp space for further operations
|
127
|
+
"prewarp_space": input["space"],
|
128
|
+
}
|
129
|
+
)
|
108
130
|
|
109
|
-
#
|
110
|
-
input
|
131
|
+
# Check for data type's mask and warp if found
|
132
|
+
if input.get("mask") is not None:
|
133
|
+
# Create a tempfile for warped mask output
|
134
|
+
applywarp_mask_out_path = element_tempdir / "warped_mask.nii.gz"
|
135
|
+
# Set applywarp command
|
136
|
+
applywarp_mask_cmd = [
|
137
|
+
"applywarp",
|
138
|
+
"--interp=nn",
|
139
|
+
f"-i {input['mask']['path'].resolve()}",
|
140
|
+
# use resampled reference
|
141
|
+
f"-r {input['reference']['path'].resolve()}",
|
142
|
+
f"-w {warp_file_path.resolve()}",
|
143
|
+
f"-o {applywarp_mask_out_path.resolve()}",
|
144
|
+
]
|
145
|
+
# Call applywarp
|
146
|
+
run_ext_cmd(name="applywarp", cmd=applywarp_mask_cmd)
|
147
|
+
|
148
|
+
logger.debug("Updating warped mask data")
|
149
|
+
input.update(
|
150
|
+
{
|
151
|
+
"mask": {
|
152
|
+
# Update path to sync with "data"
|
153
|
+
"path": applywarp_mask_out_path,
|
154
|
+
# Load nifti
|
155
|
+
"data": nib.load(applywarp_mask_out_path),
|
156
|
+
# Use reference input's space as warped input mask's
|
157
|
+
# space
|
158
|
+
"space": extra_input["T1w"]["space"],
|
159
|
+
}
|
160
|
+
}
|
161
|
+
)
|
111
162
|
|
112
163
|
return input
|
File without changes
|
@@ -3,11 +3,12 @@
|
|
3
3
|
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
4
|
# License: AGPL
|
5
5
|
|
6
|
-
from typing import Any, ClassVar,
|
6
|
+
from typing import Any, ClassVar, Optional, Union
|
7
7
|
|
8
8
|
from templateflow import api as tflow
|
9
9
|
|
10
10
|
from ...api.decorators import register_preprocessor
|
11
|
+
from ...typing import ConditionalDependencies
|
11
12
|
from ...utils import logger, raise_error
|
12
13
|
from ..base import BasePreprocessor
|
13
14
|
from ._ants_warper import ANTsWarper
|
@@ -23,11 +24,12 @@ class SpaceWarper(BasePreprocessor):
|
|
23
24
|
|
24
25
|
Parameters
|
25
26
|
----------
|
26
|
-
using : {"fsl", "ants"}
|
27
|
+
using : {"fsl", "ants", "auto"}
|
27
28
|
Implementation to use for warping:
|
28
29
|
|
29
30
|
* "fsl" : Use FSL's ``applywarp``
|
30
31
|
* "ants" : Use ANTs' ``antsApplyTransforms``
|
32
|
+
* "auto" : Auto-select tool when ``reference="T1w"``
|
31
33
|
|
32
34
|
reference : str
|
33
35
|
The data type to use as reference for warping, can be either a data
|
@@ -46,7 +48,7 @@ class SpaceWarper(BasePreprocessor):
|
|
46
48
|
|
47
49
|
"""
|
48
50
|
|
49
|
-
_CONDITIONAL_DEPENDENCIES: ClassVar[
|
51
|
+
_CONDITIONAL_DEPENDENCIES: ClassVar[ConditionalDependencies] = [
|
50
52
|
{
|
51
53
|
"using": "fsl",
|
52
54
|
"depends_on": FSLWarper,
|
@@ -55,10 +57,14 @@ class SpaceWarper(BasePreprocessor):
|
|
55
57
|
"using": "ants",
|
56
58
|
"depends_on": ANTsWarper,
|
57
59
|
},
|
60
|
+
{
|
61
|
+
"using": "auto",
|
62
|
+
"depends_on": [FSLWarper, ANTsWarper],
|
63
|
+
},
|
58
64
|
]
|
59
65
|
|
60
66
|
def __init__(
|
61
|
-
self, using: str, reference: str, on: Union[
|
67
|
+
self, using: str, reference: str, on: Union[list[str], str]
|
62
68
|
) -> None:
|
63
69
|
"""Initialize the class."""
|
64
70
|
# Validate `using` parameter
|
@@ -88,7 +94,7 @@ class SpaceWarper(BasePreprocessor):
|
|
88
94
|
else:
|
89
95
|
raise_error(f"Unknown reference: {self.reference}")
|
90
96
|
|
91
|
-
def get_valid_inputs(self) ->
|
97
|
+
def get_valid_inputs(self) -> list[str]:
|
92
98
|
"""Get valid data types for input.
|
93
99
|
|
94
100
|
Returns
|
@@ -129,9 +135,9 @@ class SpaceWarper(BasePreprocessor):
|
|
129
135
|
|
130
136
|
def preprocess(
|
131
137
|
self,
|
132
|
-
input:
|
133
|
-
extra_input: Optional[
|
134
|
-
) ->
|
138
|
+
input: dict[str, Any],
|
139
|
+
extra_input: Optional[dict[str, Any]] = None,
|
140
|
+
) -> tuple[dict[str, Any], Optional[dict[str, dict[str, Any]]]]:
|
135
141
|
"""Preprocess.
|
136
142
|
|
137
143
|
Parameters
|
@@ -155,14 +161,16 @@ class SpaceWarper(BasePreprocessor):
|
|
155
161
|
If ``extra_input`` is None when transforming to native space
|
156
162
|
i.e., using ``"T1w"`` as reference.
|
157
163
|
RuntimeError
|
158
|
-
If
|
164
|
+
If warper could not be found in ``extra_input`` when
|
165
|
+
``using="auto"`` or
|
166
|
+
if the data is in the correct space and does not require
|
159
167
|
warping or
|
160
|
-
if FSL is used
|
168
|
+
if FSL is used when ``reference="T1w"``.
|
161
169
|
|
162
170
|
"""
|
163
171
|
logger.info(f"Warping to {self.reference} space using SpaceWarper")
|
164
172
|
# Transform to native space
|
165
|
-
if self.using in ["fsl", "ants"] and self.reference == "T1w":
|
173
|
+
if self.using in ["fsl", "ants", "auto"] and self.reference == "T1w":
|
166
174
|
# Check for extra inputs
|
167
175
|
if extra_input is None:
|
168
176
|
raise_error(
|
@@ -181,6 +189,26 @@ class SpaceWarper(BasePreprocessor):
|
|
181
189
|
extra_input=extra_input,
|
182
190
|
reference=self.reference,
|
183
191
|
)
|
192
|
+
elif self.using == "auto":
|
193
|
+
warper = None
|
194
|
+
for entry in extra_input["Warp"]:
|
195
|
+
if entry["dst"] == "native":
|
196
|
+
warper = entry["warper"]
|
197
|
+
if warper is None:
|
198
|
+
raise_error(
|
199
|
+
klass=RuntimeError, msg="Could not find correct warper"
|
200
|
+
)
|
201
|
+
if warper == "fsl":
|
202
|
+
input = FSLWarper().preprocess(
|
203
|
+
input=input,
|
204
|
+
extra_input=extra_input,
|
205
|
+
)
|
206
|
+
elif warper == "ants":
|
207
|
+
input = ANTsWarper().preprocess(
|
208
|
+
input=input,
|
209
|
+
extra_input=extra_input,
|
210
|
+
reference=self.reference,
|
211
|
+
)
|
184
212
|
# Transform to template space with ANTs possible
|
185
213
|
elif self.using == "ants" and self.reference != "T1w":
|
186
214
|
# Check pre-requirements for space manipulation
|
@@ -4,7 +4,6 @@
|
|
4
4
|
# License: AGPL
|
5
5
|
|
6
6
|
import socket
|
7
|
-
from typing import TYPE_CHECKING, Tuple, Type
|
8
7
|
|
9
8
|
import pytest
|
10
9
|
from numpy.testing import assert_array_equal, assert_raises
|
@@ -14,10 +13,7 @@ from junifer.datareader import DefaultDataReader
|
|
14
13
|
from junifer.pipeline.utils import _check_ants, _check_fsl
|
15
14
|
from junifer.preprocess import SpaceWarper
|
16
15
|
from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
|
17
|
-
|
18
|
-
|
19
|
-
if TYPE_CHECKING:
|
20
|
-
from junifer.datagrabber import BaseDataGrabber
|
16
|
+
from junifer.typing import DataGrabberLike
|
21
17
|
|
22
18
|
|
23
19
|
@pytest.mark.parametrize(
|
@@ -32,7 +28,7 @@ if TYPE_CHECKING:
|
|
32
28
|
def test_SpaceWarper_errors(
|
33
29
|
using: str,
|
34
30
|
reference: str,
|
35
|
-
error_type:
|
31
|
+
error_type: type[Exception],
|
36
32
|
error_msg: str,
|
37
33
|
) -> None:
|
38
34
|
"""Test SpaceWarper errors.
|
@@ -99,7 +95,7 @@ def test_SpaceWarper_errors(
|
|
99
95
|
reason="only for juseless",
|
100
96
|
)
|
101
97
|
def test_SpaceWarper_native(
|
102
|
-
datagrabber:
|
98
|
+
datagrabber: DataGrabberLike, element: tuple[str, ...], using: str
|
103
99
|
) -> None:
|
104
100
|
"""Test SpaceWarper for native space warping.
|
105
101
|
|
@@ -162,8 +158,8 @@ def test_SpaceWarper_native(
|
|
162
158
|
_check_ants() is False, reason="requires ANTs to be in PATH"
|
163
159
|
)
|
164
160
|
def test_SpaceWarper_multi_mni(
|
165
|
-
datagrabber:
|
166
|
-
element:
|
161
|
+
datagrabber: DataGrabberLike,
|
162
|
+
element: tuple[str, ...],
|
167
163
|
space: str,
|
168
164
|
) -> None:
|
169
165
|
"""Test SpaceWarper for MNI space warping.
|
junifer/py.typed
ADDED
File without changes
|
junifer/stats.py
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
|
-
from typing import Any, Callable,
|
7
|
+
from typing import Any, Callable, Optional
|
8
8
|
|
9
9
|
import numpy as np
|
10
10
|
from scipy.stats import mode, trim_mean
|
@@ -13,11 +13,11 @@ from scipy.stats.mstats import winsorize
|
|
13
13
|
from .utils import logger, raise_error
|
14
14
|
|
15
15
|
|
16
|
-
__all__ = ["
|
16
|
+
__all__ = ["count", "get_aggfunc_by_name", "select", "winsorized_mean"]
|
17
17
|
|
18
18
|
|
19
19
|
def get_aggfunc_by_name(
|
20
|
-
name: str, func_params: Optional[
|
20
|
+
name: str, func_params: Optional[dict[str, Any]] = None
|
21
21
|
) -> Callable:
|
22
22
|
"""Get an aggregation function by its name.
|
23
23
|
|
@@ -169,8 +169,8 @@ def winsorized_mean(
|
|
169
169
|
def select(
|
170
170
|
data: np.ndarray,
|
171
171
|
axis: int = 0,
|
172
|
-
pick: Optional[
|
173
|
-
drop: Optional[
|
172
|
+
pick: Optional[list[int]] = None,
|
173
|
+
drop: Optional[list[int]] = None,
|
174
174
|
) -> np.ndarray:
|
175
175
|
"""Select a subset of the data.
|
176
176
|
|
junifer/storage/__init__.py
CHANGED
@@ -4,15 +4,7 @@
|
|
4
4
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
|
-
|
8
|
-
from .pandas_base import PandasBaseFeatureStorage
|
9
|
-
from .sqlite import SQLiteFeatureStorage
|
10
|
-
from .hdf5 import HDF5FeatureStorage
|
7
|
+
import lazy_loader as lazy
|
11
8
|
|
12
9
|
|
13
|
-
__all__ =
|
14
|
-
"BaseFeatureStorage",
|
15
|
-
"PandasBaseFeatureStorage",
|
16
|
-
"SQLiteFeatureStorage",
|
17
|
-
"HDF5FeatureStorage",
|
18
|
-
]
|
10
|
+
__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
__all__ = [
|
2
|
+
"BaseFeatureStorage",
|
3
|
+
"PandasBaseFeatureStorage",
|
4
|
+
"SQLiteFeatureStorage",
|
5
|
+
"HDF5FeatureStorage",
|
6
|
+
]
|
7
|
+
|
8
|
+
from .base import BaseFeatureStorage
|
9
|
+
from .pandas_base import PandasBaseFeatureStorage
|
10
|
+
from .sqlite import SQLiteFeatureStorage
|
11
|
+
from .hdf5 import HDF5FeatureStorage
|