File size: 23,127 Bytes
4573cc5 02efb1f 4573cc5 7b260ca 7693f10 fae365b 82be6ad eb4d595 2cc0a98 e29d0ed 36d6227 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 36d6227 2cc0a98 36d6227 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 2cc0a98 1b84290 e29d0ed 2cc0a98 1b84290 2cc0a98 1b84290 2cc0a98 e29d0ed 2cc0a98 36d6227 2cc0a98 1b84290 2cc0a98 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 e29d0ed 1b84290 36d6227 e29d0ed 36d6227 6b0e485 3b6e0e2 ee4901c 8530127 ee4901c 8530127 6b0e485 e29d0ed 3bf35c2 e29d0ed 3bf35c2 e29d0ed 2cc0a98 3bf35c2 e29d0ed d38be7b 175fcdd 8d08cff aaf71d0 3b6e0e2 aaf71d0 ee4901c 8d08cff 06c52fd 69e6749 42534cf 1e07c8e ee4901c 2cc0a98 ee4901c 69e6749 1e7b026 ee4901c 2cc0a98 ee4901c 3b6e0e2 e29d0ed ee4901c 4573cc5 ee4901c 4573cc5 1e7b026 7b4ff10 689e3b8 7b4ff10 3b6e0e2 7b4ff10 6dce6d5 ee4901c 6dce6d5 689e3b8 ee4901c 6dce6d5 99ce1ef e29d0ed c138b4f e29d0ed 2cc0a98 c138b4f 06c52fd 39c3e47 c138b4f 2cc0a98 c138b4f 2cc0a98 c138b4f ee4901c 1e07c8e e29d0ed 39c3e47 b13f5bb 2cc0a98 b13f5bb 2cc0a98 b13f5bb 2cc0a98 b13f5bb 2cc0a98 b13f5bb 2cc0a98 b13f5bb 2cc0a98 b13f5bb 2cc0a98 b13f5bb 2cc0a98 b13f5bb 39c3e47 e29d0ed 06c52fd 2cc0a98 ee4901c 99ce1ef ee4901c 5839dfa 95e0334 8530127 5839dfa b027d54 ee4901c aa68fcb c3982ed e29d0ed 3b6e0e2 2cc0a98 c138b4f 2cc0a98 c138b4f 2cc0a98 c138b4f 2cc0a98 3b6e0e2 2cc0a98 afc9f51 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 36d6227 e29d0ed 2cc0a98 e29d0ed 2cc0a98 e29d0ed 2cc0a98 7693f10 c138b4f e29d0ed ee4901c 2cc0a98 9982802 2cc0a98 9982802 2cc0a98 aef51d4 2cc0a98 aef51d4 2cc0a98 916b38d e29d0ed aef51d4 8530127 aef51d4 e29d0ed 2cc0a98 1b84290 2cc0a98 aef51d4 916b38d 2cc0a98 aef51d4 916b38d e29d0ed 2cc0a98 e29d0ed 1b84290 2cc0a98 06c52fd 2cc0a98 9982802 e29d0ed 2cc0a98 1b84290 2cc0a98 1b84290 e29d0ed 1b84290 2cc0a98 | 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 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 | import numpy as np
import cv2
import tensorflow as tf
import streamlit as st
import matplotlib.pyplot as plt
from lime import lime_image
from skimage.segmentation import mark_boundaries
from keras.layers import BatchNormalization, DepthwiseConv2D, TFSMLayer
import os
from io import BytesIO
import base64
# FIXED CSS - Removed animations and stabilized background
st.markdown(
"""
<style>
/* Main App Styling - FIXED: Stable background */
.stApp {
background: #f8fafc !important;
/* Removed gradient and animations */
}
/* Header Styling - FIXED: No animations */
.main-header {
background: #1e40af;
color: white;
padding: 1.5rem;
border-radius: 12px;
margin-bottom: 2rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
/* Removed gradient animations */
}
/* FIXED: Stable flex container */
.flex-row {
display: flex;
gap: 2rem;
align-items: stretch;
margin-top: 1rem;
}
.flex-row > div {
flex: 1;
display: flex;
flex-direction: column;
}
/* FIXED: Stable medical cards */
.medical-card {
background: white;
padding: 1.5rem;
border-radius: 12px;
border-left: 4px solid #3b82f6;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
flex-grow: 1;
border: 1px solid #e2e8f0;
/* Removed gradient and animations */
}
.medical-card h3 {
margin-top: 0;
border-bottom: 2px solid #e2e8f0;
padding-bottom: 0.5rem;
}
/* FIXED: Removed conflicting prediction styles */
.prediction-card {
background: white;
padding: 2rem;
border-radius: 16px;
margin: 2rem 0;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
/* Removed all animations and gradients */
}
/* FIXED: Stable processing container */
.processing-container {
background: white;
border-radius: 16px;
padding: 2rem;
margin: 2rem 0;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
/* Removed animations */
}
/* FIXED: Stable LIME container */
.lime-container {
background: white;
border-radius: 16px;
padding: 2rem;
margin: 2rem 0;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
/* Removed animations */
}
/* FIXED: Stable button styling */
.stDownloadButton > button {
background: #3b82f6;
color: white;
border: none;
border-radius: 12px;
padding: 0.75rem 1.5rem;
font-weight: 600;
font-size: 1rem;
width: 100%;
margin-top: 1rem;
/* Removed all hover animations and transitions */
}
/* FIXED: Stable upload instructions */
.upload-instructions {
background: #f0f9ff;
border: 2px solid #3b82f6;
border-radius: 12px;
padding: 3rem;
text-align: center;
margin: 2rem 0;
/* Removed gradient */
}
.upload-instructions h3 {
color: #1e40af;
margin-bottom: 1rem;
font-size: 1.5rem;
}
.upload-instructions p {
color: #64748b;
margin-bottom: 1rem;
}
/* FIXED: Stable feature grid */
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
margin: 2rem 0;
}
.feature-card {
background: white;
border-radius: 12px;
padding: 1.5rem;
text-align: center;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
/* Removed hover animations */
}
.feature-icon {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.feature-title {
font-size: 1.1rem;
font-weight: 600;
color: #1e40af;
margin-bottom: 0.5rem;
}
.feature-description {
color: #6b7280;
font-size: 0.9rem;
}
/* FIXED: Stable confidence bar */
.confidence-bar {
background: #e2e8f0;
border-radius: 10px;
overflow: hidden;
margin: 1rem 0;
height: 12px;
position: relative;
}
.confidence-fill {
height: 100%;
border-radius: 10px;
position: relative;
/* Removed transitions */
}
.confidence-fill.high {
background: #16a34a;
}
.confidence-fill.medium {
background: #f59e0b;
}
.confidence-fill.low {
background: #ef4444;
}
/* FIXED: Stable sidebar */
.sidebar-content {
background: white;
border-radius: 12px;
padding: 1rem;
margin: 1rem 0;
border: 1px solid #e2e8f0;
}
/* FIXED: Stable image container */
.image-container {
background: white;
border-radius: 12px;
padding: 1rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid #e2e8f0;
}
/* FIXED: Stable metrics */
.metrics-row {
display: flex;
justify-content: space-around;
margin: 1.5rem 0;
padding: 1rem;
background: #f8fafc;
border-radius: 8px;
}
.metric-item {
text-align: center;
flex: 1;
}
.metric-value {
font-size: 1.5rem;
font-weight: 700;
color: #1e40af;
margin-bottom: 0.25rem;
}
.metric-label {
font-size: 0.875rem;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.1em;
}
/* FIXED: Stable typography */
.prediction-title {
font-size: 1.75rem;
font-weight: 700;
color: #1e40af;
margin-bottom: 1rem;
text-align: center;
}
.confidence-text {
font-size: 1.2rem;
font-weight: 600;
color: #374151;
text-align: center;
margin-top: 0.5rem;
}
/* FIXED: Stable processing steps */
.processing-step {
background: white;
border-radius: 8px;
padding: 1rem;
margin: 0.5rem 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-left: 3px solid #3b82f6;
}
/* FIXED: Stable message styling */
.stSuccess {
background: #f0fdf4;
border-left: 4px solid #22c55e;
border-radius: 8px;
}
.stWarning {
background: #fffbeb;
border-left: 4px solid #f59e0b;
border-radius: 8px;
}
.stError {
background: #fef2f2;
border-left: 4px solid #ef4444;
border-radius: 8px;
}
/* FIXED: Remove any potential animation triggers */
* {
transition: none !important;
animation: none !important;
transform: none !important;
}
/* FIXED: Ensure stable viewport */
.block-container {
padding-top: 1rem;
padding-bottom: 1rem;
}
</style>
""",
unsafe_allow_html=True,
)
# --- Fix deserialization issues ---
original_bn = BatchNormalization.from_config
BatchNormalization.from_config = classmethod(
lambda cls, config, *a, **k: original_bn(
config if not isinstance(config.get("axis"), list) else {**config, "axis": config["axis"][0]}, *a, **k
)
)
original_dw = DepthwiseConv2D.from_config
DepthwiseConv2D.from_config = classmethod(
lambda cls, config, *a, **k: original_dw({k: v for k, v in config.items() if k != "groups"}, *a, **k)
)
# --- FIXED: Simplified background function (no dynamic changes) ---
def set_background():
"""Set a stable, consistent background"""
st.markdown("""
<style>
.stApp {
background: #f8fafc !important;
}
[data-testid="stSidebar"] > div:first-child {
background: #e0f7fa !important; /* Light cyan */
border-radius: 0 15px 15px 0;
padding: 1rem;
}
</style>
""", unsafe_allow_html=True)
# Apply stable background
set_background()
# --- Constants ---
IMG_SIZE = (224, 224)
CLASS_NAMES = [
'Normal', 'Diabetic Retinopathy', 'Glaucoma', 'Cataract',
'Age-related Macular Degeneration (AMD)', 'Hypertension', 'Myopia', 'Others'
]
LIME_EXPLAINER = lime_image.LimeImageExplainer()
# --- Load Model ---
@st.cache_resource
def load_model():
model_path = "Model"
if not os.path.exists(model_path):
st.error(f"π¨ Model folder '{model_path}' not found.")
st.stop()
try:
model = tf.keras.Sequential([TFSMLayer(model_path, call_endpoint="serving_default")])
return model
except Exception as e:
st.error(f"π¨ Error loading model: {e}")
st.stop()
# --- Prediction ---
def predict(images, model):
images = np.array(images)
preds = model.predict(images, verbose=0)
if isinstance(preds, dict):
for v in preds.values():
if isinstance(v, (np.ndarray, list)):
return np.array(v)
return np.array(list(preds.values())[0])
else:
return preds
# --- FIXED: Stable preprocessing with consistent styling ---
def preprocess_with_steps(img):
h, w = img.shape[:2]
center, radius = (w // 2, h // 2), min(w, h) // 2
Y, X = np.ogrid[:h, :w]
dist = np.sqrt((X - center[0]) ** 2 + (Y - center[1]) ** 2)
mask = (dist <= radius).astype(np.uint8)
circ = img.copy()
white_bg = np.ones_like(circ, dtype=np.uint8) * 255
circ = np.where(mask[:, :, np.newaxis] == 1, circ, white_bg)
lab = cv2.cvtColor(circ, cv2.COLOR_RGB2LAB)
cl = cv2.createCLAHE(clipLimit=2.0).apply(lab[:, :, 0])
merged = cv2.merge((cl, lab[:, :, 1], lab[:, :, 2]))
clahe_img = cv2.cvtColor(merged, cv2.COLOR_LAB2RGB)
sharp = cv2.addWeighted(clahe_img, 4, cv2.GaussianBlur(clahe_img, (0, 0), 10), -4, 128)
resized = cv2.resize(sharp, IMG_SIZE) / 255.0
# FIXED: Stable visualization with consistent styling
fig, axs = plt.subplots(1, 4, figsize=(16, 4))
fig.patch.set_facecolor('white') # Fixed to white background
for ax, image, title in zip(
axs, [img, circ, clahe_img, resized],
["Original", "Circular Crop", "CLAHE", "Sharpen + Resize"]
):
ax.imshow(image)
ax.set_title(title, fontsize=14, fontweight='bold', color='#1e40af')
ax.axis("off")
plt.tight_layout()
st.pyplot(fig)
plt.close(fig)
return resized
# FIXED: Stable explanation text (no dynamic styling)
explanation_text = {
'Normal': """
<div class="medical-card">
<h3 style="color:#059669; font-weight:bold;">β
Normal Retina</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>π’ <strong>Clear retinal structure</strong> - No pathological lesions detected</li>
<li>π©Ί <strong>Healthy blood vessels</strong> - Normal caliber and branching pattern</li>
<li>π <strong>Intact optic disc & macula</strong> - Proper anatomical structure</li>
<li>β
<strong>No disease indicators</strong> - Excellent retinal health</li>
</ul>
</div>
""",
'Diabetic Retinopathy': """
<div class="medical-card">
<h3 style="color:#dc2626; font-weight:bold;">β οΈ Diabetic Retinopathy</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>π΄ <strong>Microhemorrhages</strong> - Red spots indicating vessel damage</li>
<li>π©Έ <strong>Vascular leakage</strong> - Fluid accumulation in retinal tissue</li>
<li>π <strong>Macular involvement</strong> - Possible diabetic macular edema</li>
<li>π¬ <strong>Requires monitoring</strong> - Regular ophthalmologic follow-up needed</li>
</ul>
</div>
""",
'Glaucoma': """
<div class="medical-card">
<h3 style="color:#7c3aed; font-weight:bold;">π Glaucoma</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>π΄ <strong>Optic nerve damage</strong> - Thinning of nerve fiber layer</li>
<li>βͺ <strong>Increased cup-to-disc ratio</strong> - Optic disc cupping</li>
<li>π <strong>Visual field risk</strong> - Potential peripheral vision loss</li>
<li>π <strong>Pressure management</strong> - IOP control essential</li>
</ul>
</div>
""",
'Cataract': """
<div class="medical-card">
<h3 style="color:#f59e0b; font-weight:bold;">π«οΈ Cataract</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>βοΈ <strong>Lens opacity</strong> - Clouding affecting image clarity</li>
<li>π <strong>Reduced contrast</strong> - Decreased retinal detail visibility</li>
<li>π <strong>Fundus visualization</strong> - Limited view of posterior structures</li>
<li>π₯ <strong>Surgical consideration</strong> - May benefit from cataract extraction</li>
</ul>
</div>
""",
'Age-related Macular Degeneration (AMD)': """
<div class="medical-card">
<h3 style="color:#be185d; font-weight:bold;">π§ Age-related Macular Degeneration</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>π‘ <strong>Drusen deposits</strong> - Yellow spots near macular region</li>
<li>π <strong>Central vision impact</strong> - Macula-specific changes</li>
<li>π <strong>Progressive condition</strong> - Age-related degenerative process</li>
<li>π¬ <strong>Monitoring required</strong> - Regular assessment for progression</li>
</ul>
</div>
""",
'Hypertension': """
<div class="medical-card">
<h3 style="color:#dc2626; font-weight:bold;">π©Έ Hypertensive Retinopathy</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>β <strong>Cotton wool spots</strong> - Nerve fiber layer infarcts</li>
<li>π΄ <strong>Flame hemorrhages</strong> - Superficial retinal bleeding</li>
<li>π©Έ <strong>Arteriovenous nicking</strong> - Vessel caliber changes</li>
<li>π <strong>BP management</strong> - Systemic hypertension control needed</li>
</ul>
</div>
""",
'Myopia': """
<div class="medical-card">
<h3 style="color:#2563eb; font-weight:bold;">π Myopic Changes</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>π΅ <strong>Axial elongation signs</strong> - Elongated eyeball morphology</li>
<li>βͺ <strong>Peripapillary atrophy</strong> - Tissue thinning around optic disc</li>
<li>π <strong>Disc tilting</strong> - Oblique optic disc orientation</li>
<li>π <strong>Refractive changes</strong> - Associated with high myopia</li>
</ul>
</div>
""",
'Others': """
<div class="medical-card">
<h3 style="color:#6b7280; font-weight:bold;">π Unclassified Findings</h3>
<ul style="font-size:16px; line-height:1.8; color:#374151; margin:0;">
<li>β <strong>Atypical presentation</strong> - Unusual retinal patterns</li>
<li>π¬ <strong>Further evaluation</strong> - Additional testing recommended</li>
<li>π©Ί <strong>Specialist referral</strong> - Ophthalmologist consultation advised</li>
<li>π <strong>Comprehensive exam</strong> - Complete ocular assessment needed</li>
</ul>
</div>
"""
}
# --- FIXED: Stable LIME Display ---
def show_lime(img, model, pred_idx, pred_label, all_probs):
with st.spinner("π¬ Generating LIME explanation..."):
explanation = LIME_EXPLAINER.explain_instance(
image=img,
classifier_fn=lambda imgs: predict(imgs, model),
top_labels=1,
hide_color=0,
num_samples=200,
)
temp, mask = explanation.get_image_and_mask(
label=pred_idx, positive_only=True, num_features=10, hide_rest=False
)
lime_img = mark_boundaries(temp, mask)
buf = BytesIO()
plt.imsave(buf, lime_img, format="png")
buf.seek(0)
lime_data = buf.getvalue()
# FIXED: Stable layout
col1, col2 = st.columns(2)
with col1:
st.markdown("""
<div class="image-container">
<h3 style="color:#1e40af; margin-bottom:1rem;">π¬ LIME Explanation</h3>
</div>
""", unsafe_allow_html=True)
st.image(lime_data, width=280, output_format="PNG")
st.download_button(
"π₯ Download LIME Analysis",
lime_data,
file_name=f"{pred_label}_LIME_Analysis.png",
mime="image/png"
)
with col2:
st.markdown(explanation_text.get(pred_label, "<p>No explanation available.</p>"), unsafe_allow_html=True)
# --- FIXED: Stable confidence display ---
def show_confidence(confidence, pred_label):
# FIXED: Determine confidence level without dynamic styling
if confidence >= 80:
icon = "π―"
level = "high"
elif confidence >= 60:
icon = "β οΈ"
level = "medium"
else:
icon = "π"
level = "low"
st.markdown(f"""
<div class="prediction-card">
<h2 style="margin:0; color:#1e40af;">{icon} Diagnosis: <strong>{pred_label}</strong></h2>
<div class="confidence-bar">
<div class="confidence-fill {level}" style="width:{confidence}%"></div>
</div>
<p style="margin:0.5rem 0 0 0; font-size:18px; font-weight:bold;">
Confidence: {confidence:.1f}%
</p>
</div>
""", unsafe_allow_html=True)
# --- FIXED: Stable Streamlit App UI ---
st.set_page_config(
page_title="ποΈ Retina AI Classifier",
layout="wide",
initial_sidebar_state="expanded"
)
# FIXED: Stable main header
st.markdown("""
<div class="main-header">
<h1 style="margin:0; font-size:2.5rem;">ποΈ Retina Disease Classifier</h1>
<p style="margin:0.5rem 0 0 0; font-size:1.2rem;">
AI-Powered Retinal Analysis with LIME Explainability
</p>
</div>
""", unsafe_allow_html=True)
model = load_model()
# FIXED: Stable sidebar
with st.sidebar:
st.markdown("""
<div class="sidebar-content">
<h3 style="color:#1e40af; margin-top:0;">π Upload Images</h3>
<p style="color:#6b7280; margin-bottom:1rem;">
Upload retinal fundus images for AI analysis
</p>
</div>
""", unsafe_allow_html=True)
uploaded_files = st.file_uploader(
"Choose retinal images",
type=["jpg", "jpeg", "png"],
accept_multiple_files=True,
help="Upload high-quality fundus photographs"
)
selected_filename = None
if uploaded_files:
st.markdown("""
<div class="sidebar-content">
<h4 style="color:#1e40af; margin-top:0;">π― Select Image</h4>
</div>
""", unsafe_allow_html=True)
filenames = [f.name for f in uploaded_files]
selected_filename = st.selectbox(
"Choose image for analysis",
filenames,
help="Select which image to analyze with LIME"
)
# FIXED: Stable main content area
if uploaded_files and selected_filename:
file = next(f for f in uploaded_files if f.name == selected_filename)
file.seek(0)
bgr = cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR)
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
# FIXED: Stable processing steps section
st.markdown("""
<div class="processing-container">
<h3 style="color:#1e40af; margin-top:0; font-size:1.5rem;">π¬ Image Preprocessing Pipeline</h3>
<p style="color:#6b7280; margin-bottom:1rem; font-size:1.1rem;">
Standardized preprocessing steps for optimal AI analysis
</p>
</div>
""", unsafe_allow_html=True)
preprocessed = preprocess_with_steps(rgb)
input_tensor = np.expand_dims(preprocessed, axis=0)
# Prediction
preds = predict(input_tensor, model)
pred_idx = np.argmax(preds)
pred_label = CLASS_NAMES[pred_idx]
confidence = np.max(preds) * 100
# FIXED: Stable prediction display
show_confidence(confidence, pred_label)
# FIXED: Stable LIME explanation section
st.markdown("""
<div class="lime-container">
<h3 style="color:#1e40af; margin-top:0; font-size:1.5rem;">π§ AI Explanation & Clinical Insights</h3>
<p style="color:#6b7280; margin-bottom:1rem; font-size:1.1rem;">
Understanding how AI identified the diagnosis with medical context
</p>
</div>
""", unsafe_allow_html=True)
# LIME explanation
show_lime(preprocessed, model, pred_idx, pred_label, preds)
else:
# FIXED: Stable welcome screen
st.markdown("""
<div class="upload-instructions">
<h3>Welcome to the Retina AI Classifier</h3>
<p>Upload retinal fundus images to begin AI-powered analysis</p>
<p style="font-size:0.9rem;">Drag and drop your images or use the sidebar to get started</p>
</div>
""", unsafe_allow_html=True)
# FIXED: Stable feature grid
st.markdown("""
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">π¬</div>
<div class="feature-title">AI-Powered Analysis</div>
<div class="feature-description">Advanced deep learning models trained on thousands of retinal images</div>
</div>
<div class="feature-card">
<div class="feature-icon">ποΈ</div>
<div class="feature-title">8 Conditions Detected</div>
<div class="feature-description">Normal, Diabetic Retinopathy, Glaucoma, Cataract, AMD, Hypertension, Myopia, Others</div>
</div>
<div class="feature-card">
<div class="feature-icon">π</div>
<div class="feature-title">LIME Explanations</div>
<div class="feature-description">Visual explanations showing which areas influenced the AI's decision</div>
</div>
<div class="feature-card">
<div class="feature-icon">π₯</div>
<div class="feature-title">Clinical Grade</div>
<div class="feature-description">Designed for healthcare professionals with detailed medical insights</div>
</div>
</div>
""", unsafe_allow_html=True) |