kkinza commited on
Commit
681a3f3
·
1 Parent(s): 3272783

UI redesign: added Geist font, custom brand blue, embedded logo, and removed emojis

Browse files
Files changed (4) hide show
  1. .gitignore +10 -0
  2. README.md +9 -10
  3. app.py +154 -114
  4. clipart1553592.png +0 -0
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ env/
8
+ .env
9
+ .DS_Store
10
+ .venv
README.md CHANGED
@@ -1,6 +1,5 @@
1
  ---
2
  title: Flood Risk Detection
3
- emoji: 🌊
4
  colorFrom: blue
5
  colorTo: red
6
  sdk: gradio
@@ -11,13 +10,13 @@ license: mit
11
  short_description: Flood segmentation + ZoeDepth + risk scoring
12
  ---
13
 
14
- # 🌊 Flood Detection & Depth-Aware Risk Assessment
15
 
16
  An end-to-end deep learning pipeline for flood detection, explainability, depth estimation, and risk scoring from satellite/aerial imagery.
17
 
18
  ---
19
 
20
- ## 🏗️ Project Structure
21
 
22
  ```
23
  ├── app.py ← Gradio UI (HF Spaces entry point)
@@ -34,7 +33,7 @@ An end-to-end deep learning pipeline for flood detection, explainability, depth
34
 
35
  ---
36
 
37
- ## ⚙️ Full Pipeline
38
 
39
  ```
40
  Input Image
@@ -67,7 +66,7 @@ Input Image
67
 
68
  ---
69
 
70
- ## 🚀 Deploy to Hugging Face Spaces
71
 
72
  ### Step 1 — Clone your Space
73
  ```bash
@@ -114,7 +113,7 @@ HF Spaces will automatically install `requirements.txt` and launch `app.py`.
114
 
115
  ---
116
 
117
- ## 💻 Run Locally
118
 
119
  ```bash
120
  # Install dependencies
@@ -134,7 +133,7 @@ uvicorn app.main:app --reload --port 8000 # FastAPI → http://loca
134
 
135
  ---
136
 
137
- ## 📡 API Reference
138
 
139
  ### `POST /predict`
140
  ```bash
@@ -171,7 +170,7 @@ Response:
171
 
172
  ---
173
 
174
- ## 📊 Model Performance
175
 
176
  | Model | Pixel Accuracy | IoU (Jaccard) | Dice / F1 | Precision | Recall |
177
  |-------|---------------|---------------|-----------|-----------|--------|
@@ -180,7 +179,7 @@ Response:
180
 
181
  ---
182
 
183
- ## 🎓 Viva Questions & Answers
184
 
185
  ### Architecture
186
 
@@ -301,7 +300,7 @@ The `.pkl` file stores metadata only — not the model weights. Contents: thresh
301
 
302
  ---
303
 
304
- ## 📦 Dependencies
305
 
306
  | Package | Purpose |
307
  |---------|---------|
 
1
  ---
2
  title: Flood Risk Detection
 
3
  colorFrom: blue
4
  colorTo: red
5
  sdk: gradio
 
10
  short_description: Flood segmentation + ZoeDepth + risk scoring
11
  ---
12
 
13
+ # <img src="clipart1553592.png" width="36" align="top"> Flood Detection & Depth-Aware Risk Assessment
14
 
15
  An end-to-end deep learning pipeline for flood detection, explainability, depth estimation, and risk scoring from satellite/aerial imagery.
16
 
17
  ---
18
 
19
+ ## Project Structure
20
 
21
  ```
22
  ├── app.py ← Gradio UI (HF Spaces entry point)
 
33
 
34
  ---
35
 
36
+ ## Full Pipeline
37
 
38
  ```
39
  Input Image
 
66
 
67
  ---
68
 
69
+ ## Deploy to Hugging Face Spaces
70
 
71
  ### Step 1 — Clone your Space
72
  ```bash
 
113
 
114
  ---
115
 
116
+ ## Run Locally
117
 
118
  ```bash
119
  # Install dependencies
 
133
 
134
  ---
135
 
136
+ ## API Reference
137
 
138
  ### `POST /predict`
139
  ```bash
 
170
 
171
  ---
172
 
173
+ ## Model Performance
174
 
175
  | Model | Pixel Accuracy | IoU (Jaccard) | Dice / F1 | Precision | Recall |
176
  |-------|---------------|---------------|-----------|-----------|--------|
 
179
 
180
  ---
181
 
182
+ ## Viva Questions & Answers
183
 
184
  ### Architecture
185
 
 
300
 
301
  ---
302
 
303
+ ## Dependencies
304
 
305
  | Package | Purpose |
306
  |---------|---------|
app.py CHANGED
@@ -6,73 +6,94 @@ import gradio as gr
6
  import numpy as np
7
  from PIL import Image
8
  import sys, os
 
 
9
 
10
  sys.path.insert(0, os.path.dirname(__file__))
11
  from app.model_utils import run_pipeline
