Spaces:
Sleeping
Sleeping
File size: 5,119 Bytes
d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 b2945a5 d1ede70 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | """Gradio app: detect cells in a fluorescence image and return red-channel
grayscale images with cell + nucleus outlines drawn in yellow.
One output image is produced per detected cell, matching the documentation
style: grayscale background + two concentric yellow outlines, nothing else.
"""
from __future__ import annotations
import os
import cv2
import gradio as gr
import numpy as np
from PIL import Image
from quantification import analyze_image
DEFAULT_N_CELLS = 5
DEFAULT_DILATION_RADIUS = 12
OUTLINE_COLOR_BGR_AS_RGB = (255, 255, 0) # yellow in RGB
OUTLINE_THICKNESS = 2
EXAMPLES_DIR = os.path.join(os.path.dirname(__file__), "examples")
DEFAULT_EXAMPLE = os.path.join(EXAMPLES_DIR, "Picture1.jpg")
def _ensure_rgb(arr: np.ndarray) -> np.ndarray:
if arr.ndim == 2:
arr = np.stack([arr, arr, arr], axis=-1)
if arr.shape[2] == 4:
arr = arr[..., :3]
if arr.dtype != np.uint8:
arr = np.clip(arr, 0, 255).astype(np.uint8)
return arr
def _draw_cell_outline(
gray_rgb: np.ndarray,
cell_mask: np.ndarray,
nucleus_mask: np.ndarray,
) -> np.ndarray:
"""Draw the outer (cell) and inner (nucleus) outlines on a copy of `gray_rgb`."""
out = gray_rgb.copy()
for mask in (cell_mask, nucleus_mask):
contours, _ = cv2.findContours(
mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
cv2.drawContours(
out, contours, -1, OUTLINE_COLOR_BGR_AS_RGB, OUTLINE_THICKNESS
)
return out
def process_image(image_path: str | None, n_cells: int, dilation_radius: int):
"""Return a list of one annotated image per detected cell."""
if image_path is None:
return []
image_pil = Image.open(image_path).convert("RGB")
image_rgb = _ensure_rgb(np.array(image_pil))
# Background for outputs: the red channel rendered as a grayscale RGB.
red = image_rgb[..., 0]
gray_rgb = np.stack([red, red, red], axis=-1)
cells = analyze_image(
image_rgb,
n_cells=int(n_cells),
dilation_radius=int(dilation_radius),
)
return [
_draw_cell_outline(gray_rgb, c.cell_mask, c.nucleus_mask) for c in cells
]
def build_demo() -> gr.Blocks:
description = (
"Upload a fluorescence image (RGB: blue = nuclei, red = cytoplasm). "
"The app detects representative cells and returns the red channel as "
"grayscale with the cell + nucleus boundaries drawn in yellow — one "
"output image per cell."
)
with gr.Blocks(title="Cell Boundary Detection") as demo:
gr.Markdown("# Cell Boundary Detection")
gr.Markdown(description)
with gr.Row():
with gr.Column(scale=1):
image_in = gr.Image(
label="Input image",
type="filepath",
value=DEFAULT_EXAMPLE if os.path.exists(DEFAULT_EXAMPLE) else None,
)
n_cells_slider = gr.Slider(
minimum=1,
maximum=10,
value=DEFAULT_N_CELLS,
step=1,
label="Number of cells",
)
dilation_slider = gr.Slider(
minimum=4,
maximum=30,
value=DEFAULT_DILATION_RADIUS,
step=1,
label="Cytoplasm ring thickness (pixels)",
)
run_btn = gr.Button("Detect cells", variant="primary")
with gr.Column(scale=2):
gallery = gr.Gallery(
label="Detected cells (one per image)",
columns=2,
height=620,
show_label=True,
object_fit="contain",
)
run_btn.click(
fn=process_image,
inputs=[image_in, n_cells_slider, dilation_slider],
outputs=[gallery],
)
# Example images (other defaults from prior dataset).
example_files = []
if os.path.isdir(EXAMPLES_DIR):
example_files = sorted(
os.path.join(EXAMPLES_DIR, f)
for f in os.listdir(EXAMPLES_DIR)
if f.lower().endswith((".jpg", ".jpeg", ".png", ".tif", ".tiff"))
)
if example_files:
gr.Examples(
examples=[[p, DEFAULT_N_CELLS, DEFAULT_DILATION_RADIUS]
for p in example_files],
inputs=[image_in, n_cells_slider, dilation_slider],
outputs=[gallery],
fn=process_image,
cache_examples=False,
label="Example images",
)
# Preload outputs for the default image on app start.
if os.path.exists(DEFAULT_EXAMPLE):
demo.load(
fn=process_image,
inputs=[image_in, n_cells_slider, dilation_slider],
outputs=[gallery],
)
return demo
if __name__ == "__main__":
demo = build_demo()
demo.launch() |