File size: 12,059 Bytes
04fa07a
 
 
2d71661
d4e30d8
04fa07a
 
 
 
 
 
 
8c9a116
04fa07a
 
 
 
 
 
 
 
 
 
 
2d71661
 
04fa07a
 
bb48649
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04fa07a
b7608ef
bb48649
04fa07a
 
bb48649
 
b7608ef
 
 
 
 
bb48649
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7608ef
04fa07a
 
 
bb48649
 
 
 
 
 
 
 
 
 
 
 
b7608ef
04fa07a
bb48649
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7608ef
04fa07a
bb48649
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04fa07a
b7608ef
bb48649
 
 
 
 
 
 
 
 
 
 
6128586
bb48649
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04fa07a
bb48649
 
 
 
 
04fa07a
bb48649
 
04fa07a
 
bb48649
 
 
 
 
 
04fa07a
bb48649
 
 
 
 
 
 
 
2d71661
bb48649
2d71661
04fa07a
 
2d71661
04fa07a
bb48649
f1ea272
bb48649
2d71661
04fa07a
bb48649
 
 
 
de67fba
2d71661
04fa07a
bb48649
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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():
    try:
        # Fire detector (VGG16)
        vgg_model = load_model(
            'vgg16_focal_unfreeze_more.keras',
            custom_objects={'BinaryFocalCrossentropy': BinaryFocalCrossentropy}
        )
        # Severity classifier (Xception)
        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()}
        )
        # Ensemble and trend models
        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
    except Exception as e:
        print(f"Error loading models: {e}")
        return None, None, None, None, None

# --- RULES & TEMPLATES ---
# Mapping severity levels and trends
target_map = {0: 'mild', 1: 'moderate', 2: 'severe'}
trend_map = {1: 'increase', 0: 'same', -1: 'decrease'}

# Severity progression rules based on current severity and weather trend
task_rules = {
    'mild':    {'decrease':'mild','same':'mild','increase':'moderate'},
    'moderate':{'decrease':'mild','same':'moderate','increase':'severe'},
    'severe':  {'decrease':'moderate','same':'severe','increase':'severe'}
}

# Detailed recommendations for each severity level
recommendations = {
    'mild': {
        'immediate': "Deploy spot crews for initial attack. Establish command post. Monitor fire behavior with drones or aircraft. Alert local fire stations.",
        'evacuation': "No mass evacuation needed. Notify nearby communities of potential risk. Prepare evacuation routes if conditions change.",
        'containment': "Establish initial fire lines. Use hand crews for direct attack. Position water resources. Clear fuel breaks where feasible.",
        'prevention': "Implement controlled underburning in surrounding areas. Manage vegetation density. Create defensible spaces around structures.",
        'education': "Inform public on fire watch protocols and reporting mechanisms. Train local volunteers in basic firefighting techniques."
    },
    'moderate': {
        'immediate': "Dispatch multiple engines and aerial support. Establish unified command system. Deploy heavy equipment. Request additional resources.",
        'evacuation': "Prepare evacuation zones and staging areas. Advise voluntary evacuation for vulnerable populations. Alert emergency shelters.",
        'containment': "Build substantial fire breaks. Conduct water drops from helicopters. Implement indirect attack strategies. Protect critical infrastructure.",
        'prevention': "Initiate fuel reduction programs in adjacent areas. Create wider buffer zones. Assess watershed protection needs.",
        'education': "Conduct community emergency drills. Launch awareness campaigns on evacuation procedures. Distribute preparedness materials."
    },
    'severe': {
        'immediate': "Implement full suppression with air tankers and multiple resources. Establish incident management team. Request state/federal assistance. Deploy specialized teams.",
        'evacuation': "Issue mandatory evacuation orders. Open multiple emergency shelters. Implement traffic control measures. Assist vulnerable populations.",
        'containment': "Deploy fire retardant lines from aircraft. Consider backfires and burnout operations. Protect critical infrastructure. Establish multiple control lines.",
        'prevention': "Plan for reforestation and erosion control. Harden infrastructure against future fires. Implement watershed protection measures.",
        'education': "Conduct comprehensive emergency response training. Implement risk communication strategies. Develop long-term community resilience programs."
    }
}

# --- PIPELINE FUNCTIONS ---
def detect_fire(img):
    """Detect if a wildfire is present in the image"""
    try:
        if vgg_model is None:
            return True, 0.85  # Fallback if model not loaded
            
        x = keras_image.img_to_array(img.resize((128,128)))[None]
        x = vgg_preprocess(x)
        prob = float(vgg_model.predict(x)[0][0])
        return prob >= 0.5, prob
    except Exception as e:
        print(f"Error in fire detection: {e}")
        return False, 0.0

def classify_severity(img):
    """Classify the severity of the detected wildfire"""
    try:
        if xception_model is None or rf_model is None or xgb_model is None:
            return 'moderate'  # Fallback if models not loaded
            
        x = keras_image.img_to_array(img.resize((224,224)))[None]
        x = xce_preprocess(x)
        preds = xception_model.predict(x)
        rf_p = rf_model.predict(preds)[0]
        xgb_p = xgb_model.predict(preds)[0]
        ensemble = int(round((rf_p + xgb_p)/2))
        return target_map.get(ensemble, 'moderate')
    except Exception as e:
        print(f"Error in severity classification: {e}")
        return 'moderate'  # Default to moderate severity if error occurs