12
 
 
 
 
 
 
 
 
 
 
 
13
  # ── Risk config ────────────────────────────────────────────────────────────────
14
  RISK_CFG = {
15
- "Low": {"colour": "#27ae60", "bg": "#eafaf1", "icon": "🟢", "bar": 20},
16
- "Moderate": {"colour": "#f39c12", "bg": "#fef9e7", "icon": "🟡", "bar": 50},
17
- "High": {"colour": "#e67e22", "bg": "#fdf2e9", "icon": "🟠", "bar": 75},
18
- "Critical": {"colour": "#e74c3c", "bg": "#fdedec", "icon": "🔴", "bar": 100},
19
  }
20
 
21
  CSS = """
 
 
22
  /* ── page ── */
23
- body, .gradio-container { background: #0f1117 !important; color: #e8eaf0 !important; }
 
 
 
24
 
25
  /* ── header band ── */
26
  #header-band {
27
- background: linear-gradient(135deg, #0d47a1 0%, #1565c0 40%, #0277bd 100%);
28
- border-radius: 16px; padding: 28px 32px 20px; margin-bottom: 20px;
29
- box-shadow: 0 4px 24px rgba(0,0,0,0.5);
 
 
 
30
  }
31
- #header-band h1 { color: #fff; font-size: 2em; margin: 0 0 6px; }
32
- #header-band p { color: #bbdefb; margin: 0; font-size: 0.97em; }
33
 
34
  /* ── metric pill row ── */
35
  .metric-pill {
36
- display: inline-block; background: rgba(255,255,255,0.08);
37
- border: 1px solid rgba(255,255,255,0.18); border-radius: 20px;
38
- padding: 4px 14px; margin: 4px; font-size: 0.88em; color: #e3f2fd;
39
  }
40
- .metric-pill strong { color: #64b5f6; }
41
 
42
  /* ── upload + button panel ── */
43
- #left-panel { background: #1a1d27; border-radius: 14px; padding: 16px; }
44
 
45
  /* ── analyse button ── */
46
  #analyse-btn {
47
- background: linear-gradient(90deg, #1565c0, #0277bd) !important;
48
- color: #fff !important; border: none !important;
49
- border-radius: 10px !important; font-size: 1.05em !important;
50
- padding: 12px !important; margin-top: 10px !important;
51
- box-shadow: 0 3px 12px rgba(2,119,189,0.45) !important;
52
- transition: transform .15s, box-shadow .15s !important;
53
  }
54
  #analyse-btn:hover {
55
  transform: translateY(-2px) !important;
56
- box-shadow: 0 6px 20px rgba(2,119,189,0.6) !important;
 
57
  }
58
 
59
  /* ── section labels ── */
60
  .section-label {
61
- font-size: 0.78em; font-weight: 700; letter-spacing: .08em;
62
- text-transform: uppercase; color: #64b5f6; margin: 18px 0 6px;
63
  }
64
 
65
  /* ── image cards ── */
66
- .image-card { background: #1a1d27 !important; border-radius: 12px !important;
67
- border: 1px solid #2a2d3a !important; overflow: hidden; }
68
 
69
  /* ── risk card ── */
70
- #risk-card { border-radius: 14px; overflow: hidden; }
71
 
72
  /* ── how-it-works table ── */
73
- .how-table { width:100%; border-collapse:collapse; font-size:0.9em; }
74
- .how-table th { background:#1565c0; color:#fff; padding:8px 12px; text-align:left; }
75
- .how-table td { padding:8px 12px; border-bottom:1px solid #2a2d3a; color:#cfd8dc; }
76
  .how-table tr:last-child td { border-bottom: none; }
77
  """
78
 
