Détection et identification des maladies des feuilles de plantes avec YOLOv4

La détection précoce des maladies des plantes peut prévenir jusqu'à 40% des pertes annuelles de récoltes. Dans ce guide pratique, nous construisons un système de détection automatisé avec YOLOv4 atteignant 99,99% de précision sur le dataset PlantVillage — chaque étape expliquée de la collecte des données au déploiement. (Aldakheel et al., 2024)
Pourquoi la détection automatisée des maladies des plantes ?
Les maladies des plantes causent des pertes économiques estimées à des milliards de dollars chaque année dans le monde. Les agriculteurs s'appuient traditionnellement sur l'inspection visuelle manuelle — un processus lent et sujet aux erreurs humaines, surtout aux premiers stades de la maladie où les symptômes sont subtils.
Le deep learning offre une solution efficace : un système capable d'analyser des images de feuilles en quelques millisecondes et d'identifier le type de maladie avec une précision surpassant les experts humains.
YOLOv4 (You Only Look Once v4) se distingue des modèles de classification traditionnels par sa capacité à localiser la maladie sur la feuille, pas seulement la classifier — ce qui est extrêmement utile pour évaluer la sévérité de l'infection.
Ce que vous apprendrez
- Configurer l'environnement de développement avec Darknet et CUDA
- Préparer et annoter le dataset PlantVillage
- Configurer et entraîner un modèle YOLOv4 personnalisé
- Évaluer les performances avec mAP et la matrice de confusion
- Utiliser le modèle pour la détection en temps réel
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Python 3.8+ avec
pip - CUDA 11.0+ et cuDNN 8.0+ (pour l'accélération GPU)
- OpenCV 4.5+
- Au moins 6 Go de mémoire GPU (NVIDIA RTX 2060 ou supérieur recommandé)
- Connaissances de base en Python et deep learning
Étape 1 : Configuration de l'environnement
Installation de Darknet
Darknet est le framework original pour exécuter YOLO. Compilons-le avec le support GPU :
# Cloner le dépôt
git clone https://github.com/AlexeyAB/darknet.git
cd darknet
# Activer GPU et OpenCV dans le Makefile
sed -i 's/GPU=0/GPU=1/' Makefile
sed -i 's/CUDNN=0/CUDNN=1/' Makefile
sed -i 's/OPENCV=0/OPENCV=1/' Makefile
# Compiler
make -j$(nproc)Création d'un environnement virtuel Python
python3 -m venv yolov4-env
source yolov4-env/bin/activate
pip install opencv-python numpy matplotlib pillow
pip install torch torchvision # Optionnel — pour l'analyse et l'évaluationAssurez-vous que votre version de CUDA est compatible avec votre version de cuDNN et votre carte graphique. Vérifiez en exécutant nvcc --version et nvidia-smi.
Étape 2 : Le dataset PlantVillage
Vue d'ensemble
Le dataset PlantVillage contient plus de 54 000 images de feuilles de plantes provenant de 14 espèces différentes, couvrant 38 catégories de feuilles saines et malades.
| Statistique | Valeur |
|---|---|
| Total des images | 54 305 |
| Espèces de plantes | 14 |
| Catégories de maladies | 38 |
| Résolution | 256×256 px |
| Format | JPG/PNG |
Téléchargement des données
# Télécharger depuis Kaggle
pip install kaggle
kaggle datasets download -d emmarex/plantdisease
unzip plantdisease.zip -d data/plantvillageStructure des répertoires
data/plantvillage/
├── Apple___Apple_scab/
│ ├── image_001.jpg
│ ├── image_002.jpg
│ └── ...
├── Apple___Black_rot/
├── Apple___Cedar_apple_rust/
├── Apple___healthy/
├── Tomato___Bacterial_spot/
├── Tomato___Early_blight/
├── Tomato___Late_blight/
├── Tomato___healthy/
└── ... (38 répertoires)
Étape 3 : Préparation des données pour YOLOv4
YOLOv4 nécessite un format d'annotation spécifique. Chaque image a besoin d'un fichier .txt accompagnant contenant les coordonnées des boîtes englobantes.
Script de conversion
import os
import cv2
import random
from pathlib import Path
DATA_DIR = "data/plantvillage"
OUTPUT_DIR = "data/yolo_format"
CLASSES_FILE = "data/classes.txt"
# Collecter les classes
classes = sorted(os.listdir(DATA_DIR))
with open(CLASSES_FILE, "w") as f:
for cls in classes:
f.write(f"{cls}\n")
print(f"Nombre de classes : {len(classes)}")
# Convertir chaque image au format YOLO
os.makedirs(f"{OUTPUT_DIR}/images/train", exist_ok=True)
os.makedirs(f"{OUTPUT_DIR}/images/val", exist_ok=True)
os.makedirs(f"{OUTPUT_DIR}/labels/train", exist_ok=True)
os.makedirs(f"{OUTPUT_DIR}/labels/val", exist_ok=True)
all_images = []
for class_idx, class_name in enumerate(classes):
class_dir = os.path.join(DATA_DIR, class_name)
for img_name in os.listdir(class_dir):
if img_name.lower().endswith(('.jpg', '.jpeg', '.png')):
all_images.append((
os.path.join(class_dir, img_name),
class_idx,
img_name
))
# Séparation 80/20 entraînement/validation
random.shuffle(all_images)
split = int(0.8 * len(all_images))
train_set = all_images[:split]
val_set = all_images[split:]
def process_image(img_path, class_idx, img_name, split_name):
"""Convertir une image au format YOLO"""
img = cv2.imread(img_path)
h, w = img.shape[:2]
# Copier l'image
dest = f"{OUTPUT_DIR}/images/{split_name}/{img_name}"
cv2.imwrite(dest, img)
# Créer le fichier d'annotations — la feuille occupe la majeure partie
# Format : class_id center_x center_y width height (normalisé)
label_name = Path(img_name).stem + ".txt"
label_path = f"{OUTPUT_DIR}/labels/{split_name}/{label_name}"
with open(label_path, "w") as f:
# La boîte englobante couvre 90% de l'image
f.write(f"{class_idx} 0.5 0.5 0.9 0.9\n")
print(f"Entraînement : {len(train_set)} | Validation : {len(val_set)}")
for img_path, cls_idx, name in train_set:
process_image(img_path, cls_idx, name, "train")
for img_path, cls_idx, name in val_set:
process_image(img_path, cls_idx, name, "val")Normalisation des images
import numpy as np
import cv2
def normalize_image(img_path):
"""Normaliser les valeurs de pixels de [0, 255] à [0, 1]"""
img = cv2.imread(img_path)
img = img.astype(np.float32) / 255.0
# Redimensionner à 416×416 (taille par défaut de YOLOv4)
img = cv2.resize(img, (416, 416))
return img
# Vérifier une image échantillon
sample = normalize_image("data/yolo_format/images/train/sample.jpg")
print(f"Plage : [{sample.min():.2f}, {sample.max():.2f}]")
print(f"Forme : {sample.shape}") # (416, 416, 3)Étape 4 : Outils d'annotation d'images
Pour de meilleurs résultats avec YOLOv4, il est préférable d'utiliser des boîtes englobantes précises autour des zones malades plutôt que la feuille entière.
Outils d'annotation recommandés
| Outil | Plateforme | Caractéristiques |
|---|---|---|
| LabelImg | Bureau | Gratuit, support direct du format YOLO |
| CVAT | Web | Collaboratif, support vidéo |
| Roboflow | Web | Augmentation automatique, export multi-format |
| VoTT | Bureau | Par Microsoft, support Active Learning |
Utilisation de LabelImg
pip install labelImg
labelImg data/yolo_format/images/train/ data/classes.txtPour des résultats plus précis, dessinez la boîte englobante autour de la zone malade uniquement, pas la feuille entière. Cela aide le modèle à apprendre à distinguer les tissus sains des tissus malades.
Étape 5 : Comprendre l'architecture de YOLOv4
YOLOv4 se compose de trois parties principales :
1. Backbone — CSPDarknet53
Extrait les caractéristiques essentielles de l'image grâce à :
- Connexions Cross-Stage Partial (CSP) : Réduisent la redondance calculatoire
- Fonction d'activation Mish :
f(x) = x × tanh(softplus(x))— plus lisse que ReLU
2. Neck — SPP + PANet
Agrège les caractéristiques de différents niveaux :
- Spatial Pyramid Pooling (SPP) : Élargit le champ réceptif
- Path Aggregation Network (PANet) : Fusionne les caractéristiques de haut en bas et de bas en haut
3. Head — YOLOv3 Head
Produit les prédictions finales à trois échelles différentes (13×13, 26×26, 52×52).
Entrée (416×416×3)
│
▼
CSPDarknet53 (Extraction de caractéristiques)
│
├──► Petite échelle (13×13) — Grands objets
├──► Échelle moyenne (26×26) — Objets moyens
└──► Grande échelle (52×52) — Petits objets
│
▼
SPP + PANet (Agrégation de caractéristiques)
│
▼
Détection à 3 échelles
Étape 6 : Configuration des fichiers d'entraînement
Fichier de données (plant.data)
classes = 38
train = data/train.txt
valid = data/val.txt
names = data/classes.txt
backup = backup/Génération des listes d'images
import glob
# Liste des images d'entraînement
train_images = glob.glob("data/yolo_format/images/train/*.*")
with open("data/train.txt", "w") as f:
f.write("\n".join(train_images))
# Liste des images de validation
val_images = glob.glob("data/yolo_format/images/val/*.*")
with open("data/val.txt", "w") as f:
f.write("\n".join(val_images))
print(f"Images d'entraînement : {len(train_images)}")
print(f"Images de validation : {len(val_images)}")Fichier de configuration (yolov4-plant.cfg)
Copiez le fichier de configuration original et modifiez les paramètres :
cp cfg/yolov4-custom.cfg cfg/yolov4-plant.cfgParamètres clés à modifier :
[net]
batch = 64
subdivisions = 16
width = 416
height = 416
max_batches = 76000 # = nombre_classes × 2000 = 38 × 2000
steps = 60800,68400 # = 80% et 90% de max_batches# Pour chaque couche [yolo] (3 couches)
[yolo]
classes = 38
# Pour chaque couche [convolutional] avant [yolo]
[convolutional]
filters = 129 # = (classes + 5) × 3 = (38 + 5) × 3Erreur courante : Oublier de modifier filters dans les trois couches [convolutional] avant les couches [yolo]. La valeur doit toujours être (classes + 5) × 3.
Étape 7 : Entraînement du modèle
Téléchargement des poids pré-entraînés
wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.conv.137Lancement de l'entraînement
./darknet detector train \
data/plant.data \
cfg/yolov4-plant.cfg \
yolov4.conv.137 \
-map \
-dont_showSuivi de la progression
Darknet génère automatiquement un graphique de perte dans chart.png. Vous pouvez aussi suivre la progression dans le terminal :
CUDA-version: 11080 (11080)
Loading weights from yolov4.conv.137...
Learning Rate: 0.001, Momentum: 0.949, Decay: 0.0005
Iteration: 1000, loss: 2.5, avg loss: 3.1, rate: 0.001
Iteration: 2000, loss: 1.2, avg loss: 1.8, rate: 0.001
...
Iteration: 10000, loss: 0.08, avg loss: 0.15, rate: 0.0001
Le modèle est automatiquement sauvegardé toutes les 1 000 itérations dans le dossier backup/. Si l'entraînement s'arrête, vous pouvez reprendre depuis le dernier point : ./darknet detector train ... backup/yolov4-plant_last.weights
Étape 8 : Évaluation du modèle
Calcul du mAP (Mean Average Precision)
./darknet detector map \
data/plant.data \
cfg/yolov4-plant.cfg \
backup/yolov4-plant_best.weightsRésultats de performance
| Métrique | Valeur |
|---|---|
| mAP@0.5 | 99,99% |
| Précision | 99,99% |
| Rappel | 99,98% |
| F1-Score | 99,99% |
| Vitesse d'inférence | ~30 FPS (RTX 2080) |
Matrice de confusion avec Python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
def evaluate_model(predictions, ground_truths, class_names):
"""Générer et afficher la matrice de confusion"""
cm = confusion_matrix(ground_truths, predictions)
fig, ax = plt.subplots(figsize=(20, 20))
disp = ConfusionMatrixDisplay(cm, display_labels=class_names)
disp.plot(ax=ax, cmap='Greens', xticks_rotation=45)
plt.title("Matrice de confusion — YOLOv4 Plant Disease Detection")
plt.tight_layout()
plt.savefig("confusion_matrix.png", dpi=150)
plt.show()
# Métriques par classe
for i, name in enumerate(class_names):
tp = cm[i, i]
fp = cm[:, i].sum() - tp
fn = cm[i, :].sum() - tp
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
print(f"{name}: Precision={precision:.4f}, Recall={recall:.4f}")Étape 9 : Inférence et détection en temps réel
Détection sur une seule image
./darknet detector test \
data/plant.data \
cfg/yolov4-plant.cfg \
backup/yolov4-plant_best.weights \
data/test/tomato_late_blight.jpg \
-thresh 0.5Détection avec Python et OpenCV
import cv2
import numpy as np
def detect_disease(image_path, config, weights, classes_file, conf_threshold=0.5):
"""Détecter les maladies des feuilles dans une image"""
# Charger le réseau
net = cv2.dnn.readNetFromDarknet(config, weights)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
# Charger les noms de classes
with open(classes_file, "r") as f:
classes = f.read().strip().split("\n")
# Préparer l'image
img = cv2.imread(image_path)
h, w = img.shape[:2]
blob = cv2.dnn.blobFromImage(img, 1/255.0, (416, 416), swapRB=True, crop=False)
net.setInput(blob)
# Inférence
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]
outputs = net.forward(output_layers)
# Traitement des résultats
boxes, confidences, class_ids = [], [], []
for output in outputs:
for detection in output:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > conf_threshold:
center_x = int(detection[0] * w)
center_y = int(detection[1] * h)
bw = int(detection[2] * w)
bh = int(detection[3] * h)
x = int(center_x - bw / 2)
y = int(center_y - bh / 2)
boxes.append([x, y, bw, bh])
confidences.append(float(confidence))
class_ids.append(class_id)
# Suppression des non-maximums (NMS)
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, 0.4)
# Dessiner les résultats
colors = np.random.uniform(0, 255, size=(len(classes), 3))
for i in indices.flatten():
x, y, bw, bh = boxes[i]
label = f"{classes[class_ids[i]]}: {confidences[i]:.2f}"
color = colors[class_ids[i]]
cv2.rectangle(img, (x, y), (x + bw, y + bh), color, 2)
cv2.putText(img, label, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
return img, [(classes[class_ids[i]], confidences[i]) for i in indices.flatten()]
# Utilisation
result_img, detections = detect_disease(
"test_leaf.jpg",
"cfg/yolov4-plant.cfg",
"backup/yolov4-plant_best.weights",
"data/classes.txt"
)
for disease, conf in detections:
print(f" {disease}: {conf:.1%}")
cv2.imwrite("result.jpg", result_img)Détection en temps réel depuis la caméra
import cv2
cap = cv2.VideoCapture(0) # Ou chemin vers un fichier vidéo
while True:
ret, frame = cap.read()
if not ret:
break
# Préparer la frame
blob = cv2.dnn.blobFromImage(frame, 1/255.0, (416, 416), swapRB=True)
net.setInput(blob)
outputs = net.forward(output_layers)
# ... traitement des résultats (même code que ci-dessus)
cv2.imshow("Plant Disease Detection", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()Analyse comparative
L'étude a montré que YOLOv4 surpasse nettement les autres modèles :
| Modèle | Précision | Vitesse (FPS) | Taille |
|---|---|---|---|
| YOLOv4 | 99,99% | 30 | 256 Mo |
| DenseNet-121 | 99,75% | 12 | 33 Mo |
| ResNet-50 | 99,68% | 15 | 98 Mo |
| AlexNet | 97,82% | 45 | 233 Mo |
| VGG-16 | 98,40% | 8 | 528 Mo |
| SVM traditionnel | 89,50% | 2 | - |
YOLOv4 combine la plus haute précision avec une excellente vitesse en temps réel — ce qui en fait le choix optimal pour les applications sur le terrain avec des appareils mobiles et des drones.
Directions futures
- Extension à plus de maladies : Élargir la couverture aux maladies des racines, tiges et fruits
- Intégration de données multimodales : Combiner les images avec des données de capteurs (humidité, température, sol)
- Déploiement en périphérie : Convertir le modèle en TensorRT ou ONNX pour l'exécution sur Jetson Nano ou Raspberry Pi
- Apprentissage continu : Développer des mécanismes pour mettre à jour automatiquement le modèle avec de nouvelles données du terrain
- Interprétabilité du modèle : Utiliser des techniques comme Grad-CAM pour expliquer les prédictions du modèle
Conclusion
Dans ce guide, nous avons construit un système de détection des maladies des feuilles de plantes avec YOLOv4 atteignant une précision de 99,99% sur le dataset PlantVillage. La méthodologie comprend :
- Collecte et préparation des données avec des annotations précises
- Configuration et entraînement d'un modèle YOLOv4 personnalisé
- Évaluation complète utilisant des métriques multiples
- Déploiement pratique pour la détection en temps réel
Cette approche permet aux agriculteurs de détecter les maladies précocement et d'agir rapidement — réduisant les pertes et améliorant concrètement les rendements des cultures.
Références
- Aldakheel EA, Zakariah M, Alabdalall AH. Detection and identification of plant leaf diseases using YOLOv4. Front. Plant Sci. 15:1355941. doi: 10.3389/fpls.2024.1355941. Article complet
- Bochkovskiy A, Wang CY, Liao HYM. YOLOv4: Optimal Speed and Accuracy of Object Detection. arXiv:2004.10934. Article
- Hughes DP, Salathé M. An open access repository of images on plant health to enable the development of mobile disease diagnostics. arXiv:1511.08060. Dataset PlantVillage
Discutez de votre projet avec nous
Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.
Trouvons les meilleures solutions pour vos besoins.
Articles connexes

Créer un Web Scraper Intelligent avec Playwright et l'API Claude en TypeScript
Apprenez à construire un scraper web intelligent qui utilise Playwright pour l'automatisation du navigateur et l'IA Claude pour extraire, nettoyer et structurer les données de n'importe quel site — sans sélecteurs CSS fragiles.

Demarrer avec ALLaM-7B-Instruct-preview
Apprenez a utiliser le modele ALLaM-7B-Instruct-preview avec Python, et comment interagir avec lui depuis JavaScript via une API hebergee (ex: sur Hugging Face Spaces).

Créer un interpréteur de code personnalisé pour les agents LLM
Apprenez à créer un interpréteur de code personnalisé pour les agents de grands modèles de langage (LLM), permettant l'appel dynamique d'outils et l'exécution isolée de code pour une flexibilité et une sécurité accrues.