# -*- coding: utf-8 -*-
"""evaluation.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1OGttsubco0yLGS2_fj_WEtnwku09XZn7

# **Inception Score**
"""

# calculate inception score for cifar-10 in Keras
import random
from math import floor
from numpy import expand_dims
from numpy import log
from numpy import mean
from numpy import std
from numpy import exp
from numpy.random import shuffle
from keras.applications.inception_v3 import InceptionV3
from keras.applications.inception_v3 import preprocess_input
from keras.datasets import cifar10
from skimage.transform import resize
from numpy import asarray

# scale an array of images to a new size
def scale_images(images, new_shape):
  images_list = list()
  for image in images:
    # resize with nearest neighbor interpolation
    new_image = resize(image, new_shape, 0)
    # store
    images_list.append(new_image)
  return asarray(images_list)
# assumes images have any shape and pixels in [0,255]
def calculate_inception_score(images, n_split=10, eps=1E-16):
  # load inception v3 model
  model = InceptionV3()
  # enumerate splits of images/predictions
  scores = list()
  n_part = floor(images.shape[0] / n_split)
  for i in range(n_split):
    # retrieve images
    ix_start, ix_end = i * n_part, (i+1) * n_part
    subset = images[ix_start:ix_end]
    # convert from uint8 to float32
    subset = subset.astype('float32')
    # scale images to the required size
    subset = scale_images(subset, (299,299,3))
    # pre-process images, scale to [-1,1]
    subset = preprocess_input(subset)
    # predict p(y|x)
    p_yx = model.predict(subset)
    # calculate p(y)
    p_y = expand_dims(p_yx.mean(axis=0), 0)
    # calculate KL divergence using log probabilities
    kl_d = p_yx * (log(p_yx + eps) - log(p_y + eps))
    # sum over classes
    sum_kl_d = kl_d.sum(axis=1)
    # average over images
    avg_kl_d = mean(sum_kl_d)
    # undo the log
    is_score = exp(avg_kl_d)
    # store
    scores.append(is_score)
  # average across images
  is_avg, is_std = mean(scores), std(scores)
  return is_avg, is_std

# create a list of all generated images converted into np array
all = []
for pic in glob.glob('/content/drive/My Drive/randomly_generated/generated/*.png'):
  print(pic)
  img = cv2.imread(pic)
  all.append(img)
all_generated = numpy.array(all)

# check data type and shape
print(all_generated.shape)
print(all_generated.dtype)

# shuffle images
random.shuffle(all_generated)

# convert int to float
all_generated = all_generated.astype('float32')

# resize ancd check
all_generated = scale_images(all_generated, (299,299,3))
print(all_generated.shape)
print(all_generated.dtype)

# calculate inception score
is_avg, is_std = calculate_inception_score(all_generated)
print('score', is_avg, is_std)

"""# **Fréchet Inception Distance**

Import modules, write functions to compute the FID and load the inception v3 model

We computed FID for each class separately
"""

import imageio as io
import cv2
import glob
import numpy
import random
from numpy import cov
from numpy import trace
from numpy import iscomplexobj
from numpy import asarray
from numpy.random import randint
from scipy.linalg import sqrtm
from keras.applications.inception_v3 import InceptionV3
from keras.applications.inception_v3 import preprocess_input
from skimage.transform import resize

# scale an array of images to a new size
def scale_images(images, new_shape):
  images_list = list()
  for image in images:
    # resize with nearest neighbor interpolation
    new_image = resize(image, new_shape, 0)
    # store
    images_list.append(new_image)
  return asarray(images_list)

# calculate frechet inception distance
def calculate_fid(model, images1, images2):
  # calculate activations
  act1 = model.predict(images1)
  act2 = model.predict(images2)
  # calculate mean and covariance statistics
  mu1, sigma1 = act1.mean(axis=0), cov(act1, rowvar=False)
  mu2, sigma2 = act2.mean(axis=0), cov(act2, rowvar=False)
  # calculate sum squared difference between means
  ssdiff = numpy.sum((mu1 - mu2)**2.0)
  # calculate sqrt of product between cov
  covmean = sqrtm(sigma1.dot(sigma2))
  # check and correct imaginary numbers from sqrt
  if iscomplexobj(covmean):
    covmean = covmean.real
  # calculate score
  fid = ssdiff + trace(sigma1 + sigma2 - 2.0 * covmean)
  return fid

# prepare the inception v3 model
model = InceptionV3(include_top=False, pooling='avg', input_shape=(299,299,3))

