{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":9988,"databundleVersionId":868324,"sourceType":"competition"}],"dockerImageVersionId":30587,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import numpy as np \nimport pandas as pd \nimport os\nimport cv2\nimport tensorflow as tf\nimport tensorflow.keras as keras\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.utils import Sequence\nimport tensorflow.keras.backend as K\nfrom sklearn.model_selection import train_test_split\n\n# Visualization\nfrom IPython.display import display\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:06:44.355131Z","iopub.execute_input":"2023-11-27T14:06:44.355430Z","iopub.status.idle":"2023-11-27T14:06:56.537617Z","shell.execute_reply.started":"2023-11-27T14:06:44.355403Z","shell.execute_reply":"2023-11-27T14:06:56.536855Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IM_SHAPE = (768, 768)\nBATCH_SIZE = 32\nAUTOTUNE = tf.data.experimental.AUTOTUNE","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:06:56.539227Z","iopub.execute_input":"2023-11-27T14:06:56.539736Z","iopub.status.idle":"2023-11-27T14:06:56.544324Z","shell.execute_reply.started":"2023-11-27T14:06:56.539710Z","shell.execute_reply":"2023-11-27T14:06:56.543347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Helpers","metadata":{}},{"cell_type":"code","source":"def decode_mask(row, shapes=IM_SHAPE):\n    mask = np.zeros((shapes[0]*shapes[1], 1), dtype=np.uint8)\n\n    try:\n        ents = row.split()\n        starts, lengths = [np.asarray(x, dtype=int) for x in (ents[0:][::2], ents[1:][::2])]\n        starts -= 1\n        ends = starts + lengths\n\n        for wi, hi in zip(starts, ends):\n            mask[wi:hi] = 1\n    except Exception as e:\n        pass\n    finally:\n        return mask.reshape(shapes).T\n\ndef read_sample(imid, ds_root='/kaggle/input/airbus-ship-detection', subset='train', show=True):\n    subset_path = {'train': 'train_v2', 'test': 'test_v2'}\n    im = cv2.imread(os.path.join(ds_root, subset_path.get(subset), imid))[...,::-1]\n    if subset=='train':\n        mask = bboxes.set_index('ImageId').loc[imid].values.flatten().tolist()\n        mask = np.sum(np.array(list(map(decode_mask, mask))), axis=0)\n    else:\n        mask = np.zeros((1,1))\n    \n    if show:\n        fig, ax = plt.subplots(figsize=(10,5), ncols=2)\n        \n        ax[0].imshow(im)\n        ax[0].set_title(imid+' Sample')\n        ax[0].set_xticks([])\n        ax[0].set_yticks([])\n\n        ax[1].imshow(mask)\n        ax[1].set_title(imid+' Mask')\n        ax[1].set_xticks([])\n        ax[1].set_yticks([])\n    \n    return im, mask\n\ndef im_hist(im):\n    r, g, b = cv2.split(im)\n\n    hist_red, bins_red = np.histogram(r.flatten(), bins=256, range=[0, 256])\n    hist_green, bins_green = np.histogram(g.flatten(), bins=256, range=[0, 256])\n    hist_blue, bins_blue = np.histogram(b.flatten(), bins=256, range=[0, 256])\n\n    plt.plot(hist_red, color='red', label='Red Channel')\n    plt.plot(hist_green, color='green', label='Green Channel')\n    plt.plot(hist_blue, color='blue', label='Blue Channel')\n\n    plt.title('Histograms of RGB Channels')\n    plt.xlabel('Pixel Value')\n    plt.ylabel('Frequency')\n    plt.legend()\n    plt.grid(True)\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:06:56.545624Z","iopub.execute_input":"2023-11-27T14:06:56.545948Z","iopub.status.idle":"2023-11-27T14:06:56.573815Z","shell.execute_reply.started":"2023-11-27T14:06:56.545918Z","shell.execute_reply":"2023-11-27T14:06:56.572855Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"bboxes = pd.read_csv('/kaggle/input/airbus-ship-detection/train_ship_segmentations_v2.csv')\ndisplay(bboxes.head())\n\nprint(bboxes.info())\ndisplay(bboxes.describe())","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:06:56.576354Z","iopub.execute_input":"2023-11-27T14:06:56.576976Z","iopub.status.idle":"2023-11-27T14:06:58.102569Z","shell.execute_reply.started":"2023-11-27T14:06:56.576942Z","shell.execute_reply":"2023-11-27T14:06:58.101662Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"_ = bboxes.apply(lambda x: (~x.isnull()).sum()).plot(kind='bar', figsize=(8,3))\nplt.title('Distribution of labeled images')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:06:58.103791Z","iopub.execute_input":"2023-11-27T14:06:58.104101Z","iopub.status.idle":"2023-11-27T14:06:58.578970Z","shell.execute_reply.started":"2023-11-27T14:06:58.104077Z","shell.execute_reply":"2023-11-27T14:06:58.578132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"_ship_counts = bboxes[~bboxes['EncodedPixels'].isnull()]['ImageId'].value_counts()\nprint('Max. ships: ', _ship_counts.max())\nprint('25Q/75Q/95Q of c. ships: ', np.quantile(_ship_counts, 0.25), np.quantile(_ship_counts, 0.75), np.quantile(_ship_counts, 0.95))\n_ = _ship_counts.plot(kind='hist', bins=_ship_counts.max()-1, figsize=(8,3))\nplt.title('Distribution of ships on Train samples')\nplt.xticks([i for i in range(_ship_counts.max())])\nplt.grid(True)\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:06:58.579977Z","iopub.execute_input":"2023-11-27T14:06:58.580250Z","iopub.status.idle":"2023-11-27T14:06:58.953259Z","shell.execute_reply.started":"2023-11-27T14:06:58.580226Z","shell.execute_reply":"2023-11-27T14:06:58.952360Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def count_boats(data: pd.Series):\n    if data.isnull().any()==True:\n        return 0\n    else:\n        return len(data)\n    \n\nimids = pd.DataFrame(index=np.unique(bboxes['ImageId']))\nimids['Boats'] = bboxes.groupby('ImageId')['EncodedPixels'].apply(count_boats)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:06:58.954530Z","iopub.execute_input":"2023-11-27T14:06:58.954905Z","iopub.status.idle":"2023-11-27T14:07:16.910251Z","shell.execute_reply.started":"2023-11-27T14:06:58.954873Z","shell.execute_reply":"2023-11-27T14:07:16.909457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imids['Boats'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:07:16.911627Z","iopub.execute_input":"2023-11-27T14:07:16.911990Z","iopub.status.idle":"2023-11-27T14:07:16.923886Z","shell.execute_reply.started":"2023-11-27T14:07:16.911957Z","shell.execute_reply":"2023-11-27T14:07:16.922866Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"+ Fraction of labeled images is much smaller than samples without bounding boxes.\n+ Maximum of detected boats - 15, 95% 0f images has count of boats lower than 2.","metadata":{}},{"cell_type":"markdown","source":"# Visualization","metadata":{}},{"cell_type":"code","source":"# randomly select non-bounded images + all bounded\nnb_frac = 0.2\nnb_count = int((~bboxes['EncodedPixels'].isnull()).sum()*nb_frac)\n\nnb_ims = np.unique(bboxes[bboxes['EncodedPixels'].isnull()]['ImageId'].values)\nb_ims = np.unique(bboxes[~bboxes['EncodedPixels'].isnull()]['ImageId'].values)\nprint('Count of non-bounded images:', len(nb_ims))\n\nnp.random.shuffle(nb_ims)\n\nnb_ims = nb_ims[:nb_count]\n\nprint('Count of selected non-bounded images:', len(nb_ims))\nprint('Count of bounded images:', len(b_ims))","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:07:16.924954Z","iopub.execute_input":"2023-11-27T14:07:16.925299Z","iopub.status.idle":"2023-11-27T14:07:17.252734Z","shell.execute_reply.started":"2023-11-27T14:07:16.925275Z","shell.execute_reply":"2023-11-27T14:07:17.251629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Without boats\nim1, _ =read_sample(nb_ims[0])\n\n# With boats\nim2, _ = read_sample(b_ims[0])\nNone","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:07:17.257156Z","iopub.execute_input":"2023-11-27T14:07:17.257897Z","iopub.status.idle":"2023-11-27T14:07:18.332040Z","shell.execute_reply.started":"2023-11-27T14:07:17.257864Z","shell.execute_reply":"2023-11-27T14:07:18.331087Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"im2_copy = im2.copy()\nim2_copy[~_.astype('bool')]=0\nplt.imshow(im2_copy)\nplt.title('Selected boat')","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:07:18.333169Z","iopub.execute_input":"2023-11-27T14:07:18.333447Z","iopub.status.idle":"2023-11-27T14:07:18.783696Z","shell.execute_reply.started":"2023-11-27T14:07:18.333423Z","shell.execute_reply":"2023-11-27T14:07:18.782845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Examples with multiple boats\n\nmultiple_boats_ims = bboxes[~bboxes['EncodedPixels'].isnull()]['ImageId'].value_counts()[bboxes[~bboxes['EncodedPixels'].isnull()]['ImageId'].value_counts()>1].index.values\nread_sample(multiple_boats_ims[0])\nNone","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:07:18.785100Z","iopub.execute_input":"2023-11-27T14:07:18.785466Z","iopub.status.idle":"2023-11-27T14:07:19.348711Z","shell.execute_reply.started":"2023-11-27T14:07:18.785432Z","shell.execute_reply":"2023-11-27T14:07:19.347722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Histograms","metadata":{}},{"cell_type":"code","source":"test_ims = os.listdir('/kaggle/input/airbus-ship-detection/test_v2')\n\n# Selecting 1000 samples and building histograms\nn_samples_test = 100\nnp.random.shuffle(b_ims)\nnp.random.shuffle(nb_ims)\nnp.random.shuffle(test_ims)\n\nselected_b_ims = b_ims[:n_samples_test]\nselected_nb_ims = nb_ims[:n_samples_test]\nselected_t_ims = test_ims[:n_samples_test]\n\ntest_b_ims, test_b_masks = np.vectorize(lambda x: read_sample(x, show=False, subset='train'), signature='()->(n,m,3),(p,q)')(selected_b_ims)\ntest_nb_ims, test_nb_masks = np.vectorize(lambda x: read_sample(x, show=False, subset='train'), signature='()->(n,m,3),(p,q)')(selected_nb_ims)\ntest_t_ims, test_nb_masks = np.vectorize(lambda x: read_sample(x, show=False, subset='test'), signature='()->(n,m,3),(p,q)')(selected_t_ims)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:07:19.349805Z","iopub.execute_input":"2023-11-27T14:07:19.350137Z","iopub.status.idle":"2023-11-27T14:07:37.547076Z","shell.execute_reply.started":"2023-11-27T14:07:19.350111Z","shell.execute_reply":"2023-11-27T14:07:37.546074Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"boats_test_b_ims = test_b_ims.copy()\nboats_test_b_ims[~test_b_masks.astype(bool), :] = 0\nsea_test_b_ims = test_b_ims.copy()\nsea_test_b_ims[test_b_masks.astype(bool),:] = 0","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:07:37.548322Z","iopub.execute_input":"2023-11-27T14:07:37.548612Z","iopub.status.idle":"2023-11-27T14:07:40.296337Z","shell.execute_reply.started":"2023-11-27T14:07:37.548588Z","shell.execute_reply":"2023-11-27T14:07:40.295568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n_channels = test_b_ims.shape[-1]\n\nfig = plt.figure(figsize=(15,5))\n\nfor i in range(n_channels):\n    plt.subplot(2,3,1)\n    plt.hist(test_b_ims[:,:,:,i].flatten(), bins=256, alpha=0.5, density=True)\n    plt.title('Train (with boats)')\n\nfor i in range(n_channels):\n    plt.subplot(2,3,2)\n    plt.hist(boats_test_b_ims[:,:,:,i].flatten()[boats_test_b_ims[:,:,:,i].flatten()>0], bins=256, alpha=0.5, density=True)\n    plt.title('Train (boats)')\n\nfor i in range(n_channels):\n    plt.subplot(2,3,3)\n    plt.hist(sea_test_b_ims[:,:,:,i].flatten()[sea_test_b_ims[:,:,:,i].flatten()>0], bins=256, alpha=0.5, density=True)\n    plt.title('Train (sea)')\n\nfor i in range(n_channels):\n    plt.subplot(2,3,4)\n    plt.hist(test_nb_ims[:,:,:,i].flatten(), bins=256, alpha=0.5, density=True)\n    plt.title('Train (without boats)')\n\nfor i in range(n_channels):\n    plt.subplot(2,3,5)\n    plt.hist(test_t_ims[:,:,:,i].flatten(), bins=256, alpha=0.5, density=True)\n    plt.title('Test')\n    \nfig.suptitle('Dataset Histograms')\nplt.tight_layout()\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:07:40.297418Z","iopub.execute_input":"2023-11-27T14:07:40.297699Z","iopub.status.idle":"2023-11-27T14:07:58.988443Z","shell.execute_reply.started":"2023-11-27T14:07:40.297675Z","shell.execute_reply":"2023-11-27T14:07:58.987549Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"+ images with boat has more R-G values in range 50-150, which is approved with separate histogram of boats and sea\n+ test dataset has more separation between B and R-G channels","metadata":{}},{"cell_type":"code","source":"print('Test images:')\nnp.vectorize(lambda x: read_sample(x, show=True, subset='test'), signature='()->(n,m,3),(p,q)')(selected_t_ims[:3])\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:07:58.989706Z","iopub.execute_input":"2023-11-27T14:07:58.990348Z","iopub.status.idle":"2023-11-27T14:07:59.836406Z","shell.execute_reply.started":"2023-11-27T14:07:58.990313Z","shell.execute_reply":"2023-11-27T14:07:59.835469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Train images (without boats):')\nnp.vectorize(lambda x: read_sample(x, show=True, subset='train'), signature='()->(n,m,3),(p,q)')(selected_nb_ims[:3])\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:07:59.837678Z","iopub.execute_input":"2023-11-27T14:07:59.837959Z","iopub.status.idle":"2023-11-27T14:08:01.177226Z","shell.execute_reply.started":"2023-11-27T14:07:59.837934Z","shell.execute_reply":"2023-11-27T14:08:01.176392Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Train images (with boats):')\nnp.vectorize(lambda x: read_sample(x, show=True, subset='train'), signature='()->(n,m,3),(p,q)')(selected_b_ims[:3])\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:08:01.178446Z","iopub.execute_input":"2023-11-27T14:08:01.178732Z","iopub.status.idle":"2023-11-27T14:08:02.475562Z","shell.execute_reply.started":"2023-11-27T14:08:01.178708Z","shell.execute_reply":"2023-11-27T14:08:02.474648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preprocessing","metadata":{}},{"cell_type":"code","source":"train_ims, val_ims = train_test_split(imids, \n                 test_size = 0.05, \n                 stratify = imids['Boats'])\n\nprint('Number of Train images', len(train_ims))\nprint('Number of Val. images', len(val_ims))\n\ntrain_ims.plot(kind='hist', bins=15, ax=plt.subplot(2,1,1))\nplt.title('Distribution of boats` number in Train')\nplt.show()\n\nval_ims.plot(kind='hist', bins=15, ax=plt.subplot(2,1,2))\nplt.title('Distribution of boats` number in Val.')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:08:02.477054Z","iopub.execute_input":"2023-11-27T14:08:02.477608Z","iopub.status.idle":"2023-11-27T14:08:03.125404Z","shell.execute_reply.started":"2023-11-27T14:08:02.477575Z","shell.execute_reply":"2023-11-27T14:08:03.123395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def tf_dataset_generator(ims, data_augmentation=None):\n    for im in ims:\n        x, y = data_preprocessing(*read_sample(im, show=False, subset='train'))\n\n        yield x, y\n\ndef get_dataset(ims, batch_size=BATCH_SIZE, data_augmentation=None):\n    dataset = tf.data.Dataset.from_generator(\n        lambda: tf_dataset_generator(ims, data_augmentation),\n        output_signature=(\n            tf.TensorSpec(shape=(384, 384, 3), dtype=tf.float32),\n            tf.TensorSpec(shape=(384, 384, 1), dtype=tf.float32)\n        )\n    )\n    dataset = dataset.map(lambda x, y: {'image': x, 'mask': y})\n    return dataset.batch(batch_size).prefetch(tf.data.AUTOTUNE)\n\ndef visualize_batch(images, masks):\n    batch_size = images.shape[0]\n\n    plt.figure(figsize=(15, 15))\n\n    for i in range(batch_size):\n        plt.subplot(batch_size//4, 8, 2 * i + 1)\n        plt.imshow(images[i])\n        plt.title(f\"Sample {i + 1}\")\n        plt.axis('off')\n\n        plt.subplot(batch_size//4, 8, 2 * i + 2)\n        plt.imshow(masks[i], cmap='gray')\n        plt.title(f\"Mask {i + 1}\")\n        plt.axis('off')\n\n    plt.show()\n    \ndef data_preprocessing(image, mask):\n    image = tf.convert_to_tensor(image, dtype=tf.uint8)\n    mask = tf.convert_to_tensor(mask, dtype=tf.float32)\n    \n    mask = tf.reshape(mask, (*tf.shape(mask), 1))\n    \n    image = tf.image.convert_image_dtype(image, tf.float32)\n    \n    return tf.image.resize(image, (384, 384)), tf.image.resize(mask, (384,384))\n\ndef visualize_images(images):\n    batch_size = images.shape[0]\n\n    plt.figure(figsize=(15, 15//2))\n\n    for i in range(batch_size):\n        plt.subplot(batch_size//8, 8, i + 1)\n        plt.imshow(images[i])\n        plt.title(f\"Sample {i + 1}\")\n        plt.axis('off')\n\n    plt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:08:03.127207Z","iopub.execute_input":"2023-11-27T14:08:03.127510Z","iopub.status.idle":"2023-11-27T14:08:03.141214Z","shell.execute_reply.started":"2023-11-27T14:08:03.127484Z","shell.execute_reply":"2023-11-27T14:08:03.140298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds, val_ds = get_dataset(train_ims.index.tolist()), get_dataset(val_ims.index.tolist())\n\nfor sample in train_ds.take(1):\n    image, mask = sample['image'], sample['mask']\n    print(\"Image shape:\", image.shape)\n    print(\"Mask shape:\", mask.shape)\n    \nvisualize_batch(image, mask)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:08:03.142298Z","iopub.execute_input":"2023-11-27T14:08:03.142566Z","iopub.status.idle":"2023-11-27T14:08:16.914066Z","shell.execute_reply.started":"2023-11-27T14:08:03.142543Z","shell.execute_reply":"2023-11-27T14:08:16.913091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# U-net\n\n+ [U-net Paper](https://arxiv.org/abs/1505.04597)","metadata":{}},{"cell_type":"markdown","source":"## Data Augmentation","metadata":{}},{"cell_type":"code","source":"class RandomFlip(tf.keras.layers.Layer):\n    def __init__(self, seed=42, **kwargs):\n        super(RandomFlip, self).__init__(**kwargs)\n        self.augment_image = tf.keras.layers.RandomFlip(mode=\"horizontal_and_vertical\", seed=seed)\n        self.augment_mask = tf.keras.layers.RandomFlip(mode=\"horizontal_and_vertical\", seed=seed)\n\n    def call(self, inputs, training=None):\n        images, masks = inputs\n\n        if training:\n            images = self.augment_image(images)\n            masks = self.augment_mask(masks)\n\n        return images, masks\n    \nclass RandomRotation(tf.keras.layers.Layer):\n    def __init__(self, seed=42, factor=(-0.2, 0.2), **kwargs):\n        super(RandomRotation, self).__init__(**kwargs)\n        self.augment_image = tf.keras.layers.experimental.preprocessing.RandomRotation(factor, seed=seed, interpolation='nearest')\n        self.augment_mask = tf.keras.layers.experimental.preprocessing.RandomRotation(factor, seed=seed, interpolation='nearest')\n\n    def call(self, inputs, training=None):\n        images, masks = inputs\n        \n        if training:\n            images = self.augment_image(images)\n            masks = self.augment_mask(masks)\n\n        return images, masks\n    \nclass RandomLighting(tf.keras.layers.Layer):\n    def __init__(self, brightness_range=0.2, contrast_range=(0.5, 1.2), **kwargs):\n        super(RandomLighting, self).__init__(**kwargs)\n        self.brightness_range = brightness_range\n        self.contrast_range = contrast_range\n\n    def call(self, inputs, training=None):\n        images, masks = inputs\n\n        images = tf.image.random_brightness(images, self.brightness_range)\n        images = tf.image.random_contrast(images, lower=self.contrast_range[0], upper=self.contrast_range[1])\n        \n        return images, masks\n    ","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-11-27T14:08:16.915347Z","iopub.execute_input":"2023-11-27T14:08:16.915650Z","iopub.status.idle":"2023-11-27T14:08:16.929635Z","shell.execute_reply.started":"2023-11-27T14:08:16.915624Z","shell.execute_reply":"2023-11-27T14:08:16.928702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Mixed Loss\n\nDue to unweighted class distribution I would use combination of BFC and Dise Score","metadata":{}},{"cell_type":"code","source":"class DiceScore(tf.keras.metrics.Metric):\n    def __init__(self, smooth=1.0, **kwargs):\n        super(DiceScore, self).__init__(name='dice_score', **kwargs)\n        self.smooth = smooth\n        self.intersection = self.add_weight(name='intersection', initializer='zeros')\n        self.union = self.add_weight(name='union', initializer='zeros')\n\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        y_true = tf.cast(y_true, dtype=tf.float32)\n        y_pred = tf.cast(y_pred > 0, dtype=tf.float32)\n\n        intersection = tf.reduce_sum(y_true * y_pred, axis=[1, 2])\n        union = tf.reduce_sum(y_true) + tf.reduce_sum(y_pred)\n\n        self.intersection.assign_add(tf.reduce_sum(intersection))\n        self.union.assign_add(tf.reduce_sum(union))\n\n    def result(self):\n        intersection_value = self.intersection\n        union_value = self.union\n        dice = tf.reduce_mean((2. * intersection_value + self.smooth) / (union_value + self.smooth))\n        return dice\n\n\ndef dice_score(y_true, y_pred):\n    smooth=1.0\n    \n    y_true = tf.cast(y_true, dtype=tf.float32)\n    y_pred = tf.cast(y_pred > 0, dtype=tf.float32)\n\n    intersection = tf.reduce_sum(y_true * y_pred, axis=[1, 2])\n    union = tf.reduce_sum(y_true) + tf.reduce_sum(y_pred)\n    \n    dice = tf.reduce_mean((2. * intersection + smooth) / (union + smooth))\n    return dice\n    \nclass MixedLoss(tf.keras.losses.Loss):\n    def __init__(self, alpha, gamma, smooth=1.0, **kwargs):\n        super(MixedLoss, self).__init__(**kwargs)\n        self.alpha = alpha\n#         self.dice_score = DiceScore(smooth)\n        self.binary_focal_loss = tf.keras.losses.BinaryFocalCrossentropy(gamma=gamma)\n        \n    def call(self, y_true, y_pred, sample_weight=None):\n        dice_loss = tf.math.log(dice_score(y_true, y_pred))\n        focal_loss = self.binary_focal_loss(y_true, y_pred)\n\n        loss = self.alpha * focal_loss - dice_loss\n\n        return tf.reduce_mean(loss)\n","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:08:16.930751Z","iopub.execute_input":"2023-11-27T14:08:16.931035Z","iopub.status.idle":"2023-11-27T14:08:16.946614Z","shell.execute_reply.started":"2023-11-27T14:08:16.931013Z","shell.execute_reply":"2023-11-27T14:08:16.945757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"np.random.seed(42)\ny_true = np.random.randint(0, 2, size=(128, 128, 1), dtype=np.uint8)\n# y_pred = np.random.rand(128, 128, 1)\ny_pred = np.zeros((128, 128, 1))\n\ny_true = tf.constant(y_true, dtype=tf.float32)\ny_pred = tf.constant(y_pred, dtype=tf.float32)\n\ndice_metric = DiceScore(smooth=0.1)\ndice_metric.update_state(y_true, y_pred)\n\ndice_result = dice_metric.result()\n\nprint(f'Dice Score: {dice_result.numpy()}')","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:08:16.947915Z","iopub.execute_input":"2023-11-27T14:08:16.948532Z","iopub.status.idle":"2023-11-27T14:08:17.135347Z","shell.execute_reply.started":"2023-11-27T14:08:16.948500Z","shell.execute_reply":"2023-11-27T14:08:17.134411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dice_score(y_true, y_pred)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:08:17.136766Z","iopub.execute_input":"2023-11-27T14:08:17.137151Z","iopub.status.idle":"2023-11-27T14:08:17.146605Z","shell.execute_reply.started":"2023-11-27T14:08:17.137119Z","shell.execute_reply":"2023-11-27T14:08:17.145720Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## U-net","metadata":{}},{"cell_type":"code","source":"# Building convolution blocks\n\nclass ConvBlock(tf.keras.layers.Layer):\n    def __init__(self, num_filters, **kwargs):\n        super(ConvBlock, self).__init__(**kwargs)\n        self.num_filters = num_filters\n    \n        self.conv1=tf.keras.layers.Conv2D(num_filters, 3, padding='same')\n        self.bn1=tf.keras.layers.BatchNormalization()\n        \n        self.conv2=tf.keras.layers.Conv2D(num_filters, 3, padding='same')\n        self.bn2=tf.keras.layers.BatchNormalization()\n        \n    def call(self, inputs, training=False):\n        x = self.conv1(inputs)\n        x = self.bn1(x, training=training)\n        x = tf.keras.layers.Activation(\"relu\")(x)\n        \n        x = self.conv2(x)\n        x = self.bn2(x, training=training)\n        x = tf.keras.layers.Activation(\"relu\")(x)\n        \n        return x\n    \nclass UpConvBlock(tf.keras.layers.Layer):\n    def __init__(self, num_filters, **kwargs):\n        super(UpConvBlock, self).__init__(**kwargs)\n        self.num_filters = num_filters\n        \n        self.conv_transpose1 = tf.keras.layers.Conv2DTranspose(num_filters, (2,2), strides=2, padding='same')\n        self.conv_block1 = ConvBlock(num_filters)\n        \n    def call(self, inputs, skip_features, training=False):\n        x = self.conv_transpose1(inputs)\n        x = tf.keras.layers.Concatenate()([x, skip_features])\n        x = self.conv_block1(x, training=training)\n        \n        return x\n    \n    \nclass LightUNet(tf.keras.Model):\n    def __init__(self, base_model_name):\n        super(LightUNet, self).__init__(name='ResNet50v2Unet')\n        self.base_model_name = base_model_name\n        \n    def build(self, input_shape):\n        input_image_shape, input_mask_shape = input_shape['image'], input_shape['mask']\n        image = tf.keras.layers.Input(shape=input_image_shape, name='image')\n        mask = tf.keras.layers.Input(shape=input_mask_shape, name='mask')\n        \n        self.data_augmentation = tf.keras.Sequential([\n            RandomRotation(),\n            RandomFlip(),\n            RandomLighting()\n        ])\n        \n        if self.base_model_name=='resnet50v2':\n\n            base_model  = tf.keras.applications.resnet_v2.ResNet50V2(input_tensor=image,\n                                           include_top=False,\n                                           weights='imagenet')\n            base_model.trainable = False\n\n            skip_connections_ = [\n                base_model.get_layer(\"conv3_block4_1_relu\").output,  # shape=(48, 48, 512)\n                base_model.get_layer(\"conv2_block3_1_relu\").output,  # shape=(96, 96, 256)\n                base_model.get_layer(\"conv1_conv\").output,       # shape=(192, 192, 64)\n                base_model.get_layer(\"image\").output             # shape=(384, 384, 3)\n            ]\n\n            bridge_ = base_model.get_layer(\"conv4_block6_1_relu\").output  #24\n        \n        elif self.base_model_name=='vgg16':\n        \n            base_model = tf.keras.applications.vgg16.VGG16(input_tensor=image,\n                                           include_top=False,\n                                           weights='imagenet')\n\n            base_model.trainable = False\n\n            skip_connections_ = [\n                base_model.get_layer(\"block4_conv3\").output,  # shape=(48, 48, 512)\n                base_model.get_layer(\"block3_conv3\").output,  # shape=(96, 96, 256)\n                base_model.get_layer(\"block2_conv2\").output,       # shape=(192, 192, 64)\n                base_model.get_layer(\"image\").output             # shape=(384, 384, 3)\n            ]\n\n            bridge_ = base_model.get_layer(\"block5_conv3\").output  #24\n        \n        \n        self.skip_connections = tf.keras.Model(inputs = base_model.input, outputs = skip_connections_, trainable=True)\n        self.bridge = tf.keras.Model(inputs = base_model.input, outputs = bridge_, trainable=True)\n        \n        self.up_conv1 = UpConvBlock(384)\n        self.up_conv2 = UpConvBlock(192)     \n        self.up_conv3 = UpConvBlock(96)                \n        self.up_conv4 = UpConvBlock(48)\n\n        self.output_conv = tf.keras.layers.Conv2D(48, 3, padding='same', activation='relu')\n        self.output_layer = tf.keras.layers.Conv2D(1, 1, padding='same', activation='sigmoid')\n        \n        super().build({'image':(None, *input_shape['image']), 'mask':(None, *input_shape['mask'])}) \n\n    \n    def call(self, inputs, training=True):\n        image, mask = inputs['image'], inputs['mask']\n        if training: image, mask = self.data_augmentation(inputs=(image, mask), training=training)\n        skip_connections, bridge = self.skip_connections(image), self.bridge(image)\n        x = self.up_conv1(bridge, skip_connections[0], training=training)\n        x = self.up_conv2(x, skip_connections[1], training=training)\n        x = self.up_conv3(x, skip_connections[2], training=training)\n        x = self.up_conv4(x, skip_connections[3], training=training)\n        x = self.output_conv(x)\n        \n        outputs = self.output_layer(x)\n\n        return outputs\n    \n    def predict(self, image):\n        skip_connections, bridge = self.skip_connections(image), self.bridge(image)\n        x = self.up_conv1(bridge, skip_connections[0], training=False)\n        x = self.up_conv2(x, skip_connections[1], training=False)\n        x = self.up_conv3(x, skip_connections[2], training=False)\n        x = self.up_conv4(x, skip_connections[3], training=False)\n        x = self.output_conv(x)\n        pred = self.output_layer(x)\n        \n        return pred\n    \n    def test_step(self, inputs):\n        x, y_true = inputs['image'], inputs['mask']\n\n        # Forward pass\n        y_pred = self.predict(x)\n\n        dice = dice_score(y_true, y_pred)\n        bfc = tf.keras.metrics.binary_focal_crossentropy(y_true, y_pred, gamma=2.0)\n\n        return {'dice_score': dice, 'binary_focal_crossentropy': bfc}\n    \n    def train_step(self, inputs):\n        x, y_true = inputs['image'], inputs['mask']\n\n        # Forward pass\n        with tf.GradientTape() as tape:\n            y_pred = self.predict(x)\n            loss = self.compiled_loss(y_true, y_pred)\n\n        # Backward pass\n        gradients = tape.gradient(loss, self.trainable_variables)\n        self.optimizer.apply_gradients(zip(gradients, self.trainable_variables))\n\n        dice = dice_score(y_true, y_pred)\n        bfc = tf.keras.metrics.binary_focal_crossentropy(y_true, y_pred, gamma=2.0)\n\n        return {'dice_score': dice, 'binary_focal_crossentropy': bfc}\n    \n    def save(self, checkpoints_path):\n        # Save the entire model to a HDF5 file\n        self.save_weights(checkpoints_path)\n\n    def load(self, checkpoints_path):\n        # Load the model weights from a HDF5 file\n        self.load_weights(checkpoints_path)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:33:27.798465Z","iopub.execute_input":"2023-11-27T14:33:27.798883Z","iopub.status.idle":"2023-11-27T14:33:27.834924Z","shell.execute_reply.started":"2023-11-27T14:33:27.798854Z","shell.execute_reply":"2023-11-27T14:33:27.833948Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = LightUNet(base_model_name='vgg16')\nmodel.build(input_shape = {'image':(384, 384, 3), 'mask': (384, 384, 1)})\n\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:33:31.634410Z","iopub.execute_input":"2023-11-27T14:33:31.634771Z","iopub.status.idle":"2023-11-27T14:33:32.623949Z","shell.execute_reply.started":"2023-11-27T14:33:31.634743Z","shell.execute_reply":"2023-11-27T14:33:32.623027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(\n    optimizer=tf.keras.optimizers.Adam(lr = 1e-2),\n    loss = MixedLoss(alpha=10.0, gamma=2.0)\n#     metrics=[dice_score, 'binary_focal_crossentropy'],\n)\n\nhistory = model.fit(\n    train_ds.take(10),\n    validation_data=val_ds.take(2),\n    epochs=1,\n)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:33:32.674925Z","iopub.execute_input":"2023-11-27T14:33:32.675212Z","iopub.status.idle":"2023-11-27T14:34:24.925612Z","shell.execute_reply.started":"2023-11-27T14:33:32.675189Z","shell.execute_reply":"2023-11-27T14:34:24.924814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(\n    train_ds.take(10),\n    validation_data=val_ds.take(2),\n    epochs=10,\n)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:34:24.927253Z","iopub.execute_input":"2023-11-27T14:34:24.927518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predictions","metadata":{}},{"cell_type":"code","source":"def multi_rle_encode(img):\n    labels = label(img[:, :, 0])\n    return [rle_encode(labels==k) for k in np.unique(labels[labels>0.3])]\n\ndef rle_encode(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:11:08.693915Z","iopub.status.idle":"2023-11-27T14:11:08.694378Z","shell.execute_reply.started":"2023-11-27T14:11:08.694143Z","shell.execute_reply":"2023-11-27T14:11:08.694164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_sample, _ = read_sample('0010551d9.jpg', subset='test', show=True)\ntest_sample = cv2.resize(test_sample, (384,384))\ntest_sample = test_sample/255.0\ntest_sample = test_sample.reshape(1,384, 384, 3)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_pred = model.predict(test_sample).numpy()\ntest_pred = test_pred.reshape(384, 384, 1)\ndestination_size = (384 * 2, 384 * 2)\ntest_pred = cv2.resize(test_pred, destination_size)\ntest_pred[test_pred<=0.3]=0\ntest_pred[test_pred>0.3]=1\nplt.imshow(test_pred)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Cell for Kaggle submission...\n\npred_ = []\nfor test_im in test_ims:\n    test_sample, _ = read_sample('0010551d9.jpg', subset='test', show=False)\n    test_sample = cv2.resize(test_sample, (384,384))\n    test_sample = test_sample/255.0\n    test_sample = test_sample.reshape(1,384, 384, 3)\n    test_pred = model.predict(test_sample).numpy()\n    test_pred = test_pred.reshape(384, 384, 1)\n    test_pred = cv2.resize(test_pred, destination_size)\n    encodings = multi_rle_encode(test_pred)\n    pred_.append([{'ImageId': img_id, 'EncodedPixels': encoding} \n                      if encodings \n                      else {'ImageId': img_id, 'EncodedPixels': None} \n                      for encoding in encodings])\n    \nresult_df = pd.DataFrame(pred_)[['ImageId', 'EncodedPixels']]\nresult_df.to_csv('result.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2023-11-27T14:08:17.979855Z","iopub.status.idle":"2023-11-27T14:08:17.980173Z","shell.execute_reply.started":"2023-11-27T14:08:17.980016Z","shell.execute_reply":"2023-11-27T14:08:17.980030Z"},"trusted":true},"execution_count":null,"outputs":[]}]}