Natzi21/malaria-cell-detection
0
1"""
2Malaria Detection - Streamlit App
3===================================
4AI-powered blood smear analysis.
5Run with: streamlit run app.py
6"""
7
8import streamlit as st
9import tensorflow as tf
10import numpy as np
11from PIL import Image
12import time
13import datetime
14
15# ─────────────────────────────────────────────
16# PAGE CONFIG
17# ─────────────────────────────────────────────
18st.set_page_config(
19 page_title="Malaria Detection System",
20 page_icon="🦟",
21 layout="centered"
22)
23
24# ─────────────────────────────────────────────
25# CUSTOM CSS
26# ─────────────────────────────────────────────
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 STATE
157# ─────────────────────────────────────────────
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 = 0
162
163# ─────────────────────────────────────────────
164# LOAD MODEL (cached so it only loads once)
165# ─────────────────────────────────────────────
166@st.cache_resource
167def load_model():
168 from keras.layers import Dense
169
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=False
179 )
180 return model
181
182IMG_SIZE = (128, 128)
183
184# ─────────────────────────────────────────────
185# HEADER
186# ─────────────────────────────────────────────
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 BAR
197# ─────────────────────────────────────────────
198total = len(st.session_state.history)
199avg_latency = round(st.session_state.total_latency / total) if total > 0 else 0
200
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 + PREDICT
216# ─────────────────────────────────────────────
217st.markdown("#### 🔬 Upload Blood Smear Image")
218st.write("Uploader Test")
219
220uploaded_file = st.file_uploader(
221 "Upload",
222 type=["png", "jpg", "jpeg"]
223)
224
225if uploaded_file:
226 st.success("File received!")
227 st.write(uploaded_file.name)
228
229if uploaded_file:
230 col_img, col_info = st.columns([1, 2])
231 with col_img:
232 img = Image.open(uploaded_file).convert("RGB")
233 st.image(img, caption="Uploaded image", use_container_width=True)
234 with col_info:
235 st.markdown(f"""
236 **File:** `{uploaded_file.name}`
237 **Size:** `{img.size[0]} × {img.size[1]} px`
238 **Format:** `{uploaded_file.type}`
239 """)
240 st.markdown(" ")
241 analyze = st.button("🚀 Analyze via 5G Network")
242
243 if analyze:
244 model = load_model()
245
246 with st.spinner("Transmitting over 5G network... Running AI analysis..."):
247 start = time.time()
248
249 img_resized = img.resize(IMG_SIZE)
250 img_array = np.array(img_resized) / 255.0
251 img_array = np.expand_dims(img_array, axis=0)
252
253 prob = float(model.predict(img_array, verbose=0)[0][0])
254 latency_ms = round((time.time() - start) * 1000)
255
256 prediction = "Parasitized" if prob > 0.5 else "Uninfected"
257 confidence = prob if prob > 0.5 else 1 - prob
258 timestamp = datetime.datetime.now().strftime("%H:%M:%S")
259
260 st.session_state.history.insert(0, {
261 "prediction": prediction,
262 "confidence": f"{confidence:.1%}",
263 "latency_ms": latency_ms,
264 "timestamp": timestamp,
265 })
266 st.session_state.total_latency += latency_ms
267
268 st.markdown('<hr class="divider">', unsafe_allow_html=True)
269
270 if prediction == "Parasitized":
271 st.markdown(f"""
272 <div class="result-infected">
273 <div style="font-size:3rem">🦟</div>
274 <div class="result-title">Malaria Detected — Parasitized</div>
275 <div class="result-meta">Confidence: {confidence:.1%} · Latency: {latency_ms}ms · {timestamp}</div>
276 </div>
277 """, unsafe_allow_html=True)
278 else:
279 st.markdown(f"""
280 <div class="result-healthy">
281 <div style="font-size:3rem">✅</div>
282 <div class="result-title">No Malaria — Uninfected</div>
283 <div class="result-meta">Confidence: {confidence:.1%} · Latency: {latency_ms}ms · {timestamp}</div>
284 </div>
285 """, unsafe_allow_html=True)
286
287 st.markdown(" ")
288 st.progress(confidence, text=f"Confidence: {confidence:.1%}")
289
290 st.rerun()
291
292# ─────────────────────────────────────────────
293# PREDICTION HISTORY
294# ─────────────────────────────────────────────
295st.markdown('<hr class="divider">', unsafe_allow_html=True)
296st.markdown("#### 📋 Prediction History")
297
298if not st.session_state.history:
299 st.markdown('<p style="color:#8b949e; font-size:0.9rem;">No predictions yet. Upload an image to begin.</p>', unsafe_allow_html=True)
300else:
301 for entry in st.session_state.history:
302 is_infected = entry["prediction"] == "Parasitized"
303 dot_color = "#f85149" if is_infected else "#2ea043"
304 label = "🦟 Parasitized" if is_infected else "✅ Uninfected"
305 st.markdown(f"""
306 <div class="history-item">
307 <span>
308 <span style="display:inline-block;width:10px;height:10px;border-radius:50%;
309 background:{dot_color};margin-right:8px;vertical-align:middle;"></span>
310 <strong>{label}</strong>
311 </span>
312 <span style="color:#8b949e;">{entry['confidence']} confidence</span>
313 <span style="color:#8b949e;font-family:'Space Mono',monospace;">{entry['latency_ms']}ms</span>
314 <span style="color:#6e7681;">{entry['timestamp']}</span>
315 </div>
316 """, unsafe_allow_html=True)