Skip to content

Commit c669c5f

Browse files
Refactor CSS and enhance model asset loading
Updated CSS styles and improved asset loading functionality.
1 parent d05b391 commit c669c5f

1 file changed

Lines changed: 69 additions & 26 deletions

File tree

app.py

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
# ── CSS Styling ───────────────────────────────────────────────────────────────
1818
st.markdown("""
1919
<style>
20-
/* 1. Import the smooth, rounded 'Outfit' font from Google Fonts */
2120
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&display=swap');
2221
23-
/* 2. FORCE IT UNIFORM ALL OVER THE APP FOR ALL TEXT (Overrides everything) */
2422
* {
2523
font-family: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif !important;
2624
}
@@ -29,10 +27,10 @@
2927
font-family: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif !important;
3028
}
3129
32-
/* 3. Smooth adjustments for Headings (No more pointy text!) */
33-
h1, h2, h3, .top-bar h1, [data-testid="stSidebar"] h2, [data-testid="stSidebar"] h3 {
30+
h1, h2, h3 {
3431
font-weight: 700 !important;
3532
letter-spacing: -0.3px !important;
33+
color: #1B5E20;
3634
}
3735
3836
/* Main background */
@@ -85,15 +83,13 @@
8583
background-color: #F1F8E9 !important;
8684
padding: 16px !important;
8785
}
88-
/* Force inner structural text blocks to remain high-contrast dark botanical green */
8986
[data-testid="stFileUploader"] label,
9087
[data-testid="stFileUploader"] p,
9188
[data-testid="stFileUploader"] span,
9289
[data-testid="stFileUploader"] small,
9390
[data-testid="stFileUploader"] div {
9491
color: #1B5E20 !important;
9592
}
96-
/* Style the internal interactive "Browse files" button safely */
9793
[data-testid="stFileUploader"] button {
9894
background-color: #2E7D32 !important;
9995
color: #ffffff !important;
@@ -157,23 +153,27 @@
157153
MODEL_DIR = "models"
158154
MODEL_FILENAME = "final_progression_model.h5"
159155
MODEL_PATH = os.path.join(MODEL_DIR, MODEL_FILENAME)
160-
MODEL_URL = f"https://huggingface.co/Sharmistha-catalyst/sick-greens-plant-disease/resolve/main/{MODEL_FILENAME}"
156+
METADATA_FILENAME = "metadata.json"
157+
METADATA_PATH = os.path.join(MODEL_DIR, METADATA_FILENAME)
161158

162-
# ── Safe Metadata Reader ──────────────────────────────────────────────────────
163-
metadata = {}
164-
if os.path.exists("models/metadata.json"):
165-
try:
166-
with open("models/metadata.json", "r") as f:
167-
metadata = json.load(f)
168-
except Exception:
169-
pass
159+
MODEL_URL = f"https://huggingface.co/Sharmistha-catalyst/sick-greens-plant-disease/resolve/main/{MODEL_FILENAME}"
160+
METADATA_URL = f"https://huggingface.co/Sharmistha-catalyst/sick-greens-plant-disease/resolve/main/{METADATA_FILENAME}"
170161

171-
# ── Engine Core ───────────────────────────────────────────────────────────────
162+
# ── Safe Infrastructure Sync Engine ───────────────────────────────────────────
172163
@st.cache_resource
173-
def load_model():
174-
"""Seamlessly stream model weights down from your Hugging Face space."""
164+
def load_assets():
165+
"""Download weights and metadata files safely from Hugging Face."""
166+
os.makedirs(MODEL_DIR, exist_ok=True)
167+
168+
# Sync Metadata
169+
if not os.path.exists(METADATA_PATH):
170+
try:
171+
urllib.request.urlretrieve(METADATA_URL, METADATA_PATH)
172+
except Exception:
173+
pass
174+
175+
# Sync Weights
175176
if not os.path.exists(MODEL_PATH):
176-
os.makedirs(MODEL_DIR, exist_ok=True)
177177
try:
178178
with st.spinner("📥 Downloading deep learning model weights from Hugging Face..."):
179179
urllib.request.urlretrieve(MODEL_URL, MODEL_PATH)
@@ -189,6 +189,18 @@ def load_model():
189189
pass
190190
return None, "demo"
191191

192+
# Load backend assets
193+
model, framework = load_assets()
194+
195+
# Read Meta-Metrics
196+
metadata = {}
197+
if os.path.exists(METADATA_PATH):
198+
try:
199+
with open(METADATA_PATH, "r") as f:
200+
metadata = json.load(f)
201+
except Exception:
202+
pass
203+
192204
def predict(model, framework, image: Image.Image):
193205
img = image.convert("RGB").resize((224, 224))
194206
arr = np.array(img, dtype=np.float32) / 255.0
@@ -203,7 +215,7 @@ def predict(model, framework, image: Image.Image):
203215
top5_idx = np.argsort(probs)[::-1][:5]
204216
return top5_idx, probs[top5_idx], probs
205217

206-
# ── Persistent Session State Management ───────────────────────────────────────
218+
# ── Session State Management ──────────────────────────────────────────────────
207219
if "analysis_done" not in st.session_state:
208220
st.session_state.analysis_done = False
209221
if "current_file_name" not in st.session_state:
@@ -222,7 +234,7 @@ def predict(model, framework, image: Image.Image):
222234
metrics = [
223235
(metadata.get("total_samples", "61,486"), "Dataset Images"),
224236
(metadata.get("model_architecture", "MobileNetV2"), "Architecture"),
225-
(f"{metadata.get('disease_acc', 0.94)*100:.1f}%" if "disease_acc" in metadata else "94.2%", "Model Accuracy"),
237+
(f"{metadata.get('disease_acc', 0.942)*100:.1f}%" if "disease_acc" in metadata else "94.2%", "Model Accuracy"),
226238
("Deployed", "Status")
227239
]
228240
for col, (num, lbl) in zip([c1, c2, c3, c4], metrics):
@@ -235,16 +247,17 @@ def predict(model, framework, image: Image.Image):
235247
with left:
236248
st.markdown("<h3 style='color: #2E7D32; margin-top: 0;'>📸 Scan Leaf</h3>", unsafe_allow_html=True)
237249

238-
uploaded = st.file_uploader("Upload leaf sample", type=["jpg", "png", "jpeg"], label_visibility="collapsed")
250+
uploaded = st.file_uploader("Upload leaf sample", type=["jpg", "png", "jpeg", "webp"], label_visibility="collapsed")
239251

240252
if uploaded:
241253
if st.session_state.current_file_name != uploaded.name:
242254
st.session_state.current_file_name = uploaded.name
243255
st.session_state.analysis_done = False
244256

245257
img = Image.open(uploaded)
246-
st.image(img, use_container_width=True)
247-
if st.button("Analyze Diagnostics", use_container_width=True):
258+
st.image(img, width='stretch')
259+
260+
if st.button("Analyze Diagnostics", width='stretch'):
248261
st.session_state.analysis_done = True
249262
else:
250263
st.session_state.current_file_name = None
@@ -257,10 +270,40 @@ def predict(model, framework, image: Image.Image):
257270
st.info("Awaiting input sample. Drop a leaf crop profile into the scanner area to run live neural inference.")
258271

259272
elif st.session_state.analysis_done:
260-
model, framework = load_model()
261273
if framework == "demo":
262274
st.warning("Running in simulated mode. Verify that your model name matches your Hugging Face storage precisely.")
263275

264276
top5_idx, top5_prob, all_probs = predict(model, framework, img)
265277
top_label = CLASS_LABELS[top5_idx[0]]
266-
top_conf
278+
top_conf = top5_prob[0]
279+
is_healthy = any(k in top_label for k in HEALTHY_KEYWORDS)
280+
281+
badge = "badge-healthy" if is_healthy else "badge-disease"
282+
card_theme = "" if is_healthy else "card-red"
283+
284+
st.markdown(f"""
285+
<div class="card {card_theme}">
286+
<p style="font-size:0.85rem;color:#888;margin:0;">DIAGNOSIS MATRIX</p>
287+
<p style="font-size:1.4rem;font-weight:800;margin:6px 0;">{top_label}</p>
288+
<span class="{badge}">{top_conf:.1%} Match Confidence</span>
289+
</div>
290+
""", unsafe_allow_html=True)
291+
292+
if not is_healthy:
293+
matched = next((k for k in DISEASE_INFO if k.lower() in top_label.lower()), None)
294+
if matched:
295+
info = DISEASE_INFO[matched]
296+
st.markdown(f"""
297+
<div class="card card-amber">
298+
<strong>📋 Clinical Protocol:</strong><br>
299+
• <b>Pathogen Class:</b> {info['cause']}<br>
300+
• <b>Threat Profile:</b> {info['severity']}<br>
301+
• <b>Remediation Strategy:</b> {info['treatment']}
302+
</div>
303+
""", unsafe_allow_html=True)
304+
305+
# Bar Charts Breakdown
306+
st.markdown("**⚡ Class Confidence Distribution**")
307+
import pandas as pd
308+
df = pd.DataFrame({"Classification": [CLASS_LABELS[i] for i in top5_idx], "Confidence": top5_prob})
309+
st.bar_chart(df.set_index("Classification"))

0 commit comments

Comments
 (0)