Arrière-plan
Énoncé du problème
Objectifs
Portée
Chargement et préparation des images
Formatage et conversion d'images
Extraction et sélection de fonctionnalités
Régression logistique
Analyse discriminante linéaire
K Voisins les plus proches
Arbres de décision
Forêt aléatoire
Bayes naïfs
Machine à vecteurs de support
Mesures de précision et de performances
Comparaison des modèles d'apprentissage automatique
Limites et défis
Résumé des réalisations
Contributions et importance
Travaux futurs et améliorations
Les maladies des plantes constituent des menaces importantes pour la productivité agricole, entraînant des pertes de rendement et des difficultés économiques pour les agriculteurs. Une détection rapide et précise des maladies des plantes est cruciale pour mettre en œuvre des stratégies efficaces de gestion des maladies et minimiser les dommages aux cultures. Les méthodes manuelles traditionnelles de diagnostic des maladies peuvent prendre du temps, être subjectives et sujettes aux erreurs. Par conséquent, l’intégration de technologies telles que l’apprentissage automatique et le traitement d’images est apparue comme une approche prometteuse pour automatiser et améliorer la détection des maladies des plantes.
L'objectif principal de ce projet est de développer un système de détection des maladies des plantes utilisant des algorithmes d'apprentissage automatique et des techniques de traitement d'images. Le système vise à classer avec précision les feuilles des plantes comme saines ou malades en analysant les images numériques des feuilles. En automatisant le processus de détection, les agriculteurs et les experts agricoles peuvent rapidement identifier et traiter les maladies des plantes, permettant des interventions rapides et optimisant les pratiques de gestion des cultures.
Les principaux objectifs de ce projet sont les suivants
Développer un système de détection des maladies des plantes robuste et précis
Mettre en œuvre des algorithmes d'apprentissage automatique pour la classification automatisée des feuilles des plantes
Utiliser des techniques de traitement d'image pour extraire les caractéristiques pertinentes des images de feuilles
Évaluer les performances et la précision de différents modèles d'apprentissage automatique
Fournir une interface conviviale pour une interaction facile et intuitive avec le système
Ce projet se concentre sur la détection de maladies végétales spécifiquement dans les feuilles de pommier. L'ensemble de données utilisé pour entraîner et tester les modèles est obtenu à partir de l'ensemble de données Plant-Village, qui contient des images de feuilles de pommier saines et de feuilles affectées par des maladies telles que la tavelure du pommier, la pourriture noire et la rouille du pommier. Le système vise à atteindre une grande précision dans classification des maladies et fournit un outil pratique aux agriculteurs et aux professionnels de l'agriculture pour identifier et gérer efficacement les maladies des plantes. Le projet ne couvre pas la détection des maladies en temps réel sur le terrain ni l'intégration de dispositifs matériels pour l'acquisition d'images.
L'ensemble de données utilisé pour ce système de détection des maladies des plantes comprend des images de feuilles de pommier obtenues à partir de l'ensemble de données Plant-Village. L'ensemble de données est organisé en quatre catégories principales représentant différentes classes d'états des feuilles de pommier Apple___Apple_scab, Apple___Black_rot, Apple___Cedar_apple_rust et Apple___healthy.
Apple___Apple_scab : cette catégorie contient 630 images, dont 598 images affectées à la formation et 32 images à tester.
Apple___Black_rot : l'ensemble de données comprend 621 images dans cette catégorie, avec 589 images allouées à la formation et 32 images aux tests.
Apple___Cedar_apple_rust : l'ensemble de données comprend 275 images de feuilles affectées par la rouille du pommier de cèdre, avec 261 images utilisées pour l'entraînement et 14 images pour les tests.
Apple___healthy : Cette catégorie contient 1645 images de feuilles de pommier saines. Parmi celles-ci, 1 562 images sont destinées à la formation et 83 images sont réservées aux tests.
Les images de formation sont utilisées pour apprendre aux modèles d’apprentissage automatique à reconnaître des modèles et à distinguer les feuilles saines des feuilles malades. Les images de test sont utilisées pour évaluer les performances et la précision des modèles formés sur des données invisibles. En exploitant cet ensemble de données diversifié, le système de détection des maladies des plantes vise à classer avec précision les feuilles de pommier comme saines ou affectées par des maladies telles que la tavelure du pommier, la pourriture noire ou rouille du pommier de cèdre. La composition de l'ensemble de données permet au système d'apprendre d'un large éventail de conditions foliaires et d'améliorer sa capacité à généraliser et à identifier avec précision les maladies des plantes.
Chargement et préparation des images
Dans le cadre du projet de détection de la maladie des feuilles du pommier, la première étape consiste à acquérir un ensemble de données constitué d'images de feuilles de pommier touchées par différentes maladies. Ces images sont ensuite chargées dans le système pour les rendre accessibles pour un traitement ultérieur. De plus, les images sont préparées en effectuant les ajustements nécessaires tels que les redimensionner à une résolution cohérente, recadrer les parties inutiles ou normaliser la distribution des couleurs. Formatage et conversion de l'image Une fois les images de feuilles de pommier chargées, elles doivent être formatées et converties pour garantir compatibilité avec les étapes ultérieures du projet. Cela implique de normaliser le format des images en les convertissant en un type de fichier spécifique comme JPEG ou PNG. De plus, des ajustements peuvent être apportés à la résolution de l'espace colorimétrique ou à d'autres attributs de l'image pour garantir la cohérence et faciliter une analyse précise.
Extraction et sélection de fonctionnalités
L’extraction de caractéristiques est une étape cruciale dans la détection des maladies des feuilles de pommier. Diverses techniques sont utilisées pour extraire les caractéristiques pertinentes des images de feuilles. Ces techniques comprennent l'analyse de la texture pour capturer les motifs texturaux associés aux maladies, l'examen de la couleur pour identifier les variations liées à des maladies spécifiques et l'étude de la forme pour détecter les irrégularités dans la morphologie des feuilles. En extrayant ces caractéristiques distinctives, les algorithmes d’apprentissage automatique ultérieurs peuvent différencier efficacement les feuilles de pommier saines et malades.
Sélection des fonctionnalités
Cette étape consiste à choisir un sous-ensemble de fonctionnalités extraites en fonction de leur pertinence et de leur pouvoir discriminant. La sélection de fonctionnalités permet de réduire la dimensionnalité de l'ensemble de données en éliminant le bruit ou les informations redondantes. En sélectionnant les fonctionnalités les plus informatives, l'efficacité et la précision du modèle de détection des maladies peuvent être améliorées.
Le projet de détection de la maladie des feuilles du pommier utilise une gamme d’algorithmes d’apprentissage automatique pour développer un modèle efficace de classification des maladies. Les algorithmes suivants sont utilisés
Régression logistique : la régression logistique est utilisée pour prédire la probabilité qu'une feuille de pommier soit saine ou malade en fonction des caractéristiques extraites.
Analyse discriminante linéaire : l'analyse discriminante linéaire aide à classer les feuilles de pommier en trouvant une combinaison linéaire de caractéristiques qui sépare au mieux les échantillons sains et malades.
K Nearest Neighbours (KNN) : K Nearest Neighbours classe les feuilles de pommier en comparant leurs caractéristiques à celles des voisins les plus proches dans l'espace des caractéristiques.
Arbres de décision : les arbres de décision utilisent une série de conditions if-else pour classer les échantillons en fonction de leurs caractéristiques et de leurs relations hiérarchiques.
Random Forest : Random Forest est une méthode d'apprentissage d'ensemble qui combine plusieurs arbres de décision pour améliorer la précision de la classification.
Naïve Bayes : Naïve Bayes est un algorithme probabiliste qui calcule la probabilité qu'une feuille de pommier appartienne à une classe de maladies particulière.
Machine à vecteurs de support (SVM) : Support Vector Machine construit des hyperplans dans un espace de fonctionnalités de grande dimension pour classer les feuilles de pommier
Après avoir sélectionné les algorithmes d'apprentissage automatique, les modèles sont formés à l'aide d'un ensemble de données étiqueté composé d'images de feuilles de pommier avec les étiquettes de maladie correspondantes. Les modèles apprennent à reconnaître des modèles et des relations entre les caractéristiques et les classes de maladies au cours de cette phase de formation. Pour garantir la fiabilité et la généralisation des modèles, un processus de validation est effectué. Les modèles formés sont évalués à l'aide d'un ensemble de données de validation distinct qui n'a pas été utilisé pendant la formation. Cela permet d'évaluer la capacité des modèles à classer avec précision des échantillons de feuilles de pommier invisibles.
Une fois les modèles formés et validés, ils sont testés sur un ensemble de données de test distinct contenant de nouvelles images de feuilles de pommier inédites. Les modèles prédisent la classe de maladie pour chaque échantillon et des mesures d'évaluation des performances telles que l'exactitude, la précision, le rappel et le score F1 sont calculées pour mesurer l'efficacité des modèles dans la détection des maladies.
Dans [1] :
# -----------------------------------# EXTRACTION GLOBALE DE FONCTIONNALITÉS# --------- ------------------------------------de sklearn.preprocessing import LabelEncoderfrom sklearn.preprocessing import MinMaxScalerimport numpy as npimport mahotasimport cvimport osimport h5py# ----- ---------------# paramètres-réglables# --------------------images_per_class = 800fixed_size = tuple(( 500 , 500 ))train_path = "../dataset/train"test_path = "../dataset/test"h5_train_features = "../embeddings/features/features.h5"h5_train_labels = "../embeddings/labels/labels.h5" bins = 8##### Conversion BGR en RVBDans [2]:# Conversion de chaque image en RVB à partir du format BGR rgb_bgr(image):rgb_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)return rgb_img##### Conversion RVB en HSV (Hue Saturation Value) Dans [3]:# Conversion au format d'image HSV à partir de RGBdef bgr_hsv(rgb_img) : hsv_img = cv2.cvtColor(rgb_img, cv2.COLOR_RGB2HSV)return hsv_img##### Image SegmentationIn [4]:# pour l'extraction des couleurs vertes et brunesdef img_segmentation(rgb_img, hsv_img):lower_green = np.array([ 25 , 0 , 20 ])upper_green = np. tableau([ 100 , 255 , 255 ])healthy_mask = cv2.inRange(hsv_img, lower_green, upper_green)result = cv2.bitwise_and(rgb_img, rgb_img, mask=healthy_mask)lower_brown = np.array([ 10 , 0 , 10 ])upper_brown = np.array([ 30 , 255 , 255 ])disease_mask = cv2.inRange(hsv_img, lower_brown, upper_brown)disease_result = cv2.bitwise_and(rgb_img, rgb_img, mask=disease_mask)final_mask = healthy_mask + Disease_maskfinal_result = cv2.bitwise_and(rgb_img, rgb_img, mask=final_mask)return final_result##### Détermination des descripteurs de fonctionnalités###### 1. Hu MomentsIn [5]:# feature-descriptor-1: Hu Momentsdef fd_hu_moments(image):image = cv2.cvtColor(image , cv2.COLOR_BGR2GRAY)fonctionnalité = cv2.HuMoments(cv2.moments(image)).flatten()return feature###### 2. Haralick TexturesIn [6]:# feature-descriptor-2 : Haralick Texturedef fd_haralick(image):gray = cv2.cvtColor (image, cv2.COLOR_BGR2GRAY)haralick = mahotas.features.haralick(gray).mean(axis= 0 )return haralick###### 3. HIstogramme de couleurDans [7]:# feature-descriptor-3 : Histogramme de couleurdef fd_histogram(image, masque=Aucun) :image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)hist = cv2 .calcHist( [image], [ 0 , 1 , 2 ], Aucun, [bacs, bacs, bacs], [ 0 , 256 , 0 , 256 , 0 ,256 ] )cv2.normalize(hist, hist)return hist.flatten()##### Chargement de l'ensemble de données de formationDans [8] :# récupérez les étiquettes de formationtrain_labels = os.listdir(train_path)# triez les étiquettes de formationtrain_labels.sort() print(train_labels)# listes vides pour contenir les vecteurs de caractéristiques et les étiquettesglobal_features = []labels = [] ['Apple___Apple_scab', 'Apple___Black_rot', 'Apple___Cedar_apple_rust', 'Apple___healthy']##### Génération des fonctionnalités et des intégrations d'étiquettes à partir de l'ensemble de données Dans [9] :# boucle sur les sous-dossiers de données d'entraînement pour training_name dans train_labels :# join le chemin des données de formation et le dossier de formation de chaque espèceimg_dir_path = os.path.join(train_path, training_name)# récupère l'étiquette de formation actuellecurrent_label = training_name# boucle sur les images de chaque sous-dossier pour img dans os.listdir(img_dir_path):# récupère le fichier image namefile = os.path.join( img_dir_path, img)# lire l'image et la redimensionner à une taille fixeimage = cv2.imread(file)image = cv2.resize(image, fixed_size)# Fonction d'exécution bit par bitRGB_BGR = rgb_bgr(image)BGR_HSV = bgr_hsv(RGB_BGR)IMG_SEGMENT = img_segmentation(RGB_BGR, BGR_HSV)# Appel à des descripteurs de fonctionnalités globalesfv_hu_moments = fd_hu_moments(IMG_SEGMENT)fv_haralick = fd_haralick(IMG_SEGMENT)fv_histogram = fd_histogram(IMG_SEGMENT)# Concaténer les fonctionnalités globalesglobal_feature = np.hstack([fv_histogram, fv_haralick,fv_hu_moments])# mettre à jour la liste des étiquettes et des vecteurs de fonctionnalitéslabels.append(current_label)global_features.append(global_feature)print(" [STATUT] traité dossier : {}".format(current_label))print("[STATUS] extraction globale des fonctionnalités terminée...") [STATUS] dossier traité : Apple___Apple_scab[STATUS] dossier traité : Apple___Black_rot[STATUS] dossier traité : Apple___Cedar_apple_rust[STATUS] dossier traité : Apple___healthy[STATUS] terminé l'extraction des fonctionnalités globales... Dans [10] : # print(global_features)Dans [ 41]:# obtenir la taille globale du vecteur de caractéristiquesprint("[STATUS] vecteur de caractéristiques taille{}".format(np.array(global_features).shape)) [STATUS] taille du vecteur de fonctionnalité (3010, 532)Dans [12] :# obtenez la taille globale de l'étiquette de formation# print(labels)print("[STATUS] étiquettes de formation {}".format(np.array(labels).shape )) [STATUT] Étiquettes de formation (3010,)
Étiquette Valeur codéeApple___Apple_scab 0Apple___Black_rot 1Apple___Cedar_apple_rust 2Apple___healthy 3
Dans [13] :
targetNames = np.unique(labels) le = LabelEncoder() target = le.fit_transform(labels) print(targetNames) print("[STATUS] étiquettes de formation encodées...") ['Apple___Apple_scab' 'Apple___Black_rot' 'Apple___Cedar_apple_rust' ' Apple___healthy'] [STATUS] étiquettes de formation codées...
Dans [14] :
à partir de sklearn.preprocessing import MinMaxScalerscaler = MinMaxScaler(feature_range=( 0 , 1 ))rescaled_features = scaler.fit_transform(global_features)print("[STATUS] vecteur de fonctionnalités normalisé...")rescaled_features[STATUS] vecteur de fonctionnalités normalisé...Out [14]:tableau([[0.8974175 , 0,03450962, 0,01845123, ..., 0,02027887, 0,12693291,0,96573218], [0,89815922, 0,13025558, 0,02774864, ..., 0,02027767, 0,12692423,0,96573354], [0,56777027, 0, 0,01540143, ..., 0,02027886, 0,12693269,0,96573218], ..., [0,95697685, 0,01228793, 0,00548476, ..., 0,02027886, 0,12693346,0,96573218], [0,97704002, 0,10614054, 0,03136325, ..., 0,02027885, 0,12692424,0,96573217], [0.95214074, 0.03819411, 0.03671892, ..., 0.02027886, 0.12692996,0.96573217]])print("[ÉTAT] étiquettes cibles : {}".format(target))print("[ÉTAT] forme des étiquettes cibles : {}" .format(cible.forme)) [STATUT] étiquettes cibles : [0 0 0 ... 3 3 3] Forme des étiquettes cibles [STATUT] : (3010,)
un. Caractéristiques
h5f_data = h5py.File(h5_train_features, "w")h5f_data.create_dataset("dataset_1", data=np.array(rescaled_features))Out[16]:
h5f_label = h5py.File(h5_train_labels, "w")h5f_label.create_dataset("dataset_1", data=np.array(target))Out[17]:Dans [43]:h5f_data.close()h5f_label.close()
# -----------------------------------# FORMATION DE NOTRE MODÈLE# --------- ------------------------------------importer h5pyimport numpy en tant que npimport osimport cvimport warnsfrom matplotlib importer pyplotfrom sklearn.model_selection importer train_test_split, cross_val_scorefrom sklearn.model_selection importer KFold, StratifiedKFolddepuis sklearn.metrics importent confusion_matrix, precision_score, classification_report depuis sklearn.linear_model importer LogisticRegression depuis sklearn.tree importer DecisionTreeClassifier depuis sklearn.ensemble importer RandomForestClassifier depuis sklearn.neighbors importer KNeighborsClassifier depuis sklearn.discriminant_analysis importer LinearDiscriminantAnalysis depuis sklearn.naive_bayes importer GaussianNB depuis sklearn.svm importer SVCimport joblibwarnings.filterwarnings("ignore")# --------------------# paramètres-tunables# --------------- -----num_trees = 100test_size = 0.seed = 9scoring = "accuracy"# récupère les étiquettes de formationtrain_labels = os.listdir(train_path)# trie les étiquettes de formationtrain_labels.sort()sinon os.path.exists(test_path):os.makedirs(test_path)# créer tous les modèles d'apprentissage automatiquemodels = []models.append(("LR", LogisticRegression(random_state=seed)))models.append(("LDA" , LinearDiscriminantAnalysis()))models.append(("KNN", KNeighborsClassifier()))models.append(("DTC", DecisionTreeClassifier(random_state=seed)))models.append(("RF", RandomForestClassifier(n_estimators=num_trees,random_state=seed)))models.append(("NB", GaussianNB()))models.append(("SVM ", SVC(random_state=seed)))# variables pour contenir les résultats et les nomsresults = []names = []# importer le vecteur de fonctionnalités et les étiquettes entraînéesh5f_data = h5py.File(h5_train_features, "r")h5f_label = h5py.File(h5_train_labels, "r")global_features_string = h5f_data["dataset_1"]global_labels_string = h5f_label["dataset_1"]global_features = np.array(global_features_string)global_labels = np.array(global_labels_string)h5f_data.close()h5f_label.close()# vérifier la forme du vecteur de caractéristiques et labelsprint("[STATUS] caractéristiques forme : {}".format(global_features. shape))print("[STATUT] étiquettes forme : {}".format(global_labels.shape))print("[STATUS] la formation a commencé...")print(global_labels, len(global_labels), len(global_features)) Forme des caractéristiques de [STATUT] : (3010, 532) Forme des étiquettes [STATUT] : (3010,) La formation [STATUT] a commencé... [0 0 0 ... 3 3 3] 3010 3010
Dans [38] :
(trainDataGlobal, testDataGlobal, trainLabelsGlobal, testLabelsGlobal, ) = train_test_split(np.array(global_features), np.array(global_labels),test_size=test_size, random_state=seed)print("[STATUS] train divisé et données de test...")print("Données du train : {} ".format(trainDataGlobal.shape))print("Données de test : {}".format(testDataGlobal.shape)) [STATUT] données de train et de test divisées...Données de train : (2408, 532)Données de test : (602, 532)Dans [40]:trainDataGlobalOut[40]:array([[9.47066972e-01, 1.97577832e-02 , 5.34481987e-04, ...,2.02788613e-02, 1.26936845e-01, 9.65732178e-01], [9.67673181e-01, 4.20456024e-02, 5.76285634e-02, ...,2.02788294e-02, 1.26933581e-01, 9.65732217e-01], [9.84705756e-01, 2.97800312e-02, 1.34500344e-02, ...,2.02788553e-02, 1.26941878e-01, 9.65732187e-01], ..., [8.64347882e-01, 5.89053245e-02, 4.27430333e-02, ...,2.02791643e-02, 1.26961451e-01, 9.65733689e-01], [9.85818416e-01, 1.47428536e-03, 3.35008392e-03, ...,2.02767694e-02, 1.26792776e-01, 9.65732951e-01], [9.93152188e-01, 1.31020292e-03, 8.50637768e-04, ...,2.02910354e-02, 1.27475382e-01, 9.65721108e-01]])
Dans [22] :
pour le nom, le modèle dans les modèles :kfold = KFold(n_splits= 10 )cv_results = cross_val_score(model, trainDataGlobal, trainLabelsGlobal, cv=kfold,scoring=scoring)results.append(cv_results)names.append(name)msg = "%s : %f (%f)" % (nom, cv_results.mean(), cv_results.std())print(msg)LR : 0,900346 (0,020452)LDA : 0,892038 (0,017931)KNN : 0,884978 (0,019588)CART : 0,886210 (0,014771)RF : 0,967191 (0,012676)NB : 0,839293 (0,014065)SVM : 0,885813 (0,021190)
Dans [23] :
fig = pyplot.figure()fig.suptitle("Comparaison des algorithmes d'apprentissage automatique")ax = fig.add_subplot( 111 )pyplot.boxplot(results)ax.set_xticklabels(names)pyplot.show()
À partir du résultat ci-dessus, nous pouvons voir que le modèle Random Forest Classifier a la précision la plus élevée de 96,7 % et que le modèle gaussien NB a la précision la plus faible de 83,9 %.