{"cells":[{"metadata":{"_uuid":"8d84f5d2fe3e7ed64cc7c6bffde94115a117e373"},"cell_type":"markdown","source":"Current competition metric implies segmenation task. However one valid approach could incorporate object detection. In this direcrion and borrowing stuff from Kevin's excellent kernel [https://www.kaggle.com/kmader/baseline-u-net-model-part-1](http://), we attempt to extract bounding boxes information from binary rle-encoded masks."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport cv2\nfrom tqdm import tqdm\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.measure import label, regionprops\nfrom 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'\ntrain_image_dir = os.path.join(ship_dir, 'train_v2')\ntest_image_dir = os.path.join(ship_dir, 'test_v2')\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, all_masks=None):\n    # Take the individual ship masks and create a single mask array for all ships\n    if all_masks is None:\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":{"_uuid":"482b8891d20ac2b8052d77ee58d0f766aba7d674"},"cell_type":"markdown","source":"Let us read the masks:"},{"metadata":{"trusted":true,"_uuid":"24bcb040514e697fd80f03291d322b73146bceda"},"cell_type":"code","source":"masks = pd.read_csv(os.path.join('../input/',\n                                 'train_ship_segmentations_v2.csv'))\nprint(masks.shape[0], 'masks found')\nprint(masks['ImageId'].value_counts().shape[0])\nmasks.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"15feac3041c7f644fa1afc39478abfbab380583d"},"cell_type":"markdown","source":"and keep only those that contain ships. Keep in mind that image files can be repeated many times in the csv file. So a unique operator will give us the unique filenames that contain ships."},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"images_with_ship = masks.ImageId[masks.EncodedPixels.isnull()==False]\nimages_with_ship = np.unique(images_with_ship.values)\nprint('There are ' +str(len(images_with_ship)) + ' image files with masks')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"3385f11ac14a1b32559133e23498984f147a89ef"},"cell_type":"markdown","source":"In order to extract the bounding box we:\n1. Load mask as binary numpy array using Kevin's `masks_as_image`)\n\n2. Label  connected regions of this mask using `skimage.measure.label`\n\n3. Measure morphological properties of these connected regions and keep the bounding box (`skimage.measure.regionprops`). For each connected region a bounding box of the form  (min_row, min_col, max_row, max_col) is returned.  \n\n(*Note: Ships masks touching each other would be considered as one. See Image 00021ddc3.jpg below. This may hurt detection performance but we can find ways to further split them !* )\n\nLet us view some  examples:"},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"9b1ee6524f6eba8bb21921a609dfcfd2fabbf114"},"cell_type":"code","source":"for i in range(10):\n    image = images_with_ship[i]\n\n    fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize = (15, 5))\n    img_0 = cv2.imread(train_image_dir+'/' + image)\n    rle_0 = masks.query('ImageId==\"'+image+'\"')['EncodedPixels']\n    mask_0 = masks_as_image(rle_0)\n    #\n    # \n    lbl_0 = label(mask_0) \n    props = regionprops(lbl_0)\n    img_1 = img_0.copy()\n    print ('Image', image)\n    for prop in props:\n        print('Found bbox', prop.bbox)\n        cv2.rectangle(img_1, (prop.bbox[1], prop.bbox[0]), (prop.bbox[3], prop.bbox[2]), (255, 0, 0), 2)\n\n\n    ax1.imshow(img_0)\n    ax1.set_title('Image')\n    ax2.set_title('Mask')\n    ax3.set_title('Image with derived bounding box')\n    ax2.imshow(mask_0[...,0], cmap='gray')\n    ax3.imshow(img_1)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8b1c568de89f266ab84eaeecb972e644688ff017"},"cell_type":"markdown","source":"Here we calculate the bounding boxes for all `29070` images and save then into a dictionary. "},{"metadata":{"trusted":true,"_uuid":"8b7005e02786cd76b180151927b997d3b7fa62fd"},"cell_type":"code","source":"import gc \nbboxes_dict = {}\ni = 0\ncount_ships = 0\nfor image in tqdm(images_with_ship):\n    img_0 = cv2.imread(train_image_dir+'/' + image)\n    rle_0 = masks.query('ImageId==\"'+image+'\"')['EncodedPixels']\n    mask_0 = masks_as_image(rle_0)\n    \n\n    #\n    # \n    lbl_0 = label(mask_0) \n    props = regionprops(lbl_0)\n    bboxes = []\n    count_ships = count_ships + len(props)\n    for prop in props:\n        bboxes.append(prop.bbox)\n        \n        \n    i = i + 1\n    if i % 500 == 0:\n        gc.collect()    \n\n    bboxes_dict[image] = bboxes.copy()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a58ca4a95fef572afa96ddf9f2f798ee4f7a8f87","collapsed":true},"cell_type":"markdown","source":"Let us plot some bounding boxes right from the dictionary we just created. "},{"metadata":{"trusted":true,"_uuid":"e282e2a52d11fbceae34b8ea6166cf49323b29b3"},"cell_type":"code","source":"dict_images = list(bboxes_dict.keys())\nfor i in range(5):\n    image = dict_images[10+i]\n    fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize = (15, 5))\n    img_0 = cv2.imread(train_image_dir+'/' + image)\n    rle_0 = masks.query('ImageId==\"'+image+'\"')['EncodedPixels']\n    mask_0 = masks_as_image(rle_0)\n    img_1 = img_0.copy()\n    bboxs = bboxes_dict[image]\n    for bbox in bboxs:\n        cv2.rectangle(img_1, (bbox[1], bbox[0]), (bbox[3], bbox[2]), (255, 0, 0), 2)\n\n\n    ax1.imshow(img_0)\n    ax2.imshow(mask_0[...,0], cmap='gray')\n    ax3.imshow(img_1)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"6ec23497a3ac0b5503e9148d53d588c342146535"},"cell_type":"markdown","source":"The final touch.. I export these bounding boxes for everyone to use in a Pandas dataframe form."},{"metadata":{"trusted":true,"_uuid":"8b121c9848b425d6e6433c58dab0553abb6761dc","collapsed":true},"cell_type":"code","source":"bboxes_df = pd.DataFrame([bboxes_dict])\nbboxes_df = bboxes_df.transpose()\nbboxes_df.columns = ['bbox_list']\nbboxes_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"f911cbaef310eb0a649958f64dfeb94fce7035c8"},"cell_type":"code","source":"bboxes_df.to_csv('bbox_dictionary.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"a5c382017f4e914cee5b5ab0644ef0ec881b306f"},"cell_type":"code","source":" ","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}