Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 21

!

pip install tensorflow==2.9.1
# Libraries of commonly used
from PIL import Image
import pandas as pd
import numpy as np
import random
from random import sample, shuffle
import cv2, os, itertools, shutil
from tqdm.notebook import tqdm as tq
from google.colab import files

# Libraries for visualization
%matplotlib inline
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import seaborn as sns

# Libraries for image data processing
import cv2
from PIL import Image
import skimage
from skimage import io 
from skimage.transform import resize
from skimage.transform import rotate, AffineTransform, warp
from skimage import img_as_ubyte
from skimage.exposure import adjust_gamma
from skimage.util import random_noise

# Libraries for modeling and evaluation
from sklearn.metrics import confusion_matrix,classification_report
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tensorflow.keras.applications import EfficientNetB7
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D,MaxPooling2D,Dense,Flatten,Dropout,BatchNo
rmalization
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications.imagenet_utils import preprocess_input
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
from tensorflow.keras import Model, layers

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
warnings.simplefilter(action='ignore', category=UserWarning)
# Crate a dictionary storing images for each classes in the data train
Nutrient_dataset = {}

# Define source path
path = "NutrientDataset/"
for i in os.listdir(path):
    Nutrient_dataset[i] = os.listdir(os.path.join(path, i))

# Randomly display 5 images under each of the 6 categories from the training data.
# You will see different images each time.
fig, axs = plt.subplots(len(Nutrient_dataset.keys()), 5, figsize = (15, 15))
for i, item in enumerate(os.listdir(path)):
    images = sample(Nutrient_dataset[item], 5)
    
    for j, image in enumerate(images):
        img = Image.open(os.path.join(path, item, image))
        axs[i, j].imshow(img)
        axs[i, j].set(xlabel = item, xticks = [], yticks = [])

fig.tight_layout()
# Define source path
path = "NutrientDataset/"

# Create a list that stores data for each filenames, fullpaths, and labels
filename = []
labels = []
fullpath = []

# Get data image filenames, filepaths, labels one by one with looping, and store them as dataf
rame
for path, subdirs, files in os.walk(path):
    for name in files:
        fullpath.append(os.path.join(path, name)) 
        labels.append(path.split('/')[-1])        
        filename.append(name)

distribution_train = pd.DataFrame({"path":fullpath,'file_name':filename,"labels":labels})

# Plot the distribution of images across the classes
Label = distribution_train['labels']
# plt.figure(figsize = (6,6))
# sns.set_style("darkgrid")
# plot_data = sns.countplot(Label)
# Created a function to perform anti clockwise rotation
def anticlockwise_rotation(img):  
    img = cv2.cvtColor(img, 0)
    img = cv2.resize(img, (224,224))
    angle= random.randint(0,180)
    return rotate(img, angle)

# Create a function to perform clockwise rotation
def clockwise_rotation(img):   
    img = cv2.cvtColor(img, 0)
    img = cv2.resize(img, (224,224))
    angle= random.randint(0,180)
    return rotate(img, -angle)

# Create a function to flip images up and down
def flip_up_down(img):   
    img = cv2.cvtColor(img, 0)
    img = cv2.resize(img, (224,224))
    return np.flipud(img)

# Create a function to give a bright effect to the image
def add_brightness(img):    
    img = cv2.cvtColor(img, 0)
    img = cv2.resize(img, (224,224))
    img = adjust_gamma(img, gamma=0.5,gain=1)
    return img

# Create a function to give the image a blur/blur effect
def blur_image(img):    
    img = cv2.cvtColor(img, 0)
    img = cv2.resize(img, (224,224))
    return cv2.GaussianBlur(img, (9,9),0)

# Create a function to give the image a sheared effect
def sheared(img):   
    img = cv2.cvtColor(img, 0)
    img = cv2.resize(img, (224,224))
    transform = AffineTransform(shear=0.2) 
    shear_image = warp(img, transform, mode="wrap")
    return shear_image

