{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Airbus Ship Detection from Satellite Imagery\n\n**I decided to tackle this project with the motivation of getting some experience in deep learning for satellite imagery in order to recieve a thesis subject in Satellite Remote Sensing.**\n\nLet's get down to business.\n\n# 1. Overview"},{"metadata":{},"cell_type":"markdown","source":"In this competition, you are required to locate ships in images, and put an aligned bounding box segment around the ships you locate. Many images do not contain ships, and those that do may contain multiple ships. Ships within and across images may differ in size (sometimes significantly) and be located in open sea, at docks, marinas, etc.\n\nFor this metric, object segments cannot overlap. There were a small percentage of images in both the Train and Test set that had slight overlap of object segments when ships were directly next to each other. Any segments overlaps were removed by setting them to background (i.e., non-ship) encoding. Therefore, some images have a ground truth may be an aligned bounding box with some pixels removed from an edge of the segment. These small adjustments will have a minimal impact on scoring, since the scoring evaluates over increasing overlap thresholds."},{"metadata":{},"cell_type":"markdown","source":"![](https://images.unsplash.com/photo-1460186136353-977e9d6085a1?ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80)"},{"metadata":{},"cell_type":"markdown","source":"# 2. Model Parameters"},{"metadata":{},"cell_type":"markdown","source":"I put the parameters on top to tune them more easily during training."},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 4\nEDGE_CROP = 16\nNB_EPOCHS = 5\nGAUSSIAN_NOISE = 0.1\nUPSAMPLE_MODE = 'SIMPLE'\n# downsampling inside the network\nNET_SCALING = None\n# downsampling in preprocessing\nIMG_SCALING = (1, 1)\n# number of validation images to use\nVALID_IMG_COUNT = 400\n# maximum number of steps_per_epoch in training\nMAX_TRAIN_STEPS = 200\nAUGMENT_BRIGHTNESS = False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"About the Data:\n* train_ship_segmentations_v2.csv: This file contains the RLE (Run Length Encoded is a way to encode image pixels in a more summerized way, especially when images have a black or white background) masks of ships in each image. If there are no ships, the EncodedPixel column is blank.\n* train_v2: v2 contains the combined Train and Test images of the original dataset.\n* test_v2: A folder with test images, size 768x768 px.\n* sample_submission_v2csv: a file containing all the ImageId for the predictions of ships on those images."},{"metadata":{"trusted":true},"cell_type":"code","source":"#List files in directory\n!ls ../input/airbus-ship-detection","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3. Import Libraries and Make sure encode/decode works"},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom skimage.io import imread\nimport matplotlib.pyplot as plt\nfrom skimage.segmentation import mark_boundaries\nfrom skimage.util import montage\n#from skimage.util.montage import montage2d as montage\nmontage_rgb = lambda x: np.stack([montage(x[:, :, :, i]) for i in range(x.shape[3])], -1)\nship_dir = '../input/airbus-ship-detection'\ntrain_image_dir = os.path.join(ship_dir, 'train_v2')\ntest_image_dir = os.path.join(ship_dir, 'test_v2')\nimport gc; gc.enable() # memory is tight\n\nfrom skimage.morphology import label\ndef multi_rle_encode(img):\n    labels = label(img[:, :, 0])\n    return [rle_encode(labels==k) for k in np.unique(labels[labels>0])]\n\n# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\ndef rle_encode(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n\ndef rle_decode(mask_rle, shape=(768, 768)):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (height,width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape).T  # Needed to align to RLE direction\n\ndef masks_as_image(in_mask_list):\n    # Take the individual ship masks and create a single mask array for all ships\n    all_masks = np.zeros((768, 768), dtype = np.int16)\n    #if isinstance(in_mask_list, list):\n    for mask in in_mask_list:\n        if isinstance(mask, str):\n            all_masks += rle_decode(mask)\n    return np.expand_dims(all_masks, -1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"masks = pd.read_csv(os.path.join('../input/airbus-ship-detection/',\n                                 'train_ship_segmentations_v2.csv'))\nprint(masks.shape)\nprint(masks.shape[0], 'masks found')\nprint(masks['ImageId'].value_counts().shape[0], 'unique ImageId')\nmasks.head() # 2 variables of masks","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"**Checking Run Length Encoded is correctly done**"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (10, 5)) #Plotting Grid ax1 and ax2 are columns\nrle_0 = masks.query('ImageId==\"00021ddc3.jpg\"')['EncodedPixels']\nimg_0 = masks_as_image(rle_0)\nax1.imshow(img_0[:, :, 0]) #all columns and rows, red channel\nax1.set_title('Image$_0$')\nrle_1 = multi_rle_encode(img_0)\nimg_1 = masks_as_image(rle_1)\nax2.imshow(img_1[:, :, 0])\nax2.set_title('Image$_1$')\nprint('Check Decoding->Encoding',\n      'RLE_0:', len(rle_0), '->',\n      'RLE_1:', len(rle_1))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 4. Split into Training and Test Sets"},{"metadata":{},"cell_type":"markdown","source":"> **Using 70/30 for training and test set is a common and widely accepted ratio.**"},{"metadata":{"trusted":true},"cell_type":"code","source":"masks['ships'] = masks['EncodedPixels'].map(lambda c_row: 1 if isinstance(c_row, str) else 0) #create column ships with 0 or 1\nunique_img_ids = masks.groupby('ImageId').agg({'ships': 'sum'}).reset_index() # Create new table and group by ImageId and sum the 0 and 1\nunique_img_ids['has_ship'] = unique_img_ids['ships'].map(lambda x: 1.0 if x>0 else 0.0) #create column has_ship by filtering >0\nunique_img_ids['has_ship_vec'] = unique_img_ids['has_ship'].map(lambda x: [x]) #put has_ship into squared braquets\n# some files are too small/corrupt\nunique_img_ids['file_size_kb'] = unique_img_ids['ImageId'].map(lambda c_img_id: \n                                                               os.stat(os.path.join(train_image_dir, \n                                                                    c_img_id)).st_size/1024)# Create column with each ImageId size in kb\nunique_img_ids = unique_img_ids[unique_img_ids['file_size_kb']>50] # keep only 50kb files, reject small files\nunique_img_ids['file_size_kb'].hist()\nmasks.drop(['ships'], axis=1, inplace=True) # Delete the new ships column in masks\nunique_img_ids.sample(5)\nunique_img_ids.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ntrain_ids, valid_ids = train_test_split(unique_img_ids, \n                 test_size = 0.3, \n                 stratify = unique_img_ids['ships']) #create training (70%) and test sets (30%)\ntrain_df = pd.merge(masks, train_ids)\nvalid_df = pd.merge(masks, valid_ids)\nprint(train_df.shape[0], 'training masks')\nprint(valid_df.shape[0], 'validation masks')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Note that in the Ships column the amout can be >1 because ImageId has been grouped and aggregated with ship 0 or 1**\n\nSo here, we examine how often ships appear and replace the ones without any ships with 0"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['ships'].hist(bins=np.arange(16))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"> We can observe a Log Normal Distribution.\n> \n![](https://miro.medium.com/max/875/1*nOMS0KgevT7YfqtfnhgXUg.png)"},{"metadata":{},"cell_type":"markdown","source":"# 5. Undersample Empty Images"},{"metadata":{},"cell_type":"markdown","source":"**Here we want to undersample the empty images to get a better balanced group classes**\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df['grouped_ship_count'] = train_df['ships'].map(lambda x: (x+1)//2).clip(0, 7)#floor div // rounds the result down to the nearest whole number\n#train_df['grouped_ship_count'].hist()                                                #clip() sets min and max intervals.\ndef sample_ships(in_df, base_rep_val=1500):\n    if in_df['ships'].values[0]==0:\n        return in_df.sample(base_rep_val//3) # even more strongly undersample no ships\n    else:\n        return in_df.sample(base_rep_val, replace=(in_df.shape[0]<base_rep_val))\n    \nbalanced_train_df = train_df.groupby('grouped_ship_count').apply(sample_ships)\nbalanced_train_df['ships'].hist(bins=np.arange(16)) #with 10 bins","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 6. Decode all the RLEs into Images"},{"metadata":{},"cell_type":"markdown","source":"> We make a generator to produce batches of images"},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_image_gen(in_df, batch_size = BATCH_SIZE):\n    all_batches = list(in_df.groupby('ImageId'))\n    out_rgb = []\n    out_mask = []\n    while True:\n        np.random.shuffle(all_batches)\n        for c_img_id, c_masks in all_batches:\n            rgb_path = os.path.join(train_image_dir, c_img_id)\n            c_img = imread(rgb_path)\n            c_mask = masks_as_image(c_masks['EncodedPixels'].values)\n            if IMG_SCALING is not None:\n                c_img = c_img[::IMG_SCALING[0], ::IMG_SCALING[1]]\n                c_mask = c_mask[::IMG_SCALING[0], ::IMG_SCALING[1]]\n            out_rgb += [c_img]\n            out_mask += [c_mask]\n            if len(out_rgb)>=batch_size:\n                yield np.stack(out_rgb, 0)/255.0, np.stack(out_mask, 0)\n                out_rgb, out_mask=[], []","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_gen = make_image_gen(balanced_train_df)\ntrain_x, train_y = next(train_gen)\nprint('x', train_x.shape, train_x.min(), train_x.max())\nprint('y', train_y.shape, train_y.min(), train_y.max())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize = (30, 10))\nbatch_rgb = montage_rgb(train_x)\nbatch_seg = montage(train_y[:, :, :, 0])\nax1.imshow(batch_rgb)\nax1.set_title('Images')\nax2.imshow(batch_seg)\nax2.set_title('Segmentations')\nax3.imshow(mark_boundaries(batch_rgb, \n                           batch_seg.astype(int)))\nax3.set_title('Outlined Ships')\nfig.savefig('overview.png')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 6. Make the Validation Set"},{"metadata":{"trusted":true},"cell_type":"code","source":"valid_x, valid_y = next(make_image_gen(valid_df, VALID_IMG_COUNT)) #The next() function returns the next item in an iterator.\nprint(valid_x.shape, valid_y.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 7. Augment the Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\ndg_args = dict(featurewise_center = False, \n                  samplewise_center = False,\n                  rotation_range = 15, \n                  width_shift_range = 0.1, \n                  height_shift_range = 0.1, \n                  shear_range = 0.01,\n                  zoom_range = [0.9, 1.25],  \n                  horizontal_flip = True, \n                  vertical_flip = True,\n                  fill_mode = 'reflect',\n                   data_format = 'channels_last') #create a dictionary with the arguments of the Image Generator\n# brightness can be problematic since it seems to change the labels differently from the images \nif AUGMENT_BRIGHTNESS:\n    dg_args[' brightness_range'] = [0.5, 1.5]\nimage_gen = ImageDataGenerator(**dg_args)\n\nif AUGMENT_BRIGHTNESS:\n    dg_args.pop('brightness_range')\nlabel_gen = ImageDataGenerator(**dg_args)\n\ndef create_aug_gen(in_gen, seed = None):\n    np.random.seed(seed if seed is not None else np.random.choice(range(9999)))\n    for in_x, in_y in in_gen:\n        seed = np.random.choice(range(9999))\n        # keep the seeds syncronized otherwise the augmentation to the images is different from the masks\n        g_x = image_gen.flow(255*in_x, \n                             batch_size = in_x.shape[0], \n                             seed = seed, \n                             shuffle=True)\n        g_y = label_gen.flow(in_y, \n                             batch_size = in_x.shape[0], \n                             seed = seed, \n                             shuffle=True)\n\n        yield next(g_x)/255.0, next(g_y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cur_gen = create_aug_gen(train_gen)\nt_x, t_y = next(cur_gen)\nprint('x', t_x.shape, t_x.dtype, t_x.min(), t_x.max())\nprint('y', t_y.shape, t_y.dtype, t_y.min(), t_y.max())\n# only keep first 9 samples to examine in detail\nt_x = t_x[:9]\nt_y = t_y[:9]\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize = (20, 10))\nax1.imshow(montage_rgb(t_x), cmap='gray')\nax1.set_title('images')\nax2.imshow(montage(t_y[:, :, :, 0]), cmap='gray_r')\nax2.set_title('ships')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 8. Design the Model"},{"metadata":{},"cell_type":"markdown","source":"Here is the U-net model with an another example of application:\n\nhttps://tuatini.me/practical-image-segmentation-with-unet/"},{"metadata":{},"cell_type":"markdown","source":"![](https://tuatini.me/content/images/2017/09/unet_stack_decode-2.png)"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras import models, layers\n# Build U-Net model\ndef upsample_conv(filters, kernel_size, strides, padding):\n    return layers.Conv2DTranspose(filters, kernel_size, strides=strides, padding=padding)\ndef upsample_simple(filters, kernel_size, strides, padding):\n    return layers.UpSampling2D(strides)\n\nif UPSAMPLE_MODE=='DECONV':\n    upsample=upsample_conv\nelse:\n    upsample=upsample_simple\n    \ninput_img = layers.Input(t_x.shape[1:], name = 'RGB_Input')\npp_in_layer = input_img\nif NET_SCALING is not None:\n    pp_in_layer = layers.AvgPool2D(NET_SCALING)(pp_in_layer)\n    \npp_in_layer = layers.GaussianNoise(GAUSSIAN_NOISE)(pp_in_layer)\npp_in_layer = layers.BatchNormalization()(pp_in_layer)\n\nc1 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (pp_in_layer)\nc1 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (c1)          #Going Down\np1 = layers.MaxPooling2D((2, 2)) (c1) #2x2 kernel\n\nc2 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (p1)\nc2 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (c2)\np2 = layers.MaxPooling2D((2, 2)) (c2)\n\nc3 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (p2)\nc3 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (c3)\np3 = layers.MaxPooling2D((2, 2)) (c3)\n\nc4 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (p3)\nc4 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (c4)\np4 = layers.MaxPooling2D(pool_size=(2, 2)) (c4)\n\n\nc5 = layers.Conv2D(128, (3, 3), activation='relu', padding='same') (p4)       #Bottle Neck\nc5 = layers.Conv2D(128, (3, 3), activation='relu', padding='same') (c5)\n\nu6 = upsample(64, (2, 2), strides=(2, 2), padding='same') (c5)                 #Going Up\nu6 = layers.concatenate([u6, c4])\nc6 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (u6)\nc6 = layers.Conv2D(64, (3, 3), activation='relu', padding='same') (c6)\n\nu7 = upsample(32, (2, 2), strides=(2, 2), padding='same') (c6)\nu7 = layers.concatenate([u7, c3])\nc7 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (u7)\nc7 = layers.Conv2D(32, (3, 3), activation='relu', padding='same') (c7)\n\nu8 = upsample(16, (2, 2), strides=(2, 2), padding='same') (c7)\nu8 = layers.concatenate([u8, c2])\nc8 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (u8)\nc8 = layers.Conv2D(16, (3, 3), activation='relu', padding='same') (c8)\n\nu9 = upsample(8, (2, 2), strides=(2, 2), padding='same') (c8)\nu9 = layers.concatenate([u9, c1], axis=3)\nc9 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (u9)\nc9 = layers.Conv2D(8, (3, 3), activation='relu', padding='same') (c9)\n\nd = layers.Conv2D(1, (1, 1), activation='sigmoid') (c9)\nd = layers.Cropping2D((EDGE_CROP, EDGE_CROP))(d)\nd = layers.ZeroPadding2D((EDGE_CROP, EDGE_CROP))(d)\nif NET_SCALING is not None:\n    d = layers.UpSampling2D(NET_SCALING)(d)\n\nseg_model = models.Model(inputs=[input_img], outputs=[d])\nseg_model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 9. Train the Neural Network"},{"metadata":{"trusted":true},"cell_type":"code","source":"import keras.backend as K\nfrom keras.optimizers import Adam          #Learning Rate Optimizer for optimal weights\nfrom keras.losses import binary_crossentropy\ndef dice_coef(y_true, y_pred, smooth=1):\n    intersection = K.sum(y_true * y_pred, axis=[1,2,3])\n    union = K.sum(y_true, axis=[1,2,3]) + K.sum(y_pred, axis=[1,2,3])\n    return K.mean( (2. * intersection + smooth) / (union + smooth), axis=0)\ndef dice_p_bce(in_gt, in_pred):\n    return 1e-3*binary_crossentropy(in_gt, in_pred) - dice_coef(in_gt, in_pred)\ndef true_positive_rate(y_true, y_pred):\n    return K.sum(K.flatten(y_true)*K.flatten(K.round(y_pred)))/K.sum(y_true)\nseg_model.compile(optimizer=Adam(1e-4, decay=1e-6), loss=dice_p_bce, metrics=[dice_coef, 'binary_accuracy', true_positive_rate])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\nweight_path=\"{}_weights.best.hdf5\".format('seg_model')\n\ncheckpoint = ModelCheckpoint(weight_path, monitor='val_dice_coef', verbose=1, \n                             save_best_only=True, mode='max', save_weights_only = True)\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_dice_coef', factor=0.5, \n                                   patience=3, \n                                   verbose=1, mode='max', epsilon=0.0001, cooldown=2, min_lr=1e-6)\nearly = EarlyStopping(monitor=\"val_dice_coef\", \n                      mode=\"max\", \n                      patience=15) # probably needs to be more patient, but kaggle time is limited\ncallbacks_list = [checkpoint, early, reduceLROnPlat]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Here, Fitting the the model on the training data to build the predictive weights between neurons****"},{"metadata":{"trusted":true},"cell_type":"code","source":"step_count = min(MAX_TRAIN_STEPS, balanced_train_df.shape[0]//BATCH_SIZE)\naug_gen = create_aug_gen(make_image_gen(balanced_train_df))\nloss_history = [seg_model.fit_generator(aug_gen, \n                             steps_per_epoch=step_count, \n                             epochs=NB_EPOCHS, \n                             validation_data=(valid_x, valid_y),\n                             callbacks=callbacks_list,\n                            workers=1 # the generator is not very thread safe\n                                       )]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 10. Plotting Results "},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_loss(loss_history):\n    epich = np.cumsum(np.concatenate(\n        [np.linspace(0.5, 1, len(mh.epoch)) for mh in loss_history]))\n    fig, (ax1, ax2, ax3, ax4) = plt.subplots(1, 4, figsize=(22, 10))\n    _ = ax1.plot(epich,\n                 np.concatenate([mh.history['loss'] for mh in loss_history]),\n                 'b-',\n                 epich, np.concatenate(\n            [mh.history['val_loss'] for mh in loss_history]), 'r-')\n    ax1.legend(['Training', 'Validation'])\n    ax1.set_title('Loss')\n\n    _ = ax2.plot(epich, np.concatenate(\n        [mh.history['true_positive_rate'] for mh in loss_history]), 'b-',\n                     epich, np.concatenate(\n            [mh.history['val_true_positive_rate'] for mh in loss_history]),\n                     'r-')\n    ax2.legend(['Training', 'Validation'])\n    ax2.set_title('True Positive Rate\\n(Positive Accuracy)')\n    \n    _ = ax3.plot(epich, np.concatenate(\n        [mh.history['binary_accuracy'] for mh in loss_history]), 'b-',\n                     epich, np.concatenate(\n            [mh.history['val_binary_accuracy'] for mh in loss_history]),\n                     'r-')\n    ax3.legend(['Training', 'Validation'])\n    ax3.set_title('Binary Accuracy (%)')\n    \n    _ = ax4.plot(epich, np.concatenate(\n        [mh.history['dice_coef'] for mh in loss_history]), 'b-',\n                     epich, np.concatenate(\n            [mh.history['val_dice_coef'] for mh in loss_history]),\n                     'r-')\n    ax4.legend(['Training', 'Validation'])\n    ax4.set_title('DICE')\n\nshow_loss(loss_history)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 11. Vizualise Predictions and Evaluate Results"},{"metadata":{"trusted":true},"cell_type":"code","source":"seg_model.load_weights(weight_path)\nseg_model.save('seg_model.h5')\npred_y = seg_model.predict(valid_x)\nprint(pred_y.shape, pred_y.min(), pred_y.max(), pred_y.mean())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1, 1, figsize = (10, 10))\nax.hist(pred_y.ravel(), np.linspace(0, 1, 10))\nax.set_xlim(0, 1)\nax.set_yscale('log', nonposy='clip')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## **12. Prepare Full Resolution Model**"},{"metadata":{"trusted":true},"cell_type":"code","source":"if IMG_SCALING is not None:\n    fullres_model = models.Sequential()\n    fullres_model.add(layers.AvgPool2D(IMG_SCALING, input_shape = (None, None, 3)))\n    fullres_model.add(seg_model)\n    fullres_model.add(layers.UpSampling2D(IMG_SCALING))\nelse:\n    fullres_model = seg_model\nfullres_model.save('fullres_model.h5')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## **13. Run the Test Data**"},{"metadata":{"trusted":true},"cell_type":"code","source":"test_paths = os.listdir(test_image_dir)\nprint(len(test_paths), 'test images found')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, m_axs = plt.subplots(20, 2, figsize = (10, 40))\n[c_ax.axis('off') for c_ax in m_axs.flatten()]\nfor (ax1, ax2), c_img_name in zip(m_axs, test_paths):\n    c_path = os.path.join(test_image_dir, c_img_name)\n    c_img = imread(c_path)\n    first_img = np.expand_dims(c_img, 0)/255.0\n    first_seg = fullres_model.predict(first_img)\n    ax1.imshow(first_img[0])\n    ax1.set_title('Image')\n    ax2.imshow(first_seg[0, :, :, 0], vmin = 0, vmax = 1)\n    ax2.set_title('Prediction')\nfig.savefig('test_predictions.png')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## **14. Create Submission**"},{"metadata":{"trusted":true},"cell_type":"code","source":"from tqdm import tqdm_notebook\nfrom skimage.morphology import binary_opening, disk\nout_pred_rows = []\nfor c_img_name in tqdm_notebook(test_paths):\n    c_path = os.path.join(test_image_dir, c_img_name)\n    c_img = imread(c_path)\n    c_img = np.expand_dims(c_img, 0)/255.0\n    cur_seg = fullres_model.predict(c_img)[0]\n    cur_seg = binary_opening(cur_seg>0.5, np.expand_dims(disk(2), -1))\n    cur_rles = multi_rle_encode(cur_seg)\n    if len(cur_rles)>0:\n        for c_rle in cur_rles:\n            out_pred_rows += [{'ImageId': c_img_name, 'EncodedPixels': c_rle}]\n    else:\n        out_pred_rows += [{'ImageId': c_img_name, 'EncodedPixels': None}]\n    gc.collect()\nsubmission_df = pd.DataFrame(out_pred_rows)[['ImageId', 'EncodedPixels']]\nsubmission_df.to_csv('submission.csv', index=False)\nsubmission_df.sample(3)","execution_count":null,"outputs":[]}],"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":1}