Neylton commited on
Commit
98c928a
Β·
verified Β·
1 Parent(s): 87117a8

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +596 -0
app.py ADDED
@@ -0,0 +1,596 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ zeroFire - Fire Detection Classification App
4
+ AI-powered fire detection using ConvNeXt transfer learning
5
+ """
6
+
7
+ import streamlit as st
8
+ import torch
9
+ import torch.nn.functional as F
10
+ from PIL import Image
11
+ import numpy as np
12
+ import plotly.graph_objects as go
13
+ import plotly.express as px
14
+ from plotly.subplots import make_subplots
15
+ import pandas as pd
16
+ import sys
17
+ import os
18
+ import time
19
+ from io import BytesIO
20
+ import base64
21
+
22
+ # Add utils to path
23
+ sys.path.append('utils')
24
+ from model_utils import load_model, FireDetectionClassifier
25
+ from data_utils import get_inference_transform, prepare_image_for_inference, check_data_directory
26
+
27
+ # Page Configuration
28
+ st.set_page_config(
29
+ page_title="πŸ”₯ zeroFire - Fire Detection System",
30
+ page_icon="πŸ”₯",
31
+ layout="wide",
32
+ initial_sidebar_state="expanded"
33
+ )
34
+
35
+ # Custom CSS for Beautiful UI
36
+ st.markdown("""
37
+ <style>
38
+ .main-header {
39
+ background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
40
+ padding: 2rem;
41
+ border-radius: 15px;
42
+ text-align: center;
43
+ color: white;
44
+ margin-bottom: 2rem;
45
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
46
+ }
47
+
48
+ .main-header h1 {
49
+ font-size: 3rem;
50
+ margin: 0;
51
+ font-weight: bold;
52
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
53
+ }
54
+
55
+ .main-header p {
56
+ font-size: 1.2rem;
57
+ margin: 0.5rem 0 0 0;
58
+ opacity: 0.9;
59
+ }
60
+
61
+ .upload-section {
62
+ background: linear-gradient(135deg, #a8e6cf 0%, #74b9ff 100%);
63
+ color: white;
64
+ padding: 15px 20px;
65
+ border-radius: 15px;
66
+ text-align: center;
67
+ margin-bottom: 20px;
68
+ box-shadow: 0 6px 20px rgba(168, 230, 207, 0.3);
69
+ height: 100px;
70
+ display: flex;
71
+ flex-direction: column;
72
+ justify-content: center;
73
+ }
74
+
75
+ .upload-section h3 {
76
+ font-size: 1.3rem;
77
+ margin: 0 0 5px 0;
78
+ }
79
+
80
+ .upload-section p {
81
+ font-size: 0.9rem;
82
+ margin: 0;
83
+ opacity: 0.9;
84
+ }
85
+
86
+ .result-fire {
87
+ background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
88
+ color: white;
89
+ padding: 15px 20px;
90
+ border-radius: 15px;
91
+ text-align: center;
92
+ margin: 0 0 20px 0;
93
+ box-shadow: 0 6px 20px rgba(220, 53, 69, 0.3);
94
+ height: 100px;
95
+ display: flex;
96
+ flex-direction: column;
97
+ justify-content: center;
98
+ }
99
+
100
+ .result-fire h2 {
101
+ font-size: 1.2rem;
102
+ margin: 0 0 5px 0;
103
+ line-height: 1.2;
104
+ }
105
+
106
+ .result-fire p {
107
+ font-size: 0.85rem;
108
+ margin: 0;
109
+ font-weight: bold;
110
+ }
111
+
112
+ .result-no-fire {
113
+ background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
114
+ color: white;
115
+ padding: 15px 20px;
116
+ border-radius: 15px;
117
+ text-align: center;
118
+ margin: 0 0 20px 0;
119
+ box-shadow: 0 6px 20px rgba(40, 167, 69, 0.3);
120
+ height: 100px;
121
+ display: flex;
122
+ flex-direction: column;
123
+ justify-content: center;
124
+ }
125
+
126
+ .result-no-fire h2 {
127
+ font-size: 1.2rem;
128
+ margin: 0 0 5px 0;
129
+ line-height: 1.2;
130
+ }
131
+
132
+ .result-no-fire p {
133
+ font-size: 0.85rem;
134
+ margin: 0;
135
+ font-weight: bold;
136
+ }
137
+
138
+ .metric-card {
139
+ background: linear-gradient(135deg, #ff9ff3 0%, #f368e0 100%);
140
+ padding: 15px;
141
+ border-radius: 10px;
142
+ text-align: center;
143
+ color: white;
144
+ margin: 10px 0;
145
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
146
+ }
147
+
148
+ .info-card {
149
+ background: linear-gradient(135deg, #ff9ff3 0%, #f368e0 100%);
150
+ color: white;
151
+ padding: 20px;
152
+ border-radius: 15px;
153
+ margin: 15px 0;
154
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
155
+ }
156
+
157
+ .info-card-fire {
158
+ background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
159
+ color: white;
160
+ padding: 20px;
161
+ border-radius: 15px;
162
+ margin: 15px 0;
163
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
164
+ }
165
+
166
+ .stButton > button {
167
+ background: linear-gradient(45deg, #74b9ff, #0984e3);
168
+ color: white;
169
+ border: none;
170
+ border-radius: 10px;
171
+ padding: 12px 24px;
172
+ font-weight: bold;
173
+ font-size: 1.1rem;
174
+ transition: all 0.3s ease;
175
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
176
+ width: 100%;
177
+ }
178
+
179
+ .stButton > button:hover {
180
+ transform: translateY(-2px);
181
+ box-shadow: 0 6px 20px rgba(0,0,0,0.3);
182
+ }
183
+
184
+ .sidebar .stSelectbox > div > div {
185
+ background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
186
+ color: white;
187
+ }
188
+
189
+ /* Constrain image height to prevent scrolling */
190
+ .stImage > img {
191
+ max-height: 400px;
192
+ width: auto;
193
+ object-fit: contain;
194
+ }
195
+ </style>
196
+ """, unsafe_allow_html=True)
197
+
198
+ @st.cache_resource
199
+ def load_fire_model():
200
+ """Load the trained fire detection model"""
201
+ model_path = 'models/fire_detection_classifier.pth'
202
+
203
+ if not os.path.exists(model_path):
204
+ return None, "Model not found. Please train the model first."
205
+
206
+ try:
207
+ model, model_info = load_model(model_path, device='cpu')
208
+ return model, model_info
209
+ except Exception as e:
210
+ return None, f"Error loading model: {str(e)}"
211
+
212
+ def get_prediction(image, model, transform):
213
+ """Get prediction from the model"""
214
+ try:
215
+ # Prepare image
216
+ input_tensor = prepare_image_for_inference(image, transform)
217
+
218
+ # Get prediction
219
+ with torch.no_grad():
220
+ model.eval()
221
+ outputs = model(input_tensor)
222
+ probabilities = F.softmax(outputs, dim=1)
223
+ confidence, predicted = torch.max(probabilities, 1)
224
+
225
+ # Convert to numpy
226
+ predicted_class = predicted.item()
227
+ confidence_score = confidence.item()
228
+ all_probs = probabilities.squeeze().cpu().numpy()
229
+
230
+ return predicted_class, confidence_score, all_probs
231
+
232
+ except Exception as e:
233
+ st.error(f"Error during prediction: {str(e)}")
234
+ return None, None, None
235
+
236
+ def create_confidence_chart(probabilities, class_names):
237
+ """Create confidence chart using Plotly"""
238
+ fig = go.Figure(data=[
239
+ go.Bar(
240
+ x=class_names,
241
+ y=probabilities,
242
+ marker_color=['#dc3545', '#28a745'],
243
+ text=[f'{p:.1%}' for p in probabilities],
244
+ textposition='auto',
245
+ )
246
+ ])
247
+
248
+ fig.update_layout(
249
+ title="Fire Detection Confidence",
250
+ xaxis_title="Prediction",
251
+ yaxis_title="Confidence",
252
+ yaxis=dict(range=[0, 1]),
253
+ showlegend=False,
254
+ height=400,
255
+ template="plotly_white"
256
+ )
257
+
258
+ return fig
259
+
260
+ def create_safety_metrics_chart(predicted_class, confidence):
261
+ """Create safety metrics visualization"""
262
+ if predicted_class == 0: # Fire
263
+ danger_level = confidence * 100
264
+ safety_level = (1 - confidence) * 100
265
+ primary_color = '#dc3545'
266
+ status = "FIRE DETECTED"
267
+ else: # No Fire
268
+ danger_level = (1 - confidence) * 100
269
+ safety_level = confidence * 100
270
+ primary_color = '#28a745'
271
+ status = "NO FIRE"
272
+
273
+ fig = go.Figure(go.Indicator(
274
+ mode = "gauge+number+delta",
275
+ value = danger_level,
276
+ domain = {'x': [0, 1], 'y': [0, 1]},
277
+ title = {'text': "Fire Risk Level"},
278
+ delta = {'reference': 50},
279
+ gauge = {'axis': {'range': [None, 100]},
280
+ 'bar': {'color': primary_color},
281
+ 'steps' : [
282
+ {'range': [0, 25], 'color': "lightgray"},
283
+ {'range': [25, 50], 'color': "yellow"},
284
+ {'range': [50, 75], 'color': "orange"},
285
+ {'range': [75, 100], 'color': "red"}],
286
+ 'threshold' : {'line': {'color': "red", 'width': 4},
287
+ 'thickness': 0.75, 'value': 90}}))
288
+
289
+ fig.update_layout(height=400)
290
+ return fig
291
+
292
+ def analyze_fire_risk(predicted_class, confidence):
293
+ """Analyze fire risk and provide recommendations"""
294
+ if predicted_class == 0: # Fire detected
295
+ risk_level = confidence * 100
296
+
297
+ if risk_level >= 90:
298
+ return {
299
+ 'level': 'CRITICAL',
300
+ 'color': '#dc3545',
301
+ 'icon': '🚨',
302
+ 'message': 'IMMEDIATE ACTION REQUIRED',
303
+ 'recommendations': [
304
+ 'Activate fire suppression system immediately',
305
+ 'Evacuate the area',
306
+ 'Call emergency services UAE 997',
307
+ 'Shut down affected equipment if safe to do so',
308
+ 'Monitor surrounding areas for spread'
309
+ ]
310
+ }
311
+ elif risk_level >= 75:
312
+ return {
313
+ 'level': 'HIGH',
314
+ 'color': '#fd7e14',
315
+ 'icon': '⚠️',
316
+ 'message': 'HIGH FIRE RISK DETECTED',
317
+ 'recommendations': [
318
+ 'Investigate the area immediately',
319
+ 'Prepare fire suppression systems',
320
+ 'Alert security personnel',
321
+ 'Consider equipment shutdown',
322
+ 'Increase monitoring frequency'
323
+ ]
324
+ }
325
+ else:
326
+ return {
327
+ 'level': 'MODERATE',
328
+ 'color': '#ffc107',
329
+ 'icon': 'πŸ”Ά',
330
+ 'message': 'POSSIBLE FIRE DETECTED',
331
+ 'recommendations': [
332
+ 'Verify with additional sensors',
333
+ 'Send personnel to investigate',
334
+ 'Check equipment temperatures',
335
+ 'Review recent maintenance logs',
336
+ 'Maintain heightened awareness'
337
+ ]
338
+ }
339
+ else: # No fire
340
+ safety_level = confidence * 100
341
+
342
+ if safety_level >= 95:
343
+ return {
344
+ 'level': 'SAFE',
345
+ 'color': '#28a745',
346
+ 'icon': 'βœ…',
347
+ 'message': 'NORMAL OPERATION',
348
+ 'recommendations': [
349
+ 'Continue normal operations',
350
+ 'Maintain regular monitoring',
351
+ 'Keep fire suppression systems ready',
352
+ 'Perform scheduled maintenance',
353
+ 'Review safety protocols periodically'
354
+ ]
355
+ }
356
+ else:
357
+ return {
358
+ 'level': 'CAUTION',
359
+ 'color': '#17a2b8',
360
+ 'icon': '⚠️',
361
+ 'message': 'MONITOR CLOSELY',
362
+ 'recommendations': [
363
+ 'Increase monitoring frequency',
364
+ 'Check for unusual conditions',
365
+ 'Verify sensor functionality',
366
+ 'Review environmental factors',
367
+ 'Maintain readiness for action'
368
+ ]
369
+ }
370
+
371
+ # display_fire_safety_checklist function removed - content moved to right column
372
+
373
+ def main():
374
+ """Main application function"""
375
+ # Header
376
+ st.markdown("""
377
+ <div class="main-header">
378
+ <h1>πŸ”₯ zeroFire</h1>
379
+ <p>AI-Powered Fire Detection System for Data Centers</p>
380
+ </div>
381
+ """, unsafe_allow_html=True)
382
+
383
+ # Sidebar
384
+ with st.sidebar:
385
+ st.markdown("### πŸ”₯ Fire Detection System")
386
+ st.markdown("---")
387
+
388
+ # Model status
389
+ model, model_info = load_fire_model()
390
+
391
+ if model is None:
392
+ st.error("❌ Model not available")
393
+ st.info("Train the model first using: `python train_fire_detection.py`")
394
+ return
395
+ else:
396
+ st.success("βœ… Model loaded successfully")
397
+ if isinstance(model_info, dict):
398
+ accuracy = model_info.get('best_acc', 'Unknown')
399
+ if accuracy != 'Unknown':
400
+ # Format accuracy to 2 decimal places
401
+ accuracy_formatted = f"{float(accuracy):.2f}%"
402
+ st.info(f"πŸ“Š Model: ConvNeXt Large")
403
+ st.info(f"🎯 Accuracy: {accuracy_formatted}")
404
+ st.info(f"πŸ”„ Transfer Learning: Foodβ†’Fire")
405
+ st.info(f"⚑ Precision: High-recall optimized")
406
+ else:
407
+ st.info("πŸ“Š Model Accuracy: Unknown")
408
+
409
+ st.markdown("---")
410
+
411
+ # Settings
412
+ st.markdown("### βš™οΈ Settings")
413
+ confidence_threshold = st.slider(
414
+ "Confidence Threshold",
415
+ min_value=0.0,
416
+ max_value=1.0,
417
+ value=0.5,
418
+ step=0.05,
419
+ help="Minimum confidence required for fire detection"
420
+ )
421
+
422
+ show_details = st.checkbox("Show detailed analysis", value=True)
423
+
424
+ st.markdown("---")
425
+ st.markdown("### πŸ“Š Data Center Status")
426
+ check_data_directory('data')
427
+
428
+ # Main content
429
+ col1, col2 = st.columns([2, 1])
430
+
431
+ with col1:
432
+ # Upload section
433
+ st.markdown("""
434
+ <div class="upload-section">
435
+ <h3>πŸ“Έ Upload Data Center Image</h3>
436
+ <p>Upload an image to detect fire or smoke in your data center</p>
437
+ </div>
438
+ """, unsafe_allow_html=True)
439
+
440
+ uploaded_file = st.file_uploader(
441
+ "Choose an image...",
442
+ type=['jpg', 'jpeg', 'png', 'bmp', 'tiff'],
443
+ help="Upload an image from your data center for fire detection"
444
+ )
445
+
446
+ if uploaded_file is not None:
447
+ # Display uploaded image
448
+ image = Image.open(uploaded_file)
449
+ st.image(image, caption="Uploaded Image", use_column_width=True)
450
+
451
+ # Get prediction
452
+ transform = get_inference_transform()
453
+ predicted_class, confidence_score, all_probs = get_prediction(image, model, transform)
454
+
455
+ if predicted_class is not None:
456
+ class_names = ['Fire', 'No Fire']
457
+ predicted_label = class_names[predicted_class]
458
+
459
+ # Fire detection result moved to right column
460
+
461
+ # Technical details (keep only this in left column)
462
+ with st.expander("πŸ”¬ Technical Details"):
463
+ st.markdown(f"""
464
+ **Prediction Details:**
465
+ - Predicted Class: {predicted_label}
466
+ - Confidence Score: {confidence_score:.4f}
467
+ - Fire Probability: {all_probs[0]:.4f}
468
+ - No-Fire Probability: {all_probs[1]:.4f}
469
+ - Threshold: {confidence_threshold:.2f}
470
+ """)
471
+
472
+ with col2:
473
+ # Display fire detection result first
474
+ if 'predicted_class' in locals():
475
+ # Display fire detection result box
476
+ if predicted_class == 0: # Fire
477
+ st.markdown(f"""
478
+ <div class="result-fire">
479
+ <h2>🚨 FIRE DETECTED - Confidence: {confidence_score:.1%}</h2>
480
+ <p>IMMEDIATE ACTION REQUIRED</p>
481
+ </div>
482
+ """, unsafe_allow_html=True)
483
+ else: # No Fire
484
+ st.markdown(f"""
485
+ <div class="result-no-fire">
486
+ <h2>βœ… NO FIRE DETECTED - Confidence: {confidence_score:.1%}</h2>
487
+ <p>Normal Operation</p>
488
+ </div>
489
+ """, unsafe_allow_html=True)
490
+
491
+ # Quick metrics
492
+ st.markdown("### πŸ“Š Quick Metrics")
493
+
494
+ if 'predicted_class' in locals():
495
+ # Create three columns for metrics in a row
496
+ metric_col1, metric_col2, metric_col3 = st.columns(3)
497
+
498
+ with metric_col1:
499
+ # Fire risk gauge
500
+ risk_percentage = all_probs[0] * 100
501
+ st.metric(
502
+ label="Fire Risk",
503
+ value=f"{risk_percentage:.1f}%"
504
+ )
505
+
506
+ with metric_col2:
507
+ # Safety score
508
+ safety_score = all_probs[1] * 100
509
+ st.metric(
510
+ label="Safety Score",
511
+ value=f"{safety_score:.1f}%"
512
+ )
513
+
514
+ with metric_col3:
515
+ # Model status instead of redundant confidence
516
+ status = "FIRE ALERT" if predicted_class == 0 else "NORMAL"
517
+ st.metric(
518
+ label="Status",
519
+ value=status
520
+ )
521
+
522
+ # Charts removed to eliminate confusion and redundancy
523
+
524
+ # Detailed analysis moved from left column
525
+ if show_details:
526
+ st.markdown("### πŸ“‹ Detailed Analysis")
527
+
528
+ analysis = analyze_fire_risk(predicted_class, confidence_score)
529
+
530
+ # Use red styling only for fire detection
531
+ card_class = "info-card-fire" if predicted_class == 0 else "info-card"
532
+
533
+ st.markdown(f"""
534
+ <div class="{card_class}">
535
+ <h3>{analysis['icon']} Risk Level: {analysis['level']}</h3>
536
+ <p><strong>{analysis['message']}</strong></p>
537
+ </div>
538
+ """, unsafe_allow_html=True)
539
+
540
+ st.markdown("#### 🎯 Recommended Actions:")
541
+ for rec in analysis['recommendations']:
542
+ st.markdown(f"- {rec}")
543
+
544
+ # Fire Safety Checklist moved here
545
+ st.markdown("---")
546
+ st.markdown("""
547
+ <div class="info-card">
548
+ <h3>πŸ”₯ Fire Safety Checklist</h3>
549
+ <p>Essential fire safety measures for data centers:</p>
550
+ </div>
551
+ """, unsafe_allow_html=True)
552
+
553
+ safety_items = [
554
+ "πŸ”₯ **Fire Detection Systems** - Smoke, heat, and flame detectors",
555
+ "πŸ’¨ **Suppression Systems** - Clean agent, water mist, or CO2",
556
+ "🌑️ **Temperature Monitoring** - Continuous thermal monitoring",
557
+ "⚑ **Electrical Safety** - Arc fault and ground fault protection",
558
+ "πŸšͺ **Emergency Exits** - Clear and well-marked escape routes",
559
+ "πŸ“‹ **Emergency Procedures** - Staff training and evacuation plans",
560
+ "πŸ”§ **Equipment Maintenance** - Regular inspection and testing",
561
+ "πŸ“ž **Emergency Contacts** - Quick access to fire department",
562
+ "🎯 **Response Plans** - Pre-defined actions for different scenarios",
563
+ "πŸ“Š **Documentation** - Incident logging and safety records"
564
+ ]
565
+
566
+ for item in safety_items:
567
+ st.markdown(f"- {item}")
568
+
569
+ # Additional info moved to bottom
570
+
571
+ # Footer
572
+ st.markdown("---")
573
+
574
+ # Emergency Contacts only (Fire Safety Checklist moved to right column)
575
+ st.markdown("""
576
+ <div class="info-card" style="padding: 15px;">
577
+ <h3 style="margin: 0 0 10px 0; text-align: center;">🚨 Emergency Contacts</h3>
578
+ <div style="display: flex; justify-content: space-around; flex-wrap: wrap; gap: 15px;">
579
+ <span><strong>Fire Dept:</strong> UAE 997</span>
580
+ <span><strong>Security:</strong> [Your Number]</span>
581
+ <span><strong>Facilities:</strong> [Your Number]</span>
582
+ <span><strong>IT Ops:</strong> [Your Number]</span>
583
+ </div>
584
+ </div>
585
+ """, unsafe_allow_html=True)
586
+
587
+ st.markdown("---")
588
+ st.markdown("""
589
+ <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #a8e6cf 0%, #74b9ff 100%); border-radius: 15px; color: white;">
590
+ <p>πŸ”₯ zeroFire - AI-Powered Fire Detection System</p>
591
+ <p>Protecting your data center with advanced machine learning</p>
592
+ </div>
593
+ """, unsafe_allow_html=True)
594
+
595
+ if __name__ == "__main__":
596
+ main()