#-----AI2.py Supervised Machine Learning. 
#--Images are catalogrized into known classes
#--Use tensor-flow to build a model for future prediction

import cv2
import random
import numpy as np
import matplotlib.pyplot as plt
import os 
import math
import time

def cw(k): cv2.waitKey(k)
def sh0(w,img): cv2.imshow(w,img)

seed = 7
np.random.seed(seed)
isx=64; isy=64

#------------------------------------------------------
def Sload_images_from_folder(iszx,iszy,folder):
    nitems=os.listdir(folder); nsitems=sorted(nitems)
    images=[]; img5=[]
    for filename in nsitems:
        img = cv2.imread(os.path.join(folder,filename), cv2.IMREAD_GRAYSCALE)
        if img is not None:
            img1=cv2.resize(img,(iszx,iszy))/255
            images.append(img1); img5.append(img)
    lens=len(nsitems)
    return lens,nsitems,np.array(images[:lens]),img5
##################################################
def load_images_from_folder(folder,nfile):
    images = []
    for filename in os.listdir(folder):
        img = cv2.imread(os.path.join(folder,filename), cv2.IMREAD_GRAYSCALE)
        if img is not None:
            img = cv2.resize(img,(isx,isy))/255
            images.append(img)
    return np.array(images[:nfile])

##################################################
def AITEST(pathT):
    # read images
    #test = load_images_from_folder(pathT,ntest)
    ntest,fimgs,test,img5=Sload_images_from_folder(isx,isy,pathT)
    print('load is done path=',pathT)
    print('fimgs=',fimgs)
    X_test = test
    X_test = X_test.reshape(X_test.shape[0], isy,isx, 1).astype("float32")
    print('X_test:type,len,shape=',type(X_test),len(X_test),X_test.shape)
    pred = np.argmax(model.predict(X_test),axis=1)      
    #print()
    #print(X_test)
    print('pred=',pred)
    if(1==2):
        for i in range(ntest):
            img = X_test[i,:,:,0]
            print(i,'pred[i]=',pred[i],' LAB=',LAB[pred[i]])
            cv2.imshow('img',img)
            cv2.waitKey(0)


##################################################
def buildmodel(X_train,Y_train):
    # One-hot code
    Y_train = to_categorical(Y_train); print('Y_train.shape2=',Y_train.shape)
    # Transform images into 4D tensors
    X_train = X_train.reshape(X_train.shape[0], isy,isx, 1).astype("float32")
    X_train,X_test,Y_train,Y_test=train_test_split(X_train,Y_train, \
            test_size=0.1, random_state=42)
    # define the model
    model = Sequential()
    model.add(Conv2D(16,(3,3),activation = 'relu', input_shape = (isy,isx,1)))
    model.add(MaxPooling2D(pool_size=(2,2)))
    model.add(Conv2D(32,(3,3),activation = 'relu'))
    model.add(MaxPooling2D(pool_size=(2,2)))
    model.add(Flatten())
    model.add(Dense(64,activation='relu'))
    model.add(Dropout(0.2))
    model.add(Dense(32,activation='relu'))
    model.add(Dense(ncat,activation='softmax'))
    model.summary()
    model.compile(loss = 'categorical_crossentropy',
                 optimizer = "adam",
                 metrics = ['accuracy'])
    #print(X_train.shape); print(Y_train.shape)
    history = model.fit(X_train,Y_train,shuffle=True, validation_split=0.1,
                                    batch_size=128, epochs=60) 
    # training and validation accuracy
    acc = history.history["accuracy"]
    epochs = range(1, len(acc)+1)
    val_acc = history.history["val_accuracy"]
    loss = history.history["loss"]
    epochs = range(1, len(loss)+1)
    val_loss = history.history["val_loss"]
    if(1==2):
        plt.plot(epochs, acc, "bo-", label="Training Acc")
        plt.plot(epochs, val_acc, "ro--", label="Validation Acc")
        plt.title("Training and Validation Accuracy")
        plt.xlabel("Epochs")
        plt.ylabel("Accuracy")
        plt.legend()
        plt.show()
        # -----------loss-------------
        plt.plot(epochs, loss, "bo-", label="Training Loss")
        plt.plot(epochs, val_loss, "ro--", label="Validation Loss")
        plt.title("Training and Validation Loss")
        plt.xlabel("Epochs")
        plt.ylabel("Loss")
        plt.legend()
        plt.show()
    print("\nTesting ...")
    loss, accuracy = model.evaluate(X_train, Y_train, verbose=0)
    print("accuracy of training data={:.2f}".format(accuracy))
    loss, accuracy = model.evaluate(X_test, Y_test, verbose=0)
    print("accuracy of testing data={:.2f}".format(accuracy))
    model.save(mod1)
    print('model ',mod1,' is saved...')
    
    


# set no. of images for each lable
KBU=1; path='PNG2468'; ncat=4; nfile=60; ntest=20; pathT=path+'/TEST/'
mod1='model_2468_60A.h5'
LAB=['2','4','6','8']
print('load path=',path)
train = load_images_from_folder(path+'/'+LAB[0]+'/',nfile)
print('type(train)=',type(train),train.shape)
Y_train = np.zeros(train.shape[0])
X_train = train
train = load_images_from_folder(path+'/'+LAB[1]+'/',nfile)
Y_train = np.append(Y_train, np.ones(train.shape[0])*1)
X_train = np.append(X_train, train, axis=0)

#if(ncat==33):
#    train = load_images_from_folder(path+'/'+LAB[2]+'/',nfile)
#    Y_train = np.append(Y_train, np.ones(train.shape[0])*2)
#    X_train = np.append(X_train, train, axis=0)

if(ncat>2):
    for nc in range(2,ncat):
        train = load_images_from_folder(path+'/'+LAB[nc]+'/',nfile)
        Y_train = np.append(Y_train, np.ones(train.shape[0])*nc)
        X_train = np.append(X_train, train, axis=0)

print('X_train.shape=',X_train.shape); 
print('y_train.shape=',Y_train.shape)

KTEN=1
if(KTEN==1):
    t0=time.time()
    from tensorflow.keras.preprocessing import image
    from tensorflow.keras.optimizers import RMSprop
    from tensorflow import keras
    import tensorflow as tf
    import shutil
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense
    from tensorflow.keras.layers import Flatten
    from tensorflow.keras.layers import Conv2D
    from tensorflow.keras.layers import MaxPooling2D
    from tensorflow.keras.layers import Dropout
    from tensorflow.keras.utils import to_categorical
    from sklearn.model_selection import train_test_split
    t1=time.time(); TEN=round(t1-t0,3); print('TEN=',TEN)

if(KBU==1):
    buildmodel(X_train,Y_train)
    t2=time.time()
    TB=round(t2-t1,2); print('TB=',TB)

model = keras.models.load_model(mod1)
print('load ',mod1,' ... done')

for j in range(5):
    if(j==0): pathT=path+'/'+LAB[0]+'/'
    if(j==1): pathT=path+'/'+LAB[1]+'/'
    if(j==2): pathT=path+'/'+LAB[2]+'/'
    if(j==3): pathT=path+'/'+LAB[3]+'/'
    if(j==4): pathT=path+'/TEST/'
    if(j==5): pathT=path+'/TEST2/'
    AITEST(pathT)

