Natzi21/malaria-cell-detection
0
1"""2Malaria Detection - Streamlit App3===================================4AI-powered blood smear analysis.5Run with: streamlit run app.py6"""7 8import streamlit as st9import tensorflow as tf10import numpy as np11from PIL import Image12import time13import datetime14 15# ─────────────────────────────────────────────16# PAGE CONFIG17# ─────────────────────────────────────────────18st.set_page_config(19 page_title="Malaria Detection System",20 page_icon="🦟",21 layout="centered"22)23 24# ─────────────────────────────────────────────25# CUSTOM CSS26# ─────────────────────────────────────────────27st.markdown("""28<style>29 @import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Inter:wght@300;400;600;700&display=swap');30 31 html, body, [class*="css"] {32 font-family: 'Inter', sans-serif;33 }34 35 .stApp {36 background-color: #0d1117;37 color: #e6edf3;38 }39 40 .main-header {41 text-align: center;42 padding: 2rem 0 1rem;43 }44 45 .badge {46 display: inline-block;47 background: linear-gradient(90deg, #238636, #2ea043);48 color: white;49 padding: 4px 16px;50 border-radius: 20px;51 font-size: 0.75rem;52 font-weight: 700;53 letter-spacing: 2px;54 margin-bottom: 12px;55 font-family: 'Space Mono', monospace;56 }57 58 .main-title {59 font-size: 2.2rem;60 font-weight: 700;61 color: #e6edf3;62 margin: 0;63 }64 65 .subtitle {66 color: #8b949e;67 font-size: 0.95rem;68 margin-top: 8px;69 }70 71 .stat-container {72 background: #161b22;73 border: 1px solid #30363d;74 border-radius: 12px;75 padding: 1.2rem;76 text-align: center;77 margin-bottom: 1rem;78 }79 80 .stat-value {81 font-size: 1.6rem;82 font-weight: 700;83 color: #58a6ff;84 font-family: 'Space Mono', monospace;85 }86 87 .stat-label {88 font-size: 0.72rem;89 color: #8b949e;90 margin-top: 4px;91 letter-spacing: 0.5px;92 }93 94 .result-infected {95 background: rgba(248, 81, 73, 0.1);96 border: 1px solid rgba(248, 81, 73, 0.4);97 border-radius: 12px;98 padding: 1.5rem;99 text-align: center;100 }101 102 .result-healthy {103 background: rgba(46, 160, 67, 0.1);104 border: 1px solid rgba(46, 160, 67, 0.4);105 border-radius: 12px;106 padding: 1.5rem;107 text-align: center;108 }109 110 .result-title {111 font-size: 1.4rem;112 font-weight: 700;113 margin: 0.5rem 0;114 }115 116 .result-infected .result-title { color: #f85149; }117 .result-healthy .result-title { color: #2ea043; }118 119 .result-meta {120 color: #8b949e;121 font-size: 0.85rem;122 font-family: 'Space Mono', monospace;123 }124 125 .history-item {126 background: #161b22;127 border: 1px solid #30363d;128 border-radius: 8px;129 padding: 0.75rem 1rem;130 margin-bottom: 0.5rem;131 display: flex;132 justify-content: space-between;133 font-size: 0.85rem;134 }135 136 .stButton > button {137 background: linear-gradient(90deg, #1f6feb, #388bfd) !important;138 color: white !important;139 border: none !important;140 border-radius: 8px !important;141 font-weight: 600 !important;142 padding: 0.6rem 2rem !important;143 width: 100% !important;144 font-size: 1rem !important;145 }146 147 .divider {148 border: none;149 border-top: 1px solid #21262d;150 margin: 1.5rem 0;151 }152</style>153""", unsafe_allow_html=True)154 155# ─────────────────────────────────────────────156# SESSION STATE157# ─────────────────────────────────────────────158if "history" not in st.session_state:159 st.session_state.history = []160if "total_latency" not in st.session_state:161 st.session_state.total_latency = 0162 163# ─────────────────────────────────────────────164# LOAD MODEL (cached so it only loads once)165# ─────────────────────────────────────────────166@st.cache_resource167def load_model():168 from keras.layers import Dense169 170 class PatchedDense(Dense):171 def __init__(self, *args, **kwargs):172 kwargs.pop('quantization_config', None)173 super().__init__(*args, **kwargs)174 175 model = tf.keras.models.load_model(176 'malaria_model_final.h5',177 custom_objects={'Dense': PatchedDense},178 compile=False179 )180 return model181 182IMG_SIZE = (128, 128)183 184# ─────────────────────────────────────────────185# HEADER186# ─────────────────────────────────────────────187st.markdown("""188<div class="main-header">189 <div class="badge">⚡ 5G ENABLED</div>190 <div class="main-title">🦟 Malaria Detection System</div>191 <div class="subtitle">AI-powered blood smear analysis · MobileNetV2</div>192</div>193""", unsafe_allow_html=True)194 195# ─────────────────────────────────────────────196# STATS BAR197# ─────────────────────────────────────────────198total = len(st.session_state.history)199avg_latency = round(st.session_state.total_latency / total) if total > 0 else 0200 201col1, col2, col3, col4 = st.columns(4)202with col1:203 st.markdown('<div class="stat-container"><div class="stat-value">94.3%</div><div class="stat-label">MODEL ACCURACY</div></div>', unsafe_allow_html=True)204with col2:205 st.markdown('<div class="stat-container"><div class="stat-value">0.9846</div><div class="stat-label">AUC-ROC SCORE</div></div>', unsafe_allow_html=True)206with col3:207 st.markdown(f'<div class="stat-container"><div class="stat-value">{total}</div><div class="stat-label">TOTAL PREDICTIONS</div></div>', unsafe_allow_html=True)208with col4:209 latency_display = f"{avg_latency}ms" if total > 0 else "—"210 st.markdown(f'<div class="stat-container"><div class="stat-value">{latency_display}</div><div class="stat-label">AVG LATENCY</div></div>', unsafe_allow_html=True)211 212st.markdown('<hr class="divider">', unsafe_allow_html=True)213 214# ─────────────────────────────────────────────215# UPLOAD + PREDICT216# ─────────────────────────────────────────────217st.markdown("#### 🔬 Upload Blood Smear Image")218uploaded_file = st.file_uploader(219 "Choose a cell image (PNG or JPG)",220 type=["png", "jpg", "jpeg"],221 label_visibility="collapsed"222)223 224if uploaded_file:225 col_img, col_info = st.columns([1, 2])226 with col_img:227 img = Image.open(uploaded_file).convert("RGB")228 st.image(img, caption="Uploaded image", use_container_width=True)229 with col_info:230 st.markdown(f"""231 **File:** `{uploaded_file.name}` 232 **Size:** `{img.size[0]} × {img.size[1]} px` 233 **Format:** `{uploaded_file.type}`234 """)235 st.markdown(" ")236 analyze = st.button("🚀 Analyze via 5G Network")237 238 if analyze:239 model = load_model()240 241 with st.spinner("Transmitting over 5G network... Running AI analysis..."):242 start = time.time()243 244 img_resized = img.resize(IMG_SIZE)245 img_array = np.array(img_resized) / 255.0246 img_array = np.expand_dims(img_array, axis=0)247 248 prob = float(model.predict(img_array, verbose=0)[0][0])249 latency_ms = round((time.time() - start) * 1000)250 251 prediction = "Parasitized" if prob > 0.5 else "Uninfected"252 confidence = prob if prob > 0.5 else 1 - prob253 timestamp = datetime.datetime.now().strftime("%H:%M:%S")254 255 st.session_state.history.insert(0, {256 "prediction": prediction,257 "confidence": f"{confidence:.1%}",258 "latency_ms": latency_ms,259 "timestamp": timestamp,260 })261 st.session_state.total_latency += latency_ms262 263 st.markdown('<hr class="divider">', unsafe_allow_html=True)264 265 if prediction == "Parasitized":266 st.markdown(f"""267 <div class="result-infected">268 <div style="font-size:3rem">🦟</div>269 <div class="result-title">Malaria Detected — Parasitized</div>270 <div class="result-meta">Confidence: {confidence:.1%} · Latency: {latency_ms}ms · {timestamp}</div>271 </div>272 """, unsafe_allow_html=True)273 else:274 st.markdown(f"""275 <div class="result-healthy">276 <div style="font-size:3rem">✅</div>277 <div class="result-title">No Malaria — Uninfected</div>278 <div class="result-meta">Confidence: {confidence:.1%} · Latency: {latency_ms}ms · {timestamp}</div>279 </div>280 """, unsafe_allow_html=True)281 282 st.markdown(" ")283 st.progress(confidence, text=f"Confidence: {confidence:.1%}")284 285 st.rerun()286 287# ─────────────────────────────────────────────288# PREDICTION HISTORY289# ─────────────────────────────────────────────290st.markdown('<hr class="divider">', unsafe_allow_html=True)291st.markdown("#### 📋 Prediction History")292 293if not st.session_state.history:294 st.markdown('<p style="color:#8b949e; font-size:0.9rem;">No predictions yet. Upload an image to begin.</p>', unsafe_allow_html=True)295else:296 for entry in st.session_state.history:297 is_infected = entry["prediction"] == "Parasitized"298 dot_color = "#f85149" if is_infected else "#2ea043"299 label = "🦟 Parasitized" if is_infected else "✅ Uninfected"300 st.markdown(f"""301 <div class="history-item">302 <span>303 <span style="display:inline-block;width:10px;height:10px;border-radius:50%;304 background:{dot_color};margin-right:8px;vertical-align:middle;"></span>305 <strong>{label}</strong>306 </span>307 <span style="color:#8b949e;">{entry['confidence']} confidence</span>308 <span style="color:#8b949e;font-family:'Space Mono',monospace;">{entry['latency_ms']}ms</span>309 <span style="color:#6e7681;">{entry['timestamp']}</span>310 </div>311 """, unsafe_allow_html=True)