Created on Thu Jun  3 14:36:00 2021

@author: Tajinderpalsingh
"""

import matplotlib
matplotlib.use("Agg")

# import the necessary packages
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam
from keras.preprocessing.image import img_to_array
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt
from imutils import paths
import numpy as np
import argparse
import random
import pickle
import cv2
import os
import itertools
import dataframe_image as dfi

# import the necessary packages
from keras.models import Sequential
from keras.layers.normalization import BatchNormalization
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dropout
from keras.layers.core import Dense
from keras import backend as K
from sklearn.metrics import classification_report, confusion_matrix
import pandas as pd

from sklearn import metrics
import seaborn as sn
import matplotlib.pyplot as plt

class SmallerVGGNet:
    @staticmethod
    def build(width, height, depth, classes, finalAct="softmax"):
        # initialize the model along with the input shape to be
        # "channels last" and the channels dimension itself
        model = Sequential()
        inputShape = (height, width, depth)
        chanDim = -1

# if we are using "channels first", update the input shape
# and channels dimension
        if K.image_data_format() == "channels_first":
            inputShape = (depth, height, width)
            chanDim = 1

# CONV => RELU => POOL
        model.add(Conv2D(32, (3, 3), padding="same",
input_shape=inputShape))
       

   # (CONV => RELU) * 2 => POOL
        model.add(Conv2D(64, (3, 3), padding="same"))

# (CONV => RELU) * 2 => POOL
        model.add(Conv2D(128, (3, 3), padding="same"))
       

        # first (and only) set of FC => RELU layers
        model.add(Flatten())

       
        # softmax classifier
        model.add(Dense(classes))
        model.add(Activation(finalAct))
       
        # plot the model architecture in shell
        model.summary()
# return the constructed network architecture
        return model
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", default='./dataset',
help="path to input dataset (i.e., directory of images)")
ap.add_argument("-m", "--model", default='./models/fashion.h5',
help="path to output model")
ap.add_argument("-l", "--labelbin", default='lb.pickle',
help="path to output label binarizer")
ap.add_argument("-p", "--plot", type=str, default="plot.png",
help="path to output accuracy/loss plot")
ap.add_argument("-cm", "--confusion_mat", type=str, default="confusion_mat.png",
help="path to confusion_mat")

args = vars(ap.parse_args())

# initialize the number of epochs to train for, initial learning rate,
# batch size, and image dimensions
EPOCHS = 50
INIT_LR = 1e-3
BS = 30
# gray_scale
IMAGE_DIMS = (50, 50, 1)
# rgb color
# IMAGE_DIMS = (96, 96, 3)

# initialize the data and labels
data = []
labels = []
# grab the image paths and randomly shuffle them
print("[INFO] loading images...")
imagePaths = sorted(list(paths.list_images(args["dataset"])))
random.seed(42)
random.shuffle(imagePaths)

# loop over the input images
for imagePath in imagePaths:
# load the image, pre-process it, and store it in the data list
image = cv2.imread(imagePath,cv2.IMREAD_GRAYSCALE)
image = cv2.resize(image, (IMAGE_DIMS[1], IMAGE_DIMS[0]))
image = img_to_array(image)

data.append(image)
 
# extract set of class labels from the image path and update the
# labels list
l = label = imagePath.split(os.path.sep)[-2].split("_")
labels.append(l)


# scale the raw pixel intensities to the range [0, 1]
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)
print("[INFO] data matrix: {} images ({:.2f}MB)".format(
len(imagePaths), data.nbytes / (1024 * 1000.0)))
print(labels)


print("[INFO] class labels:")
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
print(labels)
# loop over each of the possible class labels and show them
for (i, label) in enumerate(lb.classes_):
print("{}. {}".format(i + 1, label))


# 80% train data, 20% valid data
(trainX, testX, trainY, testY) = train_test_split(data,
labels, test_size=0.2, random_state=42)

# construct the image generator for data augmentation
aug = ImageDataGenerator(rotation_range=25, width_shift_range=0.1,
height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True, fill_mode="nearest")


print("[INFO] compiling model...")
model = SmallerVGGNet.build(
width=IMAGE_DIMS[1], height=IMAGE_DIMS[0],
depth=IMAGE_DIMS[2], classes=len(lb.classes_),
finalAct="softmax")
# initialize the optimizer (SGD is sufficient)
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)

model.compile(loss="binary_crossentropy", optimizer=opt,
metrics=["accuracy"])

# print summary
print(model.summary())

# train the network
print("[INFO] training network...")
H = model.fit_generator(
aug.flow(trainX, trainY, batch_size=BS),
validation_data=(testX, testY),
steps_per_epoch=len(trainX) // BS,
epochs=EPOCHS, verbose=1)

# save the model to disk
print("[INFO] serializing network...")
model.save(args["model"])

# # save the multi-label binarizer to disk
print("[INFO] serializing label binarizer...")
f = open(args["labelbin"], "wb")
f.write(pickle.dumps(lb))
f.close()


plt.style.use("ggplot")
plt.figure()
N = EPOCHS
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="upper left")

plt.savefig(args["plot"])

def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]

    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')

# Predict the values from the validation dataset
predsY = model.predict(testX)
# Convert predictions classes to one hot vectors
Y_pred_classes = np.argmax(predsY,axis = 1)
# Convert validation observations to one hot vectors
Y_true = np.argmax(testY,axis = 1)
# compute the confusion matrix
confusion_mtx = confusion_matrix(Y_true, Y_pred_classes)
# plot the confusion matrix
plot_confusion_matrix(confusion_mtx, classes = range(24),)


plt.savefig(args["confusion_mat"])

np.diag(confusion_mtx).sum()/confusion_mtx.sum().sum()
for i in range (confusion_mtx.shape[0]):
    TP= confusion_mtx[i,i]
    FP= confusion_mtx[i,:].sum()- TP
    FN= confusion_mtx[:,i].sum()- TP
    TN = confusion_mtx.sum().sum()-TP-FP-FN
    Accuracy = (TP+TN)/confusion_mtx.sum().sum()
    Precision = TP/(TP+FP)
    Recall = TP/(TP+FN)
    F1_score = (2 * Precision * Recall)/(Precision+Recall)
    print (confusion_mtx[i],Accuracy,Precision,Recall,F1_score)
    df=  pd.DataFrame(classification_report(Y_true,Y_pred_classes,output_dict= True)).T
    df.to_csv('file1.csv')
