Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -19,7 +19,7 @@ from google.adk.runners import InMemoryRunner
|
|
| 19 |
from google.genai import types
|
| 20 |
|
| 21 |
# Project Imports
|
| 22 |
-
# Wrap imports to prevent immediate crash if dependencies are missing
|
| 23 |
try:
|
| 24 |
from cellemetry import root_agent
|
| 25 |
from cellemetry.config import AnalysisDeps
|
|
@@ -178,7 +178,6 @@ async def run_analysis(image_path_str, user_prompt, session_id_state):
|
|
| 178 |
# Lazy Load: Ensure model is loaded before inference
|
| 179 |
if not MODEL_CACHE["loaded"]:
|
| 180 |
yield [], None, None, [], None, waiting_df, waiting_df, waiting_df, *empty_slider_updates
|
| 181 |
-
# Load model synchronously if triggered here
|
| 182 |
load_models()
|
| 183 |
|
| 184 |
if not image_path_str:
|
|
@@ -359,79 +358,106 @@ async def unified_chat_handler(message, history, session_id, current_img_path):
|
|
| 359 |
yield history, session_id, current_img_path, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), *empty_slider_updates, None, gr.update(), gr.update(), gr.update()
|
| 360 |
|
| 361 |
# --- UI Layout ---
|
| 362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
session_id_state = gr.State(None)
|
| 364 |
current_image_path = gr.State(None)
|
| 365 |
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
</div>
|
| 395 |
</div>
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
|
|
|
|
|
|
| 407 |
</div>
|
| 408 |
-
|
| 409 |
-
</div>
|
| 410 |
-
""")
|
| 411 |
-
|
| 412 |
-
with gr.Tabs(visible=False) as results_tabs:
|
| 413 |
-
with gr.Tab("π Segmentation"):
|
| 414 |
-
with gr.Row():
|
| 415 |
-
with gr.Column(scale=3):
|
| 416 |
-
overlay_output = gr.Image(label="Segmentation Result", height=780, type="pil")
|
| 417 |
-
with gr.Column(scale=1):
|
| 418 |
-
gr.Markdown("**Layer Controls**")
|
| 419 |
-
layer_checkboxes = gr.CheckboxGroup(label="Visible Layers", choices=[], value=[], interactive=True)
|
| 420 |
-
gr.Markdown("**Opacity Controls**")
|
| 421 |
-
opacity_slider_1 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 1 Opacity", visible=False)
|
| 422 |
-
opacity_slider_2 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 2 Opacity", visible=False)
|
| 423 |
-
opacity_slider_3 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 3 Opacity", visible=False)
|
| 424 |
-
opacity_slider_4 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 4 Opacity", visible=False)
|
| 425 |
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
with gr.
|
| 429 |
-
with gr.
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
|
| 436 |
def regenerate_overlay_with_opacity(img_path, selected_layers, op1, op2, op3, op4):
|
| 437 |
if not img_path or not selected_layers: return None
|
|
|
|
| 19 |
from google.genai import types
|
| 20 |
|
| 21 |
# Project Imports
|
| 22 |
+
# Wrap imports to prevent immediate crash if dependencies are missing
|
| 23 |
try:
|
| 24 |
from cellemetry import root_agent
|
| 25 |
from cellemetry.config import AnalysisDeps
|
|
|
|
| 178 |
# Lazy Load: Ensure model is loaded before inference
|
| 179 |
if not MODEL_CACHE["loaded"]:
|
| 180 |
yield [], None, None, [], None, waiting_df, waiting_df, waiting_df, *empty_slider_updates
|
|
|
|
| 181 |
load_models()
|
| 182 |
|
| 183 |
if not image_path_str:
|
|
|
|
| 358 |
yield history, session_id, current_img_path, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), *empty_slider_updates, None, gr.update(), gr.update(), gr.update()
|
| 359 |
|
| 360 |
# --- UI Layout ---
|
| 361 |
+
|
| 362 |
+
# CSS to handle margins and width consistency
|
| 363 |
+
custom_css = """
|
| 364 |
+
/* 1. Global Margin Setting */
|
| 365 |
+
#main_container {
|
| 366 |
+
margin-left: 20% !important;
|
| 367 |
+
margin-right: 20% !important;
|
| 368 |
+
width: auto !important;
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
/* 2. Fix Tab Width Consistency (Right Panel) */
|
| 372 |
+
/* We enforce a minimum width so the panel doesn't shrink when switching to empty dataframes */
|
| 373 |
+
.right-panel {
|
| 374 |
+
min-width: 600px !important;
|
| 375 |
+
flex-grow: 2 !important;
|
| 376 |
+
}
|
| 377 |
+
"""
|
| 378 |
+
|
| 379 |
+
with gr.Blocks(title="Cellemetry Agent", css=custom_css) as demo:
|
| 380 |
session_id_state = gr.State(None)
|
| 381 |
current_image_path = gr.State(None)
|
| 382 |
|
| 383 |
+
# WRAPPER to apply 20% margins
|
| 384 |
+
with gr.Column(elem_id="main_container"):
|
| 385 |
+
|
| 386 |
+
with gr.Row():
|
| 387 |
+
# --- LEFT COLUMN (Chat) ---
|
| 388 |
+
with gr.Column(scale=1, min_width=300):
|
| 389 |
+
chatbot = gr.Chatbot(
|
| 390 |
+
label="Agent Conversation",
|
| 391 |
+
height=600,
|
| 392 |
+
value=[{"role": "assistant", "content": "π Welcome to Cellemetry! Upload a microscopy image and describe what you'd like to analyze."}],
|
| 393 |
+
show_label=True
|
| 394 |
+
)
|
| 395 |
+
chat_input = gr.MultimodalTextbox(
|
| 396 |
+
file_types=["image"],
|
| 397 |
+
placeholder="Upload an image and describe your analysis...",
|
| 398 |
+
show_label=False,
|
| 399 |
+
submit_btn="Send"
|
| 400 |
+
)
|
| 401 |
+
|
| 402 |
+
# --- RIGHT COLUMN (Results) ---
|
| 403 |
+
# Added 'right-panel' class for CSS width enforcement
|
| 404 |
+
with gr.Column(scale=2, elem_classes=["right-panel"]):
|
| 405 |
+
|
| 406 |
+
# Welcome overlay
|
| 407 |
+
with gr.Column(visible=True, elem_id="welcome-overlay") as welcome_overlay:
|
| 408 |
+
gr.HTML(f"""
|
| 409 |
+
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 780px; padding: 40px; background: #f8f9fa; border-radius: 8px; border: 2px solid #3498db;">
|
| 410 |
+
<div style="text-align: center;">
|
| 411 |
+
<div style='text-align: center;'>
|
| 412 |
+
<img src="https://raw.githubusercontent.com/hmgill/Cellemetry/main/logo.png" alt="Logo" style="height:200px; display: block; margin: 0 auto;">
|
| 413 |
+
</div>
|
| 414 |
+
<h2 style="color: #333; margin: 20px 0 10px; font-weight: 600; font-size: 28px;">Welcome to Cellemetry</h2>
|
| 415 |
+
<p style="color: #666; font-size: 16px; max-width: 400px; margin: 0 auto 30px; line-height: 1.6;">Upload a microscopy image to get started with AI-powered cell analysis and segmentation</p>
|
| 416 |
+
<div style="padding: 20px; background: #fff; border-radius: 8px; border-left: 4px solid #3498db; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
|
| 417 |
+
<p style="color: #555; margin: 0; font-size: 14px;">π Use the chat on the left to begin</p>
|
| 418 |
+
</div>
|
| 419 |
</div>
|
| 420 |
</div>
|
| 421 |
+
"""
|
| 422 |
+
)
|
| 423 |
+
|
| 424 |
+
# Loading overlay
|
| 425 |
+
with gr.Column(visible=False, elem_id="loading-overlay") as loading_overlay:
|
| 426 |
+
gr.HTML("""
|
| 427 |
+
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 780px; background: rgba(255, 255, 255, 0.95); border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
| 428 |
+
<div style="text-align: center;">
|
| 429 |
+
<div style="border: 8px solid #f3f3f3; border-top: 8px solid #3498db; border-radius: 50%; width: 60px; height: 60px; animation: spin 1s linear infinite; margin: 0 auto 20px;"></div>
|
| 430 |
+
<h3 style="color: #555; margin: 0;">βοΈ Analysis in Progress</h3>
|
| 431 |
+
<p style="color: #888; margin-top: 10px;">Please wait while we process your microscopy image...</p>
|
| 432 |
+
</div>
|
| 433 |
+
<style>@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }</style>
|
| 434 |
</div>
|
| 435 |
+
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 436 |
|
| 437 |
+
# Results tabs
|
| 438 |
+
with gr.Tabs(visible=False) as results_tabs:
|
| 439 |
+
with gr.Tab("π Segmentation"):
|
| 440 |
+
with gr.Row():
|
| 441 |
+
with gr.Column(scale=3):
|
| 442 |
+
overlay_output = gr.Image(label="Segmentation Result", height=780, type="pil")
|
| 443 |
+
with gr.Column(scale=1):
|
| 444 |
+
gr.Markdown("**Layer Controls**")
|
| 445 |
+
layer_checkboxes = gr.CheckboxGroup(label="Visible Layers", choices=[], value=[], interactive=True)
|
| 446 |
+
gr.Markdown("**Opacity Controls**")
|
| 447 |
+
opacity_slider_1 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 1 Opacity", visible=False)
|
| 448 |
+
opacity_slider_2 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 2 Opacity", visible=False)
|
| 449 |
+
opacity_slider_3 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 3 Opacity", visible=False)
|
| 450 |
+
opacity_slider_4 = gr.Slider(minimum=0, maximum=1, value=0.6, step=0.1, label="Layer 4 Opacity", visible=False)
|
| 451 |
+
|
| 452 |
+
with gr.Tab("π Quantitative Results"):
|
| 453 |
+
download_btn = gr.File(label="Download Excel Report")
|
| 454 |
+
with gr.Tabs():
|
| 455 |
+
with gr.Tab("Morphology"):
|
| 456 |
+
tbl_morph = gr.Dataframe(interactive=False, wrap=True)
|
| 457 |
+
with gr.Tab("Spatial"):
|
| 458 |
+
tbl_spatial = gr.Dataframe(interactive=False, wrap=True)
|
| 459 |
+
with gr.Tab("Relational"):
|
| 460 |
+
tbl_rel = gr.Dataframe(interactive=False, wrap=True)
|
| 461 |
|
| 462 |
def regenerate_overlay_with_opacity(img_path, selected_layers, op1, op2, op3, op4):
|
| 463 |
if not img_path or not selected_layers: return None
|