TheSeriousProgrammer
added files
3f658fa
import gradio as gr
import numpy as np
import pandas as pd
from csbdeep.utils import normalize
from skimage.color import rgb2gray
from skimage.measure import regionprops
from skimage.morphology import binary_closing
from skimage.util import img_as_ubyte
from skimage.measure import shannon_entropy
from stardist.models import StarDist2D
from stardist.plot import render_label
MODEL_NAMES = [
"2D_versatile_fluo",
"2D_versatile_he",
"2D_paper_dsb2018",
]
_MODEL_CACHE = {}
def get_model(name: str) -> StarDist2D:
if name not in _MODEL_CACHE:
_MODEL_CACHE[name] = StarDist2D.from_pretrained(name)
return _MODEL_CACHE[name]
def to_gray(image: np.ndarray) -> np.ndarray:
if image.ndim == 2:
return image
return rgb2gray(image)
def box_counting_fd(mask: np.ndarray) -> float:
if mask.sum() == 0:
return 0.0
sizes = np.array([2, 4, 8, 16, 32, 64])
sizes = sizes[sizes <= min(mask.shape)]
counts = []
for size in sizes:
shape = (
int(np.ceil(mask.shape[0] / size)),
int(np.ceil(mask.shape[1] / size)),
)
pad_h = shape[0] * size - mask.shape[0]
pad_w = shape[1] * size - mask.shape[1]
padded = np.pad(mask, ((0, pad_h), (0, pad_w)), mode="constant")
blocks = padded.reshape(shape[0], size, shape[1], size)
blocks = blocks.any(axis=(1, 3))
counts.append(np.count_nonzero(blocks))
counts = np.array(counts)
sizes = sizes[counts > 0]
counts = counts[counts > 0]
if len(counts) < 2:
return 0.0
coeffs = np.polyfit(np.log(1 / sizes), np.log(counts), 1)
return float(coeffs[0])
def compute_metrics(
labels: np.ndarray, intensity_image: np.ndarray, width_units: float
):
props = regionprops(labels, intensity_image=intensity_image)
image_width = labels.shape[1]
pixel_size = (width_units / image_width) if image_width > 0 else 0.0
rows = []
for region in props:
area_px = float(region.area)
perimeter_px = float(region.perimeter)
major_px = float(region.major_axis_length) if region.major_axis_length else 0.0
minor_px = float(region.minor_axis_length) if region.minor_axis_length else 0.0
area = area_px * (pixel_size**2)
perimeter = perimeter_px * pixel_size
major = major_px * pixel_size
minor = minor_px * pixel_size
aspect_ratio = major / minor if minor > 0 else 0.0
circularity = (4 * np.pi * area / (perimeter**2)) if perimeter > 0 else 0.0
roundness = (4 * area / (np.pi * major**2)) if major > 0 else 0.0
region_mask = labels == region.label
region_mask = binary_closing(region_mask)
entropy_val = float(
shannon_entropy(region.intensity_image[region.image], base=2)
)
fractal_dim = box_counting_fd(region_mask)
integrated_density = float(region.intensity_image.sum()) * (pixel_size**2)
ecc_rel = float(region.eccentricity * major)
rows.append(
{
"Label": int(region.label),
"Area": area,
"Perimeter": perimeter,
"Aspect ratio": aspect_ratio,
"Circularity": circularity,
"Roundness": roundness,
"Entropy": entropy_val,
"Fractal dimension": fractal_dim,
"Integrated density": integrated_density,
"Eccentricity (rel width)": ecc_rel,
}
)
metrics_df = pd.DataFrame(rows)
avg_df = pd.DataFrame()
if not metrics_df.empty:
numeric_cols = metrics_df.columns.drop("Label")
avg_row = {"Metric": "Average"}
avg_row.update(metrics_df[numeric_cols].mean().to_dict())
avg_df = pd.DataFrame([avg_row])
return metrics_df, avg_df
def run_inference(image: np.ndarray, model_name: str, width_units: float):
if image is None:
return None, None, None
if width_units <= 0:
width_units = 1.0
model = get_model(model_name)
image_input = image.copy()
if model_name == "2D_versatile_fluo":
image_input = to_gray(image_input)
image_norm = normalize(image_input, 1, 99.8, axis=(0, 1))
labels, _ = model.predict_instances(image_norm)
overlay = render_label(labels, img=image)
if np.issubdtype(overlay.dtype, np.floating):
overlay = np.clip(overlay, 0, 1)
overlay = img_as_ubyte(overlay)
intensity_image = to_gray(image)
metrics_df, avg_df = compute_metrics(labels, intensity_image, width_units)
return overlay, metrics_df, avg_df
with gr.Blocks(title="StarDist 2D Segmentation - HF app by Ram Sevuggan") as demo:
gr.Markdown("# StarDist 2D Segmentation - HF app by Ram Sevuggan")
with gr.Row():
with gr.Column():
input_image = gr.Image(label="Input image", type="numpy")
model_dropdown = gr.Dropdown(
choices=MODEL_NAMES,
value="2D_versatile_fluo",
label="Model",
)
width_units = gr.Number(
value=1.0,
minimum=1e-6,
label="Image width (units)",
info="Used for eccentricity relative to image width",
)
run_button = gr.Button("Run")
with gr.Column():
output_image = gr.Image(label="Overlay", type="numpy")
metrics_table = gr.Dataframe(
label="Object metrics",
interactive=False,
)
avg_table = gr.Dataframe(
label="Average metrics",
interactive=False,
)
run_button.click(
fn=run_inference,
inputs=[input_image, model_dropdown, width_units],
outputs=[output_image, metrics_table, avg_table],
)
if __name__ == "__main__":
demo.launch()