Spaces:
Running
Running
import os | |
import requests | |
import pandas as pd | |
import numpy as np | |
import joblib | |
import gradio as gr | |
from datetime import datetime, timedelta | |
from tensorflow.keras.models import load_model | |
from tensorflow.keras.preprocessing import image as keras_image | |
from tensorflow.keras.applications.vgg16 import preprocess_input as vgg_preprocess | |
from tensorflow.keras.applications.xception import preprocess_input as xce_preprocess | |
from tensorflow.keras.losses import BinaryFocalCrossentropy | |
from PIL import Image | |
# --- CONFIGURATION --- | |
FOREST_COORDS = {'Pakistan Forest': (34.0, 73.0)} | |
API_URL = ( | |
"https://archive-api.open-meteo.com/v1/archive" | |
"?latitude={lat}&longitude={lon}" | |
"&start_date={start}&end_date={end}" | |
"&daily=temperature_2m_max,temperature_2m_min," | |
"precipitation_sum,windspeed_10m_max," | |
"relative_humidity_2m_max,relative_humidity_2m_min" | |
"&timezone=UTC" | |
) | |
# --- LOAD MODELS --- | |
def load_models(): | |
vgg_model = load_model( | |
'vgg16_focal_unfreeze_more.keras', | |
custom_objects={'BinaryFocalCrossentropy': BinaryFocalCrossentropy} | |
) | |
def focal_loss_fixed(gamma=2., alpha=.25): | |
import tensorflow.keras.backend as K | |
def loss_fn(y_true, y_pred): | |
eps = K.epsilon(); y_pred = K.clip(y_pred, eps, 1.-eps) | |
ce = -y_true * K.log(y_pred) | |
w = alpha * K.pow(1-y_pred, gamma) | |
return K.mean(w * ce, axis=-1) | |
return loss_fn | |
xce_model = load_model( | |
'severity_post_tta.keras', | |
custom_objects={'focal_loss_fixed': focal_loss_fixed()} | |
) | |
rf_model = joblib.load('ensemble_rf_model.pkl') | |
xgb_model = joblib.load('ensemble_xgb_model.pkl') | |
lr_model = joblib.load('wildfire_logistic_model_synthetic.joblib') | |
return vgg_model, xce_model, rf_model, xgb_model, lr_model | |
vgg_model, xception_model, rf_model, xgb_model, lr_model = load_models() | |
# --- RULES & TEMPLATES (expanded!) --- | |
target_map = {0: 'mild', 1: 'moderate', 2: 'severe'} | |
trend_map = {1: 'increase', 0: 'same', -1: 'decrease'} | |
task_rules = { | |
'mild': {'decrease':'mild','same':'mild','increase':'moderate'}, | |
'moderate':{'decrease':'mild','same':'moderate','increase':'severe'}, | |
'severe': {'decrease':'moderate','same':'severe','increase':'severe'} | |
} | |
templates = { | |
'mild': ( | |
"📌 **Immediate Monitoring:** Although fire intensity is low, assign lookouts to monitor hotspots every 30 minutes. Use handheld IR cameras to detect any hidden flare-ups.\n\n" | |
"📌 **Community Alert:** Send SMS alerts to nearby villages reminding them to stay vigilant. Provide clear instructions on how to report any smoke sightings.\n\n" | |
"📌 **Fuel Management:** Conduct targeted removal of leaf litter and dry underbrush within a 100 m radius to reduce the chance of flare-ups.\n\n" | |
"📌 **Preparedness Drills:** Hold a quick drill with ground crews to review communication protocols and ensure equipment (hoses, pumps) is ready.\n\n" | |
"📌 **Public Education:** Distribute flyers on safe fire-watch practices and set up a hotline for rapid reporting." | |
), | |
'moderate': ( | |
"🚒 **Rapid Response:** Dispatch two engine crews and one aerial water-drop helicopter. Coordinate with the regional command center to stage retardant tanks nearby.\n\n" | |
"🏃♂️ **Evacuation Prep:** Pre-position evacuation buses at community centers. Issue voluntary evacuation notices to residents within 5 km downwind.\n\n" | |
"🛠 **Containment Lines:** Construct a 10 m fire break using both hand tools and bulldozers. Apply fire-retardant gel along the anticipated flank.\n\n" | |
"🌱 **Fuel Reduction:** Begin mechanical thinning of small trees and brush in high-risk zones adjacent to critical infrastructure.\n\n" | |
"📣 **Awareness Campaign:** Launch radio spots explaining what to do if fire approaches, including evacuation routes and shelter locations." | |
), | |
'severe': ( | |
"✈️ **Full Suppression:** Mobilize two air tankers for retardant drops and four ground crews with heavy equipment. Integrate real-time satellite imagery for targeting.\n\n" | |
"🚨 **Mandatory Evacuation:** Issue immediate evacuation orders for all residents within a 10 km radius. Open three emergency shelters with medical staff on standby.\n\n" | |
"🔥 **Backfire Operations:** Conduct controlled backfires under supervision of senior incident commanders to remove fuel ahead of the main front.\n\n" | |
"🌳 **Post-Fire Rehabilitation:** Plan reforestation with fire-resistant native species; stabilize soil to prevent erosion in burn scar areas.\n\n" | |
"🗣 **Crisis Communication:** Hold daily press briefings and social media updates. Provide mental-health support hotlines for displaced families." | |
) | |
} | |
# --- RECOMMENDATION GENERATOR --- | |
def generate_recommendations(original, trend): | |
projected = task_rules[original][trend] | |
header = ( | |
f"## 🔥 Wildfire Situation Update\n" | |
f"- **Original Severity:** {original.title()}\n" | |
f"- **Weather Trend:** {trend.title()}\n" | |
f"- **Projected Severity:** {projected.title()}\n\n" | |
) | |
# build bullet paragraphs | |
paras = templates[projected].split("\n\n") | |
formatted = "\n\n".join(paras) | |
return header + formatted | |
# --- PIPELINE --- | |
def pipeline(image): | |
img = Image.fromarray(image).convert('RGB') | |
fire, prob = detect_fire(img) | |
if not fire: | |
return ( | |
f"**No wildfire detected** (probability={prob:.2f})", | |
"N/A", | |
"N/A", | |
"There is currently no sign of wildfire in the image. Continue normal monitoring." | |
) | |
sev = classify_severity(img) | |
trend = fetch_weather_trend(*FOREST_COORDS['Pakistan Forest']) | |
recs = generate_recommendations(sev, trend) | |
return ( | |
f"**🔥 Fire Detected** (probability={prob:.2f})", | |
sev.title(), | |
trend.title(), | |
recs | |
) | |
# --- GRADIO BLOCKS UI --- | |
withwith gr.Blocks(css=""" | |
/* background for entire app */ | |
.gradio-container { | |
background-color: #f5f7fa !important; | |
} | |
/* style each of the three Textbox outputs */ | |
.gradio-textbox textarea { | |
background-color: #ffffff !important; | |
border: 1px solid #cbd2d9 !important; | |
border-radius: 8px !important; | |
padding: 12px !important; | |
font-size: 1rem !important; | |
color: #333333 !important; | |
min-height: 3em !important; | |
} | |
/* style the Accordion panel */ | |
.gradio-accordion { | |
background-color: #ffffff !important; | |
border: 1px solid #cbd2d9 !important; | |
border-radius: 8px !important; | |
padding: 8px !important; | |
} | |
/* style the Analyze button */ | |
.gradio-button { | |
background-color: #0072ce !important; | |
color: white !important; | |
border-radius: 6px !important; | |
padding: 8px 16px !important; | |
font-weight: 600 !important; | |
} | |
.gradio-button:hover { | |
background-color: #005bb5 !important; | |
} | |
/* section headers */ | |
.gradio-markdown h1, .gradio-markdown h2 { | |
color: #1f2937 !important; | |
margin-bottom: 0.5em !important; | |
} | |
""") as demo: | |
gr.Markdown("# Wildfire Detection & Management Assistant") | |
gr.Markdown("Upload a forest image from Pakistan; the system will detect fire, assess severity, analyze weather trends, and provide in-depth recommendations.") | |
with gr.Row(): | |
inp = gr.Image(type="numpy", label="Upload Wildfire Image") | |
with gr.Column(): | |
status = gr.Textbox(label="Fire Status", interactive=False) | |
severity = gr.Textbox(label="Severity Level", interactive=False) | |
trend = gr.Textbox(label="Weather Trend", interactive=False) | |
with gr.Accordion("📋 Detailed Recommendations", open=False): | |
rec_box = gr.Markdown(label="Recommendations") | |
btn = gr.Button("Analyze") | |
btn.click(fn=pipeline, inputs=inp, outputs=[status, severity, trend, rec_box]) | |
gr.HTML("<p style='font-size:0.8em; color:#666;'>© 2025 ForestAI Labs</p>") | |
if __name__ == "__main__": | |
demo.launch() |