junifer 0.0.4.dev829__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.dev829.dist-info → junifer-0.0.5.dist-info}/METADATA +17 -16
- junifer-0.0.5.dist-info/RECORD +275 -0
- {junifer-0.0.4.dev829.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.dev829.dist-info/RECORD +0 -257
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.4.dev829.dist-info → junifer-0.0.5.dist-info}/top_level.txt +0 -0
@@ -20,6 +20,7 @@ from junifer.testing import get_testing_data
|
|
20
20
|
from junifer.testing.datagrabbers import (
|
21
21
|
OasisVBMTestingDataGrabber,
|
22
22
|
PartlyCloudyTestingDataGrabber,
|
23
|
+
SPMAuditoryTestingDataGrabber,
|
23
24
|
)
|
24
25
|
|
25
26
|
|
@@ -42,35 +43,10 @@ def test_fMRIPrepConfoundRemover_init() -> None:
|
|
42
43
|
@pytest.mark.parametrize(
|
43
44
|
"input_",
|
44
45
|
[
|
45
|
-
["T1w"],
|
46
46
|
["BOLD"],
|
47
47
|
["T1w", "BOLD"],
|
48
48
|
],
|
49
49
|
)
|
50
|
-
def test_fMRIPrepConfoundRemover_validate_input_errors(
|
51
|
-
input_: List[str],
|
52
|
-
) -> None:
|
53
|
-
"""Test errors for fMRIPrepConfoundRemover validate_input.
|
54
|
-
|
55
|
-
Parameters
|
56
|
-
----------
|
57
|
-
input_ : list of str
|
58
|
-
The input data types.
|
59
|
-
|
60
|
-
"""
|
61
|
-
confound_remover = fMRIPrepConfoundRemover()
|
62
|
-
|
63
|
-
with pytest.raises(ValueError, match="not have the required data"):
|
64
|
-
confound_remover.validate_input(input_)
|
65
|
-
|
66
|
-
|
67
|
-
@pytest.mark.parametrize(
|
68
|
-
"input_",
|
69
|
-
[
|
70
|
-
["BOLD", "BOLD_confounds"],
|
71
|
-
["T1w", "BOLD", "BOLD_confounds"],
|
72
|
-
],
|
73
|
-
)
|
74
50
|
def test_fMRIPrepConfoundRemover_validate_input(input_: List[str]) -> None:
|
75
51
|
"""Test fMRIPrepConfoundRemover validate_input.
|
76
52
|
|
@@ -302,13 +278,13 @@ def test_fMRIPRepConfoundRemover__pick_confounds_fmriprep() -> None:
|
|
302
278
|
with PartlyCloudyTestingDataGrabber() as dg:
|
303
279
|
input = dg["sub-01"]
|
304
280
|
input = reader.fit_transform(input)
|
305
|
-
out1 = confound_remover._pick_confounds(input["
|
281
|
+
out1 = confound_remover._pick_confounds(input["BOLD"]["confounds"])
|
306
282
|
assert set(out1.columns) == {*fmriprep_all_vars, "spike"}
|
307
283
|
|
308
284
|
with PartlyCloudyTestingDataGrabber(reduce_confounds=False) as dg:
|
309
285
|
input = dg["sub-01"]
|
310
286
|
input = reader.fit_transform(input)
|
311
|
-
out2 = confound_remover._pick_confounds(input["
|
287
|
+
out2 = confound_remover._pick_confounds(input["BOLD"]["confounds"])
|
312
288
|
assert set(out2.columns) == {*fmriprep_all_vars, "spike"}
|
313
289
|
|
314
290
|
assert_frame_equal(out1, out2)
|
@@ -348,123 +324,106 @@ def test_fMRIPRepConfoundRemover__pick_confounds_fmriprep_compute() -> None:
|
|
348
324
|
def test_fMRIPrepConfoundRemover__validate_data() -> None:
|
349
325
|
"""Test fMRIPrepConfoundRemover validate data."""
|
350
326
|
confound_remover = fMRIPrepConfoundRemover(strategy={"wm_csf": "full"})
|
351
|
-
|
327
|
+
# Check correct data type
|
352
328
|
with OasisVBMTestingDataGrabber() as dg:
|
353
329
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
354
330
|
vbm = element_data["VBM_GM"]
|
355
331
|
with pytest.raises(
|
356
332
|
DimensionError, match="incompatible dimensionality"
|
357
333
|
):
|
358
|
-
confound_remover._validate_data(vbm
|
359
|
-
|
360
|
-
with
|
334
|
+
confound_remover._validate_data(vbm)
|
335
|
+
# Check missing nested type in correct data type
|
336
|
+
with SPMAuditoryTestingDataGrabber() as dg:
|
361
337
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
362
338
|
bold = element_data["BOLD"]
|
363
|
-
|
364
|
-
with pytest.raises(ValueError, match="No extra input"):
|
365
|
-
confound_remover._validate_data(bold, None)
|
339
|
+
# Test confound type
|
366
340
|
with pytest.raises(
|
367
|
-
ValueError, match="`
|
341
|
+
ValueError, match="`BOLD.confounds` data type not provided"
|
368
342
|
):
|
369
|
-
confound_remover._validate_data(bold
|
343
|
+
confound_remover._validate_data(bold)
|
344
|
+
# Test confound data
|
345
|
+
bold["confounds"] = {}
|
370
346
|
with pytest.raises(
|
371
|
-
ValueError, match="`
|
347
|
+
ValueError, match="`BOLD.confounds.data` not provided"
|
372
348
|
):
|
373
|
-
confound_remover._validate_data(bold
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
confound_remover._validate_data(bold, extra_input)
|
381
|
-
|
382
|
-
extra_input = {"BOLD_confounds": {"data": pd.DataFrame()}}
|
349
|
+
confound_remover._validate_data(bold)
|
350
|
+
# Test confound data is valid type
|
351
|
+
bold["confounds"] = {"data": None}
|
352
|
+
with pytest.raises(ValueError, match="must be a `pandas.DataFrame`"):
|
353
|
+
confound_remover._validate_data(bold)
|
354
|
+
# Test confound data dimension mismatch with BOLD
|
355
|
+
bold["confounds"] = {"data": pd.DataFrame()}
|
383
356
|
with pytest.raises(ValueError, match="Image time series and"):
|
384
|
-
confound_remover._validate_data(bold
|
385
|
-
|
386
|
-
|
387
|
-
|
357
|
+
confound_remover._validate_data(bold)
|
358
|
+
# Check nested type variations
|
359
|
+
with PartlyCloudyTestingDataGrabber(reduce_confounds=False) as dg:
|
360
|
+
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
361
|
+
# Test format
|
362
|
+
modified_bold = {
|
363
|
+
"data": element_data["BOLD"]["data"],
|
364
|
+
"confounds": {
|
365
|
+
"data": element_data["BOLD"]["confounds"]["data"],
|
366
|
+
"format": "adhoc",
|
367
|
+
},
|
388
368
|
}
|
369
|
+
# Test incorrect format
|
370
|
+
modified_bold["confounds"].update({"format": "wrong"})
|
371
|
+
with pytest.raises(ValueError, match="Invalid confounds format"):
|
372
|
+
confound_remover._validate_data(modified_bold)
|
373
|
+
# Test missing mappings for adhoc
|
374
|
+
modified_bold["confounds"].update({"format": "adhoc"})
|
389
375
|
with pytest.raises(
|
390
|
-
ValueError, match="`
|
376
|
+
ValueError, match="`BOLD.confounds.mappings` need to be set"
|
391
377
|
):
|
392
|
-
confound_remover._validate_data(
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
extra_input = {
|
404
|
-
"BOLD_confounds": {
|
405
|
-
"data": element_data["BOLD_confounds"]["data"],
|
406
|
-
"format": "adhoc",
|
407
|
-
}
|
408
|
-
}
|
409
|
-
with pytest.raises(ValueError, match="need to be set"):
|
410
|
-
confound_remover._validate_data(bold, extra_input)
|
411
|
-
|
412
|
-
extra_input = {
|
413
|
-
"BOLD_confounds": {
|
414
|
-
"data": element_data["BOLD_confounds"]["data"],
|
415
|
-
"format": "adhoc",
|
416
|
-
"mappings": {},
|
417
|
-
}
|
418
|
-
}
|
419
|
-
with pytest.raises(ValueError, match="need to be set"):
|
420
|
-
confound_remover._validate_data(bold, extra_input)
|
421
|
-
|
422
|
-
extra_input = {
|
423
|
-
"BOLD_confounds": {
|
424
|
-
"data": element_data["BOLD_confounds"]["data"],
|
425
|
-
"format": "adhoc",
|
378
|
+
confound_remover._validate_data(modified_bold)
|
379
|
+
# Test missing fmriprep mappings for adhoc
|
380
|
+
modified_bold["confounds"].update({"mappings": {}})
|
381
|
+
with pytest.raises(
|
382
|
+
ValueError,
|
383
|
+
match="`BOLD.confounds.mappings.fmriprep` need to be set",
|
384
|
+
):
|
385
|
+
confound_remover._validate_data(modified_bold)
|
386
|
+
# Test incorrect fmriprep mappings for adhoc
|
387
|
+
modified_bold["confounds"].update(
|
388
|
+
{
|
426
389
|
"mappings": {
|
427
390
|
"fmriprep": {
|
428
391
|
"rot_x": "wrong",
|
429
392
|
"rot_y": "rot_z",
|
430
393
|
"rot_z": "rot_y",
|
431
|
-
}
|
432
|
-
}
|
394
|
+
},
|
395
|
+
}
|
433
396
|
}
|
434
|
-
|
397
|
+
)
|
435
398
|
with pytest.raises(ValueError, match=r"names: \['wrong'\]"):
|
436
|
-
confound_remover._validate_data(
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
"data": element_data["BOLD_confounds"]["data"],
|
441
|
-
"format": "adhoc",
|
399
|
+
confound_remover._validate_data(modified_bold)
|
400
|
+
# Test missing fmriprep mappings for adhoc
|
401
|
+
modified_bold["confounds"].update(
|
402
|
+
{
|
442
403
|
"mappings": {
|
443
404
|
"fmriprep": {
|
444
405
|
"wrong": "rot_x",
|
445
406
|
"rot_y": "rot_z",
|
446
407
|
"rot_z": "rot_y",
|
447
|
-
}
|
448
|
-
}
|
408
|
+
},
|
409
|
+
}
|
449
410
|
}
|
450
|
-
|
411
|
+
)
|
451
412
|
with pytest.raises(ValueError, match=r"Missing columns: \['wrong'\]"):
|
452
|
-
confound_remover._validate_data(
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
"data": element_data["BOLD_confounds"]["data"],
|
457
|
-
"format": "adhoc",
|
413
|
+
confound_remover._validate_data(modified_bold)
|
414
|
+
# Test correct adhoc format
|
415
|
+
modified_bold["confounds"].update(
|
416
|
+
{
|
458
417
|
"mappings": {
|
459
418
|
"fmriprep": {
|
460
419
|
"rot_x": "rot_x",
|
461
420
|
"rot_y": "rot_z",
|
462
421
|
"rot_z": "rot_y",
|
463
|
-
}
|
464
|
-
}
|
422
|
+
},
|
423
|
+
}
|
465
424
|
}
|
466
|
-
|
467
|
-
confound_remover._validate_data(
|
425
|
+
)
|
426
|
+
confound_remover._validate_data(modified_bold)
|
468
427
|
|
469
428
|
|
470
429
|
def test_fMRIPrepConfoundRemover_preprocess() -> None:
|
@@ -476,7 +435,9 @@ def test_fMRIPrepConfoundRemover_preprocess() -> None:
|
|
476
435
|
element_data = DefaultDataReader().fit_transform(dg["sub-01"])
|
477
436
|
orig_bold = element_data["BOLD"]["data"].get_fdata().copy()
|
478
437
|
pre_input = element_data["BOLD"]
|
479
|
-
pre_extra_input = {
|
438
|
+
pre_extra_input = {
|
439
|
+
"BOLD": {"confounds": element_data["BOLD"]["confounds"]}
|
440
|
+
}
|
480
441
|
output, _ = confound_remover.preprocess(pre_input, pre_extra_input)
|
481
442
|
trans_bold = output["data"].get_fdata()
|
482
443
|
# Transformation is in place
|
@@ -530,7 +491,7 @@ def test_fMRIPrepConfoundRemover_fit_transform() -> None:
|
|
530
491
|
assert t_meta["t_r"] is None
|
531
492
|
assert t_meta["masks"] is None
|
532
493
|
|
533
|
-
assert "
|
494
|
+
assert "mask" not in output["BOLD"]
|
534
495
|
|
535
496
|
assert "dependencies" in output["BOLD"]["meta"]
|
536
497
|
dependencies = output["BOLD"]["meta"]["dependencies"]
|
@@ -582,9 +543,7 @@ def test_fMRIPrepConfoundRemover_fit_transform_masks() -> None:
|
|
582
543
|
assert "threshold" in t_meta["masks"]["compute_brain_mask"]
|
583
544
|
assert t_meta["masks"]["compute_brain_mask"]["threshold"] == 0.2
|
584
545
|
|
585
|
-
assert "
|
586
|
-
assert "mask_item" in output["BOLD"]
|
587
|
-
assert output["BOLD"]["mask_item"] == "BOLD_mask"
|
546
|
+
assert "mask" in output["BOLD"]
|
588
547
|
|
589
548
|
assert "dependencies" in output["BOLD"]["meta"]
|
590
549
|
dependencies = output["BOLD"]["meta"]["dependencies"]
|
@@ -0,0 +1,119 @@
|
|
1
|
+
"""Provide class for smoothing via AFNI."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from typing import (
|
7
|
+
TYPE_CHECKING,
|
8
|
+
ClassVar,
|
9
|
+
Dict,
|
10
|
+
List,
|
11
|
+
Set,
|
12
|
+
Union,
|
13
|
+
)
|
14
|
+
|
15
|
+
import nibabel as nib
|
16
|
+
|
17
|
+
from ...pipeline import WorkDirManager
|
18
|
+
from ...utils import logger, run_ext_cmd
|
19
|
+
|
20
|
+
|
21
|
+
if TYPE_CHECKING:
|
22
|
+
from nibabel import Nifti1Image
|
23
|
+
|
24
|
+
|
25
|
+
__all__ = ["AFNISmoothing"]
|
26
|
+
|
27
|
+
|
28
|
+
class AFNISmoothing:
|
29
|
+
"""Class for smoothing via AFNI.
|
30
|
+
|
31
|
+
This class uses AFNI's 3dBlurToFWHM.
|
32
|
+
|
33
|
+
"""
|
34
|
+
|
35
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
|
36
|
+
{
|
37
|
+
"name": "afni",
|
38
|
+
"commands": ["3dBlurToFWHM"],
|
39
|
+
},
|
40
|
+
]
|
41
|
+
|
42
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"nibabel"}
|
43
|
+
|
44
|
+
def preprocess(
|
45
|
+
self,
|
46
|
+
data: "Nifti1Image",
|
47
|
+
fwhm: Union[int, float],
|
48
|
+
) -> "Nifti1Image":
|
49
|
+
"""Preprocess using AFNI.
|
50
|
+
|
51
|
+
Parameters
|
52
|
+
----------
|
53
|
+
data : Niimg-like object
|
54
|
+
Image(s) to preprocess.
|
55
|
+
fwhm : int or float
|
56
|
+
Smooth until the value. AFNI estimates the smoothing and then
|
57
|
+
applies smoothing to reach ``fwhm``.
|
58
|
+
|
59
|
+
Returns
|
60
|
+
-------
|
61
|
+
Niimg-like object
|
62
|
+
The preprocessed image(s).
|
63
|
+
|
64
|
+
Notes
|
65
|
+
-----
|
66
|
+
For more information on ``3dBlurToFWHM``, check:
|
67
|
+
https://afni.nimh.nih.gov/pub/dist/doc/program_help/3dBlurToFWHM.html
|
68
|
+
|
69
|
+
As the process also depends on the conversion of AFNI files to NIfTI
|
70
|
+
via AFNI's ``3dAFNItoNIFTI``, the help for that can be found at:
|
71
|
+
https://afni.nimh.nih.gov/pub/dist/doc/program_help/3dAFNItoNIFTI.html
|
72
|
+
|
73
|
+
"""
|
74
|
+
logger.info("Smoothing using AFNI")
|
75
|
+
|
76
|
+
# Create component-scoped tempdir
|
77
|
+
tempdir = WorkDirManager().get_tempdir(prefix="afni_smoothing")
|
78
|
+
|
79
|
+
# Save target data to a component-scoped tempfile
|
80
|
+
nifti_in_file_path = tempdir / "input.nii" # needs to be .nii
|
81
|
+
nib.save(data, nifti_in_file_path)
|
82
|
+
|
83
|
+
# Set 3dBlurToFWHM command
|
84
|
+
blur_out_path_prefix = tempdir / "blur"
|
85
|
+
blur_cmd = [
|
86
|
+
"3dBlurToFWHM",
|
87
|
+
f"-input {nifti_in_file_path.resolve()}",
|
88
|
+
f"-prefix {blur_out_path_prefix.resolve()}",
|
89
|
+
"-automask",
|
90
|
+
f"-FWHM {fwhm}",
|
91
|
+
]
|
92
|
+
# Call 3dBlurToFWHM
|
93
|
+
run_ext_cmd(name="3dBlurToFWHM", cmd=blur_cmd)
|
94
|
+
|
95
|
+
# Create element-scoped tempdir so that the blurred output is
|
96
|
+
# available later as nibabel stores file path reference for
|
97
|
+
# loading on computation
|
98
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
99
|
+
prefix="afni_blur"
|
100
|
+
)
|
101
|
+
# Convert afni to nifti
|
102
|
+
blur_afni_to_nifti_out_path = (
|
103
|
+
element_tempdir / "output.nii" # needs to be .nii
|
104
|
+
)
|
105
|
+
convert_cmd = [
|
106
|
+
"3dAFNItoNIFTI",
|
107
|
+
f"-prefix {blur_afni_to_nifti_out_path.resolve()}",
|
108
|
+
f"{blur_out_path_prefix}+orig.BRIK",
|
109
|
+
]
|
110
|
+
# Call 3dAFNItoNIFTI
|
111
|
+
run_ext_cmd(name="3dAFNItoNIFTI", cmd=convert_cmd)
|
112
|
+
|
113
|
+
# Load nifti
|
114
|
+
output_data = nib.load(blur_afni_to_nifti_out_path)
|
115
|
+
|
116
|
+
# Delete tempdir
|
117
|
+
WorkDirManager().delete_tempdir(tempdir)
|
118
|
+
|
119
|
+
return output_data # type: ignore
|
@@ -0,0 +1,116 @@
|
|
1
|
+
"""Provide class for smoothing via FSL."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from typing import (
|
7
|
+
TYPE_CHECKING,
|
8
|
+
ClassVar,
|
9
|
+
Dict,
|
10
|
+
List,
|
11
|
+
Set,
|
12
|
+
Union,
|
13
|
+
)
|
14
|
+
|
15
|
+
import nibabel as nib
|
16
|
+
|
17
|
+
from ...pipeline import WorkDirManager
|
18
|
+
from ...utils import logger, run_ext_cmd
|
19
|
+
|
20
|
+
|
21
|
+
if TYPE_CHECKING:
|
22
|
+
from nibabel import Nifti1Image
|
23
|
+
|
24
|
+
|
25
|
+
__all__ = ["FSLSmoothing"]
|
26
|
+
|
27
|
+
|
28
|
+
class FSLSmoothing:
|
29
|
+
"""Class for smoothing via FSL.
|
30
|
+
|
31
|
+
This class uses FSL's susan.
|
32
|
+
|
33
|
+
"""
|
34
|
+
|
35
|
+
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
|
36
|
+
{
|
37
|
+
"name": "fsl",
|
38
|
+
"commands": ["susan"],
|
39
|
+
},
|
40
|
+
]
|
41
|
+
|
42
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"nibabel"}
|
43
|
+
|
44
|
+
def preprocess(
|
45
|
+
self,
|
46
|
+
data: "Nifti1Image",
|
47
|
+
brightness_threshold: float,
|
48
|
+
fwhm: float,
|
49
|
+
) -> "Nifti1Image":
|
50
|
+
"""Preprocess using FSL.
|
51
|
+
|
52
|
+
Parameters
|
53
|
+
----------
|
54
|
+
data : Niimg-like object
|
55
|
+
Image(s) to preprocess.
|
56
|
+
brightness_threshold : float
|
57
|
+
Threshold to discriminate between noise and the underlying image.
|
58
|
+
The value should be set greater than the noise level and less than
|
59
|
+
the contrast of the underlying image.
|
60
|
+
fwhm : float
|
61
|
+
Spatial extent of smoothing.
|
62
|
+
|
63
|
+
Returns
|
64
|
+
-------
|
65
|
+
Niimg-like object
|
66
|
+
The preprocessed image(s).
|
67
|
+
|
68
|
+
Notes
|
69
|
+
-----
|
70
|
+
For more information on ``SUSAN``, check [1]_
|
71
|
+
|
72
|
+
References
|
73
|
+
----------
|
74
|
+
.. [1] Smith, S.M. and Brady, J.M. (1997).
|
75
|
+
SUSAN - a new approach to low level image processing.
|
76
|
+
International Journal of Computer Vision, Volume 23(1),
|
77
|
+
Pages 45-78.
|
78
|
+
|
79
|
+
"""
|
80
|
+
logger.info("Smoothing using FSL")
|
81
|
+
|
82
|
+
# Create component-scoped tempdir
|
83
|
+
tempdir = WorkDirManager().get_tempdir(prefix="fsl_smoothing")
|
84
|
+
|
85
|
+
# Save target data to a component-scoped tempfile
|
86
|
+
nifti_in_file_path = tempdir / "input.nii.gz"
|
87
|
+
nib.save(data, nifti_in_file_path)
|
88
|
+
|
89
|
+
# Create element-scoped tempdir so that the output is
|
90
|
+
# available later as nibabel stores file path reference for
|
91
|
+
# loading on computation
|
92
|
+
element_tempdir = WorkDirManager().get_element_tempdir(
|
93
|
+
prefix="fsl_susan"
|
94
|
+
)
|
95
|
+
susan_out_path = element_tempdir / "output.nii.gz"
|
96
|
+
# Set susan command
|
97
|
+
susan_cmd = [
|
98
|
+
"susan",
|
99
|
+
f"{nifti_in_file_path.resolve()}",
|
100
|
+
f"{brightness_threshold}",
|
101
|
+
f"{fwhm}",
|
102
|
+
"3", # dimension
|
103
|
+
"1", # use median when no neighbourhood is found
|
104
|
+
"0", # use input image to find USAN
|
105
|
+
f"{susan_out_path.resolve()}",
|
106
|
+
]
|
107
|
+
# Call susan
|
108
|
+
run_ext_cmd(name="susan", cmd=susan_cmd)
|
109
|
+
|
110
|
+
# Load nifti
|
111
|
+
output_data = nib.load(susan_out_path)
|
112
|
+
|
113
|
+
# Delete tempdir
|
114
|
+
WorkDirManager().delete_tempdir(tempdir)
|
115
|
+
|
116
|
+
return output_data # type: ignore
|
@@ -0,0 +1,69 @@
|
|
1
|
+
"""Provide class for smoothing via nilearn."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from typing import (
|
7
|
+
TYPE_CHECKING,
|
8
|
+
ClassVar,
|
9
|
+
Literal,
|
10
|
+
Set,
|
11
|
+
Union,
|
12
|
+
)
|
13
|
+
|
14
|
+
from nilearn import image as nimg
|
15
|
+
from numpy.typing import ArrayLike
|
16
|
+
|
17
|
+
from ...utils import logger
|
18
|
+
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from nibabel import Nifti1Image
|
22
|
+
|
23
|
+
|
24
|
+
__all__ = ["NilearnSmoothing"]
|
25
|
+
|
26
|
+
|
27
|
+
class NilearnSmoothing:
|
28
|
+
"""Class for smoothing via nilearn.
|
29
|
+
|
30
|
+
This class uses :func:`nilearn.image.smooth_img` to smooth image(s).
|
31
|
+
|
32
|
+
"""
|
33
|
+
|
34
|
+
_DEPENDENCIES: ClassVar[Set[str]] = {"nilearn"}
|
35
|
+
|
36
|
+
def preprocess(
|
37
|
+
self,
|
38
|
+
data: "Nifti1Image",
|
39
|
+
fwhm: Union[int, float, ArrayLike, Literal["fast"], None],
|
40
|
+
) -> "Nifti1Image":
|
41
|
+
"""Preprocess using nilearn.
|
42
|
+
|
43
|
+
Parameters
|
44
|
+
----------
|
45
|
+
data : Niimg-like object
|
46
|
+
Image(s) to preprocess.
|
47
|
+
fwhm : scalar, ``numpy.ndarray``, tuple or list of scalar, "fast" or \
|
48
|
+
None
|
49
|
+
Smoothing strength, as a full-width at half maximum, in
|
50
|
+
millimeters:
|
51
|
+
|
52
|
+
* If nonzero scalar, width is identical in all 3 directions.
|
53
|
+
* If ``numpy.ndarray``, tuple, or list, it must have 3 elements,
|
54
|
+
giving the FWHM along each axis. If any of the elements is 0 or
|
55
|
+
None, smoothing is not performed along that axis.
|
56
|
+
* If ``"fast"``, a fast smoothing will be performed with a filter
|
57
|
+
``[0.2, 1, 0.2]`` in each direction and a normalisation to
|
58
|
+
preserve the local average value.
|
59
|
+
* If None, no filtering is performed (useful when just removal of
|
60
|
+
non-finite values is needed).
|
61
|
+
|
62
|
+
Returns
|
63
|
+
-------
|
64
|
+
Niimg-like object
|
65
|
+
The preprocessed image(s).
|
66
|
+
|
67
|
+
"""
|
68
|
+
logger.info("Smoothing using nilearn")
|
69
|
+
return nimg.smooth_img(imgs=data, fwhm=fwhm) # type: ignore
|