gemprf 0.1.1__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.
- gem/__init__.py +10 -0
- gem/analysis/__init__.py +6 -0
- gem/analysis/prf_analysis.py +72 -0
- gem/analysis/prf_r2_variance_explain.py +101 -0
- gem/configs/__init__.py +6 -0
- gem/configs/analysis_configs/analysis_additional_params.h5 +0 -0
- gem/configs/analysis_configs/analysis_config.xml +124 -0
- gem/configs/config_manager.py +174 -0
- gem/configs/default_config/default_config - old before concatenation.xml +141 -0
- gem/configs/default_config/default_config.xml +171 -0
- gem/configs/default_config/new_concatenationDummyTest_config.xml +180 -0
- gem/configs/default_config/new_concatenation_config.xml +180 -0
- gem/configs/gem_xml_utils.py +30 -0
- gem/configs/multiproc_prfspace_config/asus_analysis_config.xml +165 -0
- gem/configs/multiproc_prfspace_config/dgx_analysis_config.xml +165 -0
- gem/data/__init__.py +6 -0
- gem/data/bids_handler.py +208 -0
- gem/data/gem_bids_concatenation_data_info.py +42 -0
- gem/data/gem_bids_individual_run_data_info.py +35 -0
- gem/data/gem_stimulus_file_info.py +16 -0
- gem/data/observed_data.py +42 -0
- gem/fitting/__init__.py +6 -0
- gem/fitting/hpc_coefficient_matrix.py +188 -0
- gem/fitting/hpc_grid_fit.py +184 -0
- gem/fitting/hpc_refine_fit.py +632 -0
- gem/init_setup.py +136 -0
- gem/kernels/gaussian_kernel.cu +208 -0
- gem/kernels/gaussian_kernel.ptx +1408 -0
- gem/model/__init__.py +6 -0
- gem/model/prf_DoG_model.py +32 -0
- gem/model/prf_gaussian_model.py +121 -0
- gem/model/prf_model.py +130 -0
- gem/model/prf_stimulus.py +146 -0
- gem/model/selected_prf_model.py +17 -0
- gem/run/__init__.py +0 -0
- gem/run/run_gem_prf_analysis.py +728 -0
- gem/run_gem.py +42 -0
- gem/signals/__init__.py +6 -0
- gem/signals/hrf_generator.py +64 -0
- gem/signals/orthogonalization_matrix.py +40 -0
- gem/signals/signal_synthesizer.py +281 -0
- gem/space/PRFNeighbours.py +4 -0
- gem/space/PRFSpace.py +569 -0
- gem/space/__init__.py +6 -0
- gem/space/coefficient_matrix.py +43 -0
- gem/tools/__init__.py +6 -0
- gem/tools/json_file_operations.py +43 -0
- gem/utils/__init__.py +6 -0
- gem/utils/gem_gpu_manager.py +107 -0
- gem/utils/gem_h5_file_handler.py +24 -0
- gem/utils/gem_load_estimations.py +89 -0
- gem/utils/gem_write_to_file.py +121 -0
- gem/utils/hpc_cupy_utils.py +149 -0
- gem/utils/logger.py +52 -0
- gemprf-0.1.1.dist-info/METADATA +98 -0
- gemprf-0.1.1.dist-info/RECORD +59 -0
- gemprf-0.1.1.dist-info/WHEEL +5 -0
- gemprf-0.1.1.dist-info/licenses/LICENSE +28 -0
- gemprf-0.1.1.dist-info/top_level.txt +1 -0
gem/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
"@Author : Siddharth Mittal",
|
|
5
|
+
"@Version : 1.0",
|
|
6
|
+
"@Contact : siddharth.mittal@meduniwien.ac.at",
|
|
7
|
+
"@License : (C)Copyright 2024, Medical University of Vienna",
|
|
8
|
+
"@Desc : None",
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import cupy as cp
|
|
14
|
+
from typing import List
|
|
15
|
+
|
|
16
|
+
from gem.model.prf_model import PRFModel
|
|
17
|
+
from gem.model.selected_prf_model import SelectedPRFModel
|
|
18
|
+
from gem.model.prf_stimulus import Stimulus
|
|
19
|
+
from gem.space.PRFSpace import PRFSpace
|
|
20
|
+
from gem.utils.hpc_cupy_utils import HpcUtils as gpu_utils
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PRFAnalysis():
|
|
24
|
+
def __init__(self, prf_space : PRFSpace, stimulus : Stimulus) -> None:
|
|
25
|
+
self.__prf_points = prf_space
|
|
26
|
+
self.__stimulus = stimulus
|
|
27
|
+
self.__model_signals_batches = None
|
|
28
|
+
self.__dervatives_signals_batches_list = None
|
|
29
|
+
self.__error_e = None
|
|
30
|
+
self.__error_de_dtheta_list = None # contains a list of all derivative errors w.r.t each parameter
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def prf_points(self)-> PRFSpace:
|
|
34
|
+
return self.__prf_points
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def stimulus(self)-> Stimulus:
|
|
38
|
+
return self.__stimulus
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def orthonormalized_S_batches(self)-> List[cp.ndarray]:
|
|
42
|
+
return self.__model_signals_batches
|
|
43
|
+
|
|
44
|
+
@orthonormalized_S_batches.setter
|
|
45
|
+
def orthonormalized_S_batches(self, new_value)-> None:
|
|
46
|
+
self.__model_signals_batches = new_value
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def orthonormalized_dS_dtheta_batches_list(self)-> List[List[cp.ndarray]]:
|
|
50
|
+
return self.__dervatives_signals_batches_list
|
|
51
|
+
|
|
52
|
+
@orthonormalized_dS_dtheta_batches_list.setter
|
|
53
|
+
def orthonormalized_dS_dtheta_batches_list(self, new_value)-> None:
|
|
54
|
+
self.__dervatives_signals_batches_list = new_value
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def error_e(self)-> cp.ndarray:
|
|
58
|
+
return self.__error_e
|
|
59
|
+
|
|
60
|
+
@error_e.setter
|
|
61
|
+
def error_e(self, new_value)-> None:
|
|
62
|
+
self.__error_e = new_value
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def error_de_dtheta_list(self)-> List[cp.ndarray]:
|
|
66
|
+
return self.__error_de_dtheta_list
|
|
67
|
+
|
|
68
|
+
@error_de_dtheta_list.setter
|
|
69
|
+
def error_de_dtheta_list(self, new_value)-> None:
|
|
70
|
+
self.__error_de_dtheta_list = new_value
|
|
71
|
+
|
|
72
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import cupy as cp
|
|
3
|
+
from gem.tools.json_file_operations import JsonMgr
|
|
4
|
+
from gem.utils.hpc_cupy_utils import HpcUtils as Utils
|
|
5
|
+
from gem.model import prf_model
|
|
6
|
+
from gem.signals.signal_synthesizer import SignalSynthesizer
|
|
7
|
+
from gem.model.prf_model import GaussianModelParams
|
|
8
|
+
from gem.utils.gem_gpu_manager import GemGpuManager as ggm
|
|
9
|
+
|
|
10
|
+
class R2:
|
|
11
|
+
@classmethod
|
|
12
|
+
def get_r2_new_method_with_epsilon_as_Fx(cls, Y_signals_gpu, O_gpu, epsilon_results_cpu):
|
|
13
|
+
r2 = []
|
|
14
|
+
R_Rt_gpu = cp.eye(O_gpu.shape[0]) - O_gpu
|
|
15
|
+
|
|
16
|
+
num_y_signals = len(epsilon_results_cpu)
|
|
17
|
+
for yIdx in range (num_y_signals):
|
|
18
|
+
y = Y_signals_gpu[:, yIdx]
|
|
19
|
+
yty = y.T @ y
|
|
20
|
+
ystar = O_gpu @ y
|
|
21
|
+
ystarT_ystar_inv = (ystar.T @ ystar) ** (-1)
|
|
22
|
+
e = (float(epsilon_results_cpu[yIdx])) ** 2 #<----------------------------------
|
|
23
|
+
yt_RRt_y = (y.T @ R_Rt_gpu) @ y
|
|
24
|
+
r2_gpu = 1 - ((yty - e - yt_RRt_y) * ystarT_ystar_inv)
|
|
25
|
+
r2.append(float(r2_gpu))
|
|
26
|
+
|
|
27
|
+
return r2
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_r2_num_den_method_with_epsilon_as_yTs(cls, Y_signals_gpu, O_gpu, refined_matching_results, refined_S_cpu):
|
|
31
|
+
with cp.cuda.Device(ggm.get_instance().default_gpu_id):
|
|
32
|
+
numerators_gpu , denominators_gpu = cls.get_r2_numerator_denominator_terms(Y_signals_gpu, O_gpu, refined_matching_results, refined_S_cpu)
|
|
33
|
+
r2_results_gpu = 1 - (numerators_gpu / denominators_gpu)
|
|
34
|
+
|
|
35
|
+
return cp.asnumpy(r2_results_gpu)
|
|
36
|
+
|
|
37
|
+
@classmethod # R2 numerator = (yty - e - yt_RRt_y)
|
|
38
|
+
def get_r2_numerator_denominator_terms(cls, Y_signals_gpu, O_gpu, refined_matching_results, refined_signals_cpu: np.ndarray):
|
|
39
|
+
with cp.cuda.Device(ggm.get_instance().default_gpu_id):
|
|
40
|
+
refined_signals_gpu = cp.asarray(refined_signals_cpu)
|
|
41
|
+
num_y_signals = Y_signals_gpu.shape[1]
|
|
42
|
+
|
|
43
|
+
R_Rt_gpu = cp.eye(O_gpu.shape[0]) - O_gpu
|
|
44
|
+
|
|
45
|
+
# Compute yty for each signal
|
|
46
|
+
yty = cp.sum(Y_signals_gpu ** 2, axis=0)
|
|
47
|
+
|
|
48
|
+
# Compute ystar and ystarT_ystar
|
|
49
|
+
ystar = O_gpu @ Y_signals_gpu
|
|
50
|
+
ystarT_ystar = cp.sum(ystar ** 2, axis=0)
|
|
51
|
+
|
|
52
|
+
# Check for NaN refined results
|
|
53
|
+
nan_mask = np.isnan(refined_matching_results).all(axis=1)
|
|
54
|
+
nan_mask = cp.asarray(nan_mask)
|
|
55
|
+
|
|
56
|
+
# Check for zero signals
|
|
57
|
+
s = refined_signals_gpu
|
|
58
|
+
zero_mask = np.all(refined_signals_cpu == 0, axis=1)
|
|
59
|
+
zero_mask = cp.asarray(zero_mask)
|
|
60
|
+
|
|
61
|
+
# Compute s_star
|
|
62
|
+
s_star = O_gpu @ s.T # shape: [timepoints, num_signals]
|
|
63
|
+
|
|
64
|
+
# Normalize s_star to get s_prime
|
|
65
|
+
s_prime = s_star * (cp.sum(s_star ** 2, axis=0) ** (-0.5))
|
|
66
|
+
|
|
67
|
+
# Compute error-term e = (y.T @ s_prime)^2
|
|
68
|
+
e = cp.sum(Y_signals_gpu * s_prime, axis=0) ** 2 #<----------------------------------
|
|
69
|
+
|
|
70
|
+
# Compute yt_RRt_y
|
|
71
|
+
# # yt_RRt_y = cp.sum(Y_signals_gpu * (R_Rt_gpu @ Y_signals_gpu), axis=0)
|
|
72
|
+
yt_R = Y_signals_gpu * (R_Rt_gpu @ Y_signals_gpu)
|
|
73
|
+
yt_RRt_y = cp.sum(yt_R, axis=0)
|
|
74
|
+
|
|
75
|
+
# Compute final numerator and denominator
|
|
76
|
+
num = yty - e - yt_RRt_y
|
|
77
|
+
den = ystarT_ystar
|
|
78
|
+
|
|
79
|
+
# Apply masks for nan/zero
|
|
80
|
+
num = cp.where(nan_mask, 2.0, num)
|
|
81
|
+
num = cp.where(zero_mask & ~nan_mask, 3.0, num)
|
|
82
|
+
den = cp.where(nan_mask | zero_mask, 1.0, den)
|
|
83
|
+
|
|
84
|
+
return num, den
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def format_in_json_format(cls, r2_results, refined_matching_results, refined_signal_timecourses, refined_signals_present = True):
|
|
88
|
+
json_data_results_with_r2 = []
|
|
89
|
+
|
|
90
|
+
# default r2 value
|
|
91
|
+
for i in range((len(refined_matching_results))):
|
|
92
|
+
r2 = float(r2_results[i])
|
|
93
|
+
muX, muY, sigma = refined_matching_results[i]
|
|
94
|
+
if refined_signals_present:
|
|
95
|
+
refined_signal_timecourse = refined_signal_timecourses[i]
|
|
96
|
+
else:
|
|
97
|
+
refined_signal_timecourse = np.array([None])
|
|
98
|
+
json_entry = JsonMgr.args2jsonEntry(muX, muY, sigma, r2, refined_signal_timecourse)
|
|
99
|
+
json_data_results_with_r2.append(json_entry)
|
|
100
|
+
|
|
101
|
+
return json_data_results_with_r2
|
|
Binary file
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<!-- XML representation of JSON data -->
|
|
2
|
+
<!--
|
|
3
|
+
"@Author : Siddharth Mittal",
|
|
4
|
+
"@Version : 1.0",
|
|
5
|
+
"@Contact : siddharth.mittal@meduniwien.ac.at",
|
|
6
|
+
"@License : (C)Copyright 2024, Medical University of Vienna"
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
<!-- NOTE for Analysis: For DUMMY/Analsis, set correct Visual field at two positions, one for the <stimulus> and another for <search_space>-->
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
<root>
|
|
13
|
+
<!-- Path information -->
|
|
14
|
+
<path_to_append>.../</path_to_append>
|
|
15
|
+
<refine_fitting enable="True" refinefit_on_gpu="True"/> <!-- Execute refine fitting on GPU if sufficient GPU memory is available -->
|
|
16
|
+
|
|
17
|
+
<!-- Stimulus data -->
|
|
18
|
+
<stimulus>
|
|
19
|
+
<!-- File path for stimulus -->
|
|
20
|
+
<directory>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim24/BIDS/derivatives/prfprepare/analysis-01/sub-001/stimuli</directory>
|
|
21
|
+
<visual_field>9</visual_field> <!-- Visual field Radius, NOTE: Use "10" for simulated data, else "9" -->
|
|
22
|
+
<width>101</width>
|
|
23
|
+
<height>101</height>
|
|
24
|
+
<binarization enable="False" threshold="0"/> <!-- stimulus will be binarized if enabled, all values above threshold will be set to 1 and below to 0 -->
|
|
25
|
+
|
|
26
|
+
<!-- Compute model signals with high-res stimulus then downsample: "num_frames_downsampled" = your fMRI timecourse length, "slice_time_ref" for details see https://www.alivelearn.net/?p=1037 -->
|
|
27
|
+
<high_temporal_resolution enable="false" num_frames_downsampled="240" slice_time_ref="0.5"/>
|
|
28
|
+
</stimulus>
|
|
29
|
+
|
|
30
|
+
<!-- Input Data -->
|
|
31
|
+
<input_datasrc>
|
|
32
|
+
<BIDS enable="True" run_type="individual"> <!-- Set to "True" if the input data is organized in BIDS format, select "individual" or "concatenated" run type -->
|
|
33
|
+
<basepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim24/BIDS</basepath>
|
|
34
|
+
<append_to_basepath>derivatives, prfprepare</append_to_basepath> <!-- specify if the "analysis-xx" -->
|
|
35
|
+
<!-- <basepath>D:/results/fmri/generic/</basepath> -->
|
|
36
|
+
|
|
37
|
+
<!-- Directory name for results; backups created with timestamp if overwrite="True" -->
|
|
38
|
+
<results_anaylsis_id overwrite="False">GEMpRF</results_anaylsis_id>
|
|
39
|
+
|
|
40
|
+
<!--
|
|
41
|
+
NOTE: Case sensitive, provide the exact values!!!
|
|
42
|
+
For the analysis, sub, ses, task, run, hemi,
|
|
43
|
+
either provide comma separated values or specify "all" (to go through every available analysis)
|
|
44
|
+
Sample: <analysis>01, 02</analysis> or <analysis>all</analysis>
|
|
45
|
+
-->
|
|
46
|
+
<analysis>01</analysis>
|
|
47
|
+
<sub>001, 002</sub>
|
|
48
|
+
<hemi>all</hemi>
|
|
49
|
+
|
|
50
|
+
<!--INDIVIDUAL Task/stimulus Analysis-->
|
|
51
|
+
<individual>
|
|
52
|
+
<task>bar</task> <!--name of the stimulus, this will be used to find the file in the stimulus directory, ONLY one value is allowed-->
|
|
53
|
+
<ses>001nn</ses> <!-- comma separated values or specify "all" -->
|
|
54
|
+
<run>01, 02</run> <!-- comma separated values or specify "all" -->
|
|
55
|
+
</individual>
|
|
56
|
+
|
|
57
|
+
<!--CONCATENATED Analysis-->
|
|
58
|
+
<concatenated>
|
|
59
|
+
<concatenate_item>
|
|
60
|
+
<ses>001</ses> <!-- ONLY one value is allowed-->
|
|
61
|
+
<task>bar</task> <!-- ONLY one value is allowed-->
|
|
62
|
+
<run>02</run> <!-- ONLY one value is allowed-->
|
|
63
|
+
</concatenate_item>
|
|
64
|
+
<concatenate_item>
|
|
65
|
+
<ses>001</ses>
|
|
66
|
+
<task>bar</task>
|
|
67
|
+
<run>04</run>
|
|
68
|
+
</concatenate_item>
|
|
69
|
+
</concatenated>
|
|
70
|
+
</BIDS>
|
|
71
|
+
|
|
72
|
+
<!--OR, make Bids False and specify directly single/multiple filepaths -->
|
|
73
|
+
<fixed_paths>
|
|
74
|
+
<measured_data_filepath> <!-- Measured input data filepaths -->
|
|
75
|
+
<filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/tests/gem_generic_test/BIDS/sub-001/ses-3n2/func/sub-001_ses-3n2_task-prf_acq-normal_run-01_bold.nii.gz</filepath>
|
|
76
|
+
<filepath>dummy////sub-001/ses-3n2/func/sub-001_ses-3n2_task-prf_acq-normal_run-01_bold.nii.gz</filepath>
|
|
77
|
+
</measured_data_filepath>
|
|
78
|
+
<results> <!-- Results filepaths realted -->
|
|
79
|
+
<basepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/tests/gem_generic_test/derivatives/prfanalyze-gem/analysis-01/sub-001/ses-3n2/</basepath> <!-- Base path for results -->
|
|
80
|
+
<custom_filename_postfix>_[gem-new_data-simulated_v1_151x151x8_refine-validation]</custom_filename_postfix> <!-- Custom filename postfix -->
|
|
81
|
+
<prepend_date>True</prepend_date> <!-- Flag to prepend date -->
|
|
82
|
+
</results>
|
|
83
|
+
</fixed_paths>
|
|
84
|
+
</input_datasrc>
|
|
85
|
+
|
|
86
|
+
<!-- Analysis Model -->
|
|
87
|
+
<pRF_model>
|
|
88
|
+
<model>2d_gaussian</model> <!-- 2d_gauss, (DoG, CSS not avaiable at the moment) -->
|
|
89
|
+
</pRF_model>
|
|
90
|
+
|
|
91
|
+
<!-- Measured data section -->
|
|
92
|
+
<measured_data>
|
|
93
|
+
<width>101</width>
|
|
94
|
+
<height>101</height>
|
|
95
|
+
<batches>200</batches>
|
|
96
|
+
</measured_data>
|
|
97
|
+
|
|
98
|
+
<!-- GPU configuration -->
|
|
99
|
+
<gpu>
|
|
100
|
+
<!-- Default GPU -->
|
|
101
|
+
<default_gpu>0</default_gpu>
|
|
102
|
+
<!-- Additional available GPUs -->
|
|
103
|
+
<additional_available_gpus>
|
|
104
|
+
<gpu>1</gpu>
|
|
105
|
+
<gpu>2</gpu>
|
|
106
|
+
<gpu>3</gpu>
|
|
107
|
+
</additional_available_gpus>
|
|
108
|
+
</gpu>
|
|
109
|
+
|
|
110
|
+
<!-- Search space information -->
|
|
111
|
+
<search_space write_debug_info="false">
|
|
112
|
+
<!-- if the user wants to use custom HRF, pRF sizes or Spatial grid, provide the filepath to the H5 file and the keys for each parameter -->
|
|
113
|
+
<optional_analysis_params enable="False" filepath="DIR_PATH/analysis_additional_params.h5">
|
|
114
|
+
<hrf use_from_file="True" key="analysis_params/hrf_values"/>
|
|
115
|
+
<sigmas use_from_file="True" key="analysis_params/sigmas"/>
|
|
116
|
+
<spatial_grid_xy use_from_file="True" key="analysis_params/spatial_grid_xy"/>
|
|
117
|
+
</optional_analysis_params>
|
|
118
|
+
|
|
119
|
+
<!-- if the user wants to generate a rectilinear grid within the GEM-pRF program, specify the default values below. Please NOTE that these values will ONLY be used if their corresponding optional_analysis_params = False -->
|
|
120
|
+
<default_hrf t="(0, 45)" TR="auto" peak_delay="6.16" under_shoot_delay="12.0" peak_disp="0.85" under_disp="0.82" peak_to_undershoot="2.15" normalize="true"/> <!-- SPM parameters, "t" is fed to np.arange(start, stop, step), "step" represents the TR value, set it to a lower value in case of high temporal stimulus. If TR = "auto" then, its value will be read from the stimulus file -->
|
|
121
|
+
<default_spatial_grid visual_field_radius="13.5" num_horizontal_prfs="151" num_vertical_prfs="151"/>
|
|
122
|
+
<default_sigmas num_sigmas="16" min_sigma="0.5" max_sigma="5"/>
|
|
123
|
+
</search_space>
|
|
124
|
+
</root>
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
"@Author : Siddharth Mittal",
|
|
4
|
+
"@Version : 1.0",
|
|
5
|
+
"@Contact : siddharth.mittal@meduniwien.ac.at",
|
|
6
|
+
"@License : (C)Copyright 2023-2025, Medical University of Vienna",
|
|
7
|
+
"@Desc : None",
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
from enum import Enum
|
|
14
|
+
import xmltodict
|
|
15
|
+
|
|
16
|
+
class RunType(Enum):
|
|
17
|
+
ANALYSIS = 1
|
|
18
|
+
SIMULATION = 2
|
|
19
|
+
|
|
20
|
+
class ConfigurationWrapper:
|
|
21
|
+
config_data = None
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def __read_xml_file(cls, run_type: RunType = None, config_filepath : str = None):
|
|
25
|
+
script_dir = os.path.dirname(os.path.realpath(__file__)) # Get the directory of the script
|
|
26
|
+
|
|
27
|
+
if config_filepath is not None:
|
|
28
|
+
config_file_path = config_filepath
|
|
29
|
+
else:
|
|
30
|
+
config_file_path = os.path.join(script_dir, "default_config", "default_config.xml") # default config file path
|
|
31
|
+
|
|
32
|
+
if os.path.isfile(config_file_path):
|
|
33
|
+
with open(config_file_path) as xml_file:
|
|
34
|
+
config_data_dict = xmltodict.parse(xml_file.read())
|
|
35
|
+
xml_file.close()
|
|
36
|
+
|
|
37
|
+
# XML to JSON data conversion
|
|
38
|
+
str_config_data = json.dumps(config_data_dict)
|
|
39
|
+
json_config_data = json.loads(str_config_data)
|
|
40
|
+
cls.config_data = json_config_data.get("root")
|
|
41
|
+
else:
|
|
42
|
+
print("Config file not found. Make sure it's located in the same directory as the script.")
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def parse_attrs(cls, node, types=None):
|
|
46
|
+
"""Extracts attributes (removes '@'), converts values using types dict if provided."""
|
|
47
|
+
if node is None:
|
|
48
|
+
return {}
|
|
49
|
+
types = types or {}
|
|
50
|
+
return {
|
|
51
|
+
k.lstrip("@"): types.get(k.lstrip("@"), str)(v)
|
|
52
|
+
for k, v in node.items()
|
|
53
|
+
if k.startswith("@")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def load_configuration(cls, run_type: RunType = None, config_filepath : str = None):
|
|
58
|
+
# if cls.config_data is None: #NOTE: This sometimes lead to unpredictable results, to be on safe side, always load the configuration
|
|
59
|
+
cls.__read_xml_file(run_type, config_filepath)
|
|
60
|
+
# # cls._read_json_file(run_type)
|
|
61
|
+
|
|
62
|
+
# path_to_append
|
|
63
|
+
cls.path_to_append = cls.config_data.get("path_to_append")
|
|
64
|
+
|
|
65
|
+
# Input data source
|
|
66
|
+
# ...BIDS
|
|
67
|
+
cls.bids = (cls.config_data.get("input_datasrc")).get("BIDS") # sample: {'@enable': 'True', 'basepath': 'D:/results/fmri/generic/', 'analysis': None, 'sub': None, 'ses': None}
|
|
68
|
+
# ...Non-BIDS i.e. direct filepaths
|
|
69
|
+
cls.fixed_paths = (cls.config_data.get("input_datasrc")).get("fixed_paths")
|
|
70
|
+
|
|
71
|
+
# result_file_path
|
|
72
|
+
cls.result_file_path = cls.config_data.get("result_file_path")
|
|
73
|
+
|
|
74
|
+
# results
|
|
75
|
+
#cls.results = cls.config_data.get("results")
|
|
76
|
+
cls.results = (cls.config_data.get("input_datasrc")).get("fixed_paths").get("results")
|
|
77
|
+
|
|
78
|
+
# PRF Model
|
|
79
|
+
cls.pRF_model_details = cls.config_data.get("pRF_model")
|
|
80
|
+
|
|
81
|
+
# Stimulus
|
|
82
|
+
cls.stimulus = cls.config_data.get("stimulus")
|
|
83
|
+
# ...subnodes of stimulus
|
|
84
|
+
high_temp_res_node = cls.stimulus.get("high_temporal_resolution", {})
|
|
85
|
+
cls.stimulus_high_temporal_resolution = cls.parse_attrs(high_temp_res_node, {
|
|
86
|
+
"enable": lambda v: v.lower() == "true",
|
|
87
|
+
"num_frames_downsampled": int,
|
|
88
|
+
"slice_time_ref": float
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
# Measure Data
|
|
92
|
+
cls.measured_data = cls.config_data.get("measured_data")
|
|
93
|
+
|
|
94
|
+
# Search Space
|
|
95
|
+
cls.search_space = cls.config_data.get("search_space")
|
|
96
|
+
|
|
97
|
+
# ...whether to write debug info or not
|
|
98
|
+
search_space_attrs = cls.parse_attrs(cls.search_space, {
|
|
99
|
+
"write_debug_info": lambda v: v.lower() == "true"
|
|
100
|
+
})
|
|
101
|
+
cls.write_debug_info = search_space_attrs.get("write_debug_info", False)
|
|
102
|
+
|
|
103
|
+
# Concatenated runs
|
|
104
|
+
cls.concatenated_runs = cls.config_data.get("concatenated_runs")
|
|
105
|
+
|
|
106
|
+
# Test Space
|
|
107
|
+
cls.test_space = cls.config_data.get("test_space")
|
|
108
|
+
|
|
109
|
+
# Noise
|
|
110
|
+
cls.noise = cls.config_data.get("noise")
|
|
111
|
+
|
|
112
|
+
# GPU
|
|
113
|
+
cls.gpu = cls.config_data.get("gpu")
|
|
114
|
+
|
|
115
|
+
# Enable/disable Refine fitting
|
|
116
|
+
refine_fitting_node_params = cls.parse_attrs(cls.config_data.get("refine_fitting", {}), {
|
|
117
|
+
"enable": lambda v: v.lower() == "true",
|
|
118
|
+
"refinefit_on_gpu": lambda v: v.lower() == "true"
|
|
119
|
+
})
|
|
120
|
+
cls.refine_fitting_enabled = refine_fitting_node_params['enable']
|
|
121
|
+
cls.is_refinefit_on_gpu = refine_fitting_node_params['refinefit_on_gpu']
|
|
122
|
+
|
|
123
|
+
# Optional analysis params
|
|
124
|
+
opt_node = cls.search_space.get("optional_analysis_params", {})
|
|
125
|
+
cls.optional_analysis_params = cls.parse_attrs(opt_node, {
|
|
126
|
+
"enable": lambda v: v.lower() == "true",
|
|
127
|
+
"filepath": str
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
# ...subnodes of optional_analysis_params
|
|
131
|
+
for key in ["hrf", "sigmas", "spatial_grid_xy"]:
|
|
132
|
+
subnode = opt_node.get(key, {}) # <--- get from original XML dict
|
|
133
|
+
cls.optional_analysis_params[key] = cls.parse_attrs(subnode, {
|
|
134
|
+
"use_from_file": lambda v: v.lower() == "true",
|
|
135
|
+
"key": str
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
# default spatial grid
|
|
139
|
+
cls.default_spatial_grid = cls.parse_attrs(cls.search_space.get("default_spatial_grid"), {
|
|
140
|
+
"visual_field_radius": float,
|
|
141
|
+
"num_horizontal_prfs": int,
|
|
142
|
+
"num_vertical_prfs": int
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
# default sigmas
|
|
146
|
+
cls.default_sigmas = cls.parse_attrs(cls.search_space.get("default_sigmas"), {
|
|
147
|
+
"num_sigmas": int,
|
|
148
|
+
"min_sigma": float,
|
|
149
|
+
"max_sigma": float
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
# default hrf
|
|
153
|
+
cls.default_hrf = cls.parse_attrs(cls.search_space.get("default_hrf"), {
|
|
154
|
+
"t": lambda v: tuple(map(float, v.strip("()").split(","))), # expects a string like "(0, 45)"
|
|
155
|
+
"TR": lambda v: float(v) if v.replace('.', '', 1).isdigit() else None,
|
|
156
|
+
"peak_delay": float,
|
|
157
|
+
"under_shoot_delay": float,
|
|
158
|
+
"peak_disp": float,
|
|
159
|
+
"under_disp": float,
|
|
160
|
+
"peak_to_undershoot": float,
|
|
161
|
+
"normalize": lambda v: v.lower() == "true"
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
print("Successfully read the config file. ")
|
|
165
|
+
if cls.optional_analysis_params['enable']:
|
|
166
|
+
print(f"Analysis params from file: {', '.join(k for k,v in cls.optional_analysis_params.items() if isinstance(v, dict) and v.get('use_from_file', False)) or 'None'}")
|
|
167
|
+
|
|
168
|
+
# Usage:
|
|
169
|
+
if __name__ == "__main__":
|
|
170
|
+
ConfigurationWrapper.load_configuration(RunType.ANALYSIS)
|
|
171
|
+
|
|
172
|
+
# Access the configuration parameters using the class itself
|
|
173
|
+
print(f"Visual Field Size: {ConfigurationWrapper.path_to_appended}")
|
|
174
|
+
print(f"Stimulus: {ConfigurationWrapper.stimulus}")
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<!-- XML representation of JSON data -->
|
|
2
|
+
<!--
|
|
3
|
+
"@Author : Siddharth Mittal",
|
|
4
|
+
"@Version : 1.0",
|
|
5
|
+
"@Contact : siddharth.mittal@meduniwien.ac.at",
|
|
6
|
+
"@License : (C)Copyright 2024, Medical University of Vienna"
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
<!-- NOTE for Analysis: For DUMMY/Analsis, set correct Visual field at two positions, one for the <stimulus> and another for <search_space>-->
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
<root>
|
|
13
|
+
<!-- Path information -->
|
|
14
|
+
<path_to_append>/ceph/mri.meduniwien.ac.at/departments/physics/fmrilab/home/smittal/dgx-versions/fmri/</path_to_append>
|
|
15
|
+
|
|
16
|
+
<!-- Input Data -->
|
|
17
|
+
<input_datasrc>
|
|
18
|
+
<!-- either spcify the BIDS info and set enable=True -->
|
|
19
|
+
<BIDS enable="False"> <!-- Flag to indicate BIDS format -->
|
|
20
|
+
<basepath>D:/results/gem-paper-simulated-data/analysis/08/BIDS/</basepath>
|
|
21
|
+
<append_to_basepath>derivatives, prfsynth</append_to_basepath> <!-- specify if the "analysis-xx" -->
|
|
22
|
+
<!-- <basepath>D:/results/fmri/generic/</basepath> -->
|
|
23
|
+
|
|
24
|
+
<!--
|
|
25
|
+
NOTE: Case sensitive, provide the exact values!!!
|
|
26
|
+
For the analysis, sub, ses, task, run, hemi,
|
|
27
|
+
either provide comma separated values or specify "all" (to go through every available analysis)
|
|
28
|
+
Sample: <analysis>01, 02</analysis> or <analysis>all</analysis>
|
|
29
|
+
-->
|
|
30
|
+
<analysis>08</analysis>
|
|
31
|
+
<sub>HighNoise</sub>
|
|
32
|
+
<ses>0n0, 3n3, 6n6</ses>
|
|
33
|
+
<task>prf</task>
|
|
34
|
+
<run>all</run>
|
|
35
|
+
<hemi>all</hemi>
|
|
36
|
+
<results_anaylsis_id>01</results_anaylsis_id> <!-- analysis id to save the results -->
|
|
37
|
+
</BIDS>
|
|
38
|
+
|
|
39
|
+
<!--OR, make Bids False and specify directly single/multiple filepaths -->
|
|
40
|
+
<fixed_paths>
|
|
41
|
+
<measured_data_filepath> <!-- Measured input data filepaths -->
|
|
42
|
+
<filepath>Y:\data\tests\gem-paper-simulated-data\analysis\04\BIDS\derivatives\prfsynth\analysis-04\sub-001\ses-3n2\func\sub-001_ses-3n2_task-prf_acq-normal_run-01_bold.nii.gz</filepath>
|
|
43
|
+
<filepath>...dummy_filepath.....</filepath>
|
|
44
|
+
</measured_data_filepath>
|
|
45
|
+
<results> <!-- Results filepaths realted -->
|
|
46
|
+
<basepath>D:\results\fmri/generic/</basepath> <!-- Base path for results -->
|
|
47
|
+
<custom_filename_postfix>_[gem-benchmark_51x51x8]</custom_filename_postfix> <!-- Custom filename postfix -->
|
|
48
|
+
<prepend_date>True</prepend_date> <!-- Flag to prepend date -->
|
|
49
|
+
</results>
|
|
50
|
+
</fixed_paths>
|
|
51
|
+
</input_datasrc>
|
|
52
|
+
|
|
53
|
+
<!-- Analysis Model -->
|
|
54
|
+
<pRF_model_details>
|
|
55
|
+
<model>2d_gaussian</model> <!-- 2d_gauss, DoG, CSS -->
|
|
56
|
+
</pRF_model_details>
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
<!-- Stimulus data -->
|
|
61
|
+
<stimulus>
|
|
62
|
+
<!-- File path for stimulus -->
|
|
63
|
+
<filepath>Y:\data\tests\gem-paper-simulated-data\analysis\04\BIDS\stimuli\sub-001_ses-3n2_task-prf_apertures.nii.gz</filepath>
|
|
64
|
+
<visual_field>10</visual_field> <!-- Visual field Radius, NOTE: Use "10" for simulated data, else "9" -->
|
|
65
|
+
<width>101</width>
|
|
66
|
+
<height>101</height>
|
|
67
|
+
</stimulus>
|
|
68
|
+
|
|
69
|
+
<!-- Measured data section -->
|
|
70
|
+
<measured_data>
|
|
71
|
+
<width>101</width>
|
|
72
|
+
<height>101</height>
|
|
73
|
+
<batches>16</batches>
|
|
74
|
+
</measured_data>
|
|
75
|
+
|
|
76
|
+
<!-- GPU configuration -->
|
|
77
|
+
<gpu>
|
|
78
|
+
<!-- Default GPU -->
|
|
79
|
+
<default_gpu>0</default_gpu>
|
|
80
|
+
<!-- Additional available GPUs -->
|
|
81
|
+
<additional_available_gpus>
|
|
82
|
+
<gpu>0</gpu>
|
|
83
|
+
<gpu>1</gpu>
|
|
84
|
+
<gpu>3</gpu>
|
|
85
|
+
</additional_available_gpus>
|
|
86
|
+
</gpu>
|
|
87
|
+
|
|
88
|
+
<!-- Search space information -->
|
|
89
|
+
<search_space>
|
|
90
|
+
<visual_field>13.5</visual_field> <!-- NOTE: use "9" for analysis with DUMMY signal because the signal is generated using maxEcc=9 ....and the visual field is 13.5 degrees for NORMAL analysis.-->
|
|
91
|
+
<nRows>51</nRows>
|
|
92
|
+
<nCols>51</nCols>
|
|
93
|
+
<min_sigma>0.5</min_sigma>
|
|
94
|
+
<max_sigma>5</max_sigma>
|
|
95
|
+
<nSigma>8</nSigma>
|
|
96
|
+
</search_space>
|
|
97
|
+
|
|
98
|
+
<!-- Test space information -->
|
|
99
|
+
<test_space>
|
|
100
|
+
<nRows>11</nRows>
|
|
101
|
+
<nCols>11</nCols>
|
|
102
|
+
<min_sigma>0.5</min_sigma>
|
|
103
|
+
<max_sigma>4</max_sigma>
|
|
104
|
+
<nSigma>1</nSigma>
|
|
105
|
+
</test_space>
|
|
106
|
+
|
|
107
|
+
<!-- Concatenated runs information -->
|
|
108
|
+
<concatenated_runs>
|
|
109
|
+
<enable>True</enable>
|
|
110
|
+
<num_runs>2</num_runs>
|
|
111
|
+
<height>101</height>
|
|
112
|
+
<batches>4</batches>
|
|
113
|
+
|
|
114
|
+
<!-- BIDS run information -->
|
|
115
|
+
<bids_runs>
|
|
116
|
+
<run>01</run>
|
|
117
|
+
<run>02</run>
|
|
118
|
+
</bids_runs>
|
|
119
|
+
<!-- BIDS task information -->
|
|
120
|
+
<bids_tasks>
|
|
121
|
+
<task>bar</task>
|
|
122
|
+
<task>ring</task>
|
|
123
|
+
</bids_tasks>
|
|
124
|
+
<!-- Stimulus file paths -->
|
|
125
|
+
<stimulus>
|
|
126
|
+
<filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim23/derivatives/prfprepare/analysis-03/sub-sidtest/stimuli/task-bar_apertures.nii.gz</filepath>
|
|
127
|
+
<filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim23/derivatives/prfprepare/analysis-03/sub-sidtest/stimuli/task-bar_apertures.nii.gz</filepath>
|
|
128
|
+
</stimulus>
|
|
129
|
+
<!-- Measured data file paths -->
|
|
130
|
+
<measured_data>
|
|
131
|
+
<filepath>/ceph/mri.meduniwien.ac.at/projects/physics/fmri/data/stimsim23/derivatives/prfprepare/analysis-03/sub-sidtest/ses-001/func/sub-sidtest_ses-001_task-bar_run-01_hemi-L_bold.nii.gz</filepath>
|
|
132
|
+
</measured_data>
|
|
133
|
+
</concatenated_runs>
|
|
134
|
+
|
|
135
|
+
<!-- Noise information -->
|
|
136
|
+
<noise>
|
|
137
|
+
<level>0.1</level>
|
|
138
|
+
<model>gaussian</model>
|
|
139
|
+
<synthesis_ratio>1</synthesis_ratio> <!-- number of noisy signals per pRF location -->
|
|
140
|
+
</noise>
|
|
141
|
+
</root>
|