AbdullahImran commited on
Commit
2530e3b
·
verified ·
1 Parent(s): 74c1cfd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -136
app.py CHANGED
@@ -18,8 +18,8 @@ API_URL = (
18
  "https://archive-api.open-meteo.com/v1/archive"
19
  "?latitude={lat}&longitude={lon}"
20
  "&start_date={start}&end_date={end}"
21
- "&daily=temperature_2m_max,temperature_2m_min,"
22
- "precipitation_sum,windspeed_10m_max,"
23
  "relative_humidity_2m_max,relative_humidity_2m_min"
24
  "&timezone=UTC"
25
  )
@@ -34,10 +34,9 @@ def load_models():
34
  def focal_loss_fixed(gamma=2., alpha=.25):
35
  import tensorflow.keras.backend as K
36
  def loss_fn(y_true, y_pred):
37
- eps = K.epsilon()
38
- y_pred = K.clip(y_pred, eps, 1. - eps)
39
  ce = -y_true * K.log(y_pred)
40
- w = alpha * K.pow(1 - y_pred, gamma)
41
  return K.mean(w * ce, axis=-1)
42
  return loss_fn
43
  xce_model = load_model(
@@ -61,27 +60,9 @@ task_rules = {
61
  'severe': {'decrease':'moderate','same':'severe','increase':'severe'}
62
  }
63
  recommendations = {
64
- 'mild': {
65
- 'immediate': "Deploy spot crews for initial attack. Establish command post. Monitor fire behavior with drones or aircraft. Alert local fire stations.",
66
- 'evacuation': "No mass evacuation needed. Notify nearby communities of potential risk. Prepare evacuation routes if conditions change.",
67
- 'containment': "Establish initial fire lines. Use hand crews for direct attack. Position water resources. Clear fuel breaks where feasible.",
68
- 'prevention': "Implement controlled underburning in surrounding areas. Manage vegetation density. Create defensible spaces around structures.",
69
- 'education': "Inform public on fire watch protocols and reporting mechanisms. Train local volunteers in basic firefighting techniques."
70
- },
71
- 'moderate': {
72
- 'immediate': "Dispatch multiple engines and aerial support. Establish unified command system. Deploy heavy equipment. Request additional resources.",
73
- 'evacuation': "Prepare evacuation zones and staging areas. Advise voluntary evacuation for vulnerable populations. Alert emergency shelters.",
74
- 'containment': "Build substantial fire breaks. Conduct water drops from helicopters. Implement indirect attack strategies. Protect critical infrastructure.",
75
- 'prevention': "Initiate fuel reduction programs in adjacent areas. Create wider buffer zones. Assess watershed protection needs.",
76
- 'education': "Conduct community emergency drills. Launch awareness campaigns on evacuation procedures. Distribute preparedness materials."
77
- },
78
- 'severe': {
79
- 'immediate': "Implement full suppression with air tankers and multiple resources. Establish incident management team. Request state/federal assistance. Deploy specialized teams.",
80
- 'evacuation': "Issue mandatory evacuation orders. Open multiple emergency shelters. Implement traffic control measures. Assist vulnerable populations.",
81
- 'containment': "Deploy fire retardant lines from aircraft. Consider backfires and burnout operations. Protect critical infrastructure. Establish multiple control lines.",
82
- 'prevention': "Plan for reforestation and erosion control. Harden infrastructure against future fires. Implement watershed protection measures.",
83
- 'education': "Conduct comprehensive emergency response training. Implement risk communication strategies. Develop long-term community resilience programs."
84
- }
85
  }
86
 
87
  # --- PIPELINE FUNCTIONS ---
@@ -93,23 +74,21 @@ def detect_fire(img):
93
  x = vgg_preprocess(x)
94
  prob = float(vgg_model.predict(x)[0][0])
95
  return prob >= 0.5, prob
96
- except Exception as e:
97
- print(f"Error in fire detection: {e}")
98
  return False, 0.0
99
 
100
  def classify_severity(img):
101
  try:
102
- if xce_model is None or rf_model is None or xgb_model is None:
103
  return 'moderate'
104
  x = keras_image.img_to_array(img.resize((224,224)))[None]
105
  x = xce_preprocess(x)
106
- preds = xce_model.predict(x)
107
  rf_p = rf_model.predict(preds)[0]
108
  xgb_p = xgb_model.predict(preds)[0]
109
- ensemble = int(round((rf_p + xgb_p) / 2))
110
- return target_map.get(ensemble, 'moderate')
111
- except Exception as e:
112
- print(f"Error in severity classification: {e}")
113
  return 'moderate'
114
 
115
  def fetch_weather_trend(lat, lon):
@@ -119,133 +98,80 @@ def fetch_weather_trend(lat, lon):
119
  url = API_URL.format(lat=lat, lon=lon,
120
  start=start.strftime('%Y-%m-%d'),
121
  end=end.strftime('%Y-%m-%d'))
122
- response = requests.get(url, timeout=5)
123
- response.raise_for_status()
124
- df = pd.DataFrame(response.json().get('daily', {}))
125
- except Exception:
126
- # Fallback sample data
127
- df = pd.DataFrame({
128
- 'date': [(datetime.utcnow() - timedelta(days=i)).strftime('%Y-%m-%d') for i in range(1,-1,-1)],
129
- 'precipitation_sum': [5, 2],
130
- 'temperature_2m_max': [28, 30],
131
- 'temperature_2m_min': [18, 20],
132
- 'relative_humidity_2m_max': [70, 65],
133
- 'relative_humidity_2m_min': [40, 35],
134
- 'windspeed_10m_max': [15, 18]
135
- })
136
- # Numeric conversions
137
- for c in ['precipitation_sum','temperature_2m_max','temperature_2m_min',
138
- 'relative_humidity_2m_max','relative_humidity_2m_min','windspeed_10m_max']:
139
- df[c] = pd.to_numeric(df[c], errors='coerce')
140
- # Feature engineering
141
- df['precipitation'] = df['precipitation_sum']
142
- df['temperature'] = (df['temperature_2m_max'] + df['temperature_2m_min']) / 2
143
- df['humidity'] = (df['relative_humidity_2m_max'] + df['relative_humidity_2m_min']) / 2
144
  df['wind_speed'] = df['windspeed_10m_max']
145
- df['fire_risk_score'] = (
146
- 0.4 * (df['temperature'] / 55) +
147
- 0.2 * (1 - df['humidity'] / 100) +
148
- 0.3 * (df['wind_speed'] / 60) +
149
- 0.1 * (1 - df['precipitation'] / 50)
150
- )
151
  feat = df[['temperature','humidity','wind_speed','precipitation','fire_risk_score']].iloc[-1].values.reshape(1,-1)
152
  if lr_model is not None:
153
- trend_cl = lr_model.predict(feat)[0]
154
- return trend_map.get(trend_cl, 'same')
155
  return 'same'
156
 
157
- def generate_recommendations(original_severity, weather_trend):
158
- projected = task_rules[original_severity][weather_trend]
159
- rec = recommendations[projected]
160
- return (f"**Original Severity:** {original_severity.title()} \
161
- " \
162
- f"**Weather Trend:** {weather_trend.title()} \
163
- " \
164
- f"**Projected Severity:** {projected.title()}\n\n" \
165
- "### Management Recommendations:\n" \
166
- f"**Immediate:** {rec['immediate']}\n\n" \
167
- f"**Evacuation:** {rec['evacuation']}\n\n" \
168
- f"**Containment:** {rec['containment']}\n\n" \
169
- f"**Prevention:** {rec['prevention']}\n\n" \
170
- f"**Education:** {rec['education']}")
171
 
172
  # --- MAIN PIPELINE ---
173
  def pipeline(image):
174
  if image is None:
175
- return "No image provided","N/A","N/A","**Please upload an image to analyze**"
176
  img = Image.fromarray(image).convert('RGB')
177
  fire, prob = detect_fire(img)
178
  if not fire:
179
- return (
180
- f"No wildfire detected (confidence: {(1-prob)*100:.1f}%)",
181
- "N/A","N/A",
182
- "**No wildfire detected. Stay alert.**"
183
- )
184
  sev = classify_severity(img)
185
  trend = fetch_weather_trend(*FOREST_COORDS['Pakistan Forest'])
186
  recs = generate_recommendations(sev, trend)
187
- return (
188
- f"**Wildfire detected** (confidence: {prob*100:.1f}%)",
189
- f"**{sev.title()}**",
190
- f"**{trend.title()}**",
191
- recs
192
- )
193
 
194
- # --- GLOBAL MODEL LOADING ---
195
- vgg_model, xce_model, rf_model, xgb_model, lr_model = load_models()
196
 
197
- # --- UI: CUSTOM CSS & GRADIO LAYOUT ---
198
- custom_css = '''
199
- #header { text-align: center; margin: 0 0 1rem; }
200
- #header img { height: 4rem; margin-right: 1rem; }
201
- #main-title { font-size: 2.75rem; margin: 0.5rem 0; }
202
- #sub-title { font-size: 1.25rem; color: #555; }
203
- .gr-button.primary { background: #ff7043 !important; }
204
- .output-card { background: #f7f7f7; border-radius: 0.75rem; padding: 1rem;
205
- box-shadow: 0 1px 6px rgba(0,0,0,0.1); margin-bottom: 1rem; }
206
- '''
 
 
207
 
208
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
209
- # Header (add your logo.png in working directory or adjust path)
210
- with gr.Row(elem_id="header"):
211
- try:
212
- gr.Image(value="logo.png", show_label=False)
213
- except:
214
- pass
215
- with gr.Column():
216
  gr.Markdown("# 🔥 Wildfire Command Center", elem_id="main-title")
217
- gr.Markdown("Upload a forest image to detect wildfire, classify severity, and get actionable recommendations.", elem_id="sub-title")
218
-
219
- # Tabs: Analyze & Last Analysis
220
- with gr.Tabs():
221
- with gr.TabItem("Analyze 🔍"):
222
- with gr.Row():
223
- with gr.Column(scale=1):
224
- image_input = gr.Image(type="numpy", label="Forest Image", tool="editor")
225
- run_btn = gr.Button("Analyze Now", variant="primary")
226
- with gr.Column(scale=1):
227
- with gr.Spinner():
228
- status_out = gr.Markdown("*Status will appear here*", label="Status")
229
- severity_out = gr.Markdown("---", label="Severity")
230
- trend_out = gr.Markdown("---", label="Weather Trend")
231
- recs_out = gr.Markdown("---", label="Recommendations")
232
-
233
- with gr.TabItem("Last Analysis 📊"):
234
- last_status = gr.Markdown("*No analysis yet*", elem_classes="output-card")
235
- last_severity = gr.Markdown("---", elem_classes="output-card")
236
- last_trend = gr.Markdown("---", elem_classes="output-card")
237
- last_recs = gr.Markdown("---", elem_classes="output-card")
238
 
239
- # Bind actions: analyze then archive outputs
240
  run_btn.click(
241
  fn=pipeline,
242
  inputs=image_input,
243
- outputs=[status_out, severity_out, trend_out, recs_out]
244
- ).then(
245
- fn=lambda s,sv,tr,rc: (s,sv,tr,rc),
246
- inputs=[status_out, severity_out, trend_out, recs_out],
247
  outputs=[last_status, last_severity, last_trend, last_recs]
248
  )
249
 
250
  if __name__ == '__main__':
251
- demo.queue(api_open=True).launch()
 
18
  "https://archive-api.open-meteo.com/v1/archive"
19
  "?latitude={lat}&longitude={lon}"
20
  "&start_date={start}&end_date={end}"
21
+ "&daily=temperature_2m_max,temperature_2m_min,"
22
+ "precipitation_sum,windspeed_10m_max,"
23
  "relative_humidity_2m_max,relative_humidity_2m_min"
24
  "&timezone=UTC"
25
  )
 
