{"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":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom PIL import Image\nimport os\nimport pandas as pd\nfrom pathlib import Path\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport random\nimport math\nfrom tensorflow.keras.layers import Input\nfrom tensorflow.keras.layers import Conv2D\nfrom tensorflow.keras.layers import MaxPooling2D\nfrom tensorflow.keras.layers import Dropout \nfrom tensorflow.keras.layers import Conv2DTranspose\nfrom tensorflow.keras.layers import concatenate","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:11:35.841353Z","iopub.execute_input":"2022-08-19T07:11:35.841809Z","iopub.status.idle":"2022-08-19T07:11:41.313343Z","shell.execute_reply.started":"2022-08-19T07:11:35.841721Z","shell.execute_reply":"2022-08-19T07:11:41.312383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ntrain_df = pd.read_csv('../input/hubmap-organ-segmentation/train.csv')\ntrain_img_dir = '../input/hubmap-organ-segmentation/train_images'\nids = train_df['id']\n\ninput_img_paths = sorted([os.path.join(train_img_dir, f\"{id}.tiff\") for id in ids])\nrandom.Random(1337).shuffle(input_img_paths)\n\nbatch_size = 4\nimg_size = 512\nthreshold = 0.3","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:11:51.884193Z","iopub.execute_input":"2022-08-19T07:11:51.885137Z","iopub.status.idle":"2022-08-19T07:11:52.241038Z","shell.execute_reply.started":"2022-08-19T07:11:51.885091Z","shell.execute_reply":"2022-08-19T07:11:52.239980Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def preprocess_image(path):\n    image = keras.utils.load_img(path)\n    original_shape = keras.utils.img_to_array(image).shape\n    image = image.resize((img_size, img_size))\n    image_array = keras.utils.img_to_array(image)/255\n    return image_array, original_shape\n","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:11:55.857357Z","iopub.execute_input":"2022-08-19T07:11:55.857745Z","iopub.status.idle":"2022-08-19T07:11:55.864233Z","shell.execute_reply.started":"2022-08-19T07:11:55.857710Z","shell.execute_reply":"2022-08-19T07:11:55.862888Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class HubmapOrgan(keras.utils.Sequence):\n    def __init__(self, batch_size, img_size, input_img_paths, df, shuffle=True, augment=None):\n        super().__init__()\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.input_img_paths = input_img_paths\n        self.df = df\n        self.shuffle = shuffle\n        self.augment = augment\n       \n        \n    def __len__(self):\n        return math.ceil(len(self.input_img_paths) / self.batch_size)\n    \n    def __getitem__(self, idx):\n        i = idx * self.batch_size\n        batch_img_paths = self.input_img_paths[i : i + self.batch_size]\n        \n        x = np.zeros((self.batch_size, self.img_size, self.img_size, 3), dtype='uint8')\n        y = np.zeros((self.batch_size, self.img_size, self.img_size, 1), dtype='uint8')\n        for j, img_path in enumerate(batch_img_paths):\n            \n            # first load images to target size [512, 512]\n            img = keras.utils.load_img(img_path, \n                                      target_size=(self.img_size, self.img_size))\n            img_array = keras.utils.img_to_array(img, dtype='uint8')\n            x[j] = img_array\n            \n            # calculate mask\n            img_id = Path(img_path).stem\n            h, w= self.df[self.df['id']==int(img_id)]['img_height'].iloc[-1], self.df[self.df['id']==int(img_id)]['img_width'].iloc[-1]\n            rle = self.df[self.df['id']==int(img_id)]['rle'].iloc[-1]\n            s = rle.split()\n            starts, lengths = [np.asarray(t, dtype='int') for t in (s[0:][::2], s[1:][::2])]\n            starts = starts - 1\n            original_mask = np.zeros(h*w, dtype=np.uint8)\n            for s, l in zip(starts, lengths):\n                original_mask[s:s+l] = 1\n            original_mask = original_mask.reshape((h, w)).T\n            # we need at least three channels to save an image so here expand mask to (3000, 3000, 1)\n            original_mask = keras.utils.array_to_img(original_mask[:, :, tf.newaxis], scale=False)\n            \n            # resize mask\n            mask = original_mask.resize((self.img_size, self.img_size))\n            mask_array = keras.utils.img_to_array(mask, dtype='uint8')\n            y[j] = mask_array\n        if self.augment is None:\n            return x.astype('float32')/255, y\n        else:\n            aug_x, aug_y = [], []\n            for image, mask in zip(x, y):\n                transformed = self.augment(image=image, mask=mask)\n                aug_x.append(transformed['image'])\n                aug_y.append(transformed['mask'])\n            return np.array(aug_x), np.array(aug_y)\n    def on_epoch_end(self):\n        if self.shuffle == True:\n            np.random.shuffle(self.input_img_paths)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:12:01.068390Z","iopub.execute_input":"2022-08-19T07:12:01.068835Z","iopub.status.idle":"2022-08-19T07:12:01.086941Z","shell.execute_reply.started":"2022-08-19T07:12:01.068794Z","shell.execute_reply":"2022-08-19T07:12:01.085739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# AUGMENTATION_TRAIN = A.Compose([\n#     A.HorizontalFlip(),\n#     A.VerticalFlip(),\n#     A.ShiftScaleRotate(rotate_limit=0),\n#     A.RGBShift(),\n#     A.ChannelShuffle(),\n#     A.GaussNoise(),\n#     A.ToFloat(max_value=255)\n# ])\n# AUGMENTATION_TEST = A.Compose([\n#     A.ToFloat(max_value=255)\n# ])","metadata":{"execution":{"iopub.status.busy":"2022-08-19T05:58:15.272997Z","iopub.execute_input":"2022-08-19T05:58:15.273425Z","iopub.status.idle":"2022-08-19T05:58:15.284706Z","shell.execute_reply.started":"2022-08-19T05:58:15.273391Z","shell.execute_reply":"2022-08-19T05:58:15.283797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"a = HubmapOrgan(30, img_size, input_img_paths, train_df, shuffle=False, augment=None)\nimages, masks = a.__getitem__(0)\n\nrows = 5\ncols = 2\n\nplt.figure(figsize=((cols*5, rows*5)))\nfor i in range(0, rows*cols, 2):\n    plt.subplot(rows, cols, i+1)\n    plt.title('Image')\n    plt.imshow(images[i])\n\n    plt.subplot(rows, cols, i+2)\n    plt.title('Masks')\n    plt.imshow(images[i])\n    plt.imshow(masks[i], cmap='hot', alpha=0.4)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:12:52.938630Z","iopub.execute_input":"2022-08-19T07:12:52.939247Z","iopub.status.idle":"2022-08-19T07:12:59.590648Z","shell.execute_reply.started":"2022-08-19T07:12:52.939213Z","shell.execute_reply":"2022-08-19T07:12:59.589817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_num = 30\ntrain_img_paths = input_img_paths[:-val_num]\nval_img_paths = input_img_paths[-val_num:]\n\ntrain_gen = HubmapOrgan(batch_size, img_size, train_img_paths, train_df)\nval_gen = HubmapOrgan(batch_size, img_size, val_img_paths, train_df)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:13:11.672437Z","iopub.execute_input":"2022-08-19T07:13:11.672815Z","iopub.status.idle":"2022-08-19T07:13:11.678927Z","shell.execute_reply.started":"2022-08-19T07:13:11.672782Z","shell.execute_reply":"2022-08-19T07:13:11.677549Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# UNQ_C1\n# GRADED FUNCTION: conv_block\ndef conv_block(inputs=None, n_filters=32, dropout_prob=0, max_pooling=True):\n    \"\"\"\n    Convolutional downsampling block\n    \n    Arguments:\n        inputs -- Input tensor\n        n_filters -- Number of filters for the convolutional layers\n        dropout_prob -- Dropout probability\n        max_pooling -- Use MaxPooling2D to reduce the spatial dimensions of the output volume\n    Returns: \n        next_layer, skip_connection --  Next layer and skip connection outputs\n    \"\"\"\n\n    ### START CODE HERE\n    conv = Conv2D(n_filters, # Number of filters\n                  3,   # Kernel size   \n                  activation='relu',\n                  padding='same',\n                  kernel_initializer='he_normal')(inputs)\n    conv = Conv2D(n_filters, # Number of filters\n                  3,   # Kernel size\n                  activation='relu',\n                  padding='same',\n                  kernel_initializer='he_normal')(conv)\n    ### END CODE HERE\n    \n    # if dropout_prob > 0 add a dropout layer, with the variable dropout_prob as parameter\n    if dropout_prob > 0:\n         ### START CODE HERE\n        conv = Dropout(dropout_prob)(conv)\n         ### END CODE HERE\n         \n        \n    # if max_pooling is True add a MaxPooling2D with 2x2 pool_size\n    if max_pooling:\n        ### START CODE HERE\n        next_layer = MaxPooling2D((2,2))(conv)\n        ### END CODE HERE\n        \n    else:\n        next_layer = conv\n        \n    skip_connection = conv\n    \n    return next_layer, skip_connection","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:13:13.317940Z","iopub.execute_input":"2022-08-19T07:13:13.318299Z","iopub.status.idle":"2022-08-19T07:13:13.326860Z","shell.execute_reply.started":"2022-08-19T07:13:13.318269Z","shell.execute_reply":"2022-08-19T07:13:13.325579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def upsampling_block(expansive_input, contractive_input, n_filters=32):\n    \"\"\"\n    Convolutional upsampling block\n    \n    Arguments:\n        expansive_input -- Input tensor from previous layer\n        contractive_input -- Input tensor from previous skip layer\n        n_filters -- Number of filters for the convolutional layers\n    Returns: \n        conv -- Tensor output\n    \"\"\"\n    \n    ### START CODE HERE\n    up = Conv2DTranspose(\n                 n_filters,    # number of filters\n                 (3,3),    # Kernel size\n                 strides=(2,2),\n                 padding='same')(expansive_input)\n    \n    # Merge the previous output and the contractive_input\n    merge = concatenate([up, contractive_input], axis=3)\n    conv = Conv2D(n_filters,   # Number of filters\n                 (3,3),     # Kernel size\n                 activation='relu',\n                 padding='same',\n                 kernel_initializer='he_normal')(merge)\n    conv = Conv2D(n_filters,  # Number of filters\n                 (3,3),   # Kernel size\n                 activation='relu',\n                 padding='same',\n                 kernel_initializer='he_normal')(conv)\n    ### END CODE HERE\n    \n    return conv","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:13:14.777068Z","iopub.execute_input":"2022-08-19T07:13:14.779608Z","iopub.status.idle":"2022-08-19T07:13:14.786181Z","shell.execute_reply.started":"2022-08-19T07:13:14.779569Z","shell.execute_reply":"2022-08-19T07:13:14.785052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# UNQ_C3\n# GRADED FUNCTION: unet_model\ndef unet_model(input_size=(512,512, 3), n_filters=32, n_classes=1):\n    \"\"\"\n    Unet model\n    \n    Arguments:\n        input_size -- Input shape \n        n_filters -- Number of filters for the convolutional layers\n        n_classes -- Number of output classes\n    Returns: \n        model -- tf.keras.Model\n    \"\"\"\n    inputs = Input(input_size)\n    # Contracting Path (encoding)\n    # Add a conv_block with the inputs of the unet_ model and n_filters\n    ### START CODE HERE\n    cblock1 = conv_block(inputs,n_filters)\n    # Chain the first element of the output of each block to be the input of the next conv_block. \n    # Double the number of filters at each new step\n    cblock2 = conv_block(cblock1[0], 2*n_filters)\n    cblock3 = conv_block(cblock2[0],4*n_filters)\n    cblock4 = conv_block(cblock3[0],8*n_filters) # Include a dropout_prob of 0.3 for this layer\n    # Include a dropout_prob of 0.3 for this layer, and avoid the max_pooling layer\n    cblock5 = conv_block(cblock4[0],16*n_filters, dropout_prob=0.3) \n    cblock6 = conv_block(cblock5[0],32*n_filters,dropout_prob=0.3,max_pooling=False)\n    ### END CODE HERE\n    \n    # Expanding Path (decoding)\n    # Add the first upsampling_block.\n    # Use the cblock5[0] as expansive_input and cblock4[1] as contractive_input and n_filters * 8\n    ### START CODE HERE\n    ublock7 = upsampling_block(cblock6[0],cblock5[1],16*n_filters)\n    # Chain the output of the previous block as expansive_input and the corresponding contractive block output.\n    # Note that you must use the second element of the contractive block i.e before the maxpooling layer. \n    # At each step, use half the number of filters of the previous block \n    ublock8 = upsampling_block(ublock7,cblock4[1],  8*n_filters)\n    ublock9 = upsampling_block(ublock8, cblock3[1],  4*n_filters)\n    ublock10 = upsampling_block(ublock9, cblock2[1],  2*n_filters)\n    ublock11 = upsampling_block(ublock10, cblock1[1],  n_filters)\n    ### END CODE HERE\n\n    conv12 = Conv2D(n_filters,\n                 3,\n                 activation='relu',\n                 padding='same',\n                 kernel_initializer='he_normal')(ublock11)\n\n    # Add a Conv2D layer with n_classes filter, kernel size of 1 and a 'same' padding\n    ### START CODE HERE\n    conv13 = Conv2D(n_classes,1, padding='same',activation='sigmoid')(conv12)\n    ### END CODE HERE\n    \n    model = tf.keras.Model(inputs=inputs, outputs=conv13)\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:13:15.611099Z","iopub.execute_input":"2022-08-19T07:13:15.611906Z","iopub.status.idle":"2022-08-19T07:13:15.622387Z","shell.execute_reply.started":"2022-08-19T07:13:15.611862Z","shell.execute_reply":"2022-08-19T07:13:15.621405Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = unet_model(input_size=(img_size,img_size,3))\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:13:17.740956Z","iopub.execute_input":"2022-08-19T07:13:17.741320Z","iopub.status.idle":"2022-08-19T07:13:20.951337Z","shell.execute_reply.started":"2022-08-19T07:13:17.741290Z","shell.execute_reply":"2022-08-19T07:13:20.950423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(optimizer=keras.optimizers.Adam(1e-3),\n             loss=keras.losses.BinaryCrossentropy(),\n             metrics='accuracy')","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:13:28.242705Z","iopub.execute_input":"2022-08-19T07:13:28.243079Z","iopub.status.idle":"2022-08-19T07:13:28.258078Z","shell.execute_reply.started":"2022-08-19T07:13:28.243049Z","shell.execute_reply":"2022-08-19T07:13:28.256904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.fit(train_gen, epochs=8, validation_data=val_gen)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:13:30.917225Z","iopub.execute_input":"2022-08-19T07:13:30.917788Z","iopub.status.idle":"2022-08-19T07:23:24.625802Z","shell.execute_reply.started":"2022-08-19T07:13:30.917754Z","shell.execute_reply":"2022-08-19T07:23:24.623691Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def rle_encode(img):\n    \"\"\" TBD\n    \n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n    \n    Returns: \n        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":"2022-08-19T07:24:08.642723Z","iopub.execute_input":"2022-08-19T07:24:08.643200Z","iopub.status.idle":"2022-08-19T07:24:08.655756Z","shell.execute_reply.started":"2022-08-19T07:24:08.643163Z","shell.execute_reply":"2022-08-19T07:24:08.653683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = pd.read_csv('../input/hubmap-organ-segmentation/test.csv')\ntest_ids = test_df['id']\ntest_dir = '../input/hubmap-organ-segmentation/test_images'\n\nids = []\nrles = []\nfor id in test_ids:\n    path = os.path.join(test_dir, f\"{id}.tiff\")\n    image, original_shape = preprocess_image(path)\n    pred = model.predict(np.expand_dims(image, axis=0))\n    \n    pred_mask = np.where(pred > threshold, 1, 0)[0]\n\n    resized_pred_mask = keras.utils.array_to_img(pred_mask, scale=False).resize((original_shape[0], original_shape[1]), resample=0)\n\n    resized_pred_mask_array = keras.utils.img_to_array(resized_pred_mask, dtype='uint8')\n    rle = rle_encode(resized_pred_mask_array)\n    ids.append(id)\n    rles.append(rle)\n    \nsubmission_df = pd.DataFrame({'id':ids,'rle':rles})\nsubmission_df.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:24:12.988774Z","iopub.execute_input":"2022-08-19T07:24:12.989439Z","iopub.status.idle":"2022-08-19T07:24:14.552182Z","shell.execute_reply.started":"2022-08-19T07:24:12.989404Z","shell.execute_reply":"2022-08-19T07:24:14.550995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df","metadata":{"execution":{"iopub.status.busy":"2022-08-19T07:24:20.250077Z","iopub.execute_input":"2022-08-19T07:24:20.250824Z","iopub.status.idle":"2022-08-19T07:24:20.296531Z","shell.execute_reply.started":"2022-08-19T07:24:20.250778Z","shell.execute_reply":"2022-08-19T07:24:20.294860Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}