{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Start"},{"metadata":{"trusted":true},"cell_type":"code","source":"# !pip install git+https://github.com/qubvel/segmentation_models\n!pip install -U segmentation-models==0.2.1\n!pip install albumentations","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os, sys, gc\n\nimport pandas as pd\nimport numpy  as np\n\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\nfrom tqdm.auto       import tqdm\nfrom multiprocessing import Pool, cpu_count\n\nfrom cv2        import resize\nfrom skimage.io import imread     as skiImgRead\n# from imgaug     import augmenters as iaa, HooksImages\n\nfrom skimage.morphology import label\n\nfrom sklearn.model_selection import train_test_split\n\nfrom segmentation_models           import Unet\nfrom segmentation_models.backbones import get_preprocessing\nfrom segmentation_models.utils     import set_trainable\nfrom segmentation_models.losses    import bce_jaccard_loss, bce_dice_loss\nfrom segmentation_models.metrics   import iou_score, f2_score\n\nfrom keras.callbacks import ReduceLROnPlateau, ModelCheckpoint, EarlyStopping, LearningRateScheduler","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"IMG_HW    = 768\nZOOM_HW   = 384\n\nDATA_DIR  = '../input/airbus-ship-detection'\nTRAIN_DIR = os.path.join(DATA_DIR, 'train_v2')\nTEST_DIR  = os.path.join(DATA_DIR, 'test_v2')\n\nBACKBONE  = 'resnet34'\n\npreprocess_input = get_preprocessing(BACKBONE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def rle_decode(rle_mask):\n    '''\n    rle_mask: 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    '''\n    s = rle_mask.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(IMG_HW*IMG_HW, dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(IMG_HW,IMG_HW).T\n\n\ndef rle_encode(im):\n    '''\n    im: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels = im.flatten(order = 'F')\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)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def count_pix_inpool(df_col):\n    pool = Pool()\n    res = pool.map( count_pix, df_col.items() )\n    pool.close()\n    pool.join()\n    return res\n\ndef count_pix(row):\n    v = row[1]\n    if v is np.nan or type(v) != str: \n        return v\n    else:\n        return rle_decode(v).sum()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Load data"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_csv  = pd.read_csv( os.path.join( DATA_DIR, 'train_ship_segmentations_v2.csv') )\ntrain_csv['withShip'] = ~train_csv['EncodedPixels'].isnull()\ntrain_csv['npixel'] = count_pix_inpool( train_csv['EncodedPixels'] )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# EDA"},{"metadata":{"trusted":true},"cell_type":"code","source":"sns.kdeplot(train_csv['npixel'])\nplt.xscale('log')\nplt.legend().set_visible(False)\nplt.xlabel('# of pixel is ship')\nplt.ylabel('Density')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.pie(\n    x       = (train_csv['withShip'].value_counts()/train_csv.shape[0]).values, \n    labels  = (train_csv['withShip'].value_counts()/train_csv.shape[0]).index,\n    autopct = '%3.1f %%',\n    shadow  = True, \n    labeldistance = 1.1, \n    startangle  = 90,\n    pctdistance = 0.6\n);\nplt.title('Image with Ship(s) or Not');\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"figdf = train_csv.dropna().groupby('ImageId').count()['withShip'].value_counts()\nplt.bar(figdf.index, figdf.values)\nplt.xlabel('# of Ship(s) in image', fontsize=14)\nplt.ylabel('# of Images', fontsize=14)\nplt.title('Raw Dataset with Ship', fontsize=18)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"np.sqrt( train_csv['npixel'].min() )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Balance the data"},{"metadata":{"trusted":true},"cell_type":"code","source":"DROP_NO_SHIP_FRACTION = 0.8\n\nbalanced_train_csv = (\n    train_csv\n    .set_index('ImageId')\n    .drop(\n        train_csv.loc[\n            train_csv.isna().any(axis=1),\n            'ImageId'\n        ].sample( frac = DROP_NO_SHIP_FRACTION )\n    )\n    .drop(\n        train_csv.query('npixel<32')['ImageId']\n    )\n    .reset_index()\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"figdf = balanced_train_csv.fillna(0).groupby('ImageId').sum()['withShip'].value_counts()\nplt.bar(figdf.index, figdf.values)\nplt.xlabel('# of Ship(s) in image', fontsize=14)\nplt.ylabel('# of Images', fontsize=14)\nplt.title('Balanced Dataset w/o Ship', fontsize=18)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Generate data for model"},{"metadata":{"trusted":true},"cell_type":"code","source":"b_train_csv, b_valid_csv = train_test_split(balanced_train_csv['ImageId'], test_size = 0.2)\n\nb_train_csv = balanced_train_csv.set_index('ImageId').loc[b_train_csv].reset_index()\nb_valid_csv = balanced_train_csv.set_index('ImageId').loc[b_valid_csv].reset_index()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def batch_data_gen(csv_df, dir_prefix, batch_size, augmentation=None):\n    name_idx_df = csv_df.set_index('ImageId')\n\n#     img_ids = name_idx_df.index.unique().to_numpy()\n    img_ids = np.array( name_idx_df.index.unique().tolist() )\n\n    n_imgs  = img_ids.shape[0]\n    \n    while True:\n        np.random.shuffle(img_ids)\n        for idx in range(0, n_imgs, batch_size):\n            batch_x = np.zeros( (batch_size,) + (ZOOM_HW, ZOOM_HW, 3) )\n            batch_y = np.zeros( (batch_size,) + (ZOOM_HW, ZOOM_HW, 1) )\n\n            end_idx = idx + batch_size\n            batch_img_ids = img_ids[idx:end_idx]\n            \n            for i,img_id in enumerate(batch_img_ids):\n                img_df = name_idx_df.loc[[img_id]]\n                x, y = load_paired_data(img_df, dir_prefix, augmentation=augmentation)\n                batch_x[i] += x\n                batch_y[i] += y\n            \n            yield batch_x, batch_y\n\n\n\ndef load_paired_data(df, dir_prefix, augmentation=None):\n    img_id = df.index.unique()[0]\n\n    try:\n        image = preprocess_input( skiImgRead( os.path.join(dir_prefix, img_id) ) )\n    except:\n        image = preprocess_input( np.zeros((IMG_HW, IMG_HW, 3), dtype=np.uint8) )\n\n    mask = np.zeros((IMG_HW, IMG_HW, 1))\n    for _,mask_rle in df['EncodedPixels'].iteritems():\n        if mask_rle is np.nan:\n            continue\n        mask[:,:,0] += rle_decode(mask_rle)\n\n    if augmentation:\n        augmented = augmentation(image=image, mask=mask)\n        image = augmented['image']\n        mask  = augmented['mask']\n    \n    image = resize(image, (ZOOM_HW,ZOOM_HW))\n    mask  = resize(mask.reshape(IMG_HW,IMG_HW), (ZOOM_HW,ZOOM_HW)).reshape((ZOOM_HW,ZOOM_HW,1))\n    return image, mask","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from albumentations import (\n    Compose, HorizontalFlip, VerticalFlip, RandomRotate90, ShiftScaleRotate, Transpose,\n    OneOf, ToFloat,\n    RandomBrightness, RandomContrast, RandomGamma, CLAHE,\n    GridDistortion, ElasticTransform, JpegCompression,\n    RGBShift, GaussNoise, IAAAdditiveGaussianNoise, HueSaturationValue,\n    Blur, MotionBlur, MedianBlur, RandomBrightnessContrast,\n    GridDistortion, OpticalDistortion, RandomSizedCrop, CenterCrop\n)\n\naugmentor = Compose([\n    OneOf([\n        HorizontalFlip(),\n        VerticalFlip(),\n        RandomRotate90(),\n        Transpose(),\n    ], p=0.8), \n    ShiftScaleRotate(rotate_limit=20),\n    OneOf([\n        MotionBlur(blur_limit=3),\n        MedianBlur(blur_limit=3),\n        Blur(blur_limit=3),\n    ], p=0.3),\n    OneOf([\n        RandomGamma(),\n        RandomContrast(),\n        RandomBrightness(),\n        CLAHE(),\n     ], p=0.3),\n    OneOf([\n        IAAAdditiveGaussianNoise(),\n        HueSaturationValue(),\n        GaussNoise(),\n    ], p=0.2),\n    OneOf([\n        ElasticTransform(),\n        OpticalDistortion(),\n        GridDistortion(),\n    ], p=0.3),\n    RandomSizedCrop(min_max_height=(IMG_HW/2, IMG_HW), height=IMG_HW, width=IMG_HW, p=0.3),\n    ToFloat(max_value=1),\n],p=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axs = plt.subplots(ncols=2, nrows=5, figsize=(10, 10), sharex=True, sharey=True)\n\nfor i, img_id in enumerate( np.random.choice(train_csv.dropna()['ImageId'].unique(), 5) ):\n#     print (img_id)\n    img_df = train_csv.set_index('ImageId').loc[[img_id]]\n    x, y = load_paired_data(img_df, TRAIN_DIR, augmentation=augmentor)\n\n    axs[i,0].set_ylabel(img_id)\n    axs[i,0].imshow(x/255)\n    axs[i,1].imshow(y[:,:,0])\n\naxs[0,0].set_title('Input')\naxs[0,1].set_title('Mask')\n\nplt.xticks([])\nplt.yticks([])\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Unet(\n    BACKBONE, \n    encoder_weights='imagenet',\n    classes=1, \n    activation='sigmoid', \n    input_shape=(ZOOM_HW, ZOOM_HW, 3),\n    decoder_filters=(128, 64, 32, 16, 8),\n)\n\n# model.compile('Adam', loss='binary_crossentropy')\nmodel.compile(optimizer='Adam', loss=bce_dice_loss, metrics=[iou_score])\n# model.compile(optimizer='Adadelta', loss=bce_dice_loss, metrics=[iou_score])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 32","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"b_train_csv['ImageId'].unique().shape[0]//BATCH_SIZE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"checkpoint = ModelCheckpoint(\n    filepath='./best_model.h5', \n    monitor='val_iou_score', mode='max', \n    save_best_only=True, save_weights_only=True, \n    verbose=1\n)\n\nreduce_lr  = ReduceLROnPlateau(\n    monitor='val_loss', mode='min', \n    factor=0.3, patience=3, min_lr=0.00001, \n    verbose=1\n)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit_generator(\n    generator        = batch_data_gen(b_train_csv, TRAIN_DIR, BATCH_SIZE, augmentation=None), \n    validation_data  = batch_data_gen(b_valid_csv, TRAIN_DIR, BATCH_SIZE), \n    validation_steps = 50,\n    steps_per_epoch  = 500,\n    epochs           = 20,\n    verbose = 2,\n    callbacks=[ checkpoint, reduce_lr ]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"los  = model.history.history['loss']\nvlos = model.history.history['val_loss']\niou  = model.history.history['iou_score']\nviou = model.history.history['val_iou_score']\n\nepochs = np.arange(1, len(los)+1)\nplt.plot(epochs, los,  label='Training loss')\nplt.plot(epochs, vlos, label='Validation loss')\nplt.legend()\nplt.show()\n\nepochs = np.arange(1, len(los)+1)\nplt.plot(epochs, iou,  label='IoU')\nplt.plot(epochs, viou, label='Validation IoU')\nplt.legend()\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights('best_model.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"lrs = LearningRateScheduler(lambda epoch: 0.001 * 0.3 ** (epoch // 2))\n                      \nhistory = model.fit_generator(\n    generator        = batch_data_gen(b_train_csv, TRAIN_DIR, BATCH_SIZE, augmentation=augmentor), \n    validation_data  = batch_data_gen(b_valid_csv, TRAIN_DIR, BATCH_SIZE), \n    validation_steps = 20,\n    steps_per_epoch  = 150,\n    epochs           = 10,\n    verbose = 2,\n    callbacks=[ checkpoint, lrs ],\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"los  = model.history.history['loss']\nvlos = model.history.history['val_loss']\niou  = model.history.history['iou_score']\nviou = model.history.history['val_iou_score']\n\nepochs = np.arange(1, len(los)+1)\nplt.plot(epochs, los,  label='Training loss')\nplt.plot(epochs, vlos, label='Validation loss')\nplt.legend()\nplt.show()\n\nepochs = np.arange(1, len(los)+1)\nplt.plot(epochs, iou,  label='IoU')\nplt.plot(epochs, viou, label='Validation IoU')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights('best_model.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axs = plt.subplots(ncols=3, nrows=5, figsize=(10, 15), sharex=True, sharey=True)\n\nfor i, img_id in enumerate( np.random.choice(train_csv.dropna()['ImageId'].unique(), 5) ):\n    print (img_id)\n    img_df = train_csv.set_index('ImageId').loc[[img_id]]\n    x, y = load_paired_data(img_df, TRAIN_DIR)\n    yp = model.predict( np.expand_dims(preprocess_input(x), axis=0) )\n\n    axs[i,0].set_ylabel(img_id)\n    axs[i,0].imshow(x)\n    axs[i,1].imshow(y[:,:,0])\n    axs[i,2].imshow(yp[0,:,:,0])\n\naxs[0,0].set_title('Input')\naxs[0,1].set_title('Mask')\naxs[0,2].set_title('Predict')\n\nplt.xticks([])\nplt.yticks([])\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_csv = pd.DataFrame(columns=['ImageId', 'EncodedPixels'])\n\nfor test_id in tqdm( os.listdir( TEST_DIR ) ):\n    fp  = os.path.join( TEST_DIR, test_id )\n    img = skiImgRead(fp)\n    assert ( img.shape == (IMG_HW, IMG_HW, 3) ), 'Bad Shape in image: \"{}\"'.format(fp)\n\n    img = resize(img, (ZOOM_HW, ZOOM_HW))\n\n    # TTA\n    imgTTA1 = preprocess_input(img).reshape(1, ZOOM_HW, ZOOM_HW, 3)\n    \n    imgTTA1 = imgTTA1[:, :: 1, :: 1, :]\n    imgTTA2 = imgTTA1[:, :: 1, ::-1, :]\n    imgTTA3 = imgTTA1[:, ::-1, :: 1, :]\n    imgTTA4 = imgTTA1[:, ::-1, ::-1, :]\n    \n    (rTTA1,rTTA2,rTTA3,rTTA4) = model.predict( np.concatenate( [imgTTA1, imgTTA2, imgTTA3, imgTTA4] ) )[:,:,:,0]\n\n    result = (\n        rTTA1[:: 1, :: 1] + \n        rTTA2[:: 1, ::-1] + \n        rTTA3[::-1, :: 1] + \n        rTTA4[::-1, ::-1]\n    )/4\n    \n    result = resize(result, (IMG_HW, IMG_HW))\n    labels = label( (result>0.5)+0 )\n\n    \n    # No Ship\n    if labels.max() == 0:\n        sub_csv = sub_csv.append({'ImageId':test_id}, ignore_index=True)\n    else:\n        for k in np.unique(labels[labels>0]):\n            sub_csv = sub_csv.append(\n                {\n                    'ImageId'      : test_id, \n                    'EncodedPixels': rle_encode(labels==k)\n                }, ignore_index=True)\n#     break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_csv.to_csv('submission.csv', index=False)","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}