34
  def focal_loss_fixed(gamma=2., alpha=.25):
35
  import tensorflow.keras.backend as K
36
  def loss_fn(y_true, y_pred):
37
+ eps = K.epsilon(); y_pred = K.clip(y_pred, eps, 1.-eps)
 
38
  ce = -y_true * K.log(y_pred)
39
+ w = alpha * K.pow(1-y_pred, gamma)
40
  return K.mean(w * ce, axis=-1)
41
  return loss_fn
42
  xce_model = load_model(
 
60
  'severe': {'decrease':'moderate','same':'severe','increase':'severe'}
61
  }
62
  recommendations = {
63
+ 'mild': {...},
64
+ 'moderate': {...},
65
+ 'severe': {...}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
 
68
  # --- PIPELINE FUNCTIONS ---
 
74
  x = vgg_preprocess(x)
75
  prob = float(vgg_model.predict(x)[0][0])
76
  return prob >= 0.5, prob
77
+ except:
 
78
  return False, 0.0
79
 
80
  def classify_severity(img):
81
  try:
82
+ if xception_model is None:
83
  return 'moderate'
84
  x = keras_image.img_to_array(img.resize((224,224)))[None]
85
  x = xce_preprocess(x)
86
+ preds = xception_model.predict(x)
87
  rf_p = rf_model.predict(preds)[0]
88
  xgb_p = xgb_model.predict(preds)[0]
89
+ ensemble = int(round((rf_p + xgb_p)/2))
90
+ return target_map.get(ensemble,'moderate')
91
+ except:
 
92
  return 'moderate'
93
 
94
  def fetch_weather_trend(lat, lon):
 
98
  url = API_URL.format(lat=lat, lon=lon,
99
  start=start.strftime('%Y-%m-%d'),
100
  end=end.strftime('%Y-%m-%d'))
101
+ resp = requests.get(url, timeout=5)
102
+ if resp.status_code==200:
103
+ df = pd.DataFrame(resp.json().get('daily', {}))
104
+ else:
105
+ raise Exception()
106
+ except:
107
+ df = pd.DataFrame({ 'date': ['2025-04-25','2025-04-26'], 'precipitation_sum':[5,2], 'temperature_2m_max':[28,30], 'temperature_2m_min':[18,20], 'relative_humidity_2m_max':[70,65], 'relative_humidity_2m_min':[40,35], 'windspeed_10m_max':[15,18] })
108
+ df['temperature'] = (df['temperature_2m_max'] + df['temperature_2m_min'])/2
109
+ df['humidity'] = (df['relative_humidity_2m_max'] + df['relative_humidity_2m_min'])/2
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  df['wind_speed'] = df['windspeed_10m_max']
111
+ df['precipitation'] = df['precipitation_sum']
112
+ df['fire_risk_score'] = (0.4*(df['temperature']/55) + 0.2*(1-df['humidity']/100) + 0.3*(df['wind_speed']/60) + 0.1*(1-df['precipitation']/50))
 
 
 
 
113
  feat = df[['temperature','humidity','wind_speed','precipitation','fire_risk_score']].iloc[-1].values.reshape(1,-1)
114
  if lr_model is not None:
115
+ cl = lr_model.predict(feat)[0]
116
+ return trend_map.get(cl,'same')
117
  return 'same'
118
 
119
+ def generate_recommendations(orig, trend):
120
+ proj = task_rules[orig][trend]
121
+ rec = recommendations[proj]
122
+ return f"**Original Severity:** {orig.title()} \n**Weather Trend:** {trend.title()} \n**Projected Severity:** {proj.title()}\n\n### Management Recommendations:\n**Immediate:** {rec['immediate']}\n\n**Evacuation:** {rec['evacuation']}\n\n**Containment:** {rec['containment']}\n\n**Prevention:** {rec['prevention']}\n\n**Education:** {rec['education']}"
 
 
 
 
 
 
 
 
 
 
123
 
124
  # --- MAIN PIPELINE ---
125
  def pipeline(image):
126
  if image is None:
127
+ return "No image provided","N/A","N/A","**Upload image**"
128
  img = Image.fromarray(image).convert('RGB')
129
  fire, prob = detect_fire(img)
130
  if not fire:
131
+ return f"No wildfire detected ({(1-prob)*100:.1f}% sure)","N/A","N/A","**No wildfire.**"
 
 
 
 
132
  sev = classify_severity(img)
133
  trend = fetch_weather_trend(*FOREST_COORDS['Pakistan Forest'])
134
  recs = generate_recommendations(sev, trend)
135
+ return f"Wildfire detected ({prob*100:.1f}%)", sev.title(), trend.title(), recs
 
 
 
 
 
136
 
137
+ # --- LOAD MODELS ---
138
+ vgg_model, xception_model, rf_model, xgb_model, lr_model = load_models()
139
 
140
+ # --- UI STYLING & LAYOUT ---
141
+ custom_css = """
142
+ .sidebar { background: #2e3440; color: #eceff4; padding: 1rem; border-radius: 1rem; }
143
+ #main-title { font-size: 2.5rem; color: #3b4252; }
144
+ #sub-title { font-size: 1.125rem; color: #4c566a; }
145
+ .card { background: #eceff4; color: #2e3440; border-radius: 0.75rem; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
146
+ .gr-button { background: #5e81ac !important; color: white !important; border-radius: 0.5rem; }
147
+ .status-badge { padding: 0.25em 0.75em; border-radius: 9999px; font-weight: 600; }
148
+ .status-fire { background: #bf616a; color: white; }
149
+ .status-no-fire { background: #a3be8c; color: white; }
150
+ .gr-markdown { color: #2e3440; }
151
+ """
152
 
153
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo:
154
+ with gr.Row():
155
+ with gr.Column(scale=2):
 
 
 
 
 
156
  gr.Markdown("# 🔥 Wildfire Command Center", elem_id="main-title")
157
+ gr.Markdown(
158
+ "Upload a **forest image** to detect wildfire, classify severity, fetch weather trend, and get recommendations.",
159
+ elem_id="sub-title"
160
+ )
161
+ image_input = gr.Image(type="numpy", label="Upload Forest Image")
162
+ run_btn = gr.Button("Analyze Now", variant="primary")
163
+ with gr.Column(scale=1, elem_classes="sidebar"):
164
+ gr.Markdown("## 📊 Last Analysis")
165
+ last_status = gr.Markdown("*No analysis yet*")
166
+ last_severity = gr.Markdown("---")
167
+ last_trend = gr.Markdown("---")
168
+ last_recs = gr.Markdown("---")
 
 
 
 
 
 
 
 
 
169
 
 
170
  run_btn.click(
171
  fn=pipeline,
172
  inputs=image_input,
 
 
 
 
173
  outputs=[last_status, last_severity, last_trend, last_recs]
174
  )
175
 
176
  if __name__ == '__main__':
177
+ demo.queue(api_open=True).launch()