Sefat33 commited on
Commit
2cc0a98
Β·
verified Β·
1 Parent(s): 5583184

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +359 -111
app.py CHANGED
@@ -9,28 +9,161 @@ from keras.layers import BatchNormalization, DepthwiseConv2D, TFSMLayer
9
  import os
10
  from io import BytesIO
11
  import base64
 
 
12
  st.markdown(
13
  """
14
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  .flex-row {
16
  display: flex;
17
  gap: 2rem;
18
- align-items: stretch; /* ensures equal height */
 
19
  }
20
  .flex-row > div {
21
  flex: 1;
22
  display: flex;
23
  flex-direction: column;
24
  }
25
- .overlay {
26
- background-color: rgba(255, 255, 255, 0.85);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  padding: 1rem;
 
 
 
 
 
 
 
28
  border-radius: 10px;
29
- overflow-y: auto;
30
- color: #333;
31
- font-size: 16px;
32
- line-height: 1.5;
33
- flex-grow: 1; /* fills the column height */
 
 
 
 
34
  }
35
  </style>
36
  """,
@@ -49,36 +182,43 @@ DepthwiseConv2D.from_config = classmethod(
49
  lambda cls, config, *a, **k: original_dw({k: v for k, v in config.items() if k != "groups"}, *a, **k)
50
  )
51
 
52
- # --- Set Background ---
53
  def set_background(main_bg_path, sidebar_bg_path):
54
  def encode_image(path):
55
- with open(path, "rb") as f:
56
- return base64.b64encode(f.read()).decode()
 
 
57
 
58
  main_bg = encode_image(main_bg_path)
59
  sidebar_bg = encode_image(sidebar_bg_path)
60
-
61
- st.markdown(f"""
62
- <style>
63
- .stApp {{
64
- background-image: url("data:image/jpg;base64,{main_bg}");
65
- background-size: cover;
66
- background-attachment: fixed;
67
- }}
68
- [data-testid="stSidebar"] > div:first-child {{
69
- background-image: url("data:image/jpg;base64,{sidebar_bg}");
70
- background-size: cover;
71
- background-position: center;
72
- padding: 1rem;
73
- border-radius: 0 15px 15px 0;
74
- }}
75
- </style>
76
- """, unsafe_allow_html=True)
77
-
78
-
 
 
 
 
 
 
79
  set_background("5858.jpg", "7070.jpg")
80
 
81
-
82
  # --- Constants ---
83
  IMG_SIZE = (224, 224)
84
  CLASS_NAMES = [
@@ -92,13 +232,13 @@ LIME_EXPLAINER = lime_image.LimeImageExplainer()
92
  def load_model():
93
  model_path = "Model"
94
  if not os.path.exists(model_path):
95
- st.error(f"Model folder '{model_path}' not found.")
96
  st.stop()
97
  try:
98
  model = tf.keras.Sequential([TFSMLayer(model_path, call_endpoint="serving_default")])
99
  return model
100
  except Exception as e:
101
- st.error(f"Error loading model: {e}")
102
  st.stop()
103
 
104
  # --- Prediction ---
@@ -113,7 +253,7 @@ def predict(images, model):
113
  else:
114
  return preds
115
 
116
- # --- Preprocessing Steps ---
117
  def preprocess_with_steps(img):
118
  h, w = img.shape[:2]
119
  center, radius = (w // 2, h // 2), min(w, h) // 2
@@ -133,104 +273,129 @@ def preprocess_with_steps(img):
133
  sharp = cv2.addWeighted(clahe_img, 4, cv2.GaussianBlur(clahe_img, (0, 0), 10), -4, 128)
134
  resized = cv2.resize(sharp, IMG_SIZE) / 255.0
135
 
 
136
  fig, axs = plt.subplots(1, 4, figsize=(16, 4))
 
 
137
  for ax, image, title in zip(
138
  axs, [img, circ, clahe_img, resized],
139
  ["Original", "Circular Crop", "CLAHE", "Sharpen + Resize"]
140
  ):
141
  ax.imshow(image)
142
- ax.set_title(title)
143
  ax.axis("off")
 
 
 
 
 
 
144
  st.pyplot(fig)
145
  plt.close(fig)
146
  return resized
147
 
 
148
  explanation_text = {
149
  'Normal': """
150
- <h3 style="color:#2E7D32; font-weight:bold;">βœ… Normal</h3>
151
- <ul style="font-size:16px; line-height:1.6; color:#333;">
152
- <li>🟒 <strong>Clear retina</strong>, no lesions</li>
153
- <li>🩺 <strong>Blood vessels</strong> normal</li>
154
- <li>πŸ‘ <strong>Healthy optic disc & macula</strong></li>
155
- <li>βœ”οΈ No signs of retinal disease</li>
156
- </ul>
 
 
157
  """,
158
 
159
  'Diabetic Retinopathy': """
160
- <h3 style="color:#D84315; font-weight:bold;">πŸ’‰ Diabetic Retinopathy</h3>
161
- <ul style="font-size:16px; line-height:1.6; color:#333;">
162
- <li>πŸ”Ά Red spots / hemorrhages</li>
163
- <li>🩸 Leaking or swollen vessels</li>
164
- <li>πŸ‘ Macula possibly thickened</li>
165
- <li>⚠️ Diabetes-related damage</li>
166
- </ul>
 
 
167
  """,
168
 
169
  'Glaucoma': """
170
- <h3 style="color:#6A1B9A; font-weight:bold;">πŸ‘ Glaucoma</h3>
171
- <ul style="font-size:16px; line-height:1.6; color:#333;">
172
- <li>πŸ”΄ Thinned nerve fiber layer</li>
173
- <li>πŸ’‰ Cupping in optic disc</li>
174
- <li>πŸ‘ Risk of peripheral vision loss</li>
175
- <li>πŸ”΄ Long-term eye pressure control needed</li>
176
- </ul>
 
 
177
  """,
178
 
179
  'Cataract': """
180
- <h3 style="color:#FF8F00; font-weight:bold;">🌫 Cataract</h3>
181
- <ul style="font-size:16px; line-height:1.6; color:#333;">
182
- <li>🌫 Cloudy or hazy image</li>
183
- <li>πŸ‘ Disc/macula not clearly visible</li>
184
- <li>πŸ” Overall low contrast</li>
185
- <li>⚠️ Likely due to lens opacity</li>
186
- </ul>
 
 
187
  """,
188
 
189
  'Age-related Macular Degeneration (AMD)': """
190
- <h3 style="color:#AD1457; font-weight:bold;">πŸ§“ Age-related Macular Degeneration (AMD)</h3>
191
- <ul style="font-size:16px; line-height:1.6; color:#333;">
192
- <li>πŸ”΄ Yellow drusen near macula</li>
193
- <li>πŸ‘ Center vision affected</li>
194
- <li>🩺 Degenerative macula changes</li>
195
- <li>⚠️ Early/moderate AMD signs</li>
196
- </ul>
 
 
197
  """,
198
 
199
  'Hypertension': """
200
- <h3 style="color:#C62828; font-weight:bold;">⚠️ Hypertension</h3>
201
- <ul style="font-size:16px; line-height:1.6; color:#333;">
202
- <li>πŸ”Ά Bright lesions or hemorrhages</li>
203
- <li>🩸 Twisted/narrowed vessels</li>
204
- <li>πŸ‘ Star or flame-like patterns</li>
205
- <li>⚠️ Vascular damage from high BP</li>
206
- </ul>
 
 
207
  """,
208
 
209
  'Myopia': """
210
- <h3 style="color:#1565C0; font-weight:bold;">πŸ‘“ Myopia</h3>
211
- <ul style="font-size:16px; line-height:1.6; color:#333;">
212
- <li>πŸ”΅ Elongated eyeball signs</li>
213
- <li>🩺 Slight disc tilting</li>
214
- <li>πŸ‘ Possible peripapillary atrophy</li>
215
- <li>ℹ️ Common in nearsighted eyes</li>
216
- </ul>
 
 
217
  """,
218
 
219
  'Others': """
220
- <h3 style="color:#424242; font-weight:bold;">πŸ”Ž Others</h3>
221
- <ul style="font-size:16px; line-height:1.6; color:#333;">
222
- <li>βšͺ Unusual or unclassified patterns</li>
223
- <li>🩸 Irregular vascular changes</li>
224
- <li>πŸ‘ Disc or macula abnormalities</li>
225
- <li>❓ Possibly rare or overlapping conditions</li>
226
- </ul>
 
 
227
  """
228
  }
229
 
230
-
231
- # --- LIME Display ---
232
  def show_lime(img, model, pred_idx, pred_label, all_probs):
233
- with st.spinner("🟑 Generating LIME explanation..."):
234
  explanation = LIME_EXPLAINER.explain_instance(
235
  image=img,
236
  classifier_fn=lambda imgs: predict(imgs, model),
@@ -248,59 +413,142 @@ def show_lime(img, model, pred_idx, pred_label, all_probs):
248
  buf.seek(0)
249
  lime_data = buf.getvalue()
250
 
251
- # Wrap columns inside a flex container for equal height
252
  st.markdown('<div class="flex-row">', unsafe_allow_html=True)
253
 
254
  col1, col2 = st.columns(2)
255
  with col1:
256
- st.markdown("### πŸ“ LIME Explanation")
257
- st.image(lime_data, width=224, output_format="PNG")
 
 
 
 
258
  st.download_button(
259
- "πŸ“₯ Download LIME Image",
260
  lime_data,
261
- file_name=f"{pred_label}_LIME.png",
262
  mime="image/png"
263
  )
 
264
  with col2:
265
- st.markdown(
266
- f'<div class="overlay">{explanation_text.get(pred_label, "No explanation available.")}</div>',
267
- unsafe_allow_html=True
268
- )
269
 
270
  st.markdown('</div>', unsafe_allow_html=True)
271
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
- # --- Streamlit App UI ---
274
- st.set_page_config(page_title="πŸ‘ Retina Classifier with LIME", layout="wide")
275
- st.title("πŸ‘ Retina Disease Classifier with LIME Explanation")
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
  model = load_model()
278
 
 
279
  with st.sidebar:
 
 
 
 
 
 
 
 
 
280
  uploaded_files = st.file_uploader(
281
- "πŸ“‚ Upload retinal images", type=["jpg", "jpeg", "png"], accept_multiple_files=True
 
 
 
282
  )
 
283
  selected_filename = None
284
  if uploaded_files:
 
 
 
 
 
285
  filenames = [f.name for f in uploaded_files]
286
- selected_filename = st.selectbox("🎯 Select an image to explain", filenames)
 
 
 
 
287
 
 
288
  if uploaded_files and selected_filename:
289
  file = next(f for f in uploaded_files if f.name == selected_filename)
290
  file.seek(0)
291
  bgr = cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR)
292
  rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
293
 
294
- st.subheader("πŸ” Preprocessing Steps")
 
 
 
 
 
 
 
 
 
295
  preprocessed = preprocess_with_steps(rgb)
296
  input_tensor = np.expand_dims(preprocessed, axis=0)
297
 
 
298
  preds = predict(input_tensor, model)
299
  pred_idx = np.argmax(preds)
300
  pred_label = CLASS_NAMES[pred_idx]
301
  confidence = np.max(preds) * 100
302
 
303
- st.success(f"βœ… Prediction: **{pred_label}** ({confidence:.2f}%)")
 
 
 
304
  show_lime(preprocessed, model, pred_idx, pred_label, preds)
 
305
  else:
306
- st.info("πŸ“€ Upload a retinal image from the sidebar to get started.")
 
 
 
 
 
 
 
 
9
  import os
10
  from io import BytesIO
11
  import base64
12
+
13
+ # Enhanced CSS with Medical-Professional Color Scheme
14
  st.markdown(
15
  """
16
  <style>
17
+ /* Main App Styling */
18
+ .stApp {
19
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
20
+ }
21
+
22
+ /* Header Styling */
23
+ .main-header {
24
+ background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
25
+ color: white;
26
+ padding: 1.5rem;
27
+ border-radius: 12px;
28
+ margin-bottom: 2rem;
29
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
30
+ }
31
+
32
+ /* Flex Container for Equal Height Columns */
33
  .flex-row {
34
  display: flex;
35
  gap: 2rem;
36
+ align-items: stretch;
37
+ margin-top: 1rem;
38
  }
39
  .flex-row > div {
40
  flex: 1;
41
  display: flex;
42
  flex-direction: column;
43
  }
44
+
45
+ /* Medical Information Cards */
46
+ .medical-card {
47
+ background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
48
+ padding: 1.5rem;
49
+ border-radius: 12px;
50
+ border-left: 4px solid #3b82f6;
51
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
52
+ flex-grow: 1;
53
+ }
54
+
55
+ .medical-card h3 {
56
+ margin-top: 0;
57
+ border-bottom: 2px solid #e2e8f0;
58
+ padding-bottom: 0.5rem;
59
+ }
60
+
61
+ /* Prediction Result Styling */
62
+ .prediction-card {
63
+ background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
64
+ border: 2px solid #3b82f6;
65
+ border-radius: 12px;
66
+ padding: 1.5rem;
67
+ margin: 1rem 0;
68
+ text-align: center;
69
+ }
70
+
71
+ .prediction-high {
72
+ background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
73
+ border-color: #22c55e;
74
+ }
75
+
76
+ .prediction-medium {
77
+ background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
78
+ border-color: #f59e0b;
79
+ }
80
+
81
+ .prediction-low {
82
+ background: linear-gradient(135deg, #fef2f2 0%, #fecaca 100%);
83
+ border-color: #ef4444;
84
+ }
85
+
86
+ /* Processing Steps Cards */
87
+ .processing-step {
88
+ background: white;
89
+ border-radius: 8px;
90
+ padding: 1rem;
91
+ margin: 0.5rem 0;
92
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
93
+ border-left: 3px solid #3b82f6;
94
+ }
95
+
96
+ /* Sidebar Styling */
97
+ .sidebar-content {
98
+ background: rgba(255, 255, 255, 0.95);
99
+ border-radius: 12px;
100
+ padding: 1rem;
101
+ margin: 1rem 0;
102
+ border: 1px solid #e2e8f0;
103
+ }
104
+
105
+ /* Button Styling */
106
+ .stDownloadButton button {
107
+ background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
108
+ color: white;
109
+ border: none;
110
+ border-radius: 8px;
111
+ padding: 0.75rem 1.5rem;
112
+ font-weight: 500;
113
+ transition: all 0.3s ease;
114
+ }
115
+
116
+ .stDownloadButton button:hover {
117
+ transform: translateY(-2px);
118
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
119
+ }
120
+
121
+ /* Success/Warning/Error Messages */
122
+ .stSuccess {
123
+ background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
124
+ border-left: 4px solid #22c55e;
125
+ border-radius: 8px;
126
+ }
127
+
128
+ .stWarning {
129
+ background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
130
+ border-left: 4px solid #f59e0b;
131
+ border-radius: 8px;
132
+ }
133
+
134
+ .stError {
135
+ background: linear-gradient(135deg, #fef2f2 0%, #fecaca 100%);
136
+ border-left: 4px solid #ef4444;
137
+ border-radius: 8px;
138
+ }
139
+
140
+ /* Spinner Styling */
141
+ .stSpinner {
142
+ color: #3b82f6 !important;
143
+ }
144
+
145
+ /* Image Container */
146
+ .image-container {
147
+ background: white;
148
+ border-radius: 12px;
149
  padding: 1rem;
150
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
151
+ border: 1px solid #e2e8f0;
152
+ }
153
+
154
+ /* Confidence Bar */
155
+ .confidence-bar {
156
+ background: #e2e8f0;
157
  border-radius: 10px;
158
+ overflow: hidden;
159
+ margin: 1rem 0;
160
+ height: 8px;
161
+ }
162
+
163
+ .confidence-fill {
164
+ height: 100%;
165
+ background: linear-gradient(90deg, #22c55e 0%, #3b82f6 50%, #f59e0b 100%);
166
+ transition: width 0.3s ease;
167
  }
168
  </style>
169
  """,
 
182
  lambda cls, config, *a, **k: original_dw({k: v for k, v in config.items() if k != "groups"}, *a, **k)
183
  )
184
 
185
+ # --- Enhanced Background Function ---
186
  def set_background(main_bg_path, sidebar_bg_path):
187
  def encode_image(path):
188
+ if os.path.exists(path):
189
+ with open(path, "rb") as f:
190
+ return base64.b64encode(f.read()).decode()
191
+ return None
192
 
193
  main_bg = encode_image(main_bg_path)
194
  sidebar_bg = encode_image(sidebar_bg_path)
195
+
196
+ if main_bg:
197
+ st.markdown(f"""
198
+ <style>
199
+ .stApp {{
200
+ background-image: linear-gradient(rgba(248, 250, 252, 0.9), rgba(248, 250, 252, 0.9)), url("data:image/jpg;base64,{main_bg}");
201
+ background-size: cover;
202
+ background-attachment: fixed;
203
+ }}
204
+ </style>
205
+ """, unsafe_allow_html=True)
206
+
207
+ if sidebar_bg:
208
+ st.markdown(f"""
209
+ <style>
210
+ [data-testid="stSidebar"] > div:first-child {{
211
+ background-image: linear-gradient(rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.95)), url("data:image/jpg;base64,{sidebar_bg}");
212
+ background-size: cover;
213
+ background-position: center;
214
+ border-radius: 0 15px 15px 0;
215
+ }}
216
+ </style>
217
+ """, unsafe_allow_html=True)
218
+
219
+ # Try to set background if images exist
220
  set_background("5858.jpg", "7070.jpg")
221
 
 
222
  # --- Constants ---
223
  IMG_SIZE = (224, 224)
224
  CLASS_NAMES = [
 
232
  def load_model():
233
  model_path = "Model"
234
  if not os.path.exists(model_path):
235
+ st.error(f"🚨 Model folder '{model_path}' not found.")
236
  st.stop()
237
  try:
238
  model = tf.keras.Sequential([TFSMLayer(model_path, call_endpoint="serving_default")])
239
  return model
240
  except Exception as e:
241
+ st.error(f"🚨 Error loading model: {e}")
242
  st.stop()
243
 
244
  # --- Prediction ---
 
253
  else:
254
  return preds
255
 
256
+ # --- Enhanced Preprocessing Steps ---
257
  def preprocess_with_steps(img):
258
  h, w = img.shape[:2]
259
  center, radius = (w // 2, h // 2), min(w, h) // 2
 
273
  sharp = cv2.addWeighted(clahe_img, 4, cv2.GaussianBlur(clahe_img, (0, 0), 10), -4, 128)
274
  resized = cv2.resize(sharp, IMG_SIZE) / 255.0
275
 
276
+ # Enhanced visualization with medical styling
277
  fig, axs = plt.subplots(1, 4, figsize=(16, 4))
278
+ fig.patch.set_facecolor('#f8fafc')
279
+
280
  for ax, image, title in zip(
281
  axs, [img, circ, clahe_img, resized],
282
  ["Original", "Circular Crop", "CLAHE", "Sharpen + Resize"]
283
  ):
284
  ax.imshow(image)
285
+ ax.set_title(title, fontsize=14, fontweight='bold', color='#1e40af')
286
  ax.axis("off")
287
+ # Add subtle border
288
+ for spine in ax.spines.values():
289
+ spine.set_edgecolor('#e2e8f0')
290
+ spine.set_linewidth(1)
291
+
292
+ plt.tight_layout()
293
  st.pyplot(fig)
294
  plt.close(fig)
295
  return resized
296
 
297
+ # Enhanced explanation text with better medical styling
298
  explanation_text = {
299
  'Normal': """
300
+ <div class="medical-card">
301
+ <h3 style="color:#059669; font-weight:bold;">βœ… Normal Retina</h3>
302
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
303
+ <li>🟒 <strong>Clear retinal structure</strong> - No pathological lesions detected</li>
304
+ <li>🩺 <strong>Healthy blood vessels</strong> - Normal caliber and branching pattern</li>
305
+ <li>πŸ‘ <strong>Intact optic disc & macula</strong> - Proper anatomical structure</li>
306
+ <li>βœ… <strong>No disease indicators</strong> - Excellent retinal health</li>
307
+ </ul>
308
+ </div>
309
  """,
310
 
311
  'Diabetic Retinopathy': """
312
+ <div class="medical-card">
313
+ <h3 style="color:#dc2626; font-weight:bold;">⚠️ Diabetic Retinopathy</h3>
314
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
315
+ <li>πŸ”΄ <strong>Microhemorrhages</strong> - Red spots indicating vessel damage</li>
316
+ <li>🩸 <strong>Vascular leakage</strong> - Fluid accumulation in retinal tissue</li>
317
+ <li>πŸ‘ <strong>Macular involvement</strong> - Possible diabetic macular edema</li>
318
+ <li>πŸ”¬ <strong>Requires monitoring</strong> - Regular ophthalmologic follow-up needed</li>
319
+ </ul>
320
+ </div>
321
  """,
322
 
323
  'Glaucoma': """
324
+ <div class="medical-card">
325
+ <h3 style="color:#7c3aed; font-weight:bold;">πŸ‘ Glaucoma</h3>
326
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
327
+ <li>πŸ”΄ <strong>Optic nerve damage</strong> - Thinning of nerve fiber layer</li>
328
+ <li>βšͺ <strong>Increased cup-to-disc ratio</strong> - Optic disc cupping</li>
329
+ <li>πŸ“‰ <strong>Visual field risk</strong> - Potential peripheral vision loss</li>
330
+ <li>πŸ’Š <strong>Pressure management</strong> - IOP control essential</li>
331
+ </ul>
332
+ </div>
333
  """,
334
 
335
  'Cataract': """
336
+ <div class="medical-card">
337
+ <h3 style="color:#f59e0b; font-weight:bold;">🌫️ Cataract</h3>
338
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
339
+ <li>☁️ <strong>Lens opacity</strong> - Clouding affecting image clarity</li>
340
+ <li>πŸ” <strong>Reduced contrast</strong> - Decreased retinal detail visibility</li>
341
+ <li>πŸ‘ <strong>Fundus visualization</strong> - Limited view of posterior structures</li>
342
+ <li>πŸ₯ <strong>Surgical consideration</strong> - May benefit from cataract extraction</li>
343
+ </ul>
344
+ </div>
345
  """,
346
 
347
  'Age-related Macular Degeneration (AMD)': """
348
+ <div class="medical-card">
349
+ <h3 style="color:#be185d; font-weight:bold;">πŸ§“ Age-related Macular Degeneration</h3>
350
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
351
+ <li>🟑 <strong>Drusen deposits</strong> - Yellow spots near macular region</li>
352
+ <li>πŸ‘ <strong>Central vision impact</strong> - Macula-specific changes</li>
353
+ <li>πŸ“ˆ <strong>Progressive condition</strong> - Age-related degenerative process</li>
354
+ <li>πŸ”¬ <strong>Monitoring required</strong> - Regular assessment for progression</li>
355
+ </ul>
356
+ </div>
357
  """,
358
 
359
  'Hypertension': """
360
+ <div class="medical-card">
361
+ <h3 style="color:#dc2626; font-weight:bold;">🩸 Hypertensive Retinopathy</h3>
362
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
363
+ <li>⭐ <strong>Cotton wool spots</strong> - Nerve fiber layer infarcts</li>
364
+ <li>πŸ”΄ <strong>Flame hemorrhages</strong> - Superficial retinal bleeding</li>
365
+ <li>🩸 <strong>Arteriovenous nicking</strong> - Vessel caliber changes</li>
366
+ <li>πŸ’Š <strong>BP management</strong> - Systemic hypertension control needed</li>
367
+ </ul>
368
+ </div>
369
  """,
370
 
371
  'Myopia': """
372
+ <div class="medical-card">
373
+ <h3 style="color:#2563eb; font-weight:bold;">πŸ‘“ Myopic Changes</h3>
374
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
375
+ <li>πŸ”΅ <strong>Axial elongation signs</strong> - Elongated eyeball morphology</li>
376
+ <li>βšͺ <strong>Peripapillary atrophy</strong> - Tissue thinning around optic disc</li>
377
+ <li>πŸ“ <strong>Disc tilting</strong> - Oblique optic disc orientation</li>
378
+ <li>πŸ‘ <strong>Refractive changes</strong> - Associated with high myopia</li>
379
+ </ul>
380
+ </div>
381
  """,
382
 
383
  'Others': """
384
+ <div class="medical-card">
385
+ <h3 style="color:#6b7280; font-weight:bold;">πŸ” Unclassified Findings</h3>
386
+ <ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
387
+ <li>❓ <strong>Atypical presentation</strong> - Unusual retinal patterns</li>
388
+ <li>πŸ”¬ <strong>Further evaluation</strong> - Additional testing recommended</li>
389
+ <li>🩺 <strong>Specialist referral</strong> - Ophthalmologist consultation advised</li>
390
+ <li>πŸ“‹ <strong>Comprehensive exam</strong> - Complete ocular assessment needed</li>
391
+ </ul>
392
+ </div>
393
  """
394
  }
395
 
396
+ # --- Enhanced LIME Display ---
 
397
  def show_lime(img, model, pred_idx, pred_label, all_probs):
398
+ with st.spinner("πŸ”¬ Generating LIME explanation..."):
399
  explanation = LIME_EXPLAINER.explain_instance(
400
  image=img,
401
  classifier_fn=lambda imgs: predict(imgs, model),
 
413
  buf.seek(0)
414
  lime_data = buf.getvalue()
415
 
416
+ # Enhanced layout with medical styling
417
  st.markdown('<div class="flex-row">', unsafe_allow_html=True)
418
 
419
  col1, col2 = st.columns(2)
420
  with col1:
421
+ st.markdown("""
422
+ <div class="image-container">
423
+ <h3 style="color:#1e40af; margin-bottom:1rem;">πŸ”¬ LIME Explanation</h3>
424
+ </div>
425
+ """, unsafe_allow_html=True)
426
+ st.image(lime_data, width=280, output_format="PNG")
427
  st.download_button(
428
+ "πŸ“₯ Download LIME Analysis",
429
  lime_data,
430
+ file_name=f"{pred_label}_LIME_Analysis.png",
431
  mime="image/png"
432
  )
433
+
434
  with col2:
435
+ st.markdown(explanation_text.get(pred_label, "<p>No explanation available.</p>"), unsafe_allow_html=True)
 
 
 
436
 
437
  st.markdown('</div>', unsafe_allow_html=True)
438
 
439
+ # --- Enhanced Confidence Display ---
440
+ def show_confidence(confidence, pred_label):
441
+ if confidence >= 80:
442
+ card_class = "prediction-high"
443
+ icon = "🎯"
444
+ elif confidence >= 60:
445
+ card_class = "prediction-medium"
446
+ icon = "⚠️"
447
+ else:
448
+ card_class = "prediction-low"
449
+ icon = "πŸ”"
450
+
451
+ st.markdown(f"""
452
+ <div class="prediction-card {card_class}">
453
+ <h2 style="margin:0; color:#1e40af;">{icon} Diagnosis: <strong>{pred_label}</strong></h2>
454
+ <div class="confidence-bar">
455
+ <div class="confidence-fill" style="width:{confidence}%"></div>
456
+ </div>
457
+ <p style="margin:0.5rem 0 0 0; font-size:18px; font-weight:bold;">
458
+ Confidence: {confidence:.1f}%
459
+ </p>
460
+ </div>
461
+ """, unsafe_allow_html=True)
462
 
463
+ # --- Enhanced Streamlit App UI ---
464
+ st.set_page_config(
465
+ page_title="πŸ‘οΈ Retina AI Classifier",
466
+ layout="wide",
467
+ initial_sidebar_state="expanded"
468
+ )
469
+
470
+ # Main header
471
+ st.markdown("""
472
+ <div class="main-header">
473
+ <h1 style="margin:0; font-size:2.5rem;">πŸ‘οΈ Retina Disease Classifier</h1>
474
+ <p style="margin:0.5rem 0 0 0; font-size:1.2rem; opacity:0.9;">
475
+ AI-Powered Retinal Analysis with LIME Explainability
476
+ </p>
477
+ </div>
478
+ """, unsafe_allow_html=True)
479
 
480
  model = load_model()
481
 
482
+ # Enhanced sidebar
483
  with st.sidebar:
484
+ st.markdown("""
485
+ <div class="sidebar-content">
486
+ <h3 style="color:#1e40af; margin-top:0;">πŸ“‚ Upload Images</h3>
487
+ <p style="color:#6b7280; margin-bottom:1rem;">
488
+ Upload retinal fundus images for AI analysis
489
+ </p>
490
+ </div>
491
+ """, unsafe_allow_html=True)
492
+
493
  uploaded_files = st.file_uploader(
494
+ "Choose retinal images",
495
+ type=["jpg", "jpeg", "png"],
496
+ accept_multiple_files=True,
497
+ help="Upload high-quality fundus photographs"
498
  )
499
+
500
  selected_filename = None
501
  if uploaded_files:
502
+ st.markdown("""
503
+ <div class="sidebar-content">
504
+ <h4 style="color:#1e40af; margin-top:0;">🎯 Select Image</h4>
505
+ </div>
506
+ """, unsafe_allow_html=True)
507
  filenames = [f.name for f in uploaded_files]
508
+ selected_filename = st.selectbox(
509
+ "Choose image for analysis",
510
+ filenames,
511
+ help="Select which image to analyze with LIME"
512
+ )
513
 
514
+ # Main content area
515
  if uploaded_files and selected_filename:
516
  file = next(f for f in uploaded_files if f.name == selected_filename)
517
  file.seek(0)
518
  bgr = cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR)
519
  rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
520
 
521
+ # Processing steps section
522
+ st.markdown("""
523
+ <div class="processing-step">
524
+ <h3 style="color:#1e40af; margin-top:0;">πŸ” Image Preprocessing Pipeline</h3>
525
+ <p style="color:#6b7280; margin-bottom:1rem;">
526
+ Standardized preprocessing steps for optimal AI analysis
527
+ </p>
528
+ </div>
529
+ """, unsafe_allow_html=True)
530
+
531
  preprocessed = preprocess_with_steps(rgb)
532
  input_tensor = np.expand_dims(preprocessed, axis=0)
533
 
534
+ # Prediction
535
  preds = predict(input_tensor, model)
536
  pred_idx = np.argmax(preds)
537
  pred_label = CLASS_NAMES[pred_idx]
538
  confidence = np.max(preds) * 100
539
 
540
+ # Enhanced prediction display
541
+ show_confidence(confidence, pred_label)
542
+
543
+ # LIME explanation
544
  show_lime(preprocessed, model, pred_idx, pred_label, preds)
545
+
546
  else:
547
+ st.info("πŸ“€ Please upload retinal images from the sidebar to begin analysis.")
548
+ st.markdown("""
549
+ <div style="text-align:center; padding:3rem; color:#6b7280;">
550
+ <h3>Welcome to the Retina AI Classifier</h3>
551
+ <p>This system uses advanced deep learning to analyze retinal fundus images and identify various eye conditions.</p>
552
+ <p>πŸ“‹ Supported conditions: Normal, Diabetic Retinopathy, Glaucoma, Cataract, AMD, Hypertension, Myopia, and Others</p>
553
+ </div>
554
+ """, unsafe_allow_html=True)