#!/usr/bin/env python3
"""
zeroFire - Fire Detection Classification App
AI-powered fire detection using ConvNeXt transfer learning
"""
import streamlit as st
import torch
import torch.nn.functional as F
from PIL import Image
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import pandas as pd
import sys
import os
import time
from io import BytesIO
import base64
# Add utils to path
sys.path.append('utils')
from model_utils import load_model, FireDetectionClassifier
from data_utils import get_inference_transform, prepare_image_for_inference, check_data_directory
# Page Configuration
st.set_page_config(
page_title="π₯ zeroFire - Fire Detection System",
page_icon="π₯",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for Beautiful UI
st.markdown("""
""", unsafe_allow_html=True)
@st.cache_resource
def load_fire_model():
"""Load the trained fire detection model"""
model_path = 'models/fire_detection_classifier.pth'
if not os.path.exists(model_path):
return None, "Model not found. Please train the model first."
try:
model, model_info = load_model(model_path, device='cpu')
return model, model_info
except Exception as e:
return None, f"Error loading model: {str(e)}"
def get_prediction(image, model, transform):
"""Get prediction from the model"""
try:
# Prepare image
input_tensor = prepare_image_for_inference(image, transform)
# Get prediction
with torch.no_grad():
model.eval()
outputs = model(input_tensor)
probabilities = F.softmax(outputs, dim=1)
confidence, predicted = torch.max(probabilities, 1)
# Convert to numpy
predicted_class = predicted.item()
confidence_score = confidence.item()
all_probs = probabilities.squeeze().cpu().numpy()
return predicted_class, confidence_score, all_probs
except Exception as e:
st.error(f"Error during prediction: {str(e)}")
return None, None, None
def create_confidence_chart(probabilities, class_names):
"""Create confidence chart using Plotly"""
fig = go.Figure(data=[
go.Bar(
x=class_names,
y=probabilities,
marker_color=['#dc3545', '#28a745'],
text=[f'{p:.1%}' for p in probabilities],
textposition='auto',
)
])
fig.update_layout(
title="Fire Detection Confidence",
xaxis_title="Prediction",
yaxis_title="Confidence",
yaxis=dict(range=[0, 1]),
showlegend=False,
height=400,
template="plotly_white"
)
return fig
def create_safety_metrics_chart(predicted_class, confidence):
"""Create safety metrics visualization"""
if predicted_class == 0: # Fire
danger_level = confidence * 100
safety_level = (1 - confidence) * 100
primary_color = '#dc3545'
status = "FIRE DETECTED"
else: # No Fire
danger_level = (1 - confidence) * 100
safety_level = confidence * 100
primary_color = '#28a745'
status = "NO FIRE"
fig = go.Figure(go.Indicator(
mode = "gauge+number+delta",
value = danger_level,
domain = {'x': [0, 1], 'y': [0, 1]},
title = {'text': "Fire Risk Level"},
delta = {'reference': 50},
gauge = {'axis': {'range': [None, 100]},
'bar': {'color': primary_color},
'steps' : [
{'range': [0, 25], 'color': "lightgray"},
{'range': [25, 50], 'color': "yellow"},
{'range': [50, 75], 'color': "orange"},
{'range': [75, 100], 'color': "red"}],
'threshold' : {'line': {'color': "red", 'width': 4},
'thickness': 0.75, 'value': 90}}))
fig.update_layout(height=400)
return fig
def analyze_fire_risk(predicted_class, confidence):
"""Analyze fire risk and provide recommendations"""
if predicted_class == 0: # Fire detected
risk_level = confidence * 100
if risk_level >= 90:
return {
'level': 'CRITICAL',
'color': '#dc3545',
'icon': 'π¨',
'message': 'IMMEDIATE ACTION REQUIRED',
'recommendations': [
'Activate fire suppression system immediately',
'Evacuate the area',
'Call emergency services UAE 997',
'Shut down affected equipment if safe to do so',
'Monitor surrounding areas for spread'
]
}
elif risk_level >= 75:
return {
'level': 'HIGH',
'color': '#fd7e14',
'icon': 'β οΈ',
'message': 'HIGH FIRE RISK DETECTED',
'recommendations': [
'Investigate the area immediately',
'Prepare fire suppression systems',
'Alert security personnel',
'Consider equipment shutdown',
'Increase monitoring frequency'
]
}
else:
return {
'level': 'MODERATE',
'color': '#ffc107',
'icon': 'πΆ',
'message': 'POSSIBLE FIRE DETECTED',
'recommendations': [
'Verify with additional sensors',
'Send personnel to investigate',
'Check equipment temperatures',
'Review recent maintenance logs',
'Maintain heightened awareness'
]
}
else: # No fire
safety_level = confidence * 100
if safety_level >= 95:
return {
'level': 'SAFE',
'color': '#28a745',
'icon': 'β
',
'message': 'NORMAL OPERATION',
'recommendations': [
'Continue normal operations',
'Maintain regular monitoring',
'Keep fire suppression systems ready',
'Perform scheduled maintenance',
'Review safety protocols periodically'
]
}
else:
return {
'level': 'CAUTION',
'color': '#17a2b8',
'icon': 'β οΈ',
'message': 'MONITOR CLOSELY',
'recommendations': [
'Increase monitoring frequency',
'Check for unusual conditions',
'Verify sensor functionality',
'Review environmental factors',
'Maintain readiness for action'
]
}
# display_fire_safety_checklist function removed - content moved to right column
def main():
"""Main application function"""
# Header
st.markdown("""
π₯ zeroFire
AI-Powered Fire Detection System for Data Centers
""", unsafe_allow_html=True)
# Sidebar
with st.sidebar:
st.markdown("### π₯ Fire Detection System")
st.markdown("---")
# Model status
model, model_info = load_fire_model()
if model is None:
st.error("β Model not available")
st.info("Train the model first using: `python train_fire_detection.py`")
return
else:
st.success("β
Model loaded successfully")
if isinstance(model_info, dict):
accuracy = model_info.get('best_acc', 'Unknown')
if accuracy != 'Unknown':
# Format accuracy to 2 decimal places
accuracy_formatted = f"{float(accuracy):.2f}%"
st.info(f"π Model: ConvNeXt Large")
st.info(f"π― Accuracy: {accuracy_formatted}")
st.info(f"π Transfer Learning: FoodβFire")
st.info(f"β‘ Precision: High-recall optimized")
else:
st.info("π Model Accuracy: Unknown")
st.markdown("---")
# Settings
st.markdown("### βοΈ Settings")
confidence_threshold = st.slider(
"Confidence Threshold",
min_value=0.0,
max_value=1.0,
value=0.5,
step=0.05,
help="Minimum confidence required for fire detection"
)
show_details = st.checkbox("Show detailed analysis", value=True)
st.markdown("---")
st.markdown("### π Data Center Status")
check_data_directory('data')
# Main content
col1, col2 = st.columns([2, 1])
with col1:
# Upload section
st.markdown("""
πΈ Fire Detection Input
Upload an image or take a photo to detect fire or smoke
in your data center
""", unsafe_allow_html=True)
# Mobile-friendly file uploader
st.markdown("**π± Take Photo or Upload Image**")
# Clear mobile instructions
st.markdown("""
π± Mobile Users:
Tap "Browse files" below β Select "Camera" to take a photo
Or select "Photos" to choose from gallery
""", unsafe_allow_html=True)
# Troubleshooting info
with st.expander("π§ Camera not working? Click here for help"):
st.markdown("""
**If you don't see the Camera option:**
1. **β
Check your browser:**
- Use Chrome, Safari, or Firefox (latest versions)
- Edge or other browsers may not support camera
2. **π Ensure secure connection:**
- Camera requires HTTPS (β
Hugging Face uses HTTPS)
- Local development requires HTTPS for camera access
3. **π± Mobile device requirements:**
- iOS: Safari 11+ or Chrome 64+
- Android: Chrome 53+ or Firefox 68+
- Some older devices may not support camera
4. **π οΈ Try these steps:**
- Refresh the page and try again
- Clear browser cache and cookies
- Try a different browser
- Check if camera works on other websites
5. **π Alternative options:**
- Take photo with your camera app first
- Then select "Photos" to upload the saved image
- Or use the desktop version for file upload
""")
st.info("π‘ **Note**: Camera access depends on your browser and device. If it doesn't work, you can still upload photos from your gallery!")
# Standard Streamlit file uploader
uploaded_file = st.file_uploader(
"Browse files",
type=['jpg', 'jpeg', 'png'],
help="π± Mobile: Tap to see Camera and Photos options | π» Desktop: Click to browse files"
)
# Process the uploaded image
image = None
image_source = "uploaded"
if uploaded_file is not None:
image = Image.open(uploaded_file)
image_source = "uploaded"
# Process the image (whether uploaded or from camera)
if image is not None:
# Display the image with appropriate caption
st.image(image, caption="πΈ Your Image", use_column_width=True)
# Get prediction
transform = get_inference_transform()
predicted_class, confidence_score, all_probs = get_prediction(image, model, transform)
if predicted_class is not None:
class_names = ['Fire', 'No Fire']
predicted_label = class_names[predicted_class]
# Fire detection result moved to right column
# Technical details (keep only this in left column)
with st.expander("π¬ Technical Details"):
st.markdown(f"""
**Prediction Details:**
- Predicted Class: {predicted_label}
- Confidence Score: {confidence_score:.4f}
- Fire Probability: {all_probs[0]:.4f}
- No-Fire Probability: {all_probs[1]:.4f}
- Threshold: {confidence_threshold:.2f}
""")
with col2:
# Display fire detection result first
if 'predicted_class' in locals():
# Display fire detection result box
if predicted_class == 0: # Fire
st.markdown(f"""
π¨ FIRE DETECTED - Confidence: {confidence_score:.1%}
IMMEDIATE ACTION REQUIRED
""", unsafe_allow_html=True)
else: # No Fire
st.markdown(f"""
β
NO FIRE DETECTED - Confidence: {confidence_score:.1%}
Normal Operation
""", unsafe_allow_html=True)
# Quick metrics
st.markdown("### π Quick Metrics")
if 'predicted_class' in locals():
# Create three columns for metrics in a row
metric_col1, metric_col2, metric_col3 = st.columns(3)
with metric_col1:
# Fire risk gauge
risk_percentage = all_probs[0] * 100
st.metric(
label="Fire Risk",
value=f"{risk_percentage:.1f}%"
)
with metric_col2:
# Safety score
safety_score = all_probs[1] * 100
st.metric(
label="Safety Score",
value=f"{safety_score:.1f}%"
)
with metric_col3:
# Model status instead of redundant confidence
status = "FIRE ALERT" if predicted_class == 0 else "NORMAL"
st.metric(
label="Status",
value=status
)
# Charts removed to eliminate confusion and redundancy
# Detailed analysis moved from left column
if show_details:
st.markdown("### π Detailed Analysis")
analysis = analyze_fire_risk(predicted_class, confidence_score)
# Use red styling only for fire detection
card_class = "info-card-fire" if predicted_class == 0 else "info-card"
st.markdown(f"""
{analysis['icon']} Risk Level: {analysis['level']}
{analysis['message']}
""", unsafe_allow_html=True)
st.markdown("#### π― Recommended Actions:")
for rec in analysis['recommendations']:
st.markdown(f"- {rec}")
# Fire Safety Checklist moved here
st.markdown("---")
st.markdown("""
π₯ Fire Safety Checklist
Essential fire safety measures for data centers:
""", unsafe_allow_html=True)
safety_items = [
"π₯ **Fire Detection Systems** - Smoke, heat, and flame detectors",
"π¨ **Suppression Systems** - Clean agent, water mist, or CO2",
"π‘οΈ **Temperature Monitoring** - Continuous thermal monitoring",
"β‘ **Electrical Safety** - Arc fault and ground fault protection",
"πͺ **Emergency Exits** - Clear and well-marked escape routes",
"π **Emergency Procedures** - Staff training and evacuation plans",
"π§ **Equipment Maintenance** - Regular inspection and testing",
"π **Emergency Contacts** - Quick access to fire department",
"π― **Response Plans** - Pre-defined actions for different scenarios",
"π **Documentation** - Incident logging and safety records"
]
for item in safety_items:
st.markdown(f"- {item}")
# Additional info moved to bottom
# Footer
st.markdown("---")
# Emergency Contacts only (Fire Safety Checklist moved to right column)
st.markdown("""
π¨ Emergency Contacts
Fire Dept: UAE 997
Security: [Your Number]
Facilities: [Your Number]
IT Ops: [Your Number]
""", unsafe_allow_html=True)
st.markdown("---")
st.markdown("""
π₯ zeroFire - AI-Powered Fire Detection System
Protecting your data center with advanced machine learning
""", unsafe_allow_html=True)
if __name__ == "__main__":
main()