{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"},{"sourceId":8264904,"sourceType":"datasetVersion","datasetId":4906141}],"dockerImageVersionId":30005,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Import Library","metadata":{}},{"cell_type":"code","source":"import os\nimport re\nimport cv2\nimport ast\nimport random\nimport pandas as pd\nimport numpy as np\n\nfrom PIL import Image\nimport albumentations as A\nfrom collections import namedtuple\nfrom albumentations.pytorch.transforms import ToTensorV2\n\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\n\nfrom pathlib import Path\n","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load sino dataset\n","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\n\n\ndef get_custom_data_dicts(images_dir, labels_dir):\n    dataset_dicts = []\n\n    # Get list of label files\n    label_files = os.listdir(labels_dir)\n\n    for label_file in label_files:\n        # Extract image id from label file name\n        image_id = label_file.split('.')[0]\n        image_path = os.path.join(images_dir, f\"{image_id}.jpg\")\n        label_path = os.path.join(labels_dir, label_file)\n\n        # Load image to get width and height\n        img = cv2.imread(image_path)\n        height, width, _ = img.shape\n\n        # Read label file\n        with open(label_path, 'r') as f:\n            lines = f.readlines()\n\n        objs = []\n        for line in lines:\n            data = line.strip().split()\n            class_0, x_center, y_center, w, h = map(float, data)\n            \n            x_min = int((x_center - w / 2) * width)\n            y_min = int((y_center - h / 2) * height)\n            x_max = int((x_center + w / 2) * width)\n            y_max = int((y_center + h / 2) * height)\n\n            record = {\n                \"image_id\": image_id,\n                \"height\": height,\n                \"width\": width,\n                \"x_min\": x_min,\n                \"y_min\": y_min,\n                \"x_max\": x_max,\n                \"y_max\": y_max,\n            }\n\n            dataset_dicts.append(record)\n    \n    df = pd.DataFrame(dataset_dicts)\n\n    return df\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Path to image and label directories train\ntrain_dir = \"/kaggle/input/sino-nom-dataset/wb_localization_dataset/images/train\"\nimages_dir_train = \"/kaggle/input/sino-nom-dataset/wb_localization_dataset/images/train\"\nlabels_dir_train = \"/kaggle/input/sino-nom-dataset/wb_localization_dataset/labels/train\"\n\ndf = get_custom_data_dicts(images_dir_train, labels_dir_train)\n\ndf.to_excel('excel_data_dict.xlsx')\n\ndf","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Helper functions","metadata":{}},{"cell_type":"code","source":"def draw_rect(img, bboxes, color=(255, 0, 0)):\n    img = img.copy()\n    for bbox in bboxes:\n        bbox = np.array(bbox).astype(int)\n        pt1, pt2 = (bbox[0], bbox[1]), (bbox[2], bbox[3])\n        img = cv2.rectangle(img, pt1, pt2, color, int(max(img.shape[:2]) / 200))\n    return img\n\ndef read_img(img_id):\n    img_path = train_dir + '/' + img_id +'.jpg'\n    img = cv2.imread(str(img_path))\n    return img\n\ndef read_bboxes(img_id):\n    return df.loc[df.image_id == img_id, 'x_min y_min x_max y_max'.split()].values\n\ndef plot_img(img_id, bbox=False):\n    img    = read_img(img_id)\n    if bbox:\n        bboxes = read_bboxes(img_id)\n        img    = draw_rect(img, bboxes)\n    plt.imshow(img);\n    \ndef plot_multiple_img(img_matrix_list, title_list, ncols, nrows=3, main_title=\"\"):\n    fig, myaxes = plt.subplots(figsize=(20, 15), nrows=nrows, ncols=ncols, squeeze=False)\n    fig.suptitle(main_title, fontsize = 30)\n    fig.subplots_adjust(wspace=0.3)\n    fig.subplots_adjust(hspace=0.3)\n    for i, (img, title) in enumerate(zip(img_matrix_list, title_list)):\n        \n        myaxes[i // ncols][i % ncols].imshow(img)\n        myaxes[i // ncols][i % ncols].set_title(title, fontsize=15)\n        myaxes[i // ncols][i % ncols].grid(False)\n        myaxes[i // ncols][i % ncols].set_xticks([])\n        myaxes[i // ncols][i % ncols].set_yticks([])\n\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Bounding Boxes\n\nAugmenting images is easy and straight forward. But things get pretty complex when you start working with bounding boxes. When you transform an image, the coordinates of bounding boxes are also altered. Apply same transformations to your image and bounding box is very sophisticated and prone to errors. Things get even more complex when you start chaining these transformations. \n\nLucky for us, albumentations library supports working with bounding boxes. You should refer [this](https://albumentations.ai/docs/getting_started/bounding_boxes_augmentation/) doc page for more details. \n\nThe most important thing when working with bounding boxes is the **annotations**. There are different annotations formats for bounding boxes. Each format uses its specific representation of bouning boxes coordinates. Albumentations supports four formats: `pascal_voc`, `albumentations`, `coco`, and `yolo`. The image below will help you understand different annotations better.\n\n![](https://albumentations.ai/docs/images/getting_started/augmenting_bboxes/bbox_formats.jpg)\n\n**Note:** Different models (like yolo, faster-rcnn, etc.) expects bboxes in different formats. So, before using any model just make sure that you know the annotation type that it uses. And then format your bboxes accordingly.","metadata":{}},{"cell_type":"markdown","source":"# Fundamental types of augmentation","metadata":{}},{"cell_type":"code","source":"# Define paths\noutput_img_dir = 'output_aug/images'\noutput_label_dir = 'output_aug/label'\noutput_check_dir = 'output_aug/check'\n\n# Ensure output directories exist\nos.makedirs(output_img_dir, exist_ok=True)\nos.makedirs(output_label_dir, exist_ok=True)\nos.makedirs(output_check_dir, exist_ok=True)\n\nimg_list1 = [f'nlvnpf-0137-01-{str(i).zfill(3)}' for i in range(1, 45)]\nimg_list2 = [f'nlvnpf-0140-01-{str(i).zfill(3)}' for i in range(2, 16)]\nimg_list3 = [f'nlvnpf-0174-03-{str(i).zfill(3)}' for i in range(1, 13)]\nimg_list = img_list1 + img_list2 + img_list3\n\nfor img_id in img_list:\n    chosen_img = read_img(img_id)\n    bboxes = read_bboxes(img_id)\n    bbox_params = {'format': 'pascal_voc', 'label_fields': ['labels']}\n\n    albumentation_list = [A.Compose([A.Rotate(p=1, limit=15)], bbox_params=bbox_params),\n                          A.Compose([A.InvertImg(p=1)], bbox_params=bbox_params), \n                          A.Compose([A.Blur(blur_limit=10, p=1)], bbox_params=bbox_params),\n                          A.Compose([A.Downscale()], bbox_params=bbox_params), \n                          A.Compose([A.RandomSizedBBoxSafeCrop(height=1000, width=1000)], bbox_params=bbox_params),\n                          A.Compose([A.Resize(300,300)], bbox_params=bbox_params)]\n\n    titles_list = [\"Rotate\",\n                   \"InvertImg\", \n                   \"Blur\", \n                   \"Downscale\", \n                   \"RamdomSizedBBoxSafeCrop\",\n                   \"Resize\"]\n\n    img_bbox_matrix_list = []\n    anno_img_matrix_list = []\n    anno_bbox_matrix_list = []\n\n    for aug_type in albumentation_list:\n        anno = aug_type(image=chosen_img, bboxes=bboxes, labels=np.ones(len(bboxes)))\n        img  = draw_rect(anno['image'], anno['bboxes'])\n        img_bbox_matrix_list.append(img)\n        anno_img_matrix_list.append(anno['image'])\n        anno_bbox_matrix_list.append(anno['bboxes'])\n\n#     plot_multiple_img(img_bbox_matrix_list, \n#                       titles_list, \n#                       ncols = 3, \n#                       nrows = 2,\n#                       main_title=\"Different Types of Augmentations\")\n\n    \n    for i, img in enumerate(img_bbox_matrix_list):\n        cv2.imwrite(os.path.join(output_check_dir, f'{img_id}_augcheck_{i}.jpg'), img)\n        cv2.imwrite(os.path.join(output_img_dir, f'{img_id}_aug_{i}.jpg'), anno_img_matrix_list[i])\n\n        with open(os.path.join(output_label_dir, f'{img_id}_aug_{i}.txt'), 'w') as label_file:\n            img_height, img_width, _ = img.shape\n            for bbox in anno_bbox_matrix_list[i]:\n                x_min, y_min, x_max, y_max = bbox\n                # Calculate bbox in YOLO format\n                x_center = (x_min + x_max) / 2 / img_width\n                y_center = (y_min + y_max) / 2 / img_height\n                width = (x_max - x_min) / img_width\n                height = (y_max - y_min) / img_height\n                # Write to label file\n                label_file.write(f'0 {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\\n')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Download output folder","metadata":{}},{"cell_type":"code","source":"!zip -r9 augmented_data.zip /kaggle/working/output_aug > /dev/null","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Delete output files if needed","metadata":{}},{"cell_type":"code","source":"!rm -rf /kaggle/working/*","metadata":{"execution":{"iopub.status.busy":"2024-05-09T16:26:52.344087Z","iopub.execute_input":"2024-05-09T16:26:52.344512Z","iopub.status.idle":"2024-05-09T16:26:53.490692Z","shell.execute_reply.started":"2024-05-09T16:26:52.344478Z","shell.execute_reply":"2024-05-09T16:26:53.489148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Advance Augmentations","metadata":{}},{"cell_type":"markdown","source":"In recent times, many new augmentation techniques have been devloped. Some of them are extremely powerful. When applied, they can help your model generalize better and make it more robust. \n\nThis section focus on:\n- CutOut\n- MixUp\n- CutMix\n","metadata":{}},{"cell_type":"markdown","source":"## Cutout Augmentation\n\n**CutOut** is a technique for regularizing CNN models. It works by masking square regions of the image. By randomly masking regions of the image we can prevent the model from falling prey to noice in the data and overfit. \n\n**Note:** Reading research papers is one of the most under rated skill set in Deep Learning community, but to become a good practitioner you must regularly spend time reading research papers. This [cutout paper](https://arxiv.org/pdf/1708.04552) is an easy read, give it a try.\n\nAlbumentations already has a working implementation of CutOut. So, we will use it directly. ","metadata":{}},{"cell_type":"code","source":"from albumentations.augmentations.transforms import Cutout","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_id = 'nlvnpf-0137-01-001' \n\nchosen_img = read_img(img_id)\nbboxes = read_bboxes(img_id)\n\nbbox_params = {'format': 'pascal_voc', 'label_fields': ['labels']}\naugmentation = A.Compose([Cutout(num_holes=5, \n                                 max_h_size=80, \n                                 max_w_size=80, \n                                 fill_value=0, \n                                 p=1),\n                         ], \n                         bbox_params=bbox_params)\n\nimg_matrix_list = [draw_rect(chosen_img, bboxes)]\n\nanno = augmentation(image=chosen_img, bboxes=bboxes, labels=np.ones(len(bboxes)))\nimg  = draw_rect(anno['image'], anno['bboxes'])\nimg_matrix_list.append(img)\n\ntitles_list = [\"Original\", \"CutOut\"]\n\nplot_multiple_img(img_matrix_list, titles_list, ncols = 2, nrows= 1, main_title=\"\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from albumentations.core.transforms_interface import DualTransform\nfrom albumentations.augmentations.bbox_utils import denormalize_bbox, normalize_bbox\n\nclass CustomCutout(DualTransform):\n    def __init__(\n        self,\n        fill_value=0,\n        bbox_removal_threshold=0.50,\n        min_cutout_size=192,\n        max_cutout_size=512,\n        always_apply=False,\n        p=0.5\n    ):\n        \"\"\"\n        Class construstor\n        \n        :param fill_value: Value to be filled in cutout (default is 0 or black color)\n        :param bbox_removal_threshold: Bboxes having content cut by cutout path more than this threshold will be removed\n        :param min_cutout_size: minimum size of cutout (192 x 192)\n        :param max_cutout_size: maximum size of cutout (512 x 512)\n        \"\"\"\n        super(CustomCutout, self).__init__(always_apply, p)  # Initialize parent class\n        self.fill_value = fill_value\n        self.bbox_removal_threshold = bbox_removal_threshold\n        self.min_cutout_size = min_cutout_size\n        self.max_cutout_size = max_cutout_size\n        \n    def _get_cutout_position(self, img_height, img_width, cutout_size):\n        \"\"\"\n        Randomly generates cutout position as a named tuple\n        \n        :param img_height: height of the original image\n        :param img_width: width of the original image\n        :param cutout_size: size of the cutout patch (square)\n        :returns position of cutout patch as a named tuple\n        \"\"\"\n        position = namedtuple('Point', 'x y')\n        return position(\n            np.random.randint(0, img_width - cutout_size + 1),\n            np.random.randint(0, img_height - cutout_size + 1)\n        )\n        \n    def _get_cutout(self, img_height, img_width):\n        \"\"\"\n        Creates a cutout pacth with given fill value and determines the position in the original image\n        \n        :param img_height: height of the original image\n        :param img_width: width of the original image\n        :returns (cutout patch, cutout size, cutout position)\n        \"\"\"\n        cutout_size = np.random.randint(self.min_cutout_size, self.max_cutout_size + 1)\n        cutout_position = self._get_cutout_position(img_height, img_width, cutout_size)\n        return np.full((cutout_size, cutout_size, 3), self.fill_value), cutout_size, cutout_position\n        \n    def apply(self, image, **params):\n        \"\"\"\n        Applies the cutout augmentation on the given image\n        \n        :param image: The image to be augmented\n        :returns augmented image\n        \"\"\"\n        image = image.copy()  # Don't change the original image\n        self.img_height, self.img_width, _ = image.shape\n        cutout_arr, cutout_size, cutout_pos = self._get_cutout(self.img_height, self.img_width)\n        \n        # Set to instance variables to use this later\n        self.image = image\n        self.cutout_pos = cutout_pos\n        self.cutout_size = cutout_size\n        \n        image[cutout_pos.y:cutout_pos.y+cutout_size, cutout_pos.x:cutout_size+cutout_pos.x, :] = cutout_arr\n        return image\n    \n    def apply_to_bbox(self, bbox, **params):\n        \"\"\"\n        Removes the bounding boxes which are covered by the applied cutout\n        \n        :param bbox: A single bounding box coordinates in pascal_voc format\n        :returns transformed bbox's coordinates\n        \"\"\"\n\n        # Denormalize the bbox coordinates\n        bbox = denormalize_bbox(bbox, self.img_height, self.img_width)\n        x_min, y_min, x_max, y_max = tuple(map(int, bbox))\n\n        bbox_size = (x_max - x_min) * (y_max - y_min)  # width * height\n        overlapping_size = np.sum(\n            (self.image[y_min:y_max, x_min:x_max, 0] == self.fill_value) &\n            (self.image[y_min:y_max, x_min:x_max, 1] == self.fill_value) &\n            (self.image[y_min:y_max, x_min:x_max, 2] == self.fill_value)\n        )\n\n        # Remove the bbox if it has more than some threshold of content is inside the cutout patch\n        if overlapping_size / bbox_size > self.bbox_removal_threshold:\n            return normalize_bbox((0, 0, 0, 0), self.img_height, self.img_width)\n\n        return normalize_bbox(bbox, self.img_height, self.img_width)\n\n    def get_transform_init_args_names(self):\n        \"\"\"\n        Fetches the parameter(s) of __init__ method\n        :returns: tuple of parameter(s) of __init__ method\n        \"\"\"\n        return ('fill_value', 'bbox_removal_threshold', 'min_cutout_size', 'max_cutout_size', 'always_apply', 'p')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define paths\noutput_img_dir = 'output_aug/images'\noutput_label_dir = 'output_aug/label'\noutput_check_dir = 'output_aug/check'\n\n# Ensure output directories exist\nos.makedirs(output_img_dir, exist_ok=True)\nos.makedirs(output_label_dir, exist_ok=True)\nos.makedirs(output_check_dir, exist_ok=True)\n\nimg_list1 = [f'nlvnpf-0137-01-{str(i).zfill(3)}' for i in range(1, 45)]\nimg_list2 = [f'nlvnpf-0140-01-{str(i).zfill(3)}' for i in range(2, 16)]\nimg_list3 = [f'nlvnpf-0174-03-{str(i).zfill(3)}' for i in range(1, 13)]\nimg_list = img_list1 + img_list2 + img_list3\n\nfor i, img_id in enumerate(img_list):\n    chosen_img = read_img(img_id)\n    bboxes = read_bboxes(img_id)\n\n    bbox_params = {'format': 'pascal_voc', 'label_fields': ['labels']}\n    augmentation = A.Compose([CustomCutout(p=1),], bbox_params = bbox_params)\n\n    img_matrix_list = []\n    \n    anno = augmentation(image=chosen_img, bboxes=bboxes, labels=np.ones(len(bboxes)))\n    img  = draw_rect(anno['image'], anno['bboxes'])\n    img_matrix_list.append(img)\n\n    titles_list = [f'CutOut Image: Removed bboxes: {len(bboxes)-len(anno[\"bboxes\"])}']\n\n#     plot_multiple_img(img_matrix_list, titles_list, ncols = 1, nrows= 1, main_title=\"\")\n\n    cv2.imwrite(os.path.join(output_check_dir, f'{img_id}_cutoutcheck_{i}.jpg'), img)\n    cv2.imwrite(os.path.join(output_img_dir, f'{img_id}_cutout_{i}.jpg'), anno['image'])\n\n    with open(os.path.join(output_label_dir, f'{img_id}_cutout_{i}.txt'), 'w') as label_file:\n        img_height, img_width, _ = img.shape\n        for bbox in anno['bboxes']:\n            x_min, y_min, x_max, y_max = bbox\n            # Calculate bbox in YOLO format\n            x_center = (x_min + x_max) / 2 / img_width\n            y_center = (y_min + y_max) / 2 / img_height\n            width = (x_max - x_min) / img_width\n            height = (y_max - y_min) / img_height\n            # Write to label file\n            label_file.write(f'0 {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\\n')","metadata":{"execution":{"iopub.status.busy":"2024-05-09T16:27:21.282373Z","iopub.execute_input":"2024-05-09T16:27:21.282833Z","iopub.status.idle":"2024-05-09T16:27:26.177012Z","shell.execute_reply.started":"2024-05-09T16:27:21.282793Z","shell.execute_reply":"2024-05-09T16:27:26.176083Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!zip -r9 augmented_data.zip /kaggle/working/output_aug > /dev/null","metadata":{"execution":{"iopub.status.busy":"2024-05-09T16:27:33.860787Z","iopub.execute_input":"2024-05-09T16:27:33.861185Z","iopub.status.idle":"2024-05-09T16:27:36.286706Z","shell.execute_reply.started":"2024-05-09T16:27:33.861151Z","shell.execute_reply":"2024-05-09T16:27:36.285152Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## CutMix Augmentation\n","metadata":{}},{"cell_type":"code","source":"def rand_bbox(size, lamb):\n    \"\"\" Generate random bounding box \n    Args:\n        - size: [width, breadth] of the bounding box\n        - lamb: (lambda) cut ratio parameter\n    Returns:\n        - Bounding box\n    \"\"\"\n    W = size[0]\n    H = size[1]\n    cut_rat = np.sqrt(1. - lamb)\n    cut_w = np.int(W * cut_rat)\n    cut_h = np.int(H * cut_rat)\n\n    # uniform\n    cx = np.random.randint(W)\n    cy = np.random.randint(H)\n\n    bbx1 = np.clip(cx - cut_w // 2, 0, W)\n    bby1 = np.clip(cy - cut_h // 2, 0, H)\n    bbx2 = np.clip(cx + cut_w // 2, 0, W)\n    bby2 = np.clip(cy + cut_h // 2, 0, H)\n\n    return bbx1, bby1, bbx2, bby2\n\n\ndef generate_cutmix_image(images, bboxes, beta=1.0, th=0.25):\n    \"\"\" Generate a CutMix augmented image from a batch \n    Args:\n        - image_batch: a batch of input images\n        - image_batch_labels: labels corresponding to the image batch\n        - beta: a parameter of Beta distribution.\n    Returns:\n        - CutMix image batch, updated labels\n    \"\"\"\n    # generate mixed sample\n    fill_value = 255\n    lam = np.random.beta(beta, beta)\n    target_a = bboxes[0]\n    target_b = bboxes[1]\n    w, h, c = images[0].shape\n    bbx1, bby1, bbx2, bby2 = rand_bbox((w,h,c), lam)\n    cutmix_image = images[0].copy()\n    cutmix_image[bby1:bby2, bbx1:bbx2, :] = fill_value\n    \n    # bboxes\n    new_bboxes = []\n    for bbox in bboxes[0]:\n        x_min, y_min, x_max, y_max = map(int, bbox)\n    \n        bbox_size = (x_max - x_min) * (y_max - y_min)  # width * height\n        overlapping_size = np.sum(\n            (cutmix_image[y_min:y_max, x_min:x_max, 0] == fill_value) &\n            (cutmix_image[y_min:y_max, x_min:x_max, 1] == fill_value) &\n            (cutmix_image[y_min:y_max, x_min:x_max, 2] == fill_value)\n        )\n\n        # Add the bbox if it has less than some threshold of content is inside the cutout patch\n        if overlapping_size / bbox_size < th:\n            new_bboxes.append(bbox)\n            \n    mask = np.zeros(images[1].shape)\n    mask[bby1:bby2, bbx1:bbx2, :] = 1\n    image2 = images[1]*mask\n    \n    for bbox in bboxes[1]:\n        x_min, y_min, x_max, y_max = map(int, bbox)\n    \n        bbox_size = (x_max - x_min) * (y_max - y_min)  # width * height\n        overlapping_size = np.sum(\n            (image2[y_min:y_max, x_min:x_max, 0] == 0) &\n            (image2[y_min:y_max, x_min:x_max, 1] == 0) &\n            (image2[y_min:y_max, x_min:x_max, 2] == 0)\n        )\n\n        # Add the bbox if it has less than some threshold of content is inside the cutout patch\n        if overlapping_size / bbox_size < th:\n            new_bboxes.append(bbox)\n    \n    cutmix_image[bby1:bby2, bbx1:bbx2, :] = image2[bby1:bby2, bbx1:bbx2, :]\n        \n    return cutmix_image, new_bboxes","metadata":{"execution":{"iopub.status.busy":"2024-05-09T16:27:55.010656Z","iopub.execute_input":"2024-05-09T16:27:55.011127Z","iopub.status.idle":"2024-05-09T16:27:55.041247Z","shell.execute_reply.started":"2024-05-09T16:27:55.011080Z","shell.execute_reply":"2024-05-09T16:27:55.040167Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_ids = ['nlvnpf-0137-01-001', 'nlvnpf-0137-01-003']\nimages = [read_img(img_id)    for img_id in image_ids]\nbboxes = [read_bboxes(img_id) for img_id in image_ids]\n\naug_image, aug_bbox = generate_cutmix_image(images, bboxes)\n\nimages += [aug_image]\nbboxes += [aug_bbox]\n\nimg_matrix_list = []\nfor img, bbox in zip(images, bboxes): \n    img_matrix_list.append(draw_rect(img, bbox))\n    \ntitles_list     = ['Image 1', 'Image 2', 'Augmented Image']\nplot_multiple_img(img_matrix_list, titles_list, ncols = 3, nrows= 1, main_title=\"\")","metadata":{"execution":{"iopub.status.busy":"2024-05-09T16:27:58.239102Z","iopub.execute_input":"2024-05-09T16:27:58.239545Z","iopub.status.idle":"2024-05-09T16:27:58.733941Z","shell.execute_reply.started":"2024-05-09T16:27:58.239494Z","shell.execute_reply":"2024-05-09T16:27:58.732815Z"},"trusted":true},"execution_count":null,"outputs":[]}]}