{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Setup"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport shutil\nimport numpy  as np\nimport pandas as pd\n\nfrom PIL import Image\nfrom cv2 import imread, resize, IMREAD_GRAYSCALE\n\nfrom tensorflow.keras.utils import Sequence\nfrom tensorflow             import convert_to_tensor as to_T\n\nfrom tensorflow.keras.models import load_model\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data Preprocessing"},{"metadata":{},"cell_type":"markdown","source":"### Utility Functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Bounding Box\ndef bbox(image):\n    \"\"\"\n    Determines the bounding boxes for images to remove empty space where possible\n    :param image:\n    :return:\n    \"\"\"\n    HEIGHT = image.shape[0]\n    WIDTH  = image.shape[1]\n\n    for i in range(image.shape[1]):\n        if (image[:, i] > 0).sum() >= 1:\n            x_min = i - 1 if (i > 1) else 0\n            break\n\n    for i in reversed(range(image.shape[1])):\n        if (image[:, i] > 0).sum() >= 1:\n            x_max = i + 2 if (i < WIDTH - 2) else WIDTH\n            break\n\n    for i in range(image.shape[0]):\n        if (image[i] > 0).sum() >= 1:\n            y_min = i - 1 if (i > 1) else 0\n            break\n\n    for i in reversed(range(image.shape[0])):\n        if (image[i] > 0).sum() >= 1:\n            y_max = i + 2 if (i < HEIGHT - 2) else HEIGHT\n            break\n\n    return x_min, y_min, x_max, y_max\n\n    return x_min, y_min, x_max, y_max","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#%% Dataset class\nclass Dataset(Sequence):\n    def __init__( self\n                , image_list\n                , batch_size\n                , dimensions\n                ):\n        \"\"\"\n        Creates a Keras Sequence class that serves data to the model\n        \"\"\"\n\n        # Class attributes\n        self.image_list = image_list\n        self.batch_size = batch_size\n        self.dimensions = dimensions\n\n        # Initialize the list\n        self.on_epoch_end()\n\n    def __len__(self):\n        \"\"\"\n        Number of batches available in the dataset\n        \"\"\"\n        return int(np.ceil(len(self.image_list) / self.batch_size))\n\n    def __getitem__(self, index):\n        \"\"\"\n        Generate a single batch of data\n        \"\"\"\n        \n        # Indices of samples in the dataset\n        indices = self.indices[index * self.batch_size:(index + 1) * self.batch_size]\n\n        # Associated images and labels\n        images = [self.image_list[k] for k in indices]\n\n        # Data generation\n        X = self.__data_generation(images)\n\n        return X\n\n    def __data_generation(self, images):\n        \"\"\"\n        Retrieve the appropriate image and process as necessary for training\n        \"\"\"\n        # Empty storage\n        X = np.empty((len(images), self.dimensions[\"height\"], self.dimensions[\"width\"], self.dimensions[\"channels\"]))\n\n        # Loop through each ID\n        for idx, image in enumerate(images):\n            # Load the image\n            image = imread(image, IMREAD_GRAYSCALE)\n\n            # Load and resize\n            image = resize( image\n                          , (self.dimensions[\"width\"], self.dimensions[\"height\"])\n                          )\n            image = np.expand_dims(image, axis=2)\n\n            # Append to storage\n            X[idx,] = image\n\n        # Normalize X\n        X = X / 255.0\n\n        return to_T(X)\n\n    def on_epoch_end(self):\n        \"\"\"\n        Update the dataset at the end of an epoch\n        \"\"\"\n        self.indices = np.arange(len(self.image_list))\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# Load the dataset\ndf = pd.read_csv(\"../input/Kannada-MNIST/test.csv\")\n\n# Label and image ID storage\nids        = []\nimage_list = []\n\n# Make a directory for storage\nos.makedirs(\"images\")\n\n# Loop through each row\nfor i,row in df.iterrows():\n    # Get the data components\n    id    = \"{}\".format(i)\n    img   = np.reshape(row[1:].values, newshape=(28,28)).astype(np.uint8)\n\n    # Remove empty space\n    x_min, y_min, x_max, y_max = bbox(img)\n    img = img[y_min:y_max, x_min:x_max]\n\n    # Check again\n    x_min, y_min, x_max, y_max = bbox(img)\n    img = img[y_min:y_max, x_min:x_max]\n\n    # Convert and reshape\n    img = Image.fromarray(img)\n    img = img.resize((28,28), Image.ANTIALIAS)\n\n    # Write to storage\n    img.save(\"images/{}.png\".format(id))\n    ids.append(id)\n    image_list.append(\"images/{}.png\".format(id))\n    \n# Save the IDs to a DataFrame and write to CSV\ntest_df = pd.DataFrame({\"id\": ids, \"label\": 0}) # We will update this later","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"ds = Dataset( image_list=image_list\n            , batch_size=512\n            , dimensions={\"height\":28, \"width\":28, \"channels\":1}\n            )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Network"},{"metadata":{"trusted":true},"cell_type":"code","source":"net = load_model(\"../input/kannada-mnist-tensorflow-2/CustomNetV2.hdf5\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Predictions"},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions = net.predict(ds).argmax(-1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Update the submission\ntest_df[\"label\"] = predictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.to_csv(\"submission.csv\",index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Cleanup"},{"metadata":{"trusted":true},"cell_type":"code","source":"shutil.rmtree(\"images\")","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}