{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"},"cell_type":"markdown","source":"# Introduction"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","collapsed":true},"cell_type":"markdown","source":"This is a notebook exploring [U-Net: Convolutional Networks for Biomedical Image Segmentation](https://arxiv.org/pdf/1505.04597.pdf).\n\nCredit to [Kjetil Åmdal-Sævik's Keras U-Net starter - LB 0.277](https://www.kaggle.com/keegil/keras-u-net-starter-lb-0-277?scriptVersionId=2164855). This is based off the work from there.\n"},{"metadata":{"_uuid":"ec37207bd2938a2fdd7cbd3a3ff94748788a2077","_cell_guid":"43e4460f-9c64-4234-a08d-f29f85da017d","trusted":true},"cell_type":"code","source":"import os\nimport random\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom skimage.io import imread \nfrom skimage.transform import resize\nfrom skimage.morphology import label\n# from skimage.segmentation import mark_boundaries\nimport matplotlib.pyplot as plt\n\nfrom keras.layers import Input, Conv2D, Lambda, MaxPooling2D, Conv2DTranspose, concatenate, Dropout\nfrom keras.models import Model, load_model\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom keras.optimizers import Adam","execution_count":21,"outputs":[]},{"metadata":{"_uuid":"072d83a16bcacdb21f9ffef70032d4b76f42061e","_cell_guid":"b01c47b2-7219-4c09-be63-4d844995103d","collapsed":true,"trusted":true},"cell_type":"code","source":"TRAIN_PATH = '../input/stage1_train/'\nTEST_PATH = '../input/stage1_test/'\n\nIMG_WIDTH = IMG_HEIGHT = 256\nIMG_CHANNELS = 3\n\nBATCH_SIZE = 1\nNUM_EPOCHS = 30\nSTEPS_PER_EPOCH = 600  # there are 670 training images","execution_count":22,"outputs":[]},{"metadata":{"_uuid":"7cd8a6e248d047adb4962623a5e2488564197070","_cell_guid":"4a335735-ec1d-4a2c-a268-af1a5af90e0c","trusted":true},"cell_type":"code","source":"train_ids = next(os.walk(TRAIN_PATH))\n\nmask_count = 0\nfor train_id in train_ids[1]:\n    masks = next(os.walk(TRAIN_PATH + train_id + '/masks/'))[2]\n    mask_count += len(masks)\n\nprint('There are {} images.'.format(len(train_ids[1])))\nprint('There are {} masks.'.format(mask_count))\nprint('Approximately {} masks per image.'.format(mask_count // len(train_ids[1])))","execution_count":23,"outputs":[]},{"metadata":{"_uuid":"d9e5d93d3c184949c735266ba191da4393d6c28b","_cell_guid":"95a4df29-d358-4de7-8357-94e2cc0f800f","collapsed":true,"trusted":true},"cell_type":"code","source":"# image_id = train_ids[1][0]\n# path = TRAIN_PATH + image_id + '/images/' + image_id + '.png'\n# image = imread(path)[:,:,:3]\n# print(image.shape)\n# plt.imshow(image)\n# plt.show()\n\n# mask_ids = next(os.walk(TRAIN_PATH + image_id + '/masks/'))\n# mask = imread(TRAIN_PATH + image_id + '/masks/' + mask_ids[2][0])\n# print(mask.shape)\n# plt.imshow(mask)\n# plt.show()\n\n# mask2 = imread(TRAIN_PATH + image_id + '/masks/' + mask_ids[2][1])\n# masks = mask + mask2\n# plt.imshow(masks)\n# plt.show()\n\n\n# marked = mark_boundaries(masks, mask)\n# plt.imshow(marked)\n# plt.show()","execution_count":24,"outputs":[]},{"metadata":{"_uuid":"3dd001adee23354faa70707aa62755a79eefd5ac","_cell_guid":"fe8365e1-306b-4067-9b1c-659876140180","collapsed":true,"trusted":true},"cell_type":"code","source":"def preprocess_train(input_size, val_split=0.1):\n    # data/\n    #    stage1_train/\n    #         image_id/\n    #             images/\n    #             masks/\n    #         image_id/\n    # ...\n    print('Creating training generator...')\n\n    print('Processing training data...')\n    # getting the name of the images\n    train_ids = next(os.walk(TRAIN_PATH))[1]\n\n    np.random.shuffle(train_ids)\n\n    # width, height, channels\n    train_X = np.zeros((len(train_ids), input_size[0], input_size[1], input_size[2]), dtype=np.uint8)\n    train_Y = np.zeros((len(train_ids), input_size[0], input_size[1], 1), dtype=np.bool)\n\n    for index_, image_id in tqdm(enumerate(train_ids), total=len(train_ids)):\n        # base path\n        path = TRAIN_PATH + image_id\n        # getting the images\n        image_path = path + '/images/' + image_id + '.png'\n        image = imread(image_path)[:,:,:input_size[2]]\n        resized = resize(image, (input_size[0], input_size[1]), mode='constant', preserve_range=True)\n        train_X[index_] = resized\n        \n        # getting the masks\n        complete_mask = np.zeros((input_size[0], input_size[1], 1), dtype=np.bool)\n        for mask_id in next(os.walk(path + '/masks/'))[2]:\n            mask_path = path + '/masks/' + mask_id\n            single_mask = imread(mask_path)\n            single_mask = resize(single_mask, (input_size[0], input_size[1]), mode='constant', preserve_range=True)\n            single_mask = np.expand_dims(single_mask, axis=-1)\n            # creating one mask for all the masks for this image\n#             iter_ = np.nditer(single_mask, flags=['multi_index'])\n#             while not iter_.finished:\n#                 mask_index = iter_.multi_index\n#                 element = complete_mask[mask_index]\n#                 if element == True and iter_[0] is not 0:\n#                     complete_mask[mask_index] = False\n#                 else:\n#                     complete_mask[mask_index] = max(element, iter_[0])\n#                 iter_.iternext()\n            complete_mask = np.maximum(complete_mask, single_mask)\n        train_Y[index_] = complete_mask\n\n    val_size = int(len(train_X) * val_split)\n\n    return (train_X[val_size:], train_Y[val_size:], train_X[:val_size], train_Y[:val_size])\n\ndef create_train_generator():\n    return ImageDataGenerator(rotation_range=45, width_shift_range=0.25, height_shift_range=0.25, horizontal_flip=True, vertical_flip=True)\n\n\ndef create_val_generator():\n    return ImageDataGenerator()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"93e3a78a5adab842f59ab8f3aa096013e46b2593","scrolled":false,"_cell_guid":"37d264bf-ed2c-4115-8390-31a7220c3ca6","trusted":true},"cell_type":"code","source":"train_X, train_Y, val_X, val_Y = preprocess_train(input_size=(IMG_WIDTH, IMG_HEIGHT, IMG_CHANNELS))\n\ntrain_generator = create_train_generator()\nval_generator = create_val_generator()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b937f70933a7c65525fed46e0c63929ca31cf676","_cell_guid":"2a7ed359-77ee-4baa-93f8-4e2e04bf66cb","trusted":true},"cell_type":"code","source":"random_i = random.randint(0, len(train_ids)) \n\nplt.imshow(train_X[random_i])\nplt.show()\n\nplt.imshow(np.squeeze(train_Y[random_i]))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"47f4982d50c3fc66152dcfa7bf302cc044ea26f1","_cell_guid":"1e19b26e-1271-471d-a65b-b4a918d34b23"},"cell_type":"markdown","source":"# Training"},{"metadata":{"_uuid":"6c730b7c83506b15f4f09c7278bcd8394c907354","_cell_guid":"4a8ba4db-3207-4475-a4a6-0e4f73267828","collapsed":true,"trusted":true},"cell_type":"code","source":"def unet(input_size):\n    inputs = Input((input_size[0], input_size[1], input_size[2])) # height, width, channels\n    norm = Lambda(lambda x: x / 255)(inputs)\n\n    conv_1 = Conv2D(16, (3, 3), activation='relu', padding='same')(norm)\n    conv_1 = Conv2D(16, (3, 3), activation='relu', padding='same')(conv_1)\n    pool_1 = MaxPooling2D((2, 2))(conv_1)\n\n    conv_2 = Conv2D(32, (3, 3), activation='relu', padding='same')(pool_1)\n    conv_2 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv_2)\n    pool_2 = MaxPooling2D((2, 2))(conv_2)\n\n    conv_3 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool_2)\n    conv_3 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv_3)\n    pool_3 = MaxPooling2D((2, 2))(conv_3)\n\n    conv_4 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool_3)\n    conv_4 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv_4)\n    pool_4 = MaxPooling2D((2, 2))(conv_4)\n\n    conv_5 = Conv2D(256, (3, 3), activation='relu', padding='same')(pool_4)\n    dropout = Dropout(0.2)(conv_5)\n    conv_5 = Conv2D(256, (3, 3), activation='relu', padding='same')(dropout)\n\n    up_6 = Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(conv_5)\n    up_6 = concatenate([up_6, conv_4], axis=3)\n    conv_6 = Conv2D(128, (3, 3), activation='relu', padding='same')(up_6)\n    conv_6 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv_6)\n\n    up_7 = Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(conv_6)\n    up_7 = concatenate([up_7, conv_3], axis=3)\n    conv_7 = Conv2D(64, (3, 3), activation='relu', padding='same')(up_7)\n    conv_7 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv_7)\n\n    up_8 = Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(conv_7)\n    up_8 = concatenate([up_8, conv_2], axis=3)\n    conv_8 = Conv2D(32, (3, 3), activation='relu', padding='same')(up_8)\n    conv_8 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv_8)\n\n    up_9 = Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(conv_8)\n    up_9 = concatenate([up_9, conv_1], axis=3)\n    conv_9 = Conv2D(16, (3, 3), activation='relu', padding='same')(up_9)\n    conv_9 = Conv2D(16, (3, 3), activation='relu', padding='same')(conv_9)\n\n    conv_10 = Conv2D(1, (1, 1), activation='sigmoid')(conv_9)\n\n    return Model(inputs=[inputs], outputs=[conv_10])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b098e146d08c006a1869f540b6b3195ac1394ea8","_cell_guid":"57571ac6-00c6-450a-94e9-1527c866d2aa","trusted":true},"cell_type":"code","source":"model = unet(input_size=(IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS))\noptimizer = Adam(lr=1e-3)\n\nmodel.compile(optimizer=optimizer,\n              loss='binary_crossentropy',\n              metrics=['accuracy'])\n\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7f561ce432c3dbbb59bb366f9622ba890affc48d","_cell_guid":"6f3adf81-7eb2-405b-aba2-7e03498ede37","collapsed":true,"trusted":true},"cell_type":"code","source":"def create_callbacks():\n    print('Creating callbacks...')\n    early = EarlyStopping(patience=5, verbose=1)\n    checkpoint = ModelCheckpoint('keras_unet.h5', verbose=1, save_best_only=True)\n\n    return [early, checkpoint]","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"317c5c59ed6cf5e03a481f3e3ba77834f53e56e4","_cell_guid":"e4887f9d-d503-4491-9c84-7df05d414f2a","trusted":true},"cell_type":"code","source":"callbacks = create_callbacks()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"3af2152cd55361239f2fea921a87b7f3b71619b9","_cell_guid":"397bd2d0-2176-4cc6-9080-9f926398b41f","trusted":true},"cell_type":"code","source":"print('Starting training...')\nhistory = model.fit_generator(train_generator.flow(train_X, train_Y, batch_size=BATCH_SIZE),\n                              epochs=NUM_EPOCHS,\n                              steps_per_epoch=STEPS_PER_EPOCH,\n                              callbacks=callbacks,\n                              verbose=1,\n                              validation_data=val_generator.flow(\n                                  val_X, val_Y, batch_size=BATCH_SIZE),\n                              validation_steps=STEPS_PER_EPOCH * 0.1)\n\nprint('Finished training...')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"f9e5e559f41ea239bfebe0fbf72f18ebddb3a858","_cell_guid":"d953d1ee-12f8-4602-833b-13fc0a6cdeea","collapsed":true,"trusted":true},"cell_type":"code","source":"def plot_loss_history(history):\n    # validation losses\n    val_loss = history.history['val_loss']\n    loss = history.history['loss']\n\n    plt.title('Loss')\n    plt.plot(val_loss, 'r', loss, 'b')\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b50ce6b4010b9d49018d57fba171a4788e78d9b8","_cell_guid":"6c9b1d6e-0e57-4711-acda-512cb927337e","trusted":true},"cell_type":"code","source":"plot_loss_history(history)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"567452506c8134d819f2e93ddd984520b1a59a54","_cell_guid":"db119c3a-5560-4c0d-a810-2efc3c928086"},"cell_type":"markdown","source":"# Prediction"},{"metadata":{"_uuid":"3ca1acac2fb9756b385e430b4d45ddeeda10ee39","_cell_guid":"dfba37c8-4973-4348-a06a-a348909a4701","collapsed":true,"trusted":true},"cell_type":"code","source":"def preprocess_test(input_size):\n    test_ids = next(os.walk(TEST_PATH))[1]\n\n    test_X = np.zeros(\n        (len(test_ids), input_size[0], input_size[1], input_size[2]), dtype=np.uint8)\n    # we are going to resize the predicted test images back to original size\n    test_image_sizes = []\n\n    for index_, test_id in tqdm(enumerate(test_ids), total=len(test_ids)):\n        image_path = TEST_PATH + test_id + '/images/' + test_id + '.png'\n        image = imread(image_path)[:,:,:input_size[2]]\n        test_image_sizes.append((image.shape[0], image.shape[1]))\n        resized = resize(image, (input_size[0], input_size[1]), mode='constant', preserve_range=True)\n        test_X[index_] = resized\n\n    return (test_X, test_ids, test_image_sizes)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2a235a92c82128c9bbf9933f14d1753f06c8ecff","_cell_guid":"65bf2f0a-8403-4fef-82fb-5feeb47b1184","trusted":true},"cell_type":"code","source":"test_X, test_ids, test_image_sizes = preprocess_test(input_size=(IMG_WIDTH, IMG_HEIGHT, IMG_CHANNELS))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2f99961b6d387b81cf7d3d65c98cf4bc0766827b","_cell_guid":"f52e16d5-b5a8-4cd2-afd9-3b44c70fd76e","trusted":true},"cell_type":"code","source":"model = load_model('keras_unet.h5')\npredictions = model.predict(test_X, verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7adf78cc11be2eb77e70238e4654eccd39c77043","_cell_guid":"e7ea897f-8123-4cb1-95c9-1c003b7376f8","trusted":true},"cell_type":"code","source":"print(predictions.shape)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1a74a7d208c14c519fcc9726a8a21a40b2e6f5a2","_cell_guid":"2e5cc5d8-0e2b-45d6-a0bc-78ff292728bb","collapsed":true,"trusted":true},"cell_type":"code","source":"preds = np.squeeze(predictions)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bc3ad4981fb6d1c2f18040ab7c3173a8228249d7","_cell_guid":"efed29e6-efb5-4153-9bd9-6313e21436a5","trusted":true},"cell_type":"code","source":"print(preds.shape)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7112e40c1a845b0fea210a4de3b7c804617f5bde","_cell_guid":"d7af9e84-b5d5-4171-bdb1-71b29af80200","trusted":true},"cell_type":"code","source":"np.amax(preds)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b1f4317126ba07b44b59b3c507a88e9768fe2f62","_cell_guid":"7a0487c1-9bc0-41ee-bbc5-1af9a516b6fb","trusted":true,"collapsed":true},"cell_type":"code","source":"index_ = random.randint(0, len(test_X))\ntest_image = test_X[index_]\n\nplt.imshow(test_image)\nplt.show()\n\nprediction = preds[index_]\n\nplt.imshow((prediction > 0.3).astype(np.uint8))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"cfef2abf0ec586874d7a85c7be352c14102b64de","_cell_guid":"fdbf80e5-251b-48bb-9dcc-e54ac9dff733"},"cell_type":"markdown","source":"# Submission"},{"metadata":{"_uuid":"26ed5136f8881d9e44c7c55f093b8cdd87bd0677","_cell_guid":"cfaa5dcc-c4cc-422f-aa68-2bad9dfb5475"},"cell_type":"markdown","source":"[Using rakhlin's Fast Run-Length Encoding (Python)](https://www.kaggle.com/rakhlin/fast-run-length-encoding-python)"},{"metadata":{"_uuid":"363d01174ca606d0df75bf743e8f3ca081ebd94e","_cell_guid":"edbd8baf-1d58-4f01-99a5-a987e3b08543","collapsed":true,"trusted":true},"cell_type":"code","source":"def rle_encoding(x):\n    '''\n    x: numpy array of shape (height, width), 1 - mask, 0 - background\n    Returns run length as list\n    '''\n    dots = np.where(x.T.flatten() == 1)[0] # .T sets Fortran order down-then-right\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if (b > prev + 1): run_lengths.extend((b + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n        \n    return run_lengths\n\ndef prob_to_rles(x, cutoff=0.3):\n    lab_img = label(x > cutoff)\n    for i in range(1, lab_img.max() + 1):\n        yield rle_encoding(lab_img == i)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"bffeda52c584b06573c43b83241e6ebcc16200ee","_cell_guid":"bb87f1b6-6e7d-4dd3-9623-52b2fd54e486","collapsed":true,"trusted":true},"cell_type":"code","source":"# resizing the predictions to original sizea\npreds_resized = []\nfor index_, pred in enumerate(preds):\n    image = resize(pred, test_image_sizes[index_], mode='constant', preserve_range=True)\n    preds_resized.append(image)\n\nnew_test_ids = []\nrles = []\nfor n, id_ in enumerate(test_ids):\n    rle = list(prob_to_rles(preds_resized[n]))\n    rles.extend(rle)\n    new_test_ids.extend([id_] * len(rle))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"91b459cc809de0033f49c2f3827f658b22686da8","_cell_guid":"812f15b6-97f2-4c4b-ab8c-49c90588e129","collapsed":true,"trusted":true},"cell_type":"code","source":"submission = pd.DataFrame()\nsubmission['ImageId'] = new_test_ids\nsubmission['EncodedPixels'] = pd.Series(rles).apply(lambda x: ' '.join(str(y) for y in x))\nsubmission.to_csv('keras_unet_01.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"35c5e083793541c109ecb0ae105e2f4c75890e95","_cell_guid":"782bc0e4-d2b9-48a3-89a3-0810bbefa8e9","trusted":true,"collapsed":true},"cell_type":"code","source":"unique_test = set(new_test_ids)\nprint('Number of prediction test ids: {}'.format(len(unique_test)))\nprint('Number of given test ids: {}'.format(len(test_ids)))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e06f1a604bde13574d26df4ed5f28d69dd5fae03","_cell_guid":"d629b12f-e009-467b-b468-860c625a0d9b","collapsed":true,"trusted":false},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}