junifer 0.0.3.dev188__py3-none-any.whl → 0.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- junifer/_version.py +14 -2
- junifer/api/cli.py +162 -17
- junifer/api/functions.py +87 -419
- junifer/api/parser.py +24 -0
- junifer/api/queue_context/__init__.py +8 -0
- junifer/api/queue_context/gnu_parallel_local_adapter.py +258 -0
- junifer/api/queue_context/htcondor_adapter.py +365 -0
- junifer/api/queue_context/queue_context_adapter.py +60 -0
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +192 -0
- junifer/api/queue_context/tests/test_htcondor_adapter.py +257 -0
- junifer/api/res/afni/run_afni_docker.sh +6 -6
- junifer/api/res/ants/ResampleImage +3 -0
- junifer/api/res/ants/antsApplyTransforms +3 -0
- junifer/api/res/ants/antsApplyTransformsToPoints +3 -0
- junifer/api/res/ants/run_ants_docker.sh +39 -0
- junifer/api/res/fsl/applywarp +3 -0
- junifer/api/res/fsl/flirt +3 -0
- junifer/api/res/fsl/img2imgcoord +3 -0
- junifer/api/res/fsl/run_fsl_docker.sh +39 -0
- junifer/api/res/fsl/std2imgcoord +3 -0
- junifer/api/res/run_conda.sh +4 -4
- junifer/api/res/run_venv.sh +22 -0
- junifer/api/tests/data/partly_cloudy_agg_mean_tian.yml +16 -0
- junifer/api/tests/test_api_utils.py +21 -3
- junifer/api/tests/test_cli.py +232 -9
- junifer/api/tests/test_functions.py +211 -439
- junifer/api/tests/test_parser.py +1 -1
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +8 -8
- junifer/configs/juseless/datagrabbers/ucla.py +44 -26
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +6 -1
- junifer/data/VOIs/meta/AutobiographicalMemory_VOIs.txt +23 -0
- junifer/data/VOIs/meta/Power2013_MNI_VOIs.tsv +264 -0
- junifer/data/__init__.py +4 -0
- junifer/data/coordinates.py +298 -31
- junifer/data/masks.py +360 -28
- junifer/data/parcellations.py +621 -188
- junifer/data/template_spaces.py +190 -0
- junifer/data/tests/test_coordinates.py +34 -3
- junifer/data/tests/test_data_utils.py +1 -0
- junifer/data/tests/test_masks.py +202 -86
- junifer/data/tests/test_parcellations.py +266 -55
- junifer/data/tests/test_template_spaces.py +104 -0
- junifer/data/utils.py +4 -2
- junifer/datagrabber/__init__.py +1 -0
- junifer/datagrabber/aomic/id1000.py +111 -70
- junifer/datagrabber/aomic/piop1.py +116 -53
- junifer/datagrabber/aomic/piop2.py +116 -53
- junifer/datagrabber/aomic/tests/test_id1000.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop1.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop2.py +27 -27
- junifer/datagrabber/base.py +62 -10
- junifer/datagrabber/datalad_base.py +0 -2
- junifer/datagrabber/dmcc13_benchmark.py +372 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +5 -0
- junifer/datagrabber/hcp1200/hcp1200.py +30 -13
- junifer/datagrabber/pattern.py +133 -27
- junifer/datagrabber/pattern_datalad.py +111 -13
- junifer/datagrabber/tests/test_base.py +57 -6
- junifer/datagrabber/tests/test_datagrabber_utils.py +204 -76
- junifer/datagrabber/tests/test_datalad_base.py +0 -6
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +256 -0
- junifer/datagrabber/tests/test_multiple.py +43 -10
- junifer/datagrabber/tests/test_pattern.py +125 -178
- junifer/datagrabber/tests/test_pattern_datalad.py +44 -25
- junifer/datagrabber/utils.py +151 -16
- junifer/datareader/default.py +36 -10
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +6 -0
- junifer/markers/base.py +25 -16
- junifer/markers/collection.py +35 -16
- junifer/markers/complexity/__init__.py +27 -0
- junifer/markers/complexity/complexity_base.py +149 -0
- junifer/markers/complexity/hurst_exponent.py +136 -0
- junifer/markers/complexity/multiscale_entropy_auc.py +140 -0
- junifer/markers/complexity/perm_entropy.py +132 -0
- junifer/markers/complexity/range_entropy.py +136 -0
- junifer/markers/complexity/range_entropy_auc.py +145 -0
- junifer/markers/complexity/sample_entropy.py +134 -0
- junifer/markers/complexity/tests/test_complexity_base.py +19 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +69 -0
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +68 -0
- junifer/markers/complexity/tests/test_perm_entropy.py +68 -0
- junifer/markers/complexity/tests/test_range_entropy.py +69 -0
- junifer/markers/complexity/tests/test_range_entropy_auc.py +69 -0
- junifer/markers/complexity/tests/test_sample_entropy.py +68 -0
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +68 -0
- junifer/markers/complexity/weighted_perm_entropy.py +133 -0
- junifer/markers/falff/_afni_falff.py +153 -0
- junifer/markers/falff/_junifer_falff.py +142 -0
- junifer/markers/falff/falff_base.py +91 -84
- junifer/markers/falff/falff_parcels.py +61 -45
- junifer/markers/falff/falff_spheres.py +64 -48
- junifer/markers/falff/tests/test_falff_parcels.py +89 -121
- junifer/markers/falff/tests/test_falff_spheres.py +92 -127
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +1 -0
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +1 -0
- junifer/markers/functional_connectivity/functional_connectivity_base.py +1 -0
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +46 -44
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +34 -39
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +40 -52
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +62 -70
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +99 -85
- junifer/markers/parcel_aggregation.py +60 -38
- junifer/markers/reho/_afni_reho.py +192 -0
- junifer/markers/reho/_junifer_reho.py +281 -0
- junifer/markers/reho/reho_base.py +69 -34
- junifer/markers/reho/reho_parcels.py +26 -16
- junifer/markers/reho/reho_spheres.py +23 -9
- junifer/markers/reho/tests/test_reho_parcels.py +93 -92
- junifer/markers/reho/tests/test_reho_spheres.py +88 -86
- junifer/markers/sphere_aggregation.py +54 -9
- junifer/markers/temporal_snr/temporal_snr_base.py +1 -0
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +38 -37
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +34 -38
- junifer/markers/tests/test_collection.py +43 -42
- junifer/markers/tests/test_ets_rss.py +29 -37
- junifer/markers/tests/test_parcel_aggregation.py +587 -468
- junifer/markers/tests/test_sphere_aggregation.py +209 -157
- junifer/markers/utils.py +2 -40
- junifer/onthefly/read_transform.py +13 -6
- junifer/pipeline/__init__.py +1 -0
- junifer/pipeline/pipeline_step_mixin.py +105 -41
- junifer/pipeline/registry.py +17 -0
- junifer/pipeline/singleton.py +45 -0
- junifer/pipeline/tests/test_pipeline_step_mixin.py +139 -51
- junifer/pipeline/tests/test_update_meta_mixin.py +1 -0
- junifer/pipeline/tests/test_workdir_manager.py +104 -0
- junifer/pipeline/update_meta_mixin.py +8 -2
- junifer/pipeline/utils.py +154 -15
- junifer/pipeline/workdir_manager.py +246 -0
- junifer/preprocess/__init__.py +3 -0
- 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 +96 -69
- junifer/preprocess/bold_warper.py +265 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +91 -134
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +106 -111
- 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/tests/test_preprocess_base.py +6 -6
- junifer/preprocess/warping/__init__.py +6 -0
- junifer/preprocess/warping/_ants_warper.py +167 -0
- junifer/preprocess/warping/_fsl_warper.py +109 -0
- junifer/preprocess/warping/space_warper.py +213 -0
- junifer/preprocess/warping/tests/test_space_warper.py +198 -0
- junifer/stats.py +18 -4
- junifer/storage/base.py +9 -1
- junifer/storage/hdf5.py +8 -3
- junifer/storage/pandas_base.py +2 -1
- junifer/storage/sqlite.py +1 -0
- junifer/storage/tests/test_hdf5.py +2 -1
- junifer/storage/tests/test_sqlite.py +8 -8
- junifer/storage/tests/test_utils.py +6 -6
- junifer/storage/utils.py +1 -0
- junifer/testing/datagrabbers.py +11 -7
- junifer/testing/utils.py +1 -0
- junifer/tests/test_stats.py +2 -0
- junifer/utils/__init__.py +1 -0
- junifer/utils/helpers.py +53 -0
- junifer/utils/logging.py +14 -3
- junifer/utils/tests/test_helpers.py +35 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/METADATA +59 -28
- junifer-0.0.4.dist-info/RECORD +257 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/WHEEL +1 -1
- junifer/markers/falff/falff_estimator.py +0 -334
- junifer/markers/falff/tests/test_falff_estimator.py +0 -238
- junifer/markers/reho/reho_estimator.py +0 -515
- junifer/markers/reho/tests/test_reho_estimator.py +0 -260
- junifer-0.0.3.dev188.dist-info/RECORD +0 -199
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.3.dev188.dist-info → junifer-0.0.4.dist-info}/top_level.txt +0 -0
@@ -1,334 +0,0 @@
|
|
1
|
-
"""Provide estimator class for (f)ALFF."""
|
2
|
-
|
3
|
-
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
-
# Federico Raimondo <f.raimondo@fz-juelich.de>
|
5
|
-
# License: AGPL
|
6
|
-
|
7
|
-
import shutil
|
8
|
-
import subprocess
|
9
|
-
import tempfile
|
10
|
-
import typing
|
11
|
-
from functools import lru_cache
|
12
|
-
from pathlib import Path
|
13
|
-
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
|
14
|
-
|
15
|
-
import nibabel as nib
|
16
|
-
import numpy as np
|
17
|
-
from nilearn import image as nimg
|
18
|
-
from scipy.fft import fft, fftfreq
|
19
|
-
|
20
|
-
from ...utils import logger, raise_error
|
21
|
-
from ..utils import singleton
|
22
|
-
|
23
|
-
|
24
|
-
if TYPE_CHECKING:
|
25
|
-
from nibabel import Nifti1Image, Nifti2Image
|
26
|
-
|
27
|
-
|
28
|
-
@singleton
|
29
|
-
class ALFFEstimator:
|
30
|
-
"""Estimator class for (fractional) Amplitude Low Frequency Fluctuation.
|
31
|
-
|
32
|
-
This class is a singleton and is used for efficient computation of fALFF,
|
33
|
-
by caching the voxel-wise ALFF map for a given set of file path and
|
34
|
-
computation parameters.
|
35
|
-
|
36
|
-
.. warning:: This class can only be used via :class:`.ALFFBase` as it
|
37
|
-
serves a specific purpose.
|
38
|
-
|
39
|
-
Parameters
|
40
|
-
----------
|
41
|
-
use_afni : bool
|
42
|
-
Whether to use afni for computation. If False, will use python.
|
43
|
-
|
44
|
-
"""
|
45
|
-
|
46
|
-
def __init__(self) -> None:
|
47
|
-
self._file_path = None
|
48
|
-
# Create temporary directory for intermittent storage of assets during
|
49
|
-
# computation via afni's 3dReHo
|
50
|
-
self.temp_dir_path = Path(tempfile.mkdtemp())
|
51
|
-
|
52
|
-
def __del__(self) -> None:
|
53
|
-
"""Cleanup."""
|
54
|
-
print("Cleaning up temporary directory...")
|
55
|
-
# Delete temporary directory and ignore errors for read-only files
|
56
|
-
shutil.rmtree(self.temp_dir_path, ignore_errors=True)
|
57
|
-
|
58
|
-
@staticmethod
|
59
|
-
def _run_afni_cmd(cmd: str) -> None:
|
60
|
-
"""Run AFNI command.
|
61
|
-
|
62
|
-
Parameters
|
63
|
-
----------
|
64
|
-
cmd : str
|
65
|
-
AFNI command to be executed.
|
66
|
-
|
67
|
-
Raises
|
68
|
-
------
|
69
|
-
RuntimeError
|
70
|
-
If AFNI command fails.
|
71
|
-
"""
|
72
|
-
logger.info(f"AFNI command to be executed: {cmd}")
|
73
|
-
process = subprocess.run(
|
74
|
-
cmd,
|
75
|
-
stdin=subprocess.DEVNULL,
|
76
|
-
stdout=subprocess.PIPE,
|
77
|
-
stderr=subprocess.STDOUT,
|
78
|
-
shell=True,
|
79
|
-
check=False,
|
80
|
-
)
|
81
|
-
if process.returncode == 0:
|
82
|
-
logger.info(
|
83
|
-
"AFNI command succeeded with the following output: "
|
84
|
-
f"{process.stdout}"
|
85
|
-
)
|
86
|
-
else:
|
87
|
-
raise_error(
|
88
|
-
msg="AFNI command failed with the following error: "
|
89
|
-
f"{process.stdout}",
|
90
|
-
klass=RuntimeError,
|
91
|
-
)
|
92
|
-
|
93
|
-
def _compute_alff_afni(
|
94
|
-
self,
|
95
|
-
data: Union["Nifti1Image", "Nifti2Image"],
|
96
|
-
highpass: float,
|
97
|
-
lowpass: float,
|
98
|
-
tr: Optional[float],
|
99
|
-
) -> Tuple["Nifti1Image", "Nifti1Image"]:
|
100
|
-
"""Compute ALFF map via afni's commands.
|
101
|
-
|
102
|
-
Parameters
|
103
|
-
----------
|
104
|
-
data : 4D Niimg-like object
|
105
|
-
Images to process.
|
106
|
-
highpass : positive float
|
107
|
-
Highpass cutoff frequency.
|
108
|
-
lowpass : positive float
|
109
|
-
Lowpass cutoff frequency.
|
110
|
-
tr : positive float, optional
|
111
|
-
The Repetition Time of the BOLD data.
|
112
|
-
|
113
|
-
Returns
|
114
|
-
-------
|
115
|
-
alff: Niimg-like object
|
116
|
-
ALFF map.
|
117
|
-
falff: Niimg-like object
|
118
|
-
fALFF map.
|
119
|
-
|
120
|
-
Raises
|
121
|
-
------
|
122
|
-
RuntimeError
|
123
|
-
If the AFNI commands fails due to some issues
|
124
|
-
|
125
|
-
"""
|
126
|
-
|
127
|
-
# Save niimg to nii.gz
|
128
|
-
nifti_in_file_path = self.temp_dir_path / "input.nii"
|
129
|
-
nib.save(data, nifti_in_file_path)
|
130
|
-
|
131
|
-
params_suffix = f"_{highpass}_{lowpass}_{tr}"
|
132
|
-
alff_fname = self.temp_dir_path / f"alff{params_suffix}.nii"
|
133
|
-
falff_fname = self.temp_dir_path / f"falff{params_suffix}.nii"
|
134
|
-
|
135
|
-
# Use afni's 3dRSFC to compute ALFF and fALFF
|
136
|
-
falff_afni_out_path_prefix = self.temp_dir_path / "temp_falff"
|
137
|
-
|
138
|
-
bp_cmd = (
|
139
|
-
"3dRSFC "
|
140
|
-
f"-prefix {falff_afni_out_path_prefix.resolve()} "
|
141
|
-
f"-input {nifti_in_file_path.resolve()} "
|
142
|
-
f"-band {highpass} {lowpass} "
|
143
|
-
"-no_rsfa -nosat -nodetrend "
|
144
|
-
)
|
145
|
-
if tr is not None:
|
146
|
-
bp_cmd += f"-dt {tr} "
|
147
|
-
self._run_afni_cmd(bp_cmd)
|
148
|
-
|
149
|
-
# Convert afni's output to nifti
|
150
|
-
convert_cmd = (
|
151
|
-
"3dAFNItoNIFTI "
|
152
|
-
f"-prefix {alff_fname.resolve()} "
|
153
|
-
f"{falff_afni_out_path_prefix}_ALFF+tlrc.BRIK "
|
154
|
-
)
|
155
|
-
self._run_afni_cmd(convert_cmd)
|
156
|
-
|
157
|
-
convert_cmd = (
|
158
|
-
"3dAFNItoNIFTI "
|
159
|
-
f"-prefix {falff_fname.resolve()} "
|
160
|
-
f"{falff_afni_out_path_prefix}_fALFF+tlrc.BRIK "
|
161
|
-
)
|
162
|
-
self._run_afni_cmd(convert_cmd)
|
163
|
-
|
164
|
-
# Cleanup intermediate files
|
165
|
-
for fname in self.temp_dir_path.glob("temp_*"):
|
166
|
-
fname.unlink()
|
167
|
-
|
168
|
-
# Load niftis
|
169
|
-
alff_img = nib.load(alff_fname)
|
170
|
-
falff_img = nib.load(falff_fname)
|
171
|
-
|
172
|
-
return alff_img, falff_img
|
173
|
-
|
174
|
-
def _compute_alff_python(
|
175
|
-
self,
|
176
|
-
data: Union["Nifti1Image", "Nifti2Image"],
|
177
|
-
highpass: float,
|
178
|
-
lowpass: float,
|
179
|
-
tr: Optional[float],
|
180
|
-
) -> Tuple["Nifti1Image", "Nifti1Image"]:
|
181
|
-
"""Compute (f)ALFF map.
|
182
|
-
|
183
|
-
Parameters
|
184
|
-
----------
|
185
|
-
data : 4D Niimg-like object
|
186
|
-
Images to process.
|
187
|
-
highpass : positive float
|
188
|
-
Highpass cutoff frequency.
|
189
|
-
lowpass : positive float
|
190
|
-
Lowpass cutoff frequency.
|
191
|
-
tr : positive float, optional
|
192
|
-
The Repetition Time of the BOLD data.
|
193
|
-
|
194
|
-
Returns
|
195
|
-
-------
|
196
|
-
alff: Niimg-like object
|
197
|
-
ALFF map.
|
198
|
-
falff: Niimg-like object
|
199
|
-
fALFF map.
|
200
|
-
"""
|
201
|
-
timeseries = data.get_fdata().copy()
|
202
|
-
if tr is None:
|
203
|
-
tr = float(data.header["pixdim"][4]) # type: ignore
|
204
|
-
logger.info(f"TR Not provided, using TR from header = {tr}")
|
205
|
-
# bandpass the data within the lowpass and highpass cutoff freqs
|
206
|
-
|
207
|
-
ts_fft = fft(timeseries, axis=-1)
|
208
|
-
ts_fft = typing.cast(np.ndarray, ts_fft)
|
209
|
-
fft_freqs = np.abs(fftfreq(timeseries.shape[-1], tr))
|
210
|
-
|
211
|
-
dFreq = fft_freqs[1] - fft_freqs[0]
|
212
|
-
nyquist = np.max(fft_freqs)
|
213
|
-
nfft = len(fft_freqs)
|
214
|
-
logger.info(
|
215
|
-
f"FFT: nfft = {nfft}, dFreq = {dFreq}, nyquist = {nyquist}"
|
216
|
-
)
|
217
|
-
|
218
|
-
# First compute the denominator on the broadband signal
|
219
|
-
all_freq_mask = fft_freqs > 0
|
220
|
-
denominator = np.sum(np.abs(ts_fft[..., all_freq_mask]), axis=-1)
|
221
|
-
|
222
|
-
# Compute the numerator on the bandpassed signal
|
223
|
-
freq_mask = np.logical_and(fft_freqs > highpass, fft_freqs < lowpass)
|
224
|
-
# Compute ALFF
|
225
|
-
numerator = np.sum(np.abs(ts_fft[..., freq_mask]), axis=-1)
|
226
|
-
|
227
|
-
# Compute fALFF, but avoid division by zero
|
228
|
-
denom_mask = denominator <= 0.000001
|
229
|
-
denominator[denom_mask] = 1 # set to 1 to avoid division by zero
|
230
|
-
python_falff = np.divide(numerator, denominator)
|
231
|
-
# Set the values where denominator is zero to zero
|
232
|
-
python_falff[denom_mask] = 0
|
233
|
-
|
234
|
-
python_alff = numerator / np.sqrt(timeseries.shape[-1])
|
235
|
-
alff_img = nimg.new_img_like(data, python_alff)
|
236
|
-
falff_img = nimg.new_img_like(data, python_falff)
|
237
|
-
return alff_img, falff_img
|
238
|
-
|
239
|
-
@lru_cache(maxsize=None, typed=True)
|
240
|
-
def _compute(
|
241
|
-
self,
|
242
|
-
use_afni: bool,
|
243
|
-
data: Union["Nifti1Image", "Nifti2Image"],
|
244
|
-
highpass: float,
|
245
|
-
lowpass: float,
|
246
|
-
tr: Optional[float],
|
247
|
-
) -> Tuple["Nifti1Image", "Nifti1Image"]:
|
248
|
-
"""Compute the ALFF map with memorization.
|
249
|
-
|
250
|
-
Parameters
|
251
|
-
----------
|
252
|
-
use_afni : bool
|
253
|
-
Whether to use AFNI for computing.
|
254
|
-
data : 4D Niimg-like object
|
255
|
-
Images to process.
|
256
|
-
highpass : positive float
|
257
|
-
Highpass cutoff frequency.
|
258
|
-
lowpass : positive float
|
259
|
-
Lowpass cutoff frequency.
|
260
|
-
tr : positive float, optional
|
261
|
-
The Repetition Time of the BOLD data.
|
262
|
-
|
263
|
-
Returns
|
264
|
-
-------
|
265
|
-
alff: Niimg-like object
|
266
|
-
ALFF map.
|
267
|
-
falff: Niimg-like object
|
268
|
-
fALFF map.
|
269
|
-
"""
|
270
|
-
if use_afni:
|
271
|
-
output = self._compute_alff_afni(
|
272
|
-
data=data,
|
273
|
-
highpass=highpass,
|
274
|
-
lowpass=lowpass,
|
275
|
-
tr=tr,
|
276
|
-
)
|
277
|
-
else:
|
278
|
-
output = self._compute_alff_python(
|
279
|
-
data, highpass=highpass, lowpass=lowpass, tr=tr
|
280
|
-
)
|
281
|
-
return output
|
282
|
-
|
283
|
-
def fit_transform(
|
284
|
-
self,
|
285
|
-
use_afni: bool,
|
286
|
-
input_data: Dict[str, Any],
|
287
|
-
highpass: float,
|
288
|
-
lowpass: float,
|
289
|
-
tr: Optional[float],
|
290
|
-
) -> Tuple["Nifti1Image", "Nifti1Image"]:
|
291
|
-
"""Fit and transform for the estimator.
|
292
|
-
|
293
|
-
Parameters
|
294
|
-
----------
|
295
|
-
use_afni : bool
|
296
|
-
Whether to use AFNI for computing.
|
297
|
-
input_data : dict
|
298
|
-
The BOLD data as dictionary.
|
299
|
-
highpass : positive float
|
300
|
-
Highpass cutoff frequency.
|
301
|
-
lowpass : positive float
|
302
|
-
Lowpass cutoff frequency.
|
303
|
-
tr : positive float, optional
|
304
|
-
The Repetition Time of the BOLD data.
|
305
|
-
|
306
|
-
Returns
|
307
|
-
-------
|
308
|
-
alff: Niimg-like object
|
309
|
-
ALFF map.
|
310
|
-
falff: Niimg-like object
|
311
|
-
fALFF map.
|
312
|
-
"""
|
313
|
-
bold_path = input_data["path"]
|
314
|
-
bold_data = input_data["data"]
|
315
|
-
# Clear cache if file path is different from when caching was done
|
316
|
-
if self._file_path != bold_path:
|
317
|
-
logger.info(f"Removing fALFF map cache at {self._file_path}.")
|
318
|
-
# Clear the cache
|
319
|
-
self._compute.cache_clear()
|
320
|
-
# Clear temporary directory files
|
321
|
-
for file_ in self.temp_dir_path.iterdir():
|
322
|
-
file_.unlink(missing_ok=True)
|
323
|
-
# Set the new file path
|
324
|
-
self._file_path = bold_path
|
325
|
-
else:
|
326
|
-
logger.info(f"Using fALFF map cache at {self._file_path}.")
|
327
|
-
# Compute
|
328
|
-
return self._compute(
|
329
|
-
use_afni=use_afni,
|
330
|
-
data=bold_data,
|
331
|
-
highpass=highpass,
|
332
|
-
lowpass=lowpass,
|
333
|
-
tr=tr,
|
334
|
-
)
|
@@ -1,238 +0,0 @@
|
|
1
|
-
"""Provide test for (f)ALFF estimator."""
|
2
|
-
|
3
|
-
# Authors: Federico Raimondo <f.raimondo@fz-juelich.de>
|
4
|
-
# License: AGPL
|
5
|
-
|
6
|
-
import time
|
7
|
-
|
8
|
-
import pytest
|
9
|
-
from nibabel import Nifti1Image
|
10
|
-
from scipy.stats import pearsonr
|
11
|
-
|
12
|
-
from junifer.datareader import DefaultDataReader
|
13
|
-
from junifer.markers.falff.falff_estimator import ALFFEstimator
|
14
|
-
from junifer.pipeline.utils import _check_afni
|
15
|
-
from junifer.testing.datagrabbers import PartlyCloudyTestingDataGrabber
|
16
|
-
from junifer.utils import logger
|
17
|
-
|
18
|
-
|
19
|
-
def test_ALFFEstimator_cache_python() -> None:
|
20
|
-
"""Test that the cache works properly when using python."""
|
21
|
-
with PartlyCloudyTestingDataGrabber() as dg:
|
22
|
-
input = dg["sub-01"]
|
23
|
-
|
24
|
-
input = DefaultDataReader().fit_transform(input)
|
25
|
-
|
26
|
-
estimator = ALFFEstimator()
|
27
|
-
start_time = time.time()
|
28
|
-
alff, falff = estimator.fit_transform(
|
29
|
-
use_afni=False,
|
30
|
-
input_data=input["BOLD"],
|
31
|
-
highpass=0.01,
|
32
|
-
lowpass=0.1,
|
33
|
-
tr=None,
|
34
|
-
)
|
35
|
-
first_time = time.time() - start_time
|
36
|
-
logger.info(f"ALFF Estimator First time: {first_time}")
|
37
|
-
assert isinstance(alff, Nifti1Image)
|
38
|
-
assert isinstance(falff, Nifti1Image)
|
39
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
40
|
-
assert n_files == 0 # no files in python
|
41
|
-
|
42
|
-
# Now fit again, should be faster
|
43
|
-
start_time = time.time()
|
44
|
-
alff, falff = estimator.fit_transform(
|
45
|
-
use_afni=False,
|
46
|
-
input_data=input["BOLD"],
|
47
|
-
highpass=0.01,
|
48
|
-
lowpass=0.1,
|
49
|
-
tr=None,
|
50
|
-
)
|
51
|
-
second_time = time.time() - start_time
|
52
|
-
logger.info(f"ALFF Estimator Second time: {second_time}")
|
53
|
-
assert second_time < (first_time / 1000)
|
54
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
55
|
-
assert n_files == 0 # no files in python
|
56
|
-
|
57
|
-
# Now change a parameter, should compute again, without clearing the
|
58
|
-
# cache
|
59
|
-
start_time = time.time()
|
60
|
-
alff, falff = estimator.fit_transform(
|
61
|
-
use_afni=False,
|
62
|
-
input_data=input["BOLD"],
|
63
|
-
highpass=0.01,
|
64
|
-
lowpass=0.11,
|
65
|
-
tr=None,
|
66
|
-
)
|
67
|
-
third_time = time.time() - start_time
|
68
|
-
logger.info(f"ALFF Estimator Third time: {third_time}")
|
69
|
-
assert third_time > (first_time / 10)
|
70
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
71
|
-
assert n_files == 0 # no files in python
|
72
|
-
|
73
|
-
# Now fit again with the previous params, should be fast
|
74
|
-
start_time = time.time()
|
75
|
-
alff, falff = estimator.fit_transform(
|
76
|
-
use_afni=False,
|
77
|
-
input_data=input["BOLD"],
|
78
|
-
highpass=0.01,
|
79
|
-
lowpass=0.1,
|
80
|
-
tr=None,
|
81
|
-
)
|
82
|
-
fourth = time.time() - start_time
|
83
|
-
logger.info(f"ALFF Estimator Fourth time: {fourth}")
|
84
|
-
assert fourth < (first_time / 1000)
|
85
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
86
|
-
assert n_files == 0 # no files in python
|
87
|
-
|
88
|
-
# Now change the data, it should clear the cache
|
89
|
-
with PartlyCloudyTestingDataGrabber() as dg:
|
90
|
-
input = dg["sub-02"]
|
91
|
-
|
92
|
-
input = DefaultDataReader().fit_transform(input)
|
93
|
-
|
94
|
-
start_time = time.time()
|
95
|
-
alff, falff = estimator.fit_transform(
|
96
|
-
use_afni=False,
|
97
|
-
input_data=input["BOLD"],
|
98
|
-
highpass=0.01,
|
99
|
-
lowpass=0.1,
|
100
|
-
tr=None,
|
101
|
-
)
|
102
|
-
fifth = time.time() - start_time
|
103
|
-
logger.info(f"ALFF Estimator Fifth time: {fifth}")
|
104
|
-
assert fifth > (first_time / 10)
|
105
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
106
|
-
assert n_files == 0 # no files in python
|
107
|
-
|
108
|
-
|
109
|
-
@pytest.mark.skipif(
|
110
|
-
_check_afni() is False, reason="requires afni to be in PATH"
|
111
|
-
)
|
112
|
-
def test_ALFFEstimator_cache_afni() -> None:
|
113
|
-
"""Test that the cache works properly when using afni."""
|
114
|
-
with PartlyCloudyTestingDataGrabber() as dg:
|
115
|
-
input = dg["sub-01"]
|
116
|
-
|
117
|
-
input = DefaultDataReader().fit_transform(input)
|
118
|
-
|
119
|
-
estimator = ALFFEstimator()
|
120
|
-
start_time = time.time()
|
121
|
-
alff, falff = estimator.fit_transform(
|
122
|
-
use_afni=True,
|
123
|
-
input_data=input["BOLD"],
|
124
|
-
highpass=0.01,
|
125
|
-
lowpass=0.1,
|
126
|
-
tr=None,
|
127
|
-
)
|
128
|
-
first_time = time.time() - start_time
|
129
|
-
logger.info(f"ALFF Estimator First time: {first_time}")
|
130
|
-
assert isinstance(alff, Nifti1Image)
|
131
|
-
assert isinstance(falff, Nifti1Image)
|
132
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
133
|
-
assert n_files == 3 # input + alff + falff
|
134
|
-
|
135
|
-
# Now fit again, should be faster
|
136
|
-
start_time = time.time()
|
137
|
-
alff, falff = estimator.fit_transform(
|
138
|
-
use_afni=True,
|
139
|
-
input_data=input["BOLD"],
|
140
|
-
highpass=0.01,
|
141
|
-
lowpass=0.1,
|
142
|
-
tr=None,
|
143
|
-
)
|
144
|
-
second_time = time.time() - start_time
|
145
|
-
logger.info(f"ALFF Estimator Second time: {second_time}")
|
146
|
-
assert second_time < (first_time / 1000)
|
147
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
148
|
-
assert n_files == 3 # input + alff + falff
|
149
|
-
|
150
|
-
# Now change a parameter, should compute again, without clearing the
|
151
|
-
# cache
|
152
|
-
start_time = time.time()
|
153
|
-
alff, falff = estimator.fit_transform(
|
154
|
-
use_afni=True,
|
155
|
-
input_data=input["BOLD"],
|
156
|
-
highpass=0.01,
|
157
|
-
lowpass=0.11,
|
158
|
-
tr=None,
|
159
|
-
)
|
160
|
-
third_time = time.time() - start_time
|
161
|
-
logger.info(f"ALFF Estimator Third time: {third_time}")
|
162
|
-
assert third_time > (first_time / 10)
|
163
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
164
|
-
assert n_files == 5 # input + 2 * alff + 2 * falff
|
165
|
-
|
166
|
-
# Now fit again with the previous params, should be fast
|
167
|
-
start_time = time.time()
|
168
|
-
alff, falff = estimator.fit_transform(
|
169
|
-
use_afni=True,
|
170
|
-
input_data=input["BOLD"],
|
171
|
-
highpass=0.01,
|
172
|
-
lowpass=0.1,
|
173
|
-
tr=None,
|
174
|
-
)
|
175
|
-
fourth = time.time() - start_time
|
176
|
-
logger.info(f"ALFF Estimator Fourth time: {fourth}")
|
177
|
-
assert fourth < (first_time / 1000)
|
178
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
179
|
-
assert n_files == 5 # input + 2 * alff + 2 * falff
|
180
|
-
|
181
|
-
# Now change the data, it should clear the cache
|
182
|
-
with PartlyCloudyTestingDataGrabber() as dg:
|
183
|
-
input = dg["sub-02"]
|
184
|
-
|
185
|
-
input = DefaultDataReader().fit_transform(input)
|
186
|
-
|
187
|
-
start_time = time.time()
|
188
|
-
alff, falff = estimator.fit_transform(
|
189
|
-
use_afni=True,
|
190
|
-
input_data=input["BOLD"],
|
191
|
-
highpass=0.01,
|
192
|
-
lowpass=0.1,
|
193
|
-
tr=None,
|
194
|
-
)
|
195
|
-
fifth = time.time() - start_time
|
196
|
-
logger.info(f"ALFF Estimator Fifth time: {fifth}")
|
197
|
-
assert fifth > (first_time / 10)
|
198
|
-
n_files = len(list(estimator.temp_dir_path.glob("*")))
|
199
|
-
assert n_files == 3 # input + alff + falff
|
200
|
-
|
201
|
-
|
202
|
-
@pytest.mark.skipif(
|
203
|
-
_check_afni() is False, reason="requires afni to be in PATH"
|
204
|
-
)
|
205
|
-
def test_ALFFEstimator_afni_vs_python() -> None:
|
206
|
-
"""Test that the cache works properly when using afni."""
|
207
|
-
with PartlyCloudyTestingDataGrabber() as dg:
|
208
|
-
input = dg["sub-01"]
|
209
|
-
|
210
|
-
input = DefaultDataReader().fit_transform(input)
|
211
|
-
estimator = ALFFEstimator()
|
212
|
-
|
213
|
-
# Use an arbitrary TR to test the AFNI vs Python implementation
|
214
|
-
afni_alff, afni_falff = estimator.fit_transform(
|
215
|
-
use_afni=True,
|
216
|
-
input_data=input["BOLD"],
|
217
|
-
highpass=0.01,
|
218
|
-
lowpass=0.1,
|
219
|
-
tr=2.5,
|
220
|
-
)
|
221
|
-
|
222
|
-
python_alff, python_falff = estimator.fit_transform(
|
223
|
-
use_afni=False,
|
224
|
-
input_data=input["BOLD"],
|
225
|
-
highpass=0.01,
|
226
|
-
lowpass=0.1,
|
227
|
-
tr=2.5,
|
228
|
-
)
|
229
|
-
|
230
|
-
r, _ = pearsonr(
|
231
|
-
afni_alff.get_fdata().flatten(), python_alff.get_fdata().flatten()
|
232
|
-
)
|
233
|
-
assert r > 0.99
|
234
|
-
|
235
|
-
r, _ = pearsonr(
|
236
|
-
afni_falff.get_fdata().flatten(), python_falff.get_fdata().flatten()
|
237
|
-
)
|
238
|
-
assert r > 0.99
|