luigi12345
commited on
Commit
•
4234218
1
Parent(s):
f192f0b
app.py
CHANGED
@@ -8,6 +8,10 @@ import streamlit as st
|
|
8 |
from PIL import Image
|
9 |
import io
|
10 |
import zipfile
|
|
|
|
|
|
|
|
|
11 |
|
12 |
# --- GlaucomaModel Class ---
|
13 |
class GlaucomaModel(object):
|
@@ -108,6 +112,73 @@ def get_confidence_level(confidence):
|
|
108 |
else:
|
109 |
return "Very Low"
|
110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
# --- Streamlit Interface ---
|
112 |
def main():
|
113 |
st.set_page_config(layout="wide", page_title="Glaucoma Screening Tool")
|
@@ -132,63 +203,54 @@ def main():
|
|
132 |
help="Images with confidence above this threshold will be marked as reliable predictions")
|
133 |
|
134 |
if uploaded_files:
|
135 |
-
|
136 |
-
|
137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
|
139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
st.markdown("### 🔍 Classification")
|
157 |
-
diagnosis = model.cls_id2label[disease_idx]
|
158 |
-
is_confident = cls_conf >= confidence_threshold
|
159 |
-
|
160 |
-
if diagnosis == "Glaucoma":
|
161 |
-
st.markdown(f"<div style='padding: 10px; background-color: #ffebee; border-radius: 5px;'>"
|
162 |
-
f"<h4 style='color: #c62828;'>Diagnosis: {diagnosis}</h4></div>",
|
163 |
-
unsafe_allow_html=True)
|
164 |
-
else:
|
165 |
-
st.markdown(f"<div style='padding: 10px; background-color: #e8f5e9; border-radius: 5px;'>"
|
166 |
-
f"<h4 style='color: #2e7d32;'>Diagnosis: {diagnosis}</h4></div>",
|
167 |
-
unsafe_allow_html=True)
|
168 |
-
|
169 |
-
st.write(f"Classification Confidence: {cls_conf:.1f}%")
|
170 |
-
if not is_confident:
|
171 |
-
st.warning("⚠️ Below confidence threshold")
|
172 |
-
|
173 |
-
# Segmentation Results
|
174 |
-
st.markdown("### 🎯 Segmentation Quality")
|
175 |
-
st.write(f"Optic Cup Confidence: {cup_conf:.1f}%")
|
176 |
-
st.write(f"Optic Disc Confidence: {disc_conf:.1f}%")
|
177 |
-
|
178 |
-
cup_level = get_confidence_level(cup_conf)
|
179 |
-
disc_level = get_confidence_level(disc_conf)
|
180 |
-
st.info(f"Cup Detection: {cup_level}\nDisc Detection: {disc_level}")
|
181 |
-
|
182 |
-
# Clinical Metrics
|
183 |
-
st.markdown("### 📏 Clinical Metrics")
|
184 |
-
st.write(f"Cup-to-Disc Ratio (CDR): {vcdr:.3f}")
|
185 |
-
|
186 |
-
if vcdr > 0.7:
|
187 |
-
st.warning("⚠️ Elevated CDR (>0.7)")
|
188 |
-
elif vcdr > 0.5:
|
189 |
-
st.info("ℹ️ Borderline CDR (0.5-0.7)")
|
190 |
-
else:
|
191 |
-
st.success("✅ Normal CDR (<0.5)")
|
192 |
-
|
193 |
-
st.markdown("---")
|
194 |
-
# ... rest of the code remains the same ...
|
|
|
8 |
from PIL import Image
|
9 |
import io
|
10 |
import zipfile
|
11 |
+
import pandas as pd
|
12 |
+
from datetime import datetime
|
13 |
+
import os
|
14 |
+
import tempfile
|
15 |
|
16 |
# --- GlaucomaModel Class ---
|
17 |
class GlaucomaModel(object):
|
|
|
112 |
else:
|
113 |
return "Very Low"
|
114 |
|
115 |
+
def process_batch(model, images_data, progress_bar=None):
|
116 |
+
results = []
|
117 |
+
for idx, (file_name, image) in enumerate(images_data):
|
118 |
+
try:
|
119 |
+
disease_idx, disc_cup_image, vcdr, cls_conf, cup_conf, disc_conf, cropped_image = model.process(image)
|
120 |
+
results.append({
|
121 |
+
'file_name': file_name,
|
122 |
+
'diagnosis': model.cls_id2label[disease_idx],
|
123 |
+
'confidence': cls_conf,
|
124 |
+
'vcdr': vcdr,
|
125 |
+
'cup_conf': cup_conf,
|
126 |
+
'disc_conf': disc_conf,
|
127 |
+
'processed_image': disc_cup_image,
|
128 |
+
'cropped_image': cropped_image
|
129 |
+
})
|
130 |
+
if progress_bar:
|
131 |
+
progress_bar.progress((idx + 1) / len(images_data))
|
132 |
+
except Exception as e:
|
133 |
+
st.error(f"Error processing {file_name}: {str(e)}")
|
134 |
+
return results
|
135 |
+
|
136 |
+
def save_results(results, original_images):
|
137 |
+
# Create temporary directory for results
|
138 |
+
with tempfile.TemporaryDirectory() as temp_dir:
|
139 |
+
# Save report as CSV
|
140 |
+
df = pd.DataFrame([{
|
141 |
+
'File': r['file_name'],
|
142 |
+
'Diagnosis': r['diagnosis'],
|
143 |
+
'Confidence (%)': f"{r['confidence']:.1f}",
|
144 |
+
'VCDR': f"{r['vcdr']:.3f}",
|
145 |
+
'Cup Confidence (%)': f"{r['cup_conf']:.1f}",
|
146 |
+
'Disc Confidence (%)': f"{r['disc_conf']:.1f}"
|
147 |
+
} for r in results])
|
148 |
+
|
149 |
+
report_path = os.path.join(temp_dir, 'report.csv')
|
150 |
+
df.to_csv(report_path, index=False)
|
151 |
+
|
152 |
+
# Save processed images
|
153 |
+
for result, orig_img in zip(results, original_images):
|
154 |
+
img_name = result['file_name']
|
155 |
+
base_name = os.path.splitext(img_name)[0]
|
156 |
+
|
157 |
+
# Save original
|
158 |
+
orig_path = os.path.join(temp_dir, f"{base_name}_original.jpg")
|
159 |
+
Image.fromarray(orig_img).save(orig_path)
|
160 |
+
|
161 |
+
# Save segmentation
|
162 |
+
seg_path = os.path.join(temp_dir, f"{base_name}_segmentation.jpg")
|
163 |
+
Image.fromarray(result['processed_image']).save(seg_path)
|
164 |
+
|
165 |
+
# Save ROI
|
166 |
+
roi_path = os.path.join(temp_dir, f"{base_name}_roi.jpg")
|
167 |
+
Image.fromarray(result['cropped_image']).save(roi_path)
|
168 |
+
|
169 |
+
# Create ZIP file
|
170 |
+
zip_path = os.path.join(temp_dir, 'results.zip')
|
171 |
+
with zipfile.ZipFile(zip_path, 'w') as zipf:
|
172 |
+
for root, _, files in os.walk(temp_dir):
|
173 |
+
for file in files:
|
174 |
+
if file != 'results.zip':
|
175 |
+
file_path = os.path.join(root, file)
|
176 |
+
arcname = os.path.basename(file_path)
|
177 |
+
zipf.write(file_path, arcname)
|
178 |
+
|
179 |
+
with open(zip_path, 'rb') as f:
|
180 |
+
return f.read()
|
181 |
+
|
182 |
# --- Streamlit Interface ---
|
183 |
def main():
|
184 |
st.set_page_config(layout="wide", page_title="Glaucoma Screening Tool")
|
|
|
203 |
help="Images with confidence above this threshold will be marked as reliable predictions")
|
204 |
|
205 |
if uploaded_files:
|
206 |
+
st.markdown("## 📊 Batch Analysis Results")
|
207 |
+
|
208 |
+
# Initialize model once for all images
|
209 |
+
model = GlaucomaModel(device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu"))
|
210 |
+
|
211 |
+
# Prepare images data
|
212 |
+
images_data = []
|
213 |
+
original_images = []
|
214 |
+
for file in uploaded_files:
|
215 |
+
try:
|
216 |
+
image = Image.open(file).convert('RGB')
|
217 |
+
image_np = np.array(image)
|
218 |
+
images_data.append((file.name, image_np))
|
219 |
+
original_images.append(image_np)
|
220 |
+
except Exception as e:
|
221 |
+
st.error(f"Error loading {file.name}: {str(e)}")
|
222 |
+
continue
|
223 |
+
|
224 |
+
progress_bar = st.progress(0)
|
225 |
+
st.write(f"Processing {len(images_data)} images...")
|
226 |
+
|
227 |
+
# Process all images
|
228 |
+
results = process_batch(model, images_data, progress_bar)
|
229 |
+
|
230 |
+
if results:
|
231 |
+
# Generate ZIP with results
|
232 |
+
zip_data = save_results(results, original_images)
|
233 |
|
234 |
+
# Download button for ZIP
|
235 |
+
st.download_button(
|
236 |
+
label="📥 Download All Results",
|
237 |
+
data=zip_data,
|
238 |
+
file_name=f"glaucoma_screening_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip",
|
239 |
+
mime="application/zip"
|
240 |
+
)
|
241 |
|
242 |
+
# Display results
|
243 |
+
for result in results:
|
244 |
+
with st.expander(f"Results for {result['file_name']}"):
|
245 |
+
cols = st.columns(3)
|
246 |
+
with cols[0]:
|
247 |
+
st.image(result['processed_image'], caption="Segmentation", use_column_width=True)
|
248 |
+
with cols[1]:
|
249 |
+
st.image(result['cropped_image'], caption="ROI", use_column_width=True)
|
250 |
+
with cols[2]:
|
251 |
+
st.write("### Metrics")
|
252 |
+
st.write(f"Diagnosis: {result['diagnosis']}")
|
253 |
+
st.write(f"Confidence: {result['confidence']:.1f}%")
|
254 |
+
st.write(f"VCDR: {result['vcdr']:.3f}")
|
255 |
+
st.write(f"Cup Confidence: {result['cup_conf']:.1f}%")
|
256 |
+
st.write(f"Disc Confidence: {result['disc_conf']:.1f}%")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|