def fetch_weather_trend(lat, lon):
    """Fetch weather data and determine trend"""
    try:
        # Use local weather calculation if API fails
        try:
            end = datetime.utcnow()
            start = end - timedelta(days=1)
            url = API_URL.format(
                lat=lat, lon=lon,
                start=start.strftime('%Y-%m-%d'),
                end=end.strftime('%Y-%m-%d')
            )
            response = requests.get(url, timeout=5)
            if response.status_code != 200:
                raise Exception(f"API returned status code {response.status_code}")
            
            df = pd.DataFrame(response.json().get('daily', {}))
        except Exception as e:
            print(f"API error: {e}. Using synthetic data.")
            # Create synthetic weather data if API fails
            df = pd.DataFrame({
                'date': [(datetime.utcnow() - timedelta(days=i)).strftime('%Y-%m-%d') for i in range(1, -1, -1)],
                '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]
            })
        
        # Process weather data
        for c in ['precipitation_sum','temperature_2m_max','temperature_2m_min',
                'relative_humidity_2m_max','relative_humidity_2m_min','windspeed_10m_max']:
            df[c] = pd.to_numeric(df.get(c,[]), errors='coerce')
        
        df['precipitation'] = df['precipitation_sum'].fillna(0)
        df['temperature'] = (df['temperature_2m_max'] + df['temperature_2m_min'])/2
        df['humidity'] = (df['relative_humidity_2m_max'] + df['relative_humidity_2m_min'])/2
        df['wind_speed'] = df['windspeed_10m_max']
        
        # Calculate fire risk score based on weather parameters
        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)
        )
        
        # Prepare features for trend prediction
        feats = df[['temperature','humidity','wind_speed','precipitation','fire_risk_score']]
        feat = feats.fillna(feats.mean()).iloc[-1].values.reshape(1,-1)
        
        # Predict trend using logistic regression model or fallback
        if lr_model is not None:
            trend_cl = lr_model.predict(feat)[0]
            return trend_map.get(trend_cl, 'same')
        else:
            # Fallback logic if model isn't loaded
            if df['fire_risk_score'].iloc[-1] > 0.6:
                return 'increase'
            elif df['fire_risk_score'].iloc[-1] < 0.4:
                return 'decrease'
            return 'same'
            
    except Exception as e:
        print(f"Error in weather trend analysis: {e}")
        return 'same'  # Default to 'same' trend if all else fails

def generate_recommendations(original_severity, weather_trend):
    """Generate comprehensive recommendations based on severity and trend"""
    # Determine projected severity
    projected_severity = task_rules[original_severity][weather_trend]
    
    # Get recommendations for projected severity
    rec = recommendations[projected_severity]
    
    # Create detailed recommendation text
    recommendation_text = f"""**Original Severity:** {original_severity.title()}  
**Weather Trend:** {weather_trend.title()}  
**Projected Severity:** {projected_severity.title()}

### Management Recommendations:

**1. Immediate Actions:**  
{rec['immediate']}

**2. Evacuation Guidelines:**  
{rec['evacuation']}

**3. Short-term Containment:**  
{rec['containment']}

**4. Long-term Prevention & Recovery:**  
{rec['prevention']}

**5. Community Education:**  
{rec['education']}
"""
    return recommendation_text

# --- MAIN PIPELINE ---
def pipeline(image):
    """Main processing pipeline for wildfire detection and analysis"""
    if image is None:
        return "No image provided", "N/A", "N/A", "**Please upload an image to analyze**"
    
    # Convert to PIL Image
    img = Image.fromarray(image).convert('RGB')
    
    # Step 1: Detect fire
    fire, prob = detect_fire(img)
    if not fire:
        return f"No wildfire detected (confidence: {(1-prob)*100:.1f}%)", "N/A", "N/A", "**No wildfire detected. Stay alert and maintain regular monitoring.**"
    
    # Step 2: Classify severity
    severity = classify_severity(img)
    
    # Step 3: Fetch weather trend
    trend = fetch_weather_trend(*FOREST_COORDS['Pakistan Forest'])
    
    # Step 4: Generate recommendations
    recommendations_text = generate_recommendations(severity, trend)
    
    return f"Wildfire detected (confidence: {prob*100:.1f}%)", severity.title(), trend.title(), recommendations_text

# --- LOAD MODELS GLOBALLY ---
vgg_model, xception_model, rf_model, xgb_model, lr_model = load_models()

# --- GRADIO INTERFACE ---
interface = gr.Interface(
    fn=pipeline,
    inputs=gr.Image(type='numpy', label='Upload Wildfire Image'),
    outputs=[
        gr.Textbox(label='Fire Status'),
        gr.Textbox(label='Current Severity Level'),
        gr.Textbox(label='Weather Trend'),
        gr.Markdown(label='Management Recommendations')
    ],
    title='Wildfire Detection & Management Assistant',
    description='Upload an image from a forest region in Pakistan to determine wildfire presence, severity, weather-driven trend, and get expert management recommendations.',
    examples=[],
    theme=gr.themes.Base(),
    allow_flagging='never'
).queue(api_open=True)

if __name__ == '__main__':
    interface.launch(share=False)