Download app.py from fire-detection-team/Fire_Detection_Streamlit: direct link, hf CLI and curl.
- Browser
- Download file 8.79 kB
-
https://huggingface.co/spaces/fire-detection-team/Fire_Detection_Streamlit/resolve/main/app.py
- Command line
-
hf download hf://spaces/fire-detection-team/Fire_Detection_Streamlit/app.py
-
curl -L -o app.py https://huggingface.co/spaces/fire-detection-team/Fire_Detection_Streamlit/resolve/main/app.py
8.79 kB
| 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) | |
| # ----------------------------------------------------------- | |
| 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')} | | |
| """ | |
| ) | |