# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# + id="GpJAY7aLsLlz" colab_type="code" colab={}
# #!pip install mnist

# + id="Gfm1JHAisSEV" colab_type="code" colab={}
import numpy as np
import cv2
import random
import occlusion
import matplotlib.pyplot as plt
from PIL import Image
random.seed(1)
np.random.seed(1)


# + id="4fGCi0dorddL" colab_type="code" colab={}
def preprocess(img, size, patchCorner=(0, 0), patchDim=None, unskew=True):
    """
    Resizes, crops, and unskewes images
  
    """
    if patchDim == None: patchDim = size
    nImg = np.shape(img)[0]
    procImg = np.empty((nImg, size[0], size[1]))

    # Unskew and Resize
    if unskew == True:
        for i in range(nImg):
            procImg[i, :, :] = deskew(cv2.resize(img[i, :, :], size), size)

    # Crop
    cropImg = np.empty((nImg, patchDim[0], patchDim[1]))
    for i in range(nImg):
        cropImg[i, :, :] = procImg[i, patchCorner[0]:patchCorner[0] + patchDim[0], \
                           patchCorner[1]:patchCorner[1] + patchDim[1]]
    procImg = cropImg

    return procImg


def deskew(image, image_shape, negated=True):
    """
    This method deskwes an image using moments
    :param image: a numpy nd array input image
    :param image_shape: a tuple denoting the image`s shape
    :param negated: a boolean flag telling whether the input image is negated
  
    :returns: a numpy nd array deskewd image
  
    source: https://github.com/vsvinayak/mnist-helper
    """

    # negate the image
    if not negated:
        image = 255 - image
    # calculate the moments of the image
    m = cv2.moments(image)
    if abs(m['mu02']) < 1e-2:
        return image.copy()
    # caclulating the skew
    skew = m['mu11'] / m['mu02']
    M = np.float32([[1, skew, -0.5 * image_shape[0] * skew], [0, 1, 0]])
    img = cv2.warpAffine(image, M, image_shape, flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)
    return img

def mnist_256():
  ''' 
  Converts 28x28 mnist digits to [16x16] 
  [samples x pixels]  ([N X 256])
  '''  
  import mnist
  z = (mnist.train_images()/255)
  z = preprocess(z,(16,16))

  z = z.reshape(-1, (256))
  return z, mnist.train_labels()

def mnist_256_test():
  ''' 
  Converts 28x28 mnist digits to [16x16] 
  [samples x pixels]  ([N X 256])
  '''  
  import mnist
  z = (mnist.test_images()/255)
  z = preprocess(z,(16,16))

  z = z.reshape(-1, (256))
  return z, mnist.test_labels()

test = mnist_256_test()
train = mnist_256()


# + id="hptvPXIOuDz2" colab_type="code" colab={}
x_train = train[0]
y_train = train[1]
x_test = test[0]
y_test = test[1]
x_train = x_train.reshape(60000, 16, 16)
x_test = x_test.reshape(10000, 16, 16)

# + id="OgTA0r-hpIxU" colab_type="code" colab={"base_uri": "https://localhost:8080/", "height": 86} outputId="15c6ec3a-31ca-47d9-8c7e-d5ed564028a2"
from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K

batch_size = 128
num_classes = 10
epochs = 12

# input image dimensions
img_rows, img_cols = 16, 16

# the data, split between train and test sets
#(x_train, y_train), (x_test, y_test) = mnist.load_data()

if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

#x_train = x_train.astype('float32')
#x_test = x_test.astype('float32')
#x_train /= 255
#x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

# + id="Ta4qleXotRrs" colab_type="code" colab={"base_uri": "https://localhost:8080/", "height": 871} outputId="12270ea6-692d-4744-8676-5d1225b20bb9"
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

# + id="p5-15HqY0yvF" colab_type="code" colab={}
hdm = occlusion.HausdorffDistanceMasks(16, 16)
hdm.generate_masks(1, 1)

# + id="vH42-mXzz8Au" colab_type="code" colab={"base_uri": "https://localhost:8080/", "height": 1000} outputId="b8f1be3b-6b40-4fc3-ea15-86194b2b4fd6"
for index in [9953, 3850, 4962, 3886, 5437, 8517, 2041, 1989, 1933, 9984]:
    image = x_test[index].reshape(16, 16)

    label = np.argmax(y_test[index])
    print('label:', label)

    baseline = model.predict(image.reshape(1, 16, 16, 1))

    print('prediction', np.argmax(baseline[0]))

    result = hdm.explain(model, image, baseline[0], output_class=label)

    plt.imshow(image, cmap='Greys_r')
    plt.show()

    img = plt.imshow(result.distances(occlusion.RAW))
    plt.colorbar(img)
    plt.show()

StarcoderdataJupyterScriptsDedupFiltered