{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Divide images into 4 pieces & Merge randomly\n\nInspired by [@peter](https://www.kaggle.com/c/global-wheat-detection/discussion/149805)'s awesome article, I generated some images which is made by spliting & randomly merging train_input_images. ","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        if 'jpg' in filename:\n            continue\n        print(os.path.join(dirname, filename))\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import gc\nimport os\nimport time\nimport random\nimport collections\nimport uuid\n\nfrom glob import glob\nfrom datetime import datetime\n\nimport cv2\nimport torch\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nSEED = 42\n\ndef seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\n\nseed_everything(SEED)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"This code is from [@shonenkov/training-efficientdet](https://www.kaggle.com/shonenkov/training-efficientdet/data)","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"df = pd.read_csv('../input/global-wheat-detection/train.csv')\n\nbboxs = np.stack(df['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep=',')))\nfor i, column in enumerate(['x', 'y', 'w', 'h']):\n    df[column] = bboxs[:,i]\ndf.drop(columns=['bbox'], inplace=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Augmentation\n1. divide an image into 4 pieces(puzzles) with bbox\n2. make pool of puzzles of all original images (in train_data)\n3. sample 4 pieces & merge (repeat k-times)\n4. save as a dataframe & jpg images","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def adjust_bbox_in_image(\n    i_w,\n    i_h,\n    bbox,\n    min_bbox_size=20):\n    \"\"\"crop bbox if it cover the edge of the cropped image\"\"\"\n    \n    x, y, w, h = bbox\n    if x < 0:\n        w += x\n        x = 0\n    if y < 0:\n        h += y\n        y = 0\n    if i_w < x+w:\n        w -= (x+w-i_w)\n    if i_h < y+h:\n        h -= (y+h-i_h)\n    \n    on_border = x < 3 or y < 3 or i_w-3 < (x+w) or i_h-3 < (y+h)\n    under_min_size = w < min_bbox_size or h < min_bbox_size\n    if on_border and under_min_size:\n        return None\n    \n    return (x, y, w, h)\n\n\ndef make_puzzles(\n    image_id,\n    bboxes,\n    min_bbox_size=20,\n    image_root='/kaggle/input/global-wheat-detection/train/'):\n    \"\"\"divide given image into 4 pieces with bboxes\"\"\"\n\n    img_path = os.path.join(image_root, '{}.jpg'.format(image_id))\n    \n    image = cv2.imread(img_path)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    \n    row, col, _ = image.shape\n    \n    y_div = int(row/2)\n    x_div = int(col/2)\n    lt = image[:y_div, :x_div] # left top\n    rt = image[:y_div, x_div:] # right top\n    lb = image[y_div:, :x_div] # left bottom\n    rb = image[y_div:, x_div:] # right bottom\n    \n    lt_bboxes, rt_bboxes, lb_bboxes, rb_bboxes = [], [], [], []\n    \n    for bbox in bboxes:\n        x, y, w, h = bbox\n        \n        # Quandrant-2\n        lt_x, lt_y = x, y\n        if lt_y < y_div and lt_x < x_div:\n            _bbox = adjust_bbox_in_image(col/2, row/2, bbox, min_bbox_size=min_bbox_size)\n            if _bbox:\n                lt_bboxes.append(_bbox)\n        \n        # Quandrant-1\n        rt_x, rt_y = x+w-1, y\n        if rt_y < y_div and x_div <= rt_x:\n            _bbox = (bbox[0]-col/2, bbox[1], bbox[2], bbox[3])\n            _bbox = adjust_bbox_in_image(col/2, row/2, _bbox, min_bbox_size=min_bbox_size)\n            if _bbox:\n                rt_bboxes.append(_bbox)\n            \n        # Quandrant-3\n        lb_x, lb_y = x, y+h-1\n        if y_div <= lb_y and lb_x < x_div:\n            _bbox = (bbox[0], bbox[1]-row/2, bbox[2], bbox[3])\n            _bbox = adjust_bbox_in_image(col/2, row/2, _bbox, min_bbox_size=min_bbox_size)\n            if _bbox:\n                lb_bboxes.append(_bbox)\n        \n        # Quandrant-4\n        rb_x, rb_y = x+w-1, y+h-1\n        if y_div <= rb_y and x_div <= rb_x:\n            _bbox = (bbox[0]-col/2, bbox[1]-row/2, bbox[2], bbox[3])\n            _bbox = adjust_bbox_in_image(col/2, row/2, _bbox, min_bbox_size=min_bbox_size)\n            if _bbox:\n                rb_bboxes.append(_bbox)\n    \n    puzzle_bbox_pairs = [\n        (lt, lt_bboxes),\n        (rt, rt_bboxes),\n        (lb, lb_bboxes),\n        (rb, rb_bboxes)\n    ]\n    \n    return puzzle_bbox_pairs\n\n\ndef merge_random_4_puzzles(puzzle_bbox_pairs):\n    lt_img, lt_bboxes = puzzle_bbox_pairs[0]\n    rt_img, rt_bboxes = puzzle_bbox_pairs[1]\n    lb_img, lb_bboxes = puzzle_bbox_pairs[2]\n    rb_img, rb_bboxes = puzzle_bbox_pairs[3]\n    \n    row, col, ch = lt_img.shape\n    x_div = col\n    y_div = row\n    \n    empty_img = np.zeros((row*2, col*2, ch), np.uint8)\n    \n    empty_img[:y_div,:x_div,:] = lt_img\n    empty_img[:y_div,x_div:,:] = rt_img\n    empty_img[y_div:,:x_div,:] = lb_img\n    empty_img[y_div:,x_div:,:] = rb_img\n    \n    _lt_bboxes = lt_bboxes[:]\n    _rt_bboxes = rt_bboxes[:]\n    for i, bbox in enumerate(_rt_bboxes):\n        x, y, w, h = bbox\n        _rt_bboxes[i] = (x+x_div, y, w, h)\n    \n    _lb_bboxes = lb_bboxes[:]\n    for i, bbox in enumerate(_lb_bboxes):\n        x, y, w, h = bbox\n        _lb_bboxes[i] = (x, y+y_div, w, h)\n    \n    _rb_bboxes = rb_bboxes[:]\n    for i, bbox in enumerate(_rb_bboxes):\n        x, y, w, h = bbox\n        _rb_bboxes[i] = (x+x_div, y+y_div, w, h)\n        \n    merged_bbox = _lt_bboxes + _rt_bboxes + _lb_bboxes + _rb_bboxes\n    \n    return (empty_img, merged_bbox)\n    \n\ndef visualize_4_image_bbox(puzzle_bbox_pairs):\n    fig, ax = plt.subplots(2, 2, figsize=(12, 12))\n    ax = ax.flatten()\n\n    labels = ['left top', 'right top', 'left bot', 'right bot']\n\n    for i in range(4):\n        image, bboxes = puzzle_bbox_pairs[i]\n        for row in bboxes:\n            x, y, w, h = (int(n) for n in row)\n            cv2.rectangle(image,\n                          (x, y),\n                          (x+w, y+h),\n                          (220, 0, 0), 3)\n        ax[i].set_axis_off()\n        ax[i].imshow(image)\n        ax[i].set_title(labels[i], color='yellow')\n\n        \ndef visualize_image_bbox(image, bboxes):\n    fig, ax = plt.subplots(1, 1, figsize=(12, 12))\n\n    for row in bboxes:\n        x, y, w, h = (int(n) for n in row)\n        cv2.rectangle(image,\n                      (x, y),\n                      (x+w, y+h),\n                      (220, 0, 0), 3)\n    ax.imshow(image)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"## Make puzzle pool","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from tqdm import tqdm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pool = []\nimage_ids = set(df.image_id.values)\n\nfor i, image_id in tqdm(enumerate(image_ids)):\n    \n    filtered = df[df['image_id'] == image_id]\n    bboxes = filtered[['x', 'y', 'w', 'h']].values\n    \n    puzzles = make_puzzles(image_id, bboxes, min_bbox_size=20)\n    \n    pool += puzzles","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Generate Augmented k-Images","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"k = 1000","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"AugData = collections.namedtuple('AugData', 'image_id,width,height,source,x,y,w,h')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"aug_data = []\n\nos.makedirs('./merged_puzzles', exist_ok=True)\n\nfor i in tqdm(range(k)):\n    random.shuffle(pool)\n    a = ([bbox for img, bbox in pool[:4]])\n    merged_image, merged_bboxes = merge_random_4_puzzles(pool[:4])\n    ih, iw, ch = merged_image.shape\n    image_id = str(uuid.uuid4())\n    for bbox in merged_bboxes:\n        x, y, w, h = bbox\n        aug_data.append(AugData(image_id=image_id, width=iw, height=ih, source='aug', x=x, y=y, w=w, h=h))\n    merged_image = cv2.cvtColor(merged_image, cv2.COLOR_RGB2BGR)\n    cv2.imwrite('./merged_puzzles/{}.jpg'.format(image_id), merged_image)        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"aug_data[:10]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"aug_df = pd.DataFrame(data=aug_data)\naug_df.to_csv('merged_puzzles.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}