Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -21,6 +21,10 @@ try:
|
|
| 21 |
except ImportError:
|
| 22 |
YOLO_AVAILABLE = False
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
# Category names mapping (0-27)
|
| 25 |
CATEGORY_NAMES = {
|
| 26 |
0: "Nucleoplasm", 1: "Nuclear membrane", 2: "Nucleoli",
|
|
@@ -60,13 +64,13 @@ def extract_image_id(filename):
|
|
| 60 |
return name_without_ext.replace(color, '')
|
| 61 |
return name_without_ext
|
| 62 |
|
| 63 |
-
def
|
| 64 |
-
"""
|
| 65 |
-
if
|
| 66 |
-
return
|
| 67 |
|
| 68 |
try:
|
| 69 |
-
state.csv_data = pd.read_csv(
|
| 70 |
state.image_categories = {}
|
| 71 |
|
| 72 |
for _, row in state.csv_data.iterrows():
|
|
@@ -79,36 +83,31 @@ def load_csv_file(csv_file):
|
|
| 79 |
'indices': category_indices,
|
| 80 |
'names': category_names
|
| 81 |
}
|
| 82 |
-
|
| 83 |
-
return f"✅ Loaded CSV with {len(state.csv_data)} entries", ""
|
| 84 |
except Exception as e:
|
| 85 |
-
|
| 86 |
|
| 87 |
-
def scan_folder(
|
| 88 |
-
"""
|
| 89 |
-
if not
|
| 90 |
-
return None
|
| 91 |
-
|
| 92 |
-
if not os.path.isdir(folder_path):
|
| 93 |
-
return None, "❌ Not a directory", "0 images"
|
| 94 |
|
| 95 |
try:
|
| 96 |
extensions = {'.png', '.jpg', '.jpeg', '.tif', '.tiff', '.bmp'}
|
| 97 |
state.image_files = []
|
| 98 |
|
| 99 |
-
for f in sorted(Path(
|
| 100 |
if f.suffix.lower() in extensions:
|
| 101 |
state.image_files.append(str(f))
|
| 102 |
|
| 103 |
if len(state.image_files) == 0:
|
| 104 |
-
return None
|
| 105 |
|
| 106 |
# Generate gallery
|
| 107 |
gallery_items = [(img, os.path.basename(img)) for img in state.image_files]
|
| 108 |
-
|
| 109 |
-
return gallery_items, f"✅ Found {len(state.image_files)} images", f"{len(state.image_files)} images"
|
| 110 |
except Exception as e:
|
| 111 |
-
|
|
|
|
| 112 |
|
| 113 |
def prepare_image_for_yolo(image):
|
| 114 |
"""Convert grayscale to RGB for YOLO."""
|
|
@@ -124,7 +123,7 @@ def prepare_image_for_yolo(image):
|
|
| 124 |
def select_image_from_gallery(evt: gr.SelectData):
|
| 125 |
"""Handle image selection from gallery."""
|
| 126 |
if not state.image_files or evt.index >= len(state.image_files):
|
| 127 |
-
return None, "
|
| 128 |
|
| 129 |
state.selected_image = state.image_files[evt.index]
|
| 130 |
|
|
@@ -147,9 +146,9 @@ def select_image_from_gallery(evt: gr.SelectData):
|
|
| 147 |
# Show original image
|
| 148 |
fig = create_visualization(show_numbers=False)
|
| 149 |
|
| 150 |
-
return fig, f"
|
| 151 |
except Exception as e:
|
| 152 |
-
return None, f"
|
| 153 |
|
| 154 |
def get_image_categories():
|
| 155 |
"""Get category information for selected image."""
|
|
@@ -160,7 +159,7 @@ def get_image_categories():
|
|
| 160 |
categories = state.image_categories.get(img_id)
|
| 161 |
|
| 162 |
if categories:
|
| 163 |
-
result = "
|
| 164 |
for idx, name in zip(categories['indices'], categories['names']):
|
| 165 |
result += f"[{idx}] {name}\n"
|
| 166 |
return result
|
|
@@ -169,10 +168,10 @@ def get_image_categories():
|
|
| 169 |
def run_cellpose_segmentation(model_type, diameter, use_gpu):
|
| 170 |
"""Run Cellpose segmentation."""
|
| 171 |
if state.current_image is None:
|
| 172 |
-
return None, "
|
| 173 |
|
| 174 |
if not CELLPOSE_AVAILABLE:
|
| 175 |
-
return None, "
|
| 176 |
|
| 177 |
try:
|
| 178 |
with warnings.catch_warnings():
|
|
@@ -204,20 +203,20 @@ def run_cellpose_segmentation(model_type, diameter, use_gpu):
|
|
| 204 |
)
|
| 205 |
|
| 206 |
if state.masks is None or state.masks.max() == 0:
|
| 207 |
-
return None, "
|
| 208 |
|
| 209 |
return finalize_segmentation()
|
| 210 |
|
| 211 |
except Exception as e:
|
| 212 |
-
return None, f"
|
| 213 |
|
| 214 |
def run_yolo_segmentation(model_path, confidence, iou, use_gpu):
|
| 215 |
"""Run YOLO segmentation."""
|
| 216 |
if state.current_image is None:
|
| 217 |
-
return None, "
|
| 218 |
|
| 219 |
if not YOLO_AVAILABLE:
|
| 220 |
-
return None, "
|
| 221 |
|
| 222 |
try:
|
| 223 |
with warnings.catch_warnings():
|
|
@@ -244,12 +243,12 @@ def run_yolo_segmentation(model_path, confidence, iou, use_gpu):
|
|
| 244 |
state.masks = yolo_results_to_masks(results[0])
|
| 245 |
|
| 246 |
if state.masks is None or state.masks.max() == 0:
|
| 247 |
-
return None, "
|
| 248 |
|
| 249 |
return finalize_segmentation()
|
| 250 |
|
| 251 |
except Exception as e:
|
| 252 |
-
return None, f"
|
| 253 |
|
| 254 |
def yolo_results_to_masks(result):
|
| 255 |
"""Convert YOLO results to mask format."""
|
|
@@ -283,10 +282,10 @@ def finalize_segmentation():
|
|
| 283 |
# Create cell list
|
| 284 |
cell_list = [f"Cell {prop.label} | Area: {prop.area}px²" for prop in state.cell_properties]
|
| 285 |
|
| 286 |
-
return fig, f"
|
| 287 |
|
| 288 |
except Exception as e:
|
| 289 |
-
return None, f"
|
| 290 |
|
| 291 |
def create_visualization(show_numbers=False, highlight_cell=None):
|
| 292 |
"""Create segmentation visualization."""
|
|
@@ -402,15 +401,18 @@ def select_cell(cell_choice):
|
|
| 402 |
# Find cell properties
|
| 403 |
for prop in state.cell_properties:
|
| 404 |
if prop.label == cell_id:
|
| 405 |
-
details = f"
|
| 406 |
-
details += "
|
| 407 |
details += f"Area: {prop.area}px²\n"
|
| 408 |
details += f"Centroid: ({prop.centroid[1]:.0f}, {prop.centroid[0]:.0f})\n"
|
| 409 |
details += f"Eccentricity: {prop.eccentricity:.3f}\n"
|
| 410 |
details += f"Solidity: {prop.solidity:.3f}\n"
|
| 411 |
details += f"Intensity: {prop.mean_intensity:.1f}\n"
|
| 412 |
-
|
| 413 |
-
|
|
|
|
|
|
|
|
|
|
| 414 |
|
| 415 |
# Update visualization
|
| 416 |
fig = create_visualization(show_numbers=False, highlight_cell=cell_id)
|
|
@@ -418,7 +420,7 @@ def select_cell(cell_choice):
|
|
| 418 |
|
| 419 |
return None, "Cell not found"
|
| 420 |
except Exception as e:
|
| 421 |
-
return None, f"
|
| 422 |
|
| 423 |
def run_segmentation(method, cp_model, diameter, yolo_model, confidence, iou, use_gpu):
|
| 424 |
"""Run segmentation based on selected method."""
|
|
@@ -430,7 +432,7 @@ def run_segmentation(method, cp_model, diameter, yolo_model, confidence, iou, us
|
|
| 430 |
def save_results():
|
| 431 |
"""Save segmentation results."""
|
| 432 |
if state.masks is None:
|
| 433 |
-
return None, "
|
| 434 |
|
| 435 |
try:
|
| 436 |
import tempfile
|
|
@@ -451,41 +453,31 @@ def save_results():
|
|
| 451 |
f"{prop.centroid[0]:.1f},{prop.eccentricity:.3f},"
|
| 452 |
f"{prop.solidity:.3f},{prop.mean_intensity:.1f}\n")
|
| 453 |
|
| 454 |
-
return [mask_path, csv_path], "
|
| 455 |
except Exception as e:
|
| 456 |
-
return None, f"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
|
| 458 |
# Create Gradio interface
|
| 459 |
with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
| 460 |
-
gr.Markdown("#
|
| 461 |
|
| 462 |
with gr.Row():
|
| 463 |
-
# LEFT COLUMN - Image
|
| 464 |
with gr.Column(scale=1):
|
| 465 |
-
gr.Markdown("###
|
| 466 |
-
|
| 467 |
-
folder_input = gr.Textbox(
|
| 468 |
-
label="Folder Path",
|
| 469 |
-
value="./imgs",
|
| 470 |
-
placeholder="Enter folder path"
|
| 471 |
-
)
|
| 472 |
|
| 473 |
-
with gr.Row():
|
| 474 |
-
scan_button = gr.Button("Scan Folder", variant="primary")
|
| 475 |
-
|
| 476 |
-
image_count = gr.Textbox(label="Images Found", value="0 images", interactive=False)
|
| 477 |
-
|
| 478 |
-
csv_file = gr.File(label="Upload CSV (optional)", file_types=[".csv"])
|
| 479 |
-
csv_status = gr.Textbox(label="CSV Status", interactive=False, visible=False)
|
| 480 |
-
|
| 481 |
-
# Image gallery (thumbnails)
|
| 482 |
image_gallery = gr.Gallery(
|
| 483 |
-
|
|
|
|
| 484 |
show_label=True,
|
| 485 |
elem_id="gallery",
|
| 486 |
columns=1,
|
| 487 |
rows=None,
|
| 488 |
-
height=
|
| 489 |
object_fit="contain"
|
| 490 |
)
|
| 491 |
|
|
@@ -493,7 +485,7 @@ with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
|
| 493 |
|
| 494 |
# CENTER COLUMN - Image View
|
| 495 |
with gr.Column(scale=2):
|
| 496 |
-
gr.Markdown("###
|
| 497 |
|
| 498 |
with gr.Row():
|
| 499 |
view_mode = gr.Radio(
|
|
@@ -507,7 +499,7 @@ with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
|
| 507 |
|
| 508 |
# RIGHT COLUMN - Controls & Results
|
| 509 |
with gr.Column(scale=1):
|
| 510 |
-
gr.Markdown("###
|
| 511 |
|
| 512 |
method = gr.Radio(
|
| 513 |
["Cellpose", "YOLO"],
|
|
@@ -532,9 +524,9 @@ with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
|
| 532 |
|
| 533 |
use_gpu = gr.Checkbox(label="Use GPU", value=False)
|
| 534 |
|
| 535 |
-
run_button = gr.Button("
|
| 536 |
|
| 537 |
-
gr.Markdown("###
|
| 538 |
|
| 539 |
cell_dropdown = gr.Dropdown(
|
| 540 |
label="Select Cell",
|
|
@@ -542,14 +534,14 @@ with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
|
| 542 |
interactive=True
|
| 543 |
)
|
| 544 |
|
| 545 |
-
gr.Markdown("###
|
| 546 |
cell_details = gr.Textbox(
|
| 547 |
label="",
|
| 548 |
lines=12,
|
| 549 |
interactive=False
|
| 550 |
)
|
| 551 |
|
| 552 |
-
save_button = gr.Button("
|
| 553 |
output_files = gr.File(label="Download", file_count="multiple")
|
| 554 |
|
| 555 |
# Event handlers
|
|
@@ -561,18 +553,6 @@ with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
|
| 561 |
|
| 562 |
method.change(toggle_method, inputs=[method], outputs=[cellpose_group, yolo_group])
|
| 563 |
|
| 564 |
-
scan_button.click(
|
| 565 |
-
scan_folder,
|
| 566 |
-
inputs=[folder_input],
|
| 567 |
-
outputs=[image_gallery, status_text, image_count]
|
| 568 |
-
)
|
| 569 |
-
|
| 570 |
-
csv_file.change(
|
| 571 |
-
load_csv_file,
|
| 572 |
-
inputs=[csv_file],
|
| 573 |
-
outputs=[csv_status, cell_details]
|
| 574 |
-
)
|
| 575 |
-
|
| 576 |
image_gallery.select(
|
| 577 |
select_image_from_gallery,
|
| 578 |
outputs=[image_display, status_text, cell_details, cell_dropdown]
|
|
@@ -606,20 +586,6 @@ with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
|
| 606 |
save_results,
|
| 607 |
outputs=[output_files, status_text]
|
| 608 |
)
|
| 609 |
-
|
| 610 |
-
gr.Markdown("""
|
| 611 |
-
### 📝 Instructions:
|
| 612 |
-
1. Enter folder path and click "Scan Folder"
|
| 613 |
-
2. Click on image thumbnail in gallery to select
|
| 614 |
-
3. (Optional) Upload train.csv for category information
|
| 615 |
-
4. Choose segmentation method (Cellpose or YOLO)
|
| 616 |
-
5. Configure parameters
|
| 617 |
-
6. Click "Run Segmentation"
|
| 618 |
-
7. View results in overlay mode
|
| 619 |
-
8. Select cells from dropdown to view details and highlight
|
| 620 |
-
9. Toggle "Show Cell Numbers" to display labels
|
| 621 |
-
10. Save results to download masks and measurements
|
| 622 |
-
""")
|
| 623 |
|
| 624 |
if __name__ == "__main__":
|
| 625 |
demo.launch(share=False)
|
|
|
|
| 21 |
except ImportError:
|
| 22 |
YOLO_AVAILABLE = False
|
| 23 |
|
| 24 |
+
# Configuration
|
| 25 |
+
IMAGE_FOLDER = "./imgs"
|
| 26 |
+
CSV_FILE = "train.csv"
|
| 27 |
+
|
| 28 |
# Category names mapping (0-27)
|
| 29 |
CATEGORY_NAMES = {
|
| 30 |
0: "Nucleoplasm", 1: "Nuclear membrane", 2: "Nucleoli",
|
|
|
|
| 64 |
return name_without_ext.replace(color, '')
|
| 65 |
return name_without_ext
|
| 66 |
|
| 67 |
+
def load_csv_data():
|
| 68 |
+
"""Auto-load CSV file."""
|
| 69 |
+
if not os.path.exists(CSV_FILE):
|
| 70 |
+
return
|
| 71 |
|
| 72 |
try:
|
| 73 |
+
state.csv_data = pd.read_csv(CSV_FILE)
|
| 74 |
state.image_categories = {}
|
| 75 |
|
| 76 |
for _, row in state.csv_data.iterrows():
|
|
|
|
| 83 |
'indices': category_indices,
|
| 84 |
'names': category_names
|
| 85 |
}
|
|
|
|
|
|
|
| 86 |
except Exception as e:
|
| 87 |
+
print(f"Could not load CSV: {e}")
|
| 88 |
|
| 89 |
+
def scan_folder():
|
| 90 |
+
"""Auto-scan folder for images."""
|
| 91 |
+
if not os.path.exists(IMAGE_FOLDER) or not os.path.isdir(IMAGE_FOLDER):
|
| 92 |
+
return None
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
try:
|
| 95 |
extensions = {'.png', '.jpg', '.jpeg', '.tif', '.tiff', '.bmp'}
|
| 96 |
state.image_files = []
|
| 97 |
|
| 98 |
+
for f in sorted(Path(IMAGE_FOLDER).iterdir()):
|
| 99 |
if f.suffix.lower() in extensions:
|
| 100 |
state.image_files.append(str(f))
|
| 101 |
|
| 102 |
if len(state.image_files) == 0:
|
| 103 |
+
return None
|
| 104 |
|
| 105 |
# Generate gallery
|
| 106 |
gallery_items = [(img, os.path.basename(img)) for img in state.image_files]
|
| 107 |
+
return gallery_items
|
|
|
|
| 108 |
except Exception as e:
|
| 109 |
+
print(f"Scan error: {e}")
|
| 110 |
+
return None
|
| 111 |
|
| 112 |
def prepare_image_for_yolo(image):
|
| 113 |
"""Convert grayscale to RGB for YOLO."""
|
|
|
|
| 123 |
def select_image_from_gallery(evt: gr.SelectData):
|
| 124 |
"""Handle image selection from gallery."""
|
| 125 |
if not state.image_files or evt.index >= len(state.image_files):
|
| 126 |
+
return None, "Invalid selection", "", gr.update(choices=[])
|
| 127 |
|
| 128 |
state.selected_image = state.image_files[evt.index]
|
| 129 |
|
|
|
|
| 146 |
# Show original image
|
| 147 |
fig = create_visualization(show_numbers=False)
|
| 148 |
|
| 149 |
+
return fig, f"Loaded: {os.path.basename(state.selected_image)}", categories_text, gr.update(choices=[])
|
| 150 |
except Exception as e:
|
| 151 |
+
return None, f"Load failed: {str(e)}", "", gr.update(choices=[])
|
| 152 |
|
| 153 |
def get_image_categories():
|
| 154 |
"""Get category information for selected image."""
|
|
|
|
| 159 |
categories = state.image_categories.get(img_id)
|
| 160 |
|
| 161 |
if categories:
|
| 162 |
+
result = "Image Categories\n" + "=" * 30 + "\n"
|
| 163 |
for idx, name in zip(categories['indices'], categories['names']):
|
| 164 |
result += f"[{idx}] {name}\n"
|
| 165 |
return result
|
|
|
|
| 168 |
def run_cellpose_segmentation(model_type, diameter, use_gpu):
|
| 169 |
"""Run Cellpose segmentation."""
|
| 170 |
if state.current_image is None:
|
| 171 |
+
return None, "No image selected", gr.update(choices=[])
|
| 172 |
|
| 173 |
if not CELLPOSE_AVAILABLE:
|
| 174 |
+
return None, "Cellpose not installed", gr.update(choices=[])
|
| 175 |
|
| 176 |
try:
|
| 177 |
with warnings.catch_warnings():
|
|
|
|
| 203 |
)
|
| 204 |
|
| 205 |
if state.masks is None or state.masks.max() == 0:
|
| 206 |
+
return None, "No cells detected", gr.update(choices=[])
|
| 207 |
|
| 208 |
return finalize_segmentation()
|
| 209 |
|
| 210 |
except Exception as e:
|
| 211 |
+
return None, f"Error: {str(e)}", gr.update(choices=[])
|
| 212 |
|
| 213 |
def run_yolo_segmentation(model_path, confidence, iou, use_gpu):
|
| 214 |
"""Run YOLO segmentation."""
|
| 215 |
if state.current_image is None:
|
| 216 |
+
return None, "No image selected", gr.update(choices=[])
|
| 217 |
|
| 218 |
if not YOLO_AVAILABLE:
|
| 219 |
+
return None, "YOLO not installed", gr.update(choices=[])
|
| 220 |
|
| 221 |
try:
|
| 222 |
with warnings.catch_warnings():
|
|
|
|
| 243 |
state.masks = yolo_results_to_masks(results[0])
|
| 244 |
|
| 245 |
if state.masks is None or state.masks.max() == 0:
|
| 246 |
+
return None, "No objects detected", gr.update(choices=[])
|
| 247 |
|
| 248 |
return finalize_segmentation()
|
| 249 |
|
| 250 |
except Exception as e:
|
| 251 |
+
return None, f"Error: {str(e)}", gr.update(choices=[])
|
| 252 |
|
| 253 |
def yolo_results_to_masks(result):
|
| 254 |
"""Convert YOLO results to mask format."""
|
|
|
|
| 282 |
# Create cell list
|
| 283 |
cell_list = [f"Cell {prop.label} | Area: {prop.area}px²" for prop in state.cell_properties]
|
| 284 |
|
| 285 |
+
return fig, f"{state.masks.max()} cells detected", gr.update(choices=cell_list)
|
| 286 |
|
| 287 |
except Exception as e:
|
| 288 |
+
return None, f"Error: {str(e)}", gr.update(choices=[])
|
| 289 |
|
| 290 |
def create_visualization(show_numbers=False, highlight_cell=None):
|
| 291 |
"""Create segmentation visualization."""
|
|
|
|
| 401 |
# Find cell properties
|
| 402 |
for prop in state.cell_properties:
|
| 403 |
if prop.label == cell_id:
|
| 404 |
+
details = f"Cell {cell_id}\n"
|
| 405 |
+
details += "=" * 25 + "\n"
|
| 406 |
details += f"Area: {prop.area}px²\n"
|
| 407 |
details += f"Centroid: ({prop.centroid[1]:.0f}, {prop.centroid[0]:.0f})\n"
|
| 408 |
details += f"Eccentricity: {prop.eccentricity:.3f}\n"
|
| 409 |
details += f"Solidity: {prop.solidity:.3f}\n"
|
| 410 |
details += f"Intensity: {prop.mean_intensity:.1f}\n"
|
| 411 |
+
|
| 412 |
+
# Add categories if available
|
| 413 |
+
categories = get_image_categories()
|
| 414 |
+
if categories:
|
| 415 |
+
details += "\n" + categories
|
| 416 |
|
| 417 |
# Update visualization
|
| 418 |
fig = create_visualization(show_numbers=False, highlight_cell=cell_id)
|
|
|
|
| 420 |
|
| 421 |
return None, "Cell not found"
|
| 422 |
except Exception as e:
|
| 423 |
+
return None, f"Error: {str(e)}"
|
| 424 |
|
| 425 |
def run_segmentation(method, cp_model, diameter, yolo_model, confidence, iou, use_gpu):
|
| 426 |
"""Run segmentation based on selected method."""
|
|
|
|
| 432 |
def save_results():
|
| 433 |
"""Save segmentation results."""
|
| 434 |
if state.masks is None:
|
| 435 |
+
return None, "No results to save"
|
| 436 |
|
| 437 |
try:
|
| 438 |
import tempfile
|
|
|
|
| 453 |
f"{prop.centroid[0]:.1f},{prop.eccentricity:.3f},"
|
| 454 |
f"{prop.solidity:.3f},{prop.mean_intensity:.1f}\n")
|
| 455 |
|
| 456 |
+
return [mask_path, csv_path], "Results saved"
|
| 457 |
except Exception as e:
|
| 458 |
+
return None, f"Error: {str(e)}"
|
| 459 |
+
|
| 460 |
+
# Initialize: Load CSV and scan folder
|
| 461 |
+
load_csv_data()
|
| 462 |
+
initial_gallery = scan_folder()
|
| 463 |
|
| 464 |
# Create Gradio interface
|
| 465 |
with gr.Blocks(title="Cell Segmentation Tool", theme=gr.themes.Soft()) as demo:
|
| 466 |
+
gr.Markdown("# Cell Segmentation Application")
|
| 467 |
|
| 468 |
with gr.Row():
|
| 469 |
+
# LEFT COLUMN - Image Gallery
|
| 470 |
with gr.Column(scale=1):
|
| 471 |
+
gr.Markdown("### Image Gallery")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
image_gallery = gr.Gallery(
|
| 474 |
+
value=initial_gallery,
|
| 475 |
+
label=f"{len(state.image_files)} images" if state.image_files else "No images",
|
| 476 |
show_label=True,
|
| 477 |
elem_id="gallery",
|
| 478 |
columns=1,
|
| 479 |
rows=None,
|
| 480 |
+
height=600,
|
| 481 |
object_fit="contain"
|
| 482 |
)
|
| 483 |
|
|
|
|
| 485 |
|
| 486 |
# CENTER COLUMN - Image View
|
| 487 |
with gr.Column(scale=2):
|
| 488 |
+
gr.Markdown("### Image View")
|
| 489 |
|
| 490 |
with gr.Row():
|
| 491 |
view_mode = gr.Radio(
|
|
|
|
| 499 |
|
| 500 |
# RIGHT COLUMN - Controls & Results
|
| 501 |
with gr.Column(scale=1):
|
| 502 |
+
gr.Markdown("### Segmentation Settings")
|
| 503 |
|
| 504 |
method = gr.Radio(
|
| 505 |
["Cellpose", "YOLO"],
|
|
|
|
| 524 |
|
| 525 |
use_gpu = gr.Checkbox(label="Use GPU", value=False)
|
| 526 |
|
| 527 |
+
run_button = gr.Button("Run Segmentation", variant="primary", size="lg")
|
| 528 |
|
| 529 |
+
gr.Markdown("### Detected Cells")
|
| 530 |
|
| 531 |
cell_dropdown = gr.Dropdown(
|
| 532 |
label="Select Cell",
|
|
|
|
| 534 |
interactive=True
|
| 535 |
)
|
| 536 |
|
| 537 |
+
gr.Markdown("### Cell Details")
|
| 538 |
cell_details = gr.Textbox(
|
| 539 |
label="",
|
| 540 |
lines=12,
|
| 541 |
interactive=False
|
| 542 |
)
|
| 543 |
|
| 544 |
+
save_button = gr.Button("Save Results", variant="secondary")
|
| 545 |
output_files = gr.File(label="Download", file_count="multiple")
|
| 546 |
|
| 547 |
# Event handlers
|
|
|
|
| 553 |
|
| 554 |
method.change(toggle_method, inputs=[method], outputs=[cellpose_group, yolo_group])
|
| 555 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
image_gallery.select(
|
| 557 |
select_image_from_gallery,
|
| 558 |
outputs=[image_display, status_text, cell_details, cell_dropdown]
|
|
|
|
| 586 |
save_results,
|
| 587 |
outputs=[output_files, status_text]
|
| 588 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
|
| 590 |
if __name__ == "__main__":
|
| 591 |
demo.launch(share=False)
|