{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Challenge: create an algorithm to automate nucleus detection"},{"metadata":{},"cell_type":"markdown","source":"1. Import necessary libraries"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","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\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* 2. Importing TensorFlow and Keras modules - an open source library for numerical computation and large-scale machine learning\n* 2.1. Import support libraries\n* 2.2 Import matplotlib for visualization\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\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\n\nimport sklearn.model_selection     # For using KFold\nimport keras.preprocessing.image   # For using image generation\nimport datetime                    # To measure running time \nimport skimage.transform           # For resizing images\nimport skimage.morphology          # For using image labeling\nimport cv2                         # To read and manipulate images\nimport sys                         # System-specific parameters and functions\nimport tqdm                        # Use smart progress meter\nimport matplotlib.pyplot as plt    # Python 2D plotting library\nimport random\nfrom tqdm import tqdm\nfrom skimage.io import imread, imshow, imread_collection, concatenate_images\nfrom skimage.transform import resize\nfrom skimage.morphology import label","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"3. Input files: Unzipping the train files"},{"metadata":{"trusted":true},"cell_type":"code","source":"from zipfile import ZipFile  \nfile_name = \"../input/data-science-bowl-2018/stage1_train.zip\" \nwith ZipFile(file_name, 'r') as zip: \n    print('Extracting the files') \n    zip.extractall(\"stage1_train\") \n    print('Done!')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"3.1 Input files: Unzipping the test files"},{"metadata":{"trusted":true},"cell_type":"code","source":"from zipfile import ZipFile \nfile_name = \"../input/data-science-bowl-2018/stage1_test.zip\"\nwith ZipFile(file_name, 'r') as zip: \n    print('Extracting the files') \n    zip.extractall(\"stage1_test\") \n    print('Done!') ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"4. Preprocessing phase: \n1) load the data;\n2) get data ready for model\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_PATH=\"stage1_train/\"\nTEST_PATH='stage1_test/'\ntrain_ids=next(os.walk(TRAIN_PATH))[1]\ntest_ids=next(os.walk(TEST_PATH))[1]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"5. Tunning png image parameters: almost all pictures are in 256 * 256 resolution, we will leave this resolution to save more information"},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_HEIGHT=256\nIMG_WIDTH=256\nIMG_CHANNELS=3","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"6. Obtain from train and test input data images and masks"},{"metadata":{"trusted":true},"cell_type":"code","source":"X_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)\nY_train = np.zeros((len(train_ids), IMG_HEIGHT, IMG_WIDTH, 1), dtype=np.bool)\nprint('Get train images and masks')\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_CHANNELS]\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    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        mask = np.maximum(mask, mask_)\n    Y_train[n] = mask\n\n# And the same for test images\nX_test = np.zeros((len(test_ids), IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS), dtype=np.uint8)\nsizes_test = []\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\n\nprint('OK!')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"7. Visualizing training images and the segmented images related to it"},{"metadata":{"trusted":true},"cell_type":"code","source":"image_x=random.randint(0,len(train_ids))\nimshow(X_train[image_x])\nplt.show()\nimshow(np.squeeze(Y_train[image_x]))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"8. Building semantic segmentation model with UNet architecture using Keras. UNET paper https://arxiv.org/abs/1505.04597"},{"metadata":{"trusted":true},"cell_type":"code","source":"inputs = tf.keras.layers.Input((IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS))\ns = tf.keras.layers.Lambda(lambda x: x / 255)(inputs)\n\n#Contraction path\nc1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(s)\nc1 = tf.keras.layers.Dropout(0.1)(c1)\nc1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c1)\np1 = tf.keras.layers.MaxPooling2D((2, 2))(c1)\n\nc2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1)\nc2 = tf.keras.layers.Dropout(0.1)(c2)\nc2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c2)\np2 = tf.keras.layers.MaxPooling2D((2, 2))(c2)\n \nc3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p2)\nc3 = tf.keras.layers.Dropout(0.2)(c3)\nc3 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c3)\np3 = tf.keras.layers.MaxPooling2D((2, 2))(c3)\n \nc4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p3)\nc4 = tf.keras.layers.Dropout(0.2)(c4)\nc4 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c4)\np4 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(c4)\n \nc5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p4)\nc5 = tf.keras.layers.Dropout(0.3)(c5)\nc5 = tf.keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c5)\n\n#Expansive path \nu6 = tf.keras.layers.Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(c5)\nu6 = tf.keras.layers.concatenate([u6, c4])\nc6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u6)\nc6 = tf.keras.layers.Dropout(0.2)(c6)\nc6 = tf.keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c6)\n \nu7 = tf.keras.layers.Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(c6)\nu7 = tf.keras.layers.concatenate([u7, c3])\nc7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u7)\nc7 = tf.keras.layers.Dropout(0.2)(c7)\nc7 = tf.keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c7)\n \nu8 = tf.keras.layers.Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(c7)\nu8 = tf.keras.layers.concatenate([u8, c2])\nc8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u8)\nc8 = tf.keras.layers.Dropout(0.1)(c8)\nc8 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c8)\n \nu9 = tf.keras.layers.Conv2DTranspose(16, (2, 2), strides=(2, 2), padding='same')(c8)\nu9 = tf.keras.layers.concatenate([u9, c1], axis=3)\nc9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(u9)\nc9 = tf.keras.layers.Dropout(0.1)(c9)\nc9 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(c9)\n \noutputs = tf.keras.layers.Conv2D(1, (1, 1), activation='sigmoid')(c9)\nmodel = tf.keras.Model(inputs=[inputs], outputs=[outputs])\nmodel.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"And callbacks:"},{"metadata":{"trusted":true},"cell_type":"code","source":"callbacks=[tf.keras.callbacks.EarlyStopping(patience=2,monitor='val_loss'),\n           tf.keras.callbacks.TensorBoard(log_dir='logs')]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**And save our model model**"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.save('Semantic segmentation model with U-Net & TF.h5')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"9. Training the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"results=model.fit(X_train,Y_train,validation_split=0.1,batch_size=32,epochs=28,callbacks=callbacks)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"10. Apply the semantic segmentation algorighm for predictions on the train and test images using our trained model"},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_train = model.predict(X_train[:int(X_train.shape[0]*0.9)], verbose=1)\npreds_test = model.predict(X_test, verbose=1)\n#and prediction thresholds\npreds_train_t = (preds_train > 0.5).astype(np.uint8)\npreds_test_t = (preds_test > 0.5).astype(np.uint8)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"11. Visualizing model results on train data"},{"metadata":{"trusted":true},"cell_type":"code","source":"ix = random.randint(0, len(preds_train_t))\nimshow(X_train[ix])\nplt.title(\"Original image\")\nplt.show()\nimshow(np.squeeze(Y_train[ix]))\nplt.title(\"Mask\")\nplt.show()\nimshow(np.squeeze(preds_train_t[ix]))\nplt.title(\"Prediction\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"12. Visualizing model results on test data"},{"metadata":{"trusted":true},"cell_type":"code","source":"ix = random.randint(0, len(preds_test_t))\nimshow(X_test[ix])\nplt.title(\"Original image\")\nplt.show()\nimshow(np.squeeze(preds_test_t[ix]))\nplt.title(\"Prediction\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"13. Create list of unsampled test masks"},{"metadata":{"trusted":true},"cell_type":"code","source":"preds_test_upsampled = []\nfor i in range(len(preds_test)):\n    preds_test_upsampled.append(resize(np.squeeze(preds_test[i]), (sizes_test[i][0], sizes_test[i][1]),mode='constant', preserve_range=True))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"14. Implement Run-length encoding for each separate mask identified by skimage (from https://www.kaggle.com/rakhlin/fast-run-length-encoding-python )"},{"metadata":{"trusted":true},"cell_type":"code","source":"def rle_encoding(x):\n    dots = np.where(x.T.flatten() == 1)[0]\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    return run_lengths\n\ndef prob_to_rles(x, cutoff=0.5):\n    lab_img = label(x > cutoff)\n    for i in range(1, lab_img.max() + 1):\n        yield rle_encoding(lab_img == i)\nnew_test_ids = []\nrles = []\nfor n, id_ in enumerate(test_ids):\n    rle = list(prob_to_rles(preds_test_upsampled[n]))\n    rles.extend(rle)\n    new_test_ids.extend([id_] * len(rle))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"15. Obtain our semantic segmentation model results (encoded pixels) and create submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"sub = pd.DataFrame()\nsub['ImageId'] = new_test_ids\nsub['EncodedPixels'] = pd.Series(rles).apply(lambda x: ' '.join(str(y) for y in x))\nsub.to_csv('submission_file.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Thanks for ideas: Kjetil Åmdal-Sævik, Rakhlin"}],"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}