File size: 5,804 Bytes
e4a7008
d487db0
70fca4b
 
 
 
d487db0
70fca4b
 
d487db0
 
 
 
 
e4a7008
d487db0
 
70fca4b
d487db0
70fca4b
d487db0
e4a7008
 
 
 
70fca4b
 
e4a7008
94fee7e
70fca4b
94fee7e
 
 
70fca4b
 
 
 
 
 
 
94fee7e
 
 
 
70fca4b
 
e4a7008
70fca4b
e4a7008
 
 
 
 
 
 
 
 
 
70fca4b
e4a7008
 
d487db0
 
e4a7008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d487db0
e4a7008
 
 
 
 
 
 
 
 
 
 
94fee7e
 
 
e4a7008
 
 
 
70fca4b
 
 
 
 
d487db0
 
 
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
"""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 = """
<div style="text-align:center; margin: 0.2rem 0 0.7rem;">
  <h1 style="font-size:2.1rem; font-weight:750; letter-spacing:-0.01em;
             margin:0 0 0.4rem;">Neuron Quantification using AI</h1>
  <div style="font-weight:700; font-size:1.15rem; color:#1a1a1a;">
    Iman Sabir Ezzat, Randa K Ismail, Ayden Chavez, Marisa Zallocchi, PhD, Steven Fernandes, PhD
  </div>
  <div style="font-weight:600; font-size:0.95rem; color:#4b5563; margin-top:0.25rem;">
    MedCLIPSeg variant — text-prompted vision-language fiber tracking
  </div>
</div>
"""

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()