junifer 0.0.4.dev831__py3-none-any.whl → 0.0.5__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 +17 -0
- junifer/_version.py +2 -2
- junifer/api/__init__.py +4 -1
- junifer/api/cli.py +91 -1
- junifer/api/decorators.py +9 -0
- junifer/api/functions.py +56 -10
- junifer/api/parser.py +3 -0
- junifer/api/queue_context/__init__.py +4 -1
- junifer/api/queue_context/gnu_parallel_local_adapter.py +16 -6
- junifer/api/queue_context/htcondor_adapter.py +16 -5
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +41 -12
- junifer/api/queue_context/tests/test_htcondor_adapter.py +48 -15
- junifer/api/res/afni/run_afni_docker.sh +1 -1
- junifer/api/res/ants/run_ants_docker.sh +1 -1
- junifer/api/res/freesurfer/mri_binarize +3 -0
- junifer/api/res/freesurfer/mri_mc +3 -0
- junifer/api/res/freesurfer/mri_pretess +3 -0
- junifer/api/res/freesurfer/mris_convert +3 -0
- junifer/api/res/freesurfer/run_freesurfer_docker.sh +61 -0
- junifer/api/res/fsl/run_fsl_docker.sh +1 -1
- junifer/api/res/{run_conda.sh → run_conda.bash} +1 -1
- junifer/api/res/run_conda.zsh +23 -0
- junifer/api/res/run_venv.bash +22 -0
- junifer/api/res/{run_venv.sh → run_venv.zsh} +1 -1
- junifer/api/tests/test_api_utils.py +4 -2
- junifer/api/tests/test_cli.py +83 -0
- junifer/api/tests/test_functions.py +27 -2
- junifer/configs/__init__.py +1 -1
- junifer/configs/juseless/__init__.py +4 -1
- junifer/configs/juseless/datagrabbers/__init__.py +10 -1
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +4 -3
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +3 -0
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +4 -3
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +1 -3
- junifer/configs/juseless/datagrabbers/ucla.py +12 -9
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +3 -0
- junifer/data/__init__.py +21 -1
- junifer/data/coordinates.py +10 -19
- junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz +0 -0
- junifer/data/masks.py +58 -87
- junifer/data/parcellations.py +14 -3
- junifer/data/template_spaces.py +4 -1
- junifer/data/tests/test_masks.py +26 -37
- junifer/data/utils.py +3 -0
- junifer/datagrabber/__init__.py +18 -1
- junifer/datagrabber/aomic/__init__.py +3 -0
- junifer/datagrabber/aomic/id1000.py +70 -37
- junifer/datagrabber/aomic/piop1.py +69 -36
- junifer/datagrabber/aomic/piop2.py +71 -38
- junifer/datagrabber/aomic/tests/test_id1000.py +44 -100
- junifer/datagrabber/aomic/tests/test_piop1.py +65 -108
- junifer/datagrabber/aomic/tests/test_piop2.py +45 -102
- junifer/datagrabber/base.py +13 -6
- junifer/datagrabber/datalad_base.py +13 -1
- junifer/datagrabber/dmcc13_benchmark.py +36 -53
- junifer/datagrabber/hcp1200/__init__.py +3 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +3 -0
- junifer/datagrabber/hcp1200/hcp1200.py +4 -1
- junifer/datagrabber/multiple.py +45 -6
- junifer/datagrabber/pattern.py +170 -62
- junifer/datagrabber/pattern_datalad.py +25 -12
- junifer/datagrabber/pattern_validation_mixin.py +388 -0
- junifer/datagrabber/tests/test_datalad_base.py +4 -4
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +46 -19
- junifer/datagrabber/tests/test_multiple.py +161 -84
- junifer/datagrabber/tests/test_pattern.py +45 -0
- junifer/datagrabber/tests/test_pattern_datalad.py +4 -4
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +249 -0
- junifer/datareader/__init__.py +4 -1
- junifer/datareader/default.py +95 -43
- junifer/external/BrainPrint/brainprint/__init__.py +4 -0
- junifer/external/BrainPrint/brainprint/_version.py +3 -0
- junifer/external/BrainPrint/brainprint/asymmetry.py +91 -0
- junifer/external/BrainPrint/brainprint/brainprint.py +441 -0
- junifer/external/BrainPrint/brainprint/surfaces.py +258 -0
- junifer/external/BrainPrint/brainprint/utils/__init__.py +1 -0
- junifer/external/BrainPrint/brainprint/utils/_config.py +112 -0
- junifer/external/BrainPrint/brainprint/utils/utils.py +188 -0
- junifer/external/__init__.py +1 -1
- junifer/external/nilearn/__init__.py +5 -1
- junifer/external/nilearn/junifer_connectivity_measure.py +483 -0
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +23 -9
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +1089 -0
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +76 -1
- junifer/markers/__init__.py +23 -1
- junifer/markers/base.py +68 -28
- junifer/markers/brainprint.py +459 -0
- junifer/markers/collection.py +10 -2
- junifer/markers/complexity/__init__.py +10 -0
- junifer/markers/complexity/complexity_base.py +26 -43
- junifer/markers/complexity/hurst_exponent.py +3 -0
- junifer/markers/complexity/multiscale_entropy_auc.py +3 -0
- junifer/markers/complexity/perm_entropy.py +3 -0
- junifer/markers/complexity/range_entropy.py +3 -0
- junifer/markers/complexity/range_entropy_auc.py +3 -0
- junifer/markers/complexity/sample_entropy.py +3 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +11 -3
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +11 -3
- junifer/markers/complexity/tests/test_perm_entropy.py +11 -3
- junifer/markers/complexity/tests/test_range_entropy.py +11 -3
- junifer/markers/complexity/tests/test_range_entropy_auc.py +11 -3
- junifer/markers/complexity/tests/test_sample_entropy.py +11 -3
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +11 -3
- junifer/markers/complexity/weighted_perm_entropy.py +3 -0
- junifer/markers/ets_rss.py +27 -42
- junifer/markers/falff/__init__.py +3 -0
- junifer/markers/falff/_afni_falff.py +5 -2
- junifer/markers/falff/_junifer_falff.py +3 -0
- junifer/markers/falff/falff_base.py +20 -46
- junifer/markers/falff/falff_parcels.py +56 -27
- junifer/markers/falff/falff_spheres.py +60 -29
- junifer/markers/falff/tests/test_falff_parcels.py +39 -23
- junifer/markers/falff/tests/test_falff_spheres.py +39 -23
- junifer/markers/functional_connectivity/__init__.py +9 -0
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +63 -60
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +45 -32
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +49 -36
- junifer/markers/functional_connectivity/functional_connectivity_base.py +71 -70
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +34 -25
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +40 -30
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +11 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +27 -7
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +28 -12
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +35 -11
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +36 -62
- junifer/markers/parcel_aggregation.py +47 -61
- junifer/markers/reho/__init__.py +3 -0
- junifer/markers/reho/_afni_reho.py +5 -2
- junifer/markers/reho/_junifer_reho.py +4 -1
- junifer/markers/reho/reho_base.py +8 -27
- junifer/markers/reho/reho_parcels.py +28 -17
- junifer/markers/reho/reho_spheres.py +27 -18
- junifer/markers/reho/tests/test_reho_parcels.py +8 -3
- junifer/markers/reho/tests/test_reho_spheres.py +8 -3
- junifer/markers/sphere_aggregation.py +43 -59
- junifer/markers/temporal_snr/__init__.py +3 -0
- junifer/markers/temporal_snr/temporal_snr_base.py +23 -32
- junifer/markers/temporal_snr/temporal_snr_parcels.py +9 -6
- junifer/markers/temporal_snr/temporal_snr_spheres.py +9 -6
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +6 -3
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +6 -3
- junifer/markers/tests/test_brainprint.py +58 -0
- junifer/markers/tests/test_collection.py +9 -8
- junifer/markers/tests/test_ets_rss.py +15 -9
- junifer/markers/tests/test_markers_base.py +17 -18
- junifer/markers/tests/test_parcel_aggregation.py +93 -32
- junifer/markers/tests/test_sphere_aggregation.py +72 -19
- junifer/onthefly/__init__.py +4 -1
- junifer/onthefly/read_transform.py +3 -0
- junifer/pipeline/__init__.py +9 -1
- junifer/pipeline/pipeline_step_mixin.py +21 -4
- junifer/pipeline/registry.py +3 -0
- junifer/pipeline/singleton.py +3 -0
- junifer/pipeline/tests/test_registry.py +1 -1
- junifer/pipeline/update_meta_mixin.py +3 -0
- junifer/pipeline/utils.py +67 -1
- junifer/pipeline/workdir_manager.py +3 -0
- junifer/preprocess/__init__.py +10 -2
- junifer/preprocess/base.py +6 -3
- junifer/preprocess/confounds/__init__.py +3 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +47 -60
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +72 -113
- junifer/preprocess/smoothing/__init__.py +9 -0
- junifer/preprocess/smoothing/_afni_smoothing.py +119 -0
- junifer/preprocess/smoothing/_fsl_smoothing.py +116 -0
- junifer/preprocess/smoothing/_nilearn_smoothing.py +69 -0
- junifer/preprocess/smoothing/smoothing.py +174 -0
- junifer/preprocess/smoothing/tests/test_smoothing.py +94 -0
- junifer/preprocess/warping/__init__.py +3 -0
- junifer/preprocess/warping/_ants_warper.py +3 -0
- junifer/preprocess/warping/_fsl_warper.py +3 -0
- junifer/stats.py +4 -1
- junifer/storage/__init__.py +9 -1
- junifer/storage/base.py +40 -1
- junifer/storage/hdf5.py +71 -9
- junifer/storage/pandas_base.py +3 -0
- junifer/storage/sqlite.py +3 -0
- junifer/storage/tests/test_hdf5.py +82 -10
- junifer/storage/utils.py +9 -0
- junifer/testing/__init__.py +4 -1
- junifer/testing/datagrabbers.py +13 -6
- junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
- junifer/testing/utils.py +3 -0
- junifer/utils/__init__.py +13 -2
- junifer/utils/fs.py +3 -0
- junifer/utils/helpers.py +32 -1
- junifer/utils/logging.py +33 -4
- junifer/utils/tests/test_logging.py +8 -0
- {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/METADATA +17 -16
- junifer-0.0.5.dist-info/RECORD +275 -0
- {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/WHEEL +1 -1
- junifer/datagrabber/tests/test_datagrabber_utils.py +0 -218
- junifer/datagrabber/utils.py +0 -230
- junifer/preprocess/ants/__init__.py +0 -4
- junifer/preprocess/ants/ants_apply_transforms_warper.py +0 -185
- junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +0 -56
- junifer/preprocess/bold_warper.py +0 -265
- junifer/preprocess/fsl/__init__.py +0 -4
- junifer/preprocess/fsl/apply_warper.py +0 -179
- junifer/preprocess/fsl/tests/test_apply_warper.py +0 -45
- junifer/preprocess/tests/test_bold_warper.py +0 -159
- junifer-0.0.4.dev831.dist-info/RECORD +0 -257
- {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.4.dev831.dist-info → junifer-0.0.5.dist-info}/top_level.txt +0 -0
@@ -25,14 +25,65 @@ COORDS = "DMNBuckner"
|
|
25
25
|
RADIUS = 8
|
26
26
|
|
27
27
|
|
28
|
-
|
29
|
-
"
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
@pytest.mark.parametrize(
|
29
|
+
"input_type, storage_type",
|
30
|
+
[
|
31
|
+
(
|
32
|
+
"T1w",
|
33
|
+
"vector",
|
34
|
+
),
|
35
|
+
(
|
36
|
+
"T2w",
|
37
|
+
"vector",
|
38
|
+
),
|
39
|
+
(
|
40
|
+
"BOLD",
|
41
|
+
"timeseries",
|
42
|
+
),
|
43
|
+
(
|
44
|
+
"VBM_GM",
|
45
|
+
"vector",
|
46
|
+
),
|
47
|
+
(
|
48
|
+
"VBM_WM",
|
49
|
+
"vector",
|
50
|
+
),
|
51
|
+
(
|
52
|
+
"VBM_CSF",
|
53
|
+
"vector",
|
54
|
+
),
|
55
|
+
(
|
56
|
+
"fALFF",
|
57
|
+
"vector",
|
58
|
+
),
|
59
|
+
(
|
60
|
+
"GCOR",
|
61
|
+
"vector",
|
62
|
+
),
|
63
|
+
(
|
64
|
+
"LCOR",
|
65
|
+
"vector",
|
66
|
+
),
|
67
|
+
],
|
68
|
+
)
|
69
|
+
def test_SphereAggregation_input_output(
|
70
|
+
input_type: str, storage_type: str
|
71
|
+
) -> None:
|
72
|
+
"""Test SphereAggregation input and output types.
|
33
73
|
|
34
|
-
|
35
|
-
|
74
|
+
Parameters
|
75
|
+
----------
|
76
|
+
input_type : str
|
77
|
+
The parametrized input type.
|
78
|
+
storage_type : str
|
79
|
+
The parametrized storage type.
|
80
|
+
|
81
|
+
"""
|
82
|
+
assert storage_type == SphereAggregation(
|
83
|
+
coords="DMNBuckner",
|
84
|
+
method="mean",
|
85
|
+
on=input_type,
|
86
|
+
).get_output_type(input_type=input_type, output_feature="aggregation")
|
36
87
|
|
37
88
|
|
38
89
|
def test_SphereAggregation_3D() -> None:
|
@@ -44,8 +95,8 @@ def test_SphereAggregation_3D() -> None:
|
|
44
95
|
coords=COORDS, method="mean", radius=RADIUS, on="VBM_GM"
|
45
96
|
)
|
46
97
|
sphere_agg_vbm_gm_data = marker.fit_transform(element_data)["VBM_GM"][
|
47
|
-
"
|
48
|
-
]
|
98
|
+
"aggregation"
|
99
|
+
]["data"]
|
49
100
|
|
50
101
|
# Compare with nilearn
|
51
102
|
# Load testing coordinates
|
@@ -76,8 +127,8 @@ def test_SphereAggregation_4D() -> None:
|
|
76
127
|
coords=COORDS, method="mean", radius=RADIUS, on="BOLD"
|
77
128
|
)
|
78
129
|
sphere_agg_bold_data = marker.fit_transform(element_data)["BOLD"][
|
79
|
-
"
|
80
|
-
]
|
130
|
+
"aggregation"
|
131
|
+
]["data"]
|
81
132
|
|
82
133
|
# Compare with nilearn
|
83
134
|
# Load testing coordinates
|
@@ -120,7 +171,8 @@ def test_SphereAggregation_storage(tmp_path: Path) -> None:
|
|
120
171
|
marker.fit_transform(input=element_data, storage=storage)
|
121
172
|
features = storage.list_features()
|
122
173
|
assert any(
|
123
|
-
x["name"] == "
|
174
|
+
x["name"] == "VBM_GM_SphereAggregation_aggregation"
|
175
|
+
for x in features.values()
|
124
176
|
)
|
125
177
|
|
126
178
|
# Store 4D
|
@@ -135,7 +187,8 @@ def test_SphereAggregation_storage(tmp_path: Path) -> None:
|
|
135
187
|
marker.fit_transform(input=element_data, storage=storage)
|
136
188
|
features = storage.list_features()
|
137
189
|
assert any(
|
138
|
-
x["name"] == "
|
190
|
+
x["name"] == "BOLD_SphereAggregation_aggregation"
|
191
|
+
for x in features.values()
|
139
192
|
)
|
140
193
|
|
141
194
|
|
@@ -152,8 +205,8 @@ def test_SphereAggregation_3D_mask() -> None:
|
|
152
205
|
masks="compute_brain_mask",
|
153
206
|
)
|
154
207
|
sphere_agg_vbm_gm_data = marker.fit_transform(element_data)["VBM_GM"][
|
155
|
-
"
|
156
|
-
]
|
208
|
+
"aggregation"
|
209
|
+
]["data"]
|
157
210
|
|
158
211
|
# Compare with nilearn
|
159
212
|
# Load testing coordinates
|
@@ -195,8 +248,8 @@ def test_SphereAggregation_4D_agg_time() -> None:
|
|
195
248
|
on="BOLD",
|
196
249
|
)
|
197
250
|
sphere_agg_bold_data = marker.fit_transform(element_data)["BOLD"][
|
198
|
-
"
|
199
|
-
]
|
251
|
+
"aggregation"
|
252
|
+
]["data"]
|
200
253
|
|
201
254
|
# Compare with nilearn
|
202
255
|
# Load testing coordinates
|
@@ -231,8 +284,8 @@ def test_SphereAggregation_4D_agg_time() -> None:
|
|
231
284
|
on="BOLD",
|
232
285
|
)
|
233
286
|
sphere_agg_bold_data = marker.fit_transform(element_data)["BOLD"][
|
234
|
-
"
|
235
|
-
]
|
287
|
+
"aggregation"
|
288
|
+
]["data"]
|
236
289
|
|
237
290
|
assert sphere_agg_bold_data.ndim == 2
|
238
291
|
assert_array_equal(
|
junifer/onthefly/__init__.py
CHANGED
junifer/pipeline/__init__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Pipeline components."""
|
2
2
|
|
3
3
|
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
4
|
# License: AGPL
|
@@ -7,3 +7,11 @@ from . import registry
|
|
7
7
|
from .pipeline_step_mixin import PipelineStepMixin
|
8
8
|
from .update_meta_mixin import UpdateMetaMixin
|
9
9
|
from .workdir_manager import WorkDirManager
|
10
|
+
|
11
|
+
|
12
|
+
__all__ = [
|
13
|
+
"registry",
|
14
|
+
"PipelineStepMixin",
|
15
|
+
"UpdateMetaMixin",
|
16
|
+
"WorkDirManager",
|
17
|
+
]
|
@@ -4,10 +4,14 @@
|
|
4
4
|
# Synchon Mandal <s.mandal@fz-juelich.de>
|
5
5
|
# License: AGPL
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
import sys
|
8
|
+
|
9
|
+
|
10
|
+
if sys.version_info < (3, 11): # pragma: no cover
|
10
11
|
from importlib_metadata import packages_distributions
|
12
|
+
else:
|
13
|
+
from importlib.metadata import packages_distributions
|
14
|
+
|
11
15
|
|
12
16
|
from importlib.util import find_spec
|
13
17
|
from itertools import chain
|
@@ -17,6 +21,9 @@ from ..utils import raise_error
|
|
17
21
|
from .utils import check_ext_dependencies
|
18
22
|
|
19
23
|
|
24
|
+
__all__ = ["PipelineStepMixin"]
|
25
|
+
|
26
|
+
|
20
27
|
class PipelineStepMixin:
|
21
28
|
"""Mixin class for a pipeline step."""
|
22
29
|
|
@@ -203,7 +210,17 @@ class PipelineStepMixin:
|
|
203
210
|
# Validate input
|
204
211
|
fit_input = self.validate_input(input=input)
|
205
212
|
# Validate output type
|
206
|
-
|
213
|
+
# Nested output type for marker
|
214
|
+
if hasattr(self, "_MARKER_INOUT_MAPPINGS"):
|
215
|
+
outputs = list(
|
216
|
+
{
|
217
|
+
val
|
218
|
+
for t_input in fit_input
|
219
|
+
for val in self._MARKER_INOUT_MAPPINGS[t_input].values()
|
220
|
+
}
|
221
|
+
)
|
222
|
+
else:
|
223
|
+
outputs = [self.get_output_type(t_input) for t_input in fit_input]
|
207
224
|
return outputs
|
208
225
|
|
209
226
|
def fit_transform(
|
junifer/pipeline/registry.py
CHANGED
junifer/pipeline/singleton.py
CHANGED
junifer/pipeline/utils.py
CHANGED
@@ -10,6 +10,9 @@ from typing import Any, List, Optional
|
|
10
10
|
from junifer.utils.logging import raise_error, warn_with_log
|
11
11
|
|
12
12
|
|
13
|
+
__all__ = ["check_ext_dependencies"]
|
14
|
+
|
15
|
+
|
13
16
|
def check_ext_dependencies(
|
14
17
|
name: str, optional: bool = False, **kwargs: Any
|
15
18
|
) -> bool:
|
@@ -37,7 +40,7 @@ def check_ext_dependencies(
|
|
37
40
|
If ``name`` is mandatory and is not found.
|
38
41
|
|
39
42
|
"""
|
40
|
-
valid_ext_dependencies = ("afni", "fsl", "ants")
|
43
|
+
valid_ext_dependencies = ("afni", "fsl", "ants", "freesurfer")
|
41
44
|
if name not in valid_ext_dependencies:
|
42
45
|
raise_error(
|
43
46
|
"Invalid value for `name`, should be one of: "
|
@@ -52,6 +55,9 @@ def check_ext_dependencies(
|
|
52
55
|
# Check for ants
|
53
56
|
elif name == "ants":
|
54
57
|
found = _check_ants(**kwargs)
|
58
|
+
# Check for freesurfer
|
59
|
+
elif name == "freesurfer":
|
60
|
+
found = _check_freesurfer(**kwargs)
|
55
61
|
|
56
62
|
# Check if the dependency is mandatory in case it's not found
|
57
63
|
if not found and not optional:
|
@@ -245,3 +251,63 @@ def _check_ants(commands: Optional[List[str]] = None) -> bool:
|
|
245
251
|
f"{commands_found_results}"
|
246
252
|
)
|
247
253
|
return ants_found
|
254
|
+
|
255
|
+
|
256
|
+
def _check_freesurfer(commands: Optional[List[str]] = None) -> bool:
|
257
|
+
"""Check if FreeSurfer is present in the system.
|
258
|
+
|
259
|
+
Parameters
|
260
|
+
----------
|
261
|
+
commands : list of str, optional
|
262
|
+
The commands to specifically check for from FreeSurfer. If None, only
|
263
|
+
the basic FreeSurfer help would be looked up, else, would also
|
264
|
+
check for specific commands (default None).
|
265
|
+
|
266
|
+
Returns
|
267
|
+
-------
|
268
|
+
bool
|
269
|
+
Whether FreeSurfer is found or not.
|
270
|
+
|
271
|
+
"""
|
272
|
+
completed_process = subprocess.run(
|
273
|
+
"recon-all -help",
|
274
|
+
stdin=subprocess.DEVNULL,
|
275
|
+
stdout=subprocess.DEVNULL,
|
276
|
+
stderr=subprocess.STDOUT,
|
277
|
+
shell=True, # is unsafe but kept for resolution via PATH
|
278
|
+
check=False,
|
279
|
+
)
|
280
|
+
fs_found = completed_process.returncode == 0
|
281
|
+
|
282
|
+
# Check for specific commands
|
283
|
+
if fs_found and commands is not None:
|
284
|
+
if not isinstance(commands, list):
|
285
|
+
commands = [commands]
|
286
|
+
# Store command found results
|
287
|
+
commands_found_results = {}
|
288
|
+
# Set all commands found flag to True
|
289
|
+
all_commands_found = True
|
290
|
+
# Check commands' existence
|
291
|
+
for command in commands:
|
292
|
+
command_process = subprocess.run(
|
293
|
+
[command],
|
294
|
+
stdin=subprocess.DEVNULL,
|
295
|
+
stdout=subprocess.DEVNULL,
|
296
|
+
stderr=subprocess.STDOUT,
|
297
|
+
shell=True, # is unsafe but kept for resolution via PATH
|
298
|
+
check=False,
|
299
|
+
)
|
300
|
+
command_found = command_process.returncode == 0
|
301
|
+
commands_found_results[command] = (
|
302
|
+
"found" if command_found else "not found"
|
303
|
+
)
|
304
|
+
# Set flag to trigger warning
|
305
|
+
all_commands_found = all_commands_found and command_found
|
306
|
+
# One or more commands were missing
|
307
|
+
if not all_commands_found:
|
308
|
+
warn_with_log(
|
309
|
+
"FreeSurfer is installed but some of the required commands "
|
310
|
+
"were not found. These are the results: "
|
311
|
+
f"{commands_found_results}"
|
312
|
+
)
|
313
|
+
return fs_found
|
junifer/preprocess/__init__.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Preprocessors for preprocessing data before feature extraction."""
|
2
2
|
|
3
3
|
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
4
|
# Leonard Sasse <l.sasse@fz-juelich.de>
|
@@ -7,5 +7,13 @@
|
|
7
7
|
|
8
8
|
from .base import BasePreprocessor
|
9
9
|
from .confounds import fMRIPrepConfoundRemover
|
10
|
-
from .bold_warper import BOLDWarper
|
11
10
|
from .warping import SpaceWarper
|
11
|
+
from .smoothing import Smoothing
|
12
|
+
|
13
|
+
|
14
|
+
__all__ = [
|
15
|
+
"BasePreprocessor",
|
16
|
+
"fMRIPrepConfoundRemover",
|
17
|
+
"SpaceWarper",
|
18
|
+
"Smoothing",
|
19
|
+
]
|
junifer/preprocess/base.py
CHANGED
@@ -11,6 +11,9 @@ from ..pipeline import PipelineStepMixin, UpdateMetaMixin
|
|
11
11
|
from ..utils import logger, raise_error
|
12
12
|
|
13
13
|
|
14
|
+
__all__ = ["BasePreprocessor"]
|
15
|
+
|
16
|
+
|
14
17
|
class BasePreprocessor(ABC, PipelineStepMixin, UpdateMetaMixin):
|
15
18
|
"""Abstract base class for all preprocessors.
|
16
19
|
|
@@ -136,7 +139,7 @@ class BasePreprocessor(ABC, PipelineStepMixin, UpdateMetaMixin):
|
|
136
139
|
A single input from the Junifer Data object to preprocess.
|
137
140
|
extra_input : dict, optional
|
138
141
|
The other fields in the Junifer Data object. Useful for accessing
|
139
|
-
other data
|
142
|
+
other data type that needs to be used in the computation. For
|
140
143
|
example, the confound removers can make use of the
|
141
144
|
confounds if available (default None).
|
142
145
|
|
@@ -146,8 +149,8 @@ class BasePreprocessor(ABC, PipelineStepMixin, UpdateMetaMixin):
|
|
146
149
|
The computed result as dictionary.
|
147
150
|
dict or None
|
148
151
|
Extra "helper" data types as dictionary to add to the Junifer Data
|
149
|
-
object.
|
150
|
-
|
152
|
+
object. If no new "helper" data type(s) is(are) created, None is to
|
153
|
+
be passed.
|
151
154
|
|
152
155
|
"""
|
153
156
|
raise_error(
|
@@ -27,6 +27,9 @@ from ...utils import logger, raise_error
|
|
27
27
|
from ..base import BasePreprocessor
|
28
28
|
|
29
29
|
|
30
|
+
__all__ = ["fMRIPrepConfoundRemover"]
|
31
|
+
|
32
|
+
|
30
33
|
FMRIPREP_BASICS = {
|
31
34
|
"motion": [
|
32
35
|
"trans_x",
|
@@ -203,9 +206,7 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
203
206
|
"include it in the future",
|
204
207
|
klass=ValueError,
|
205
208
|
)
|
206
|
-
super().__init__(
|
207
|
-
on="BOLD", required_data_types=["BOLD", "BOLD_confounds"]
|
208
|
-
)
|
209
|
+
super().__init__(on="BOLD", required_data_types=["BOLD"])
|
209
210
|
|
210
211
|
def get_valid_inputs(self) -> List[str]:
|
211
212
|
"""Get valid data types for input.
|
@@ -361,7 +362,7 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
361
362
|
Parameters
|
362
363
|
----------
|
363
364
|
input : dict
|
364
|
-
Dictionary containing the ``
|
365
|
+
Dictionary containing the ``BOLD.confounds`` value from the
|
365
366
|
Junifer Data object.
|
366
367
|
|
367
368
|
Returns
|
@@ -370,7 +371,6 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
370
371
|
Dataframe containing the relevant confounds.
|
371
372
|
|
372
373
|
"""
|
373
|
-
|
374
374
|
confounds_format = input["format"]
|
375
375
|
if confounds_format == "adhoc":
|
376
376
|
self._map_adhoc_to_fmriprep(input)
|
@@ -416,50 +416,42 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
416
416
|
def _validate_data(
|
417
417
|
self,
|
418
418
|
input: Dict[str, Any],
|
419
|
-
extra_input: Optional[Dict[str, Any]] = None,
|
420
419
|
) -> None:
|
421
420
|
"""Validate input data.
|
422
421
|
|
423
422
|
Parameters
|
424
423
|
----------
|
425
424
|
input : dict
|
426
|
-
Dictionary containing the ``BOLD``
|
425
|
+
Dictionary containing the ``BOLD`` data from the
|
427
426
|
Junifer Data object.
|
428
|
-
extra_input : dict, optional
|
429
|
-
Dictionary containing the rest of the Junifer Data object. Must
|
430
|
-
include the ``BOLD_confounds`` key.
|
431
427
|
|
432
428
|
Raises
|
433
429
|
------
|
434
430
|
ValueError
|
435
|
-
If ``
|
436
|
-
if ``"
|
437
|
-
if ``"data"``
|
438
|
-
if ``"data"`` is not pandas.DataFrame or
|
431
|
+
If ``"confounds"`` is not found in ``input`` or
|
432
|
+
if ``"data"`` key is not found in ``"input.confounds"`` or
|
433
|
+
if ``"input.confounds.data"`` is not pandas.DataFrame or
|
439
434
|
if image time series and confounds have different lengths or
|
440
|
-
if ``
|
441
|
-
|
442
|
-
|
443
|
-
not found or
|
435
|
+
if ``format = "adhoc"`` and ``"mappings"`` key is not found or
|
436
|
+
``"fmriprep"`` key is not found in ``"mappings"`` or
|
437
|
+
``"fmriprep"`` has incorrect fMRIPrep mappings or required
|
438
|
+
fMRIPrep mappings are not found or
|
439
|
+
if invalid confounds format is found.
|
444
440
|
|
445
441
|
"""
|
446
442
|
# BOLD must be 4D niimg
|
447
443
|
check_niimg_4d(input["data"])
|
448
|
-
# Check for
|
449
|
-
if
|
450
|
-
raise_error(
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
if
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
if not isinstance(extra_input["BOLD_confounds"]["data"], pd.DataFrame):
|
460
|
-
raise_error("`BOLD_confounds.data` must be a `pandas.DataFrame`")
|
461
|
-
|
462
|
-
confound_df = extra_input["BOLD_confounds"]["data"]
|
444
|
+
# Check for confound data
|
445
|
+
if "confounds" not in input:
|
446
|
+
raise_error("`BOLD.confounds` data type not provided")
|
447
|
+
if "data" not in input["confounds"]:
|
448
|
+
raise_error("`BOLD.confounds.data` not provided")
|
449
|
+
# Confounds must be a pandas.DataFrame;
|
450
|
+
# if extension is unknown, will not be read, which will give None
|
451
|
+
if not isinstance(input["confounds"]["data"], pd.DataFrame):
|
452
|
+
raise_error("`BOLD.confounds.data` must be a `pandas.DataFrame`")
|
453
|
+
|
454
|
+
confound_df = input["confounds"]["data"]
|
463
455
|
bold_img = input["data"]
|
464
456
|
if bold_img.get_fdata().shape[3] != len(confound_df):
|
465
457
|
raise_error(
|
@@ -469,23 +461,19 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
469
461
|
)
|
470
462
|
|
471
463
|
# Check format
|
472
|
-
|
473
|
-
raise_error("`BOLD_confounds.format` not provided")
|
474
|
-
t_format = extra_input["BOLD_confounds"]["format"]
|
464
|
+
t_format = input["confounds"]["format"]
|
475
465
|
if t_format == "adhoc":
|
476
|
-
if "mappings" not in
|
466
|
+
if "mappings" not in input["confounds"]:
|
477
467
|
raise_error(
|
478
|
-
"`
|
479
|
-
"`
|
468
|
+
"`BOLD.confounds.mappings` need to be set when "
|
469
|
+
"`BOLD.confounds.format == 'adhoc'`"
|
480
470
|
)
|
481
|
-
if "fmriprep" not in
|
471
|
+
if "fmriprep" not in input["confounds"]["mappings"]:
|
482
472
|
raise_error(
|
483
|
-
"`
|
484
|
-
"`
|
473
|
+
"`BOLD.confounds.mappings.fmriprep` need to be set when "
|
474
|
+
"`BOLD.confounds.format == 'adhoc'`"
|
485
475
|
)
|
486
|
-
fmriprep_mappings =
|
487
|
-
"fmriprep"
|
488
|
-
]
|
476
|
+
fmriprep_mappings = input["confounds"]["mappings"]["fmriprep"]
|
489
477
|
wrong_names = [
|
490
478
|
x
|
491
479
|
for x in fmriprep_mappings.values()
|
@@ -525,22 +513,22 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
525
513
|
input : dict
|
526
514
|
A single input from the Junifer Data object to preprocess.
|
527
515
|
extra_input : dict, optional
|
528
|
-
The other fields in the Junifer Data object.
|
529
|
-
``BOLD_confounds`` key.
|
516
|
+
The other fields in the Junifer Data object.
|
530
517
|
|
531
518
|
Returns
|
532
519
|
-------
|
533
520
|
dict
|
534
|
-
The computed result as dictionary.
|
535
|
-
|
536
|
-
|
537
|
-
|
521
|
+
The computed result as dictionary. If `self.masks` is not None,
|
522
|
+
then the target data computed mask is updated for further steps.
|
523
|
+
None
|
524
|
+
Extra "helper" data types as dictionary to add to the Junifer Data
|
525
|
+
object.
|
538
526
|
|
539
527
|
"""
|
540
528
|
# Validate data
|
541
|
-
self._validate_data(input
|
529
|
+
self._validate_data(input)
|
542
530
|
# Pick confounds
|
543
|
-
confounds_df = self._pick_confounds(
|
531
|
+
confounds_df = self._pick_confounds(input["confounds"]) # type: ignore
|
544
532
|
# Get BOLD data
|
545
533
|
bold_img = input["data"]
|
546
534
|
# Set t_r
|
@@ -553,7 +541,6 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
553
541
|
)
|
554
542
|
# Set mask data
|
555
543
|
mask_img = None
|
556
|
-
bold_mask_dict = None
|
557
544
|
if self.masks is not None:
|
558
545
|
logger.debug(f"Masking with {self.masks}")
|
559
546
|
mask_img = get_mask(
|
@@ -561,15 +548,15 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
561
548
|
)
|
562
549
|
# Return the BOLD mask and link it to the BOLD data type dict;
|
563
550
|
# this allows to use "inherit" down the pipeline
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
"BOLD_mask": {
|
551
|
+
logger.debug("Setting `BOLD.mask`")
|
552
|
+
input.update(
|
553
|
+
{
|
554
|
+
"mask": {
|
569
555
|
"data": mask_img,
|
570
556
|
"space": input["space"],
|
571
557
|
}
|
572
558
|
}
|
559
|
+
)
|
573
560
|
# Clean image
|
574
561
|
logger.info("Cleaning image using nilearn")
|
575
562
|
logger.debug(f"\tdetrend: {self.detrend}")
|
@@ -587,4 +574,4 @@ class fMRIPrepConfoundRemover(BasePreprocessor):
|
|
587
574
|
mask_img=mask_img,
|
588
575
|
)
|
589
576
|
|
590
|
-
return input,
|
577
|
+
return input, None
|