@@ -80,17 +101,17 @@ body, .gradio-container { background: #0f1117 !important; color: #e8eaf0 !import
80
  def _score_bar(score: float, colour: str) -> str:
81
  """Animated CSS progress bar."""
82
  return f"""
83
- <div style="background:#2a2d3a; border-radius:8px; height:10px; margin:8px 0 14px; overflow:hidden;">
84
- <div style="width:{score}%; background:linear-gradient(90deg,{colour}99,{colour});
85
- height:100%; border-radius:8px;
86
  transition: width 1s ease;"></div>
87
  </div>"""
88
 
89
- def _stat_row(icon, label, value, colour) -> str:
90
  return f"""
91
  <div style="display:flex; justify-content:space-between; align-items:center;
92
- padding:7px 0; border-bottom:1px solid rgba(255,255,255,0.07);">
93
- <span style="color:#90a4ae;">{icon} {label}</span>
94
  <span style="font-weight:700; color:{colour};">{value}</span>
95
  </div>"""
96
 
@@ -106,13 +127,13 @@ def build_risk_html(risk: dict, depth_info: dict) -> str:
106
  warning_html = ""
107
  if warning:
108
  warning_html = f"""
109
- <div style="background:#7f1d1d; border:1px solid #ef4444; border-radius:8px;
110
- padding:10px 14px; margin-bottom:14px; font-size:0.9em; color:#fca5a5;">
111
  {warning}
112
  </div>"""
113
 
114
  recs_html = "".join(
115
- f'<li style="margin:5px 0; color:#cfd8dc;">{r}</li>'
116
  for r in risk["recommendations"]
117
  )
118
 
@@ -120,36 +141,37 @@ def build_risk_html(risk: dict, depth_info: dict) -> str:
120
  metrics_html = ""
121
  if metrics:
122
  pills = "".join(
123
- f'<span class="metric-pill">{k}: <strong>{v}</strong></span>'
124
  for k, v in metrics.items() if k != "Model"
125
  )
126
  metrics_html = f"""
127
- <div style="margin-top:14px; padding-top:12px;
128
- border-top:1px solid rgba(255,255,255,0.1);">
129
- <div style="font-size:0.78em; text-transform:uppercase; letter-spacing:.08em;
130
- color:#64b5f6; margin-bottom:6px;">📊 Model Performance</div>
131
  {pills}
132
  </div>"""
133
 
134
  return f"""
135
- <div id="risk-card" style="background:linear-gradient(160deg,#1a1d27 60%,{c}18);
136
- border:2px solid {c}; border-radius:14px; padding:20px; font-family:sans-serif;">
137
 
138
  {warning_html}
139
 
140
  <!-- header -->
141
- <div style="display:flex; align-items:center; gap:12px; margin-bottom:4px;">
142
- <span style="font-size:2.2em;">{cfg['icon']}</span>
143
- <div>
144
- <div style="font-size:0.75em; text-transform:uppercase; letter-spacing:.1em;
145
- color:{c}; font-weight:700;">Risk Level</div>
146
- <div style="font-size:1.9em; font-weight:800; color:{c}; line-height:1.1;">
147
- {level}
 
 
148
  </div>
149
  </div>
150
  <div style="margin-left:auto; text-align:right;">
151
- <div style="font-size:2.4em; font-weight:900; color:{c};">{score}</div>
152
- <div style="font-size:0.75em; color:#78909c;">/ 100</div>
153
  </div>
154
  </div>
155
 
@@ -157,25 +179,25 @@ def build_risk_html(risk: dict, depth_info: dict) -> str:
157
 
158
  <!-- confidence row -->
159
  <div style="display:flex; justify-content:space-between; align-items:center;
160
- padding:5px 0 10px; border-bottom:1px solid rgba(255,255,255,0.07);">
161
- <span style="color:#90a4ae; font-size:0.88em;">🎯 Model Confidence</span>
162
- <span style="font-weight:700; color:{'#27ae60' if conf >= 50 else '#e74c3c'};">
163
  {conf}%
164
  </span>
165
  </div>
166
 
167
  <!-- stats grid -->
168
- {_stat_row("🌊", "Flood Coverage", f"{risk['flood_pct']}%", c)}
169
- {_stat_row("📏", "Avg Flood Depth", f"{risk['avg_depth_m']} m", c)}
170
- {_stat_row("📐", "Max Flood Depth", f"{depth_info['max_depth_m']} m", c)}
171
- {_stat_row("🔵", "Depth Category", depth_info['depth_category'], c)}
172
 
173
  <!-- recommendations -->
174
- <div style="margin-top:14px; padding-top:12px;
175
- border-top:1px solid rgba(255,255,255,0.1);">
176
- <div style="font-size:0.78em; text-transform:uppercase; letter-spacing:.08em;
177
- color:#64b5f6; margin-bottom:8px;">📋 Recommendations</div>
178
- <ul style="margin:0; padding-left:18px; line-height:1.7;">
179
  {recs_html}
180
  </ul>
181
  </div>
@@ -187,12 +209,12 @@ def build_risk_html(risk: dict, depth_info: dict) -> str:
187
  # ── inference ─────────────────────────────────────────────────────────────────
188
  def predict(image: Image.Image):
189
  if image is None:
190
- placeholder = """
191
- <div style="background:#1a1d27; border:2px dashed #2a2d3a; border-radius:14px;
192
- padding:40px; text-align:center; color:#546e7a; font-family:sans-serif;">
193
- <div style="font-size:2.5em; margin-bottom:10px;">🌊</div>
194
- <div style="font-size:1.1em;">Upload an image and click <strong>Analyse</strong></div>
195
- <div style="font-size:0.85em; margin-top:6px; color:#37474f;">
196
  Supports satellite, aerial, or ground-level flood images
197
  </div>
198
  </div>"""
@@ -215,12 +237,12 @@ def predict(image: Image.Image):
215
 
216
 
217
  # ── UI layout ─────────────────────────────────────────────────────────────────
218
- with gr.Blocks(title="🌊 Flood Detection AI", theme=gr.themes.Soft()) as demo:
219
 
220
  # ── Header ──────────────────────────────────────────────────────────────
221
- gr.HTML("""
222
  <div id="header-band">
223
- <h1>🌊 Flood Detection &amp; Risk Assessment</h1>
224
  <p>
225
  Attention UNet segmentation &nbsp;·&nbsp; Grad-CAM explainability
226
  &nbsp;·&nbsp; ZoeDepth metric depth &nbsp;·&nbsp; AI-powered risk scoring
@@ -239,7 +261,7 @@ with gr.Blocks(title="🌊 Flood Detection AI", theme=gr.themes.Soft()) as demo:
239
  with gr.Row(equal_height=False):
240
 
241
  with gr.Column(scale=4, elem_id="left-panel"):
242
- gr.HTML('<div class="section-label">📷 Input Image</div>')
243
  input_image = gr.Image(
244
  type="pil",
245
  label="",
@@ -247,71 +269,71 @@ with gr.Blocks(title="🌊 Flood Detection AI", theme=gr.themes.Soft()) as demo:
247
  show_label=False,
248
  )
249
  run_btn = gr.Button(
250
- "🔍 Analyse Flood Risk",
251
  variant="primary",
252
  elem_id="analyse-btn",
253
  )
254
  gr.HTML("""
255
- <div style="margin-top:12px; padding:10px 14px; background:#12151f;
256
- border-radius:10px; border-left:3px solid #1565c0;">
257
- <div style="font-size:0.78em; color:#64b5f6; font-weight:700;
258
- text-transform:uppercase; letter-spacing:.07em; margin-bottom:6px;">
259
  Pipeline Steps
260
  </div>
261
- <div style="font-size:0.85em; color:#90a4ae; line-height:2;">
262
- 1️⃣ &nbsp;Preprocess → 512×512 RGB, normalise<br>
263
- 2️⃣ &nbsp;Attention UNet → binary flood mask<br>
264
- 3️⃣ &nbsp;Grad-CAM → attention heatmap<br>
265
- 4️⃣ &nbsp;ZoeDepth → per-pixel depth (metres)<br>
266
- 5️⃣ &nbsp;Risk engine → level + score + advice
267
  </div>
268
  </div>
269
  """)
270
 
271
  with gr.Column(scale=6):
272
- gr.HTML('<div class="section-label">⚠️ Risk Assessment</div>')
273
  risk_display = gr.HTML(
274
- value="""
275
- <div style="background:#1a1d27; border:2px dashed #2a2d3a;
276
- border-radius:14px; padding:40px; text-align:center;
277
- color:#546e7a; font-family:sans-serif;">
278
- <div style="font-size:2.5em; margin-bottom:10px;">🌊</div>
279
- <div style="font-size:1.1em;">
280
  Upload an image and click <strong>Analyse</strong>
281
  </div>
282
  </div>"""
283
  )
284
 
285
  # ── Visual outputs ───────────────────────────────────────────────────────
286
- gr.HTML('<div class="section-label">🗺️ Segmentation &amp; Explainability</div>')
287
  with gr.Row():
288
  overlay_out = gr.Image(
289
- label="🔵 Flood Mask Overlay",
290
  elem_classes=["image-card"],
291
  )
292
  mask_bw_out = gr.Image(
293
- label="Binary Flood Mask (White = Flooded)",
294
  elem_classes=["image-card"],
295
  image_mode="L",
296
  )
297
  gradcam_out = gr.Image(
298
- label="🔥 Grad-CAM (TURBO)",
299
  elem_classes=["image-card"],
300
  )
301
 
302
- gr.HTML('<div class="section-label">📏 ZoeDepth Estimation</div>')
303
  with gr.Row():
304
  depth_map_out = gr.Image(
305
- label="🌈 Full Depth Map (PLASMA)",
306
  elem_classes=["image-card"],
307
  )
308
  depth_flood_out = gr.Image(
309
- label="🌊 Flood-Region Depth",
310
  elem_classes=["image-card"],
311
  )
312
 
313
  # ── How it works ─────────────────────────────────────────────────────────
314
- with gr.Accordion("⚙️ How it works", open=False):
315
  gr.HTML("""
316
  <table class="how-table">
317
  <tr>
@@ -325,17 +347,17 @@ with gr.Blocks(title="🌊 Flood Detection AI", theme=gr.themes.Soft()) as demo:
325
  <tr>
326
  <td>2</td>
327
  <td><strong>Attention UNet</strong></td>
328
- <td>Predicts binary flood mask attention gates suppress irrelevant features</td>
329
  </tr>
330
  <tr>
331
  <td>3</td>
332
  <td><strong>Grad-CAM</strong></td>
333
- <td>Gradient-weighted class activation map at conv2d_130 TURBO colormap with contour</td>
334
  </tr>
335
  <tr>
336
  <td>4</td>
337
  <td><strong>ZoeDepth (Intel/zoedepth-nyu)</strong></td>
338
- <td>Monocular metric depth estimation outputs depth in metres per pixel</td>
339
  </tr>
340
  <tr>
341
  <td>5</td>
@@ -343,13 +365,13 @@ with gr.Blocks(title="🌊 Flood Detection AI", theme=gr.themes.Soft()) as demo:
343
  <td>Combines flood coverage % + avg depth → Low / Moderate / High / Critical</td>
344
  </tr>
345
  </table>
346
- <div style="margin-top:14px; padding:12px 16px; background:#12151f;
347
- border-radius:10px; font-size:0.88em; color:#90a4ae;">
348
- <strong style="color:#64b5f6;">Risk thresholds:</strong>
349
- &nbsp; 🟢 Low (&lt;15% flood, &lt;0.8m)
350
- &nbsp; 🟡 Moderate (15–35%, 0.8–1.5m)
351
- &nbsp; 🟠 High (35–60%, 1.5–2.2m)
352
- &nbsp; 🔴 Critical (&gt;60% or &gt;2.2m)
353
  </div>
354
  """)
355
 
@@ -361,4 +383,22 @@ with gr.Blocks(title="🌊 Flood Detection AI", theme=gr.themes.Soft()) as demo:
361
  )