# Create a function to perform warp shifts 
def warp_shift(img):   
    img = cv2.cvtColor(img, 0)
    img = cv2.resize(img, (224,224))
    transform = AffineTransform(translation=(0,40))
    warp_image = warp(img, transform, mode="wrap")
    return warp_image
# Create a transformation variable that will hold all the preprocessing processes that have bee
n made above
transformations = { 'rotate anticlockwise': anticlockwise_rotation,
                    'rotate clockwise': clockwise_rotation,
                    'warp shift': warp_shift,
                    'blurring image': blur_image,
                    'add brightness' : add_brightness,
                    'flip up down': flip_up_down,
                    'shear image': sheared
                  }                

images_path="NutrientDataset/Iron" # Path for the original image
augmented_path="NutrientDataset/Iron" # Path to put the augmented image
images=[] # To save images that have been preprocessed from the folder

# Read image name from folder and add path into "images" array 
for im in os.listdir(images_path):    
    images.append(os.path.join(images_path,im))

# The number of images that will be added with the results of the augmentation transformatio
n, the number is adjusted according to needs
# Variable to iterate up to a predefined number of images_to_generate
images_to_generate=1000  
i=1                      

while i<=images_to_generate:    
    image=random.choice(images)
    try:
        original_image = io.imread(image)
        transformed_image=None
        n = 0      # Variables to iterate up to the number of transformations to apply
        transformation_count = random.randint(1, len(transformations)) # Choose the number of 
random transformations to apply to the image
        
        while n <= transformation_count:
            key = random.choice(list(transformations)) # Randomly select and call methods
            transformed_image = transformations[key](original_image)
            n = n + 1
            
        new_image_path= "%s/augmented_image_%s.jpg" %(augmented_path, i)
        transformed_image = img_as_ubyte(transformed_image)  # Convert images to unsigned 
byte format, with values in [0, 255]
        cv2.imwrite(new_image_path, transformed_image)  # Save the result of the augmentatio
n transformation on the image into the specified path
        i =i+1
    except ValueError as e:
        print('could not read the',image ,':',e,'hence skipping it.')
# Create a transformation variable that will hold all the preprocessing processes that have bee
n made above
transformations = { 'rotate anticlockwise': anticlockwise_rotation,
                    'rotate clockwise': clockwise_rotation,
                    'warp shift': warp_shift,
                    'blurring image': blur_image,
                    'add brightness' : add_brightness,
                    'flip up down': flip_up_down,
                    'shear image': sheared
                  }                

images_path="NutrientDataset/Magnesium" # Path for the original image
augmented_path="NutrientDataset/Magnesium" # Path to put the augmented image
images=[] # To save images that have been preprocessed from the folder

# Read image name from folder and add path into "images" array 
for im in os.listdir(images_path):    
    images.append(os.path.join(images_path,im))

# The number of images that will be added with the results of the augmentation transformatio
n, the number is adjusted according to needs
# Variable to iterate up to a predefined number of images_to_generate
images_to_generate=1000  
i=1                      

while i<=images_to_generate:    
    image=random.choice(images)
    try:
        original_image = io.imread(image)
        transformed_image=None
        n = 0      # Variables to iterate up to the number of transformations to apply
        transformation_count = random.randint(1, len(transformations)) # Choose the number of 
random transformations to apply to the image
        
        while n <= transformation_count:
            key = random.choice(list(transformations)) # Randomly select and call methods
            transformed_image = transformations[key](original_image)
            n = n + 1
            
        new_image_path= "%s/augmented_image_%s.jpg" %(augmented_path, i)
        transformed_image = img_as_ubyte(transformed_image)  # Convert images to unsigned 
byte format, with values in [0, 255]
        cv2.imwrite(new_image_path, transformed_image)  # Save the result of the augmentatio
n transformation on the image into the specified path
        i =i+1
    except ValueError as e:
        print('could not read the',image ,':',e,'hence skipping it.')
# Remove file image with format .webp in directory path Magnesium
directory = "NutrientDataset/Magnesium"

