1717# ── CSS Styling ───────────────────────────────────────────────────────────────
1818st .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 }
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 */
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;
157153MODEL_DIR = "models"
158154MODEL_FILENAME = "final_progression_model.h5"
159155MODEL_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+
192204def 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 ─────────── ───────────────────────────────────────
207219if "analysis_done" not in st .session_state :
208220 st .session_state .analysis_done = False
209221if "current_file_name" not in st .session_state :
@@ -222,7 +234,7 @@ def predict(model, framework, image: Image.Image):
222234metrics = [
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]
228240for col , (num , lbl ) in zip ([c1 , c2 , c3 , c4 ], metrics ):
@@ -235,16 +247,17 @@ def predict(model, framework, image: Image.Image):
235247with 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