Installing Required dependencies

  • Creating separate conda environment for the project:

    conda create -n plant python=3.7 -y

  • Activating the conda environment:

    conda activate plant

  • Installing the required dependencies:

    pip install sklearn

    pip install pandas

    pip install numpy

    pip install matplotlib

    pip install tensorflow-gpu For GPU Only

    pip install opencv-python

  • You can find data for this project in this repository and name is dataset.rar

  • Now importing required modules.

import numpy as np
import pandas as pd
import tensorflow
import matplotlib.pyplot as plt
from matplotlib.image import imread
import cv2
import os
import random
from os import listdir
from PIL import Image
from sklearn.preprocessing import label_binarize,  LabelBinarizer
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import img_to_array, array_to_img
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.layers import Activation, Flatten, Dropout, Dense
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import model_from_json
from tensorflow.keras.utils import to_categorical 
  • Following class is used to keep the log of every pipeline step.

    Visualization Log

    log11

    Training Log

log12

from datetime import datetime
class App_Logger:
    def __init__(self):
        pass

    def log(self, file_object, log_message):
        self.now = datetime.now()
        self.date = self.now.date()
        self.current_time = self.now.strftime("%H:%M:%S")
        file_object.write(
            str(self.date) + "/" + str(self.current_time) + "\t\t" + log_message +"\n")
  • Following Visualize class is used to display the sample images we have for training.
class Visualize:
    def __init__(self):
        self.log_writer = App_Logger()
        self.file_object = open("./Visualization_Logs/VisualizationLog.txt", 'a+')
    def plot(self, path):
        self.path = path
        self.log_writer.log(self.file_object, 'Entered the plot method of the Visualize class')
        plt.figure(figsize=(12, 12))
        
        for i in range(1, 17):
            plt.subplot(4, 4, i)
            plt.tight_layout()
            self.rand_img = imread(self.path +'/'+ random.choice(sorted(os.listdir(self.path))))
            plt.imshow(self.rand_img)
            plt.xlabel(self.rand_img.shape[1], fontsize = 10) # Width of image
            plt.ylabel(self.rand_img.shape[0], fontsize = 10) # height of image
        plt.show()
  • Plotting some sample images of Corn_Common_rust, Potato_Early_blight and Tomato_Bacterial_spot.
path = "./dataset/Corn_Common_rust"
viz = Visualize()
viz.plot(path)
path = "./dataset/Potato_Early_blight"
viz = Visualize()
viz.plot(path)
path = "./dataset/Tomato_Bacterial_spot"
viz = Visualize()
viz.plot(path)
  • This Preprocessor class is used to prepare the data for training and it has one method:

    • convert_image_to_array

      This method is used to read image, resize it and converting it to array.

class Preprocessor:
    def __init__(self):
        self.log_writer = App_Logger()
        self.file_object = open("./Processing_Logs/ProcessingLog.txt", 'a+')
        
    def convert_image_to_array(self, image_dir):
        self.log_writer.log(self.file_object, "Entered the convert_image_to_array method of the Preprocessor class.")
        self.image_dir = image_dir
        
        try:
            self.log_writer.log(self.file_object, "Reading images.")
            self.image = cv2.imread(self.image_dir)
            if self.image is not None:
                self.image = cv2.resize(self.image, (256, 256))
                return img_to_array(self.image)
                self.log_writer.log(self.file_object, "Image to array conversion Successful.")
            else:
                return np.array([])
        except Exception as e:
            self.log_writer.log(self.file_object, "Exception occured in convert_image_to_array of the Preprocessor class. Exception message:  "+str(e))
            self.log_writer.log(self.file_object, "Convert image to array Unsuccessful. Exited the convert_image_to_array method of the Preprocessor class")
            raise Exception()
                
  • Following Trainmodel class is used to train our dataset which has three methods :

    • prepare_data : This method is used to prepare the data for training by

      • Separating images and labesls.
      • Converting them in train and test dataset.
      • Normalize them for easy training.
      • Changing label to categorical format.
    • model :

      This method is the main step where we build our convolutional neural network model by introducing two convolutional followed by two pooling, dense and one flatten layer.

    • train :

      In this method we train our model by using the fit method of the model. Also before training we again perform train test split to again increase the performance.

      Here we use Adam as a optimizer and catagorical cross entropy as a loss function.

      Here we also plot the traing and validation accuracy and loss.

