import os
import zipfile

local_zip = r'C:\Users\kirin\tmp\horse-or-human.zip'
zip_ref = zipfile.ZipFile(local_zip, 'r')
zip_ref.extractall(r'C:\Users\kirin\tmp')
zip_ref.close()

#Directory with our training horse pictures
train_horse_dir = os.path.join(r'C:\Users\kirin\tmp\horses')
#Directory with our training human pictures
train_human_dir = os.path.join(r'C:\Users\kirin\tmp\humans')

validation_horse_dir = os.path.join(r'C:\Users\kirin\tmp\horses')
validation_human_dir = os.path.join(r'C:\Users\kirin\tmp\humans')

train_horse_names = os.listdir(train_horse_dir)
print(train_horse_names[:10])

train_human_names = os.listdir(train_human_dir)
print(train_human_names[:10])

validation_horse_hames = os.listdir(validation_horse_dir)
print(validation_horse_hames[:10])

validation_human_names = os.listdir(validation_human_dir)
print(validation_human_names[:10])

print('total training horse images:', len(os.listdir(train_horse_dir)))
print("total training human images:", len(os.listdir(train_human_dir)))
print('total validation horse images:', len(os.listdir(validation_horse_dir)))
print('total validation human images:', len(os.listdir(validation_human_dir)))

#matplotlib inline

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
'''
#Parameters for our graph; we'll output images in a 4x4 configuration
nrows = 4
ncols = 4
#Index for iterating over images
pic_index = 0
#Set up matplotlib fig, and size it to fit 4x4 pics
fig = plt.gcf()
fig.set_size_inches(ncols * 4, nrows * 4)

pic_index += 8
next_horse_pix = [os.path.join(train_horse_dir, fname)
                for fname in train_horse_names[pic_index-8:pic_index]]
next_human_pix = [os.path.join(train_human_dir, fname)
                for fname in train_human_names[pic_index-8:pic_index]]
for i, img_path in enumerate(next_horse_pix+next_human_pix):
    #Set up subplot; subplot indices start at 1
    sp = plt.subplot(nrows, ncols, i + 1)
    sp.axis('Off')#Don't show axes (or gridlines)
    img = mpimg.imread(img_path)
    plt.imshow(img)
plt.show()
'''