"""# Class Eyeglasses"""

# create a list of images converted into np array
glasses = []
for pic in glob.glob('/content/drive/My Drive/randomly_generated/glasses/*.png'):
  print(pic)
  img = cv2.imread(pic)
  glasses.append(img)
glasses_fake = numpy.array(glasses) # nested array (96,128,128,3) of 96 elements

# check data shape and type
print(glasses_fake.shape) 
print(glasses_fake.dtype)

# create a list of images converted into np array
glasses_1 = []
i = 0
for pic in glob.glob('/content/drive/My Drive/categorical_pictures.zip (Unzipped Files)/Eyeglasses/*.jpg'):
  if i < 96:
    img = cv2.imread(pic)
    glasses_1.append(img)
  else:
    break
  i+=1
glasses_real = numpy.array(glasses_1) # nested array

# convert integer to floating point values
glasses_fake = glasses_fake.astype('float32')
glasses_real = glasses_real.astype('float32')

# resize images
glasses_fake = scale_images(glasses_fake, (299,299,3))
print('Scaled', glasses_fake.shape)
glasses_real = scale_images(glasses_real, (299,299,3))
print('Scaled', glasses_real.shape)

# pre-process images
glasses_fake = preprocess_input(glasses_fake)
glasses_real = preprocess_input(glasses_real)

# fid between glasses_fake and glasses_real
fid = calculate_fid(model, glasses_fake, glasses_real)
print('FID (different): %.3f' % fid)

"""# Class Rosy_cheeks"""

# create a list of images converted into np array
rosy = []
for pic in glob.glob('/content/drive/My Drive/randomly_generated/rosy/*.png'):
  print(pic)
  img = cv2.imread(pic)
  rosy.append(img)
rosy_cheeks_fake = numpy.array(rosy)

# check shape and datatype
print(rosy_cheeks_fake.shape)
print(rosy_cheeks_fake.dtype)

# create a list of real images converted into np array
rosy_1 = []
i = 0
for pic in glob.glob('/content/drive/My Drive/categorical_pictures.zip (Unzipped Files)/Rosy_Cheeks/*.jpg'):
  if i < 100:
    img = cv2.imread(pic)
    rosy_1.append(img)
  else:
    break
  i+=1
rosy_cheeks_real = numpy.array(rosy_1) # nested array

# convert to float
rosy_cheeks_fake = rosy_cheeks_fake.astype('float32')
rosy_cheeks_real = rosy_cheeks_real.astype('float32')

# resize
rosy_cheeks_fake = scale_images(rosy_cheeks_fake, (299,299,3))
print('Scaled', rosy_cheeks_fake.shape)

rosy_cheeks_real = scale_images(rosy_cheeks_real, (299,299,3))
print('Scaled', rosy_cheeks_real.shape)

# preprocess
rosy_cheeks_fake = preprocess_input(rosy_cheeks_fake)
rosy_cheeks_real = preprocess_input(rosy_cheeks_real)

# fid between rosy_cheeks_fake and rosy_cheeks_real
fid = calculate_fid(model, rosy_cheeks_fake, rosy_cheeks_real)
print('FID (different): %.3f' % fid)

"""# Class Goatee"""

# create a list of images converted into np array
got = []
for pic in glob.glob('/content/drive/My Drive/randomly_generated/goatee/*.png'):
  print(pic)
  img = cv2.imread(pic)
  got.append(img)
goatee_fake = numpy.array(got)

# check shape and datatype
print(goatee_fake.shape)
print(goatee_fake.dtype)

# create a list of real images converted into np array
got_1 = []
i = 0
for pic in glob.glob('/content/drive/My Drive/categorical_pictures.zip (Unzipped Files)/Goatee/*.jpg'):
  if i < 104:
    img = cv2.imread(pic)
    got_1.append(img)
  else:
    break
  i+=1
goatee_real = numpy.array(got_1) # nested array

# convert to float
goatee_fake = goatee_fake.astype('float32')
goatee_real = goatee_real.astype('float32')

# resize
goatee_fake = scale_images(goatee_fake, (299,299,3))
print('Scaled', goatee_fake.shape)

goatee_real = scale_images(goatee_real, (299,299,3))
print('Scaled', goatee_real.shape)

# preprocess
goatee_fake = preprocess_input(goatee_fake)
goatee_real = preprocess_input(goatee_real)

# fid between goatee_fake and goatee_real
fid = calculate_fid(model, goatee_fake, goatee_real)
print('FID (different): %.3f' % fid)
