{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"33d00425-f595-41fa-b822-f50882d56476","_uuid":"98189f36fd3e16592fc072ff705a5d3073fd06cb"},"cell_type":"markdown","source":"# Overview\nThe kernel goes through\n1. the preprocessing steps to load the data\n1. a quick visualization of the color-space\n1. training a simple CNN\n1. applying the model to the test data\n1. creating the RLE test data"},{"metadata":{"_cell_guid":"01ef30eb-1363-4e51-9d5d-42f7011348e1","_uuid":"bd906a2f847afc7d10764bfc7a91a3e4b2be6996","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom glob import glob\nimport os\nfrom skimage.io import imread\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n%matplotlib inline\ndsb_data_dir = os.path.join('..', 'input')\nstage_label = 'stage1'","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"4700c733-6359-4727-91dd-fada2f4e397e","_uuid":"eb73ed385cad7f50245eb3e35190d402ed5cdece"},"cell_type":"markdown","source":"# Read in the labels\nLoad the RLE-encoded output for the training set"},{"metadata":{"_cell_guid":"766bc1d7-c066-4429-9fe7-5eb61d3c38e4","_uuid":"9b9b10ba7c40e3092dfccb86cf41298a64de62c4","trusted":true},"cell_type":"code","source":"train_labels = pd.read_csv(os.path.join(dsb_data_dir,'{}_train_labels.csv'.format(stage_label)))\ntrain_labels['EncodedPixels'] = train_labels['EncodedPixels'].map(lambda ep: [int(x) for x in ep.split(' ')])\ntrain_labels.sample(3)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"493040fb-d048-4e65-9d91-cc9c022df15f","_uuid":"835cbe19871b155ea14d3aa6a238567c50dccd21"},"cell_type":"markdown","source":"# Load in all Images\nHere we load in the images and process the paths so we have the appropriate information for each image"},{"metadata":{"_cell_guid":"4ba1187a-33e4-40b7-aa17-b49d0df11dd5","_uuid":"d5bf5405f63a39c9e3a6da0a7d71eb0ff03cfb90","trusted":true},"cell_type":"code","source":"all_images = glob(os.path.join(dsb_data_dir, 'stage1_*', '*', '*', '*'))\nimg_df = pd.DataFrame({'path': all_images})\nimg_id = lambda in_path: in_path.split('/')[-3]\nimg_type = lambda in_path: in_path.split('/')[-2]\nimg_group = lambda in_path: in_path.split('/')[-4].split('_')[1]\nimg_stage = lambda in_path: in_path.split('/')[-4].split('_')[0]\nimg_df['ImageId'] = img_df['path'].map(img_id)\nimg_df['ImageType'] = img_df['path'].map(img_type)\nimg_df['TrainingSplit'] = img_df['path'].map(img_group)\nimg_df['Stage'] = img_df['path'].map(img_stage)\nimg_df.sample(2)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"363ff396-e102-4cc4-b31b-156d994fadfb","_uuid":"243582157d1c6f41c7822da75e1ddc2c2bf65159"},"cell_type":"markdown","source":"# Create Training Data\nHere we make training data and load all the images into the dataframe. We take a simplification here of grouping all the regions together (rather than keeping them distinct)."},{"metadata":{"_cell_guid":"4c3ec4b5-bc8d-4ab9-a54c-9971da341845","_uuid":"47c96aec86be1cd5292554483310a06ee9d45da2","trusted":true},"cell_type":"code","source":"%%time\ntrain_df = img_df.query('TrainingSplit==\"train\"')\ntrain_rows = []\ngroup_cols = ['Stage', 'ImageId']\nfor n_group, n_rows in train_df.groupby(group_cols):\n    c_row = {col_name: col_value for col_name, col_value in zip(group_cols, n_group)}\n    c_row['masks'] = n_rows.query('ImageType == \"masks\"')['path'].values.tolist()\n    c_row['images'] = n_rows.query('ImageType == \"images\"')['path'].values.tolist()\n    train_rows += [c_row]\ntrain_img_df = pd.DataFrame(train_rows)    \nIMG_CHANNELS = 3\ndef read_and_stack(in_img_list):\n    return np.sum(np.stack([imread(c_img) for c_img in in_img_list], 0), 0)/255.0\ntrain_img_df['images'] = train_img_df['images'].map(read_and_stack).map(lambda x: x[:,:,:IMG_CHANNELS])\ntrain_img_df['masks'] = train_img_df['masks'].map(read_and_stack).map(lambda x: x.astype(int))\ntrain_img_df.sample(1)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"2b85d62b-92f6-465d-8277-516e3b6b46bf","_uuid":"895ed6676984a257cd1e5ba0cd51a5dd31e64584"},"cell_type":"markdown","source":"# Show a few images\nHere we show a few images of the cells where we see there is a mixture of brightfield and fluorescence which will probably make using a single segmentation algorithm difficult"},{"metadata":{"_cell_guid":"877319ab-2cb1-4da8-b67b-12d3d8c8d6dc","_uuid":"48836bbf0dd9f45245b9a7edeca6a817d53e0426","trusted":true},"cell_type":"code","source":"n_img = 6\nfig, m_axs = plt.subplots(2, n_img, figsize = (12, 4))\nfor (_, c_row), (c_im, c_lab) in zip(train_img_df.sample(n_img).iterrows(), \n                                     m_axs.T):\n    c_im.imshow(c_row['images'])\n    c_im.axis('off')\n    c_im.set_title('Microscope')\n    \n    c_lab.imshow(c_row['masks'])\n    c_lab.axis('off')\n    c_lab.set_title('Labeled')","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"198ce366-56f2-43d9-96a6-830b9239effc","_uuid":"5fb9320cd737a2ee6d5a64024331ae09ebfe4b4b"},"cell_type":"markdown","source":"# Look at the intensity distribution\nHere we look briefly at the distribution of intensity and see a few groups forming, they should probably be handled separately. "},{"metadata":{"_cell_guid":"067f8ae5-cac2-4ee6-9275-ee4ce8fc1e5a","_uuid":"31a22cd0a9f7a8a7339781688407f2c793e7f0e6","trusted":true},"cell_type":"code","source":"train_img_df['Red'] = train_img_df['images'].map(lambda x: np.mean(x[:,:,0]))\ntrain_img_df['Green'] = train_img_df['images'].map(lambda x: np.mean(x[:,:,1]))\ntrain_img_df['Blue'] = train_img_df['images'].map(lambda x: np.mean(x[:,:,2]))\ntrain_img_df['Gray'] = train_img_df['images'].map(lambda x: np.mean(x))\ntrain_img_df['Red-Blue'] = train_img_df['images'].map(lambda x: np.mean(x[:,:,0]-x[:,:,2]))\nsns.pairplot(train_img_df[['Gray', 'Red', 'Green', 'Blue', 'Red-Blue']])","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"8c0aea8a-67ba-4b29-8218-132651a16ef4","_uuid":"30deb6f5f1a0596dd5803b312eef5b94bb4c83cc"},"cell_type":"markdown","source":"# Check Dimensions \nHere we show the dimensions of the data to see the variety in the input images"},{"metadata":{"_cell_guid":"63d036a9-7cf5-4c53-b11d-d25abb436c29","_uuid":"fb77a503aed7cf9725f3d7b6f79217ad2704facc","trusted":true},"cell_type":"code","source":"train_img_df['images'].map(lambda x: x.shape).value_counts()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"bdd7f061-b4a8-401f-ad0e-060dfe985abc","_uuid":"a8f7221aa69a07ec7f5ce8b55220c7b0884d8935"},"cell_type":"markdown","source":"## Making a simple CNN\nHere we make a very simple CNN just to get a quick idea of how well it works. For this we use a batch normalization to normalize the inputs. We cheat a bit with the padding to keep problems simple."},{"metadata":{"_cell_guid":"7b874c1d-ecb8-4fd6-b031-300e8d27b056","_uuid":"f48b15ce268eb0973606feece814ed6f25b6ca6c","trusted":true},"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import BatchNormalization, Conv2D, UpSampling2D, Lambda\nsimple_cnn = Sequential()\nsimple_cnn.add(BatchNormalization(input_shape = (None, None, IMG_CHANNELS), \n                                  name = 'NormalizeInput'))\nsimple_cnn.add(Conv2D(8, kernel_size = (3,3), padding = 'same'))\nsimple_cnn.add(Conv2D(8, kernel_size = (3,3), padding = 'same'))\n# use dilations to get a slightly larger field of view\nsimple_cnn.add(Conv2D(16, kernel_size = (3,3), dilation_rate = 2, padding = 'same'))\nsimple_cnn.add(Conv2D(16, kernel_size = (3,3), dilation_rate = 2, padding = 'same'))\nsimple_cnn.add(Conv2D(32, kernel_size = (3,3), dilation_rate = 3, padding = 'same'))\n\n# the final processing\nsimple_cnn.add(Conv2D(16, kernel_size = (1,1), padding = 'same'))\nsimple_cnn.add(Conv2D(1, kernel_size = (1,1), padding = 'same', activation = 'sigmoid'))\nsimple_cnn.summary()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"50e75f24-8a9e-4ee9-bae7-748accd3416d","_uuid":"437e0cd78132c5716ceb8d18ae990aeca6f790b4"},"cell_type":"markdown","source":"# Loss\nSince we are being evaulated with intersection over union we can use the inverse of the DICE score as the loss function to optimize"},{"metadata":{"_cell_guid":"2b143d3a-61ff-4b67-ba5d-28f1271fae0d","_uuid":"b23a8972f9f8f6d3b72e939243565c1bcb600e8d","trusted":true},"cell_type":"code","source":"from keras import backend as K\nsmooth = 1.\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 = K.sum(y_true_f * y_pred_f)\n    return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)\ndef dice_coef_loss(y_true, y_pred):\n    return -dice_coef(y_true, y_pred)\nsimple_cnn.compile(optimizer = 'adam', \n                   loss = dice_coef_loss, \n                   metrics = [dice_coef, 'acc', 'mse'])","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"7af51570-8670-4b43-8ecf-fe8f2f4124a1","_uuid":"7b84ef9770c5a7ed96acdf2efa2ff576f167e79d"},"cell_type":"markdown","source":"# Simple Training\nHere we run a simple training, with each image being it's own batch (not a very good idea), but it keeps the code simple"},{"metadata":{"_cell_guid":"ac26f410-4661-44e0-a6e8-15219d25a8b8","_uuid":"1085d9cd838c788bc075c76fe89766a6e53309be","trusted":true},"cell_type":"code","source":"def simple_gen():\n    while True:\n        for _, c_row in train_img_df.iterrows():\n            yield np.expand_dims(c_row['images'],0), np.expand_dims(np.expand_dims(c_row['masks'],-1),0)\n\nsimple_cnn.fit_generator(simple_gen(), \n                         steps_per_epoch=train_img_df.shape[0],\n                        epochs = 3)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"5c7c329a-3185-4a6b-a963-99248deedb15","_uuid":"1cc1bc84b76c1b41a3fa5b057b72feed880b3068"},"cell_type":"markdown","source":"# Apply Model to Test\nHere we apply the model to the test data"},{"metadata":{"_cell_guid":"5afdbc12-09b7-456c-9a72-dbeb10a289a7","_uuid":"058a5059e44f710a31cd19e1e9b8a72180befc49","trusted":true},"cell_type":"code","source":"%%time\ntest_df = img_df.query('TrainingSplit==\"test\"')\ntest_rows = []\ngroup_cols = ['Stage', 'ImageId']\nfor n_group, n_rows in test_df.groupby(group_cols):\n    c_row = {col_name: col_value for col_name, col_value in zip(group_cols, n_group)}\n    c_row['images'] = n_rows.query('ImageType == \"images\"')['path'].values.tolist()\n    test_rows += [c_row]\ntest_img_df = pd.DataFrame(test_rows)    \n\ntest_img_df['images'] = test_img_df['images'].map(read_and_stack).map(lambda x: x[:,:,:IMG_CHANNELS])\nprint(test_img_df.shape[0], 'images to process')\ntest_img_df.sample(1)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"9d784556-bcf6-4dfe-90b2-aa44dc54fe0c","_uuid":"e0db0bd3e6af0e2d79f7aa2fa81e3371ab11621a","trusted":true},"cell_type":"code","source":"%%time\ntest_img_df['masks'] = test_img_df['images'].map(lambda x: simple_cnn.predict(np.expand_dims(x, 0))[0, :, :, 0])","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"6fb7b659-e779-4cce-a729-5fe2e72021aa","_uuid":"c5c545456e4a10e33d4df4dcbbab254155bea42f"},"cell_type":"markdown","source":"## Show a few predictions"},{"metadata":{"_cell_guid":"31f9f734-b193-48db-9342-9ceb49faab39","_uuid":"d7e88a1c070da81a0255a3792eea2a3c0b0ec3e3","trusted":true},"cell_type":"code","source":"n_img = 3\nfrom skimage.morphology import closing, opening, disk\ndef clean_img(x):\n    return opening(closing(x, disk(1)), disk(3))\nfig, m_axs = plt.subplots(3, n_img, figsize = (12, 6))\nfor (_, d_row), (c_im, c_lab, c_clean) in zip(test_img_df.sample(n_img).iterrows(), \n                                     m_axs):\n    c_im.imshow(d_row['images'])\n    c_im.axis('off')\n    c_im.set_title('Microscope')\n    \n    c_lab.imshow(d_row['masks'])\n    c_lab.axis('off')\n    c_lab.set_title('Predicted')\n    \n    c_clean.imshow(clean_img(d_row['masks']))\n    c_clean.axis('off')\n    c_clean.set_title('Clean')","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"52734b1b-617a-4075-b6c1-4eaf019594cb","collapsed":true,"_uuid":"fdabcaf0484232ba704aeaa1b9bfeba1a539271f"},"cell_type":"markdown","source":"# Check RLE\nCheck that our approach for RLE encoding (stolen from [here](https://www.kaggle.com/rakhlin/fast-run-length-encoding-python)) works"},{"metadata":{"_cell_guid":"1d207c46-f3a8-42ec-8381-d4e6362cac9c","_uuid":"8fa0c109c0f035833d60a94e93867f0228c2e6dd","trusted":true},"cell_type":"code","source":"from skimage.morphology import label # label regions\ndef 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    return run_lengths\n\ndef prob_to_rles(x, cut_off = 0.5):\n    lab_img = label(x>cut_off)\n    if lab_img.max()<1:\n        lab_img[0,0] = 1 # ensure at least one prediction per image\n    for i in range(1, lab_img.max()+1):\n        yield rle_encoding(lab_img==i)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"3731ae6b-b38c-4bf7-afa7-4338126fc0a7","_uuid":"ccf9ce4c65200467574c93a8a4adb8d4b4cb4815"},"cell_type":"markdown","source":"## Calculate the RLEs for a Train Image"},{"metadata":{"_cell_guid":"6a247714-aa63-482e-a8a1-b0f11a3ac31f","_uuid":"5f2bb3e72ec8ed8186b55212f141d67beee101c7","trusted":true},"cell_type":"code","source":"_, train_rle_row = next(train_img_df.tail(5).iterrows()) \ntrain_row_rles = list(prob_to_rles(train_rle_row['masks']))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"817ae2a7-e0b3-469f-b41e-691512e582cc","_uuid":"d311881b690401d4032da7bbe2aa5f7f97b207e5"},"cell_type":"markdown","source":"## Take the RLEs from the CSV"},{"metadata":{"_cell_guid":"c74939d8-f78d-4856-b2ae-a69c2ca0b83c","_uuid":"984845ff5072fc3ebb1b106831e4992170d5902f","trusted":true},"cell_type":"code","source":"tl_rles = train_labels.query('ImageId==\"{ImageId}\"'.format(**train_rle_row))['EncodedPixels']","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"7af84b83-644c-44f7-a902-4797a0da9487","_uuid":"80f40ffd0afe3710f3646d843abfb0ed0491630e"},"cell_type":"markdown","source":"## Check\nSince we made some simplifications, we don't expect everything to be perfect, but pretty close"},{"metadata":{"_cell_guid":"72dd2317-89c7-4408-b42e-56c843fd54f6","_uuid":"4841b68b07de08b6d3bb7e054e2d5830962148d8","trusted":true},"cell_type":"code","source":"match, mismatch = 0, 0\nfor img_rle, train_rle in zip(sorted(train_row_rles, key = lambda x: x[0]), \n                             sorted(tl_rles, key = lambda x: x[0])):\n    for i_x, i_y in zip(img_rle, train_rle):\n        if i_x == i_y:\n            match += 1\n        else:\n            mismatch += 1\nprint('Matches: %d, Mismatches: %d, Accuracy: %2.1f%%' % (match, mismatch, 100.0*match/(match+mismatch)))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"d4801c91-c922-452f-a835-a4e93ae1f356","collapsed":true,"_uuid":"19c34be3cc8c907e4d36bfcb98b6781948ec21df"},"cell_type":"markdown","source":"# Calculate RLE for all the masks\nHere we generate the RLE for all the masks and output the the results to a table. We use a few morphological operations to clean up the images before submission since they can be very messy (remove single pixels, connect nearby regions, etc)"},{"metadata":{"_cell_guid":"2e9375d2-e6b8-462d-95b7-0fda696fbfc4","_uuid":"26d2c83004921a8b985084d8d168a54f4b728b61","trusted":true},"cell_type":"code","source":"test_img_df['rles'] = test_img_df['masks'].map(clean_img).map(lambda x: list(prob_to_rles(x)))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"65be8e2b-b46a-452f-a1d6-fe97b1932e68","_uuid":"93f011b814251537f4c574908876e6157566c0b3","trusted":true},"cell_type":"code","source":"out_pred_list = []\nfor _, c_row in test_img_df.iterrows():\n    for c_rle in c_row['rles']:\n        out_pred_list+=[dict(ImageId=c_row['ImageId'], \n                             EncodedPixels = ' '.join(np.array(c_rle).astype(str)))]\nout_pred_df = pd.DataFrame(out_pred_list)\nprint(out_pred_df.shape[0], 'regions found for', test_img_df.shape[0], 'images')\nout_pred_df.sample(3)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"7f5f0c3a-222f-4470-88a7-d2f77bd54542","_uuid":"4524606e4ae416dcf364a5170f82cf4ceea6d2af","trusted":true},"cell_type":"code","source":"out_pred_df[['ImageId', 'EncodedPixels']].to_csv('predictions.csv', index = False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"name":"python3","language":"python","display_name":"Python 3"},"language_info":{"name":"python","nbconvert_exporter":"python","version":"3.6.4","pygments_lexer":"ipython3","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"file_extension":".py"}},"nbformat":4,"nbformat_minor":1}