{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Residual UNet Keras-Implementation for Nuclei Segmentation"},{"metadata":{},"cell_type":"markdown","source":"Note: I run this notebook on my local computer, so you can experiment on your own"},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.compat.v1 import ConfigProto\nfrom tensorflow.compat.v1 import InteractiveSession\n\nconfig = ConfigProto()\nconfig.gpu_options.allow_growth = True\nsession = InteractiveSession(config=config)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Load Packages"},{"metadata":{},"cell_type":"markdown","source":"import some necessary packages"},{"metadata":{"trusted":true},"cell_type":"code","source":"import os \nimport sys \nimport random \nimport warnings\nimport numpy as np \nfrom time import time \nimport matplotlib.pyplot as plt \nfrom tqdm import tqdm\nfrom itertools import chain\nfrom skimage.io import imread, imshow, imread_collection, concatenate_images\nfrom skimage.transform import resize\nfrom skimage.morphology import label\n\nimport tensorflow as tf \nfrom tensorflow import keras\nfrom tensorflow.keras.layers import Input, Dropout, Lambda, Conv2D, Conv2DTranspose, MaxPooling2D, concatenate, Activation, add, multiply, UpSampling2D\nfrom tensorflow.keras.models import Model, load_model\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom tensorflow.keras import backend as K \n\nfrom sklearn.metrics import classification_report, confusion_matrix\nfrom IPython.display import Image\n%matplotlib inline \n\nIMG_WIDTH = 128\nIMG_HEIGHT = 128\nIMG_CHANNEL = 3\nTRAIN_PATH = 'E:/Kaggle Dataset/Nuclei/train/'\nTEST_PATH = 'E:/Kaggle Dataset/Nuclei/test/'\n\nwarnings.filterwarnings('ignore', category=UserWarning, module='skimage')\nseed = 42\nrandom.seed = seed \nnp.random.seed = seed\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_ids = next(os.walk(TRAIN_PATH))[1]\ntest_ids = next(os.walk(TEST_PATH))[1]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Resizing Images"},{"metadata":{},"cell_type":"markdown","source":"because we will train in local machine, we resizing images to 128x128 pixel to reduce complexity"},{"metadata":{"tags":[],"trusted":false},"cell_type":"code","source":"print('Getting and resizing training images ...')\nX_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNEL), dtype=np.uint8)\nY_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)\n\nsys.stdout.flush()\nfor n, id_ in tqdm(enumerate(train_ids), total=len(train_ids)):\n    path = TRAIN_PATH + id_\n    img = imread(path + '/images/' + id_ + '.png')[:,:,:IMG_CHANNEL]\n    img = resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)\n    X_train[n] = img\n    mask = np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)\n\n    for mask_file in next(os.walk(path + '/masks/'))[2]:\n        mask_ = imread(path + '/masks/' + mask_file)\n        mask_ = np.expand_dims(resize(mask_, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True), axis=-1)\n        \n        mask = np.maximum(mask, mask_)\n\n    Y_train[n] = mask\n\nX_test = np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNEL), dtype=np.uint8)\nsize_test = []\nprint('Getting and resizing test images ...')\nsys.stdout.flush()\n\nfor n, id_ in tqdm(enumerate(test_ids), total=len(test_ids)):\n    path = TEST_PATH + id_\n    img = imread(path + '/images/' + id_ + '.png')[:,:,:IMG_CHANNEL]\n    size_test.append([img.shape[0], img.shape[1]])\n    img = resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True)\n    X_test[n] = img\n\nprint('Done')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Visualize images and mask"},{"metadata":{},"cell_type":"markdown","source":"in this step we visualize images and its ground thruth mask"},{"metadata":{"trusted":false},"cell_type":"code","source":"# Illustrate the train images and masks\nplt.figure(figsize=(20, 16))\nx, y = 12, 4\nfor i in range(y):\n    for j in range(x):\n        plt.subplot(y*2, x, i*2*x+j+1)\n        pos = i*120 + j*10\n        plt.imshow(X_train[pos])\n        plt.title('Image #{}'.format(pos))\n        plt.axis('off')\n        plt.subplot(y*2, x, (i*2+1)*x+j+1)\n\n        plt.imshow(np.squeeze(Y_train[pos]), cmap='gray_r')\n        plt.title('Mask #{}'.format(pos))\n        plt.axis('off')\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Image(filename=\"../input/segment/nuclei.JPG\", width= 1000, height=1000)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create Dice Coeff and Dice Coeff Loss"},{"metadata":{},"cell_type":"markdown","source":"In the image segmentation task, we use dice coefficient for the metric"},{"metadata":{"trusted":false},"cell_type":"code","source":"smooth = 1\n\ndef dice_coef(y_true, y_pred):\n    y_true_f = K.flatten(y_true)\n    y_pred_f = K.flatten(y_pred)\n    intersection = tf.reduce_sum(y_true_f * y_pred_f)\n    return (2. * intersection + smooth) / (tf.reduce_sum(y_true_f) + tf.reduce_sum(y_pred_f) + smooth)\n\ndef dice_coef_loss(y_true, y_pred):\n    return 1.0 - dice_coef(y_true, y_pred)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Build the Model"},{"metadata":{},"cell_type":"markdown","source":"Below is the image about Residual U-Net (ResUNet)"},{"metadata":{"trusted":true},"cell_type":"code","source":"Image(filename=\"../input/resunet/model.JPG\", width= 350, height=350)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"def bn_act(x, act=True):\n    x = tf.keras.layers.BatchNormalization()(x)\n    if act == True:\n        x = tf.keras.layers.Activation(\"relu\")(x)\n    return x \n\ndef conv_block(x, filters, kernel_size=(3, 3), padding=\"same\", strides=1):\n    conv = bn_act(x)\n    conv = tf.keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides)(conv)\n    return conv \n\ndef stem(x, filters, kernel_size=(3, 3), padding=\"same\", strides=1):\n    conv = tf.keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides)(x)\n    conv = conv_block(conv, filters, kernel_size=kernel_size, padding=padding, strides=strides)\n\n    shortcut = tf.keras.layers.Conv2D(filters, kernel_size=(1, 1), padding=padding, strides=strides)(x)\n    shortcut = bn_act(shortcut, act=False)\n\n    output = tf.keras.layers.Add()([conv, shortcut])\n    return output \n\ndef residual_block(x, filters, kernel_size=(3, 3), padding='same', strides=1):\n    res = conv_block(x, filters, kernel_size=kernel_size, padding=padding, strides=strides)\n    res = conv_block(res, filters, kernel_size=kernel_size, padding=padding, strides=1)\n\n    shortcut = tf.keras.layers.Conv2D(filters, kernel_size=(1, 1), padding=padding, strides=strides)(x)\n    shortcut = bn_act(shortcut, act=False)\n    \n    output = tf.keras.layers.Add()([shortcut, res])\n    return output\n\ndef upsample_concat_block(x, xskip):\n    u = tf.keras.layers.UpSampling2D((2, 2))(x)\n    c = tf.keras.layers.Concatenate()([u, xskip])\n    return c ","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"def ResUNet():\n    f = [16, 32, 64, 128, 256]\n    inputs = keras.layers.Input((IMG_HEIGHT, IMG_WIDTH, IMG_CHANNEL))\n\n    ## ENCODER \n    e0 = inputs\n    e1 = stem(e0, f[0])\n    e2 = residual_block(e1, f[1], strides=2)\n    e3 = residual_block(e2, f[2], strides=2)\n    e4 = residual_block(e3, f[3], strides=2)\n    e5 = residual_block(e4, f[4], strides=2)\n\n    # BRIDGE\n    b0 = conv_block(e5, f[4], strides=1)\n    b1 = conv_block(b0, f[4], strides=1)\n\n    # DECODER \n    u1 = upsample_concat_block(b1, e4)\n    d1 = residual_block(u1, f[4])\n\n    u2 = upsample_concat_block(d1, e3)\n    d2 = residual_block(u2, f[3])\n\n    u3 = upsample_concat_block(d2, e2)\n    d3 = residual_block(u3, f[2])\n\n    u4 = upsample_concat_block(d3, e1)\n    d4 = residual_block(u4, f[1])\n\n    outputs = keras.layers.Conv2D(1, (1, 1), padding='same', activation='sigmoid')(d4)\n    model = keras.models.Model(inputs, outputs)\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"model = ResUNet()","execution_count":null,"outputs":[]},{"metadata":{"tags":["outputPrepend"],"trusted":false},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"model.compile(\n    optimizer='adam',\n    loss='binary_crossentropy',\n    metrics=[dice_coef]\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Train "},{"metadata":{"tags":[],"trusted":false},"cell_type":"code","source":"epochs = 10\nmodel.fit(X_train, Y_train, validation_split=0.1, batch_size=16, epochs=epochs)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Test"},{"metadata":{"tags":["outputPrepend"],"trusted":false},"cell_type":"code","source":"# The first 90% used for training\npred_train = model.predict(X_train[:int(X_train.shape[0]*0.9)].astype(np.float16), verbose=1)\n# The last 10% used for validation\npred_val = model.predict(X_train[int(X_train.shape[0]*0.9):].astype(np.float16), verbose=1)\n\n# pred_test = model.predict(X_test, verbose=1)\n\n# Thresholds prediction\npred_train_threshold = (pred_train > 0.5).astype(np.float16)\npred_val_threshold = (pred_val > 0.5).astype(np.float16)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"## Showing our predicted masks on our training data\nix = random.randint(0, 682)\nplt.figure(figsize=(20, 28))\n\n# Our original training image\nplt.subplot(131)\nimshow(X_train[ix])\nplt.title('Image')\n\n# Our original combined mask\nplt.subplot(132)\nimshow(np.squeeze(Y_train[ix]))\nplt.title('Mask')\n\n# The mask of our model U-Net prediction\nplt.subplot(133)\nimshow(np.squeeze(pred_train_threshold[ix] > 0.5))\nplt.title('Prediction')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Image(filename=\"../input/segment/trainpred.JPG\", width= 1000, height=1000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"## Showing our predicted masks on our training data\nix = random.randint(602, 668)\nplt.figure(figsize=(20, 28))\n\n# Our original training image\nplt.subplot(121)\nimshow(X_train[ix])\nplt.title('Image')\n\n# The mask of our model U-Net prediction\nplt.subplot(122)\nix = ix - 603\nimshow(np.squeeze(pred_val_threshold[ix] > 0.5))\nplt.title('Prediction')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Image(filename=\"../input/segment/testpred.JPG\", width= 800, height=800)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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":4}