for file_name in os.listdir(directory):
    if file_name.endswith(".webp"):
        os.remove(os.path.join(directory, file_name))
# Create a transformation variable that will hold all the preprocessing processes that have bee
n made above
transformations = { 'rotate anticlockwise': anticlockwise_rotation,
                    'rotate clockwise': clockwise_rotation,
                    'warp shift': warp_shift,
                    'blurring image': blur_image,
                    'add brightness' : add_brightness,
                    'flip up down': flip_up_down,
                    'shear image': sheared
                  }                
images_path="NutrientDataset/Nitrogen" # Path for the original image
augmented_path="NutrientDataset/Nitrogen" # Path to put the augmented image
images=[] # To save images that have been preprocessed from the folder

# Read image name from folder and add path into "images" array 
for im in os.listdir(images_path):    
    images.append(os.path.join(images_path,im))

# The number of images that will be added with the results of the augmentation transformatio
n, the number is adjusted according to needs
# Variable to iterate up to a predefined number of images_to_generate
images_to_generate=1000  
i=1                      

while i<=images_to_generate:    
    image=random.choice(images)
    try:
        original_image = io.imread(image)
        transformed_image=None
        n = 0      # Variables to iterate up to the number of transformations to apply
        transformation_count = random.randint(1, len(transformations)) # Choose the number of 
random transformations to apply to the image
        
        while n <= transformation_count:
            key = random.choice(list(transformations)) # Randomly select and call methods
            transformed_image = transformations[key](original_image)
            n = n + 1
            
        new_image_path= "%s/augmented_image_%s.jpg" %(augmented_path, i)
        transformed_image = img_as_ubyte(transformed_image)  # Convert images to unsigned 
byte format, with values in [0, 255]
        cv2.imwrite(new_image_path, transformed_image)  # Save the result of the augmentatio
n transformation on the image into the specified path
        i =i+1
    except ValueError as e:
        print('could not read the',image ,':',e,'hence skipping it.')
# Create a transformation variable that will hold all the preprocessing processes that have bee
n made above
transformations = { 'rotate anticlockwise': anticlockwise_rotation,
                    'rotate clockwise': clockwise_rotation,
                    'warp shift': warp_shift,
                    'blurring image': blur_image,
                    'add brightness' : add_brightness,
                    'flip up down': flip_up_down,
                    'shear image': sheared
                  }                

images_path="NutrientDataset/Potassium" # Path for the original image
augmented_path="NutrientDataset/Potassium" # Path to put the augmented image
images=[] # To save images that have been preprocessed from the folder

# Read image name from folder and add path into "images" array 
for im in os.listdir(images_path):    
    images.append(os.path.join(images_path,im))

# The number of images that will be added with the results of the augmentation transformatio
n, the number is adjusted according to needs
# Variable to iterate up to a predefined number of images_to_generate
images_to_generate=1000  
i=1                      

while i<=images_to_generate:    
    image=random.choice(images)
    try:
        original_image = io.imread(image)
        transformed_image=None
        n = 0      # Variables to iterate up to the number of transformations to apply
        transformation_count = random.randint(1, len(transformations)) # Choose the number of 
random transformations to apply to the image
        
        while n <= transformation_count:
            key = random.choice(list(transformations)) # Randomly select and call methods
            transformed_image = transformations[key](original_image)
            n = n + 1
            
        new_image_path= "%s/augmented_image_%s.jpg" %(augmented_path, i)
        transformed_image = img_as_ubyte(transformed_image)  # Convert images to unsigned 
byte format, with values in [0, 255]
        cv2.imwrite(new_image_path, transformed_image)  # Save the result of the augmentatio
n transformation on the image into the specified path
        i =i+1
    except ValueError as e:
        print('could not read the',image ,':',e,'hence skipping it.')
