"""Neuron Quantification using AI — MedCLIPSeg variant.
Applies the MedCLIPSeg (CVPR 2026) vision-language approach: a text prompt
describes the target ("nerve fibers ..."), a frozen CLIP backbone produces a
fiber-probability map, and that map is traced into a skeleton. The app reports a
white-on-black **tracked skeleton** and its **total trace length** in microns.
Zero-shot general CLIP is only a coarse prior on this domain — train the bundled
model (reference_medclipseg/) on a GPU with masks for real segmentation quality.
"""
import traceback
import gradio as gr
import processing as P
import medclipseg as MC
def analyze(file_obj, fg_prompt, threshold):
if file_obj is None:
return None, None, "Upload a CZI or TIFF z-stack."
try:
img = P.load_image(file_obj)
nf, _ = P.guess_channels(img)
nf_mip = P.channel_preview(img.data[nf])
if fg_prompt and fg_prompt.strip():
MC.FG_PROMPTS = [p.strip() for p in fg_prompt.split("|") if p.strip()]
# --- MedCLIP text-prompted region prior -> raw-signal fiber geometry ---
prob, _ = MC.segment_best(nf_mip)
trace = P.trace_fibers_medclip_gated(nf_mip, prob, img.voxel,
gate_threshold=float(threshold),
prune_um=3.0)
m = P.compute_metrics(trace, "MedCLIP", min_fiber_um=5.0)
skel_img = P.skeleton_image(trace.skeleton, dilate=1) # white on black
model = ("few-shot trained decoder" if MC.has_trained_model()
else "zero-shot CLIP prior")
status = (f"**MedCLIP total trace length: "
f"{m.total_length_um:,.1f} µm.**\n\n"
f"Fiber geometry traced from the raw neurofilament signal "
f"(Sato tubeness), gated by the MedCLIP region prior "
f"({model}, text-prompted) at semantic gate "
f"{float(threshold):.2f}. "
f"Prompt(s): *{', '.join(MC.FG_PROMPTS)}*.")
return nf_mip, skel_img, status
except Exception as e: # noqa: BLE001
return None, None, f"Error:\n{e}\n{traceback.format_exc()}"
HEADER_HTML = """
Neuron Quantification using AI
Iman Sabir Ezzat, Randa K Ismail, Ayden Chavez, Marisa Zallocchi, PhD, Steven Fernandes, PhD
MedCLIPSeg variant — text-prompted vision-language fiber tracking
"""
THEME = gr.themes.Base(
primary_hue=gr.themes.colors.slate, secondary_hue=gr.themes.colors.slate,
neutral_hue=gr.themes.colors.gray,
font=["system-ui", "-apple-system", "Segoe UI", "Roboto", "sans-serif"],
).set(
body_background_fill="#ffffff", body_text_color="#1a1a1a",
background_fill_primary="#ffffff", background_fill_secondary="#f7f7f8",
block_background_fill="#ffffff", block_border_color="#e5e7eb",
block_label_text_color="#1a1a1a", block_title_text_color="#1a1a1a",
border_color_primary="#e5e7eb", button_primary_background_fill="#1f2937",
button_primary_text_color="#ffffff", input_background_fill="#ffffff",
input_border_color="#c0c5cc",
body_background_fill_dark="#ffffff", body_text_color_dark="#1a1a1a",
background_fill_primary_dark="#ffffff", background_fill_secondary_dark="#f7f7f8",
block_background_fill_dark="#ffffff", block_border_color_dark="#e5e7eb",
block_label_text_color_dark="#1a1a1a", block_title_text_color_dark="#1a1a1a",
panel_background_fill_dark="#ffffff", border_color_primary_dark="#e5e7eb",
button_primary_background_fill_dark="#1f2937",
button_primary_text_color_dark="#ffffff", input_background_fill_dark="#ffffff",
)
CSS = """
.gradio-container { max-width: 1200px !important; margin: 0 auto !important; }
:root, .dark {
color-scheme: light; --body-background-fill:#ffffff;
--background-fill-primary:#ffffff; --block-background-fill:#ffffff;
--body-text-color:#1a1a1a; --block-label-text-color:#1a1a1a;
--block-title-text-color:#1a1a1a; --border-color-primary:#e5e7eb;
--input-background-fill:#ffffff; --neutral-950:#1a1a1a;
}
body, gradio-app, .gradio-container, .dark { background:#ffffff !important; color:#1a1a1a !important; }
"""
with gr.Blocks(title="Neuron Quantification using AI — MedCLIPSeg",
theme=THEME, css=CSS) as demo:
gr.HTML(HEADER_HTML)
with gr.Row():
with gr.Column(scale=1):
file_in = gr.File(label="Neurofilament z-stack (.czi / .tif)",
type="filepath")
prompt = gr.Textbox(
label="Text prompt(s) for the target (separate with | )",
value="a fluorescence microscopy image of nerve fibers | "
"neurofilament nerve fibers and axons")
thresh = gr.Slider(0.2, 0.8, value=0.4, step=0.05,
label="MedCLIP semantic gate (region prior) — "
"lower = include more of the band")
btn = gr.Button("Analyze", variant="primary")
with gr.Column(scale=2):
status = gr.Markdown()
with gr.Row():
out_orig = gr.Image(label="Neurofilament (MIP)", height=280)
out_skel = gr.Image(label="Tracked skeleton (white on black)",
height=280)
btn.click(analyze, [file_in, prompt, thresh],
[out_orig, out_skel, status])
if __name__ == "__main__":
demo.launch()