362
 
363
  if __name__ == "__main__":
364
- demo.launch(server_name="0.0.0.0", server_port=7860, css=CSS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  import numpy as np
7
  from PIL import Image
8
  import sys, os
9
+ import base64
10
+ from gradio.themes.utils.colors import Color
11
 
12
  sys.path.insert(0, os.path.dirname(__file__))
13
  from app.model_utils import run_pipeline
14
 
15
+ def get_base64_image(path):
16
+ try:
17
+ with open(path, "rb") as img_file:
18
+ return base64.b64encode(img_file.read()).decode('utf-8')
19
+ except Exception:
20
+ return ""
21
+
22
+ logo_base64 = get_base64_image(os.path.join(os.path.dirname(__file__), "clipart1553592.png"))
23
+ logo_src = f"data:image/png;base64,{logo_base64}"
24
+
25
  # ── Risk config ────────────────────────────────────────────────────────────────
26
  RISK_CFG = {
27
+ "Low": {"colour": "#27ae60", "bg": "#eafaf1", "bar": 20},
28
+ "Moderate": {"colour": "#f39c12", "bg": "#fef9e7", "bar": 50},
29
+ "High": {"colour": "#e67e22", "bg": "#fdf2e9", "bar": 75},
30
+ "Critical": {"colour": "#e74c3c", "bg": "#fdedec", "bar": 100},
31
  }
32
 
33
  CSS = """
34
+ @import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700;800&display=swap');
35
+
36
  /* ── page ── */
37
+ body, .gradio-container {
38
+ font-family: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
39
+ }
40
+ .light body, .light .gradio-container { background-color: #f4f5f8 !important; }
41
 
42
  /* ── header band ── */
43
  #header-band {
44
+ background: var(--block-background-fill);
45
+ border-radius: 12px;
46
+ padding: 32px 36px 24px;
47
+ margin-bottom: 24px;
48
+ box-shadow: var(--block-shadow);
49
+ border: 1px solid var(--border-color-primary);
50
  }
51
+ #header-band h1 { color: var(--body-text-color); font-size: 2.2em; margin: 0 0 8px; font-weight: 800; letter-spacing: -0.03em; }
52
+ #header-band p { color: var(--body-text-color-subdued); margin: 0; font-size: 1.05em; font-weight: 400; }
53
 
54
  /* ── metric pill row ── */
55
  .metric-pill {
56
+ display: inline-block; background: var(--background-fill-secondary);
57
+ border: 1px solid var(--border-color-primary); border-radius: 8px;
58
+ padding: 6px 16px; margin: 6px 6px 6px 0; font-size: 0.9em; color: var(--body-text-color-subdued); font-weight: 500;
59
  }
60
+ .metric-pill strong { color: var(--body-text-color); font-weight: 700; }
61
 
62
  /* ── upload + button panel ── */
63
+ #left-panel { background: var(--block-background-fill); border-radius: 12px; padding: 20px; border: 1px solid var(--border-color-primary); box-shadow: var(--block-shadow); }
64
 
65
  /* ── analyse button ── */
66
  #analyse-btn {
67
+ background: var(--primary-500) !important;
68
+ color: white !important; border: none !important;
69
+ border-radius: 8px !important; font-size: 1.1em !important; font-weight: 600 !important;
70
+ padding: 14px !important; margin-top: 12px !important;
71
+ box-shadow: 0 4px 14px rgba(0,0,0,0.1) !important;
72
+ transition: all .2s ease !important;
73
  }
74
  #analyse-btn:hover {
75
  transform: translateY(-2px) !important;
76
+ box-shadow: 0 8px 25px rgba(0,0,0,0.15) !important;
77
+ background: var(--primary-600) !important;
78
  }
79
 
80
  /* ── section labels ── */
81
  .section-label {
82
+ font-size: 0.85em; font-weight: 700; letter-spacing: .1em;
83
+ text-transform: uppercase; color: var(--body-text-color-subdued); margin: 24px 0 10px;
84
  }
85
 
86
  /* ── image cards ── */
87
+ .image-card { background: var(--block-background-fill) !important; border-radius: 12px !important;
88
+ border: 1px solid var(--border-color-primary) !important; overflow: hidden; box-shadow: var(--block-shadow) !important; }
89
 
90
  /* ── risk card ── */
91
+ #risk-card { border-radius: 12px; overflow: hidden; background: var(--block-background-fill); box-shadow: var(--block-shadow); border: 1px solid var(--border-color-primary); }
92
 
93
  /* ── how-it-works table ── */
94
+ .how-table { width:100%; border-collapse:collapse; font-size:0.95em; }
95
+ .how-table th { background: var(--background-fill-secondary); color: var(--body-text-color); padding:14px 16px; text-align:left; border-bottom: 2px solid var(--border-color-primary); font-weight: 600; }
96
+ .how-table td { padding:14px 16px; border-bottom:1px solid var(--border-color-primary); color: var(--body-text-color); }
97
  .how-table tr:last-child td { border-bottom: none; }
98
  """
99
 
 
101
  def _score_bar(score: float, colour: str) -> str:
102
  """Animated CSS progress bar."""
103
  return f"""
104
+ <div style="background:var(--background-fill-secondary); border-radius:99px; height:12px; margin:12px 0 20px; overflow:hidden;">
105
+ <div style="width:{score}%; background:linear-gradient(90deg,{colour}bb,{colour});
106
+ height:100%; border-radius:99px;
107
  transition: width 1s ease;"></div>
108
  </div>"""
109
 
110
+ def _stat_row(label, value, colour) -> str:
111
  return f"""
112
  <div style="display:flex; justify-content:space-between; align-items:center;
113
+ padding:12px 0; border-bottom:1px solid var(--border-color-primary);">
114
+ <span style="color:var(--body-text-color-subdued); font-weight: 500;">{label}</span>
115
  <span style="font-weight:700; color:{colour};">{value}</span>
116
  </div>"""
117
 
 
127
  warning_html = ""
128
  if warning:
129
  warning_html = f"""
130
+ <div style="background:var(--background-fill-secondary); border-left: 4px solid #ef4444; border-radius:8px;
131
+ padding:14px 18px; margin-bottom:20px; font-size:0.95em; color:var(--body-text-color); font-weight: 500;">
132
  {warning}
133
  </div>"""
134
 
135
  recs_html = "".join(
136
+ f'<li style="margin:8px 0; color:var(--body-text-color);">{r}</li>'
137
  for r in risk["recommendations"]
138
  )
139
 
 
141
  metrics_html = ""
142
  if metrics:
143
  pills = "".join(
144
+ f'<span class="metric-pill" style="font-size: 0.85em; padding: 4px 12px;">{k}: <strong>{v}</strong></span>'
145
  for k, v in metrics.items() if k != "Model"
146
  )
147
  metrics_html = f"""
148
+ <div style="margin-top:24px; padding-top:20px;
149
+ border-top:1px solid var(--border-color-primary);">
150
+ <div style="font-size:0.8em; text-transform:uppercase; letter-spacing:.1em; font-weight: 700;
151
+ color:var(--body-text-color-subdued); margin-bottom:12px;">Model Performance</div>
152
  {pills}
153
  </div>"""
154
 
155
  return f"""
156
+ <div id="risk-card" style="padding:32px; font-family:'Geist', -apple-system, BlinkMacSystemFont, sans-serif;">
 
157
 
158
  {warning_html}
159
 
160
  <!-- header -->
161
+ <div style="display:flex; align-items:center; gap:20px; margin-bottom:12px;">
162
+ <div style="display:flex; align-items:center;">
163
+ <span style="display:block; width: 16px; height: 16px; border-radius: 50%; background: {c}; margin-right: 12px;"></span>
164
+ <div>
165
+ <div style="font-size:0.8em; text-transform:uppercase; letter-spacing:.12em;
166
+ color:{c}; font-weight:800;">Risk Level</div>
167
+ <div style="font-size:2.4em; font-weight:800; color:{c}; line-height:1.2; letter-spacing:-0.02em;">
168
+ {level}
169
+ </div>
170
  </div>
171
  </div>
172
  <div style="margin-left:auto; text-align:right;">
173
+ <div style="font-size:3em; font-weight:900; color:var(--body-text-color); letter-spacing:-0.03em; line-height:1;">{score}</div>
174
+ <div style="font-size:0.9em; color:var(--body-text-color-subdued); font-weight: 600; margin-top:4px;">/ 100</div>
175
  </div>
176
  </div>
177
 
 
179
 
180
  <!-- confidence row -->
181
  <div style="display:flex; justify-content:space-between; align-items:center;
182
+ padding:12px 0 16px; border-bottom:1px solid var(--border-color-primary);">
183
+ <span style="color:var(--body-text-color-subdued); font-size:0.95em; font-weight: 500;">Model Confidence</span>
184
+ <span style="font-weight:700; font-size: 1.15em; color:{'#10b981' if conf >= 50 else '#ef4444'};">
185
  {conf}%
186
  </span>
187
  </div>
188
 
189
  <!-- stats grid -->
190
+ {_stat_row("Flood Coverage", f"{risk['flood_pct']}%", c)}
191
+ {_stat_row("Avg Flood Depth", f"{risk['avg_depth_m']} m", c)}
192
+ {_stat_row("Max Flood Depth", f"{depth_info['max_depth_m']} m", c)}
193
+ {_stat_row("Depth Category", depth_info['depth_category'], c)}
194
 
195
  <!-- recommendations -->
196
+ <div style="margin-top:24px; padding-top:20px;
197
+ border-top:1px solid var(--border-color-primary);">
198
+ <div style="font-size:0.8em; text-transform:uppercase; letter-spacing:.1em; font-weight: 700;
199
+ color:var(--body-text-color-subdued); margin-bottom:12px;">Recommendations</div>
200
+ <ul style="margin:0; padding-left:24px; line-height:1.8; font-size:0.95em; color:var(--body-text-color);">
201
  {recs_html}
202
  </ul>
203
  </div>
 
209
  # ── inference ─────────────────────────────────────────────────────────────────
210
  def predict(image: Image.Image):
211
  if image is None:
212
+ placeholder = f"""
213
+ <div style="background:var(--background-fill-primary); border:2px dashed var(--border-color-primary); border-radius:12px;
214
+ padding:60px 20px; text-align:center; color:var(--body-text-color-subdued); font-family:'Geist', -apple-system, BlinkMacSystemFont, sans-serif;">
215
+ <div style="display:flex; justify-content:center;"><img src="{logo_src}" style="width: 60px; margin-bottom:16px; opacity: 0.8;"></div>
216
+ <div style="font-size:1.3em; font-weight: 600; color: var(--body-text-color);">Upload an image and click <strong>Analyse</strong></div>
217
+ <div style="font-size:1em; margin-top:10px; color:var(--body-text-color-subdued); font-weight: 400;">
218
  Supports satellite, aerial, or ground-level flood images
219
  </div>
220
  </div>"""
 
237
 
238
 
239
  # ── UI layout ─────────────────────────────────────────────────────────────────
240
+ with gr.Blocks(title="Flood Detection AI") as demo:
241
 
242
  # ── Header ──────────────────────────────────────────────────────────────
243
+ gr.HTML(f"""
244
  <div id="header-band">
245
+ <h1><img src="{logo_src}" style="width: 40px; vertical-align: middle; margin-right: 12px; margin-bottom: 6px;"> Flood Detection &amp; Risk Assessment</h1>
246
  <p>
247
  Attention UNet segmentation &nbsp;·&nbsp; Grad-CAM explainability
248
  &nbsp;·&nbsp; ZoeDepth metric depth &nbsp;·&nbsp; AI-powered risk scoring
 
261
  with gr.Row(equal_height=False):
262
 
263
  with gr.Column(scale=4, elem_id="left-panel"):
264
+ gr.HTML('<div class="section-label">Input Image</div>')
265
  input_image = gr.Image(
266
  type="pil",
267
  label="",
 
269
  show_label=False,
270
  )
271
  run_btn = gr.Button(
272
+ "Analyse Flood Risk",
273
  variant="primary",
274
  elem_id="analyse-btn",
275
  )
276
  gr.HTML("""
277
+ <div style="margin-top:20px; padding:20px 24px; background:var(--background-fill-secondary);
278
+ border-radius:12px; border: 1px solid var(--border-color-primary); border-left:4px solid var(--primary-500);">
279
+ <div style="font-size:0.85em; color:var(--body-text-color); font-weight:800;
280
+ text-transform:uppercase; letter-spacing:.1em; margin-bottom:12px;">
281
  Pipeline Steps
282
  </div>
283
+ <div style="font-size:0.95em; color:var(--body-text-color-subdued); line-height:2.2; font-weight: 500;">
284
+ <span style="opacity: 0.5; margin-right: 8px;">1.</span> Preprocess → 512×512 RGB, normalise<br>
285
+ <span style="opacity: 0.5; margin-right: 8px;">2.</span> Attention UNet → binary flood mask<br>
286
+ <span style="opacity: 0.5; margin-right: 8px;">3.</span> Grad-CAM → attention heatmap<br>
287
+ <span style="opacity: 0.5; margin-right: 8px;">4.</span> ZoeDepth → per-pixel depth (metres)<br>
288
+ <span style="opacity: 0.5; margin-right: 8px;">5.</span> Risk engine → level + score + advice
289
  </div>
290
  </div>
291
  """)
292
 
293
  with gr.Column(scale=6):
294
+ gr.HTML('<div class="section-label">Risk Assessment</div>')
295
  risk_display = gr.HTML(
296
+ value=f"""
297
+ <div style="background:var(--background-fill-primary); border:2px dashed var(--border-color-primary);
298
+ border-radius:12px; padding:60px 20px; text-align:center;
299
+ color:var(--body-text-color-subdued); font-family:'Geist', -apple-system, BlinkMacSystemFont, sans-serif; height: 100%; display: flex; flex-direction: column; justify-content: center; min-height: 400px; box-shadow: var(--block-shadow);">
300
+ <div style="display:flex; justify-content:center;"><img src="{logo_src}" style="width: 60px; margin-bottom:16px; opacity: 0.8;"></div>
301
+ <div style="font-size:1.3em; font-weight: 600; color: var(--body-text-color);">
302
  Upload an image and click <strong>Analyse</strong>
303
  </div>
304
  </div>"""
305
  )
306
 
307
  # ── Visual outputs ───────────────────────────────────────────────────────
308
+ gr.HTML('<div class="section-label">Segmentation &amp; Explainability</div>')
309
  with gr.Row():
310
  overlay_out = gr.Image(
311
+ label="Flood Mask Overlay",
312
  elem_classes=["image-card"],
313
  )
314
  mask_bw_out = gr.Image(
315
+ label="Binary Flood Mask (White = Flooded)",
316
  elem_classes=["image-card"],
317
  image_mode="L",
318
  )
319
  gradcam_out = gr.Image(
320
+ label="Grad-CAM (TURBO)",
321
  elem_classes=["image-card"],
322
  )
323
 
324
+ gr.HTML('<div class="section-label">ZoeDepth Estimation</div>')
325
  with gr.Row():
326
  depth_map_out = gr.Image(
327
+ label="Full Depth Map (PLASMA)",
328
  elem_classes=["image-card"],
329
  )
330
  depth_flood_out = gr.Image(
331
+ label="Flood-Region Depth",
332
  elem_classes=["image-card"],
333
  )
334
 
335
  # ── How it works ─────────────────────────────────────────────────────────
336
+ with gr.Accordion("How it works", open=False):
337
  gr.HTML("""
338
  <table class="how-table">
339
  <tr>
 
347
  <tr>
348
  <td>2</td>
349
  <td><strong>Attention UNet</strong></td>
350
+ <td>Predicts binary flood mask: attention gates suppress irrelevant features</td>
351
  </tr>
352
  <tr>
353
  <td>3</td>
354
  <td><strong>Grad-CAM</strong></td>
355
+ <td>Gradient-weighted class activation map at conv2d_130: TURBO colormap with contour</td>
356
  </tr>
357
  <tr>
358
  <td>4</td>
359
  <td><strong>ZoeDepth (Intel/zoedepth-nyu)</strong></td>
360
+ <td>Monocular metric depth estimation: outputs depth in metres per pixel</td>
361
  </tr>
362
  <tr>
363
  <td>5</td>
 
365
  <td>Combines flood coverage % + avg depth → Low / Moderate / High / Critical</td>
366
  </tr>
367
  </table>
368
+ <div style="margin-top:20px; padding:16px 20px; background:var(--background-fill-secondary); border: 1px solid var(--border-color-primary);
369
+ border-radius:12px; font-size:0.95em; color:var(--body-text-color); font-weight: 500;">
370
+ <strong style="color:var(--body-text-color); margin-right: 12px;">Risk thresholds:</strong>
371
+ <span style="display:inline-block; width:8px; height:8px; border-radius:50%; background:#27ae60; margin-right:6px;"></span> Low (&lt;15% flood, &lt;0.8m) &nbsp;&nbsp;
372
+ <span style="display:inline-block; width:8px; height:8px; border-radius:50%; background:#f39c12; margin-right:6px;"></span> Moderate (15–35%, 0.8–1.5m) &nbsp;&nbsp;
373
+ <span style="display:inline-block; width:8px; height:8px; border-radius:50%; background:#e67e22; margin-right:6px;"></span> High (35–60%, 1.5–2.2m) &nbsp;&nbsp;
374
+ <span style="display:inline-block; width:8px; height:8px; border-radius:50%; background:#e74c3c; margin-right:6px;"></span> Critical (&gt;60% or &gt;2.2m)
375
  </div>
376
  """)
377
 
 
383
  )
384
 
385
  if __name__ == "__main__":
386
+ brand_blue = Color(
387
+ name="brand_blue",
388
+ c50="#f0f7fb",
389
+ c100="#e1f0f7",
390
+ c200="#c4e1ef",
391
+ c300="#a6d2e8",
392
+ c400="#89c3e0",
393
+ c500="#43a0d6",
394
+ c600="#3680ab",
395
+ c700="#286080",
396
+ c800="#1b4056",
397
+ c900="#0d202b",
398
+ c950="#071015",
399
+ )
400
+ custom_theme = gr.themes.Soft(
401
+ primary_hue=brand_blue,
402
+ neutral_hue=gr.themes.colors.slate,
403
+ )
404
+ demo.launch(server_name="0.0.0.0", server_port=7860, css=CSS, theme=custom_theme)
clipart1553592.png ADDED