{"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_minor":4,"nbformat":4,"cells":[{"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":"8d84f5d2fe3e7ed64cc7c6bffde94115a117e373"}},{"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)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-10-28T08:08:33.577600Z","iopub.execute_input":"2021-10-28T08:08:33.577948Z","iopub.status.idle":"2021-10-28T08:08:33.598472Z","shell.execute_reply.started":"2021-10-28T08:08:33.577891Z","shell.execute_reply":"2021-10-28T08:08:33.597583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let us read the masks:","metadata":{"_uuid":"482b8891d20ac2b8052d77ee58d0f766aba7d674"}},{"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()","metadata":{"_uuid":"24bcb040514e697fd80f03291d322b73146bceda","execution":{"iopub.status.busy":"2021-10-28T08:08:37.755437Z","iopub.execute_input":"2021-10-28T08:08:37.755782Z","iopub.status.idle":"2021-10-28T08:08:38.760563Z","shell.execute_reply.started":"2021-10-28T08:08:37.755724Z","shell.execute_reply":"2021-10-28T08:08:38.759609Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{"_uuid":"15feac3041c7f644fa1afc39478abfbab380583d"}},{"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')","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.status.busy":"2021-10-28T08:08:42.344702Z","iopub.execute_input":"2021-10-28T08:08:42.345028Z","iopub.status.idle":"2021-10-28T08:08:42.473116Z","shell.execute_reply.started":"2021-10-28T08:08:42.344978Z","shell.execute_reply":"2021-10-28T08:08:42.472229Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{"_uuid":"3385f11ac14a1b32559133e23498984f147a89ef"}},{"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()","metadata":{"_uuid":"9b1ee6524f6eba8bb21921a609dfcfd2fabbf114","execution":{"iopub.status.busy":"2021-10-28T08:08:49.861094Z","iopub.execute_input":"2021-10-28T08:08:49.861397Z","iopub.status.idle":"2021-10-28T08:08:57.714891Z","shell.execute_reply.started":"2021-10-28T08:08:49.861349Z","shell.execute_reply":"2021-10-28T08:08:57.714039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we calculate the bounding boxes for all `29070` images and save then into a dictionary. ","metadata":{"_uuid":"8b1c568de89f266ab84eaeecb972e644688ff017"}},{"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()","metadata":{"_uuid":"8b7005e02786cd76b180151927b997d3b7fa62fd","execution":{"iopub.status.busy":"2021-10-28T08:09:03.802152Z","iopub.execute_input":"2021-10-28T08:09:03.802524Z","iopub.status.idle":"2021-10-28T08:43:32.824804Z","shell.execute_reply.started":"2021-10-28T08:09:03.802459Z","shell.execute_reply":"2021-10-28T08:43:32.823868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let us plot some bounding boxes right from the dictionary we just created. ","metadata":{"_uuid":"a58ca4a95fef572afa96ddf9f2f798ee4f7a8f87","trusted":true}},{"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()","metadata":{"_uuid":"e282e2a52d11fbceae34b8ea6166cf49323b29b3","execution":{"iopub.status.busy":"2021-10-28T08:51:36.292524Z","iopub.execute_input":"2021-10-28T08:51:36.292963Z","iopub.status.idle":"2021-10-28T08:51:40.044156Z","shell.execute_reply.started":"2021-10-28T08:51:36.292895Z","shell.execute_reply":"2021-10-28T08:51:40.043467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The final touch.. I export these bounding boxes for everyone to use in a Pandas dataframe form.","metadata":{"_uuid":"6ec23497a3ac0b5503e9148d53d588c342146535"}},{"cell_type":"code","source":"bboxes_df = pd.DataFrame([bboxes_dict])\nbboxes_df = bboxes_df.transpose()\nbboxes_df.columns = ['bbox_list']\nbboxes_df.head()","metadata":{"_uuid":"8b121c9848b425d6e6433c58dab0553abb6761dc","execution":{"iopub.status.busy":"2021-10-28T08:51:58.539275Z","iopub.execute_input":"2021-10-28T08:51:58.539781Z","iopub.status.idle":"2021-10-28T08:52:02.243781Z","shell.execute_reply.started":"2021-10-28T08:51:58.539732Z","shell.execute_reply":"2021-10-28T08:52:02.242474Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"bboxes_df.to_csv('bbox_dictionary.csv')","metadata":{"_uuid":"f911cbaef310eb0a649958f64dfeb94fce7035c8","execution":{"iopub.status.busy":"2021-10-28T08:52:08.320563Z","iopub.execute_input":"2021-10-28T08:52:08.321029Z","iopub.status.idle":"2021-10-28T08:52:08.834624Z","shell.execute_reply.started":"2021-10-28T08:52:08.320978Z","shell.execute_reply":"2021-10-28T08:52:08.833473Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":" ","metadata":{"_uuid":"a5c382017f4e914cee5b5ab0644ef0ec881b306f","collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]}]}