{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Bibliotecas"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"%matplotlib inline\nimport 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\n\n#Descobrir autilidade\nfrom tqdm.notebook import tqdm\nfrom IPython.core.interactiveshell import InteractiveShell\nInteractiveShell.ast_node_interactivity = \"all\"\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Funções úteis"},{"metadata":{"trusted":true},"cell_type":"code","source":"def skip_csv_header(file):\n    has_header = csv.Sniffer().has_header(file.read(1024))\n    file.seek(0)\n    if has_header:\n        next(file)\n\n\ndef total_image_list(image_folder_path):\n    total_img_list = [os.path.basename(img_path_name) for img_path_name in glob.glob(os.path.join(image_folder_path, \"*.jpg\"))]\n    return total_img_list\n\ndef draw_rect(img, bboxes, color=None):\n    img = img.copy()\n    bboxes = bboxes[:, :4]\n    bboxes = bboxes.reshape(-1, 4)\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]) / 200))\n    return img\n\ndef plot_multiple_img(img_matrix_list, title_list, ncols, main_title=\"\"):\n    fig, myaxes = plt.subplots(figsize=(20, 15), nrows=ceil(len(img_matrix_list) / ncols), 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        myaxes[i // ncols][i % ncols].imshow(img)\n        myaxes[i // ncols][i % ncols].set_title(title, fontsize=15)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Neste kernel, apresento algumas funções utilitárias para fazer uma verificação de sanidade em imagens, bem como algumas funções que você pode reutilizar para projetos futuros quando quiser plotar várias imagens em uma grade. Uma prévia de como um gráfico de caixa delimitadora múltipla é assim:\n\n![](https://i.ibb.co/9GXMpWT/img.png)"},{"metadata":{},"cell_type":"markdown","source":"# 1.Ler e carregar o conjunto de dados"},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/global-wheat-detection/train.csv\")  \nimage_folder_path = \"/kaggle/input/global-wheat-detection/train/\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1.1. Bounding boxes"},{"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)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Obeservar dados \ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1.2. Verificação de alcance nas coordenadas das bounding boxes\n\nVerificação de intervalo na coordenada da caixa delimitadora Além disso, devido aos problemas internos de flutuação do python, pode haver valores estranhos como valores negativos ou que somam mais de 1024 em x_max, y_max. Precisamos ter cuidado aqui.\n\nEste é um problema sério que pode ocorrer quando você normaliza a caixa delimitadora, pode exceder 1 e isso causará um erro, especialmente se você decidir aumentar as imagens também."},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"train[train[\"x_max\"] > 1024]\ntrain[train[\"y_max\"] > 1024]\ntrain[train[\"x_min\"] < 0]\ntrain[train[\"y_min\"] < 0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A única razão pela qual, por exemplo, a linha 31785 tem x_max mais de 1024, é devido à rotulagem do conjunto de dados original. Vejamos as respectivas linhas problemáticas. Por exemplo, na linha 31785, o x_min fornecido é 873,200012, e quando você adiciona isso à largura de 150,800003, fornece 1024,000015, que já excede o tamanho da imagem. Então você tem que arredondar para baixo. E, tanto quanto eu sinto, as caixas delimitadoras, quando desnormalizadas, devem ser em números inteiros. Mas esta é apenas a minha opinião. Vamos mudar esses valores problemáticos para 1024"},{"metadata":{"trusted":true},"cell_type":"code","source":"x_max = np.array(train[\"x_max\"].values.tolist())\ny_max = np.array(train[\"y_max\"].values.tolist())\ntrain[\"x_max\"] = np.where(x_max > 1024, 1024, x_max).tolist()\ntrain[\"y_max\"] = np.where(y_max > 1024, 1024, y_max).tolist()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Podemos deletar colunas de largura e altura porque não precisamos delas, pode ser facilmente retirado das próprias imagens."},{"metadata":{"trusted":true},"cell_type":"code","source":"del train[\"width\"]\ndel train[\"height\"]\ndel train[\"source\"]\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"class\"] = \"1\"\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2. Verifique se as extensões de imagem são todas jpg\n\nPrimeiro, verificamos se todas as imagens da pasta train estão no formato .jpg. É melhor verificar porque se houver uma mistura de tipos de imagem, podemos enfrentar problemas mais tarde."},{"metadata":{"trusted":true},"cell_type":"code","source":"def check_file_type(image_folder_path):\n    extension_type = []\n    file_list = os.listdir(image_folder_path)\n    \n    for file in file_list:\n        extension_type.append(file.rsplit(\".\", 1)[1].lower())\n    print(Counter(extension_type).keys())\n    print(Counter(extension_type).values())\n    \ncheck_file_type(image_folder_path)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Bom, parece que todas as nossas imagens na pasta estão no formato .jpg. Em seguida, é melhor anexar .jpg atrás de todo o image_id no dataframe. Isso nos tornará mais fácil manipular os dados posteriormente."},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"image_id\"] = train[\"image_id\"].apply(lambda x: str(x) + \".jpg\")\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"image_id\"] = train[\"image_id\"].astype(\"str\")\ntrain.to_csv(\"wheat.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3. Verifique se há imagens corrompidas e se todas as imagens são 1.024 por 1.024"},{"metadata":{"trusted":true},"cell_type":"code","source":"def check_image_size(image_folder_path):\n    total_img_list = glob.glob(os.path.join(image_folder_path,\"*\"))\n    counter = 0\n    for image in tqdm(total_img_list, desc = \"Checking in progress\"):\n        try:\n            img = cv2.imread(image)\n            height, width = img.shape[1], img.shape[0]\n            if not (height == 1024 and width == 1024):\n                counter = counter + 1\n        except:\n            print(\"This {} is problematic.\".format(image))\n    return counter ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"check_image_size(image_folder_path)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Ótimo, na verdade todas as nossas imagens têm tamanho de 1024 x 1024. E o bom é que esse código também nos ajuda a verificar se há imagens corrompidas, portanto, se houver uma imagem corrompida, com certeza irá mostrar que o contador é diferente de zero. E a partir daí você pode verificar qual imagem está causando o problema."},{"metadata":{},"cell_type":"markdown","source":"# 4. Verificação de sanidade entre o csv do trem e as imagens do treino \nWe will write a function to check if the number of unique image_ids match the number of unique images in the folder."},{"metadata":{"trusted":true},"cell_type":"code","source":"wheat = pd.read_csv(\"wheat.csv\") \nimage_folder_path = \"/kaggle/input/global-wheat-detection/train/\"\nimage_annotation_file = \"wheat.csv\"\nwheat.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def sanity_tally(image_folder_path, image_annotation_file):\n    img_dict = {}\n    with open(image_annotation_file, \"r\") as file:\n        skip_csv_header(file)\n        for row in file:\n            try:\n                image_name, x_min, y_min, x_max, y_max, class_idx = row.split(\",\")\n                if image_name not in img_dict:\n                    img_dict[image_name] = list()\n                img_dict[image_name].append(\n                    [float(x_min), float(y_min), float(x_max), float(y_max), int(class_idx)]\n                )\n            except ValueError:\n                print(\"Could not convert float to string, likely that your data has empty values.\")\n        \n    img_annotation_list = [*img_dict]\n    total_img_list = total_image_list(image_folder_path)\n    if set(img_annotation_list) == set(total_img_list):\n        print(\"Sanity Check Status: True\")\n    else:\n        print(\"Sanity Check Status: Failed. \\nThe elements in wheat/train.csv but not in the train image folder is {}. \\nThe elements in train image folder but not in wheat/train.csv is {}\".format(\n                set(img_annotation_list) - set(total_img_list), set(total_img_list) - set(img_annotation_list)))\n        return list(set(img_annotation_list) - set(total_img_list)), list(set(total_img_list) - set(img_annotation_list))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"set_diff1, set_diff2 = sanity_tally(image_folder_path, image_annotation_file = image_annotation_file)\n\nprint(\"There are {} images without annotations in the train/wheat.csv\".format(len(set_diff2)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Como podemos ver acima, existem 49 imagens sem anotações de caixa delimitadora porque elas não têm trigos na imagem e, portanto, não aparecem no train.csv. Pode ser uma ideia colocar essas 49 imagens dentro do train.csv e rotulá-las como 0."},{"metadata":{},"cell_type":"markdown","source":"# 5. Plotando várias imagens\nHere we define a nice function that is useful not only for this competition, but for similar project as well. Note that we used our utility function here to plot them. One can tune the parameters accordingly."},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_random_images(image_folder_path, image_annotation_file, num = 12):\n    img_dict = {}\n    with open(image_annotation_file, \"r\") as file:\n        skip_csv_header(file)\n        for row in file:\n            try:\n                image_name, x_min, y_min, x_max, y_max, class_idx = row.split(\",\")\n                if image_name not in img_dict:\n                    img_dict[image_name] = list()\n                img_dict[image_name].append(\n                    [float(x_min), float(y_min), float(x_max), float(y_max), int(class_idx)]\n                )\n            except ValueError:\n                print(\"Could not convert float to string, likely that your data has empty values.\")\n\n    # randomly choose 12 images to plot\n    img_files_list = np.random.choice(list(img_dict.keys()), num)\n    print(\"The images' names are {}\".format(img_files_list))\n    img_matrix_list = []\n    \n    for img_file in img_files_list:\n        image_file_path = os.path.join(image_folder_path, img_file)\n        img = cv2.imread(image_file_path)[:,:,::-1]  \n        img_matrix_list.append(img)\n\n    \n    return plot_multiple_img(img_matrix_list, title_list = img_files_list, ncols = 4, main_title=\"Wheat Images\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Aqui vemos uma bela grade de 12 imagens plotadas."},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_random_images(image_folder_path, image_annotation_file, num = 12)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 6. Plotagem de várias imagens com caixas delimitadoras\n\nNa detecção de objetos com caixas delimitadoras, é sempre uma boa ideia plotar aleatoriamente algumas imagens com suas caixas delimitadoras para verificar as coordenadas incorretas da caixa delimitadora. Embora eu deva dizer que nesta competição em particular, há muitas imagens com muitas caixas delimitadoras e, portanto, você deve examinar claramente."},{"metadata":{"trusted":true},"cell_type":"code","source":"def random_bbox_check(image_folder_path, image_annotation_file, num = 12):\n    img_dict = {}\n    labels = [\"wheat\", \"no wheat\"]\n    with open(image_annotation_file, \"r\") as file:\n        skip_csv_header(file)\n        for row in file:\n            try:\n                image_name, x_min, y_min, x_max, y_max, class_idx = row.split(\",\")\n                if image_name not in img_dict:\n                    img_dict[image_name] = list()\n                img_dict[image_name].append(\n                    [float(x_min), float(y_min), float(x_max), float(y_max), int(class_idx)]\n                )\n            except ValueError:\n                print(\"Could not convert float to string, likely that your data has empty values.\")\n\n    # randomly choose 12 image.\n    img_files_list = np.random.choice(list(img_dict.keys()), num)\n    print(\"The images' names are {}\".format(img_files_list))\n    image_file_path_list = []\n\n    bbox_list = []\n    img_matrix_list = []\n    random_image_matrix_list = []\n    \n    for img_file in img_files_list:\n        image_file_path = os.path.join(image_folder_path, img_file)\n        img = cv2.imread(image_file_path)[:,:,::-1]  \n        height, width, channels = img.shape\n        bbox_list.append(img_dict[img_file])\n        img_matrix_list.append(img)\n\n    \n    final_bbox_list = []\n    for bboxes, img in zip(bbox_list, img_matrix_list):\n        final_bbox_array = np.array([])\n        #bboxes is a 2d array [[...], [...]]\n        for bbox in bboxes:\n            bbox = np.array(bbox).reshape(1,5)\n            final_bbox_array = np.append(final_bbox_array, bbox)\n        final_bbox_array = final_bbox_array.reshape(-1,5)\n        random_image = draw_rect(img.copy(), final_bbox_array.copy(), color = (255,0,0))\n        random_image_matrix_list.append(random_image)\n    plot_multiple_img(random_image_matrix_list, title_list = img_files_list, ncols = 4, main_title=\"Bounding Box Wheat Images\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Aplicar\nrandom_bbox_check(image_folder_path, image_annotation_file)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 7. Aumentos\n\nO aumento é uma técnica importante para aumentar artificialmente o tamanho dos dados. Em particular, quando o conjunto de dados é pequeno, o aumento antes do treinamento do modelo ajudará a rede a aprender melhor."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Albumentations\nimport albumentations as A","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_folder_path = \"/kaggle/input/global-wheat-detection/train/\"\nchosen_image = cv2.imread(os.path.join(image_folder_path, \"1ee6b9669.jpg\"))[:,:,::-1]\nplt.imshow(chosen_image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"chosen_image_dataframe = wheat.loc[wheat[\"image_id\"]==\"1ee6b9669.jpg\",[\"x_min\",\"y_min\",\"x_max\",\"y_max\",\"class\"]]\nbbox_array_of_chosen_image = np.array(chosen_image_dataframe.values.tolist())\nbbox_array_of_chosen_image.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"draw_chosen_image = draw_rect(chosen_image.copy(), bbox_array_of_chosen_image.copy(), color = (255,0,0))\nplt.imshow(draw_chosen_image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"albumentation_list = [A.RandomSunFlare(p=1), A.RandomFog(p=1), A.RandomBrightness(p=1),\n                      A.RandomCrop(p=1,height = 512, width = 512), A.Rotate(p=1, limit=90),\n                      A.RGBShift(p=1), A.RandomSnow(p=1),\n                      A.HorizontalFlip(p=1), A.VerticalFlip(p=1), A.RandomContrast(limit = 0.5,p = 1),\n                      A.HueSaturationValue(p=1,hue_shift_limit=20, sat_shift_limit=30, val_shift_limit=50)]\n\nimg_matrix_list = []\nbboxes_list = []\nfor aug_type in albumentation_list:\n    img = aug_type(image = chosen_image)['image']\n    img_matrix_list.append(img)\n\nimg_matrix_list.insert(0,chosen_image)    \n\ntitles_list = [\"Original\",\"RandomSunFlare\",\"RandomFog\",\"RandomBrightness\",\n               \"RandomCrop\",\"Rotate\", \"RGBShift\", \"RandomSnow\",\"HorizontalFlip\", \"VerticalFlip\", \"RandomContrast\",\"HSV\"]\n\n##reminder of helper function\ndef plot_multiple_img(img_matrix_list, title_list, ncols, main_title=\"\"):\n    fig, myaxes = plt.subplots(figsize=(20, 15), nrows=3, 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        myaxes[i // ncols][i % ncols].imshow(img)\n        myaxes[i // ncols][i % ncols].set_title(title, fontsize=15)\n    plt.show()\n    \nplot_multiple_img(img_matrix_list, titles_list, ncols = 4,main_title=\"Different Types of Augmentations\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 8. Caixas delimitadoras com albumentações\n\nLembre-se de que estamos usando nossa imagem escolhida como exemplo, por conveniência, vou lembrá-lo da matriz de imagem de imagens escolhidas e suas coordenadas de caixas delimitadoras abaixo. Mas há uma advertência aqui, minha matriz de caixas delimitadoras tem a forma [N, 5], onde o último elemento são os rótulos. Mas quando você quiser usar Albumentations para plotar caixas delimitadoras, use bboxes no formato de pascal_voc que é [x_min, y_min, x_max, y_max]; ele também leva em label_fields que são os rótulos de cada caixa delimitadora. Portanto, ainda precisamos fazer um pré-processamento simples abaixo."},{"metadata":{"trusted":true},"cell_type":"code","source":"chosen_image = cv2.imread(os.path.join(image_folder_path, \"1ee6b9669.jpg\"))[:,:,::-1]\nchosen_image_dataframe = wheat.loc[wheat[\"image_id\"]==\"1ee6b9669.jpg\",[\"x_min\",\"y_min\",\"x_max\",\"y_max\"]]\nbbox_array_of_chosen_image = np.array(chosen_image_dataframe.values.tolist())\nlabels_of_chosen_image = np.ones((len(bbox_array_of_chosen_image),))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_rect_with_labels(img, bboxes,class_id, class_dict, color=None):\n    img = img.copy()\n    bboxes = bboxes[:, :4]\n    bboxes = bboxes.reshape(-1, 4)\n    for bbox, label in zip(bboxes, class_id):\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        class_name = class_dict[label]\n        ((text_width, text_height), _) = cv2.getTextSize(class_name, cv2.FONT_HERSHEY_SIMPLEX, 0.35, 1) \n        img = cv2.rectangle(img.copy(), pt1, pt2, color, int(max(img.shape[:2]) / 200))\n        img = cv2.putText(img.copy(), class_name, (int(bbox[0]), int(bbox[1]) - int(0.3 * text_height)), cv2.FONT_HERSHEY_SIMPLEX,fontScale=1,color = (255,255,255), lineType=cv2.LINE_AA)\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ver_flip = A.Compose([\n        A.VerticalFlip(p=1),\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\n\nver_flip_annotations = ver_flip(image=chosen_image, bboxes=bbox_array_of_chosen_image, labels=labels_of_chosen_image)\nver_flip_annotations['bboxes'] = [list(bbox) for bbox in ver_flip_annotations['bboxes']]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ver_flip = A.Compose([\n        A.VerticalFlip(p=1),\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\n\nver_flip_annotations = ver_flip(image=chosen_image, bboxes=bbox_array_of_chosen_image, labels=labels_of_chosen_image)\nver_flip_annotations['bboxes'] = [list(bbox) for bbox in ver_flip_annotations['bboxes']]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hor_flip = A.Compose([\n        A.HorizontalFlip(p=1),\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\n\nhor_flip_annotations = hor_flip(image=chosen_image, bboxes=bbox_array_of_chosen_image, labels=labels_of_chosen_image)\nhor_flip_annotations['bboxes'] = [list(bbox) for bbox in hor_flip_annotations['bboxes']]\n\n\nhor_flip_img = draw_rect_with_labels(img = hor_flip_annotations['image'], bboxes = np.array(hor_flip_annotations['bboxes']),\n                          class_id = hor_flip_annotations['labels'], class_dict = {0: \"background\",1: \"wheat\"}, color=(255,0,0))\n    \nimg_matrix_list = [draw_chosen_image, hor_flip_img]\ntitles_list = [\"Original\", \"HorizontalFlipped\"]\n\nplot_multiple_img(img_matrix_list, titles_list, ncols = 2,main_title=\"Horizontal Flip\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"transform = A.Compose([\n    A.CoarseDropout(max_height=100, max_width=100, p = 1),\n    A.RandomBrightnessContrast(p=0.9),\n    A.HueSaturationValue(\n                        hue_shift_limit=0.2,\n                        sat_shift_limit=0.2,\n                        val_shift_limit=0.2,\n                        p=0.9,\n                        )\n])\nchosen_image = cv2.imread(os.path.join(image_folder_path, \"1ee6b9669.jpg\"))[:,:,::-1]\naugmented_image = transform(image=chosen_image)['image']\nplt.imshow(augmented_image)","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}