File size: 7,733 Bytes
f9b628d de441de f9b628d de441de f9b628d de441de f9b628d de441de f9b628d de441de f9b628d de441de 686b162 de441de f9b628d de441de f9b628d 8d62536 de441de f9b628d de441de f9b628d de441de f9b628d de441de f9b628d 8d62536 de441de 8d62536 de441de f9b628d 5217f8a de441de f9b628d de441de f9b628d de441de f9b628d de441de f9b628d de441de f9b628d de441de 5217f8a de441de f9b628d de441de 686b162 de441de 686b162 de441de f9b628d de441de 5217f8a f9b628d 5217f8a f9b628d 5217f8a f9b628d de441de f9b628d 5217f8a f9b628d 5217f8a f9b628d de441de 5217f8a de441de e269dff de441de e269dff de441de e269dff de441de e269dff de441de f9b628d de441de 5217f8a de441de 686b162 5217f8a de441de f9b628d de441de | 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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | """
Glaucoma CDSS β HuggingFace Spaces
Input : retinal fundus image (JPEG / PNG)
Output: plain-text clinical report + downloadable PDF
"""
import os
import sys
import tempfile
import cv2
import numpy as np
import gradio as gr
sys.path.insert(0, os.path.dirname(__file__))
from phase3pipeline import Phase3Pipeline
# ββ CONFIG ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
REPO_ID = "Nj-1111/EyeeSEE"
EPOCH = None
TOKEN = os.getenv("HF_TOKEN_2") or os.getenv("HF_TOKEN")
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
print(f"Loading model repo={REPO_ID} epoch={'latest' if EPOCH is None else EPOCH}")
pipeline = Phase3Pipeline(
repo_id=REPO_ID,
epoch=EPOCH,
mc_passes=2,
uncertainty_threshold=0.05,
token=TOKEN,
)
print("Model ready.")
# ββ IMAGE LOADER ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def _load_image(path: str) -> np.ndarray:
img = cv2.imread(path)
if img is None:
raw = np.fromfile(path, dtype=np.uint8)
img = cv2.imdecode(raw, cv2.IMREAD_COLOR)
if img is None:
raise ValueError(f"Cannot read image: {path}")
return img
# ββ REPORT BUILDER ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def _build_text(r: dict, mc_passes: int) -> str:
isnt = r["isnt"]
lines = [
"=" * 52,
" GLAUCOMA CDSS β CLINICAL REPORT",
"=" * 52,
f" vCDR : {r['vcdr']:.4f}",
f" Risk Level : {r['risk_level']}",
f" Uncertainty : {r['uncertainty']:.6f}",
f" Sanity Check : {'PASSED' if r['sanity_passed'] else 'CORRECTED (auto)'}",
f" ISNT Rule : {'Satisfied' if isnt['rule_satisfied'] else 'Violated'}",
"",
" ISNT Rim Thickness",
f" Inferior : {isnt['inferior']:.2f}",
f" Superior : {isnt['superior']:.2f}",
f" Nasal : {isnt['nasal']:.2f}",
f" Temporal : {isnt['temporal']:.2f}",
"",
" Structural",
f" Disc Area : {r['disc_area_px']:,} px",
f" Cup Area : {r['cup_area_px']:,} px",
f" Cup/Disc : {r['cup_area_px'] / max(r['disc_area_px'], 1) * 100:.1f}%",
f" MC Passes : {mc_passes}",
]
warnings = r.get("warnings", [])
if warnings:
lines += ["", " Warnings"]
for w in warnings:
lines.append(f" ! {w}")
lines += [
"",
"=" * 52,
" DISCLAIMER: Research prototype.",
" Not a certified medical device.",
" Validate with a qualified ophthalmologist.",
"=" * 52,
]
return "\n".join(lines)
# ββ INFERENCE βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def analyse(file_path):
if file_path is None:
return "No file uploaded.", None, "Please upload a JPEG or PNG fundus image."
try:
print(f"Received file: {file_path}")
img = _load_image(file_path)
print(f"Image loaded successfully. Shape: {img.shape}")
result = pipeline.run(img)
print("Pipeline inference completed.")
text = _build_text(result["report"], pipeline.mc_passes)
status = "Analysis completed successfully."
return text, status
except Exception as e:
print(f"Analysis error: {e}")
return f"Error: {e}", None, f"Analysis failed: {e}"
def set_busy():
return gr.update(interactive=False), "Running analysis, please wait..."
def set_ready():
return gr.update(interactive=True)
# ββ UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
with gr.Blocks(title="Glaucoma CDSS") as demo:
gr.Markdown(
"## Glaucoma CDSS\n"
"Upload a retinal fundus image to receive a clinical screening report."
)
with gr.Row():
file_in = gr.File(
label="Fundus Image (JPEG / PNG)",
file_types=[".jpg", ".jpeg", ".png"],
type="filepath",
)
run_btn = gr.Button("Analyse", variant="primary")
status_box = gr.Textbox(
label="Status",
value="Awaiting image upload.",
interactive=False,
)
report_box = gr.Textbox(
label="Clinical Report",
lines=28,
interactive=False,
)
gr.Markdown(
"_Research prototype β NOT a medical device. "
"All results must be reviewed by a qualified ophthalmologist._"
)
gr.Markdown(
"""
# Glaucoma CDSS
Upload a retinal fundus image to generate an AI-assisted glaucoma screening report.
---
## How to Interpret the Report
### 1. vCDR (Vertical Cup-to-Disc Ratio)
The most important glaucoma screening metric.
General interpretation:
- **0.30 β 0.50** β Usually within healthy range
- **0.50 β 0.65** β Borderline / monitor carefully
- **0.65 β 0.80** β Glaucoma suspect
- **> 0.80** β High glaucoma risk
Higher values indicate enlargement of the optic cup relative to the optic disc.
---
## 2. ISNT Rule
Healthy optic nerves typically follow:
**Inferior > Superior > Nasal > Temporal**
This is called the **ISNT Rule**.
- **Satisfied** β Anatomically more normal
- **Violated** β Possible neuro-retinal rim thinning associated with glaucoma
ISNT violation alone does NOT confirm glaucoma, but it is an important warning sign.
---
## 3. Uncertainty Score
Represents model confidence.
- **< 0.05** β Stable prediction
- **0.05 β 0.10** β Moderate uncertainty
- **> 0.10** β Low confidence prediction
High uncertainty may occur with:
- poor image quality,
- blur,
- extreme lighting,
- incomplete optic disc visibility.
---
## 4. Structural Measurements
### Disc Area
Estimated optic disc size in pixels.
### Cup Area
Estimated optic cup size in pixels.
### Cup/Disc %
Percentage of cup area relative to disc area.
Larger cup proportions may indicate glaucomatous damage.
---
## 5. Risk Levels
### Healthy
No major structural glaucoma indicators detected.
### Glaucoma Suspect
One or more warning signs detected:
- elevated vCDR,
- ISNT violation,
- anatomical inconsistency,
- or uncertain segmentation.
### High Risk
Strong structural indicators of glaucoma detected.
Clinical ophthalmology review is strongly recommended.
---
## Important Disclaimer
This system is a **research prototype** and NOT a certified medical device.
The generated report is intended for:
- educational use,
- AI research,
- and preliminary screening assistance only.
All clinical decisions must be made by a qualified ophthalmologist.
"""
)
run_btn.click(
fn=set_busy,
inputs=None,
outputs=[run_btn, status_box],
queue=False,
).then(
fn=analyse,
inputs=[file_in],
outputs=[report_box, status_box],
).then(
fn=set_ready,
inputs=None,
outputs=[run_btn],
queue=False,
)
if __name__ == "__main__":
demo.launch() |