mmrech commited on
Commit
9d1f79d
·
verified ·
1 Parent(s): 049aaae

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +167 -2
app.py CHANGED
@@ -2,6 +2,17 @@ import gradio as gr
2
  import numpy as np
3
  from PIL import Image, ImageFilter, ImageEnhance, ImageOps
4
  from transformers import pipeline
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  # ---- Load models (cached on first use) ----
7
  classifier = pipeline("image-classification", model="google/vit-base-patch16-224")
@@ -80,7 +91,7 @@ def detect_objects(image, threshold):
80
  return (image, annotations)
81
 
82
 
83
- # ---- Tab 4: Segmentation ----
84
  def segment_image(image):
85
  if image is None:
86
  raise gr.Error("Please upload an image first.")
@@ -95,6 +106,114 @@ def segment_image(image):
95
  return (image, annotations)
96
 
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  # ---- Build the UI ----
99
  css = """
100
  .main-title { text-align: center; margin-bottom: 0.5em; }
@@ -104,10 +223,56 @@ css = """
104
  with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
105
  gr.Markdown("# Image Processing Studio", elem_classes="main-title")
106
  gr.Markdown(
107
- "Upload an image and explore filters, classification, object detection, and segmentation -- all powered by state-of-the-art models.",
108
  elem_classes="subtitle"
109
  )
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  with gr.Tab("Filters & Effects"):
112
  with gr.Row():
113
  with gr.Column():
 
2
  import numpy as np
3
  from PIL import Image, ImageFilter, ImageEnhance, ImageOps
4
  from transformers import pipeline
5
+ from segment_neuroimaging import (
6
+ segment_nph, segment_ventricles, compute_evans_index,
7
+ compute_callosal_angle, compute_temporal_horn_width,
8
+ compute_third_ventricle_width, score_pvh, assess_desh,
9
+ create_overlay, add_annotations, create_comparison,
10
+ preprocess_image, create_roi_mask, Modality, COLORS
11
+ )
12
+ import cv2
13
+ import json
14
+ import tempfile
15
+ import os
16
 
17
  # ---- Load models (cached on first use) ----
18
  classifier = pipeline("image-classification", model="google/vit-base-patch16-224")
 
91
  return (image, annotations)
92
 
93
 
94
+ # ---- Tab 4: General Segmentation ----
95
  def segment_image(image):
96
  if image is None:
97
  raise gr.Error("Please upload an image first.")
 
106
  return (image, annotations)
107
 
108
 
109
+ # ---- Tab 5: NPH Neuroimaging Analysis ----
110
+ def analyze_nph(image, modality, overlay_alpha):
111
+ if image is None:
112
+ raise gr.Error("Please upload a brain MRI or CT image first.")
113
+
114
+ # Save temp file for the segmentation pipeline
115
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
116
+ Image.fromarray(image).save(f.name)
117
+ temp_path = f.name
118
+
119
+ try:
120
+ # Map display name to modality key
121
+ modality_map = {
122
+ "Axial FLAIR": "FLAIR",
123
+ "Axial T1": "T1",
124
+ "Axial T2": "T2",
125
+ "Coronal T2": "T2",
126
+ "Axial T2 FFE": "T2",
127
+ "Sagittal T1": "T1",
128
+ "CT Head": "CT_HEAD",
129
+ }
130
+ mod_key = modality_map.get(modality, "T1")
131
+ is_coronal = "Coronal" in modality
132
+
133
+ result = segment_nph(
134
+ temp_path,
135
+ modality=mod_key,
136
+ compute_biomarkers=True,
137
+ )
138
+
139
+ # Build biomarker report
140
+ meta = result.metadata
141
+ report_lines = ["## NPH Biomarker Report\n"]
142
+
143
+ # Evans' Index
144
+ ei = meta.get("evans_index")
145
+ if ei is not None:
146
+ status = "**ABNORMAL (>0.3)**" if ei > 0.3 else "Normal"
147
+ report_lines.append(f"**Evans' Index:** {ei:.3f} -- {status}")
148
+
149
+ # Temporal Horn Width
150
+ thw = meta.get("temporal_horn_width_px", 0)
151
+ if thw > 0:
152
+ report_lines.append(f"**Temporal Horn Width:** {thw} px")
153
+
154
+ # Third Ventricle Width
155
+ tvw = meta.get("third_ventricle_width_px", 0)
156
+ if tvw > 0:
157
+ report_lines.append(f"**Third Ventricle Width:** {tvw} px")
158
+
159
+ # PVH Grade (FLAIR only)
160
+ pvh = meta.get("pvh_grade")
161
+ if pvh is not None:
162
+ grade_desc = {0: "None", 1: "Pencil-thin rim", 2: "Smooth halo", 3: "Irregular, deep WM"}
163
+ report_lines.append(f"**PVH Grade:** {pvh}/3 -- {grade_desc.get(pvh, '')}")
164
+
165
+ # DESH Assessment
166
+ desh = meta.get("is_desh_positive")
167
+ if desh is not None:
168
+ desh_str = "**POSITIVE**" if desh else "Negative"
169
+ desh_score = meta.get("desh_total_score", "")
170
+ report_lines.append(f"**DESH Pattern:** {desh_str} (score: {desh_score}/6)")
171
+
172
+ # DESH details
173
+ desh_details = meta.get("desh_details")
174
+ if desh_details:
175
+ report_lines.append(f"\n### DESH Components")
176
+ report_lines.append(f"- Ventriculomegaly: {desh_details.get('ventriculomegaly_score', 'N/A')}/2")
177
+ report_lines.append(f"- Sylvian dilation: {desh_details.get('sylvian_dilation_score', 'N/A')}/2")
178
+ report_lines.append(f"- Convexity tightness: {desh_details.get('convexity_tightness_score', 'N/A')}/2")
179
+
180
+ # Callosal angle for coronal images
181
+ if is_coronal:
182
+ vent_mask = result.masks.get("ventricles")
183
+ if vent_mask is not None:
184
+ ca_data = compute_callosal_angle(vent_mask)
185
+ ca = ca_data.get("callosal_angle_deg")
186
+ if ca is not None:
187
+ ca_status = "Suggestive of NPH" if ca < 90 else ("Indeterminate" if ca < 120 else "Normal/ex vacuo")
188
+ report_lines.append(f"\n**Callosal Angle:** {ca:.1f} deg -- {ca_status}")
189
+
190
+ report_lines.append("\n---")
191
+ report_lines.append("*Structures segmented:* " + ", ".join(meta.get("structures_found", [])))
192
+ report_lines.append("\n*Note: Pixel-based measurements without DICOM spacing are approximate.*")
193
+
194
+ report = "\n".join(report_lines)
195
+
196
+ # Build overlay with custom alpha
197
+ display_masks = {k: v for k, v in result.masks.items() if k != "skull"}
198
+ img_rgb, gray, _ = preprocess_image(temp_path)
199
+ overlay = create_overlay(img_rgb, display_masks, alpha=overlay_alpha)
200
+
201
+ biomarkers_for_annotation = {
202
+ k: v for k, v in meta.items()
203
+ if k in ("evans_index", "callosal_angle_deg", "temporal_horn_width_px",
204
+ "pvh_grade", "is_desh_positive")
205
+ }
206
+ annotated = add_annotations(overlay, display_masks, f"{modality} -- NPH Analysis", biomarkers_for_annotation)
207
+
208
+ # Side-by-side comparison
209
+ comparison = create_comparison(img_rgb, annotated, f"{modality} -- NPH Analysis")
210
+
211
+ return annotated, comparison, report
212
+
213
+ finally:
214
+ os.unlink(temp_path)
215
+
216
+
217
  # ---- Build the UI ----
