{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\nfrom glob import glob\nfrom PIL import Image\nimport cv2\n\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport seaborn as sns\n\nfrom bokeh.plotting import figure\nfrom bokeh.io import output_notebook, show, output_file\nfrom bokeh.models import ColumnDataSource, HoverTool, Panel\nfrom bokeh.models.widgets import Tabs\n\nimport albumentations as albu","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"![image](https://raw.githubusercontent.com/Lexie88rus/GlobalWheatDetection/master/wheat_image_cropped.png)"},{"metadata":{},"cell_type":"markdown","source":"# Global Wheat Detection EDA 한글화"},{"metadata":{},"cell_type":"markdown","source":"Computer Vision을 이용한 밀 이삭 탐지는 관련 사육업저, 농부들에게 도움이 될 것입니다!\n예를 들면 :\n* 작물의 생장 단계 조절 : 이삭이 열린 정도에 따라 수확일에 가까워짐\n* 작물의 상태 조절 : 개수가 적거나 크기가 작을 경우 안좋은 신호\n* 여러 품종의 밀에 대해 전체적인 특성과 산출량을 추정  \n\n이 노트북을 통해 전체적인 데이터를 살펴보고, 모델을 만들고 검증하는 과정에서 유의할 내용에 대해 알아보겠습니다."},{"metadata":{},"cell_type":"markdown","source":"# 데이터셋에 대한 정보"},{"metadata":{},"cell_type":"markdown","source":"숫자부터 살펴보겠습니다. 수치로부터 특별한 이미지가 나올 수도 있으니까요!"},{"metadata":{},"cell_type":"markdown","source":"# 1. train, test 이미지의 개수:"},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAIN_DIR = '../input/global-wheat-detection/train/'\nTEST_DIR = '../input/global-wheat-detection/test/'\nTRAIN_CSV_PATH = '../input/global-wheat-detection/train.csv'\n\ntrain_fns = glob(TRAIN_DIR + '*')\ntest_fns = glob(TEST_DIR + '*')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"train, test 이미지의 개수 계산"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Number of train images is {}\".format(len(train_fns)))\nprint(\"Number of test images is {}\".format(len(test_fns)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"test용 이미지가 10개밖에 없으며, 다른 test용 이미지들은 모델을 평가하기 위한 submission에서 사용됩니다.  \ntrain을 위한 이미지로 3422개는 충분해보이지 않습니다. Data augmentation(데이터 복제)기술이 필요해보입니다."},{"metadata":{},"cell_type":"markdown","source":"# 2. 이미지당 박스(밀 이삭)의 개수:"},{"metadata":{},"cell_type":"markdown","source":"이미지를 dataframe형태로 변환합니다.  \n(박스가 없는 이미지는 `image_id`를 제외한 모든 열이 nan값일 것입니다.)"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 박스 dataframe로드\ntrain = pd.read_csv(TRAIN_CSV_PATH)\n\n#train 이미지로 dataframe 생성\nall_train_images = pd.DataFrame([fns.split('/')[-1][:-4] for fns in train_fns])\nall_train_images.columns = ['image_id']\n\n# 박스 dataframe과 train이미지 병합\n## train이미지 위에 박스 그리기\nall_train_images = all_train_images.merge(train, on='image_id', how='left')\n\n# nan값을 0으로 대체\nall_train_images['bbox'] = all_train_images.bbox.fillna('[0,0,0,0]')\n\n# bbox 열 나누기\nbbox_items = all_train_images.bbox.str.split(',', expand = True)\nall_train_images['bbox_xmin'] = bbox_items[0].str.strip('[ ').astype(float)\nall_train_images['bbox_ymin'] = bbox_items[1].str.strip(' ').astype(float)\nall_train_images['bbox_width'] = bbox_items[2].str.strip(' ').astype(float)\nall_train_images['bbox_height'] = bbox_items[3].str.strip(' ]').astype(float)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('{} images without wheat heads.'.format(len(all_train_images) - len(train)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이미지를 살펴보겠습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_all_bboxes(df, image_id):\n    image_bboxes = df[df.image_id == image_id]\n    \n    bboxes = []\n    for _,row in image_bboxes.iterrows():\n        bboxes.append((row.bbox_xmin, row.bbox_ymin, row.bbox_width, row.bbox_height))\n        \n    return bboxes\n\ndef plot_image_examples(df, rows = 3, cols = 3, title=\"Image examples\"):\n    fig, axs = plt.subplots(rows, cols, figsize = (10, 10))\n    for row in range(rows):\n        for col in range(cols):\n            idx = np.random.randint(len(df), size = 1)[0]\n            img_id = df.iloc[idx].image_id\n            \n            img = Image.open(TRAIN_DIR + img_id + '.jpg')\n            axs[row, col].imshow(img)\n            \n            bboxes = get_all_bboxes(df, img_id)\n            \n            for bbox in bboxes:\n                rect = patches.Rectangle((bbox[0], bbox[1]), bbox[2], bbox[3], linewidth = 1, edgecolor = 'r', facecolor = 'none')\n                axs[row, col].add_patch(rect)\n                \n            axs[row, col].axis('off')\n            \n        plt.suptitle(title)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_image_examples(all_train_images)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이미지의 밝기와 작물의 익은 정도가 모두 다른 것을 확인할 수 있습니다."},{"metadata":{},"cell_type":"markdown","source":"이미지당 박스의 개수:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# train이미지당 박스의 개수 계산\nall_train_images['count'] = all_train_images.apply(lambda row : 1 if np.isfinite(row.width) else 0, axis = 1)\ntrain_images_count = all_train_images.groupby('image_id').sum().reset_index()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Bokeh를 이용한 바 차트 그리기\n# https://towardsdatascience.com/interactive-histograms-with-bokeh-202b522265f3\ndef hist_hover(dataframe, column, colors=[\"#94c8d8\", \"#ea5e51\"], bins = 30, title=''):\n    hist, edges = np.histogram(dataframe[column], bins = bins)\n    \n    hist_df = pd.DataFrame({column:hist,\n                           \"left\": edges[:-1],\n                           \"right\": edges[1:]})\n    hist_df[\"interval\"] = [\"%d to %d\" % (left, right) for left,\n                          right in zip(hist_df[\"left\"], hist_df[\"right\"])]\n    \n    src = ColumnDataSource(hist_df)\n    plot = figure(plot_height = 400, plot_width = 600,\n                 title = title,\n                 x_axis_label = column,\n                 y_axis_label = \"Count\")\n    plot.quad(bottom = 0, top = column, left = \"left\",\n             right = \"right\", source = src, fill_color = colors[0],\n             line_color = \"#35838d\", fill_alpha = 0.7,\n             hover_fill_alpha = 0.7, hover_fill_color = colors[1])\n    \n    hover = HoverTool(tooltips = [('Interval', '@interval'),\n                                 ('Count', str(\"@\" + column))])\n    \n    plot.add_tools(hover)\n    \n    output_notebook()\n    show(plot)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hist_hover(train_images_count, 'count', title = 'Number of wheat spikes per image')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"대부분 이미지당 20~50개의 이삭을 갖고있습니다."},{"metadata":{},"cell_type":"markdown","source":" 이삭의 개수가 적은 이미지 예시를 그려보겠습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"less_spikes_ids = train_images_count[train_images_count['count'] < 10].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(less_spikes_ids)], title =\"Example images with small number of spikes\" )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이미지 예시에 이상한 점이 있습니다.\n* 땅만 보이는 이미지가 있습니다.\n* 많이 확대된 듯이 보이는 이미지가 있습니다."},{"metadata":{},"cell_type":"markdown","source":"이삭이 많은 이미지:"},{"metadata":{"trusted":true},"cell_type":"code","source":"many_spikes_ids = train_images_count[train_images_count['count'] > 100].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(many_spikes_ids)], title = \"Example images with large number of spikes\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이삭 개수가 적은 이미지보다는 나아보입니다!"},{"metadata":{},"cell_type":"markdown","source":"# 3. 박스의 면적"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 박스 면적 계산\nall_train_images['bbox_area'] = all_train_images['bbox_width'] * all_train_images['bbox_height']\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 박스 면적에 대한 히스토그램\nhist_hover(all_train_images, 'bbox_area', title = 'Area of a single bounding box')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"박스 면적의 최대값 :"},{"metadata":{"trusted":true},"cell_type":"code","source":"all_train_images.bbox_area.max()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"박스 각각의 면적은 long tail 분포로 나타나고 있습니다. 면적이 넓은 이미지를 살펴보는 것이 좋을 것 같습니다."},{"metadata":{},"cell_type":"markdown","source":"면적이 넓은 박스 :"},{"metadata":{"trusted":true},"cell_type":"code","source":"large_boxes_ids = all_train_images[all_train_images['bbox_area'] > 200000].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(large_boxes_ids)], title = \"Example images with large bbox area\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"이상하게 넓은 이 박스들은 뭘까요?? 훈련과정에서 반드시 제거해야할 것 같습니다!!"},{"metadata":{},"cell_type":"markdown","source":"면적이 좁은 박스:"},{"metadata":{"trusted":true},"cell_type":"code","source":"min_area = all_train_images[all_train_images['bbox_area'] > 0].bbox_area.min()\nprint('The smallest bounding box area is {}'.format(min_area))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"small_boxes_ids = all_train_images[(all_train_images['bbox_area'] < 50) & (all_train_images['bbox_area'] > 0)].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(small_boxes_ids)], title = \"Example images with large bbox area\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"자세히 살펴보면, 모서리와 테두리 근처에 있는 미세한 박스들을 볼 수 있습니다. 아마 경계가 먼저 그려진 뒤에, 하나의 이미지를 여러개로 잘랐기 때문에 나타난 현상같습니다.\n\n이것들을 굳이 정리할 필요는 없어보입니다. IOU metric에 영향은 없을테니까요.\n##IOU metric(Intersection over Union) - Bounding Box가 얼마나 일치하는지 나타내는 지표"},{"metadata":{},"cell_type":"markdown","source":"# 4. 이미지당 박스의 면적"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 이미지당 전체 박스의 면적 계산\narea_per_image = all_train_images.groupby(by = 'image_id').sum().reset_index()\n\n# 박스의 비율 계산\narea_per_image_percentage = area_per_image.copy()\narea_per_image_percentage['bbox_area'] = area_per_image_percentage['bbox_area'] / (1024 * 1024) *100","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hist_hover(area_per_image_percentage, 'bbox_area', title = \"Percentage of image area covered by bounding boxes\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"정규분포와 아주 유사한 형태로 나타나고 있습니다! 이미지 면적의 20~40%를 박스가 차지하고 있습니다.\n\n이러한 관측은 예측 모델을 평가할 때 사용될 수 있습니다. 예측 박스의 면적 역시 정규분포를 따르겠죠.\n\n또한 최대치가 100%을 넘어가는 것도 확인 가능합니다. 박스가 겹쳐져 있다는 의미입니다."},{"metadata":{},"cell_type":"markdown","source":"박스가 차지하는 면적이 좁은 이미지 :"},{"metadata":{"trusted":true},"cell_type":"code","source":"small_area_perc_ids = area_per_image_percentage[area_per_image_percentage['bbox_area'] < 7].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(small_area_perc_ids)], title = \"Example images with small percentage of area covered by bounding boxes\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"박스가 차지하는 면적이 넓은 이미지 :"},{"metadata":{"trusted":true},"cell_type":"code","source":"large_area_perc_ids = area_per_image_percentage[area_per_image_percentage['bbox_area'] > 95].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(large_area_perc_ids)], title = \"Example images with large percentage of area covered by bounding boxes\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 5. 이미지의 밝기"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_image_brightness(image):\n    # grayscale(흑백)으로 전환\n    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n    \n    # 평균 밝기 구하기\n    return np.array(gray).mean()\n\ndef add_brightness(df):\n    brightness = []\n    for _, row in df.iterrows():\n        img_id = row.image_id\n        image = cv2.imread(TRAIN_DIR + img_id + '.jpg')\n        brightness.append(get_image_brightness(image))\n        \n    brightness_df = pd.DataFrame(brightness)\n    brightness_df.columns = ['brightness']\n    df = pd.concat([df, brightness_df], ignore_index = True, axis = 1)\n    df.columns = ['image_id', 'brightness']\n    \n    return df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images_df = pd.DataFrame(all_train_images.image_id.unique())\nimages_df.columns = ['image_id']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#데이터프레임의 전체적인 밝기 올리기\nimages_df = pd.DataFrame(all_train_images.image_id.unique())\nimages_df.columns = ['image_id']\nbrightness_df = add_brightness(images_df)\n\nall_train_images = all_train_images.merge(brightness_df, on = 'image_id')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hist_hover(all_train_images, 'brightness', title = '이미지 밝기 분포')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"어두운 이미지 :"},{"metadata":{"trusted":true},"cell_type":"code","source":"dark_ids = all_train_images[all_train_images['brightness'] < 30].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(dark_ids)], title='Darkest images')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"몇몇 이미지는 사람이 보기에도 구분이 어렵네요...  \n\n밝은 이미지:"},{"metadata":{"trusted":true},"cell_type":"code","source":"bright_ids = all_train_images[all_train_images.brightness > 130].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(bright_ids)], title='Brightest images')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"어두운 이미지랑 정반대네요. 잘보이게 해줄 필터가 필요합니다!\n잘못된 바운딩 박스도 보이는 것 같네요."},{"metadata":{},"cell_type":"markdown","source":"#  6. 노란색, 녹색이 최대, 최소인 이미지"},{"metadata":{},"cell_type":"markdown","source":"특정 색상이 많은 이미지를 뽑아보겠습니다. 녹색이 많다면 건강하단 의미겠죠? 노란색이 많다면 수확하기 좋은 이미지일 것이고, 갈색이 많다면 땅위에 있는 이삭일 것 같습니다!"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_percentage_of_green_pixels(image):\n    #HSV로 변환\n    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)\n    \n    #녹색부분 구하기\n    hsv_lower = (40, 40, 40)\n    hsv_higher = (70, 255, 255)\n    green_mask = cv2.inRange(hsv, hsv_lower, hsv_higher)\n    \n    return float(np.sum(green_mask)) / 255 / (1024 * 1024)\n\ndef get_percentage_of_yellow_pixels(image):\n    #HSV로 변환\n    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)\n    \n    #녹색부분 구하기\n    hsv_lower = (25, 40, 40)\n    hsv_higher = (35, 255, 255)\n    yellow_mask = cv2.inRange(hsv, hsv_lower, hsv_higher)\n    \n    return float(np.sum(yellow_mask)) / 255 / (1024 * 1024)\n\ndef add_green_pixels_percentage(df):\n    green = []\n    for _, row in df.iterrows():\n        img_id = row.image_id\n        image = cv2.imread(TRAIN_DIR + img_id + '.jpg')\n        green.append(get_percentage_of_green_pixels(image))\n        \n    green_df = pd.DataFrame(green)\n    green_df.columns = ['green_pixels']\n    df = pd.concat([df, green_df], ignore_index = True, axis = 1)\n    df.columns = ['image_id', 'green_pixels']\n    \n    return df\n\ndef add_yellow_pixels_percentage(df):\n    yellow = []\n    for _, row in df.iterrows():\n        img_id = row.image_id\n        image = cv2.imread(TRAIN_DIR + img_id + '.jpg')\n        yellow.append(get_percentage_of_yellow_pixels(image))\n        \n    yellow_df = pd.DataFrame(yellow)\n    yellow_df.columns = ['yellow_pixels']\n    df = pd.concat([df, yellow_df], ignore_index = True, axis = 1)\n    df.columns = ['image_id', 'yellow_pixels']\n    \n    return df\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#녹색의 픽셀의 비율 열 추가\ngreen_pixels_df = add_green_pixels_percentage(images_df)\nall_train_images = all_train_images.merge(green_pixels_df, on = 'image_id')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hist_hover(all_train_images, 'green_pixels', title = '녹색 픽셀 비율 분포', colors = ['#c3ea84', '#3e7a17'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"녹색이 가장 많은 이미지의 비율은 60%입니다.\n\n대부분의 이미지가 녹색이 전혀 없습니다! 아마 대부분 노란색으로 수확에 가깝다는 의미인 것 같습니다."},{"metadata":{},"cell_type":"markdown","source":"녹색이 많은 이미지:"},{"metadata":{"trusted":true},"cell_type":"code","source":"green_ids = all_train_images[all_train_images['green_pixels'] > 0.55].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(green_ids)], title = 'The most green images')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"녹색이 많은 이미지는 대부분 이제 막 피기 시작한 작은 작물들이 포함된 것 같습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"#노란색 픽셀의 비율 열 추가\nyellow_pixels_df = add_yellow_pixels_percentage(images_df)\nall_train_images = all_train_images.merge(yellow_pixels_df, on = 'image_id')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"hist_hover(all_train_images, 'yellow_pixels', title = '노란색 픽셀 비율 분포도', colors = ['#fffedb', '#fffeab'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"노란색이 많은 이미지를 보겠습니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"yellow_ids = all_train_images[all_train_images['yellow_pixels'] > 0.55].image_id\nplot_image_examples(all_train_images[all_train_images.image_id.isin(yellow_ids)], title = 'The most yellow images')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Data Augmentation(데이터 증강)에 대한 생각(원작자)\n\n비교적 train데이터가 적은 이번 컴피티션에서, Data augmentaion는 중요한 요소일 것 같습니다. Data augmentaion을 통해 주어진 환경 내에서 더 robust한(견고한) 모델을 만들 수 있읍니다. 어떤 augmentations/필터를 사용할 수 있을까요?:\n* 원본 이미지의 방향이 제각기 다르기 때문에 *이미지를 수직 혹은 수평으로 뒤집기*\n* 각각이 확대된 정도가 다르기 때문에 *크롭-리사이즈*\n* 밝기를 조정하기 위한 다양한 필터. [예시 컴퍼티션](https://www.kaggle.com/c/aptos2019-blindness-detection)\n\n주의할 점은:\n* 바운딩 박스 처리가 어렵기 때문에 회전은 어려워보입니다.\n\n"},{"metadata":{},"cell_type":"markdown","source":"augmentation 파이프라인 예시:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 예시 설치\n# 바운딩박스에 영향이 없는 augmentation으로 진행\nexample_transforms = albu.Compose([\n    albu.RandomSizedBBoxSafeCrop(512, 512, erosion_rate = 0.0, interpolation = 1, p = 1.0),\n    albu.HorizontalFlip(p = 0.5),\n    albu.VerticalFlip(p = 0.5),\n    albu.OneOf([albu.RandomContrast(),\n                albu.RandomGamma(),\n                albu.RandomBrightness()], p = 1.0),\n    albu.CLAHE(p = 1.0)], p = 1.0, bbox_params = albu.BboxParams(format = 'coco', label_fields = ['category_id']))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def apply_transforms(transforms, df, n_transforms=3):\n    idx = np.random.randint(len(df), size=1)[0]\n    \n    image_id = df.iloc[idx].image_id\n    bboxes = []\n    for _, row in df[df.image_id == image_id].iterrows():\n        bboxes.append([row.bbox_xmin, row.bbox_ymin, row.bbox_width, row.bbox_height])\n        \n    image = Image.open(TRAIN_DIR + image_id + '.jpg')\n    \n    fig, axs = plt.subplots(1, n_transforms+1, figsize=(15,7))\n    \n    # 원본이미지\n    axs[0].imshow(image)\n    axs[0].set_title('original')\n    for bbox in bboxes:\n        rect = patches.Rectangle((bbox[0],bbox[1]),bbox[2],bbox[3],linewidth=1,edgecolor='r',facecolor='none')\n        axs[0].add_patch(rect)\n    \n    # n번 변환 적용\n    for i in range(n_transforms):\n        params = {'image': np.asarray(image),\n                  'bboxes': bboxes,\n                  'category_id': [1 for j in range(len(bboxes))]}\n        augmented_boxes = transforms(**params)\n        bboxes_aug = augmented_boxes['bboxes']\n        image_aug = augmented_boxes['image']\n\n        # plot the augmented image and augmented bounding boxes\n        axs[i+1].imshow(image_aug)\n        axs[i+1].set_title('augmented_' + str(i+1))\n        for bbox in bboxes_aug:\n            rect = patches.Rectangle((bbox[0],bbox[1]),bbox[2],bbox[3],linewidth=1,edgecolor='r',facecolor='none')\n            axs[i+1].add_patch(rect)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"apply_transforms(example_transforms, all_train_images, n_transforms = 3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"apply_transforms(example_transforms, all_train_images, n_transforms=3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"CLAHE가 어두운 곳을 강조하는 법을 알아두세요. 이 대회에서 꼭 필요할 것 같네요."},{"metadata":{},"cell_type":"markdown","source":"### 결론:\n\n1. 이미지의 줌 레벨이 다양합니다. 모델 훈련을 위해 크롭-리사이즈 augmentation을 사용했습니다.\n1. 이미지의 밝기가 다양합니다. 이를 위한 특별한 필터가 필요합니다.\n1. 바운딩박스가 지저분합니다!\n    모델에 훈련을 하이게 앞서 큰 박스를 정리해야 합니다.\n    너무 작은 박스는 IOU 메트릭에 영향이 없으므로 둬도 될 것 같습니다.\n    몇몇 이삭들은 바운딩 박스가 없습니다.\n    \n    "}],"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}