{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Fixed wheat bounding boxes\n\nThis notebook fixes some bounding boxes in the training set for the **Global Wheat Detection** dataset. This object detection dataset has almost 3500 wheat head images and their corresponding bounding boxes (~150k bounding boxes).","execution_count":null},{"metadata":{"ExecuteTime":{"end_time":"2020-05-10T15:23:13.735111Z","start_time":"2020-05-10T15:23:13.139469Z"},"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import os\nimport cv2\nimport csv\nimport glob\nimport pandas as pd\nimport numpy as np\nimport random\nimport itertools\nfrom collections import Counter\nfrom math import ceil\nimport matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm\nfrom IPython.core.interactiveshell import InteractiveShell\nInteractiveShell.ast_node_interactivity = \"all\"\n%matplotlib inline\n\nimage_folder_path = \"/kaggle/input/global-wheat-detection/train/\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Reading and Loading the boxes (train csv)","execution_count":null},{"metadata":{"ExecuteTime":{"end_time":"2020-05-10T15:23:18.556841Z","start_time":"2020-05-10T15:23:18.425088Z"},"trusted":true},"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/global-wheat-detection/train.csv\")\ntrain","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Changing bounding boxes from [x_min, y_min, width, height] to [x_min, y_min, x_max, y_max]","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"bboxes = np.stack(train['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep=',')))\nfor i, column in enumerate(['x_min', 'y_min', 'width', 'height']):\n    train[column] = bboxes[:,i]\n    \ntrain[\"x_max\"] = train.apply(lambda col: col.x_min + col.width, axis=1)\ntrain[\"y_max\"] = train.apply(lambda col: col.y_min + col.height, axis = 1)\ntrain.drop(columns=['bbox'], inplace=True)\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Add column indicating that the image has wheat heads and add .jpg extension to image_id column","execution_count":null},{"metadata":{"ExecuteTime":{"end_time":"2020-05-10T15:23:55.105905Z","start_time":"2020-05-10T15:23:55.101893Z"},"trusted":true},"cell_type":"code","source":"train[\"class\"] = \"1\"\ntrain[\"image_id\"] = train[\"image_id\"].apply(lambda x: str(x) + \".jpg\").astype(\"str\")\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plotting Images with Bounding Boxes","execution_count":null},{"metadata":{"ExecuteTime":{"end_time":"2020-05-10T15:05:03.610126Z","start_time":"2020-05-10T15:05:03.599127Z"},"trusted":true},"cell_type":"code","source":"# all util functions\ndef draw_rect(img, bboxes, color=(255,0,0)):\n    # get an image and return it with all rectangles from bboxes (a 2d-array)\n    img = img.copy()\n    for bbox in bboxes:\n        pt1, pt2 = (bbox[0], bbox[1]), (bbox[2], bbox[3])\n        pt1 = int(pt1[0]), int(pt1[1])\n        pt2 = int(pt2[0]), int(pt2[1])\n        img = cv2.rectangle(img.copy(), pt1, pt2, color, int(max(img.shape[:2]) / 500))\n    return img\n\ndef plot_bboxes(img, bboxes, size=(12, 10)):\n    if isinstance(img, str):\n        image_file_path = os.path.join(image_folder_path, img)\n        img = cv2.imread(image_file_path)[:,:,::-1]\n\n    # get an image and plot it with all bounding boxes\n    img2 = draw_rect(img.copy(), bboxes)\n    plt.figure(figsize=size)\n    plt.imshow(img2)\n\n    \ndef img_check(image_name_or_index, large=False):\n    # find image by image_id or index, plot it with bboxes\n    # and returns the image, bounding boxes array and image_id\n    if isinstance(image_name_or_index, int):\n        unique_images = train.image_id.unique()\n        img_file = unique_images[image_name_or_index]\n    else:\n        img_file = image_name_or_index\n    \n    bboxes = train.loc[train.image_id == img_file, ['x_min', 'y_min', 'x_max', 'y_max']].values\n    image_file_path = os.path.join(image_folder_path, img_file)\n    img = cv2.imread(image_file_path)[:,:,::-1]\n    print(img_file)\n    size = (16, 16) if large else (12, 10)\n    plot_bboxes(img, bboxes, size)\n    return img, bboxes, img_file\n\n\ndef plot_image(image_id, size=(12, 12)):\n    # just find image by id and plot with bounding boxes\n    img_file = image_id\n    bboxes = train.loc[train.image_id == img_file, ['x_min', 'y_min', 'x_max', 'y_max']].values\n    image_file_path = os.path.join(image_folder_path, img_file)\n    img = cv2.imread(image_file_path)[:,:,::-1]\n    plot_bboxes(img, bboxes, size)\n    \n\ndef find_bbox_index(image_id, bbox):\n    # return the index of the bounding box given the image_id and bounding box coords\n    f = train[(train.image_id == image_id) & (train.x_min == bbox[0]) & (train.y_min == bbox[1]) \n              & (train.x_max == bbox[2])  & (train.y_max == bbox[3])]\n    return f.index\n\ndef plot_bbox_by_number(num):\n    plot_bboxes(ximg, xboxes[num:num+1])\n    print(\"Bounding box:\", xboxes[num])\n    print(\"Train frame index:\", find_bbox_index(ximg_id, xboxes[num]))\n    \n    \ndef add_new_bbox(df, image_id, bbox, index=0):\n    # return a frame with a new bounding box for the given image_id\n    # index=0 will add the new box to last index + 1\n    tmp = df[df.image_id == image_id]\n    serie = tmp.iloc[0].copy()\n    serie[['x_min', 'y_min', 'x_max', 'y_max']] = bbox\n    serie['height'] = serie.y_max - serie.y_min\n    serie['width'] = serie.x_max - serie.x_min\n    serie.name = index if index != 0 else df.index.max() + 1\n    return df.append(serie, verify_integrity=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 41c0123cc.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"ximg, xboxes, ximg_id = img_check('41c0123cc.jpg')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bbox_by_number(6)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, np.array([[0, 15, 140, 80], [290, 270, 370, 420]]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# fix image bounding boxes\nprint(train.shape)\ntrain.drop(173, inplace=True)\ntrain = add_new_bbox(train, ximg_id, [0, 15, 140, 80])\ntrain = add_new_bbox(train, ximg_id, [290, 270, 370, 420])\nprint(train.shape)\n#show fixed image\nximg, xboxes, ximg_id = img_check('41c0123cc.jpg')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## e46378032.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"ximg, xboxes, ximg_id = img_check(6)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, np.array([[240, 0, 300, 80]]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# fix image bounding boxes\nprint(train.shape)\ntrain = add_new_bbox(train, ximg_id, [240, 0, 300, 80])\nprint(train.shape)\n#show fixed image\nximg, xboxes, ximg_id = img_check('e46378032.jpg')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## a22cdd5eb.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"ximg, xboxes, ximg_id = img_check(33)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.drop(1021, inplace=True)\nximg, xboxes, ximg_id = img_check('a22cdd5eb.jpg')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## e99cca2a3.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"ximg, xboxes, ximg_id = img_check(43)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# the first bounding box is totally wrong\n# its also missing two bounding boxes\nprint(xboxes[0])\nfind_bbox_index(ximg_id, [714, 559, 865, 806])\nplot_bboxes(ximg, xboxes[0:1])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, np.array([[730, 560, 810, 710],\n                            [810, 715, 860, 798]]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.drop(1274, inplace=True)\ntrain = add_new_bbox(train, ximg_id, [730, 560, 810, 710])\ntrain = add_new_bbox(train, ximg_id, [810, 715, 860, 798])\nximg, xboxes, ximg_id = img_check(ximg_id, large=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## a1321ca95.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"ximg, xboxes, ximg_id = img_check('a1321ca95.jpg', large=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, xboxes[[13, 23]])\nfind_bbox_index(ximg_id, xboxes[13])\nfind_bbox_index(ximg_id, xboxes[23])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.drop(2169, inplace=True)\ntrain.drop(2159, inplace=True)\nximg, xboxes, ximg_id = img_check('a1321ca95.jpg', large=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, np.array([[300, 450, 510, 620], [140, 610, 400, 820],\n                            [95, 520, 350, 615]]), size=(16, 16))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = add_new_bbox(train, ximg_id, [300, 450, 510, 620])\ntrain = add_new_bbox(train, ximg_id, [140, 610, 400, 820])\ntrain = add_new_bbox(train, ximg_id, [95, 520, 350, 615])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_image('a1321ca95.jpg')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 9a30dd802.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"ximg, xboxes, xid = img_check('9a30dd802.jpg')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, xboxes[11:12])\nfind_bbox_index(xid, [3, 301, 420, 671])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# fix the bounding box and plot the image\ntrain.loc[52868, 'y_min'] = 450\ntrain.loc[52868, 'x_max'] = 200\ntrain.loc[52868, 'y_max'] = 640\nplot_image('9a30dd802.jpg')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## d067ac2b1.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"ximg, xboxes, xid = img_check('d067ac2b1.jpg')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, xboxes[37:39])\nfind_bbox_index(xid, xboxes[37])\nfind_bbox_index(xid, xboxes[38]) # resize","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_bboxes(ximg, [[620, 5, 900, 135], [580, 130, 720, 206]], size=(16, 16))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[121634, 'y_min'] = 300\ntrain.loc[121634, 'x_min'] = 700\ntrain.drop(121633, inplace=True)\ntrain = add_new_bbox(train, 'd067ac2b1.jpg', [620, 5, 900, 135])\ntrain = add_new_bbox(train, 'd067ac2b1.jpg', [580, 130, 720, 206])\nplot_image('d067ac2b1.jpg', size=(14, 14))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2840176b4.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"imgl = np.unique(train.image_id.values).tolist()\nprint(imgl[503]); plot_image(imgl[503], size=(12,12))\ntrain.drop(73348, inplace=True) # box at 0, 400","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 284a79f05.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"print(imgl[504]); plot_image(imgl[504], size=(12,12))\ntrain.drop(41429, inplace=True) # box at 0,0","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 29b48c0a7.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train = add_new_bbox(train, imgl[522], [616, 42, 800, 162])\nprint(imgl[522]); plot_image(imgl[522], size=(12,12))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 29f09fa58.jpg","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train = add_new_bbox(train, imgl[525], [978, 930, 1022, 1018])\nprint(imgl[525]); plot_image(imgl[525], size=(12,12))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Finding wrong boxes by height","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train.height.hist(bins=30)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"top_height = train[train.height > 300].sort_values(by='height', ascending=False)\nxids = top_height.image_id.values\nxboxes = top_height[['x_min', 'y_min', 'x_max', 'y_max']].values\ntop_height","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 0; plot_bboxes(xids[i], xboxes[i:i+1], size=(10,8))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 1; plot_bboxes(xids[i], xboxes[i:i+1], size=(10,8))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# remove wrong bounding boxes\nfor i in [0, 1]:\n    idx = find_bbox_index(xids[i], xboxes[i])\n    train.drop(idx, inplace=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Finding wrong boxes by width","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"top_width = train[train.width > 300].sort_values(by='width', ascending=False)\nxids = top_width.image_id.values\nxboxes = top_width[['x_min', 'y_min', 'x_max', 'y_max']].values\ntop_width","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 0; plot_bboxes(xids[i], xboxes[i:i+1], size=(10,8))\nfind_bbox_index(xids[i], xboxes[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[3687, 'x_max'] = 132\ntrain.loc[3687, 'y_max'] = 215\nplot_image(xids[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 7; plot_bboxes(xids[i], xboxes[i:i+1], size=(14,12))\nfind_bbox_index(xids[i], xboxes[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[147552, 'x_min'] = 408\nplot_image(xids[7], size=(14, 14))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 14; plot_bboxes(xids[i], xboxes[i:i+1], size=(14,12))\nfind_bbox_index(xids[i], xboxes[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[4412, 'x_min'] = 538\nplot_image(xids[i], size=(14, 14))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i = 32; plot_bboxes(xids[i], xboxes[i:i+1], size=(14,12))\nfind_bbox_index(xids[i], xboxes[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[86917, 'y_max'] = 455\ntrain.drop(86913, inplace=True)\ntrain.drop(86922, inplace=True)\ntrain = add_new_bbox(train, 'd561f23d2.jpg', [376, 422, 488, 490])\nplot_image('d561f23d2.jpg')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Filter boxes by median size ratio","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train.height = train.y_max - train.y_min\ntrain.width = train.x_max - train.x_min\ntrain['bbox_size'] = train.height * train.width\ntrain['median_bbox_size'] = train.groupby('image_id')['bbox_size'].transform('median')\ntrain['median_ratio_diff'] = (train.bbox_size - train.median_bbox_size) / train.median_bbox_size\n\nsort_train = train.sort_values(by='median_ratio_diff', ascending=False)\nxboxes = sort_train[['x_min', 'y_min', 'x_max', 'y_max']].values\nxids = sort_train['image_id'].values\nsort_train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i=0\nplot_image(xids[i], size=(12, 12))\nfind_bbox_index(xids[i], xboxes[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[117344, 'x_min'] = 468\ntrain.loc[117344, 'y_max'] = 356\nplot_image(xids[i], size=(10, 10))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"i=1\nplot_image(xids[i], size=(12, 12))\nfind_bbox_index(xids[i], xboxes[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[147504, 'x_max'] = 420\ntrain.loc[147504, 'y_max'] = 155\nplot_image(xids[i], size=(16, 16))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# two bounding boxes without visible wheat heads\ni=-1\nplot_image(xids[i], size=(14, 14))\nfind_bbox_index(xids[i], [483,  31, 534,  89])\nfind_bbox_index(xids[i], [563,  41, 608,  85])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.drop(126792, inplace=True)\ntrain.drop(126793, inplace=True)\nplot_image(xids[i], size=(14, 14))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Filter very small bounding boxes","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train['smallest_dim'] = train.apply(lambda x: min(x['width'], x['height']), axis=1)\nsort_train = train.sort_values(by='smallest_dim', ascending=True)\nsort_train.head()\nxboxes = sort_train[['x_min', 'y_min', 'x_max', 'y_max']].values\nxids = sort_train['image_id'].values","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Example of \"click-error\" box (top right corner)\ni = 80\nprint(sort_train.iloc[i])\nplot_bboxes(xids[i], [xboxes[i]], size=(14, 14))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Filter every bounding box where the height or width is less than 9px\ntrain = train[train.smallest_dim >= 9]\ntrain.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Save new train csv file","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# recalculate the height and width of bounding boxes\ntrain.height = train.y_max - train.y_min\ntrain.width = train.x_max - train.x_min\n# remove dummy columns\ntrain.drop(['bbox_size', 'median_bbox_size', 'median_ratio_diff', 'smallest_dim'], axis=1, inplace=True)\n# save to new csv file (note: we keep the same original index)\ntrain.to_csv('gwd_train_fixed', index=False)\ntrain","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":4}