# Create a transformation variable that will hold all the preprocessing processes that have bee
n made above
transformations = { 'rotate anticlockwise': anticlockwise_rotation,
                    'rotate clockwise': clockwise_rotation,
                    'warp shift': warp_shift,
                    'blurring image': blur_image,
                    'add brightness' : add_brightness,
                    'flip up down': flip_up_down,
                    'shear image': sheared
                  }                

images_path="NutrientDataset/Zinc" # Path for the original image
augmented_path="NutrientDataset/Zinc" # Path to put the augmented image
images=[] # To save images that have been preprocessed from the folder

# Read image name from folder and add path into "images" array 
for im in os.listdir(images_path):    
    images.append(os.path.join(images_path,im))
# The number of images that will be added with the results of the augmentation transformatio
n, the number is adjusted according to needs
# Variable to iterate up to a predefined number of images_to_generate
images_to_generate=1000  
i=1                      

while i<=images_to_generate:    
    image=random.choice(images)
    try:
        original_image = io.imread(image)
        transformed_image=None
        n = 0      # Variables to iterate up to the number of transformations to apply
        transformation_count = random.randint(1, len(transformations)) # Choose the number of 
random transformations to apply to the image
        
        while n <= transformation_count:
            key = random.choice(list(transformations)) # Randomly select and call methods
            transformed_image = transformations[key](original_image)
            n = n + 1
            
        new_image_path= "%s/augmented_image_%s.jpg" %(augmented_path, i)
        transformed_image = img_as_ubyte(transformed_image)  # Convert images to unsigned 
byte format, with values in [0, 255]
        cv2.imwrite(new_image_path, transformed_image)  # Save the result of the augmentatio
n transformation on the image into the specified path
        i =i+1
    except ValueError as e:
        print('could not read the',image ,':',e,'hence skipping it.')
# Define source path
path = "NutrientDataset/"

# Get data image filenames, filepaths, labels one by one with looping, and store them as dataf
rame
filename = []
labels = []
fullpath = []
for path, subdirs, files in os.walk(path):
    for name in files:
        fullpath.append(os.path.join(path, name)) 
        labels.append(path.split('/')[-1])        
        filename.append(name)

# Memasukan variabel yang sudah dikumpulkan pada looping di atas menjadi sebuah datafra
me
df_nutrient = pd.DataFrame({"path":fullpath,'file_name':filename,"labels":labels})
# Melihat jumlah data gambar pada masing-masing label
df_nutrient.groupby(['labels']).size()
# Variables used in this data separation where variable x = data path and y = data labels
X_nutrient = df_nutrient['path']
y_nutrient = df_nutrient['labels']

# Split the initial dataset into training and testing data
# Use proportion 80% for training data and 20% for testing data
X_train, X_test, y_train, y_test = train_test_split(
    X_nutrient, y_nutrient, test_size=0.2, random_state=42)
# Insert into each dataframe, X_train and y_train which obtained from the results of the splitti
ng dataset
df_train_nutrient = pd.DataFrame({'path':X_train,'labels':y_train,'set':'train'})
df_test_nutrient = pd.DataFrame({'path':X_test,'labels':y_test,'set':'test'})

# Print the results above to see the training and test length data
print('Jumlah data training', len(df_train_nutrient))
print('Jumlah data testing', len(df_test_nutrient))
# Look at the proportions in each set, is it ok or still want to change it
df_all_nutrient = df_train_nutrient.append([df_test_nutrient]).reset_index(drop=1)\

print('===================================================== \n')
print(df_all_nutrient.groupby(['set','labels']).size(),'\n')

print('===================================================== \n')

# Check the sample data
df_all_nutrient.sample(5)
# Call the original dataset which contains all image data according to the label
ORIGINAL_DIR = "NutrientDataset/"
# Create a Dataset variable, which will later store data that has been splitting into training and 
testing data
FINAL_DIR = "dataset-final/"
for index, row in tq(df_all_nutrient.iterrows()):
        
    # Filepath detection
    file_path = row['path']
    if os.path.exists(file_path) == False:
            file_path = os.path.join(ORIGINAL_DIR,row['labels'],row['image'].split('.')[0])            
    
    # Create a folder destination directory
    if os.path.exists(os.path.join(FINAL_DIR,row['set'],row['labels'])) == False:
        os.makedirs(os.path.join(FINAL_DIR,row['set'],row['labels']))
    
    # Define file destination
    destination_file_name = file_path.split('/')[-1]
    file_dest = os.path.join(FINAL_DIR,row['set'],row['labels'],destination_file_name)
    
    # Copy files from source to destination
    if os.path.exists(file_dest) == False:
        shutil.copy2(file_path,file_dest)
