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 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)
|
||||
- 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
|
||||
- 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
|
||||
@@ -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 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 progress bar colors would not reset if the data was reprocessed
|
||||
|
||||
|
||||
# Version 1.6.0
|
||||
|
||||
+3
-1
@@ -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 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)
|
||||
- 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
|
||||
- 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
|
||||
@@ -31,4 +32,5 @@
|
||||
- Fixed an issue where file associations would refuse to associate on macOS once they have attempted to be associated
|
||||
- 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 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)",
|
||||
}
|
||||
|
||||
ROI_MAP = {} # TODO: Should be grabbed from the json file
|
||||
|
||||
DOWNSAMPLE: bool
|
||||
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
|
||||
fir_feature_dict = {'features': np.array([]), 'feature_names': [], 'feature_channels': []}
|
||||
|
||||
# if HRF_MODEL.lower() == "fir":
|
||||
# try:
|
||||
# fir_feature_dict = extract_fir_features_real_data(
|
||||
# raw=raw_haemo,
|
||||
# target_condition=None, # e.g., 'reach'
|
||||
# fir_delays=FIR_DELAYS, # e.g., np.arange(0, 15)
|
||||
# selected_metrics=tuple(METRIC_REGISTRY.keys()), # e.g., ('Peak_Amp', 'TTP', 'AUC')
|
||||
# roi_map=ROI_MAP,
|
||||
# glm_est=glm_est,
|
||||
# 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.warning(f"Step 27.5 Failed to extract FIR features: {e}")
|
||||
if HRF_MODEL.lower() == "fir":
|
||||
try:
|
||||
fir_feature_dict = extract_fir_features_real_data(
|
||||
raw=raw_haemo,
|
||||
target_condition=None, # e.g., 'reach'
|
||||
fir_delays=FIR_DELAYS, # e.g., np.arange(0, 15)
|
||||
selected_metrics=tuple(METRIC_REGISTRY.keys()), # e.g., ('Peak_Amp', 'TTP', 'AUC')
|
||||
roi_map=JSON_LOCATION,
|
||||
chromophores=('hbo', 'hbr', 'hbt'),
|
||||
glm_est=glm_est,
|
||||
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.warning(f"Step 27.5 Failed to extract FIR features: {e}")
|
||||
|
||||
|
||||
# Step 28: Finishing Up
|
||||
@@ -7197,8 +7196,8 @@ def _compute_roi_fir_curves(
|
||||
raw=None,
|
||||
target_condition='reach',
|
||||
fir_delays=np.arange(0, 15),
|
||||
roi_map=ROI_MAP,
|
||||
chromophores=('hbr',),
|
||||
roi_map={},
|
||||
chromophores=('hbo', 'hbr', 'hbt'),
|
||||
glm_est=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.
|
||||
"""
|
||||
roi_curves = {}
|
||||
active_roi_map = normalize_roi_map(roi_map)
|
||||
|
||||
# --- SHORT-CIRCUIT: Reuse pre-calculated GLM estimation if available ---
|
||||
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()
|
||||
print(f"Matched rows for '{target_condition}': {len(fir_df)}")
|
||||
|
||||
active_roi_map = normalize_roi_map(roi_map)
|
||||
|
||||
if fir_df.empty:
|
||||
logger.warning(f"Condition '{target_condition}' not found in precalculated GLM estimates.")
|
||||
return roi_curves
|
||||
|
||||
for chromo in chromophores:
|
||||
chromo_df = fir_df[fir_df[ch_col].str.lower().str.contains(chromo.lower())] if ch_col in fir_df.columns else fir_df
|
||||
# 1. Extract base measured chromophores (e.g., hbo, hbr) directly from GLM data
|
||||
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 = {}
|
||||
for ch_name, ch_group in chromo_df.groupby(ch_col):
|
||||
pair = ch_name.split(' ')[0]
|
||||
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 roi_map.items():
|
||||
for roi_name, channels in active_roi_map.items():
|
||||
matching_curves = [ch_curves[ch] for ch in channels if ch in ch_curves]
|
||||
if matching_curves:
|
||||
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
|
||||
|
||||
|
||||
@@ -7252,7 +7263,8 @@ def extract_fir_features_real_data(
|
||||
target_condition=None,
|
||||
fir_delays=np.arange(0, 15),
|
||||
selected_metrics=('Peak_Amp',),
|
||||
roi_map=ROI_MAP,
|
||||
roi_map={},
|
||||
chromophores=('hbo', 'hbr', 'hbt'),
|
||||
glm_est=None,
|
||||
df_design_matrix=None,
|
||||
png_queue=None
|
||||
@@ -7285,7 +7297,7 @@ def extract_fir_features_real_data(
|
||||
target_condition=cond,
|
||||
fir_delays=fir_delays,
|
||||
roi_map=roi_map,
|
||||
chromophores=('hbr',),
|
||||
chromophores=chromophores,
|
||||
glm_est=glm_est,
|
||||
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():
|
||||
metrics = compute_waveform_metrics(roi_fir_curve, fir_delays=fir_delays, selected_metrics=selected_metrics)
|
||||
collapsed_features.extend(metrics)
|
||||
# Prefix feature names with condition
|
||||
feature_names.extend([f"{cond}_{roi_name}_{m}" for m in metric_labels])
|
||||
feature_channels.extend([roi_name] * len(metric_labels))
|
||||
|
||||
# Prefix feature names with condition AND chromophore
|
||||
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
|
||||
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]
|
||||
|
||||
|
||||
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__":
|
||||
print("This file has no functionality when not used in tandem with the FLARES application.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user