Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,59 +4,128 @@ import numpy as np
|
|
| 4 |
import pandas as pd
|
| 5 |
import os
|
| 6 |
|
| 7 |
-
#
|
| 8 |
MODEL_FILE = "smartrack_health_alert_vet_model.pkl"
|
| 9 |
ENCODER_FILE = "species_encoder_vet.pkl"
|
| 10 |
|
| 11 |
-
#
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
try:
|
| 16 |
model = pickle.load(open(MODEL_FILE, "rb"))
|
| 17 |
encoder = pickle.load(open(ENCODER_FILE, "rb"))
|
| 18 |
except Exception as e:
|
| 19 |
-
|
| 20 |
-
print(f"Erreur de chargement des artefacts : {e}")
|
| 21 |
model = None
|
| 22 |
|
|
|
|
| 23 |
classes = ['OK (Santé Normale)', 'ALERTE (Intervention Recommandée)']
|
| 24 |
SPECIES_LIST = list(encoder.categories_[0])
|
| 25 |
|
| 26 |
|
| 27 |
-
# 2. Fonction de Prédiction avec Output Audio
|
| 28 |
-
def predict_health_alert_vocal(species, hr, spo2, temp, acc_mean, gyro_mean):
|
| 29 |
-
""" Prend les données et renvoie le texte d'alerte
|
| 30 |
|
| 31 |
if model is None:
|
| 32 |
-
return "Erreur: Modèle non chargé.
|
| 33 |
|
|
|
|
| 34 |
input_data = pd.DataFrame({
|
| 35 |
'hr': [hr], 'spo2': [spo2], 'temp': [temp],
|
| 36 |
'acc_mean': [acc_mean], 'gyro_mean': [gyro_mean],
|
| 37 |
'species': [species]
|
| 38 |
})
|
| 39 |
|
|
|
|
| 40 |
numerical_features = input_data.drop('species', axis=1)
|
| 41 |
species_encoded = encoder.transform(input_data[['species']])
|
| 42 |
species_df = pd.DataFrame(species_encoded, columns=encoder.get_feature_names_out(['species']))
|
| 43 |
|
| 44 |
X_final = pd.concat([numerical_features, species_df], axis=1)
|
| 45 |
|
|
|
|
| 46 |
prediction = model.predict(X_final)[0]
|
| 47 |
probas = model.predict_proba(X_final)[0]
|
| 48 |
predicted_class = classes[prediction]
|
| 49 |
|
| 50 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
if prediction == 1:
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
| 55 |
else:
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
output_text = f"## {alert_style} \n\n"
|
| 61 |
output_text += f"**Espèce :** {species.capitalize()}\n"
|
| 62 |
output_text += f"**Statut Analysé :** **{predicted_class}**\n\n"
|
|
@@ -64,13 +133,14 @@ def predict_health_alert_vocal(species, hr, spo2, temp, acc_mean, gyro_mean):
|
|
| 64 |
output_text += f"**Action Recommandée :** {action_message}\n\n"
|
| 65 |
output_text += f"*(Probabilité d'Alerte : {probas[1]*100:.2f}%)*"
|
| 66 |
|
| 67 |
-
return output_text, audio_path
|
| 68 |
|
| 69 |
|
| 70 |
-
# 3. Création de l'Interface Gradio (avec le composant
|
| 71 |
iface = gr.Interface(
|
| 72 |
-
fn=predict_health_alert_vocal,
|
| 73 |
inputs=[
|
|
|
|
| 74 |
gr.Dropdown(choices=SPECIES_LIST, label="1. Espèce Animale", value="boeuf"),
|
| 75 |
gr.Slider(minimum=30, maximum=200, value=75, label="2. Rythme Cardiaque (BPM)"),
|
| 76 |
gr.Slider(minimum=80, maximum=100, value=96, label="3. Saturation O₂ (SpO2 %)"),
|
|
@@ -79,12 +149,18 @@ iface = gr.Interface(
|
|
| 79 |
gr.Slider(minimum=0.0, maximum=5.0, value=1.5, label="6. Mouvement (Gyro. Moy.)"),
|
| 80 |
],
|
| 81 |
outputs=[
|
| 82 |
-
gr.Markdown(label="Rapport Textuel d'Alerte"),
|
| 83 |
-
gr.Audio(label="Alerte Vocale (
|
| 84 |
],
|
| 85 |
-
title="Système d'Alerte de Santé Vétérinaire SmarTrack (Vocal)",
|
| 86 |
-
description="Le modèle prédit l'état de santé et
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
)
|
| 88 |
|
| 89 |
-
#
|
| 90 |
-
#
|
|
|
|
|
|
| 4 |
import pandas as pd
|
| 5 |
import os
|
| 6 |
|
| 7 |
+
# --- VÉRIFICATION ET CHARGEMENT ---
|
| 8 |
MODEL_FILE = "smartrack_health_alert_vet_model.pkl"
|
| 9 |
ENCODER_FILE = "species_encoder_vet.pkl"
|
| 10 |
|
| 11 |
+
# Fichiers audio par langue (simulés)
|
| 12 |
+
# IMPORTANT: Pour une implémentation réelle, ces fichiers MP3 devraient
|
| 13 |
+
# contenir l'alerte enregistrée dans la langue spécifiée.
|
| 14 |
+
AUDIO_PATHS = {
|
| 15 |
+
'Français': {
|
| 16 |
+
'alert': "alert_high.mp3", # Français (Alerte Grave)
|
| 17 |
+
'ok': "alert_ok.mp3" # Français (Statut OK)
|
| 18 |
+
},
|
| 19 |
+
'Pular (Peul)': {
|
| 20 |
+
'alert': "alert_high_pular.mp3", # Fichier audio Pular (à fournir)
|
| 21 |
+
'ok': "alert_ok_pular.mp3" # Fichier audio Pular (à fournir)
|
| 22 |
+
},
|
| 23 |
+
'Mandinka (Malinké)': {
|
| 24 |
+
'alert': "alert_high_mandinka.mp3", # Fichier audio Mandinka (à fournir)
|
| 25 |
+
'ok': "alert_ok_mandinka.mp3" # Fichier audio Mandinka (à fournir)
|
| 26 |
+
},
|
| 27 |
+
'Soussou': {
|
| 28 |
+
'alert': "alert_high_soussou.mp3", # Fichier audio Soussou (à fournir)
|
| 29 |
+
'ok': "alert_ok_soussou.mp3" # Fichier audio Soussou (à fournir)
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
|
| 33 |
+
# Messages d'alerte dans différentes langues vernaculaires (texte)
|
| 34 |
+
MESSAGES = {
|
| 35 |
+
'Français': {
|
| 36 |
+
'alert': "🚨 ALERTE: Problème de Santé Détecté. Vérification immédiate recommandée.",
|
| 37 |
+
'ok': "✅ Statut de Santé: OK. Les signes vitaux sont dans la plage normale.",
|
| 38 |
+
'action': "Veuillez vérifier immédiatement l'animal ou consulter un vétérinaire."
|
| 39 |
+
},
|
| 40 |
+
'Pular (Peul)': {
|
| 41 |
+
'alert': "🚨 **Habbere Sellal Habi !** (Problème de santé grave !)",
|
| 42 |
+
'ok': "✅ **Cellal Habi: No Ngol'li.** (Santé OK : Tout est bien.)",
|
| 43 |
+
'action': "Jooni jooni ngeesa ndabba oo malla yaara ngesa." # Vérifiez immédiatement...
|
| 44 |
+
},
|
| 45 |
+
'Mandinka (Malinké)': {
|
| 46 |
+
'alert': "🚨 **Nteeri-nkoo Kori!** (Problème de collier !) (Simulation - Le message sera le même)",
|
| 47 |
+
'ok': "✅ **Jato Kamma: Ikoo.** (Statut OK : Il n'y a rien.) (Simulation - Le message sera le même)",
|
| 48 |
+
'action': "I ye dabba lafii siininiwalla yaara dakteri." # Vous devez vérifier l'animal ou aller chez le docteur.
|
| 49 |
+
},
|
| 50 |
+
'Soussou': {
|
| 51 |
+
'alert': "🚨 **Lanfana Xadi !** (Problème de santé grave !) (Simulation)",
|
| 52 |
+
'ok': "✅ **Lanfana: Mu Wali.** (Santé : Tout va bien.) (Simulation)",
|
| 53 |
+
'action': "Yandi lanfana xadi, ye dabba la fisa walla ye dakteri xadi." # Vérifiez rapidement l'animal ou allez voir le docteur.
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
# Vérification préalable pour s'assurer que les artefacts existent
|
| 58 |
+
if not os.path.exists(MODEL_FILE) or not os.path.exists(ENCODER_FILE):
|
| 59 |
+
print(f"❌ ERREUR FATALE: Les fichiers du modèle ({MODEL_FILE} ou {ENCODER_FILE}) sont introuvables.")
|
| 60 |
+
print("Veuillez vous assurer d'avoir exécuté les étapes de Simulation et d'Entraînement.")
|
| 61 |
+
# On laisse le FileNotFoundError, mais on continue avec model=None pour le déploiement local
|
| 62 |
+
|
| 63 |
try:
|
| 64 |
model = pickle.load(open(MODEL_FILE, "rb"))
|
| 65 |
encoder = pickle.load(open(ENCODER_FILE, "rb"))
|
| 66 |
except Exception as e:
|
| 67 |
+
print(f"❌ ERREUR: Problème lors du chargement des fichiers pickle : {e}")
|
|
|
|
| 68 |
model = None
|
| 69 |
|
| 70 |
+
# --- DÉFINITION GLOBALE ---
|
| 71 |
classes = ['OK (Santé Normale)', 'ALERTE (Intervention Recommandée)']
|
| 72 |
SPECIES_LIST = list(encoder.categories_[0])
|
| 73 |
|
| 74 |
|
| 75 |
+
# 2. Fonction de Prédiction avec Output Audio (mis à jour avec Langue)
|
| 76 |
+
def predict_health_alert_vocal(langue_choisie, species, hr, spo2, temp, acc_mean, gyro_mean):
|
| 77 |
+
""" Prend les données, la langue choisie, et renvoie le texte d'alerte et l'audio correspondant. """
|
| 78 |
|
| 79 |
if model is None:
|
| 80 |
+
return "Erreur: Modèle non chargé. Veuillez corriger l'erreur ci-dessus.", None
|
| 81 |
|
| 82 |
+
# 1. Préparation des données d'entrée
|
| 83 |
input_data = pd.DataFrame({
|
| 84 |
'hr': [hr], 'spo2': [spo2], 'temp': [temp],
|
| 85 |
'acc_mean': [acc_mean], 'gyro_mean': [gyro_mean],
|
| 86 |
'species': [species]
|
| 87 |
})
|
| 88 |
|
| 89 |
+
# 2. Prétraitement
|
| 90 |
numerical_features = input_data.drop('species', axis=1)
|
| 91 |
species_encoded = encoder.transform(input_data[['species']])
|
| 92 |
species_df = pd.DataFrame(species_encoded, columns=encoder.get_feature_names_out(['species']))
|
| 93 |
|
| 94 |
X_final = pd.concat([numerical_features, species_df], axis=1)
|
| 95 |
|
| 96 |
+
# 3. Prédiction
|
| 97 |
prediction = model.predict(X_final)[0]
|
| 98 |
probas = model.predict_proba(X_final)[0]
|
| 99 |
predicted_class = classes[prediction]
|
| 100 |
|
| 101 |
+
# Récupération des messages et chemins audio dans la langue choisie
|
| 102 |
+
messages = MESSAGES.get(langue_choisie, MESSAGES['Français'])
|
| 103 |
+
audio_files = AUDIO_PATHS.get(langue_choisie, AUDIO_PATHS['Français'])
|
| 104 |
+
|
| 105 |
+
# 4. Formatage des Sorties
|
| 106 |
if prediction == 1:
|
| 107 |
+
# Sélectionne le bon chemin audio pour l'alerte
|
| 108 |
+
audio_filename = audio_files['alert']
|
| 109 |
+
audio_path = audio_filename if os.path.exists(audio_filename) else None
|
| 110 |
+
|
| 111 |
+
alert_style = messages['alert']
|
| 112 |
+
action_message = messages['action']
|
| 113 |
else:
|
| 114 |
+
# Sélectionne le bon chemin audio pour l'OK
|
| 115 |
+
audio_filename = audio_files['ok']
|
| 116 |
+
audio_path = audio_filename if os.path.exists(audio_filename) else None
|
| 117 |
+
|
| 118 |
+
alert_style = messages['ok']
|
| 119 |
+
action_message = "" # Pas d'action si OK
|
| 120 |
|
| 121 |
+
# Si le fichier audio n'existe pas, nous utilisons le français par défaut si disponible
|
| 122 |
+
if audio_path is None:
|
| 123 |
+
if prediction == 1 and os.path.exists(AUDIO_PATHS['Français']['alert']):
|
| 124 |
+
audio_path = AUDIO_PATHS['Français']['alert']
|
| 125 |
+
elif prediction == 0 and os.path.exists(AUDIO_PATHS['Français']['ok']):
|
| 126 |
+
audio_path = AUDIO_PATHS['Français']['ok']
|
| 127 |
+
|
| 128 |
+
# Construction du rapport textuel final
|
| 129 |
output_text = f"## {alert_style} \n\n"
|
| 130 |
output_text += f"**Espèce :** {species.capitalize()}\n"
|
| 131 |
output_text += f"**Statut Analysé :** **{predicted_class}**\n\n"
|
|
|
|
| 133 |
output_text += f"**Action Recommandée :** {action_message}\n\n"
|
| 134 |
output_text += f"*(Probabilité d'Alerte : {probas[1]*100:.2f}%)*"
|
| 135 |
|
| 136 |
+
return output_text, audio_path
|
| 137 |
|
| 138 |
|
| 139 |
+
# 3. Création de l'Interface Gradio (avec le composant Langue en premier)
|
| 140 |
iface = gr.Interface(
|
| 141 |
+
fn=predict_health_alert_vocal,
|
| 142 |
inputs=[
|
| 143 |
+
gr.Dropdown(choices=list(MESSAGES.keys()), label="0. Langue de l'Alerte", value="Français"),
|
| 144 |
gr.Dropdown(choices=SPECIES_LIST, label="1. Espèce Animale", value="boeuf"),
|
| 145 |
gr.Slider(minimum=30, maximum=200, value=75, label="2. Rythme Cardiaque (BPM)"),
|
| 146 |
gr.Slider(minimum=80, maximum=100, value=96, label="3. Saturation O₂ (SpO2 %)"),
|
|
|
|
| 149 |
gr.Slider(minimum=0.0, maximum=5.0, value=1.5, label="6. Mouvement (Gyro. Moy.)"),
|
| 150 |
],
|
| 151 |
outputs=[
|
| 152 |
+
gr.Markdown(label="Rapport Textuel d'Alerte"),
|
| 153 |
+
gr.Audio(label="Alerte Vocale (Si disponible dans la langue choisie)"),
|
| 154 |
],
|
| 155 |
+
title="Système d'Alerte de Santé Vétérinaire SmarTrack (Vocal Multi-Langue)",
|
| 156 |
+
description="Validation locale : Le modèle prédit l'état de santé et affiche les alertes dans la langue choisie (le fichier audio est sélectionné en conséquence).",
|
| 157 |
+
examples=[
|
| 158 |
+
['Français', 'boeuf', 55, 96, 38.5, 0.1, 0.1], # Normal
|
| 159 |
+
['Pular (Peul)', 'chat', 170, 95, 38.5, 3.0, 3.0], # Normal
|
| 160 |
+
['Soussou', 'boeuf', 150, 85, 41.0, 0.0, 0.0], # Alerte en Soussou
|
| 161 |
+
]
|
| 162 |
)
|
| 163 |
|
| 164 |
+
# --- LANCEMENT LOCAL ---
|
| 165 |
+
# C'est cette ligne qui fait la différence pour le test local !
|
| 166 |
+
iface.launch(share=True)
|