# Define training and test directories
TRAINING_DIR = "dataset-final/train/"
TESTING_DIR = "dataset-final/test/"

# Tentukan default dimensi tinggi dan lebar gambar
img_width, img_height = 224, 224  

# Jumlah epoch yang dilakukan untuk proses training
epochs = 10

# Ukuran batch yang digunakan pada saat ImageDataGenerator dan predict model
batch_size = 16

# Channel gambar yang digunakan 1 = grayscale, 3 = rgb
channel = 3
# Create Image Data Generator for data training and validation
# Data validation comes from 20% proportion of training data
datagen = ImageDataGenerator(rescale=1. / 255, validation_split=0.2) 
# Create Image Data Generator for data testing
test_datagen = ImageDataGenerator(rescale=1. / 255) 

train_generator = datagen.flow_from_directory(TRAINING_DIR,
                                              batch_size=batch_size,
                                              color_mode='rgb',
                                              target_size=(img_width,img_height),
                                              class_mode='categorical',
                                              subset='training',
                                              shuffle=True)

validation_generator = datagen.flow_from_directory(TRAINING_DIR,
                                                   batch_size=batch_size,
                                                   color_mode='rgb',
                                                   target_size=(img_width,img_height),
                                                   class_mode='categorical',
                                                   subset='validation',
                                                   shuffle=False)

test_generator = test_datagen.flow_from_directory(TESTING_DIR,
                                                  batch_size=batch_size,
                                                  color_mode='rgb',
                                                  target_size=(img_width,img_height),
                                                  class_mode='categorical',
                                                  shuffle=False)
conv_base_effnet = EfficientNetB7(include_top=False, weights='imagenet', input_shape=(im
g_width, img_height, channel))

for layer in conv_base_effnet.layers:
    layers.trainable = True
x = conv_base_effnet.output
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(64, activation='relu')(x)
x = BatchNormalization()(x)
x = layers.Dropout(0.2)(x)
x = layers.Dense(64, activation='relu')(x)
x = BatchNormalization()(x)
predictions = layers.Dense(5, activation='softmax')(x)
model_effnet = Model(conv_base_effnet.input, predictions)
# Compile model
model_effnet.compile(loss='categorical_crossentropy',
                     optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
                     metrics=['accuracy'])

# Summary of the Model Architecture
model_effnet.summary() 
%time

# Callback Early Stopping
# early_stop = EarlyStopping(monitor='val_loss', patience=2)

# Fitting / training model
history_effnet = model_effnet.fit(train_generator, 
                                  epochs=epochs, 
                                  batch_size=batch_size,
                                  # callbacks=[early_stop],
                                  validation_data=validation_generator)
# Membuat grafik plot hasil akurasi training validation dan loss training validation
acc = history_effnet.history['accuracy']
val_acc = history_effnet.history['val_accuracy']
loss = history_effnet.history['loss']
val_loss = history_effnet.history['val_loss']
epochs = range(len(acc))

