Spaces:
Running
Running
Mattias Cosarinsky
commited on
Commit
·
7d0bd10
1
Parent(s):
59d4532
add app
Browse files
app.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
|
| 3 |
+
import gradio as gr
|
| 4 |
+
from utils import HF_REPO, ROIS, load_roi_image, load_images, get_hypotheses_for_selection
|
| 5 |
+
|
| 6 |
+
# -------- CONFIGURATION --------
|
| 7 |
+
MODELS = ["SAE", "SAE_ICA", "ICA", "PCA", "NMF", "Voxel"]
|
| 8 |
+
|
| 9 |
+
# Initial values derived from the helper functions after the structure is loaded
|
| 10 |
+
INITIAL_HYPOTHESES = get_hypotheses_for_selection(MODELS[0], ROIS[0])
|
| 11 |
+
INITIAL_ROI_IMAGE = load_roi_image(ROIS[0])
|
| 12 |
+
|
| 13 |
+
# -------- GRADIO UI AND CALLBACK LOGIC --------
|
| 14 |
+
def update_hypotheses(model, roi):
|
| 15 |
+
new_hypotheses = get_hypotheses_for_selection(model, roi)
|
| 16 |
+
return gr.Dropdown(choices=new_hypotheses, value=new_hypotheses[0] if new_hypotheses else None)
|
| 17 |
+
|
| 18 |
+
with gr.Blocks() as demo:
|
| 19 |
+
gr.Markdown("## BrainExplore: Visual Concept Explorer")
|
| 20 |
+
|
| 21 |
+
# 1. TOP ROW: Dropdowns (Menus)
|
| 22 |
+
with gr.Row():
|
| 23 |
+
model_dropdown = gr.Dropdown(label="Select Model", choices=MODELS, value=MODELS[0], scale=1)
|
| 24 |
+
roi_dropdown = gr.Dropdown(label="Select ROI", choices=ROIS, value=ROIS[0], scale=1)
|
| 25 |
+
|
| 26 |
+
hyp_dropdown = gr.Dropdown(
|
| 27 |
+
label="Select Hypothesis",
|
| 28 |
+
choices=INITIAL_HYPOTHESES,
|
| 29 |
+
value=INITIAL_HYPOTHESES[0] if INITIAL_HYPOTHESES else None,
|
| 30 |
+
scale=1
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
# 2. SECOND ROW: Visualizations (ROI Image and Gallery)
|
| 34 |
+
with gr.Row(equal_height=True):
|
| 35 |
+
|
| 36 |
+
roi_image = gr.Image(
|
| 37 |
+
label="Selected Brain Region (ROI)",
|
| 38 |
+
value=INITIAL_ROI_IMAGE,
|
| 39 |
+
interactive=False,
|
| 40 |
+
height=400,
|
| 41 |
+
scale=1
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
image_gallery = gr.Gallery(
|
| 45 |
+
label="Top Activating Images",
|
| 46 |
+
columns=None, rows=1, scale=3,
|
| 47 |
+
object_fit="contain", preview=True, height=400
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
# -------- CALLBACKS --------
|
| 51 |
+
model_dropdown.change(
|
| 52 |
+
fn=update_hypotheses,
|
| 53 |
+
inputs=[model_dropdown, roi_dropdown],
|
| 54 |
+
outputs=[hyp_dropdown],
|
| 55 |
+
queue=False
|
| 56 |
+
).then(
|
| 57 |
+
fn=load_images,
|
| 58 |
+
inputs=[model_dropdown, roi_dropdown, hyp_dropdown],
|
| 59 |
+
outputs=[image_gallery]
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
# ROI Change: Update Hypotheses, THEN update ROI Image, THEN update Activating Images
|
| 63 |
+
roi_dropdown.change(
|
| 64 |
+
fn=update_hypotheses,
|
| 65 |
+
inputs=[model_dropdown, roi_dropdown],
|
| 66 |
+
outputs=[hyp_dropdown],
|
| 67 |
+
queue=False
|
| 68 |
+
).then(
|
| 69 |
+
fn=load_roi_image,
|
| 70 |
+
inputs=[roi_dropdown],
|
| 71 |
+
outputs=[roi_image]
|
| 72 |
+
).then(
|
| 73 |
+
fn=load_images,
|
| 74 |
+
inputs=[model_dropdown, roi_dropdown, hyp_dropdown],
|
| 75 |
+
outputs=[image_gallery]
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
# Hypothesis Change: Update Activating Images only
|
| 79 |
+
hyp_dropdown.change(fn=load_images, inputs=[model_dropdown, roi_dropdown, hyp_dropdown], outputs=[image_gallery])
|
| 80 |
+
|
| 81 |
+
if __name__ == "__main__":
|
| 82 |
+
demo.launch()
|
utils.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from datasets import load_dataset
|
| 3 |
+
from PIL import Image
|
| 4 |
+
from huggingface_hub import hf_hub_download
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
# -------- CONFIG & STATE --------
|
| 8 |
+
HF_REPO = "mcosarinsky/BrainExplore-Images"
|
| 9 |
+
STRUCTURE_DATA = None
|
| 10 |
+
ROIS = ["EBA","FBA-1","FBA-2","FFA-1","FFA-2","OFA","OPA","OWFA","PPA","RSC","VWFA-1","VWFA-2","hV4"]
|
| 11 |
+
|
| 12 |
+
# -------- 1. JSON STRUCTURE HELPERS --------
|
| 13 |
+
def load_structure(repo_id, filename="structure.json"):
|
| 14 |
+
"""
|
| 15 |
+
Downloads the structure.json file from the Hugging Face Hub and loads it into memory.
|
| 16 |
+
"""
|
| 17 |
+
global STRUCTURE_DATA
|
| 18 |
+
if STRUCTURE_DATA is not None:
|
| 19 |
+
return STRUCTURE_DATA
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
local_path = hf_hub_download(
|
| 23 |
+
repo_id=repo_id,
|
| 24 |
+
filename=filename,
|
| 25 |
+
repo_type="dataset"
|
| 26 |
+
)
|
| 27 |
+
with open(local_path, 'r') as f:
|
| 28 |
+
STRUCTURE_DATA = json.load(f)
|
| 29 |
+
|
| 30 |
+
print(f"Successfully loaded {filename}.")
|
| 31 |
+
return STRUCTURE_DATA
|
| 32 |
+
|
| 33 |
+
except Exception as e:
|
| 34 |
+
print(f"CRITICAL ERROR: Could not load {filename} via hf_hub_download: {e}")
|
| 35 |
+
return {}
|
| 36 |
+
|
| 37 |
+
def get_hypotheses_for_selection(model, roi):
|
| 38 |
+
"""
|
| 39 |
+
Retrieves the list of hypotheses based on the selected model and ROI
|
| 40 |
+
from the pre-loaded STRUCTURE_DATA.
|
| 41 |
+
"""
|
| 42 |
+
if not STRUCTURE_DATA:
|
| 43 |
+
return []
|
| 44 |
+
|
| 45 |
+
try:
|
| 46 |
+
hypotheses = STRUCTURE_DATA.get(model, {}).get(roi, [])
|
| 47 |
+
return hypotheses
|
| 48 |
+
except Exception as e:
|
| 49 |
+
print(f"Error accessing structure data for {model}/{roi}: {e}")
|
| 50 |
+
return []
|
| 51 |
+
|
| 52 |
+
# -------- 2. IMAGE PROCESSING HELPERS --------
|
| 53 |
+
def pad_image(pil_img, padding_x=10, padding_y=0, color=(255, 255, 255)):
|
| 54 |
+
"""
|
| 55 |
+
Adds padding to the sides of a PIL image.
|
| 56 |
+
"""
|
| 57 |
+
old_width, old_height = pil_img.size
|
| 58 |
+
new_width = old_width + 2 * padding_x
|
| 59 |
+
new_height = old_height + 2 * padding_y
|
| 60 |
+
new_img = Image.new(pil_img.mode, (new_width, new_height), color)
|
| 61 |
+
new_img.paste(pil_img, (padding_x, padding_y))
|
| 62 |
+
return new_img
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
# -------- 3. DATA LOADING HELPERS --------
|
| 66 |
+
def load_images(model, roi, hyp):
|
| 67 |
+
"""
|
| 68 |
+
Loads top activating images for the given parameters and applies padding.
|
| 69 |
+
"""
|
| 70 |
+
folder_path = f"{model}/{roi}/{hyp}"
|
| 71 |
+
ds = load_dataset(HF_REPO, data_dir=folder_path, split="train", streaming=True)
|
| 72 |
+
image_paths = [img["image"] for img in ds.take(2)]
|
| 73 |
+
padded_images = [pad_image(img, padding_x=10, padding_y=0) for img in image_paths]
|
| 74 |
+
return padded_images
|
| 75 |
+
|
| 76 |
+
def load_roi_image(roi_name):
|
| 77 |
+
"""
|
| 78 |
+
Downloads the ROI PNG from the Hugging Face Hub, looking inside the 'ROI/' folder,
|
| 79 |
+
and loads it as a PIL Image.
|
| 80 |
+
"""
|
| 81 |
+
if not roi_name:
|
| 82 |
+
return None
|
| 83 |
+
|
| 84 |
+
file_name = f"ROI/{roi_name}.png"
|
| 85 |
+
|
| 86 |
+
try:
|
| 87 |
+
local_path = hf_hub_download(
|
| 88 |
+
repo_id=HF_REPO,
|
| 89 |
+
filename=file_name,
|
| 90 |
+
repo_type="dataset"
|
| 91 |
+
)
|
| 92 |
+
return Image.open(local_path)
|
| 93 |
+
except Exception as e:
|
| 94 |
+
# A 404 error here likely means the file doesn't exist under that path
|
| 95 |
+
print(f"Error loading ROI image {roi_name} via hf_hub_download. Path used: {file_name}. Details: {e}")
|
| 96 |
+
return None
|
| 97 |
+
|
| 98 |
+
# Perform initial structure load for dynamic dependency resolution in app.py
|
| 99 |
+
load_structure(HF_REPO)
|