{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"\n# for data handling \nimport cv2\nimport glob \nimport math\nimport numpy as np \nimport pandas as pd\nimport matplotlib.pyplot as plt \nfrom keras.preprocessing.image import save_img\nfrom sklearn.model_selection import train_test_split\n\n# for neural network\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom keras.models import Model, Sequential # Models\n#from keras.layers import LSTM, Dense, RepeatVector,TimeDistributed, Input # Layers\n#from keras.layers import Conv2D, MaxPooling2D, UpSampling2D, Flatten, Reshape # Layers\n\n# for data visualization \nfrom keras.callbacks import ModelCheckpoint, History\nfrom keras_tqdm import TQDMCallback, TQDMNotebookCallback\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"#Input data files are available in the \"../input/\" directory\n#Convert from markdown to code to visualize all available input \n\nimport os \ninputFolder = '../input/' \nfor root, directories, filenames in os.walk(inputFolder): \n    for filename in filenames: print(os.path.join(root,filename))"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# Set paths to data\ntrainPath = '../input/understanding_cloud_organization/train_images/'\ntestPath = '../input/understanding_cloud_organization/test_images/'\n\n# Get the csv with labels and the images for training and testing\ndataCSV = pd.read_csv('../input/understanding_cloud_organization/train.csv')\ntrainData= sorted(glob.glob(trainPath+ '*.jpg'))\ntestData = sorted(glob.glob(testPath + '*.jpg'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Exploring the dataset\nprint('There are', len(trainData), ' images in the train set and',len(testData),'images in the test set ')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Visualizing the dataset\nlabels = 'Train', 'Test'\nsizes = [len(trainData), len(testData)]\nexplode = (0, 0.1)\n\nfig, ax = plt.subplots(figsize=(6, 6))\nax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90)\nax.axis('equal')\nax.set_title('Train and Test Sets')\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Lets look at the CSV data\ndataCSV.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"So there are two columns, one for the image label, and another one for the masks in the cloud. But not all images have masks. Lets separate file name from label. "},{"metadata":{"trusted":true},"cell_type":"code","source":"# split column\nsplitCSV = dataCSV[\"Image_Label\"].str.split(\"_\", n = 1, expand = True)\n# add new columns to dataCSV\ndataCSV['Image'] = splitCSV[0]\ndataCSV['Label'] = splitCSV[1]\n\n# check the result\ndataCSV.head()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fish = dataCSV[dataCSV['Label'] == 'Fish'].EncodedPixels.count()\nflower = dataCSV[dataCSV['Label'] == 'Flower'].EncodedPixels.count()\ngravel = dataCSV[dataCSV['Label'] == 'Gravel'].EncodedPixels.count()\nsugar = dataCSV[dataCSV['Label'] == 'Sugar'].EncodedPixels.count()\n\nprint('There are {} fish clouds'.format(fish))\nprint('There are {} flower clouds'.format(flower))\nprint('There are {} gravel clouds'.format(gravel))\nprint('There are {} sugar clouds'.format(sugar))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# plotting a pie chart\nlabels = 'Fish', 'Flower', 'Gravel', 'Sugar'\nsizes = [fish, flower, gravel, sugar]\n\nfig, ax = plt.subplots(figsize=(6, 6))\nax.pie(sizes, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90)\nax.axis('equal')\nax.set_title('Cloud Types')\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Run-length decoder\ndef rle_decode(mask_rle: str = '', shape: tuple = (1400, 2100)):\n    '''\n    Decode rle encoded mask.\n    \n    :param mask_rle: run-length as string formatted (start length)\n    :param shape: (height, width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0] * shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape, order='F')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Plot 4 images of each class\nimport os\npath = '../input/understanding_cloud_organization'\nos.listdir(path)\n\n# Images known to contain the classes\nfish_imgs = ['0766057', '0770f53', '07c5a0d' , '07c5fc9']\nflower_imgs = ['0741fda', '0745d08', '07551f3', '0761274']\ngravel_imgs = ['077bd40', '080c004' , '08329b8', '0862841']\nsugar_imgs = ['0770f53', '0778609', '0799206',  '079aef5']\n\ncolumns = 4\nrows = 4\nfig, ax = plt.subplots(rows, columns, figsize=(18, 13))\nax[0, 0].set_title('Fish', fontsize=20)\nax[0, 1].set_title('Flower', fontsize=20)\nax[0, 2].set_title('Gravel', fontsize=20)\nax[0, 3].set_title('Sugar', fontsize=20)\nfor i in range(len(fish_imgs)):\n    fish_img = plt.imread(f\"{path}/train_images/{fish_imgs[i]}.jpg\")\n    ax[i, 0].imshow(fish_img)\n    image_label = f'{fish_imgs[i]}.jpg_Fish'\n    mask_rle = dataCSV.loc[dataCSV['Image_Label'] == image_label, 'EncodedPixels'].values[0]\n    mask = rle_decode(mask_rle)\n    ax[i, 0].imshow(mask, alpha=0.5, cmap='gray')\n    \n    flower_img = plt.imread(f\"{path}/train_images/{flower_imgs[i]}.jpg\")\n    ax[i, 1].imshow(flower_img)\n    image_label = f'{flower_imgs[i]}.jpg_Flower'\n    mask_rle = dataCSV.loc[dataCSV['Image_Label'] == image_label, 'EncodedPixels'].values[0]\n    mask = rle_decode(mask_rle)\n    ax[i, 1].imshow(mask, alpha=0.5, cmap='gray')\n    \n    gravel_img = plt.imread(f\"{path}/train_images/{gravel_imgs[i]}.jpg\")\n    ax[i, 2].imshow(gravel_img)\n    image_label = f'{gravel_imgs[i]}.jpg_Gravel'\n    mask_rle = dataCSV.loc[dataCSV['Image_Label'] == image_label, 'EncodedPixels'].values[0]\n    mask = rle_decode(mask_rle)\n    ax[i, 2].imshow(mask, alpha=0.5, cmap='gray')\n    \n    sugar_img = plt.imread(f\"{path}/train_images/{sugar_imgs[i]}.jpg\")\n    ax[i, 3].imshow(sugar_img)\n    image_label = f'{sugar_imgs[i]}.jpg_Sugar'\n    mask_rle = dataCSV.loc[dataCSV['Image_Label'] == image_label, 'EncodedPixels'].values[0]\n    mask = rle_decode(mask_rle)\n    ax[i, 3].imshow(mask, alpha=0.5, cmap='gray')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Preprocessing"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/understanding_cloud_organization/train.csv')\ntrain_df['ImageId'] = train_df['Image_Label'].apply(lambda x: x.split('_')[0])\ntrain_df['ClassId'] = train_df['Image_Label'].apply(lambda x: x.split('_')[1])\ntrain_df['hasMask'] = ~ train_df['EncodedPixels'].isna()\n\nprint(train_df.shape)\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mask_count_df = train_df.groupby('ImageId').agg(np.sum).reset_index()\nmask_count_df.sort_values('hasMask', ascending=False, inplace=True)\nprint(mask_count_df.shape)\nmask_count_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Loss function"},{"metadata":{"trusted":true},"cell_type":"code","source":"def dice_coef(y_true, y_pred, smooth=1):\n    y_true_f = K.flatten(y_true)\n    y_pred_f = K.flatten(y_pred)\n    intersection = K.sum(y_true_f * y_pred_f)\n    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n\ndef dice_loss(y_true, y_pred):\n    smooth = 1.\n    y_true_f = K.flatten(y_true)\n    y_pred_f = K.flatten(y_pred)\n    intersection = y_true_f * y_pred_f\n    score = (2. * K.sum(intersection) + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\n    return 1. - score\n\ndef bce_dice_loss(y_true, y_pred):\n    return binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Data generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DataGenerator(keras.utils.Sequence):\n    'Generates data for Keras'\n    def __init__(self, list_IDs, df, target_df=None, mode='fit',\n                 base_path='../input/train_images',\n                 batch_size=32, dim=(1400, 2100), n_channels=3, reshape=None,\n                 augment=False, n_classes=4, random_state=2019, shuffle=True):\n        self.dim = dim\n        self.batch_size = batch_size\n        self.df = df\n        self.mode = mode\n        self.base_path = base_path\n        self.target_df = target_df\n        self.list_IDs = list_IDs\n        self.reshape = reshape\n        self.n_channels = n_channels\n        self.augment = augment\n        self.n_classes = n_classes\n        self.shuffle = shuffle\n        self.random_state = random_state\n        \n        self.on_epoch_end()\n        np.random.seed(self.random_state)\n\n    def __len__(self):\n        'Denotes the number of batches per epoch'\n        return int(np.floor(len(self.list_IDs) / self.batch_size))\n\n    def __getitem__(self, index):\n        'Generate one batch of data'\n        # Generate indexes of the batch\n        indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]\n\n        # Find list of IDs\n        list_IDs_batch = [self.list_IDs[k] for k in indexes]\n        \n        X = self.__generate_X(list_IDs_batch)\n        \n        if self.mode == 'fit':\n            y = self.__generate_y(list_IDs_batch)\n            \n            if self.augment:\n                X, y = self.__augment_batch(X, y)\n            \n            return X, y\n        \n        elif self.mode == 'predict':\n            return X\n\n        else:\n            raise AttributeError('The mode parameter should be set to \"fit\" or \"predict\".')\n        \n    def on_epoch_end(self):\n        'Updates indexes after each epoch'\n        self.indexes = np.arange(len(self.list_IDs))\n        if self.shuffle == True:\n            np.random.seed(self.random_state)\n            np.random.shuffle(self.indexes)\n    \n    def __generate_X(self, list_IDs_batch):\n        'Generates data containing batch_size samples'\n        # Initialization\n        if self.reshape is None:\n            X = np.empty((self.batch_size, *self.dim, self.n_channels))\n        else:\n            X = np.empty((self.batch_size, *self.reshape, self.n_channels))\n        \n        # Generate data\n        for i, ID in enumerate(list_IDs_batch):\n            im_name = self.df['ImageId'].iloc[ID]\n            img_path = f\"{self.base_path}/{im_name}\"\n            img = self.__load_rgb(img_path)\n            \n            if self.reshape is not None:\n                img = np_resize(img, self.reshape)\n            \n            # Store samples\n            X[i,] = img\n\n        return X\n    \n    def __generate_y(self, list_IDs_batch):\n        if self.reshape is None:\n            y = np.empty((self.batch_size, *self.dim, self.n_classes), dtype=int)\n        else:\n            y = np.empty((self.batch_size, *self.reshape, self.n_classes), dtype=int)\n        \n        for i, ID in enumerate(list_IDs_batch):\n            im_name = self.df['ImageId'].iloc[ID]\n            image_df = self.target_df[self.target_df['ImageId'] == im_name]\n            \n            rles = image_df['EncodedPixels'].values\n            \n            if self.reshape is not None:\n                masks = build_masks(rles, input_shape=self.dim, reshape=self.reshape)\n            else:\n                masks = build_masks(rles, input_shape=self.dim)\n            \n            y[i, ] = masks\n\n        return y\n    \n    def __load_grayscale(self, img_path):\n        img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)\n        img = img.astype(np.float32) / 255.\n        img = np.expand_dims(img, axis=-1)\n\n        return img\n    \n    def __load_rgb(self, img_path):\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = img.astype(np.float32) / 255.\n\n        return img\n    \n    def __random_transform(self, img, masks):\n        composition = albu.Compose([\n            albu.HorizontalFlip(),\n            albu.VerticalFlip(),\n            albu.ShiftScaleRotate(rotate_limit=45, shift_limit=0.15, scale_limit=0.15)\n        ])\n        \n        composed = composition(image=img, mask=masks)\n        aug_img = composed['image']\n        aug_masks = composed['mask']\n        \n        return aug_img, aug_masks\n    \n    def __augment_batch(self, img_batch, masks_batch):\n        for i in range(img_batch.shape[0]):\n            img_batch[i, ], masks_batch[i, ] = self.__random_transform(\n                img_batch[i, ], masks_batch[i, ])\n        \n        return img_batch, masks_batch","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The Network"},{"metadata":{"trusted":true},"cell_type":"code","source":"def unet(input_shape):\n    \"\"\"\n    This is the old model. Best LB is ~0.5\n    \"\"\"\n    inputs = Input(input_shape)\n\n    c1 = Conv2D(8, (3, 3), activation='elu', padding='same') (inputs)\n    c1 = Conv2D(8, (3, 3), activation='elu', padding='same') (c1)\n    p1 = MaxPooling2D((2, 2), padding='same') (c1)\n\n    c2 = Conv2D(16, (3, 3), activation='elu', padding='same') (p1)\n    c2 = Conv2D(16, (3, 3), activation='elu', padding='same') (c2)\n    p2 = MaxPooling2D((2, 2), padding='same') (c2)\n\n    c3 = Conv2D(32, (3, 3), activation='elu', padding='same') (p2)\n    c3 = Conv2D(32, (3, 3), activation='elu', padding='same') (c3)\n    p3 = MaxPooling2D((2, 2), padding='same') (c3)\n\n    c4 = Conv2D(64, (3, 3), activation='elu', padding='same') (p3)\n    c4 = Conv2D(64, (3, 3), activation='elu', padding='same') (c4)\n    p4 = MaxPooling2D((2, 2), padding='same') (c4)\n\n    c5 = Conv2D(64, (3, 3), activation='elu', padding='same') (p4)\n    c5 = Conv2D(64, (3, 3), activation='elu', padding='same') (c5)\n    p5 = MaxPooling2D((2, 2), padding='same') (c5)\n\n    c55 = Conv2D(128, (3, 3), activation='elu', padding='same') (p5)\n    c55 = Conv2D(128, (3, 3), activation='elu', padding='same') (c55)\n\n    u6 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same') (c55)\n    u6 = concatenate([u6, c5])\n    c6 = Conv2D(64, (3, 3), activation='elu', padding='same') (u6)\n    c6 = Conv2D(64, (3, 3), activation='elu', padding='same') (c6)\n\n    u71 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same') (c6)\n    u71 = concatenate([u71, c4])\n    c71 = Conv2D(32, (3, 3), activation='elu', padding='same') (u71)\n    c61 = Conv2D(32, (3, 3), activation='elu', padding='same') (c71)\n\n    u7 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same') (c61)\n    u7 = concatenate([u7, c3])\n    c7 = Conv2D(32, (3, 3), activation='elu', padding='same') (u7)\n    c7 = Conv2D(32, (3, 3), activation='elu', padding='same') (c7)\n\n    u8 = Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same') (c7)\n    u8 = concatenate([u8, c2])\n    c8 = Conv2D(16, (3, 3), activation='elu', padding='same') (u8)\n    c8 = Conv2D(16, (3, 3), activation='elu', padding='same') (c8)\n\n    u9 = Conv2DTranspose(8, (2, 2), strides=(2, 2), padding='same') (c8)\n    u9 = concatenate([u9, c1], axis=3)\n    c9 = Conv2D(8, (3, 3), activation='elu', padding='same') (u9)\n    c9 = Conv2D(8, (3, 3), activation='elu', padding='same') (c9)\n\n    outputs = Conv2D(4, (1, 1), activation='sigmoid') (c9)\n\n    model = Model(inputs=[inputs], outputs=[outputs])\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Train"},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 32\n\ntrain_idx, val_idx = train_test_split(\n    mask_count_df.index, random_state=2019, test_size=0.2\n)\n\ntrain_generator = DataGenerator(\n    train_idx, \n    df=mask_count_df,\n    target_df=train_df,\n    batch_size=BATCH_SIZE,\n    reshape=(320, 480),\n    augment=True,\n    n_channels=3,\n    n_classes=4\n)\n\nval_generator = DataGenerator(\n    val_idx, \n    df=mask_count_df,\n    target_df=train_df,\n    batch_size=BATCH_SIZE, \n    reshape=(320, 480),\n    augment=False,\n    n_channels=3,\n    n_classes=4\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install segmentation-models --quiet","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = sm.Unet(\n    'resnet34', \n    classes=4,\n    input_shape=(320, 480, 3),\n    activation='sigmoid'\n)\nmodel.compile(optimizer=Nadam(lr=0.0002), loss=bce_dice_loss, metrics=[dice_coef])\nmodel.summary()","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}