plt.plot(epochs, acc, 'r', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation EfficientNetB7 accuracy')
plt.ylabel('accuracy')  
plt.xlabel('epoch')
plt.legend()
plt.figure()

plt.plot(epochs, loss, 'r', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation EfficientNetB7 V2 loss')
plt.ylabel('loss')  
plt.xlabel('epoch')
plt.legend()
plt.show()
# Prediction using testing data
test_generator.reset()
preds_effnet = model_effnet.predict(test_generator,verbose=0)
test_labels = [np.argmax(pred) for pred in preds_effnet]

cm = pd.DataFrame(data=confusion_matrix(test_generator.classes, test_labels, 
                                        labels=[0, 1, 2, 3, 4]),
                                        index=['Actual Iron', 'Actual Magnesium',
                                               'Actual Nitrogen', 'Actual Potassium',
                                               'Actual Zinc'],
                                        columns=['Predicted Iron', 'Predicted Magnesium',
                                                 'Predicted Nitrogen', 'Predicted Potassium',
                                                 'Predicted Zinc'
                                                ])
sns.heatmap(cm,annot=True,fmt="d")
plt.show()
# Print Classification Report
print(classification_report(y_true=test_generator.classes,y_pred=test_labels,
                            target_names =['Iron','Magnesium','Nitrogen',
                                           'Potassium', 'Zinc'], digits=4))
# Save model
model_effnet.save('Nutrient-Model.h5')
# Display result of prediction testing data
probabilities = model_effnet.predict(test_generator, 30, verbose=0)
class_names = list(test_generator.class_indices.keys())

for index, probability in enumerate(probabilities):
    image_path = TESTING_DIR + "/" + test_generator.filenames[index]
    img = mpimg.imread(image_path)
    plt.imshow(img)
    
    # Get the top predicted class and its probability
    top_class_index = np.argmax(probability)
    top_class_prob = probability[top_class_index]
    top_class_name = class_names[top_class_index]
    
    # Set the title to display the top predicted class and its probability
    plt.title(f"{top_class_name}: {top_class_prob:.2f}")
    
    plt.show()
# Display result of prediction new data
model = tf.keras.models.load_model('Nutrient-Model.h5')

uploaded = files.upload()
for fn in uploaded.keys():
  path = fn
  img = image.load_img(path, color_mode="rgb", target_size=(224, 224))
  img = image.img_to_array(img)
  img = np.expand_dims(img, axis=0)
  img = img/255

  images = np.vstack([img])

  classes = list(train_generator.class_indices.keys())
  pred = model.predict(images, verbose=0)
  index = np.argmax(pred[0])
  probability= round(pred[0][index]*100, 2)
  
  plt.figure(figsize=(6,6))
  plt.axis('off')
  title = f'Predict : {probability}% {classes[index]}'
  plt.title(title)  
  plt.imshow(np.squeeze(images))
  plt.show()  
# Display result of prediction new data
model = tf.keras.models.load_model('Nutrient-Model.h5')

uploaded = files.upload()

for fn in uploaded.keys():
  path = fn
  img = image.load_img(path, color_mode="rgb", target_size=(224, 224))
  img = image.img_to_array(img)
  img = np.expand_dims(img, axis=0)
  img = img/255

  images = np.vstack([img])
  classes = list(train_generator.class_indices.keys())
  pred = model.predict(images, verbose=0)
  index = np.argmax(pred[0])
  probability= round(pred[0][index]*100, 2)
  
  plt.figure(figsize=(6,6))
  plt.axis('off')
  title = f'Predict : {probability}% {classes[index]}'
  plt.title(title)  
  plt.imshow(np.squeeze(images))
  plt.show()  
# Display result of prediction new data
model = tf.keras.models.load_model('Nutrient-Model.h5')

uploaded = files.upload()

for fn in uploaded.keys():
  path = fn
  img = image.load_img(path, color_mode="rgb", target_size=(224, 224))
  img = image.img_to_array(img)
  img = np.expand_dims(img, axis=0)
  img = img/255

  images = np.vstack([img])

  classes = list(train_generator.class_indices.keys())
  pred = model.predict(images, verbose=0)
  index = np.argmax(pred[0])
  probability= round(pred[0][index]*100, 2)
  
  plt.figure(figsize=(6,6))
  plt.axis('off')
  title = f'Predict : {probability}% {classes[index]}'
  plt.title(title)  
  plt.imshow(np.squeeze(images))
  plt.show()  

You might also like