--- title: Flood Detection AI colorFrom: blue colorTo: green sdk: gradio sdk_version: 6.14.0 app_file: app.py pinned: true short_description: AI flood segmentation, depth estimation, and risk assessment thumbnail: >- https://cdn-uploads.huggingface.co/production/uploads/69ff632213d56d419860a39d/Rq8pQDogwufM4s3RXsZo7.png --- # Flood Detection & Depth-Aware Risk Assessment An end-to-end deep learning pipeline for flood detection, explainability, depth estimation, and risk scoring from satellite/aerial imagery. --- ## Project Structure ``` ├── app.py ← Gradio UI (HF Spaces entry point) ├── app/ │ ├── __init__.py │ ├── main.py ← FastAPI REST backend │ └── model_utils.py ← Core engine (model, Grad-CAM, ZoeDepth, risk) ├── models/ │ ├── flood_best_model_export.keras ← Attention UNet weights │ └── flood_model_production.pkl ← Metadata (thresholds, metrics, layer names) ├── requirements.txt └── run_all.py ← Local launcher (FastAPI + Gradio together) ``` --- ## Full Pipeline ``` Input Image │ ▼ 1. Preprocessing └─ Convert to RGB → resize 512×512 → normalise [0,1] │ ▼ 2. Attention UNet (flood_best_model_export.keras) └─ Predicts per-pixel flood probability map (512×512) └─ OOD detection: colour check + spatial coherence + coverage cap └─ Threshold 0.5 → binary mask (1=flooded, 0=dry) │ ▼ 3. Grad-CAM (layer: conv2d_130) └─ GradientTape → gradients w.r.t. last conv feature maps └─ Weighted sum → activation map → JET colormap overlay │ ▼ 4. ZoeDepth (Intel/zoedepth-nyu, CPU) └─ Monocular depth estimation → relative depth map └─ Normalise to [0,3m] → stats within flooded pixels only │ ▼ 5. Risk Assessment └─ flood_pct + avg_depth_m → Low / Moderate / High / Critical └─ Score = flood_pct×0.7 + depth_score×0.3 ``` --- ## Deploy to Hugging Face Spaces ### Step 1 — Clone your Space ```bash git clone https://huggingface.co/spaces/YOUR_USERNAME/flood-risk-detection cd flood-risk-detection ``` ### Step 2 — Set up Git LFS (for large model files) ```bash git lfs install git lfs track "*.keras" "*.pkl" git add .gitattributes ``` ### Step 3 — Copy project files Copy these into the cloned folder: ``` app.py app/__init__.py app/model_utils.py models/flood_best_model_export.keras models/flood_model_production.pkl requirements.txt README.md ``` ### Step 4 — Add HF_TOKEN as a Secret In your Space → **Settings → Variables and Secrets → New Secret**: ``` Name: HF_TOKEN Value: your_huggingface_token ``` This is used by ZoeDepth to download model weights from the Hub. ### Step 5 — Push ```bash git add . git commit -m "Deploy flood detection app" git push ``` HF Spaces will automatically install `requirements.txt` and launch `app.py`. > **Note:** First inference is slow (~60s) — ZoeDepth downloads ~1.4GB weights on first run, then caches them. --- ## Run Locally ```bash # Install dependencies pip install -r requirements.txt # Set environment variables set HF_TOKEN=your_token # Windows CMD $env:HF_TOKEN="your_token" # PowerShell # Start both servers python run_all.py # OR individually: python app.py # Gradio → http://localhost:7860 uvicorn app.main:app --reload --port 8000 # FastAPI → http://localhost:8000/docs ``` --- ## API Reference ### `POST /predict` ```bash curl -X POST http://localhost:8000/predict \ -F "file=@flood_image.jpg" ``` Response: ```json { "mask_b64": "", "overlay_b64": "", "gradcam_b64": "", "depth_map_b64": "", "depth_flood_b64": "", "depth_info": { "avg_depth_m": 0.82, "max_depth_m": 1.45, "depth_category": "Moderate (30 cm – 80 cm)" }, "risk": { "risk_level": "High", "risk_score": 43.5, "flood_pct": 38.4, "avg_depth_m": 0.82, "recommendations": ["..."] } } ``` ### `GET /health` ```json {"status": "ok"} ``` --- ## Model Performance | Model | Pixel Accuracy | IoU (Jaccard) | Dice / F1 | Precision | Recall | |-------|---------------|---------------|-----------|-----------|--------| | UNet (baseline) | 82.55% | 63.94% | 78.00% | 78.75% | 77.26% | | **Attention UNet (deployed)** | **89.36%** | **76.91%** | **86.95%** | **85.41%** | **88.54%** | --- ## Viva Questions & Answers ### Architecture **Q: Why did you choose UNet for flood segmentation?** UNet is a fully convolutional encoder-decoder architecture with skip connections. The skip connections preserve spatial detail lost during downsampling — critical for pixel-accurate segmentation. The encoder captures semantic context (what is flooded) while the decoder reconstructs spatial resolution (where exactly). **Q: What is the difference between UNet and Attention UNet?** Attention UNet adds attention gates at each skip connection. These gates learn to suppress feature responses in irrelevant regions (dry land, buildings) and amplify responses in relevant regions (water, flooded areas). This is done by computing a soft attention coefficient per spatial location using the decoder signal as a query. Result: IoU improved from 63.94% to 76.91%. **Q: How does the attention gate work mathematically?** Given encoder feature `x` and decoder signal `g`: ``` attention = sigmoid(W_x(x) + W_g(g) + b) output = x * attention ``` The sigmoid produces values 0–1 per pixel. Multiplying by the encoder feature selectively passes only relevant spatial information to the decoder. **Q: Why 512×512 input size?** Balance between spatial resolution and memory. UNet memory scales quadratically with input size due to skip connections storing intermediate feature maps. 512×512 gives sufficient detail for flood boundary detection while fitting in GPU/CPU memory. **Q: What loss function did you use and why?** Combined BCE + Dice loss: - **Binary Cross-Entropy (BCE)**: pixel-level classification loss, handles each pixel independently - **Dice Loss**: `1 - (2×intersection)/(sum+sum)` — directly optimises the overlap metric, handles class imbalance (flooded pixels are often a minority in the image) - Combined: `L = BCE + Dice` — BCE provides stable gradients early in training, Dice refines the boundary **Q: What is IoU and how is it calculated?** Intersection over Union (Jaccard Index): ``` IoU = |Predicted ∩ Ground Truth| / |Predicted ∪ Ground Truth| ``` Ranges 0–1. Measures overlap between predicted and actual flood regions. More robust than accuracy for imbalanced segmentation tasks. --- ### Explainability **Q: What is Grad-CAM and why did you use it?** Gradient-weighted Class Activation Mapping. It computes the gradient of the output with respect to the feature maps of a target convolutional layer, then takes a weighted sum of those feature maps. The result is a spatial heatmap showing which image regions most influenced the prediction. Used because: (1) native to CNNs, (2) computationally cheap — one forward + one backward pass, (3) produces spatial maps that directly overlay on the image, (4) no model modification needed. **Q: Which layer did you target for Grad-CAM and why?** `conv2d_130` — the last convolutional layer before the output head. This layer has the best trade-off: it has learned high-level semantic features (water texture, flood patterns) while still retaining spatial resolution sufficient for meaningful visualisation. **Q: Why JET colormap for Grad-CAM?** JET (blue→cyan→green→yellow→red) is intuitive: red = high attention (model is confident about flooding here), blue = low attention. It matches the mental model of "heat" = importance. --- ### Depth Estimation **Q: What is ZoeDepth and how does it work?** ZoeDepth (Zero-shot Depth Estimation) is a monocular depth estimation model from Intel. It uses a DPT (Dense Prediction Transformer) backbone pretrained on large datasets, then fine-tuned for metric depth. It takes a single RGB image and outputs a per-pixel depth map in metres — no stereo cameras or LiDAR needed. **Q: Why use ZoeDepth instead of a simpler depth estimate?** Rule-based depth estimates (e.g., "flood coverage × constant") are not spatially aware. ZoeDepth provides actual per-pixel depth variation — a flood scene may have shallow water at the edges and deep water in the centre. This spatial depth information makes the risk assessment more accurate. **Q: ZoeDepth outputs relative depth — how do you convert to metres?** ZoeDepth's `Intel/zoedepth-nyu` model outputs relative disparity values. We normalise to [0,1] then map to [0, 3m] — a realistic maximum flood depth for the scenarios in our training data. The depth is only computed within flooded pixels (masked by the UNet output). --- ### Risk Assessment **Q: How is the risk level determined?** Two signals combined: 1. **Flood coverage %** — what fraction of the image is flooded 2. **Average depth (m)** — mean ZoeDepth value within flooded pixels ``` level = max(level_from_pct(flood_pct), level_from_depth(avg_depth_m)) score = flood_pct × 0.7 + depth_score × 0.3 ``` Thresholds: Low (<15%, <0.3m) → Moderate (15–35%, 0.3–0.8m) → High (35–60%, 0.8–1.5m) → Critical (>60%, >1.5m) **Q: Why does depth only contribute when flood_pct ≥ 2%?** Below 2% coverage, the flooded pixels are likely noise or false positives. Using depth on noise pixels would produce meaningless depth values that could incorrectly escalate the risk level. --- ### OOD Detection **Q: What is out-of-distribution (OOD) detection and why is it needed?** The model was trained only on flood/water imagery. When given a completely different image (car, building, portrait), the model produces garbage predictions — it has never seen these inputs during training. OOD detection identifies such inputs and suppresses the results rather than showing false flood detections. **Q: How does your OOD detection work?** Four signals: 1. **Brightness gate** — rejects pitch-black (<15) or overexposed (>240) images 2. **Spatial coherence** — real flood masks are large connected blobs; OOD produces >50 fragmented disconnected regions with the largest blob <25% of total flood pixels 3. **Coverage cap** — >90% flood coverage is physically implausible 4. **Colour-coverage cross-check** — if flood coverage >40% but <15% of pixels have water-like colours (blue hue 100–130°, grey low-saturation, brown 8–22°) → OOD **Q: Why not use a separate classifier for OOD?** A separate classifier would require additional training data and labels. Our heuristic approach is training-free, interpretable, and works well for the specific failure modes of this model (non-water textures being misclassified as flood). --- ### Deployment **Q: Why FastAPI for the backend?** FastAPI is async, type-safe (Pydantic models), auto-generates OpenAPI docs, and has excellent performance. The Pydantic response models also prevent schema issues with Gradio's API introspection. **Q: Why Gradio for the frontend?** Gradio is purpose-built for ML demos, integrates directly with Python functions (no JavaScript needed), and deploys natively to Hugging Face Spaces with zero configuration. **Q: What is the PKL file and what does it store?** The `.pkl` file stores metadata only — not the model weights. Contents: threshold (0.5), Grad-CAM target layer name (conv2d_130), risk thresholds, model performance metrics, and custom object keys. The actual weights are in the `.keras` file. **Q: How would you scale this to production?** - Replace CPU inference with GPU (CUDA) for 10–50× speedup - Add a message queue (Redis/Celery) for async processing of multiple requests - Cache ZoeDepth results for similar images - Add authentication to the API - Deploy on Kubernetes with horizontal scaling - Add monitoring (Prometheus/Grafana) for inference latency and error rates --- ## Dependencies | Package | Purpose | |---------|---------| | `tensorflow-cpu` | Attention UNet inference | | `torch` | ZoeDepth (PyTorch model) | | `transformers` | ZoeDepth pipeline from HuggingFace | | `opencv-python-headless` | Image processing, Grad-CAM, colormaps | | `gradio` | Frontend UI | | `fastapi` + `uvicorn` | REST API backend | | `Pillow` | Image loading/conversion | | `numpy` | Array operations |