218
  css = """
219
  .main-title { text-align: center; margin-bottom: 0.5em; }
 
223
  with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
224
  gr.Markdown("# Image Processing Studio", elem_classes="main-title")
225
  gr.Markdown(
226
+ "Upload an image and explore filters, classification, object detection, segmentation, and **NPH neuroimaging analysis** -- all powered by state-of-the-art models.",
227
  elem_classes="subtitle"
228
  )
229
 
230
+ with gr.Tab("NPH Analysis"):
231
+ gr.Markdown(
232
+ "### Normal Pressure Hydrocephalus -- Neuroimaging Segmentation & Biomarkers\n"
233
+ "Upload a brain MRI or CT scan to compute Evans' index, DESH pattern, PVH scoring, "
234
+ "callosal angle (coronal), and more."
235
+ )
236
+ with gr.Row():
237
+ with gr.Column():
238
+ nph_input = gr.Image(label="Upload Brain Scan", type="numpy")
239
+ nph_modality = gr.Dropdown(
240
+ choices=[
241
+ "Axial FLAIR", "Axial T1", "Axial T2",
242
+ "Coronal T2", "Axial T2 FFE", "Sagittal T1",
243
+ "CT Head"
244
+ ],
245
+ value="Axial FLAIR",
246
+ label="Modality / Sequence"
247
+ )
248
+ nph_alpha = gr.Slider(
249
+ minimum=0.1, maximum=0.9, value=0.45, step=0.05,
250
+ label="Overlay Opacity"
251
+ )
252
+ nph_btn = gr.Button("Analyze for NPH", variant="primary")
253
+ with gr.Column():
254
+ nph_overlay = gr.Image(label="Segmentation Overlay", type="numpy")
255
+ nph_comparison = gr.Image(label="Side-by-Side Comparison", type="numpy")
256
+
257
+ nph_report = gr.Markdown(label="Biomarker Report")
258
+
259
+ nph_btn.click(
260
+ fn=analyze_nph,
261
+ inputs=[nph_input, nph_modality, nph_alpha],
262
+ outputs=[nph_overlay, nph_comparison, nph_report]
263
+ )
264
+
265
+ gr.Markdown(
266
+ "#### NPH Reference Values\n"
267
+ "| Biomarker | Normal | Suggestive of NPH |\n"
268
+ "|---|---|---|\n"
269
+ "| Evans' Index | < 0.3 | > 0.3 |\n"
270
+ "| Callosal Angle | > 120 deg | < 90 deg |\n"
271
+ "| Temporal Horn | < 2 mm | > 5 mm |\n"
272
+ "| PVH (FLAIR) | Grade 0 | Grade 2-3 |\n"
273
+ "| DESH Pattern | Absent | Present |"
274
+ )
275
+
276
  with gr.Tab("Filters & Effects"):
277
  with gr.Row():
278
  with gr.Column():