class TrainModel:
    def __init__(self):
        self.log_writer = App_Logger()
        self.file_object = open("./Training_Logs/ModelTrainingLog.txt", 'a+')
        self.Preprocessor = Preprocessor()
        
    def prepare_data(self):
        # Logging the start of training
        self.log_writer.log(self.file_object, 'Start of Training')
        """doing the preprocessing"""
            
        self.log_writer.log(self.file_object, 'Doing Preprocessing.')
        # Doing necessary preprocessing
        self.log_writer.log(self.file_object, 'I enter Preprocessing.')
             
        dir = "./dataset"
        root_dir = listdir(dir)
        image_list, label_list = [], []
        all_labels = ['Corn-Common_rust', 'Potato-Early_blight', 'Tomato-Bacterial_spot']
        binary_labels = [0,1,2]
        temp = -1

        # Reading and converting image to numpy array
        for directory in root_dir:
            plant_image_list = listdir(f"{dir}/{directory}")
            temp += 1
            for files in plant_image_list:
                image_path = f"{dir}/{directory}/{files}"
                image_list.append(self.Preprocessor.convert_image_to_array(image_path))
                label_list.append(binary_labels[temp])
            
        # splitting the data into training and test set
        self.log_writer.log(self.file_object, 'Doing train_test_split.')
        x_train, x_test, y_train, y_test = train_test_split(image_list, label_list, test_size=0.2, random_state = 10)  
        self.log_writer.log(self.file_object, 'Finish train_test_split.')
            
        # Normalizing the dataset
        self.log_writer.log(self.file_object, 'Doing Normalizing.')
        x_train = np.array(x_train, dtype=np.float16) / 225.0
        x_test = np.array(x_test, dtype=np.float16) / 225.0
        x_train = x_train.reshape( -1, 256,256,3)
        x_test = x_test.reshape( -1, 256,256,3)
            
        # Changing label to categorical
        self.log_writer.log(self.file_object, 'Changing to categorical.')
        y_train = to_categorical(y_train)
        y_test = to_categorical(y_test)
        return x_train, y_train
    def model(self):
            
        # Model Building
        self.log_writer.log(self.file_object, 'Model Building.')
        model = Sequential()
        model.add(Conv2D(32, (3, 3), padding="same",input_shape=(256,256,3), activation="relu"))
        model.add(MaxPooling2D(pool_size=(3, 3)))
        model.add(Conv2D(16, (3, 3), padding="same", activation="relu"))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        model.add(Flatten())
        model.add(Dense(8, activation="relu"))
        model.add(Dense(3, activation="softmax"))
        model.summary()
        return model
    
    def train(self):   
        model = self.model()
        x_train, y_train = self.prepare_data()
        # Compiling Modell
        self.log_writer.log(self.file_object, 'Compiling Model.')
        model.compile(loss = 'categorical_crossentropy', optimizer = Adam(0.0001), metrics=['accuracy'])
            
        # Again splitting the training dataset into training and validation datasets
        self.log_writer.log(self.file_object, 'Again doing train_test_split.')
        x_tr, x_val, y_tr, y_val = train_test_split(x_train, y_train, test_size = 0.2)
            
        # Training the model
        epochs = 50
        batch_size = 20
        self.log_writer.log(self.file_object, 'Fitting a model.')
        history = model.fit(x_tr, y_tr, batch_size = batch_size, epochs = epochs, validation_data = (x_val, y_val), shuffle=True)
            
        # Saving Model
        self.log_writer.log(self.file_object, 'Saving Model.')
        model.save("./trained_model/disease.h5")
        # Serialize model to json
        json_model = model.to_json()
        # save the model architechture to JSON file
        with open("./dataset/disease.json", "w") as json_file:
            json_file.write(json_model)
                
        #saving the weights of the model
        model.save_weights("./trained_model/disease_weights.h5")
        
            
        #Plot the training history
        self.log_writer.log(self.file_object, 'Plotting the training history.')
        plt.figure(figsize=(12, 5))
        plt.plot(history.history['accuracy'], color='r')
        plt.plot(history.history['val_accuracy'], color='b')
        plt.title('Model Accuracy')
        plt.ylabel('Accuracy')
        plt.xlabel('Epochs')
        plt.legend(['train', 'val'])
        plt.show()
        plt.savefig("./plot_fig/plot.png")   # save the figure to file
            
        # logging the successful Training
        self.log_writer.log(self.file_object, 'Successful End of Training')
        self.file_object.close()
  • Now making object of TrainModel and train the model.