import tensorflow as tf
model = tf.keras.models.Sequential([
    #Note the input shape is the desired size of the image 300x300 with 3 bytes color
    #This is the first convolution
    tf.keras.layers.Conv2D(16,(3,3),activation='relu',input_shape=(300,300,3)),
    tf.keras.layers.MaxPooling2D(2,2),
    #The second convolution
    tf.keras.layers.Conv2D(32,(3,3),activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    #The third convolution
    tf.keras.layers.Conv2D(64,(3,3),activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    #The fourth convolution
    tf.keras.layers.Conv2D(64,(3,3),activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    #The fifth convolution
    tf.keras.layers.Conv2D(64,(3,3),activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2),
    #Flatten the results to feed into a DNN
    tf.keras.layers.Flatten(),
    #512 neuron hidden layer
    tf.keras.layers.Dense(512, activation='relu'),
    #Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('horses') and 1 for the other('humans')
    tf.keras.layers.Dense(1,activation='sigmoid')
])
#Summary the mofel
model.summary()

from tensorflow.keras.optimizers import RMSprop
model.compile(loss='binary_crossentropy',
                optimizer=RMSprop(lr = 0.001),
                metrics=['acc'])
from tensorflow.keras.preprocessing.image import ImageDataGenerator
#All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1/255)
validation_datagen = ImageDataGenerator(rescale=1/255)
#Flow training images in batches of 128 using train_datagen generator
train_generator = train_datagen.flow_from_directory(
    r'C:\Users\kirin\tmp',   #This is the source directory for training images
    target_size=(300,300),  #All images will be resized to 150x150
    batch_size=128, 
    #Since we use binary_crossentropy loss, we need binary labels
    class_mode='binary'
    )
#Flow training images in batches of 128 using train_datagen generator
validation_generator = validation_datagen.flow_from_directory(
    r'C:\Users\kirin\tmp',
    target_size=(300,300),
    batch_size=32,
    #since we use binary_crossentropy loss, we need binary labels
    class_mode='binary'
)

'''
#Training
history = model.fit_generator(
    train_generator,
    steps_per_epoch=8,
    epochs=15,
    verbose=1,
    validation_data = validation_generator,
    validation_steps = 8
    )
'''

#Visualizing Intermediate Representations
import numpy as np
import random
from tensorflow.keras.preprocessing.image import img_to_array, load_img
#let's define a new Model that will take an image as input, and will output
#intermediate representations for all layers in the previous model after the first
successive_outputs = [layer.output for layer in model.layers[1:]]
#visualization_model = Model(img_input, successive_outputs)
visualization_model = tf.keras.models.Model(inputs = model.input, outputs = successive_outputs)
#Let's prepare a random input image from the training set.
horse_img_files = [os.path.join(train_horse_dir, f) for f in train_horse_names]
human_img_files = [os.path.join(train_human_dir, f) for f in train_human_names]
img_path = random.choice(horse_img_files + human_img_files)
img = load_img(img_path, target_size=(300,300)) #this is a PIL image
x = img_to_array(img) #Numpy array with shape (150, 150, 3)
x = x.reshape((1,) + x.shape) #Numpy array with shape (1,150,150,3)
#Rescal by 1/255
x /= 255
#Let's run our image through our network, thus obtaining all intermediate representations for this image
successive_feature_maps = visualization_model.predict(x)
#These are the name of the layers, so can have them as part of our plot
layer_names = [layer.name for layer in model.layers]
#Now let's display our representations
for layer_name, feature_map in zip(layer_names, successive_feature_maps):
    if len(feature_map.shape) == 4:
        #Just do this for the conv/maxpool layers, not the fully-connected layers
        n_features = feature_map.shape[-1] #number of features in feature map
        #The feature map has shape(1,size,size,n_featrues)
        size = feature_map.shape[1]
        #We will tile our images in this matrix
        display_grid = np.zeros((size, size * n_features))
        for i in range(n_features):
            #Postprocess the feature to make it visually palatable
            x = feature_map[0, :, :, i]
            x -= x.mean()
            np.seterr(divide='ignore',invalid='ignore')#ignore runtime warning:invalid value encountered in true_divide
            x /= x.std()
            x *= 64
            x += 128
            x = np.clip(x, 0, 255).astype('uint8')
            #We'll tile each filter into this big horizontal grid
            display_grid[:, i * size : (i + 1) * size] = x
        #Display the grid
        scale = 20. / n_features
        plt.figure(figsize=(scale * n_features, scale))
        plt.title(layer_name)
        plt.grid(False)
        plt.imshow(display_grid, aspect='auto', cmap='viridis')
plt.show()


#predicting images
import cv2
from tensorflow.keras.preprocessing import image
path1 = r'C:\Users\kirin\tmp\horses\horse01-1.png'
img1 = image.load_img(path1, target_size=(300,300))
x1 = image.img_to_array(img1)
x1 = np.expand_dims(x1, axis=0)
images1 = np.vstack([x1])
classes1 = model.predict(images1,batch_size=1)
print(classes1[0])
if classes1[0]>0.5:
    print('orimg1 is a human')
else:
    print("orimg1 is a horse")
orimg1 = cv2.imread(path1,cv2.IMREAD_COLOR)
cv2.imshow('orimg1',orimg1)

path2 = r'C:\Users\kirin\tmp\humans\human01-01.png'
img2 = image.load_img(path2, target_size=(300,300))
x2 = image.img_to_array(img2)
x2 = np.expand_dims(x2, axis=0)
images2 = np.vstack([x2])
classes2 = model.predict(images2,batch_size=1)
print(classes2[0])
if classes2[0]>0.5:
    print('orimg2 is a human')
else:
    print("orimg2 is a horse")
orimg2 = cv2.imread(path2,cv2.IMREAD_COLOR)
cv2.imshow('orimg2',orimg2)
cv2.waitKey(0)