roi from user provided file
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
- Added a new parameter to the PSP section on the right side of the screen: PSP_USE_HEART_RATE_BAND. This functions similarly to the existing SCI_USE_HEART_RATE_BAND
|
- Added a new parameter to the PSP section on the right side of the screen: PSP_USE_HEART_RATE_BAND. This functions similarly to the existing SCI_USE_HEART_RATE_BAND
|
||||||
- Added description text to the Inter-Group and Intra-Group Brain and Image Viewers, as well as the Functional Connectivity windows to explain what output can be expected
|
- Added description text to the Inter-Group and Intra-Group Brain and Image Viewers, as well as the Functional Connectivity windows to explain what output can be expected
|
||||||
- Added a new Preference option of Theme. Allows from selecting Auto (System default), Light, or Dark. Fixes [Issue 7](https://git.research.dezeeuw.ca/tyler/flares/issues/7)
|
- Added a new Preference option of Theme. Allows from selecting Auto (System default), Light, or Dark. Fixes [Issue 7](https://git.research.dezeeuw.ca/tyler/flares/issues/7)
|
||||||
|
- Optode updater can now update optode locations in multiple snirf files at once
|
||||||
- Removed image index 1 (Significance) from the Intra-Group Brain and Image Viewer as it is now provided more in depth with the Stats viewers
|
- Removed image index 1 (Significance) from the Intra-Group Brain and Image Viewer as it is now provided more in depth with the Stats viewers
|
||||||
- Modified the timeout when waiting for the application to close while performing updates down to a reasonable number
|
- Modified the timeout when waiting for the application to close while performing updates down to a reasonable number
|
||||||
- Modified the help messages for parameters in the SCI and PSP areas to better reflect how the parameters are used
|
- Modified the help messages for parameters in the SCI and PSP areas to better reflect how the parameters are used
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
- Fixed an issue where certain parameters would not enable or disable depending on other parameters when they should've
|
- Fixed an issue where certain parameters would not enable or disable depending on other parameters when they should've
|
||||||
- Fixed an issue where not all widgets would close when attempting to close the application causing the application to crash
|
- Fixed an issue where not all widgets would close when attempting to close the application causing the application to crash
|
||||||
- Fixed an issue where events were not created correctly after the data had been resampled by the design matrix
|
- Fixed an issue where events were not created correctly after the data had been resampled by the design matrix
|
||||||
|
- Fixed an issue where progress bar colors would not reset if the data was reprocessed
|
||||||
|
|
||||||
|
|
||||||
# Version 1.6.0
|
# Version 1.6.0
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
- Added a new parameter to the PSP section on the right side of the screen: PSP_USE_HEART_RATE_BAND. This functions similarly to the existing SCI_USE_HEART_RATE_BAND
|
- Added a new parameter to the PSP section on the right side of the screen: PSP_USE_HEART_RATE_BAND. This functions similarly to the existing SCI_USE_HEART_RATE_BAND
|
||||||
- Added description text to the Inter-Group and Intra-Group Brain and Image Viewers, as well as the Functional Connectivity windows to explain what output can be expected
|
- Added description text to the Inter-Group and Intra-Group Brain and Image Viewers, as well as the Functional Connectivity windows to explain what output can be expected
|
||||||
- Added a new Preference option of Theme. Allows from selecting Auto (System default), Light, or Dark. Fixes [Issue 7](https://git.research.dezeeuw.ca/tyler/flares/issues/7)
|
- Added a new Preference option of Theme. Allows from selecting Auto (System default), Light, or Dark. Fixes [Issue 7](https://git.research.dezeeuw.ca/tyler/flares/issues/7)
|
||||||
|
- Optode updater can now update optode locations in multiple snirf files at once
|
||||||
- Removed image index 1 (Significance) from the Intra-Group Brain and Image Viewer as it is now provided more in depth with the Stats viewers
|
- Removed image index 1 (Significance) from the Intra-Group Brain and Image Viewer as it is now provided more in depth with the Stats viewers
|
||||||
- Modified the timeout when waiting for the application to close while performing updates down to a reasonable number
|
- Modified the timeout when waiting for the application to close while performing updates down to a reasonable number
|
||||||
- Modified the help messages for parameters in the SCI and PSP areas to better reflect how the parameters are used
|
- Modified the help messages for parameters in the SCI and PSP areas to better reflect how the parameters are used
|
||||||
@@ -32,3 +33,4 @@
|
|||||||
- Fixed an issue where certain parameters would not enable or disable depending on other parameters when they should've
|
- Fixed an issue where certain parameters would not enable or disable depending on other parameters when they should've
|
||||||
- Fixed an issue where not all widgets would close when attempting to close the application causing the application to crash
|
- Fixed an issue where not all widgets would close when attempting to close the application causing the application to crash
|
||||||
- Fixed an issue where events were not created correctly after the data had been resampled by the design matrix
|
- Fixed an issue where events were not created correctly after the data had been resampled by the design matrix
|
||||||
|
- Fixed an issue where progress bar colors would not reset if the data was reprocessed
|
||||||
@@ -162,8 +162,6 @@ QC_METRIC_LABELS = {
|
|||||||
"total_processing_seconds": "Processing Time (s)",
|
"total_processing_seconds": "Processing Time (s)",
|
||||||
}
|
}
|
||||||
|
|
||||||
ROI_MAP = {} # TODO: Should be grabbed from the json file
|
|
||||||
|
|
||||||
DOWNSAMPLE: bool
|
DOWNSAMPLE: bool
|
||||||
DOWNSAMPLE_FREQUENCY: int
|
DOWNSAMPLE_FREQUENCY: int
|
||||||
|
|
||||||
@@ -6059,21 +6057,22 @@ def process_participant(file_path, file_start, progress_callback=None):
|
|||||||
# Step 27.5: Extract FIR Waveform Features & Enqueue Metric Plots
|
# Step 27.5: Extract FIR Waveform Features & Enqueue Metric Plots
|
||||||
fir_feature_dict = {'features': np.array([]), 'feature_names': [], 'feature_channels': []}
|
fir_feature_dict = {'features': np.array([]), 'feature_names': [], 'feature_channels': []}
|
||||||
|
|
||||||
# if HRF_MODEL.lower() == "fir":
|
if HRF_MODEL.lower() == "fir":
|
||||||
# try:
|
try:
|
||||||
# fir_feature_dict = extract_fir_features_real_data(
|
fir_feature_dict = extract_fir_features_real_data(
|
||||||
# raw=raw_haemo,
|
raw=raw_haemo,
|
||||||
# target_condition=None, # e.g., 'reach'
|
target_condition=None, # e.g., 'reach'
|
||||||
# fir_delays=FIR_DELAYS, # e.g., np.arange(0, 15)
|
fir_delays=FIR_DELAYS, # e.g., np.arange(0, 15)
|
||||||
# selected_metrics=tuple(METRIC_REGISTRY.keys()), # e.g., ('Peak_Amp', 'TTP', 'AUC')
|
selected_metrics=tuple(METRIC_REGISTRY.keys()), # e.g., ('Peak_Amp', 'TTP', 'AUC')
|
||||||
# roi_map=ROI_MAP,
|
roi_map=JSON_LOCATION,
|
||||||
# glm_est=glm_est,
|
chromophores=('hbo', 'hbr', 'hbt'),
|
||||||
# df_design_matrix=df_design_matrix,
|
glm_est=glm_est,
|
||||||
# png_queue=png_queue
|
df_design_matrix=df_design_matrix,
|
||||||
# )
|
png_queue=png_queue
|
||||||
# logger.info("Step 27.5: FIR features successfully extracted and metric images enqueued.")
|
)
|
||||||
# except Exception as e:
|
logger.info("Step 27.5: FIR features successfully extracted and metric images enqueued.")
|
||||||
# logger.warning(f"Step 27.5 Failed to extract FIR features: {e}")
|
except Exception as e:
|
||||||
|
logger.warning(f"Step 27.5 Failed to extract FIR features: {e}")
|
||||||
|
|
||||||
|
|
||||||
# Step 28: Finishing Up
|
# Step 28: Finishing Up
|
||||||
@@ -7197,8 +7196,8 @@ def _compute_roi_fir_curves(
|
|||||||
raw=None,
|
raw=None,
|
||||||
target_condition='reach',
|
target_condition='reach',
|
||||||
fir_delays=np.arange(0, 15),
|
fir_delays=np.arange(0, 15),
|
||||||
roi_map=ROI_MAP,
|
roi_map={},
|
||||||
chromophores=('hbr',),
|
chromophores=('hbo', 'hbr', 'hbt'),
|
||||||
glm_est=None,
|
glm_est=None,
|
||||||
df_design_matrix=None
|
df_design_matrix=None
|
||||||
):
|
):
|
||||||
@@ -7207,6 +7206,7 @@ def _compute_roi_fir_curves(
|
|||||||
it reuses pre-calculated GLM results directly to avoid duplicate processing.
|
it reuses pre-calculated GLM results directly to avoid duplicate processing.
|
||||||
"""
|
"""
|
||||||
roi_curves = {}
|
roi_curves = {}
|
||||||
|
active_roi_map = normalize_roi_map(roi_map)
|
||||||
|
|
||||||
# --- SHORT-CIRCUIT: Reuse pre-calculated GLM estimation if available ---
|
# --- SHORT-CIRCUIT: Reuse pre-calculated GLM estimation if available ---
|
||||||
if glm_est is not None and df_design_matrix is not None:
|
if glm_est is not None and df_design_matrix is not None:
|
||||||
@@ -7225,25 +7225,36 @@ def _compute_roi_fir_curves(
|
|||||||
fir_df = glm_df[glm_df[cond_col].astype(str).str.lower().str.contains(target_condition.lower())].copy()
|
fir_df = glm_df[glm_df[cond_col].astype(str).str.lower().str.contains(target_condition.lower())].copy()
|
||||||
print(f"Matched rows for '{target_condition}': {len(fir_df)}")
|
print(f"Matched rows for '{target_condition}': {len(fir_df)}")
|
||||||
|
|
||||||
|
active_roi_map = normalize_roi_map(roi_map)
|
||||||
|
|
||||||
if fir_df.empty:
|
if fir_df.empty:
|
||||||
logger.warning(f"Condition '{target_condition}' not found in precalculated GLM estimates.")
|
logger.warning(f"Condition '{target_condition}' not found in precalculated GLM estimates.")
|
||||||
return roi_curves
|
return roi_curves
|
||||||
|
|
||||||
for chromo in chromophores:
|
# 1. Extract base measured chromophores (e.g., hbo, hbr) directly from GLM data
|
||||||
chromo_df = fir_df[fir_df[ch_col].str.lower().str.contains(chromo.lower())] if ch_col in fir_df.columns else fir_df
|
base_chromos = [c.lower() for c in chromophores if c.lower() != 'hbt']
|
||||||
|
|
||||||
|
for chromo in base_chromos:
|
||||||
|
chromo_df = fir_df[fir_df[ch_col].str.lower().str.contains(chromo)] if ch_col in fir_df.columns else fir_df
|
||||||
|
|
||||||
ch_curves = {}
|
ch_curves = {}
|
||||||
for ch_name, ch_group in chromo_df.groupby(ch_col):
|
for ch_name, ch_group in chromo_df.groupby(ch_col):
|
||||||
pair = ch_name.split(' ')[0]
|
pair = ch_name.split(' ')[0]
|
||||||
ch_curves[pair] = ch_group['theta'].values if 'theta' in ch_group.columns else ch_group['beta'].values
|
ch_curves[pair] = ch_group['theta'].values if 'theta' in ch_group.columns else ch_group['beta'].values
|
||||||
|
|
||||||
print("Extracted channel keys:", list(ch_curves.keys())[:5])
|
for roi_name, channels in active_roi_map.items():
|
||||||
|
|
||||||
for roi_name, channels in roi_map.items():
|
|
||||||
matching_curves = [ch_curves[ch] for ch in channels if ch in ch_curves]
|
matching_curves = [ch_curves[ch] for ch in channels if ch in ch_curves]
|
||||||
if matching_curves:
|
if matching_curves:
|
||||||
roi_curves[(chromo, roi_name)] = np.mean(matching_curves, axis=0)
|
roi_curves[(chromo, roi_name)] = np.mean(matching_curves, axis=0)
|
||||||
|
|
||||||
|
# 2. Derive HbT (HbO + HbR) dynamically if requested
|
||||||
|
if 'hbt' in [c.lower() for c in chromophores]:
|
||||||
|
for roi_name in active_roi_map.keys():
|
||||||
|
hbo_key = ('hbo', roi_name)
|
||||||
|
hbr_key = ('hbr', roi_name)
|
||||||
|
if hbo_key in roi_curves and hbr_key in roi_curves:
|
||||||
|
roi_curves[('hbt', roi_name)] = roi_curves[hbo_key] + roi_curves[hbr_key]
|
||||||
|
|
||||||
return roi_curves
|
return roi_curves
|
||||||
|
|
||||||
|
|
||||||
@@ -7252,7 +7263,8 @@ def extract_fir_features_real_data(
|
|||||||
target_condition=None,
|
target_condition=None,
|
||||||
fir_delays=np.arange(0, 15),
|
fir_delays=np.arange(0, 15),
|
||||||
selected_metrics=('Peak_Amp',),
|
selected_metrics=('Peak_Amp',),
|
||||||
roi_map=ROI_MAP,
|
roi_map={},
|
||||||
|
chromophores=('hbo', 'hbr', 'hbt'),
|
||||||
glm_est=None,
|
glm_est=None,
|
||||||
df_design_matrix=None,
|
df_design_matrix=None,
|
||||||
png_queue=None
|
png_queue=None
|
||||||
@@ -7285,7 +7297,7 @@ def extract_fir_features_real_data(
|
|||||||
target_condition=cond,
|
target_condition=cond,
|
||||||
fir_delays=fir_delays,
|
fir_delays=fir_delays,
|
||||||
roi_map=roi_map,
|
roi_map=roi_map,
|
||||||
chromophores=('hbr',),
|
chromophores=chromophores,
|
||||||
glm_est=glm_est,
|
glm_est=glm_est,
|
||||||
df_design_matrix=df_design_matrix
|
df_design_matrix=df_design_matrix
|
||||||
)
|
)
|
||||||
@@ -7300,9 +7312,10 @@ def extract_fir_features_real_data(
|
|||||||
for (chromo, roi_name), roi_fir_curve in roi_curves.items():
|
for (chromo, roi_name), roi_fir_curve in roi_curves.items():
|
||||||
metrics = compute_waveform_metrics(roi_fir_curve, fir_delays=fir_delays, selected_metrics=selected_metrics)
|
metrics = compute_waveform_metrics(roi_fir_curve, fir_delays=fir_delays, selected_metrics=selected_metrics)
|
||||||
collapsed_features.extend(metrics)
|
collapsed_features.extend(metrics)
|
||||||
# Prefix feature names with condition
|
|
||||||
feature_names.extend([f"{cond}_{roi_name}_{m}" for m in metric_labels])
|
# Prefix feature names with condition AND chromophore
|
||||||
feature_channels.extend([roi_name] * len(metric_labels))
|
feature_names.extend([f"{cond}_{chromo.upper()}_{roi_name}_{m}" for m in metric_labels])
|
||||||
|
feature_channels.extend([f"{roi_name} ({chromo.upper()})"] * len(metric_labels))
|
||||||
|
|
||||||
# Enqueue plots for this specific condition
|
# Enqueue plots for this specific condition
|
||||||
plot_and_enqueue_waveform_metrics(
|
plot_and_enqueue_waveform_metrics(
|
||||||
@@ -7366,6 +7379,43 @@ def compute_waveform_metrics(fir_curve, fir_delays, selected_metrics=('Peak_Amp'
|
|||||||
return [calculated_metrics[m] for m in selected_metrics]
|
return [calculated_metrics[m] for m in selected_metrics]
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_roi_map(roi_map):
|
||||||
|
"""
|
||||||
|
Normalizes ROI mapping inputs into a flat {roi_name: [channel_list]} dict.
|
||||||
|
|
||||||
|
Accepts:
|
||||||
|
1. String or Path pointing to a JSON file (JSON_LOCATION).
|
||||||
|
2. Dict with 'regions_of_interest' list (loaded JSON).
|
||||||
|
3. Standard flat dict {roi_name: [channels]}.
|
||||||
|
"""
|
||||||
|
# 1. If roi_map is a file path string or Path, load JSON from disk
|
||||||
|
if isinstance(roi_map, (str, Path)):
|
||||||
|
json_path = Path(roi_map)
|
||||||
|
if json_path.is_file():
|
||||||
|
try:
|
||||||
|
with open(json_path, 'r', encoding='utf-8') as f:
|
||||||
|
roi_map = json.load(f)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to load ROI JSON file from {json_path}: {e}")
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
logger.error(f"ROI JSON file path does not exist: {json_path}")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# 2. Handle nested JSON structure with 'regions_of_interest'
|
||||||
|
if isinstance(roi_map, dict) and 'regions_of_interest' in roi_map:
|
||||||
|
return {
|
||||||
|
roi['name']: roi['channels']
|
||||||
|
for roi in roi_map['regions_of_interest']
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Fallback for flat dictionary {roi_name: [channels]}
|
||||||
|
if isinstance(roi_map, dict):
|
||||||
|
return roi_map
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("This file has no functionality when not used in tandem with the FLARES application.")
|
print("This file has no functionality when not used in tandem with the FLARES application.")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user