{"cells": [{"outputs": [], "source": ["# This Python 3 environment comes with many helpful analytics libraries installed\n", "# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n", "# For example, here's several helpful packages to load in \n", "\n", "import numpy as np # linear algebra\n", "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n", "\n", "# Input data files are available in the \"../input/\" directory.\n", "# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n", "\n", "from subprocess import check_output\n", "print(check_output([\"ls\", \"../input\"]).decode(\"utf8\"))\n", "\n", "# Any results you write to the current directory are saved as output.\n", "\n", "import numpy as np\n", "import gzip\n", "import os\n", "from os.path import basename\n", "import glob\n", "import time\n", "import cv2\n", "import pandas as pd\n", "import random\n", "from PIL import Image\n", "#import scipy.ndimage\n", "from scipy import ndimage\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "\n", "from scipy.misc import imresize\n", "from skimage.transform import resize\n", "\n", "from sklearn.model_selection import train_test_split\n", "from keras.models import Model\n", "from keras.layers import Input, merge, Conv2D, MaxPooling2D, UpSampling2D, Concatenate\n", "#from keras.layers import Dense, Conv2D, Input, MaxPool2D, UpSampling2D, Concatenate, Conv2DTranspose\n", "from keras.optimizers import Adam\n", "from keras.optimizers import SGD\n", "from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping\n", "from keras.preprocessing.image import array_to_img, img_to_array, load_img, ImageDataGenerator\n", "from keras import backend as K\n", "\n", "K.set_image_dim_ordering('th') # Theano dimension ordering in this code\n"], "cell_type": "code", "metadata": {"_cell_guid": "a1d71cec-86ee-469f-8882-46248d06ae77", "_uuid": "d8ced1a0aa872c3cc87411fc768cf85f71280e66"}, "execution_count": 1}, {"outputs": [], "source": ["INPUT_PATH = '../input/'\n", "dims = [128, 128] \n", "img_rows = dims[0]\n", "img_cols = dims[1]\n", "train = sorted(glob.glob(INPUT_PATH + 'train/*.jpg'))\n", "masks = sorted(glob.glob(INPUT_PATH + 'train_masks/*.gif'))\n", "test  = sorted(glob.glob(INPUT_PATH + 'test/*.jpg'))\n", "print('Number of training images: ', len(train), ' Number of corresponding masks: ', len(masks), ' Number of test images: ', len(test))\n", "\n", "meta = pd.read_csv(INPUT_PATH + 'metadata.csv')\n", "mask_df = pd.read_csv(INPUT_PATH + 'train_masks.csv')\n", "ids_train = mask_df['img'].map(lambda s: s.split('_')[0]).unique()\n", "print('Length of ids_train ', len(ids_train))"], "cell_type": "code", "metadata": {"_cell_guid": "edba2f8f-fa80-4c23-8ee0-fd1fe3eca466", "_uuid": "262c7b4cd14b034c5a9a896f7f0dca2ab9e93218"}, "execution_count": 2}, {"outputs": [], "source": ["mask_df.head()\n", "#'''rle_mask is the run-length encoded version of the training set masks the input for the encoder has to be binary; zeroes and ones.\n", "#I read the encoding output as the pixel number of the first 1 (in the flattened mask) followed by the count of consecutive (uninterupted) series of pixels with value \n", "#'''"], "cell_type": "code", "metadata": {"_cell_guid": "01329c52-6e6f-4949-b69b-910c55d44958", "_uuid": "5f084de85087d51719704bc41f483b0bb3848e80"}, "execution_count": 3}, {"outputs": [], "source": ["image = cv2.imread(INPUT_PATH + 'train/00087a6bd4dc_01.jpg')\n", "plt.imshow(image)\n", "plt.show()"], "cell_type": "code", "metadata": {"_cell_guid": "ff3f3cb0-92b3-47a8-8b71-3ff2e5d1ec89", "_uuid": "1d67c7bffeddb8e4c71868b3aad8df31c8f96271"}, "execution_count": 4}, {"outputs": [], "source": ["img = Image.open(INPUT_PATH + 'train_masks/00087a6bd4dc_01_mask.gif').convert('RGB')\n", "plt.imshow(img)\n", "plt.show()"], "cell_type": "code", "metadata": {"_cell_guid": "35bf6466-b2e3-4257-a2a9-3468f4f6692e", "_uuid": "dcb7cea3aabe842c74d6abe80ee06692d39433cf"}, "execution_count": 5}, {"outputs": [], "source": ["# from ecobill:\n", "\n", "smooth = 1.\n", "\n", "def dice_coef(y_true, y_pred):\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", "\n", "def dice_coef_np(y_true,y_pred):\n", "    y_true_f = y_true.flatten()\n", "    y_pred_f = y_pred.flatten()\n", "    intersection = np.sum(y_true_f * y_pred_f)\n", "    return (2. * intersection + smooth) / (np.sum(y_true_f) + np.sum(y_pred_f) + smooth)\n", "\n", "def dice_coef_loss(y_true, y_pred):\n", "    return -dice_coef(y_true, y_pred)\n"], "cell_type": "code", "metadata": {"_cell_guid": "412f9461-d051-45d2-ac97-6e08b615bf78", "collapsed": true, "_uuid": "67b164b7bbfcc1de536b6b70b5256fd361f65c5a"}, "execution_count": 6}, {"outputs": [], "source": ["# this is the U-Net model I encountered in the LUNA16 Challenge\n", "def get_unet():\n", "    inputs = Input((3,img_rows, img_cols))\n", "    conv1 = Conv2D(32, (3, 3), padding=\"same\", activation='relu')(inputs)    \n", "    conv1 = Conv2D(32, (3, 3), padding=\"same\", activation='relu')(conv1)\n", "    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)\n", "\n", "    conv2 = Conv2D(64, (3, 3), padding=\"same\", activation='relu')(pool1)\n", "    conv2 = Conv2D(64, (3, 3), padding=\"same\", activation='relu')(conv2)    \n", "    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)\n", "\n", "    conv3 = Conv2D(128, (3, 3), padding=\"same\", activation='relu')(pool2)\n", "    conv3 = Conv2D(128, (3, 3), padding=\"same\", activation='relu')(conv3)\n", "    pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)\n", "\n", "    conv4 = Conv2D(256, (3, 3), padding=\"same\", activation='relu')(pool3)\n", "    conv4 = Conv2D(256, (3, 3), padding=\"same\", activation='relu')(conv4)\n", "    pool4 = MaxPooling2D(pool_size=(2, 2))(conv4)\n", "\n", "    conv5 = Conv2D(512, (3, 3), padding=\"same\", activation='relu')(pool4)\n", "    conv5 = Conv2D(512, (3, 3), padding=\"same\", activation='relu')(conv5)\n", "\n", "    up6 = Concatenate(axis=1)([UpSampling2D(size=(2, 2))(conv5), conv4])\n", "    #      Concatenate(axis=3)([residual, upconv])\n", "    conv6 = Conv2D(256, (3, 3), padding=\"same\", activation='relu')(up6)\n", "    conv6 = Conv2D(256, (3, 3), padding=\"same\", activation='relu')(conv6)\n", "\n", "    up7 = Concatenate(axis=1)([UpSampling2D(size=(2, 2))(conv6), conv3])\n", "    conv7 = Conv2D(128, (3, 3), padding=\"same\", activation='relu')(up7)\n", "    conv7 = Conv2D(128, (3, 3), padding=\"same\", activation='relu')(conv7)\n", "\n", "    up8 = Concatenate(axis=1)([UpSampling2D(size=(2, 2))(conv7), conv2])\n", "    conv8 = Conv2D(64, (3, 3), padding=\"same\", activation='relu')(up8)\n", "    conv8 = Conv2D(64, (3, 3), padding=\"same\", activation='relu')(conv8)\n", "\n", "    up9 = Concatenate(axis=1)([UpSampling2D(size=(2, 2))(conv8), conv1])\n", "    conv9 = Conv2D(32, (3, 3), padding=\"same\", activation='relu')(up9)\n", "    conv9 = Conv2D(32, (3, 3), padding=\"same\", activation='relu')(conv9)\n", "\n", "    conv10 = Conv2D(1, (1, 1), activation='sigmoid')(conv9)   #9\n", "\n", "    model = Model(inputs=inputs, outputs=conv10)\n", "    #      `Model(inputs=/input_19, outputs=sigmoid.0)`\n", "\n", "    #model.compile(optimizer=Adam(lr=1.0e-5), loss=dice_coef_loss, metrics=[dice_coef])  #LUNA16\n", "    model.compile(optimizer=Adam(5e-4), loss='binary_crossentropy', metrics=[dice_coef]) #ecobill\n", "\n", "    return model\n"], "cell_type": "code", "metadata": {"_cell_guid": "864b9559-b741-4984-9a4b-6ea567da2644", "collapsed": true, "_uuid": "891e9c523397c37a4f80186ae0322f6c4a601b90"}, "execution_count": 7}, {"outputs": [], "source": ["# split the train set into train and validation:\n", "train_images, validation_images = train_test_split(train, train_size=0.8, test_size=0.2)\n", "print('Split into training set with ', len(train_images), ' images and validation set with  ', len(validation_images), ' images')"], "cell_type": "code", "metadata": {"_cell_guid": "41ab6140-b753-4829-a470-40ffa935d86a", "_uuid": "ee2c720c8191804cb8fd953c2397e19c4980b800"}, "execution_count": 8}, {"outputs": [], "source": ["#use loading functions from: ecobill\n", "\n", "# utility function to convert greyscale images to rgb\n", "def grey2rgb(img):\n", "    new_img = []\n", "    for i in range(img.shape[0]):\n", "        for j in range(img.shape[1]):\n", "            new_img.append(list(img[i][j])*3)\n", "    new_img = np.array(new_img).reshape(img.shape[0], img.shape[1], 3)\n", "    return new_img\n", "\n", "\n", "# generator that we will use to read the data from the directory\n", "def data_gen_small(data_dir, masks, images, batch_size, dims):\n", "        \"\"\"\n", "        data_dir: where the actual images are kept\n", "        mask_dir: where the actual masks are kept\n", "        images: the filenames of the images we want to generate batches from\n", "        batch_size: self explanatory\n", "        dims: the dimensions in which we want to rescale our images\n", "        \n", "        Image.resize(size, resample=0)\n", "\n", "        Returns a resized copy of this image.\n", "        Parameters:\t\n", "\n", "        size \u2013 The requested size in pixels, as a 2-tuple: (width, height).\n", "        resample \u2013 An optional resampling filter. This can be one of PIL.Image.NEAREST, \n", "        PIL.Image.BOX, PIL.Image.BILINEAR, PIL.Image.HAMMING, PIL.Image.BICUBIC or PIL.Image.LANCZOS. \n", "        If omitted, or if the image has mode \u201c1\u201d or \u201cP\u201d, it is set PIL.Image.NEAREST\n", "        \"\"\"\n", "        while True:\n", "            ix = np.random.choice(np.arange(len(images)), batch_size)\n", "            imgs = []\n", "            labels = []\n", "            for i in ix:\n", "                # images\n", "                original_img = cv2.imread(images[i])\n", "                resized_img = imresize(original_img, dims+[3]) #this looks like TensorFlow ordering \n", "                array_img = resized_img / 255   \n", "                array_img = array_img.swapaxes(0,2)\n", "                imgs.append(array_img)\n", "                #imgs is a numpy array with dim: (batch size X 128 X 128 X 3)\n", "                \n", "                # masks\n", "                mask_filename = basename(images[i])\n", "                no_extension = os.path.splitext(mask_filename)[0]\n", "                correct_mask = INPUT_PATH + 'train_masks/' + no_extension + '_mask.gif' \n", "                original_mask = Image.open(correct_mask).convert('L')\n", "                data = np.asarray( original_mask, dtype=\"int32\" )\n", "                resized_mask = imresize(original_mask, dims+[3])\n", "                array_mask = resized_mask / 255\n", "                labels.append(array_mask)\n", "            imgs = np.array(imgs)\n", "            labels = np.array(labels)\n", "            relabel = labels.reshape(-1, dims[0], dims[1], 1)\n", "            yield imgs, relabel.swapaxes(1, 3)\n", "\n"], "cell_type": "code", "metadata": {"_cell_guid": "bae7a945-ebd4-496e-be5b-d02a1fe46944", "collapsed": true, "_uuid": "664af9161caffe4969bd497b177a187432628098"}, "execution_count": 9}, {"outputs": [], "source": ["train_gen = data_gen_small(INPUT_PATH + 'train/', masks, train_images, 2, dims) \n", "img, msk = next(train_gen)\n", "print('Size of batch: ', len(img))\n", "print('shape of img ', img.shape, 'number dimensions: ', img[0].ndim)\n", "print('shape of msk ', msk.shape, 'number dimensions: ', msk[0].ndim)\n", "newshape = img[0].swapaxes(0,2)\n", "plt.imshow(newshape)\n", "plt.show()\n", "\n", "#try resize up \n", "\n", "resized_img = imresize(img[0], [1280, 1918]+[3])\n", "print('resized up: ', resized_img.shape)\n", "newshape = resized_img.swapaxes(0,1)\n", "print('resized swapaxes: ', newshape.shape)\n", "print('resized swapaxes shape[-1]: ', newshape.shape[-1])\n", "\n", "plt.imshow(newshape)\n", "plt.show()\n", "\n", "newshape = msk.swapaxes(1,3)\n", "print(newshape.shape)\n", "plt.imshow(grey2rgb(newshape[0]), alpha=0.5)\n", "plt.show()"], "cell_type": "code", "metadata": {"_cell_guid": "b820af94-942c-4f32-a909-513e2c70126e", "_uuid": "698b55db65166fea536bd1e83b15e01f61b1d4df"}, "execution_count": 10}, {"outputs": [], "source": ["# create an instance of a validation generator:\n", "validation_gen = data_gen_small(INPUT_PATH + 'train/', masks, validation_images, 4, dims) "], "cell_type": "code", "metadata": {"_cell_guid": "ae0f75ca-c1ad-4418-bc1d-3aa75353f264", "collapsed": true, "_uuid": "beccf98a40eb129b2069c6adf966a55d30c25c44"}, "execution_count": 11}, {"outputs": [], "source": ["# define and compile the model\n", "model = get_unet()\n", "model.summary()"], "cell_type": "code", "metadata": {"_cell_guid": "f9b24275-296e-4c49-b0f6-0866ce33b2e9", "_uuid": "c453eb9b43c70369f458fbf5e39a908c59868260"}, "execution_count": 12}, {"outputs": [], "source": ["# fit the model and check dice_coef on validation data at end of each epoch\n", "model.fit_generator(train_gen, steps_per_epoch=50, epochs=1, validation_data=validation_gen, validation_steps=50)"], "cell_type": "code", "metadata": {"_cell_guid": "ab099a5b-6cb0-48af-a3ac-caf1fa786d8d", "_uuid": "1f6474172ac73246e238d9172032013ccde29a2e"}, "execution_count": 13}, {"outputs": [], "source": ["# lets look at one of the predicted masks\n", "img, msk = next(validation_gen)\n", "predicted_mask = model.predict(img)\n", "predicted_mask.shape"], "cell_type": "code", "metadata": {"_cell_guid": "4e55ca8a-4484-41e4-af92-9a919d4721ac", "_uuid": "120d88683c9139026fa1583aebe0ac2deaafb48c"}, "execution_count": 14}, {"outputs": [], "source": ["newshape = predicted_mask.swapaxes(1,3)\n", "print('newshape shape ', newshape.shape)\n", "grey = grey2rgb(newshape[3])\n", "print('grey shape ', grey.shape)\n", "plt.imshow(grey, alpha = 0.5)\n", "plt.show()"], "cell_type": "code", "metadata": {"_cell_guid": "facfa412-a7ff-4c53-b944-77a40bce995d", "_uuid": "6bc63d5939f19157b1522d51f1c0ee8b913b42e4"}, "execution_count": 17}, {"outputs": [], "source": ["# the corresponding image is:\n", "newshape = img[3].swapaxes(0,2)\n", "plt.imshow(newshape)\n", "plt.show()"], "cell_type": "code", "metadata": {"_cell_guid": "21f97d6a-d5a6-4c54-9a15-e9c0dbb5df4b", "collapsed": true, "_uuid": "306a30277889ed3d9bf8f531bbb06f8c0bb7de4f"}, "execution_count": null}, {"outputs": [], "source": [], "cell_type": "code", "metadata": {"_cell_guid": "4efa0f6b-4b27-4789-be1e-81d04bf71101", "collapsed": true, "_uuid": "a4dfa6d4e6a38aa8923f5f234904902e275d956a"}, "execution_count": null}], "nbformat": 4, "nbformat_minor": 1, "metadata": {"kernelspec": {"language": "python", "display_name": "Python 3", "name": "python3"}, "language_info": {"codemirror_mode": {"version": 3, "name": "ipython"}, "file_extension": ".py", "pygments_lexer": "ipython3", "mimetype": "text/x-python", "nbconvert_exporter": "python", "version": "3.6.3", "name": "python"}}}