{"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":"# Intro\nMost of the code is coppied from this kernel:https://www.kaggle.com/keegil/keras-u-net-starter-lb-0-277 \n\nThat kernal is really good one but not up to date. There are some issues in iou metrics. I solved those issue and made the kernal easy to get started.\nAlso the dataset he used is zipped. So, to work with that data, you have to unzip it, which takes time. So, I also unziped and uploaded the dataset. So, all of these are stolen mostly.","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"# Importing the libraries","metadata":{}},{"cell_type":"code","source":"from IPython.display import clear_output\nimport os\nimport sys\nimport random\nimport warnings\nimport numpy as np\nimport pandas as pd\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\nfrom keras.models import Model, load_model\nfrom keras.layers import Input\nfrom keras.layers.core import Dropout, Lambda\nfrom keras.layers.convolutional import Conv2D, Conv2DTranspose\nfrom keras.layers.pooling import MaxPooling2D\nfrom keras.layers.merge import concatenate\nfrom keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom keras import backend as K\nimport tensorflow as tf\nwarnings.filterwarnings('ignore', category=UserWarning, module='skimage')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Preprocessing the data for training and testing","metadata":{}},{"cell_type":"code","source":"!mkdir ./train\n!mkdir ./test\n\n!unzip ../input/data-science-bowl-2018/stage1_train.zip -d ./train\n!unzip ../input/data-science-bowl-2018/stage1_test.zip -d ./test\nclear_output()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set some parameters\n\nIMG_WIDTH = 128\nIMG_HEIGHT = 128\nIMG_CHANNELS = 3\nTRAIN_PATH = './train/'\nTEST_PATH = './test/'\n\ntrain_ids = os.listdir(TRAIN_PATH)\ntest_ids = os.listdir(TEST_PATH)\nprint(\"Total training images: \",len(train_ids))\nprint(\"Total test images: \",len(test_ids))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preprocess the data\nWe will import all the training images and their associated mask. Then we will resize the images and mask according to our need(Faster training vs Accuracy).\nWe will keep track of the test images to upsample our mask(Predicted)","metadata":{}},{"cell_type":"code","source":"# Get and resize train images and masks\n\nX_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)\n# This created a array shape of (669,128,128,3), We will apped training images on this array.\nY_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)\n# This created a array shape of (669,128,128,1), We will apped test images on this array.\nprint('Getting and resizing train images and masks ... ')\nsys.stdout.flush() \n# stdout. flush() forces it to “flush” the buffer, meaning that it will write everything in the buffer to the terminal\nfor n, id_ in tqdm(enumerate(train_ids), total=len(train_ids)): # tqdm for progressive animation\n    path = TRAIN_PATH + id_\n    img = imread(path + '/images/' + id_ + '.png')[:,:,:IMG_CHANNELS] # Read the images with 3 channels(Usually imread reads images with 4 channels)\n    img = resize(img, (IMG_HEIGHT, IMG_WIDTH), mode='constant', preserve_range=True) # DownSample the images keeping the orginal aspect ration\n    X_train[n] = img # Appending the images to the X_train array\n    \n    # Now we will stich the mask images and make it similar size to the training images\n    mask = np.zeros((IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)\n    for mask_file in os.listdir(path + '/masks/'):\n        mask_ = imread(path + '/masks/' + mask_file) # Read mask as shape of (256, 256)\n        mask_ = np.expand_dims(resize(mask_, (IMG_HEIGHT, IMG_WIDTH), mode='constant', \n                                      preserve_range=True), axis=-1) # convert the image to shape of (128,128,1)\n        mask = np.maximum(mask, mask_) # takes max values as mask(white nucleus exclue background)\n    Y_train[n] = mask # Merge all mask and put it on Y_train","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get and resize test images\nX_test = np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)\nsizes_test = [] # To upsample during making predicting\nprint('Getting and resizing test images ... ')\nsys.stdout.flush()\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_CHANNELS]\n    sizes_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","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualizing if training images and mask processed correctly","metadata":{}},{"cell_type":"code","source":"ix = random.randint(0, len(train_ids))\nprint(\"Training image\")\nimshow(X_train[ix])\nplt.show()\nprint(\"Mask of the image\")\nimshow(np.squeeze(Y_train[ix]))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Custom metric\nThis code is copied from here: https://www.kaggle.com/aglotero/another-iou-metric .You can also use tf.keras.metrics.MeanIoU(num_classes=****Total classes(I don't know actually)****)","metadata":{}},{"cell_type":"code","source":"def iou_metric(y_true_in, y_pred_in, print_table=False):\n    labels = label(y_true_in > 0.5)\n    y_pred = label(y_pred_in > 0.5)\n    \n    true_objects = len(np.unique(labels))\n    pred_objects = len(np.unique(y_pred))\n\n    intersection = np.histogram2d(labels.flatten(), y_pred.flatten(), bins=(true_objects, pred_objects))[0]\n\n    # Compute areas (needed for finding the union between all objects)\n    area_true = np.histogram(labels, bins = true_objects)[0]\n    area_pred = np.histogram(y_pred, bins = pred_objects)[0]\n    area_true = np.expand_dims(area_true, -1)\n    area_pred = np.expand_dims(area_pred, 0)\n\n    # Compute union\n    union = area_true + area_pred - intersection\n\n    # Exclude background from the analysis\n    intersection = intersection[1:,1:]\n    union = union[1:,1:]\n    union[union == 0] = 1e-9\n\n    # Compute the intersection over union\n    iou = intersection / union\n\n    # Precision helper function\n    def precision_at(threshold, iou):\n        matches = iou > threshold\n        true_positives = np.sum(matches, axis=1) == 1   # Correct objects\n        false_positives = np.sum(matches, axis=0) == 0  # Missed objects\n        false_negatives = np.sum(matches, axis=1) == 0  # Extra objects\n        tp, fp, fn = np.sum(true_positives), np.sum(false_positives), np.sum(false_negatives)\n        return tp, fp, fn\n\n    # Loop over IoU thresholds\n    prec = []\n    if print_table:\n        print(\"Thresh\\tTP\\tFP\\tFN\\tPrec.\")\n    for t in np.arange(0.5, 1.0, 0.05):\n        tp, fp, fn = precision_at(t, iou)\n        if (tp + fp + fn) > 0:\n            p = tp / (tp + fp + fn)\n        else:\n            p = 0\n        if print_table:\n            print(\"{:1.3f}\\t{}\\t{}\\t{}\\t{:1.3f}\".format(t, tp, fp, fn, p))\n        prec.append(p)\n    \n    if print_table:\n        print(\"AP\\t-\\t-\\t-\\t{:1.3f}\".format(np.mean(prec)))\n    return np.mean(prec)\n\ndef iou_metric_batch(y_true_in, y_pred_in):\n    batch_size = y_true_in.shape[0]\n    metric = []\n    for batch in range(batch_size):\n        value = iou_metric(y_true_in[batch], y_pred_in[batch])\n        metric.append(value)\n    return np.array(np.mean(metric), dtype=np.float32)\n\ndef my_iou_metric(label, pred):\n    metric_value = tf.py_function(iou_metric_batch, [label, pred], tf.float32)\n    return metric_value","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Making the model (U-Net)\nU-Net is a convolutional neural network that was developed for biomedical image segmentation at the Computer Science Department of the University of Freiburg.\nThis work really well in small dataset too.\n<img src=\"https://www.researchgate.net/profile/Alan-Jackson-2/publication/323597886/figure/fig2/AS:601386504957959@1520393124691/Convolutional-neural-network-CNN-architecture-based-on-UNET-Ronneberger-et-al.png\" alt=\"U-Net\">","metadata":{}},{"cell_type":"code","source":"# Build U-Net model\ninputs = Input((IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS))\ns = Lambda(lambda x: x / 255) (inputs)\n\nc1 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (s)\nc1 = Dropout(0.1) (c1)\nc1 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c1)\np1 = MaxPooling2D((2, 2)) (c1)\n\nc2 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p1)\nc2 = Dropout(0.1) (c2)\nc2 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c2)\np2 = MaxPooling2D((2, 2)) (c2)\n\nc3 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p2)\nc3 = Dropout(0.2) (c3)\nc3 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c3)\np3 = MaxPooling2D((2, 2)) (c3)\n\nc4 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p3)\nc4 = Dropout(0.2) (c4)\nc4 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c4)\np4 = MaxPooling2D(pool_size=(2, 2)) (c4)\n\nc5 = Conv2D(256, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (p4)\nc5 = Dropout(0.3) (c5)\nc5 = Conv2D(256, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c5)\n\nu6 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same') (c5)\nu6 = concatenate([u6, c4])\nc6 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u6)\nc6 = Dropout(0.2) (c6)\nc6 = Conv2D(128, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c6)\n\nu7 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same') (c6)\nu7 = concatenate([u7, c3])\nc7 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u7)\nc7 = Dropout(0.2) (c7)\nc7 = Conv2D(64, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c7)\n\nu8 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same') (c7)\nu8 = concatenate([u8, c2])\nc8 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u8)\nc8 = Dropout(0.1) (c8)\nc8 = Conv2D(32, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c8)\n\nu9 = Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same') (c8)\nu9 = concatenate([u9, c1], axis=3)\nc9 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (u9)\nc9 = Dropout(0.1) (c9)\nc9 = Conv2D(16, (3, 3), activation='elu', kernel_initializer='he_normal', padding='same') (c9)\n\noutputs = Conv2D(1, (1, 1), activation='sigmoid') (c9)\n\nmodel = Model(inputs=[inputs], outputs=[outputs])\nmodel.compile(optimizer='adam', loss='binary_crossentropy', metrics=[my_iou_metric])\nprint(\"Model Compiled\")\nprint(\"_______________________________________________________________________________\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training the model","metadata":{}},{"cell_type":"code","source":"# Initialize our callbacks\n# This will save the best weights only\ncheckpoint = ModelCheckpoint('model-dsbowl2018-1.h5',\n                             monitor=\"val_loss\",\n                             mode=\"min\",\n                             save_best_only = True,\n                             verbose=1)\n# It will stop training if validation accuracy doesn't improve\nearlystop = EarlyStopping(monitor = 'val_loss', \n                          min_delta = 0, \n                          patience = 5,\n                          verbose = 1,\n                          restore_best_weights = True)\n\n# Fit our model \nresults = model.fit(X_train, Y_train, validation_split=0.2,\n                    batch_size=16, epochs=30, \n                    callbacks=[earlystop, checkpoint])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prediction on test data","metadata":{}},{"cell_type":"code","source":"# Predict on train, val and test\nmodel = load_model('model-dsbowl2018-1.h5', custom_objects={'my_iou_metric': my_iou_metric})\n\n# Predict on train, val and test\npreds_train = model.predict(X_train[:int(X_train.shape[0]*0.9)], verbose=1)\npreds_val = model.predict(X_train[int(X_train.shape[0]*0.9):], verbose=1)\npreds_test = model.predict(X_test, verbose=1)\n\n# Threshold predictions\npreds_train_t = (preds_train > 0.5).astype(np.uint8)\npreds_val_t = (preds_val > 0.5).astype(np.uint8)\npreds_test_t = (preds_test > 0.5).astype(np.uint8)\n\n# Create list of upsampled test masks\npreds_test_upsampled = []\nfor i in range(len(preds_test)):\n    preds_test_upsampled.append(resize(np.squeeze(preds_test[i]), \n                                       (sizes_test[i][0], sizes_test[i][1]), \n                                       mode='constant', preserve_range=True))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualizing few predicting example","metadata":{}},{"cell_type":"code","source":"# Perform a sanity check on some random training samples\nix = random.randint(0, len(preds_train_t))\nprint(\"Training image\")\nimshow(X_train[ix])\nplt.show()\nprint(\"Actual Mask\")\nimshow(np.squeeze(Y_train[ix]))\nplt.show()\nprint(\"Predicted Mask\")\nimshow(np.squeeze(preds_train_t[ix]))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Perform a sanity check on some random validation samples\nix = random.randint(0, len(preds_val_t))\nimshow(X_train[int(X_train.shape[0]*0.9):][ix])\nplt.show()\nimshow(np.squeeze(Y_train[int(Y_train.shape[0]*0.9):][ix]))\nplt.show()\nimshow(np.squeeze(preds_val_t[ix]))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}