junifer 0.0.5__py3-none-any.whl → 0.0.5.dev11__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 +0 -17
- junifer/_version.py +2 -2
- junifer/api/__init__.py +1 -4
- junifer/api/cli.py +1 -91
- junifer/api/decorators.py +0 -9
- junifer/api/functions.py +10 -56
- junifer/api/parser.py +0 -3
- junifer/api/queue_context/__init__.py +1 -4
- junifer/api/res/afni/run_afni_docker.sh +1 -1
- junifer/api/res/ants/run_ants_docker.sh +1 -1
- junifer/api/res/fsl/run_fsl_docker.sh +1 -1
- junifer/api/tests/test_api_utils.py +2 -4
- junifer/api/tests/test_cli.py +0 -83
- junifer/api/tests/test_functions.py +2 -27
- junifer/configs/__init__.py +1 -1
- junifer/configs/juseless/__init__.py +1 -4
- junifer/configs/juseless/datagrabbers/__init__.py +1 -10
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +0 -3
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +0 -3
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +0 -3
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +3 -1
- junifer/configs/juseless/datagrabbers/ucla.py +9 -12
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +0 -3
- junifer/data/__init__.py +1 -21
- junifer/data/coordinates.py +19 -10
- junifer/data/masks.py +87 -58
- junifer/data/parcellations.py +3 -14
- junifer/data/template_spaces.py +1 -4
- junifer/data/tests/test_masks.py +37 -26
- junifer/data/utils.py +0 -3
- junifer/datagrabber/__init__.py +1 -18
- junifer/datagrabber/aomic/__init__.py +0 -3
- junifer/datagrabber/aomic/id1000.py +37 -70
- junifer/datagrabber/aomic/piop1.py +36 -69
- junifer/datagrabber/aomic/piop2.py +38 -71
- junifer/datagrabber/aomic/tests/test_id1000.py +99 -44
- junifer/datagrabber/aomic/tests/test_piop1.py +108 -65
- junifer/datagrabber/aomic/tests/test_piop2.py +102 -45
- junifer/datagrabber/base.py +6 -13
- junifer/datagrabber/datalad_base.py +1 -13
- junifer/datagrabber/dmcc13_benchmark.py +53 -36
- junifer/datagrabber/hcp1200/__init__.py +0 -3
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +0 -3
- junifer/datagrabber/hcp1200/hcp1200.py +1 -4
- junifer/datagrabber/multiple.py +6 -45
- junifer/datagrabber/pattern.py +62 -170
- junifer/datagrabber/pattern_datalad.py +12 -25
- junifer/datagrabber/tests/test_datagrabber_utils.py +218 -0
- junifer/datagrabber/tests/test_datalad_base.py +4 -4
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +19 -46
- junifer/datagrabber/tests/test_multiple.py +84 -161
- junifer/datagrabber/tests/test_pattern.py +0 -45
- junifer/datagrabber/tests/test_pattern_datalad.py +4 -4
- junifer/datagrabber/utils.py +230 -0
- junifer/datareader/__init__.py +1 -4
- junifer/datareader/default.py +43 -95
- junifer/external/__init__.py +1 -1
- junifer/external/nilearn/__init__.py +1 -5
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +9 -23
- junifer/external/nilearn/tests/test_junifer_nifti_spheres_masker.py +1 -76
- junifer/markers/__init__.py +1 -23
- junifer/markers/base.py +28 -68
- junifer/markers/collection.py +2 -10
- junifer/markers/complexity/__init__.py +0 -10
- junifer/markers/complexity/complexity_base.py +43 -26
- junifer/markers/complexity/hurst_exponent.py +0 -3
- junifer/markers/complexity/multiscale_entropy_auc.py +0 -3
- junifer/markers/complexity/perm_entropy.py +0 -3
- junifer/markers/complexity/range_entropy.py +0 -3
- junifer/markers/complexity/range_entropy_auc.py +0 -3
- junifer/markers/complexity/sample_entropy.py +0 -3
- junifer/markers/complexity/tests/test_hurst_exponent.py +3 -11
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +3 -11
- junifer/markers/complexity/tests/test_perm_entropy.py +3 -11
- junifer/markers/complexity/tests/test_range_entropy.py +3 -11
- junifer/markers/complexity/tests/test_range_entropy_auc.py +3 -11
- junifer/markers/complexity/tests/test_sample_entropy.py +3 -11
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +3 -11
- junifer/markers/complexity/weighted_perm_entropy.py +0 -3
- junifer/markers/ets_rss.py +42 -27
- junifer/markers/falff/__init__.py +0 -3
- junifer/markers/falff/_afni_falff.py +2 -5
- junifer/markers/falff/_junifer_falff.py +0 -3
- junifer/markers/falff/falff_base.py +46 -20
- junifer/markers/falff/falff_parcels.py +27 -56
- junifer/markers/falff/falff_spheres.py +29 -60
- junifer/markers/falff/tests/test_falff_parcels.py +23 -39
- junifer/markers/falff/tests/test_falff_spheres.py +23 -39
- junifer/markers/functional_connectivity/__init__.py +0 -9
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +60 -63
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +32 -45
- junifer/markers/functional_connectivity/edge_functional_connectivity_spheres.py +36 -49
- junifer/markers/functional_connectivity/functional_connectivity_base.py +70 -71
- junifer/markers/functional_connectivity/functional_connectivity_parcels.py +25 -34
- junifer/markers/functional_connectivity/functional_connectivity_spheres.py +30 -40
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +7 -11
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +7 -27
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +12 -28
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +11 -35
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +62 -36
- junifer/markers/parcel_aggregation.py +61 -47
- junifer/markers/reho/__init__.py +0 -3
- junifer/markers/reho/_afni_reho.py +2 -5
- junifer/markers/reho/_junifer_reho.py +1 -4
- junifer/markers/reho/reho_base.py +27 -8
- junifer/markers/reho/reho_parcels.py +17 -28
- junifer/markers/reho/reho_spheres.py +18 -27
- junifer/markers/reho/tests/test_reho_parcels.py +3 -8
- junifer/markers/reho/tests/test_reho_spheres.py +3 -8
- junifer/markers/sphere_aggregation.py +59 -43
- junifer/markers/temporal_snr/__init__.py +0 -3
- junifer/markers/temporal_snr/temporal_snr_base.py +32 -23
- junifer/markers/temporal_snr/temporal_snr_parcels.py +6 -9
- junifer/markers/temporal_snr/temporal_snr_spheres.py +6 -9
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +3 -6
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +3 -6
- junifer/markers/tests/test_collection.py +8 -9
- junifer/markers/tests/test_ets_rss.py +9 -15
- junifer/markers/tests/test_markers_base.py +18 -17
- junifer/markers/tests/test_parcel_aggregation.py +32 -93
- junifer/markers/tests/test_sphere_aggregation.py +19 -72
- junifer/onthefly/__init__.py +1 -4
- junifer/onthefly/read_transform.py +0 -3
- junifer/pipeline/__init__.py +1 -9
- junifer/pipeline/pipeline_step_mixin.py +4 -21
- junifer/pipeline/registry.py +0 -3
- junifer/pipeline/singleton.py +0 -3
- junifer/pipeline/tests/test_registry.py +1 -1
- junifer/pipeline/update_meta_mixin.py +0 -3
- junifer/pipeline/utils.py +1 -67
- junifer/pipeline/workdir_manager.py +0 -3
- junifer/preprocess/__init__.py +2 -10
- junifer/preprocess/ants/__init__.py +4 -0
- junifer/preprocess/ants/ants_apply_transforms_warper.py +185 -0
- junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +56 -0
- junifer/preprocess/base.py +3 -6
- junifer/preprocess/bold_warper.py +265 -0
- junifer/preprocess/confounds/__init__.py +0 -3
- junifer/preprocess/confounds/fmriprep_confound_remover.py +60 -47
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +113 -72
- junifer/preprocess/fsl/__init__.py +4 -0
- junifer/preprocess/fsl/apply_warper.py +179 -0
- junifer/preprocess/fsl/tests/test_apply_warper.py +45 -0
- junifer/preprocess/tests/test_bold_warper.py +159 -0
- junifer/preprocess/warping/__init__.py +0 -3
- junifer/preprocess/warping/_ants_warper.py +0 -3
- junifer/preprocess/warping/_fsl_warper.py +0 -3
- junifer/stats.py +1 -4
- junifer/storage/__init__.py +1 -9
- junifer/storage/base.py +1 -40
- junifer/storage/hdf5.py +9 -71
- junifer/storage/pandas_base.py +0 -3
- junifer/storage/sqlite.py +0 -3
- junifer/storage/tests/test_hdf5.py +10 -82
- junifer/storage/utils.py +0 -9
- junifer/testing/__init__.py +1 -4
- junifer/testing/datagrabbers.py +6 -13
- junifer/testing/tests/test_partlycloudytesting_datagrabber.py +7 -7
- junifer/testing/utils.py +0 -3
- junifer/utils/__init__.py +2 -13
- junifer/utils/fs.py +0 -3
- junifer/utils/helpers.py +1 -32
- junifer/utils/logging.py +4 -33
- junifer/utils/tests/test_logging.py +0 -8
- {junifer-0.0.5.dist-info → junifer-0.0.5.dev11.dist-info}/METADATA +16 -17
- junifer-0.0.5.dev11.dist-info/RECORD +259 -0
- {junifer-0.0.5.dist-info → junifer-0.0.5.dev11.dist-info}/WHEEL +1 -1
- junifer/api/res/freesurfer/mri_binarize +0 -3
- junifer/api/res/freesurfer/mri_mc +0 -3
- junifer/api/res/freesurfer/mri_pretess +0 -3
- junifer/api/res/freesurfer/mris_convert +0 -3
- junifer/api/res/freesurfer/run_freesurfer_docker.sh +0 -61
- junifer/data/masks/ukb/UKB_15K_GM_template.nii.gz +0 -0
- junifer/datagrabber/pattern_validation_mixin.py +0 -388
- junifer/datagrabber/tests/test_pattern_validation_mixin.py +0 -249
- junifer/external/BrainPrint/brainprint/__init__.py +0 -4
- junifer/external/BrainPrint/brainprint/_version.py +0 -3
- junifer/external/BrainPrint/brainprint/asymmetry.py +0 -91
- junifer/external/BrainPrint/brainprint/brainprint.py +0 -441
- junifer/external/BrainPrint/brainprint/surfaces.py +0 -258
- junifer/external/BrainPrint/brainprint/utils/__init__.py +0 -1
- junifer/external/BrainPrint/brainprint/utils/_config.py +0 -112
- junifer/external/BrainPrint/brainprint/utils/utils.py +0 -188
- junifer/external/nilearn/junifer_connectivity_measure.py +0 -483
- junifer/external/nilearn/tests/test_junifer_connectivity_measure.py +0 -1089
- junifer/markers/brainprint.py +0 -459
- junifer/markers/tests/test_brainprint.py +0 -58
- junifer/preprocess/smoothing/__init__.py +0 -9
- junifer/preprocess/smoothing/_afni_smoothing.py +0 -119
- junifer/preprocess/smoothing/_fsl_smoothing.py +0 -116
- junifer/preprocess/smoothing/_nilearn_smoothing.py +0 -69
- junifer/preprocess/smoothing/smoothing.py +0 -174
- junifer/preprocess/smoothing/tests/test_smoothing.py +0 -94
- junifer-0.0.5.dist-info/RECORD +0 -275
- {junifer-0.0.5.dist-info → junifer-0.0.5.dev11.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.5.dist-info → junifer-0.0.5.dev11.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.5.dist-info → junifer-0.0.5.dev11.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.5.dist-info → junifer-0.0.5.dev11.dist-info}/top_level.txt +0 -0
junifer/markers/brainprint.py
DELETED
@@ -1,459 +0,0 @@
|
|
1
|
-
"""Provide class for BrainPrint."""
|
2
|
-
|
3
|
-
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
-
# License: AGPL
|
5
|
-
|
6
|
-
import uuid
|
7
|
-
from pathlib import Path
|
8
|
-
from typing import (
|
9
|
-
Any,
|
10
|
-
ClassVar,
|
11
|
-
Dict,
|
12
|
-
List,
|
13
|
-
Optional,
|
14
|
-
Set,
|
15
|
-
Union,
|
16
|
-
)
|
17
|
-
|
18
|
-
import numpy as np
|
19
|
-
import numpy.typing as npt
|
20
|
-
|
21
|
-
from ..api.decorators import register_marker
|
22
|
-
from ..external.BrainPrint.brainprint.brainprint import (
|
23
|
-
compute_asymmetry,
|
24
|
-
compute_brainprint,
|
25
|
-
)
|
26
|
-
from ..external.BrainPrint.brainprint.surfaces import surf_to_vtk
|
27
|
-
from ..pipeline import WorkDirManager
|
28
|
-
from ..utils import logger, run_ext_cmd
|
29
|
-
from .base import BaseMarker
|
30
|
-
|
31
|
-
|
32
|
-
__all__ = ["BrainPrint"]
|
33
|
-
|
34
|
-
|
35
|
-
@register_marker
|
36
|
-
class BrainPrint(BaseMarker):
|
37
|
-
"""Class for BrainPrint.
|
38
|
-
|
39
|
-
Parameters
|
40
|
-
----------
|
41
|
-
num : positive int, optional
|
42
|
-
Number of eigenvalues to compute (default 50).
|
43
|
-
skip_cortex : bool, optional
|
44
|
-
Whether to skip cortical surface or not (default False).
|
45
|
-
keep_eigenvectors : bool, optional
|
46
|
-
Whether to also return eigenvectors or not (default False).
|
47
|
-
norm : str, optional
|
48
|
-
Eigenvalues normalization method (default "none").
|
49
|
-
reweight : bool, optional
|
50
|
-
Whether to reweight eigenvalues or not (default False).
|
51
|
-
asymmetry : bool, optional
|
52
|
-
Whether to calculate asymmetry between lateral structures
|
53
|
-
(default False).
|
54
|
-
asymmetry_distance : {"euc"}, optional
|
55
|
-
Distance measurement to use if ``asymmetry=True``:
|
56
|
-
|
57
|
-
* ``"euc"`` : Euclidean
|
58
|
-
|
59
|
-
(default "euc").
|
60
|
-
use_cholmod : bool, optional
|
61
|
-
If True, attempts to use the Cholesky decomposition for improved
|
62
|
-
execution speed. Requires the ``scikit-sparse`` library. If it cannot
|
63
|
-
be found, an error will be thrown. If False, will use slower LU
|
64
|
-
decomposition (default False).
|
65
|
-
name : str, optional
|
66
|
-
The name of the marker. If None, will use the class name (default
|
67
|
-
None).
|
68
|
-
|
69
|
-
"""
|
70
|
-
|
71
|
-
_EXT_DEPENDENCIES: ClassVar[List[Dict[str, Union[str, List[str]]]]] = [
|
72
|
-
{
|
73
|
-
"name": "freesurfer",
|
74
|
-
"commands": [
|
75
|
-
"mri_binarize",
|
76
|
-
"mri_pretess",
|
77
|
-
"mri_mc",
|
78
|
-
"mris_convert",
|
79
|
-
],
|
80
|
-
},
|
81
|
-
]
|
82
|
-
|
83
|
-
_DEPENDENCIES: ClassVar[Set[str]] = {"lapy", "numpy"}
|
84
|
-
|
85
|
-
_MARKER_INOUT_MAPPINGS: ClassVar[Dict[str, Dict[str, str]]] = {
|
86
|
-
"FreeSurfer": {
|
87
|
-
"eigenvalues": "scalar_table",
|
88
|
-
"areas": "vector",
|
89
|
-
"volumes": "vector",
|
90
|
-
"distances": "vector",
|
91
|
-
}
|
92
|
-
}
|
93
|
-
|
94
|
-
def __init__(
|
95
|
-
self,
|
96
|
-
num: int = 50,
|
97
|
-
skip_cortex=False,
|
98
|
-
keep_eigenvectors: bool = False,
|
99
|
-
norm: str = "none",
|
100
|
-
reweight: bool = False,
|
101
|
-
asymmetry: bool = False,
|
102
|
-
asymmetry_distance: str = "euc",
|
103
|
-
use_cholmod: bool = False,
|
104
|
-
name: Optional[str] = None,
|
105
|
-
) -> None:
|
106
|
-
self.num = num
|
107
|
-
self.skip_cortex = skip_cortex
|
108
|
-
self.keep_eigenvectors = keep_eigenvectors
|
109
|
-
self.norm = norm
|
110
|
-
self.reweight = reweight
|
111
|
-
self.asymmetry = asymmetry
|
112
|
-
self.asymmetry_distance = asymmetry_distance
|
113
|
-
self.use_cholmod = use_cholmod
|
114
|
-
super().__init__(name=name, on="FreeSurfer")
|
115
|
-
|
116
|
-
def _create_aseg_surface(
|
117
|
-
self,
|
118
|
-
aseg_path: Path,
|
119
|
-
norm_path: Path,
|
120
|
-
indices: List,
|
121
|
-
) -> Path:
|
122
|
-
"""Generate a surface from the aseg and label files.
|
123
|
-
|
124
|
-
Parameters
|
125
|
-
----------
|
126
|
-
aseg_path : pathlib.Path
|
127
|
-
The FreeSurfer aseg path.
|
128
|
-
norm_path : pathlib.Path
|
129
|
-
The FreeSurfer norm path.
|
130
|
-
indices : list
|
131
|
-
List of label indices to include in the surface generation.
|
132
|
-
|
133
|
-
Returns
|
134
|
-
-------
|
135
|
-
pathlib.Path
|
136
|
-
Path to the generated surface in VTK format.
|
137
|
-
|
138
|
-
"""
|
139
|
-
tempfile_prefix = f"aseg.{uuid.uuid4()}"
|
140
|
-
|
141
|
-
# Set mri_binarize command
|
142
|
-
mri_binarize_output_path = self._tempdir / f"{tempfile_prefix}.mgz"
|
143
|
-
mri_binarize_cmd = [
|
144
|
-
"mri_binarize",
|
145
|
-
f"--i {aseg_path.resolve()}",
|
146
|
-
f"--match {''.join(indices)}",
|
147
|
-
f"--o {mri_binarize_output_path.resolve()}",
|
148
|
-
]
|
149
|
-
# Call mri_binarize command
|
150
|
-
run_ext_cmd(name="mri_binarize", cmd=mri_binarize_cmd)
|
151
|
-
|
152
|
-
label_value = "1"
|
153
|
-
# Fix label (pretess)
|
154
|
-
# Set mri_pretess command
|
155
|
-
mri_pretess_cmd = [
|
156
|
-
"mri_pretess",
|
157
|
-
f"{mri_binarize_output_path.resolve()}",
|
158
|
-
f"{label_value}",
|
159
|
-
f"{norm_path.resolve()}",
|
160
|
-
f"{mri_binarize_output_path.resolve()}",
|
161
|
-
]
|
162
|
-
# Call mri_pretess command
|
163
|
-
run_ext_cmd(name="mri_pretess", cmd=mri_pretess_cmd)
|
164
|
-
|
165
|
-
# Run marching cube to extract surface
|
166
|
-
# Set mri_mc command
|
167
|
-
mri_mc_output_path = self._tempdir / f"{tempfile_prefix}.surf"
|
168
|
-
mri_mc_cmd = [
|
169
|
-
"mri_mc",
|
170
|
-
f"{mri_binarize_output_path.resolve()}",
|
171
|
-
f"{label_value}",
|
172
|
-
f"{mri_mc_output_path.resolve()}",
|
173
|
-
]
|
174
|
-
# Run mri_mc command
|
175
|
-
run_ext_cmd(name="mri_mc", cmd=mri_mc_cmd)
|
176
|
-
|
177
|
-
# Convert to vtk
|
178
|
-
# Set mris_convert command
|
179
|
-
surface_path = (
|
180
|
-
self._element_tempdir / f"aseg.final.{'_'.join(indices)}.vtk"
|
181
|
-
)
|
182
|
-
mris_convert_cmd = [
|
183
|
-
"mris_convert",
|
184
|
-
f"{mri_mc_output_path.resolve()}",
|
185
|
-
f"{surface_path.resolve()}",
|
186
|
-
]
|
187
|
-
# Run mris_convert command
|
188
|
-
run_ext_cmd(name="mris_convert", cmd=mris_convert_cmd)
|
189
|
-
|
190
|
-
return surface_path
|
191
|
-
|
192
|
-
def _create_aseg_surfaces(
|
193
|
-
self,
|
194
|
-
aseg_path: Path,
|
195
|
-
norm_path: Path,
|
196
|
-
) -> Dict[str, Path]:
|
197
|
-
"""Create surfaces from FreeSurfer aseg labels.
|
198
|
-
|
199
|
-
Parameters
|
200
|
-
----------
|
201
|
-
aseg_path : pathlib.Path
|
202
|
-
The FreeSurfer aseg path.
|
203
|
-
norm_path : pathlib.Path
|
204
|
-
The FreeSurfer norm path.
|
205
|
-
|
206
|
-
Returns
|
207
|
-
-------
|
208
|
-
dict
|
209
|
-
Dictionary of label names mapped to corresponding surface paths.
|
210
|
-
|
211
|
-
"""
|
212
|
-
# Define aseg labels
|
213
|
-
|
214
|
-
# combined and individual aseg labels:
|
215
|
-
# - Left Striatum: left Caudate + Putamen + Accumbens
|
216
|
-
# - Right Striatum: right Caudate + Putamen + Accumbens
|
217
|
-
# - CorpusCallosum: 5 subregions combined
|
218
|
-
# - Cerebellum: brainstem + (left+right) cerebellum WM and GM
|
219
|
-
# - Ventricles: (left+right) lat.vent + inf.lat.vent + choroidplexus +
|
220
|
-
# 3rdVent + CSF
|
221
|
-
# - Lateral-Ventricle: lat.vent + inf.lat.vent + choroidplexus
|
222
|
-
# - 3rd-Ventricle: 3rd-Ventricle + CSF
|
223
|
-
|
224
|
-
aseg_labels = {
|
225
|
-
"CorpusCallosum": ["251", "252", "253", "254", "255"],
|
226
|
-
"Cerebellum": ["7", "8", "16", "46", "47"],
|
227
|
-
"Ventricles": ["4", "5", "14", "24", "31", "43", "44", "63"],
|
228
|
-
"3rd-Ventricle": ["14", "24"],
|
229
|
-
"4th-Ventricle": ["15"],
|
230
|
-
"Brain-Stem": ["16"],
|
231
|
-
"Left-Striatum": ["11", "12", "26"],
|
232
|
-
"Left-Lateral-Ventricle": ["4", "5", "31"],
|
233
|
-
"Left-Cerebellum-White-Matter": ["7"],
|
234
|
-
"Left-Cerebellum-Cortex": ["8"],
|
235
|
-
"Left-Thalamus-Proper": ["10"],
|
236
|
-
"Left-Caudate": ["11"],
|
237
|
-
"Left-Putamen": ["12"],
|
238
|
-
"Left-Pallidum": ["13"],
|
239
|
-
"Left-Hippocampus": ["17"],
|
240
|
-
"Left-Amygdala": ["18"],
|
241
|
-
"Left-Accumbens-area": ["26"],
|
242
|
-
"Left-VentralDC": ["28"],
|
243
|
-
"Right-Striatum": ["50", "51", "58"],
|
244
|
-
"Right-Lateral-Ventricle": ["43", "44", "63"],
|
245
|
-
"Right-Cerebellum-White-Matter": ["46"],
|
246
|
-
"Right-Cerebellum-Cortex": ["47"],
|
247
|
-
"Right-Thalamus-Proper": ["49"],
|
248
|
-
"Right-Caudate": ["50"],
|
249
|
-
"Right-Putamen": ["51"],
|
250
|
-
"Right-Pallidum": ["52"],
|
251
|
-
"Right-Hippocampus": ["53"],
|
252
|
-
"Right-Amygdala": ["54"],
|
253
|
-
"Right-Accumbens-area": ["58"],
|
254
|
-
"Right-VentralDC": ["60"],
|
255
|
-
}
|
256
|
-
return {
|
257
|
-
label: self._create_aseg_surface(
|
258
|
-
aseg_path=aseg_path,
|
259
|
-
norm_path=norm_path,
|
260
|
-
indices=indices,
|
261
|
-
)
|
262
|
-
for label, indices in aseg_labels.items()
|
263
|
-
}
|
264
|
-
|
265
|
-
def _create_cortical_surfaces(
|
266
|
-
self,
|
267
|
-
lh_white_path: Path,
|
268
|
-
rh_white_path: Path,
|
269
|
-
lh_pial_path: Path,
|
270
|
-
rh_pial_path: Path,
|
271
|
-
) -> Dict[str, Path]:
|
272
|
-
"""Create cortical surfaces from FreeSurfer labels.
|
273
|
-
|
274
|
-
Parameters
|
275
|
-
----------
|
276
|
-
lh_white_path : pathlib.Path
|
277
|
-
The FreeSurfer lh.white path.
|
278
|
-
rh_white_path : pathlib.Path
|
279
|
-
The FreeSurfer rh.white path.
|
280
|
-
lh_pial_path : pathlib.Path
|
281
|
-
The FreeSurfer lh.pial path.
|
282
|
-
rh_pial_path : pathlib.Path
|
283
|
-
The FreeSurfer rh.pial path.
|
284
|
-
|
285
|
-
Returns
|
286
|
-
-------
|
287
|
-
dict
|
288
|
-
Cortical surface label names with their paths as dictionary.
|
289
|
-
|
290
|
-
"""
|
291
|
-
return {
|
292
|
-
"lh-white-2d": surf_to_vtk(
|
293
|
-
lh_white_path.resolve(),
|
294
|
-
(self._element_tempdir / "lh.white.vtk").resolve(),
|
295
|
-
),
|
296
|
-
"rh-white-2d": surf_to_vtk(
|
297
|
-
rh_white_path.resolve(),
|
298
|
-
(self._element_tempdir / "rh.white.vtk").resolve(),
|
299
|
-
),
|
300
|
-
"lh-pial-2d": surf_to_vtk(
|
301
|
-
lh_pial_path.resolve(),
|
302
|
-
(self._element_tempdir / "lh.pial.vtk").resolve(),
|
303
|
-
),
|
304
|
-
"rh-pial-2d": surf_to_vtk(
|
305
|
-
rh_pial_path.resolve(),
|
306
|
-
(self._element_tempdir / "rh.pial.vtk").resolve(),
|
307
|
-
),
|
308
|
-
}
|
309
|
-
|
310
|
-
def _fix_nan(
|
311
|
-
self,
|
312
|
-
input_data: List[Union[float, str, npt.ArrayLike]],
|
313
|
-
) -> np.ndarray:
|
314
|
-
"""Convert BrainPrint output with string NaN to ``numpy.nan``.
|
315
|
-
|
316
|
-
Parameters
|
317
|
-
----------
|
318
|
-
input_data : list of str, float or numpy.ndarray-like
|
319
|
-
The data to convert.
|
320
|
-
|
321
|
-
Returns
|
322
|
-
-------
|
323
|
-
np.ndarray
|
324
|
-
The converted data as ``numpy.ndarray``.
|
325
|
-
|
326
|
-
"""
|
327
|
-
arr = np.asarray(input_data)
|
328
|
-
arr[arr == "NaN"] = np.nan
|
329
|
-
return arr.astype(np.float64)
|
330
|
-
|
331
|
-
def compute(
|
332
|
-
self,
|
333
|
-
input: Dict[str, Any],
|
334
|
-
extra_input: Optional[Dict] = None,
|
335
|
-
) -> Dict:
|
336
|
-
"""Compute.
|
337
|
-
|
338
|
-
Parameters
|
339
|
-
----------
|
340
|
-
input : dict
|
341
|
-
The FreeSurfer data as dictionary.
|
342
|
-
extra_input : dict, optional
|
343
|
-
The other fields in the pipeline data object (default None).
|
344
|
-
|
345
|
-
Returns
|
346
|
-
-------
|
347
|
-
dict
|
348
|
-
The computed result as dictionary. This will be either returned
|
349
|
-
to the user or stored in the storage by calling the store method
|
350
|
-
with this as a parameter. The dictionary has the following keys:
|
351
|
-
|
352
|
-
* ``eigenvalues`` : dictionary with the following keys:
|
353
|
-
|
354
|
-
- ``data`` : eigenvalues as ``np.ndarray``
|
355
|
-
- ``col_names`` : surface labels as list of str
|
356
|
-
- ``row_names`` : eigenvalue count labels as list of str
|
357
|
-
- ``row_header_col_name`` : "eigenvalue"
|
358
|
-
()
|
359
|
-
* ``areas`` : dictionary with the following keys:
|
360
|
-
|
361
|
-
- ``data`` : areas as ``np.ndarray``
|
362
|
-
- ``col_names`` : surface labels as list of str
|
363
|
-
|
364
|
-
* ``volumes`` : dictionary with the following keys:
|
365
|
-
|
366
|
-
- ``data`` : volumes as ``np.ndarray``
|
367
|
-
- ``col_names`` : surface labels as list of str
|
368
|
-
|
369
|
-
* ``distances`` : dictionary with the following keys
|
370
|
-
if ``asymmetry = True``:
|
371
|
-
|
372
|
-
- ``data`` : distances as ``np.ndarray``
|
373
|
-
- ``col_names`` : surface labels as list of str
|
374
|
-
|
375
|
-
References
|
376
|
-
----------
|
377
|
-
.. [1] Wachinger, C., Golland, P., Kremen, W. et al. (2015)
|
378
|
-
BrainPrint: A discriminative characterization of brain
|
379
|
-
morphology.
|
380
|
-
NeuroImage, Volume 109, Pages 232-248.
|
381
|
-
https://doi.org/10.1016/j.neuroimage.2015.01.032.
|
382
|
-
.. [2] Reuter, M., Wolter, F.E., Peinecke, N. (2006)
|
383
|
-
Laplace-Beltrami spectra as 'Shape-DNA' of surfaces and solids.
|
384
|
-
Computer-Aided Design, Volume 38, Issue 4, Pages 342-366.
|
385
|
-
https://doi.org/10.1016/j.cad.2005.10.011.
|
386
|
-
|
387
|
-
"""
|
388
|
-
logger.debug("Computing BrainPrint")
|
389
|
-
|
390
|
-
# Create component-scoped tempdir
|
391
|
-
self._tempdir = WorkDirManager().get_tempdir(prefix="brainprint")
|
392
|
-
# Create element-scoped tempdir so that the files are
|
393
|
-
# available later as nibabel stores file path reference for
|
394
|
-
# loading on computation
|
395
|
-
self._element_tempdir = WorkDirManager().get_element_tempdir(
|
396
|
-
prefix="brainprint"
|
397
|
-
)
|
398
|
-
# Generate surfaces
|
399
|
-
surfaces = self._create_aseg_surfaces(
|
400
|
-
aseg_path=input["aseg"]["path"],
|
401
|
-
norm_path=input["norm"]["path"],
|
402
|
-
)
|
403
|
-
if not self.skip_cortex:
|
404
|
-
cortical_surfaces = self._create_cortical_surfaces(
|
405
|
-
lh_white_path=input["lh_white"]["path"],
|
406
|
-
rh_white_path=input["rh_white"]["path"],
|
407
|
-
lh_pial_path=input["lh_pial"]["path"],
|
408
|
-
rh_pial_path=input["rh_pial"]["path"],
|
409
|
-
)
|
410
|
-
surfaces.update(cortical_surfaces)
|
411
|
-
# Compute brainprint
|
412
|
-
eigenvalues, _ = compute_brainprint(
|
413
|
-
surfaces=surfaces,
|
414
|
-
keep_eigenvectors=self.keep_eigenvectors,
|
415
|
-
num=self.num,
|
416
|
-
norm=self.norm,
|
417
|
-
reweight=self.reweight,
|
418
|
-
use_cholmod=self.use_cholmod,
|
419
|
-
)
|
420
|
-
# Calculate distances (if required)
|
421
|
-
distances = None
|
422
|
-
if self.asymmetry:
|
423
|
-
distances = compute_asymmetry(
|
424
|
-
eigenvalues=eigenvalues,
|
425
|
-
distance=self.asymmetry_distance,
|
426
|
-
skip_cortex=self.skip_cortex,
|
427
|
-
)
|
428
|
-
|
429
|
-
# Delete tempdir
|
430
|
-
WorkDirManager().delete_tempdir(self._tempdir)
|
431
|
-
|
432
|
-
output = {
|
433
|
-
"eigenvalues": {
|
434
|
-
"data": self._fix_nan(
|
435
|
-
[val[2:] for val in eigenvalues.values()]
|
436
|
-
).T,
|
437
|
-
"col_names": list(eigenvalues.keys()),
|
438
|
-
"row_names": [f"ev{i}" for i in range(self.num)],
|
439
|
-
"row_header_col_name": "eigenvalue",
|
440
|
-
},
|
441
|
-
"areas": {
|
442
|
-
"data": self._fix_nan(
|
443
|
-
[val[0] for val in eigenvalues.values()]
|
444
|
-
),
|
445
|
-
"col_names": list(eigenvalues.keys()),
|
446
|
-
},
|
447
|
-
"volumes": {
|
448
|
-
"data": self._fix_nan(
|
449
|
-
[val[1] for val in eigenvalues.values()]
|
450
|
-
),
|
451
|
-
"col_names": list(eigenvalues.keys()),
|
452
|
-
},
|
453
|
-
}
|
454
|
-
if self.asymmetry:
|
455
|
-
output["distances"] = {
|
456
|
-
"data": self._fix_nan(list(distances.values())),
|
457
|
-
"col_names": list(distances.keys()),
|
458
|
-
}
|
459
|
-
return output
|
@@ -1,58 +0,0 @@
|
|
1
|
-
"""Provide tests for BrainPrint."""
|
2
|
-
|
3
|
-
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
-
# License: AGPL
|
5
|
-
|
6
|
-
import socket
|
7
|
-
|
8
|
-
import pytest
|
9
|
-
|
10
|
-
from junifer.datagrabber import DataladAOMICID1000
|
11
|
-
from junifer.datareader import DefaultDataReader
|
12
|
-
from junifer.markers import BrainPrint
|
13
|
-
from junifer.pipeline.utils import _check_freesurfer
|
14
|
-
|
15
|
-
|
16
|
-
@pytest.mark.parametrize(
|
17
|
-
"feature, storage_type",
|
18
|
-
[
|
19
|
-
("eigenvalues", "scalar_table"),
|
20
|
-
("areas", "vector"),
|
21
|
-
("volumes", "vector"),
|
22
|
-
("distances", "vector"),
|
23
|
-
],
|
24
|
-
)
|
25
|
-
def test_get_output_type(feature: str, storage_type: str) -> None:
|
26
|
-
"""Test BrainPrint get_output_type().
|
27
|
-
|
28
|
-
Parameters
|
29
|
-
----------
|
30
|
-
feature : str
|
31
|
-
The parametrized feature name.
|
32
|
-
storage_type : str
|
33
|
-
The parametrized storage type.
|
34
|
-
|
35
|
-
"""
|
36
|
-
assert storage_type == BrainPrint().get_output_type(
|
37
|
-
input_type="FreeSurfer", output_feature=feature
|
38
|
-
)
|
39
|
-
|
40
|
-
|
41
|
-
@pytest.mark.skipif(
|
42
|
-
_check_freesurfer() is False, reason="requires FreeSurfer to be in PATH"
|
43
|
-
)
|
44
|
-
@pytest.mark.skipif(
|
45
|
-
socket.gethostname() != "juseless",
|
46
|
-
reason="only for juseless",
|
47
|
-
)
|
48
|
-
def test_compute() -> None:
|
49
|
-
"""Test BrainPrint compute()."""
|
50
|
-
with DataladAOMICID1000(types="FreeSurfer") as dg:
|
51
|
-
# Fetch element
|
52
|
-
element = dg["sub-0001"]
|
53
|
-
# Fetch element data
|
54
|
-
element_data = DefaultDataReader().fit_transform(element)
|
55
|
-
# Compute marker
|
56
|
-
feature_map = BrainPrint().fit_transform(element_data)
|
57
|
-
# Assert the output keys
|
58
|
-
assert {"eigenvalues", "areas", "volumes"} == set(feature_map.keys())
|
@@ -1,119 +0,0 @@
|
|
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
|