{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"![header](https://user-images.githubusercontent.com/17668390/157607672-03ad3db9-8c6e-4a95-b6b1-79c1e179d7be.png)\n\n\n# Carvana Image Masking : Binary Image Segmentation \n\nA simple starter on binary car image segmentation task. Original data source, [HERE](https://www.kaggle.com/c/carvana-image-masking-challenge). The original competition data is zipped. The target mask is in `GIF` format. These file is converted to `PNG` format for easy use. [HERE](https://www.kaggle.com/ipythonx/carvana-image-masking-png) is the converted data.","metadata":{}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os, cv2\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n\nimport matplotlib.pyplot as plt \nimport tensorflow_io as tfio\nimport tensorflow as tf\n\nroot = '../input/carvana-image-masking-png'\nexts = ('jpg', 'JPG', 'png', 'PNG', 'tif', 'gif', 'ppm')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-03-10T06:37:31.843794Z","iopub.execute_input":"2022-03-10T06:37:31.844311Z","iopub.status.idle":"2022-03-10T06:37:37.197626Z","shell.execute_reply.started":"2022-03-10T06:37:31.844210Z","shell.execute_reply":"2022-03-10T06:37:37.196868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prepare Dataset","metadata":{}},{"cell_type":"code","source":"input_data = os.path.join(root, 'train_images')\nimages = sorted(\n    [\n        os.path.join(input_data, fname)\n        for fname in os.listdir(input_data)\n        if fname.endswith(exts) and not fname.startswith(\".\")\n    ]\n)\n\n\ntarget_data = os.path.join(root, 'train_masks')\nmasks = sorted(\n    [\n        os.path.join(target_data, fname)\n        for fname in os.listdir(target_data)\n        if fname.endswith(exts) and not fname.startswith(\".\")\n    ]\n)\n\nprint(\"Number of samples:\", len(images), len(masks))\nfor input_path, target_path in zip(images[:10], masks[:10]):\n    print(input_path[-32:], \"|\", target_path[-31:], '|', np.unique(cv2.imread(target_path)))","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:37.201003Z","iopub.execute_input":"2022-03-10T06:37:37.201228Z","iopub.status.idle":"2022-03-10T06:37:39.295265Z","shell.execute_reply.started":"2022-03-10T06:37:37.201181Z","shell.execute_reply":"2022-03-10T06:37:39.294502Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def read_image(image_path, mask=False):\n    image = tf.io.read_file(image_path)\n    \n    if mask:\n        image = tf.image.decode_png(image, channels=1)\n        image.set_shape([None, None, 1])\n        image = tf.image.resize(images=image, size=[IMAGE_SIZE, IMAGE_SIZE])\n        image = tf.cast(image, tf.int32)\n    else:\n        image = tf.image.decode_png(image, channels=3)\n        image.set_shape([None, None, 3])\n        image = tf.image.resize(images=image, size=[IMAGE_SIZE, IMAGE_SIZE])\n        image = image / 255.\n        \n    return image\n\ndef load_data(image_list, mask_list):\n    image = read_image(image_list)\n    mask  = read_image(mask_list, mask=True)\n    return image, mask\n\ndef data_generator(image_list, mask_list, split='train'):\n    dataset = tf.data.Dataset.from_tensor_slices((image_list, mask_list))\n    dataset = dataset.shuffle(8*BATCH_SIZE) if split == 'train' else dataset \n    dataset = dataset.map(load_data, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.batch(BATCH_SIZE, drop_remainder=True)\n    dataset = dataset.prefetch(tf.data.AUTOTUNE)\n    return dataset\n\nIMAGE_SIZE = 128\nBATCH_SIZE = 86\n\ntrain_dataset = data_generator(images, masks)\nprint(\"Train Dataset:\", train_dataset)","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:39.296667Z","iopub.execute_input":"2022-03-10T06:37:39.297163Z","iopub.status.idle":"2022-03-10T06:37:41.749565Z","shell.execute_reply.started":"2022-03-10T06:37:39.297123Z","shell.execute_reply":"2022-03-10T06:37:41.748856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt \n\ndef visualize(**images):\n    \"\"\"PLot images in one row.\"\"\"\n    n = len(images)\n    plt.figure(figsize=(16, 5))\n    for i, (name, image) in enumerate(images.items()):\n        plt.subplot(1, n, i + 1)\n        plt.xticks([])\n        plt.yticks([])\n        plt.title(' '.join(name.split('_')).title())\n        plt.imshow(image)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:41.751414Z","iopub.execute_input":"2022-03-10T06:37:41.751809Z","iopub.status.idle":"2022-03-10T06:37:41.758234Z","shell.execute_reply.started":"2022-03-10T06:37:41.751770Z","shell.execute_reply":"2022-03-10T06:37:41.757559Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image, mask = next(iter(train_dataset.take(1))) # train_dataset\nprint(image.shape, mask.shape)\n\nfor (img, msk) in zip(image[:5], mask[:5]):\n    print(mask.numpy().min(), mask.numpy().max())\n    visualize(\n        image=img.numpy(),\n        gt_mask=msk.numpy(), \n    )","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:41.759583Z","iopub.execute_input":"2022-03-10T06:37:41.760032Z","iopub.status.idle":"2022-03-10T06:37:44.226923Z","shell.execute_reply.started":"2022-03-10T06:37:41.759989Z","shell.execute_reply":"2022-03-10T06:37:44.226036Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# U-Net Model (Xception)","metadata":{}},{"cell_type":"code","source":"from tensorflow import keras \nfrom tensorflow.keras import layers\n\n# https://keras.io/examples/vision/oxford_pets_image_segmentation/\ndef get_model(img_size, num_classes):\n    inputs = keras.Input(shape=img_size + (3,))\n\n    ### [First half of the network: downsampling inputs] ###\n\n    # Entry block\n    x = layers.Conv2D(32, 3, strides=2, padding=\"same\")(inputs)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation(\"relu\")(x)\n\n    previous_block_activation = x  # Set aside residual\n\n    # Blocks 1, 2, 3 are identical apart from the feature depth.\n    for filters in [64, 128, 256]:\n        x = layers.Activation(\"relu\")(x)\n        x = layers.SeparableConv2D(filters, 3, padding=\"same\")(x)\n        x = layers.BatchNormalization()(x)\n\n        x = layers.Activation(\"relu\")(x)\n        x = layers.SeparableConv2D(filters, 3, padding=\"same\")(x)\n        x = layers.BatchNormalization()(x)\n\n        x = layers.MaxPooling2D(3, strides=2, padding=\"same\")(x)\n\n        # Project residual\n        residual = layers.Conv2D(filters, 1, strides=2, padding=\"same\")(\n            previous_block_activation\n        )\n        x = layers.add([x, residual])  # Add back residual\n        previous_block_activation = x  # Set aside next residual\n\n    ### [Second half of the network: upsampling inputs] ###\n\n    for filters in [256, 128, 64, 32]:\n        x = layers.Activation(\"relu\")(x)\n        x = layers.Conv2DTranspose(filters, 3, padding=\"same\")(x)\n        x = layers.BatchNormalization()(x)\n\n        x = layers.Activation(\"relu\")(x)\n        x = layers.Conv2DTranspose(filters, 3, padding=\"same\")(x)\n        x = layers.BatchNormalization()(x)\n\n        x = layers.UpSampling2D(2)(x)\n\n        # Project residual\n        residual = layers.UpSampling2D(2)(previous_block_activation)\n        residual = layers.Conv2D(filters, 1, padding=\"same\")(residual)\n        x = layers.add([x, residual])  # Add back residual\n        previous_block_activation = x  # Set aside next residual\n\n    # Add a per-pixel classification layer\n    outputs = layers.Conv2D(num_classes, 3, activation=\"sigmoid\", padding=\"same\")(x)\n\n    # Define the model\n    model = keras.Model(inputs, outputs)\n    return model\n\n\n# Free up RAM in case the model definition cells were run multiple times\nkeras.backend.clear_session()\n\n# Build model\nmodel = get_model(img_size=(IMAGE_SIZE, IMAGE_SIZE), num_classes=1)\nkeras.utils.plot_model(model, show_shapes=True)","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:44.228298Z","iopub.execute_input":"2022-03-10T06:37:44.229044Z","iopub.status.idle":"2022-03-10T06:37:46.171103Z","shell.execute_reply.started":"2022-03-10T06:37:44.229007Z","shell.execute_reply":"2022-03-10T06:37:46.170418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Compile","metadata":{}},{"cell_type":"code","source":"# define optomizer\noptim = keras.optimizers.Adam(0.001)\nbce   = keras.losses.BinaryCrossentropy()\nmetrics = [\"accuracy\"]\n\n# compile keras model with defined optimozer, loss and metrics\nmodel.compile(optim, bce, metrics)","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:46.172724Z","iopub.execute_input":"2022-03-10T06:37:46.173082Z","iopub.status.idle":"2022-03-10T06:37:46.196587Z","shell.execute_reply.started":"2022-03-10T06:37:46.173046Z","shell.execute_reply":"2022-03-10T06:37:46.195932Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Callback : Monitoring Training Progress","metadata":{}},{"cell_type":"code","source":"class DisplayCallback(tf.keras.callbacks.Callback):\n    def __init__(self, dataset, epoch_interval=5):\n        self.dataset = dataset\n        self.epoch_interval = epoch_interval\n    \n    def display(self, display_list, extra_title=''):\n        plt.figure(figsize=(15, 15))\n        title = ['Input Image', 'True Mask', 'Predicted Mask']\n\n        if len(display_list) > len(title):\n            title.append(extra_title)\n\n        for i in range(len(display_list)):\n            plt.subplot(1, len(display_list), i+1)\n            plt.title(title[i])\n            plt.imshow(display_list[i])\n            plt.axis('off')\n        plt.show()\n        \n    def create_mask(self, pred_mask):\n        pred_mask = (pred_mask > 0.5).astype(\"int32\")\n        return pred_mask[0]\n    \n    def show_predictions(self, dataset, num=1):\n        for image, mask in dataset.take(num):\n            pred_mask = model.predict(image)\n            self.display([image[0], mask[0], self.create_mask(pred_mask)])\n        \n    def on_epoch_end(self, epoch, logs=None):\n        if epoch and epoch % self.epoch_interval == 0:\n            self.show_predictions(self.dataset)\n            print ('\\nSample Prediction after epoch {}\\n'.format(epoch+1))","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:46.198113Z","iopub.execute_input":"2022-03-10T06:37:46.199477Z","iopub.status.idle":"2022-03-10T06:37:46.211674Z","shell.execute_reply.started":"2022-03-10T06:37:46.199440Z","shell.execute_reply":"2022-03-10T06:37:46.210931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Fit","metadata":{}},{"cell_type":"code","source":"epochs = 30\nmodel.fit(\n    train_dataset, \n    epochs=epochs, \n    callbacks=[DisplayCallback(train_dataset)]\n)","metadata":{"execution":{"iopub.status.busy":"2022-03-10T06:37:46.212804Z","iopub.execute_input":"2022-03-10T06:37:46.213126Z","iopub.status.idle":"2022-03-10T07:18:35.764577Z","shell.execute_reply.started":"2022-03-10T06:37:46.213090Z","shell.execute_reply":"2022-03-10T07:18:35.763856Z"},"trusted":true},"execution_count":null,"outputs":[]}]}