new preferences and bug fixes
This commit is contained in:
@@ -182,6 +182,10 @@ IQR: float
|
||||
WAVELET_TYPE: str
|
||||
WAVELET_LEVEL: int
|
||||
|
||||
OVERRIDE_PPF: bool
|
||||
PPF_LOWER_WAVELENGTH: float
|
||||
PPF_UPPER_WAVELENGTH: float
|
||||
|
||||
ENHANCE_NEGATIVE_CORRELATION: bool
|
||||
|
||||
FILTER: bool
|
||||
@@ -214,12 +218,10 @@ N_JOBS: int
|
||||
|
||||
JSON_LOCATION: str
|
||||
|
||||
TIME_WINDOW_START: int
|
||||
TIME_WINDOW_END: int
|
||||
MAX_WORKERS: int
|
||||
VERBOSITY: bool
|
||||
|
||||
AGE: int = 25 # Assume 25 if not set from the GUI. This will result in a reasonable PPF
|
||||
AGE: int = 25 # Assume 25 if not set from the GUI. This will result in a reasonable PPF if calculated dynamically
|
||||
GENDER: str = ""
|
||||
GROUP: str = "Default"
|
||||
|
||||
@@ -257,8 +259,6 @@ REQUIRED_KEYS: dict[str, Any] = {
|
||||
|
||||
|
||||
"REMOVE_EVENTS": list,
|
||||
"TIME_WINDOW_START": int,
|
||||
"TIME_WINDOW_END": int,
|
||||
"L_FREQ": float,
|
||||
"H_FREQ": float,
|
||||
|
||||
@@ -870,71 +870,6 @@ def calculate_scalp_coupling(data, l_freq: float = 0.7, h_freq: float = 1.5):
|
||||
|
||||
|
||||
|
||||
def calculate_signal_noise_ratio(data):
|
||||
"""
|
||||
Calculates the signal-to-noise ratio (SNR) for each channel and identifies those below a defined threshold.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : BaseRaw
|
||||
The loaded data object to process.
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[list[str], Figure]
|
||||
- list[str]: A list of channel names that fall below the SNR threshold and are considered bad.
|
||||
- Figure: A matplotlib Figure showing the channels' SNR values.
|
||||
"""
|
||||
|
||||
print("Calculating signal to noise ratio...")
|
||||
|
||||
# Compute the signal-to-noise ratio values
|
||||
print("Computing the signal to noise power...")
|
||||
signal_band=(0.01, 0.5)
|
||||
noise_band=(1.0, 10.0)
|
||||
data_signal = data.copy().filter(*signal_band, verbose=False) #type: ignore
|
||||
data_noise = data.copy().filter(*noise_band, verbose=False) #type: ignore
|
||||
signal_power = np.mean(data_signal.get_data()**2, axis=1) #type: ignore
|
||||
noise_power = np.mean(data_noise.get_data()**2, axis=1) #type: ignore
|
||||
|
||||
# Calculate the snr using the standard formula for dB
|
||||
snr = 10 * np.log10(signal_power / (noise_power + np.finfo(float).eps))
|
||||
|
||||
# TODO: Understand what this does
|
||||
groups: dict[str, list[str]] = {}
|
||||
for ch in getattr(data, "ch_names"):
|
||||
# Look for the space in the channel names and remove the characters after
|
||||
# This is so we can get both oxy and deoxy to remove, as they will have the same source and detector
|
||||
base = ch.rsplit(' ', 1)[0]
|
||||
groups.setdefault(base, []).append(ch) # type: ignore
|
||||
|
||||
# If any of the channels do not meet our threshold, they will get inserted into the bad_channels set
|
||||
bad_channels: set[str] = set()
|
||||
for base, ch_list in groups.items():
|
||||
if any(s < SNR_THRESHOLD for s, ch in zip(snr, getattr(data, "ch_names")) if ch in ch_list):
|
||||
bad_channels.update(ch_list)
|
||||
|
||||
# Design and create the figure
|
||||
print("Creating the figure...")
|
||||
snr_fig, ax = plt.subplots(figsize=(12, 4), layout="constrained") # type: ignore
|
||||
colors = [(0/20, 'red'), (SNR_THRESHOLD/20, 'red'), ((SNR_THRESHOLD+.5)/20, 'yellow'), ((SNR_THRESHOLD+1)/20, 'green'), (20/20, 'green')]
|
||||
cmap = LinearSegmentedColormap.from_list('custom_snr_cmap', colors)
|
||||
norm = mcolors.Normalize(vmin=0, vmax=20)
|
||||
scatter = ax.scatter(range(len(snr)), snr, c=snr, cmap=cmap, alpha=0.8, s=100, norm=norm) # type: ignore
|
||||
ax.set(xlabel="Channel Number", ylabel="Signal-to-Noise Ratio (dB)", xlim=[0, len(snr)], ylim=[0, 20])
|
||||
ax.axhline(SNR_THRESHOLD, color='black', linestyle='--', alpha=0.3, linewidth=1) # type: ignore
|
||||
cbar = snr_fig.colorbar(scatter, ax=ax, label="SNR Thresholds (dB)") # type: ignore
|
||||
cbar.set_ticks([0, SNR_THRESHOLD, SNR_THRESHOLD+1, 20]) # type: ignore
|
||||
cbar.set_ticklabels(['0', str(SNR_THRESHOLD), str(SNR_THRESHOLD+1), '20']) # type: ignore
|
||||
|
||||
plt.close()
|
||||
|
||||
print("Successfully calculated signal to noise ratio.")
|
||||
|
||||
return list(bad_channels), snr_fig
|
||||
|
||||
|
||||
|
||||
def build_fnirs_adjacency(raw, threshold_meters=0.03):
|
||||
"""Build an adjacency dictionary for fNIRS channels using 3D distance."""
|
||||
# Extract channel positions
|
||||
@@ -2468,16 +2403,6 @@ def brain_landmarks_3d(raw_haemo: BaseRaw, show_optodes: Literal['sensors', 'lab
|
||||
return brain
|
||||
|
||||
|
||||
def verify_channel_positions(data: BaseRaw) -> None:
|
||||
"""
|
||||
Visualizes the sensor/channel positions of the raw data for verification.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : BaseRaw
|
||||
The loaded data object to process.
|
||||
"""
|
||||
|
||||
def convert_fig_dict_to_png_bytes(fig_dict: dict[str, Figure]) -> dict[str, bytes]:
|
||||
png_dict = {}
|
||||
for label, fig in fig_dict.items():
|
||||
@@ -5515,6 +5440,14 @@ def generate_contrast_results(df_design_matrix, glm_est, file_path):
|
||||
return contrast_results_dict
|
||||
|
||||
|
||||
def haemoglobin_concentration(raw_od, file_path, override_ppf=False, ppf_lower_wavelength=6.0, ppf_upper_wavelength=6.0):
|
||||
if override_ppf:
|
||||
raw_haemo = beer_lambert_law(raw_od, ppf=(ppf_lower_wavelength, ppf_upper_wavelength))
|
||||
else:
|
||||
raw_haemo = beer_lambert_law(raw_od, ppf=calculate_dpf(file_path))
|
||||
return raw_haemo
|
||||
|
||||
|
||||
def process_participant(file_path, progress_callback=None):
|
||||
|
||||
# Step 0: Setting up
|
||||
@@ -5525,8 +5458,6 @@ def process_participant(file_path, progress_callback=None):
|
||||
if k in globals() and k != "REQUIRED_KEYS"
|
||||
}
|
||||
|
||||
print(config_dict)
|
||||
|
||||
# Step 1: Preprocessing
|
||||
raw = load_snirf(file_path)
|
||||
fig_raw = raw.plot(duration=raw.times[-1], n_channels=raw.info['nchan'], title="Loaded Raw", show=False)
|
||||
@@ -5667,7 +5598,7 @@ def process_participant(file_path, progress_callback=None):
|
||||
logger.info("Step 16 Completed.")
|
||||
|
||||
# Step 17: Haemoglobin Concentration
|
||||
raw_haemo = beer_lambert_law(raw_od, ppf=calculate_dpf(file_path))
|
||||
raw_haemo = haemoglobin_concentration(raw_od, file_path, OVERRIDE_PPF, PPF_LOWER_WAVELENGTH, PPF_UPPER_WAVELENGTH)
|
||||
fig_raw_haemo_bll = raw_haemo.plot(duration=raw_haemo.times[-1], n_channels=raw_haemo.info['nchan'], title="HbO and HbR Signals", show=False)
|
||||
fig_individual["Modified Beer Lambert Law"] = fig_raw_haemo_bll
|
||||
if progress_callback: progress_callback(17)
|
||||
@@ -5711,7 +5642,7 @@ def process_participant(file_path, progress_callback=None):
|
||||
if progress_callback: progress_callback(22)
|
||||
logger.info("Step 22 Completed.")
|
||||
|
||||
# Step 23: Run GLM
|
||||
# Step 23: General Linear Model
|
||||
glm_est, fig_glm_topo = make_and_run_glm(raw_haemo, df_design_matrix)
|
||||
fig_individual["GLM Topography"] = fig_glm_topo
|
||||
if progress_callback: progress_callback(23)
|
||||
|
||||
Reference in New Issue
Block a user