mvp_proj_CS / app.py
Fatimasane26's picture
Update app.py
3019bf5 verified
Raw
History Blame Contribute Delete
15.8 kB
import gradio as gr
import torch
import torch.nn as nn
from torchvision import transforms, models
from PIL import Image
DEVICE = torch.device("cpu")
MODEL_PATH = "plant_disease_mvp.pth"
TREATMENTS = {
"Apple___Apple_scab": ["Appliquer un fongicide à base de captane ou myclobutanil", "Ramasser et détruire les feuilles tombées", "Tailler pour améliorer la circulation d'air"],
"Apple___Black_rot": ["Tailler et brûler les branches infectées", "Appliquer un fongicide à base de cuivre au printemps", "Désinfecter les outils de taille"],
"Apple___Cedar_apple_rust": ["Utiliser un fongicide à base de myclobutanil", "Éviter de planter près des cèdres", "Traiter dès l'apparition des premiers symptômes"],
"Apple___healthy": ["Plante saine — aucun traitement nécessaire", "Maintenir les bonnes pratiques d'irrigation", "Surveiller régulièrement les feuilles"],
"Blueberry___healthy": ["Plante saine — aucun traitement nécessaire", "Maintenir un bon drainage du sol"],
"Cherry_(including_sour)___Powdery_mildew": ["Appliquer du soufre en poudre ou fongicide soufré", "Améliorer la circulation de l'air autour de l'arbre", "Éviter l'excès d'azote dans les engrais"],
"Cherry_(including_sour)___healthy": ["Plante saine — aucun traitement nécessaire", "Continuer les bonnes pratiques agricoles"],
"Corn_(maize)___Cercospora_leaf_spot Gray_leaf_spot": ["Utiliser des variétés résistantes à la maladie", "Appliquer un fongicide à base de strobilurine", "Pratiquer la rotation des cultures"],
"Corn_(maize)___Common_rust_": ["Appliquer un fongicide préventif dès les premiers signes", "Utiliser des semences résistantes", "Détruire les résidus de récolte infectés"],
"Corn_(maize)___Northern_Leaf_Blight": ["Appliquer un fongicide systémique (propiconazole)", "Pratiquer la rotation des cultures", "Utiliser des hybrides résistants"],
"Corn_(maize)___healthy": ["Plante saine — aucun traitement nécessaire", "Continuer les bonnes pratiques agricoles"],
"Grape___Black_rot": ["Tailler les parties infectées et les brûler", "Appliquer un fongicide à base de mancozèbe", "Éliminer les baies momifiées sur la vigne"],
"Grape___Esca_(Black_Measles)": ["Tailler les bois infectés en période sèche", "Protéger les plaies avec un produit cicatrisant", "Éviter le stress hydrique de la vigne"],
"Grape___Leaf_blight_(Isariopsis_Leaf_Spot)": ["Appliquer un fongicide cuivrique", "Améliorer la ventilation du vignoble", "Éliminer les feuilles tombées infectées"],
"Grape___healthy": ["Plante saine — aucun traitement nécessaire", "Continuer les bonnes pratiques viticoles"],
"Orange___Haunglongbing_(Citrus_greening)": ["Maladie grave sans traitement curatif", "Arracher et détruire les arbres infectés immédiatement", "Contrôler le psylle asiatique (insecticide)"],
"Peach___Bacterial_spot": ["Appliquer un bactéricide à base de cuivre", "Éviter les blessures sur les fruits et rameaux", "Tailler en période sèche et désinfecter les outils"],
"Peach___healthy": ["Plante saine — aucun traitement nécessaire", "Continuer les bonnes pratiques agricoles"],
"Pepper,_bell___Bacterial_spot": ["Appliquer un bactéricide cuivrique dès les premiers symptômes", "Éviter l'irrigation par aspersion", "Éliminer et détruire les plants très infectés"],
"Pepper,_bell___healthy": ["Plante saine — aucun traitement nécessaire", "Maintenir un bon drainage du sol"],
"Potato___Early_blight": ["Appliquer un fongicide à base de chlorothalonil", "Pratiquer la rotation des cultures (3 ans minimum)", "Éliminer les feuilles infectées du champ"],
"Potato___Late_blight": ["Traitement urgent avec fongicide systémique (métalaxyl)", "Détruire immédiatement les plants très infectés", "Ne pas composter les tubercules infectés"],
"Potato___healthy": ["Plante saine — aucun traitement nécessaire", "Surveiller régulièrement pour détecter tôt"],
"Raspberry___healthy": ["Plante saine — aucun traitement nécessaire", "Continuer les bonnes pratiques agricoles"],
"Soybean___healthy": ["Plante saine — aucun traitement nécessaire", "Maintenir la rotation des cultures"],
"Squash___Powdery_mildew": ["Appliquer du bicarbonate de soude dilué (1%) en spray", "Utiliser un fongicide soufré si sévère", "Améliorer la circulation d'air entre les plants"],
"Strawberry___Leaf_scorch": ["Appliquer un fongicide à base de captan", "Éviter l'excès d'humidité sur les feuilles", "Éliminer les feuilles infectées du champ"],
"Strawberry___healthy": ["Plante saine — aucun traitement nécessaire", "Continuer les bonnes pratiques agricoles"],
"Tomato___Bacterial_spot": ["Appliquer un bactéricide à base de cuivre", "Éviter l'arrosage sur les feuilles", "Éliminer et brûler les plants très infectés"],
"Tomato___Early_blight": ["Appliquer un fongicide à base de chlorothalonil", "Éliminer les feuilles infectées en bas de la plante", "Éviter l'excès d'humidité au niveau du sol"],
"Tomato___Late_blight": ["Traitement urgent avec fongicide systémique", "Détruire les plants très infectés immédiatement", "Éviter l'irrigation par aspersion"],
"Tomato___Leaf_Mold": ["Améliorer la ventilation de la serre ou du champ", "Appliquer un fongicide à base de mancozèbe", "Réduire l'humidité relative autour des plants"],
"Tomato___Septoria_leaf_spot": ["Appliquer un fongicide (chlorothalonil ou cuivre)", "Éviter d'arroser le feuillage directement", "Éliminer les feuilles infectées régulièrement"],
"Tomato___Spider_mites Two-spotted_spider_mite": ["Appliquer un acaricide homologué", "Augmenter l'humidité autour des plants", "Introduire des prédateurs naturels (acariens utiles)"],
"Tomato___Target_Spot": ["Appliquer un fongicide systémique (azoxystrobine)", "Éliminer les débris végétaux du sol", "Pratiquer la rotation des cultures"],
"Tomato___Tomato_Yellow_Leaf_Curl_Virus": ["Maladie virale sans traitement curatif", "Contrôler les aleurodes (mouches blanches) vecteurs", "Arracher et détruire les plants infectés"],
"Tomato___Tomato_mosaic_virus": ["Pas de traitement curatif disponible", "Désinfecter tous les outils de travail (eau de javel)", "Arracher et détruire les plants infectés"],
"Tomato___healthy": ["Plante saine — aucun traitement nécessaire", "Continuer les bonnes pratiques agricoles", "Surveiller régulièrement les feuilles"],
}
def load_model(model_path):
checkpoint = torch.load(model_path, map_location=DEVICE)
class_names = checkpoint["class_names"]
num_classes = checkpoint["num_classes"]
model = models.mobilenet_v2(pretrained=False)
model.classifier = nn.Sequential(
nn.Dropout(p=0.3),
nn.Linear(model.last_channel, num_classes)
)
model.load_state_dict(checkpoint["model_state_dict"])
model.eval()
return model, class_names
model, CLASS_NAMES = load_model(MODEL_PATH)
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
def predict(image):
if image is None:
return "", "", ""
img_tensor = transform(image).unsqueeze(0).to(DEVICE)
with torch.no_grad():
outputs = model(img_tensor)
probs = torch.softmax(outputs, dim=1)
conf, idx = probs.max(1)
pred_class = CLASS_NAMES[idx.item()]
confidence = conf.item() * 100
parts = pred_class.split("___")
plant = parts[0].replace("_", " ")
disease = parts[1].replace("_", " ") if len(parts) > 1 else "Inconnue"
is_healthy = "healthy" in pred_class.lower()
status = "SAINE" if is_healthy else "MALADE"
color = "#2d6a4f" if is_healthy else "#c1121f"
bg_color = "#e8f5e9" if is_healthy else "#fdecea"
icon = "✅" if is_healthy else "⚠️"
conf_bar = int(confidence)
result_html = f"""
<div style="font-family:'Inter',sans-serif; padding:20px; border-radius:14px;
background:{bg_color}; border-left:5px solid {color}; margin-bottom:6px;">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:14px;">
<div style="font-size:1.5em; font-weight:800; color:{color};">{icon} Plante {status}</div>
</div>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:14px;">
<div style="background:white; border-radius:10px; padding:12px;">
<div style="font-size:0.75em; color:#888; text-transform:uppercase; letter-spacing:1px; margin-bottom:4px;">🌱 Plante</div>
<div style="font-size:1em; font-weight:700; color:#1b4332;">{plant}</div>
</div>
<div style="background:white; border-radius:10px; padding:12px;">
<div style="font-size:0.75em; color:#888; text-transform:uppercase; letter-spacing:1px; margin-bottom:4px;">🦠 Maladie</div>
<div style="font-size:1em; font-weight:700; color:{color};">{disease}</div>
</div>
</div>
<div style="background:white; border-radius:10px; padding:12px;">
<div style="display:flex; justify-content:space-between; margin-bottom:6px;">
<span style="font-size:0.8em; color:#888;">📊 Niveau de confiance</span>
<span style="font-size:0.9em; font-weight:700; color:{color};">{confidence:.1f}%</span>
</div>
<div style="background:#e0e0e0; border-radius:20px; height:10px;">
<div style="background:{color}; width:{conf_bar}%; height:10px; border-radius:20px; transition:width 0.5s;"></div>
</div>
</div>
</div>
"""
steps = TREATMENTS.get(pred_class, ["Consulter un agronome local."])
steps_html = ""
for i, step in enumerate(steps):
urgent = "urgent" in step.lower() or "grave" in step.lower() or "arracher" in step.lower()
step_bg = "#fff3cd" if urgent else "white"
steps_html += f"""
<div style="display:flex; align-items:flex-start; gap:12px; padding:10px;
background:{step_bg}; border-radius:8px; margin-bottom:8px;">
<div style="background:#2d6a4f; color:white; border-radius:50%; min-width:26px; height:26px;
display:flex; align-items:center; justify-content:center; font-size:0.8em; font-weight:700;">{i+1}</div>
<div style="color:#1b4332; font-size:0.92em; line-height:1.5;">{step}</div>
</div>"""
treatment_html = f"""
<div style="font-family:'Inter',sans-serif; padding:20px; border-radius:14px;
background:#f8fffe; border:1.5px solid #95d5b2;">
<div style="font-size:1em; font-weight:700; color:#1b4332; margin-bottom:14px;">
💊 Protocole de Traitement
</div>
{steps_html}
</div>
"""
top3_probs, top3_idx = probs.topk(3, dim=1)
top3_rows = ""
medals = ["🥇", "🥈", "🥉"]
colors = ["#2d6a4f", "#74c69d", "#b7e4c7"]
for i in range(3):
cls = CLASS_NAMES[top3_idx[0][i].item()]
prob = top3_probs[0][i].item() * 100
p, d = cls.split("___") if "___" in cls else (cls, "—")
p = p.replace("_", " ")
d = d.replace("_", " ")
w = int(prob)
bold = "700" if i == 0 else "400"
top3_rows += f"""
<div style="margin-bottom:14px;">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:5px;">
<span style="font-size:0.88em; color:#1b4332; font-weight:{bold};">
{medals[i]} {p}{d}
</span>
<span style="font-size:0.88em; font-weight:700; color:{colors[i]};">{prob:.1f}%</span>
</div>
<div style="background:#e8f5e9; border-radius:20px; height:8px;">
<div style="background:{colors[i]}; width:{w}%; height:8px; border-radius:20px;"></div>
</div>
</div>"""
top3_html = f"""
<div style="font-family:'Inter',sans-serif; padding:20px; border-radius:14px;
background:#f8fffe; border:1.5px solid #95d5b2;">
<div style="font-size:1em; font-weight:700; color:#1b4332; margin-bottom:14px;">
📊 Top 3 Prédictions
</div>
{top3_rows}
</div>
"""
return result_html, treatment_html, top3_html
css = """
body, .gradio-container { background: #f0f7f4 !important; }
.gradio-container { max-width: 1050px !important; margin: auto !important; }
footer { display: none !important; }
button.primary { background: #2d6a4f !important; border-radius: 10px !important; font-size: 1em !important; }
"""
with gr.Blocks(css=css, theme=gr.themes.Base(
primary_hue="green",
font=gr.themes.GoogleFont("Inter")
)) as app:
gr.HTML("""
<div style="text-align:center; padding:32px 20px 16px; font-family:'Inter',sans-serif;">
<div style="font-size:2.8em; margin-bottom:10px;">🌿</div>
<h1 style="color:#1b4332; font-size:1.9em; font-weight:800; margin:0 0 8px;">
Détection des Maladies des Plantes
</h1>
<p style="color:#52b788; font-size:0.95em; margin:0;">
Diagnostic intelligent par IA · MobileNetV2 · 96.59% de précision sur 38 classes
</p>
<div style="width:50px; height:3px; background:#2d6a4f; margin:14px auto 0; border-radius:2px;"></div>
</div>
""")
with gr.Row():
with gr.Column(scale=1):
gr.HTML('<p style="font-family:Inter; font-size:0.82em; color:#666; text-transform:uppercase; letter-spacing:1px; margin-bottom:6px;">📸 Image de la plante</p>')
image_input = gr.Image(type="pil", label="", height=300)
predict_btn = gr.Button("🔍 Analyser la plante", variant="primary", size="lg")
gr.HTML('<p style="font-family:Inter; font-size:0.75em; color:#aaa; text-align:center; margin-top:6px;">Formats acceptés : JPG · PNG · WEBP</p>')
with gr.Column(scale=1):
result_out = gr.HTML()
treatment_out = gr.HTML()
top3_out = gr.HTML()
predict_btn.click(fn=predict, inputs=image_input, outputs=[result_out, treatment_out, top3_out])
gr.HTML("""
<div style="text-align:center; padding:18px; font-family:'Inter',sans-serif;
font-size:0.76em; color:#aaa; border-top:1px solid #e0e0e0; margin-top:10px;">
🌍 Projet IA · AIMS Sénégal · 2025 &nbsp;·&nbsp; MobileNetV2 · PyTorch · Gradio
&nbsp;·&nbsp; Dataset : PlantVillage (54 000+ images · 38 classes)
</div>
""")
app.launch()