{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Introduction"},{"metadata":{},"cell_type":"markdown","source":"### **Car Model Classification**\n차 종(모델)의 분류는 일반적인 이미지 분류 예측 보다는 난이도가 있습니다. 예를 들어, 우리가 강아지와 사람을 분류 예측 해야 한다고 가정해봅시다. 사람의 이미지와 강아지의 이미지를 서로 비교해서 보면, 사람에게 드러나는 특징과 강아지에게 드러나는 특징은 비교적 차이가 존재합니다.\n\n하지만, 자동차라는 하나의 카테고리의 이미지들 가운데, 심지어는 브랜드마저 같은 차량이 존재하는 이미지들 사이에서 차량의 모델을 분류하는 작업은 강아지와 사람을 구분하는 것 처럼 간단히 할 수 있는 문제가 아닙니다.\n\n### **Competition**\n이번 컴페티션에서는, 주어진 자동차 이미지를 총 196개의 클래스로 분류 예측하는 것이 목표입니다.  \nsample_submission에 매핑된 테스트 이미지의 클래스를 예측하여 제출하시면 됩니다."},{"metadata":{},"cell_type":"markdown","source":"# Prepare the Data"},{"metadata":{},"cell_type":"markdown","source":"### **Load library**\n기본적인 라이브러리를 로드 합니다. 이번 베이스라인은 pytorch pretrained 모델 기반으로 실행됩니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport time\nimport math\nimport random\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom pathlib import Path\nimport glob\nimport zipfile\nimport matplotlib.pyplot as plt\nfrom PIL import Image, ImageEnhance, ImageOps\n\nimport torch\nfrom tqdm import tqdm, tqdm_notebook","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('pytorch version: {}'.format(torch.__version__))\nprint('GPU 사용 가능 여부: {}'.format(torch.cuda.is_available()))\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"   # GPU 사용 가능 여부에 따라 device 정보 저장","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### **File load**"},{"metadata":{},"cell_type":"markdown","source":"주어진 파일을 확인하고 로드합니다. "},{"metadata":{"trusted":true},"cell_type":"code","source":"# 혹 다른 데이터 셋 추가(Pretrained Model Weights)로 인해 PATH가 변경된다면 아래 PATH를 수정\nDATA_PATH = '../input/'\nos.listdir(DATA_PATH)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 이미지 폴더 경로\nTRAIN_IMG_PATH = os.path.join(DATA_PATH, 'train')\nTEST_IMG_PATH = os.path.join(DATA_PATH, 'test')\n\n# CSV 파일 경로\ndf_train = pd.read_csv(os.path.join(DATA_PATH, 'train.csv'))\ndf_test = pd.read_csv(os.path.join(DATA_PATH, 'test.csv'))\ndf_class = pd.read_csv(os.path.join(DATA_PATH, 'class.csv'))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Exploration\n실제 데이터가 Description과 일치 하는지, 데이터는 어떻게 구성되어 있고 클래스 별로 어떤 분포를 가지고 있는지 등 데이터에 대한 전반적인 궁금증을 해결해보는 과정입니다."},{"metadata":{},"cell_type":"markdown","source":"### **Check Data**\nData Description에 나와 있는 컬럼 별 세부 설명 입니다.\n* **img_file** - 데이터 셋의 각 로우와 연결되는 이미지 파일 이름\n* **bbox_x1** - 바운딩 박스 x1 좌표 (좌상단 x)\n* **bbox_y1** - 바운딩 박스 y1 좌표 (좌상단 y)\n* **bbox_x2** - 바운딩 박스 x2 좌표 (우하단 x)\n* **bbox_y2** - 바운딩 박스 y2 좌표 (우하단 y)\n* **class** - 예측하려는 차종(Target)\n* **id** - 각 데이터 셋에 기입 되어 있는 클래스 id\n* **name** - 클래스 id에 대응되는 실제 차종 레이블"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data 누락 체크\nif set(list(df_train.img_file)) == set(os.listdir(TRAIN_IMG_PATH)) :\n    print(\"Train file 누락 없음!\")\nelse : \n    print(\"Train file 누락\")\n\nif set(list(df_test.img_file)) == set(os.listdir(TEST_IMG_PATH)) :\n    print(\"Test file 누락 없음!\")\nelse : \n    print(\"Test file 누락\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data 갯수\nprint(\"Number of Train Data : {}\".format(df_train.shape[0]))\nprint(\"Number of Test Data : {}\".format(df_test.shape[0]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"타겟 클래스 총 갯수 : {}\".format(df_class.shape[0]))\nprint(\"Train Data의 타겟 종류 갯수 : {}\".format(df_train['class'].nunique()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### **Class Distribution**  \n분류 문제에서 가장 먼저 의심해봐야 할 부분이 바로 Target Class의 분포입니다. 학습에 사용해야 하는 Train Set의 타겟 분포를 확인해서 밸런스가 어느정도인지 체크해야 합니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(12, 6))\nsns.countplot(df_train[\"class\"], order=df_train[\"class\"].value_counts(ascending=True).index)\nplt.title(\"Number of data per each class\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cntEachClass = df_train[\"class\"].value_counts(ascending=False)\nprint(\"Class with most count  : {}\".format(cntEachClass.index[0]))\nprint(\"Most Count : {}\".format(cntEachClass.max()))\n\nprint(\"Class with fewest count  : {}\".format(cntEachClass.index[-1]))\nprint(\"Fewest Count : {}\".format(cntEachClass.min()))\n\nprint(\"Mean  : {}\".format(cntEachClass.mean()))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"대체적으로 카운트 수는 비슷합니다. 84번 클래스가 유독 카운트 숫자가 높고 평균은 51정도 입니다."},{"metadata":{},"cell_type":"markdown","source":"### **Image Visualization**\n파이썬 커널에서 이미지를 보고 싶을땐 어떻게 할까요? 이미지를 로드하는 방법은 여러 방법이 있지만, 이 커널에서는 PIL 라이브러리를 사용합니다.  \nDocumentation : https://pillow.readthedocs.io/en/stable/"},{"metadata":{"trusted":true},"cell_type":"code","source":"import PIL\nfrom PIL import ImageDraw\n\ntmp_imgs = df_train['img_file'][100:110]\nplt.figure(figsize=(12,20))\n\nfor num, f_name in enumerate(tmp_imgs):\n    img = PIL.Image.open(os.path.join(TRAIN_IMG_PATH, f_name))\n    plt.subplot(5, 2, num + 1)\n    plt.title(f_name)\n    plt.imshow(img)\n    plt.axis('off')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### **Bounding Box**\n이미지 대회를 처음 해보시는 분이라면, 이 바운딩 박스라는 것이 생소할 수 있습니다.\n \n바운딩 박스란?  \n이미지 내부에서 특정 Object를 박스로 레이블한 좌표를 말하며, 보통 좌측 상단 (x1, y1)과, 우측 하단 (x2, y2) 좌표가 주어져서   \n직사각형 모양의 박스를 그릴 수 있게됩니다. 이때, 좌표는 이미지의 픽셀 좌표입니다."},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_rect(drawcontext, pos, outline=None, width=0):\n    (x1, y1) = (pos[0], pos[1])\n    (x2, y2) = (pos[2], pos[3])\n    points = (x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1)\n    drawcontext.line(points, fill=outline, width=width)\n\ndef make_boxing_img(img_name) :\n    if img_name.split('_')[0] == \"train\" :\n        PATH = TRAIN_IMG_PATH\n        data = df_train\n    elif img_name.split('_')[0] == \"test\" :\n        PATH = TEST_IMG_PATH\n        data = df_test\n        \n    img = PIL.Image.open(os.path.join(PATH, img_name))\n    pos = data.loc[data[\"img_file\"] == img_name, \\\n                   ['bbox_x1','bbox_y1', 'bbox_x2', 'bbox_y2']].values.reshape(-1)\n    draw = ImageDraw.Draw(img)\n    draw_rect(draw, pos, outline='red', width=10)\n    \n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f_name = \"train_00102.jpg\"\n\nplt.figure(figsize=(20,10))\nplt.subplot(1, 2, 1)\n# Original Image\norigin_img = PIL.Image.open(os.path.join(TRAIN_IMG_PATH, f_name))\nplt.title(\"Original Image - {}\".format(f_name))\nplt.imshow(origin_img)\nplt.axis('off')\n\n# Image included bounding box\nplt.subplot(1, 2, 2)\nboxing = make_boxing_img(f_name)\nplt.title(\"Boxing Image - {}\".format(f_name))\nplt.imshow(boxing)\nplt.axis('off')\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def crop_boxing_img(img_name, margin=16) :\n    if img_name.split('_')[0] == \"train\" :\n        PATH = TRAIN_IMG_PATH\n        data = df_train\n    elif img_name.split('_')[0] == \"test\" :\n        PATH = TEST_IMG_PATH\n        data = df_test\n        \n    img = PIL.Image.open(os.path.join(PATH, img_name))\n    pos = data.loc[data[\"img_file\"] == img_name, \\\n                   ['bbox_x1','bbox_y1', 'bbox_x2', 'bbox_y2']].values.reshape(-1)\n\n    width, height = img.size\n    x1 = max(0, pos[0] - margin)\n    y1 = max(0, pos[1] - margin)\n    x2 = min(pos[2] + margin, width)\n    y2 = min(pos[3] + margin, height)\n    \n    if abs(pos[2] - pos[0]) > width or abs(pos[3] - pos[1]) > height:\n        print(f'{img_name} is wrong bounding box, img size: {img.size},  bbox_x1: {pos[0]}, bbox_x2: {pos[2]}, bbox_y1: {pos[1]}, bbox_y2: {pos[3]}')\n        return img\n\n    return img.crop((x1,y1,x2,y2))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Process Image Data Crop"},{"metadata":{"trusted":true},"cell_type":"code","source":"# train\nfor i, row in df_train.iterrows():\n    cropped = crop_boxing_img(row['img_file'])\n    cropped.save(row['img_file'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i, row in df_test.iterrows():\n    cropped = crop_boxing_img(row['img_file'])\n    cropped.save(row['img_file'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tmp_imgs = df_train['img_file'][100:105]\nplt.figure(figsize=(12,20))\n\nfor num, f_name in enumerate(tmp_imgs):\n    img = PIL.Image.open(os.path.join(TRAIN_IMG_PATH, f_name))\n    plt.subplot(5, 2, 2*num + 1)\n    plt.title(f_name)\n    plt.imshow(img)\n    plt.axis('off')\n    \n    img_crop = PIL.Image.open(f_name)\n    plt.subplot(5, 2, 2*num + 2)\n    plt.title(f_name + ' cropped')\n    plt.imshow(img_crop)\n    plt.axis('off')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with zipfile.ZipFile('train_crop.zip','w') as zip: \n        # writing each file one by one \n        for file in glob.glob('train*.jpg'): \n            zip.write(file)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with zipfile.ZipFile('test_crop.zip','w') as zip: \n        # writing each file one by one \n        for file in glob.glob('test*.jpg'): \n            zip.write(file)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -rf *.jpg","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}