ninafr8175
update dashboard 6
2e0d90d
import os # pour vérifier la présence des fichiers de modèles
import streamlit as st # librairie pour le dashboard
from PIL import Image # pour ouvrir les images
from inference import ( # fonctions importées du fichier inference.py
load_model,
get_val_transform,
predict_from_pil
)
# -----------------------------------------------------------
# Modèles disponibles (théoriques)
# -----------------------------------------------------------
ALL_MODEL_FILES = {
"Modèle EfficientnetB0 Baseline": "efficientnet_fire.pt",
"Modèle EfficientnetB0 FE": "efficientnet_fire_2.pt",
"Modèle EfficientnetB0 FT": "efficientnet_fire_3.pt",
"Modèle YOLOv8": "yolov8_fire.pt",
"Modèle Inception3": "inception3_fire.pt",
}
# Ne garder que les modèles réellement présents dans le repo
MODEL_FILES = {
name: path for name, path in ALL_MODEL_FILES.items() if os.path.exists(path)
}
if len(MODEL_FILES) == 0:
st.error("❌ Aucun modèle trouvé dans le repository. Ajoutez au moins un fichier .pt.")
st.stop()
# -----------------------------------------------------------
# Métriques par modèle (à remplir plus tard si besoin)
# Clé = chemin du fichier de poids (ex: 'efficientnet_fire.pt')
# -----------------------------------------------------------
MODEL_METRICS = {
"efficientnet_fire.pt": {
"accuracy": 0.94,
"precision": 0.92,
"recall": 0.89,
"f1": 0.90,
"fp": 12,
"fn": 4,
},
"efficientnet_fire_2.pt": {
"accuracy": 0.7727,
"precision": 0.9111,
"recall": 0.7704,
"f1": 0.8349,
"fp": 275,
"fn": 840,
},
"efficientnet_fire_3.pt": {
"accuracy": 0.7717,
"precision": 0.8713,
"recall": 0.8142,
"f1": 0.8418,
"fp": 440,
"fn": 680,
},
# "yolov8_fire.pt": {
# "accuracy": 0.7717,
# "precision": 0.8713,
# "recall": 0.8142,
# "f1": 0.8418,
# "fp": 440,
# "fn": 680,
# },
# "inception3_fire.pt": {
# "accuracy": 0.7717,
# "precision": 0.8713,
# "recall": 0.8142,
# "f1": 0.8418,
# "fp": 440,
# "fn": 680,
# },
}
# -----------------------------------------------------------
# Configuration de la page
# -----------------------------------------------------------
st.set_page_config(
page_title="Fire Detection Dashboard", # titre de l’onglet du navigateur
page_icon="🔥", # icône (emoji)
layout="centered" # mise en page centrée
)
# -----------------------------------------------------------
# Sidebar : choix du modèle + paramètres
# -----------------------------------------------------------
st.sidebar.title("⚙️ Paramètres")
selected_model_name = st.sidebar.selectbox(
"Choisir le modèle à utiliser",
options=list(MODEL_FILES.keys()),
index=0
)
selected_model_path = MODEL_FILES[selected_model_name]
st.sidebar.markdown(f"🧠 Modèle sélectionné : **{selected_model_name}**")
st.sidebar.markdown(
"""
Ce dashboard prédit **FIRE / NO FIRE** sur des images.
- Classe 0 : **no_fire**
- Classe 1 : **fire**
"""
)
threshold = st.sidebar.slider(
"Seuil de détection du feu (probabilité minimale pour 'fire')",
min_value=0.1,
max_value=0.9,
value=0.5,
step=0.05,
)
st.sidebar.markdown(f"Seuil actuel : **{threshold:.2f}**")
# -----------------------------------------------------------
# Chargement du modèle (en fonction du choix)
# -----------------------------------------------------------
@st.cache_resource
def load_app_model(model_path: str):
"""
Charge le modèle, le device et la transform une seule fois
pour un chemin donné, puis les réutilise pour toutes les prédictions.
"""
model, device = load_model(model_path) # charge les poids du modèle choisi
transform = get_val_transform() # transform validation/inférence
return model, device, transform
model, device, transform = load_app_model(selected_model_path)
# Récupération éventuelle des métriques pour ce modèle
metrics = MODEL_METRICS.get(selected_model_path, None)
# -----------------------------------------------------------
# Titre principal + guide d’utilisation
# -----------------------------------------------------------
st.title("🔥 Fire Detection Dashboard")
st.markdown(
"""
Ce prototype permet de tester un modèle de détection de feu
sur des images individuelles.
"""
)
st.markdown(
"""
### 🧭 Guide d’utilisation
1. 🧠 **Choisissez le modèle** dans la barre latérale
2. 🖼️ **Chargez une image** - JPG, JPEG, PNG - Maximum 200MB
3. 🔍 **Obtenez la prédiction** (FEU / PAS DE FEU DÉTECTÉ + probabilité)
4. 📊 **Comparez les modèles** pour explorer leurs différences
5. ⚙️ **Ajustez le seuil** pour un modèle +/- strict
"""
)
st.markdown("<br>", unsafe_allow_html=True)
# -----------------------------------------------------------
# Zone d'upload d'image
# -----------------------------------------------------------
uploaded_file = st.file_uploader(
"📂 Déposez une image ici (ou cliquez sur Browse Files pour choisir une image)",
type=["jpg", "jpeg", "png"],
help="Formats supportés : JPG, JPEG, PNG\nMaximum 200MB par image",
accept_multiple_files=False
)
# -----------------------------------------------------------
# Si aucune image n'est encore uploadée
# -----------------------------------------------------------
if uploaded_file is None:
st.info("👉 En attente d'une image. Charger une photo de forêt, flamme, paysage, etc.")
else:
# -------------------------------------------------------
# Afficher l'image uploadée
# -------------------------------------------------------
image = Image.open(uploaded_file)
# -------------------------------------------------------
# Prédiction
# -------------------------------------------------------
with st.spinner("Analyse de l'image en cours..."):
label, prob, annotated_image = predict_from_pil(
image=image,
model=model,
device=device,
transform=transform,
threshold=threshold
)
# Image originale
st.image(image, caption="Image chargée", use_container_width=True)
# Si YOLO a fourni une image annotée, on l'affiche
if annotated_image is not None:
st.image(
annotated_image,
caption="Zones détectées (modèle de détection)",
use_container_width=True,
)
# -------------------------------------------------------
# Affichage du résultat avec couleur
# -------------------------------------------------------
prob_percent = prob * 100
if label == "fire":
st.error(
f"🔥 **FEU DÉTECTÉ** \nProbabilité de feu : **{prob_percent:.2f}%** \n(Seuil utilisé : {threshold:.2f})"
)
else:
st.success(
f"✅ **PAS DE FEU DÉTECTÉ** \nProbabilité de feu : **{prob_percent:.2f}%** \n(Seuil utilisé : {threshold:.2f})"
)
# -------------------------------------------------------
# Détails techniques (expander)
# -------------------------------------------------------
with st.expander("🔍 Détails techniques"):
st.markdown(
f"""
- Modèle utilisé : **{selected_model_name}**
- Fichier de poids : `{selected_model_path}`
- Label retourné : **{label}**
- Probabilité brute de la classe *fire* : **{prob:.4f}**
- Seuil de décision : **{threshold:.2f}**
Si `prob_fire >= seuil` → prédiction = *fire*,
sinon → *no_fire*.
"""
)
# -------------------------------------------------------
# Performances du modèle (expander conditionnel)
# -------------------------------------------------------
if metrics is not None:
with st.expander("📊 Performances du modèle (validation) – ⚠️ Valeurs fictives pour EfficientnetB0 Baseline"):
st.markdown(
f"""
| Métriques | Valeurs |
|----------------------|-------|
| Accuracy | {metrics.get('accuracy', 'non disponible')} |
| Precision (fire) | {metrics.get('precision', 'non disponible')} |
| Recall (fire) | {metrics.get('recall', 'non disponible')} |
| F1-score (fire) | {metrics.get('f1', 'non disponible')} |
| False Positives (FP) | {metrics.get('fp', 'non disponible')} |
| False Negatives (FN) | {metrics.get('fn', 'non disponible')} |
"""
)