trainmodel = TrainModel()
trainmodel.train()
Model: "sequential_4"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d_8 (Conv2D)           (None, 256, 256, 32)      896       
                                                                 
 max_pooling2d_8 (MaxPooling  (None, 85, 85, 32)       0         
 2D)                                                             
                                                                 
 conv2d_9 (Conv2D)           (None, 85, 85, 16)        4624      
                                                                 
 max_pooling2d_9 (MaxPooling  (None, 42, 42, 16)       0         
 2D)                                                             
                                                                 
 flatten_4 (Flatten)         (None, 28224)             0         
                                                                 
 dense_8 (Dense)             (None, 8)                 225800    
                                                                 
 dense_9 (Dense)             (None, 3)                 27        
                                                                 
=================================================================
Total params: 231,347
Trainable params: 231,347
Non-trainable params: 0
_________________________________________________________________
Epoch 1/50
29/29 [==============================] - 3s 62ms/step - loss: 0.8101 - accuracy: 0.6146 - val_loss: 0.5880 - val_accuracy: 0.6389
Epoch 2/50
29/29 [==============================] - 1s 47ms/step - loss: 0.4412 - accuracy: 0.7396 - val_loss: 0.3670 - val_accuracy: 0.8889
Epoch 3/50
29/29 [==============================] - 1s 46ms/step - loss: 0.2800 - accuracy: 0.9410 - val_loss: 0.2346 - val_accuracy: 0.9375
Epoch 4/50
29/29 [==============================] - 1s 47ms/step - loss: 0.1775 - accuracy: 0.9583 - val_loss: 0.1747 - val_accuracy: 0.9653
Epoch 5/50
29/29 [==============================] - 1s 50ms/step - loss: 0.1257 - accuracy: 0.9757 - val_loss: 0.1264 - val_accuracy: 0.9792
Epoch 6/50
29/29 [==============================] - 1s 48ms/step - loss: 0.1043 - accuracy: 0.9774 - val_loss: 0.1308 - val_accuracy: 0.9514
Epoch 7/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0803 - accuracy: 0.9861 - val_loss: 0.0813 - val_accuracy: 0.9792
Epoch 8/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0599 - accuracy: 0.9896 - val_loss: 0.0813 - val_accuracy: 0.9861
Epoch 9/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0556 - accuracy: 0.9844 - val_loss: 0.0995 - val_accuracy: 0.9653
Epoch 10/50
29/29 [==============================] - 1s 46ms/step - loss: 0.0606 - accuracy: 0.9826 - val_loss: 0.0760 - val_accuracy: 0.9792
Epoch 11/50
29/29 [==============================] - 1s 46ms/step - loss: 0.0544 - accuracy: 0.9861 - val_loss: 0.0612 - val_accuracy: 0.9792
Epoch 12/50
29/29 [==============================] - 1s 46ms/step - loss: 0.0374 - accuracy: 0.9896 - val_loss: 0.0468 - val_accuracy: 0.9861
Epoch 13/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0258 - accuracy: 0.9983 - val_loss: 0.0339 - val_accuracy: 1.0000
Epoch 14/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0215 - accuracy: 0.9965 - val_loss: 0.0325 - val_accuracy: 1.0000
Epoch 15/50
29/29 [==============================] - 1s 50ms/step - loss: 0.0203 - accuracy: 0.9983 - val_loss: 0.0297 - val_accuracy: 1.0000
Epoch 16/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0162 - accuracy: 0.9983 - val_loss: 0.0284 - val_accuracy: 1.0000
Epoch 17/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0180 - accuracy: 0.9983 - val_loss: 0.0239 - val_accuracy: 1.0000
Epoch 18/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0149 - accuracy: 0.9983 - val_loss: 0.0299 - val_accuracy: 1.0000
Epoch 19/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0235 - accuracy: 0.9931 - val_loss: 0.0368 - val_accuracy: 0.9861
Epoch 20/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0130 - accuracy: 0.9983 - val_loss: 0.0192 - val_accuracy: 1.0000
Epoch 21/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0119 - accuracy: 1.0000 - val_loss: 0.0200 - val_accuracy: 1.0000
Epoch 22/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0104 - accuracy: 0.9983 - val_loss: 0.0167 - val_accuracy: 1.0000
Epoch 23/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0080 - accuracy: 1.0000 - val_loss: 0.0167 - val_accuracy: 1.0000
Epoch 24/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0070 - accuracy: 1.0000 - val_loss: 0.0146 - val_accuracy: 1.0000
Epoch 25/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0062 - accuracy: 1.0000 - val_loss: 0.0140 - val_accuracy: 1.0000
Epoch 26/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0064 - accuracy: 1.0000 - val_loss: 0.0187 - val_accuracy: 1.0000
Epoch 27/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0059 - accuracy: 1.0000 - val_loss: 0.0127 - val_accuracy: 1.0000
Epoch 28/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0048 - accuracy: 1.0000 - val_loss: 0.0133 - val_accuracy: 1.0000
Epoch 29/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0046 - accuracy: 1.0000 - val_loss: 0.0123 - val_accuracy: 1.0000
Epoch 30/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0047 - accuracy: 1.0000 - val_loss: 0.0162 - val_accuracy: 1.0000
Epoch 31/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0058 - accuracy: 1.0000 - val_loss: 0.0112 - val_accuracy: 1.0000
Epoch 32/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0043 - accuracy: 1.0000 - val_loss: 0.0134 - val_accuracy: 1.0000
Epoch 33/50
29/29 [==============================] - 1s 46ms/step - loss: 0.0043 - accuracy: 1.0000 - val_loss: 0.0144 - val_accuracy: 1.0000
Epoch 34/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0032 - accuracy: 1.0000 - val_loss: 0.0104 - val_accuracy: 1.0000
Epoch 35/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0032 - accuracy: 1.0000 - val_loss: 0.0103 - val_accuracy: 1.0000
Epoch 36/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0030 - accuracy: 1.0000 - val_loss: 0.0091 - val_accuracy: 1.0000
Epoch 37/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0024 - accuracy: 1.0000 - val_loss: 0.0093 - val_accuracy: 1.0000
Epoch 38/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0023 - accuracy: 1.0000 - val_loss: 0.0081 - val_accuracy: 1.0000
Epoch 39/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0022 - accuracy: 1.0000 - val_loss: 0.0078 - val_accuracy: 1.0000
Epoch 40/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0024 - accuracy: 1.0000 - val_loss: 0.0096 - val_accuracy: 1.0000
Epoch 41/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0022 - accuracy: 1.0000 - val_loss: 0.0090 - val_accuracy: 1.0000
Epoch 42/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0020 - accuracy: 1.0000 - val_loss: 0.0069 - val_accuracy: 1.0000
Epoch 43/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0018 - accuracy: 1.0000 - val_loss: 0.0070 - val_accuracy: 1.0000
Epoch 44/50
29/29 [==============================] - 1s 50ms/step - loss: 0.0017 - accuracy: 1.0000 - val_loss: 0.0080 - val_accuracy: 1.0000
Epoch 45/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0017 - accuracy: 1.0000 - val_loss: 0.0133 - val_accuracy: 0.9931
Epoch 46/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0015 - accuracy: 1.0000 - val_loss: 0.0082 - val_accuracy: 1.0000
Epoch 47/50
29/29 [==============================] - 1s 48ms/step - loss: 0.0015 - accuracy: 1.0000 - val_loss: 0.0082 - val_accuracy: 1.0000
Epoch 48/50
29/29 [==============================] - 1s 51ms/step - loss: 0.0014 - accuracy: 1.0000 - val_loss: 0.0079 - val_accuracy: 1.0000
Epoch 49/50
29/29 [==============================] - 1s 49ms/step - loss: 0.0012 - accuracy: 1.0000 - val_loss: 0.0060 - val_accuracy: 1.0000
Epoch 50/50
29/29 [==============================] - 1s 47ms/step - loss: 0.0012 - accuracy: 1.0000 - val_loss: 0.0081 - val_accuracy: 1.0000
<Figure size 432x288 with 0 Axes>
  • We can see our model is performing pretty well. So lets see how the web app seems which I built and deployed at heroku:

plant