{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# MMDetectionによるベースライン\n- OpenMMLabs( https://github.com/open-mmlab )のライブラリ群の一つであるMMDetection( https://github.com/open-mmlab/mmdetection )を使用して物体検出モデルの学習・推論を行う\n- MMDetectionには比較的最近のモデルまで含む多数が実装されており、使用するconfigを変えるだけでモデルを試すことができるため、課題に応じたモデル選択やアンサンブルの実装が容易にできる\n- 今回はライセンスフリーで商用利用可能なモデルの中で比較的高性能とされるYOLOXを使用する","metadata":{}},{"cell_type":"markdown","source":"# 必要なライブラリの準備\nオフラインでインストールするためには右上の+Add Dataから\"mmdetection-2-26-0\"データセットを検索して追加してください","metadata":{}},{"cell_type":"code","source":"ls /kaggle/input","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:43:17.834711Z","iopub.execute_input":"2022-11-26T08:43:17.835112Z","iopub.status.idle":"2022-11-26T08:43:18.860156Z","shell.execute_reply.started":"2022-11-26T08:43:17.835030Z","shell.execute_reply":"2022-11-26T08:43:18.858901Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 必要なライブラリのインストール（オンライン用）\n# !pip install openmim\n# !mim install mmdet\n# !git clone https://github.com/open-mmlab/mmdetection.git\n# %cd mmdetection\n# !pip install -q -e .\n\n# 必要なライブラリのインストール（オフライン用）\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/addict-2.4.0-py3-none-any.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/yapf-0.32.0-py2.py3-none-any.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminal-0.4.0-py3-none-any.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/terminaltables-3.1.10-py2.py3-none-any.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmcv_full-1.7.0-cp37-cp37m-linux_x86_64.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/pycocotools-2.0.6-cp37-cp37m-linux_x86_64.whl\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmpycocotools-12.0.3-cp37-cp37m-linux_x86_64.whl\n\n!cp -r /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmdetection/ /kaggle/working/\n%cd /kaggle/working/mmdetection\n!pip install -e . --no-deps\n%cd /kaggle/working/\n\n!pip install /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/mmdet-2.26.0-py3-none-any.whl","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2022-11-26T08:43:18.862522Z","iopub.execute_input":"2022-11-26T08:43:18.862847Z","iopub.status.idle":"2022-11-26T08:48:29.243965Z","shell.execute_reply.started":"2022-11-26T08:43:18.862817Z","shell.execute_reply":"2022-11-26T08:48:29.242794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import ast\nimport base64\nfrom collections import OrderedDict\nfrom glob import glob\nimport IPython\nfrom io import BytesIO\nimport json\nimport numpy as np\nimport os\nimport pandas as pd\nfrom PIL import Image\nfrom sklearn.model_selection import train_test_split\nfrom tqdm.notebook import tqdm\n\nSEED = 0\nCATEGORYS = {1: 'wheat'}\nDATA_ROOT = '/kaggle/input/global-wheat-detection/'","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:29.245954Z","iopub.execute_input":"2022-11-26T08:48:29.246324Z","iopub.status.idle":"2022-11-26T08:48:29.940235Z","shell.execute_reply.started":"2022-11-26T08:48:29.246284Z","shell.execute_reply":"2022-11-26T08:48:29.939275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 学習の準備\nMMDetectionを使用するために必要な準備は以下の2つ\n1. アノテーションデータをCOCOフォーマットに変換する\n1. モデルごとのconfigをコピーしてきて今回の内容に応じて書き換える","metadata":{}},{"cell_type":"markdown","source":"## アノテーションデータをCOCOフォーマットに変換する\n参考 -> https://www.kaggle.com/code/impiyush/simply-convert-data-to-coco-format","metadata":{}},{"cell_type":"code","source":"coco = {\"info\": {}, \"licenses\": [], \"images\": [], \"annotations\": [], \"categories\": []}","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:29.943087Z","iopub.execute_input":"2022-11-26T08:48:29.943493Z","iopub.status.idle":"2022-11-26T08:48:29.948810Z","shell.execute_reply.started":"2022-11-26T08:48:29.943455Z","shell.execute_reply":"2022-11-26T08:48:29.947666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco[\"info\"] = {\n    \"description\": \"Global Wheat Detection Dataset, Kaggle 2020\",\n    \"url\": \"https://www.kaggle.com/c/global-wheat-detection/data\",\n    \"version\": \"1.0\",\n    \"year\": 2020,\n    \"contributor\": \"http://www.global-wheat.com/contributors/\",\n    \"date_created\": \"2020/05/29\"\n}","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:29.951404Z","iopub.execute_input":"2022-11-26T08:48:29.952190Z","iopub.status.idle":"2022-11-26T08:48:29.959589Z","shell.execute_reply.started":"2022-11-26T08:48:29.952152Z","shell.execute_reply":"2022-11-26T08:48:29.958762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco[\"licenses\"].append({\n    \"url\": \"https://opensource.org/licenses/MIT\",\n    \"id\": 1,\n    \"name\": \"MIT License\"\n})","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:29.962211Z","iopub.execute_input":"2022-11-26T08:48:29.962520Z","iopub.status.idle":"2022-11-26T08:48:29.973594Z","shell.execute_reply.started":"2022-11-26T08:48:29.962496Z","shell.execute_reply":"2022-11-26T08:48:29.972786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco[\"categories\"].append({\"supercategory\": \"grain\",\"id\": 1,\"name\": \"wheat\"})","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:29.976914Z","iopub.execute_input":"2022-11-26T08:48:29.977169Z","iopub.status.idle":"2022-11-26T08:48:29.986024Z","shell.execute_reply.started":"2022-11-26T08:48:29.977145Z","shell.execute_reply":"2022-11-26T08:48:29.985084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/global-wheat-detection/train.csv')\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:29.987246Z","iopub.execute_input":"2022-11-26T08:48:29.987528Z","iopub.status.idle":"2022-11-26T08:48:30.283525Z","shell.execute_reply.started":"2022-11-26T08:48:29.987503Z","shell.execute_reply":"2022-11-26T08:48:30.282562Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"unique_images = train_df.image_id.unique()\nlen(unique_images)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:30.285025Z","iopub.execute_input":"2022-11-26T08:48:30.285615Z","iopub.status.idle":"2022-11-26T08:48:30.310749Z","shell.execute_reply.started":"2022-11-26T08:48:30.285579Z","shell.execute_reply":"2022-11-26T08:48:30.309817Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_bboxes_per_image(df):\n    \"\"\"author: @impiyush\"\"\"\n    # convert from string list, to python list\n    df.bbox = df.bbox.apply(ast.literal_eval)\n    # group-by on image id and return all bboxes for that image as a list of lists\n    return train_df.groupby('image_id')['bbox'].apply(list).reset_index(name='bboxes')","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:30.317059Z","iopub.execute_input":"2022-11-26T08:48:30.317807Z","iopub.status.idle":"2022-11-26T08:48:30.323436Z","shell.execute_reply.started":"2022-11-26T08:48:30.317772Z","shell.execute_reply":"2022-11-26T08:48:30.322314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df_bboxes_grouped = get_bboxes_per_image(train_df)\ntrain_df_bboxes_grouped.head()","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:30.324864Z","iopub.execute_input":"2022-11-26T08:48:30.325316Z","iopub.status.idle":"2022-11-26T08:48:32.225351Z","shell.execute_reply.started":"2022-11-26T08:48:30.325280Z","shell.execute_reply":"2022-11-26T08:48:32.224384Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"assert train_df_bboxes_grouped.shape[0] == len(unique_images), \"Number of images differ when grouped\"","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.227480Z","iopub.execute_input":"2022-11-26T08:48:32.228658Z","iopub.status.idle":"2022-11-26T08:48:32.233992Z","shell.execute_reply.started":"2022-11-26T08:48:32.228603Z","shell.execute_reply":"2022-11-26T08:48:32.232964Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train, df_valid = train_test_split(\n    train_df_bboxes_grouped,\n    test_size=0.2,\n    random_state=SEED,\n    shuffle=True\n)\n\ndf_train = df_train.reset_index(drop=True)\ndf_valid = df_valid.reset_index(drop=True)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.235468Z","iopub.execute_input":"2022-11-26T08:48:32.235904Z","iopub.status.idle":"2022-11-26T08:48:32.247166Z","shell.execute_reply.started":"2022-11-26T08:48:32.235867Z","shell.execute_reply":"2022-11-26T08:48:32.245880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco_train = coco.copy()\ncoco_valid = coco.copy()","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.248890Z","iopub.execute_input":"2022-11-26T08:48:32.249270Z","iopub.status.idle":"2022-11-26T08:48:32.256273Z","shell.execute_reply.started":"2022-11-26T08:48:32.249235Z","shell.execute_reply":"2022-11-26T08:48:32.255166Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def set_coco_images(df):\n    \"\"\"author: @impiyush\"\"\"\n    images = []\n    for _,img in enumerate(tqdm(df.image_id.unique())):\n        img_dict = {\"license\":1, \n                    \"height\":1024, \n                    \"width\":1024,\n                    \"id\":img}\n        img_dict[\"file_name\"] = f\"{img}.jpg\"\n        images.append(img_dict)\n\n    return images","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.257560Z","iopub.execute_input":"2022-11-26T08:48:32.258431Z","iopub.status.idle":"2022-11-26T08:48:32.267049Z","shell.execute_reply.started":"2022-11-26T08:48:32.258396Z","shell.execute_reply":"2022-11-26T08:48:32.266137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco_train[\"images\"] = set_coco_images(df_train)\ncoco_valid[\"images\"] = set_coco_images(df_valid)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.268569Z","iopub.execute_input":"2022-11-26T08:48:32.268954Z","iopub.status.idle":"2022-11-26T08:48:32.355826Z","shell.execute_reply.started":"2022-11-26T08:48:32.268909Z","shell.execute_reply":"2022-11-26T08:48:32.354866Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco_train[\"images\"][:3]","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.357378Z","iopub.execute_input":"2022-11-26T08:48:32.358034Z","iopub.status.idle":"2022-11-26T08:48:32.365308Z","shell.execute_reply.started":"2022-11-26T08:48:32.357997Z","shell.execute_reply":"2022-11-26T08:48:32.364314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco_valid[\"images\"][:3]","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.366867Z","iopub.execute_input":"2022-11-26T08:48:32.367475Z","iopub.status.idle":"2022-11-26T08:48:32.380013Z","shell.execute_reply.started":"2022-11-26T08:48:32.367441Z","shell.execute_reply":"2022-11-26T08:48:32.378752Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"assert len(coco_train[\"images\"])==len(df_train), \"Number of images differ from df_train\"\nassert len(coco_valid[\"images\"])==len(df_valid), \"Number of images differ from df_valid\"","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.381553Z","iopub.execute_input":"2022-11-26T08:48:32.382255Z","iopub.status.idle":"2022-11-26T08:48:32.388700Z","shell.execute_reply.started":"2022-11-26T08:48:32.382220Z","shell.execute_reply":"2022-11-26T08:48:32.387885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def set_coco_annotations(df):\n    \"\"\"author: @impiyush\"\"\"\n    annos = []\n    id_cnt = 1\n\n    for _,row in tqdm(df.iterrows(), total=len(df)):\n    #     print(row)\n        anno = {}\n        anno['segmentation'] = []\n        anno['iscrowd'] = 0\n        anno['image_id'] = row['image_id']\n        anno['category_id'] = 1\n        bboxes = row['bboxes']\n    #     annos.append(box)\n        for ix, box in enumerate(bboxes):\n            anno['bbox'] = box # x,y,w,h\n            anno['area'] = box[2] * box[3] # w*h\n            anno['id'] = f\"{id_cnt:05}\"\n            annos.append(anno.copy()) # copy is necessary here, otherwise it will always point to the last value of anno\n            id_cnt += 1\n    \n    return annos","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.390362Z","iopub.execute_input":"2022-11-26T08:48:32.391102Z","iopub.status.idle":"2022-11-26T08:48:32.406372Z","shell.execute_reply.started":"2022-11-26T08:48:32.391066Z","shell.execute_reply":"2022-11-26T08:48:32.405387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coco_train['annotations'] = set_coco_annotations(df_train)\ncoco_valid['annotations'] = set_coco_annotations(df_valid)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:32.407998Z","iopub.execute_input":"2022-11-26T08:48:32.408612Z","iopub.status.idle":"2022-11-26T08:48:33.026055Z","shell.execute_reply.started":"2022-11-26T08:48:32.408577Z","shell.execute_reply":"2022-11-26T08:48:33.025003Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# COCO形式に変換したアノテーションをtrain/validに分けてファイルに出力する\nos.makedirs('./annotations', exist_ok=True)\nwith open('./annotations/train_coco.json', 'w') as jf:\n    json.dump(coco_train, jf, ensure_ascii=False, indent=2)\nwith open('./annotations/valid_coco.json', 'w') as jf:\n    json.dump(coco_valid, jf, ensure_ascii=False, indent=2)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:33.027550Z","iopub.execute_input":"2022-11-26T08:48:33.028174Z","iopub.status.idle":"2022-11-26T08:48:37.233139Z","shell.execute_reply.started":"2022-11-26T08:48:33.028137Z","shell.execute_reply":"2022-11-26T08:48:37.232063Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## アノテーションの可視化\n参考 -> https://www.kaggle.com/code/ericdepotter/visualize-coco-annotations","metadata":{}},{"cell_type":"code","source":"# Load the dataset json\nclass CocoDataset():\n    def __init__(self, annotation_path, image_dir):\n        self.annotation_path = annotation_path\n        self.image_dir = image_dir\n        self.colors = ['red'] * 10000\n        \n        json_file = open(self.annotation_path)\n        self.coco = json.load(json_file)\n        json_file.close()\n        \n        #self.process_info()\n        #self.process_licenses()\n        self.process_categories()\n        self.process_images()\n        self.process_segmentations()\n\n    def display_info(self):\n        print('Dataset Info:')\n        print('=============')\n        for key, item in self.info.items():\n            print('  {}: {}'.format(key, item))\n        \n        requirements = [['description', str],\n                        ['url', str],\n                        ['version', str],\n                        ['year', int],\n                        ['contributor', str],\n                        ['date_created', str]]\n        for req, req_type in requirements:\n            if req not in self.info:\n                print('ERROR: {} is missing'.format(req))\n            elif type(self.info[req]) != req_type:\n                print('ERROR: {} should be type {}'.format(req, str(req_type)))\n        print('')\n        \n    def display_licenses(self):\n        print('Licenses:')\n        print('=========')\n        \n        requirements = [['id', int],\n                        ['url', str],\n                        ['name', str]]\n        for license in self.licenses:\n            for key, item in license.items():\n                print('  {}: {}'.format(key, item))\n            for req, req_type in requirements:\n                if req not in license:\n                    print('ERROR: {} is missing'.format(req))\n                elif type(license[req]) != req_type:\n                    print('ERROR: {} should be type {}'.format(req, str(req_type)))\n            print('')\n        print('')\n        \n    def display_categories(self):\n        print('Categories:')\n        print('=========')\n        for sc_key, sc_val in self.super_categories.items():\n            print('  super_category: {}'.format(sc_key))\n            for cat_id in sc_val:\n                print('    id {}: {}'.format(cat_id, self.categories[cat_id]['name']))\n            print('')\n    \n    def display_image(self, image_id, show_polys=True, show_bbox=True, show_labels=True, show_crowds=True, use_url=False):\n        print('Image:')\n        print('======')\n        if image_id == 'random':\n            image_id = random.choice(list(self.images.keys()))\n        \n        # Print the image info\n        image = self.images[image_id]\n        for key, val in image.items():\n            print('  {}: {}'.format(key, val))\n            \n        # Open the image\n        if use_url:\n            image_path = image['coco_url']\n            response = requests.get(image_path)\n            image = Image.open(BytesIO(response.content))\n            \n        else:\n            image_path = os.path.join(self.image_dir, image['file_name'])\n            image = Image.open(image_path)\n            \n        buffered = BytesIO()\n        image.save(buffered, format=\"PNG\")\n        img_str = \"data:image/png;base64, \" + base64.b64encode(buffered.getvalue()).decode()\n        \n        # Calculate the size and adjusted display size\n        max_width = 900\n        image_width, image_height = image.size\n        adjusted_width = min(image_width, max_width)\n        adjusted_ratio = adjusted_width / image_width\n        adjusted_height = adjusted_ratio * image_height\n        \n        # Create list of polygons to be drawn\n        polygons = {}\n        bbox_polygons = {}\n        rle_regions = {}\n        poly_colors = {}\n        labels = {}\n        print('  segmentations ({}):'.format(len(self.segmentations[image_id])))\n        for i, segm in enumerate(self.segmentations[image_id]):\n            polygons_list = []\n            if segm['iscrowd'] != 0:\n                # Gotta decode the RLE\n                px = 0\n                x, y = 0, 0\n                rle_list = []\n                for j, counts in enumerate(segm['segmentation']['counts']):\n                    if j % 2 == 0:\n                        # Empty pixels\n                        px += counts\n                    else:\n                        # Need to draw on these pixels, since we are drawing in vector form,\n                        # we need to draw horizontal lines on the image\n                        x_start = trunc(trunc(px / image_height) * adjusted_ratio)\n                        y_start = trunc(px % image_height * adjusted_ratio)\n                        px += counts\n                        x_end = trunc(trunc(px / image_height) * adjusted_ratio)\n                        y_end = trunc(px % image_height * adjusted_ratio)\n                        if x_end == x_start:\n                            # This is only on one line\n                            rle_list.append({'x': x_start, 'y': y_start, 'width': 1 , 'height': (y_end - y_start)})\n                        if x_end > x_start:\n                            # This spans more than one line\n                            # Insert top line first\n                            rle_list.append({'x': x_start, 'y': y_start, 'width': 1, 'height': (image_height - y_start)})\n                            \n                            # Insert middle lines if needed\n                            lines_spanned = x_end - x_start + 1 # total number of lines spanned\n                            full_lines_to_insert = lines_spanned - 2\n                            if full_lines_to_insert > 0:\n                                full_lines_to_insert = trunc(full_lines_to_insert * adjusted_ratio)\n                                rle_list.append({'x': (x_start + 1), 'y': 0, 'width': full_lines_to_insert, 'height': image_height})\n                                \n                            # Insert bottom line\n                            rle_list.append({'x': x_end, 'y': 0, 'width': 1, 'height': y_end})\n                if len(rle_list) > 0:\n                    rle_regions[segm['id']] = rle_list  \n            else:\n                # Add the polygon segmentation\n                for segmentation_points in segm['segmentation']:\n                    segmentation_points = np.multiply(segmentation_points, adjusted_ratio).astype(int)\n                    polygons_list.append(str(segmentation_points).lstrip('[').rstrip(']'))\n\n            polygons[segm['id']] = polygons_list\n\n            if i < len(self.colors):\n                poly_colors[segm['id']] = self.colors[i]\n            else:\n                poly_colors[segm['id']] = 'white'\n            \n            bbox = segm['bbox']\n            bbox_points = [bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1],\n                           bbox[0] + bbox[2], bbox[1] + bbox[3], bbox[0], bbox[1] + bbox[3],\n                           bbox[0], bbox[1]]\n            bbox_points = np.multiply(bbox_points, adjusted_ratio).astype(int)\n            bbox_polygons[segm['id']] = str(bbox_points).lstrip('[').rstrip(']')\n            \n            labels[segm['id']] = (self.categories[segm['category_id']]['name'], (bbox_points[0], bbox_points[1] - 4))\n            \n            # Print details\n            print('    {}:{}:{}'.format(segm['id'], poly_colors[segm['id']], self.categories[segm['category_id']]))\n\n        # Draw segmentation polygons on image\n        html = '<div class=\"container\" style=\"position:relative;\">'\n        html += '<img src=\"{}\" style=\"position:relative;top:0px;left:0px;width:{}px;\">'.format(img_str, adjusted_width)\n        html += '<div class=\"svgclass\"><svg width=\"{}\" height=\"{}\">'.format(adjusted_width, adjusted_height)\n        \n        if show_polys:\n            for seg_id, points_list in polygons.items():\n                fill_color = poly_colors[seg_id]\n                stroke_color = poly_colors[seg_id]\n                for points in points_list:\n                    html += '<polygon points=\"{}\" style=\"fill:{}; stroke:{}; stroke-width:1; fill-opacity:0.5\" />'.format(points, fill_color, stroke_color)\n        \n        if show_crowds:\n            for seg_id, rect_list in rle_regions.items():\n                fill_color = poly_colors[seg_id]\n                stroke_color = poly_colors[seg_id]\n                for rect_def in rect_list:\n                    x, y = rect_def['x'], rect_def['y']\n                    w, h = rect_def['width'], rect_def['height']\n                    html += '<rect x=\"{}\" y=\"{}\" width=\"{}\" height=\"{}\" style=\"fill:{}; stroke:{}; stroke-width:1; fill-opacity:0.5; stroke-opacity:0.5\" />'.format(x, y, w, h, fill_color, stroke_color)\n            \n        if show_bbox:\n            for seg_id, points in bbox_polygons.items():\n                fill_color = poly_colors[seg_id]\n                stroke_color = poly_colors[seg_id]\n                html += '<polygon points=\"{}\" style=\"fill:{}; stroke:{}; stroke-width:1; fill-opacity:0\" />'.format(points, fill_color, stroke_color)\n                \n        if show_labels:\n            for seg_id, label in labels.items():\n                color = poly_colors[seg_id]\n                html += '<text x=\"{}\" y=\"{}\" style=\"fill:{}; font-size: 12pt;\">{}</text>'.format(label[1][0], label[1][1], color, label[0])\n                \n        html += '</svg></div>'\n        html += '</div>'\n        html += '<style>'\n        html += '.svgclass { position:absolute; top:0px; left:0px;}'\n        html += '</style>'\n        return html\n       \n    def process_info(self):\n        self.info = self.coco['info']\n    \n    def process_licenses(self):\n        self.licenses = self.coco['licenses']\n    \n    def process_categories(self):\n        self.categories = {}\n        self.super_categories = {}\n        for category in self.coco['categories']:\n            cat_id = category['id']\n            super_category = category['supercategory']\n            \n            # Add category to the categories dict\n            if cat_id not in self.categories:\n                self.categories[cat_id] = category\n            else:\n                print(\"ERROR: Skipping duplicate category id: {}\".format(category))\n\n            # Add category to super_categories dict\n            if super_category not in self.super_categories:\n                self.super_categories[super_category] = {cat_id} # Create a new set with the category id\n            else:\n                self.super_categories[super_category] |= {cat_id} # Add category id to the set\n                \n    def process_images(self):\n        self.images = {}\n        for image in self.coco['images']:\n            image_id = image['id']\n            if image_id in self.images:\n                print(\"ERROR: Skipping duplicate image id: {}\".format(image))\n            else:\n                self.images[image_id] = image\n                \n    def process_segmentations(self):\n        self.segmentations = {}\n        for segmentation in self.coco['annotations']:\n            image_id = segmentation['image_id']\n            if image_id not in self.segmentations:\n                self.segmentations[image_id] = []\n            self.segmentations[image_id].append(segmentation)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:37.234755Z","iopub.execute_input":"2022-11-26T08:48:37.235327Z","iopub.status.idle":"2022-11-26T08:48:37.276162Z","shell.execute_reply.started":"2022-11-26T08:48:37.235291Z","shell.execute_reply":"2022-11-26T08:48:37.275200Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"annotation_path = './annotations/train_coco.json'\nimage_dir = '/kaggle/input/global-wheat-detection/train/'\n\ncoco_dataset = CocoDataset(annotation_path, image_dir)\n# coco_dataset.display_info()\n# coco_dataset.display_licenses()\ncoco_dataset.display_categories()","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:37.277927Z","iopub.execute_input":"2022-11-26T08:48:37.278324Z","iopub.status.idle":"2022-11-26T08:48:37.990354Z","shell.execute_reply.started":"2022-11-26T08:48:37.278290Z","shell.execute_reply":"2022-11-26T08:48:37.989282Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"html = coco_dataset.display_image(unique_images[0], use_url=False)\nIPython.display.HTML(html)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:37.992118Z","iopub.execute_input":"2022-11-26T08:48:37.992509Z","iopub.status.idle":"2022-11-26T08:48:38.533247Z","shell.execute_reply.started":"2022-11-26T08:48:37.992471Z","shell.execute_reply":"2022-11-26T08:48:38.531661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## configファイルの書き換え\n- YOLOX-Lを使用するため、`configs/yolox/yolox_l_8x8_300e_coco.py`をベースにデータのパスなどを書き換える\n- ベースラインのため最小限の書き換えしかしていないが、augmentationなどもconfigで設定できるので試行錯誤の余地あり\n- kaggle環境のT4x2 Accelaratorの使用を前提とするため、分散学習用の設定をしている","metadata":{}},{"cell_type":"code","source":"ls mmdetection/","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:38.534827Z","iopub.execute_input":"2022-11-26T08:48:38.535255Z","iopub.status.idle":"2022-11-26T08:48:39.519558Z","shell.execute_reply.started":"2022-11-26T08:48:38.535206Z","shell.execute_reply":"2022-11-26T08:48:39.518362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile ./mmdetection/configs/yolox/custom_yolox_l_8x8_300e_coco.py\n\n_base_ = ['../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py']\n\nimg_scale = (640, 640)  # height, width\n\nload_from = 'checkpoints/yolox_l_8x8_300e_coco_20211126_140236-d3bd2b23.pth'\n\n# model settings\nmodel = dict(\n    type='YOLOX',\n    input_size=img_scale,\n    random_size_range=(15, 25),\n    random_size_interval=10,\n    backbone=dict(type='CSPDarknet', deepen_factor=1.0, widen_factor=1.0),\n    neck=dict(\n        type='YOLOXPAFPN',\n        in_channels=[256, 512, 1024],\n        out_channels=256,\n        num_csp_blocks=3),\n    bbox_head=dict(\n        type='YOLOXHead', num_classes=1, in_channels=256, feat_channels=256),\n    train_cfg=dict(assigner=dict(type='SimOTAAssigner', center_radius=2.5)),\n    # In order to align the source code, the threshold of the val phase is\n    # 0.01, and the threshold of the test phase is 0.001.\n    test_cfg=dict(score_thr=0.01, nms=dict(type='nms', iou_threshold=0.65)))\n\n# dataset settings\ndata_root = '/kaggle/input/global-wheat-detection/'\ndataset_type = 'CocoDataset'\nclasses = ('wheat',)\n\ntrain_pipeline = [\n    dict(type='Mosaic', img_scale=img_scale, pad_val=114.0),\n    dict(\n        type='RandomAffine',\n        scaling_ratio_range=(0.1, 2),\n        border=(-img_scale[0] // 2, -img_scale[1] // 2)),\n    dict(\n        type='MixUp',\n        img_scale=img_scale,\n        ratio_range=(0.8, 1.6),\n        pad_val=114.0),\n    dict(type='YOLOXHSVRandomAug'),\n    dict(type='RandomFlip', flip_ratio=0.5),\n    # According to the official implementation, multi-scale\n    # training is not considered here but in the\n    # 'mmdet/models/detectors/yolox.py'.\n    dict(type='Resize', img_scale=img_scale, keep_ratio=True),\n    dict(\n        type='Pad',\n        pad_to_square=True,\n        # If the image is three-channel, the pad value needs\n        # to be set separately for each channel.\n        pad_val=dict(img=(114.0, 114.0, 114.0))),\n    dict(type='FilterAnnotations', min_gt_bbox_wh=(1, 1), keep_empty=False),\n    dict(type='DefaultFormatBundle'),\n    dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])\n]\n\ntrain_dataset = dict(\n    type='MultiImageMixDataset',\n    dataset=dict(\n        type=dataset_type,\n        classes=classes,\n        ann_file='annotations/train_coco.json',\n        img_prefix=data_root + 'train/',\n        pipeline=[\n            dict(type='LoadImageFromFile'),\n            dict(type='LoadAnnotations', with_bbox=True)\n        ],\n        filter_empty_gt=False,\n    ),\n    pipeline=train_pipeline)\n\ntest_pipeline = [\n    dict(type='LoadImageFromFile'),\n    dict(\n        type='MultiScaleFlipAug',\n        img_scale=img_scale,\n        flip=False,\n        transforms=[\n            dict(type='Resize', keep_ratio=True),\n            dict(type='RandomFlip'),\n            dict(\n                type='Pad',\n                pad_to_square=True,\n                pad_val=dict(img=(114.0, 114.0, 114.0))),\n            dict(type='DefaultFormatBundle'),\n            dict(type='Collect', keys=['img'])\n        ])\n]\n\ntest_dataset = dict(\n    type=dataset_type,\n    classes=classes,\n    ann_file='annotations/valid_coco.json',\n    img_prefix=data_root + 'train/',\n    pipeline=test_pipeline\n)\n\ndata = dict(\n    samples_per_gpu=8,\n    workers_per_gpu=1,\n    persistent_workers=True,\n    train=train_dataset,\n    val=test_dataset,\n    test=test_dataset\n)\n\n# optimizer\n# default 8 gpu\noptimizer = dict(\n    type='SGD',\n    lr=0.001,\n    momentum=0.9,\n    weight_decay=5e-4,\n    nesterov=True,\n    paramwise_cfg=dict(norm_decay_mult=0., bias_decay_mult=0.))\noptimizer_config = dict(grad_clip=None)\n\nmax_epochs = 10\nnum_last_epochs = 0\nresume_from = None\ninterval = 1\n\n# fp16 settings \nfp16 = dict(loss_scale=512.) \n\n# learning policy\nlr_config = dict(\n    _delete_=True,\n    policy='YOLOX',\n    warmup='exp',\n    by_epoch=False,\n    warmup_by_epoch=True,\n    warmup_ratio=1,\n    warmup_iters=5,  # 5 epoch\n    num_last_epochs=num_last_epochs,\n    min_lr_ratio=0.05)\n\nrunner = dict(type='EpochBasedRunner', max_epochs=max_epochs)\n\ncustom_hooks = [\n    dict(\n        type='YOLOXModeSwitchHook',\n        num_last_epochs=num_last_epochs,\n        priority=48),\n    dict(\n        type='SyncNormHook',\n        num_last_epochs=num_last_epochs,\n        interval=interval,\n        priority=48),\n    dict(\n        type='ExpMomentumEMAHook',\n        resume_from=resume_from,\n        momentum=0.0001,\n        priority=49)\n]\ncheckpoint_config = dict(interval=interval)\nevaluation = dict(\n    save_best='auto',\n    # The evaluation interval is 'interval' when running epoch is\n    # less than ‘max_epochs - num_last_epochs’.\n    # The evaluation interval is 1 when running epoch is greater than\n    # or equal to ‘max_epochs - num_last_epochs’.\n    interval=interval,\n    dynamic_intervals=[(max_epochs - num_last_epochs, 1)],\n    metric='bbox')\nlog_config = dict(interval=50)\n\n# NOTE: `auto_scale_lr` is for automatically scaling LR,\n# USER SHOULD NOT CHANGE ITS VALUES.\n# base_batch_size = (8 GPUs) x (8 samples per GPU)\ngpu_ids = [0, 1]\nauto_scale_lr = dict(base_batch_size=len(gpu_ids) * data['samples_per_gpu'])\nfind_unused_parameters = True","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:39.521772Z","iopub.execute_input":"2022-11-26T08:48:39.522104Z","iopub.status.idle":"2022-11-26T08:48:39.533417Z","shell.execute_reply.started":"2022-11-26T08:48:39.522073Z","shell.execute_reply":"2022-11-26T08:48:39.532404Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 学習済みパラメータをダウンロード（オンライン用）\n# !wget https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_l_8x8_300e_coco/yolox_l_8x8_300e_coco_20211126_140236-d3bd2b23.pth -P ./checkpoints","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:39.540335Z","iopub.execute_input":"2022-11-26T08:48:39.541194Z","iopub.status.idle":"2022-11-26T08:48:39.556275Z","shell.execute_reply.started":"2022-11-26T08:48:39.541158Z","shell.execute_reply":"2022-11-26T08:48:39.555383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 学習済みパラメータをダウンロード（オフライン用）\nos.makedirs('./checkpoints', exist_ok=True)\n!cp /kaggle/input/mmdetection-2-26-0/mmdetection-2-26-0/yolox_l_8x8_300e_coco_20211126_140236-d3bd2b23.pth ./checkpoints/","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:48:39.557585Z","iopub.execute_input":"2022-11-26T08:48:39.558438Z","iopub.status.idle":"2022-11-26T08:48:43.530444Z","shell.execute_reply.started":"2022-11-26T08:48:39.558399Z","shell.execute_reply":"2022-11-26T08:48:43.528961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 学習スクリプト実行\n# !python ./mmdetection/tools/train.py ./mmdetection/configs/yolox/custom_yolox_l_8x8_300e_coco.py     # train with a single GPU\n!bash ./mmdetection/tools/dist_train.sh ./mmdetection/configs/yolox/custom_yolox_l_8x8_300e_coco.py 2  # train with multiple GPUs","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2022-11-26T08:48:43.532803Z","iopub.execute_input":"2022-11-26T08:48:43.533195Z","iopub.status.idle":"2022-11-26T08:54:41.382016Z","shell.execute_reply.started":"2022-11-26T08:48:43.533159Z","shell.execute_reply":"2022-11-26T08:54:41.380825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 推論","metadata":{}},{"cell_type":"code","source":"# 学習終了後、モデルが以下のディレクトリに出力される\nos.listdir('./work_dirs/custom_yolox_l_8x8_300e_coco/')","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:54:41.383783Z","iopub.execute_input":"2022-11-26T08:54:41.384525Z","iopub.status.idle":"2022-11-26T08:54:41.393887Z","shell.execute_reply.started":"2022-11-26T08:54:41.384481Z","shell.execute_reply":"2022-11-26T08:54:41.392808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.makedirs('predictions', exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:54:41.395722Z","iopub.execute_input":"2022-11-26T08:54:41.396097Z","iopub.status.idle":"2022-11-26T08:54:41.406182Z","shell.execute_reply.started":"2022-11-26T08:54:41.396063Z","shell.execute_reply":"2022-11-26T08:54:41.405175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from mmdet.apis import init_detector, inference_detector","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:54:41.407646Z","iopub.execute_input":"2022-11-26T08:54:41.408403Z","iopub.status.idle":"2022-11-26T08:54:44.372841Z","shell.execute_reply.started":"2022-11-26T08:54:41.408369Z","shell.execute_reply":"2022-11-26T08:54:44.371683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"config_path = './work_dirs/custom_yolox_l_8x8_300e_coco/custom_yolox_l_8x8_300e_coco.py'\ncheckpoint_path = glob('./work_dirs/custom_yolox_l_8x8_300e_coco/best_bbox_mAP_epoch_*.pth')[0]\ntest_images = sorted(glob(DATA_ROOT + '/test/*.jpg'))\nmodel = init_detector(config_path, checkpoint_path, device='cuda')","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2022-11-26T08:54:44.374958Z","iopub.execute_input":"2022-11-26T08:54:44.375658Z","iopub.status.idle":"2022-11-26T08:54:48.531960Z","shell.execute_reply.started":"2022-11-26T08:54:44.375586Z","shell.execute_reply":"2022-11-26T08:54:48.530881Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 提出用ファイル作成","metadata":{}},{"cell_type":"code","source":"submission = {'image_id':[], 'PredictionString':[]}\nfor image_path in tqdm(test_images):\n    predictions = inference_detector(model, image_path)[0]\n    if len(predictions) == 0:\n        # 検出結果がないときはスキップする\n        continue\n    \n    prediction_string = ''\n    for pred in predictions:\n        pred = [ max(p, 0) for p in pred ]\n        score = pred[4]\n        if score >= 0.6:  # 一定以上の確信度の予測を出力する\n            xmin, ymin, xmax, ymax =  [ int(p) for p in pred[:4] ]\n            width = xmax - xmin\n            height = ymax - ymin\n            prediction_string += f'{score} {xmin} {ymin} {width} {height} '\n    submission['image_id'].append(os.path.splitext(os.path.basename(image_path))[0])\n    submission['PredictionString'].append(prediction_string)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:54:48.533279Z","iopub.execute_input":"2022-11-26T08:54:48.533980Z","iopub.status.idle":"2022-11-26T08:54:50.390252Z","shell.execute_reply.started":"2022-11-26T08:54:48.533942Z","shell.execute_reply":"2022-11-26T08:54:50.389189Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df = pd.DataFrame(submission)\nsubmission_df","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:54:50.391607Z","iopub.execute_input":"2022-11-26T08:54:50.392569Z","iopub.status.idle":"2022-11-26T08:54:50.407866Z","shell.execute_reply.started":"2022-11-26T08:54:50.392531Z","shell.execute_reply":"2022-11-26T08:54:50.406917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## テスト画像の一部の予測結果を可視化","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport cv2\nfig, ax = plt.subplots(3, 3, figsize=(30, 30))\nDIR_TEST = '../input/global-wheat-detection/test'\n\nfor i in range(3):\n    for j in range(3):\n        num = j * 3 + i\n        img_pth = os.path.join(DIR_TEST, submission_df.image_id[num]+'.jpg')\n        image = cv2.imread(img_pth)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        bbox = [ float(x) for x in submission_df['PredictionString'].iloc[num].split(' ') if len(x) > 0 ]\n        boxes = []\n        ii = 0\n        while ii < len(bbox)//5:\n            boxes.append([int(bbox[ii+1]),int(bbox[ii+2]),int(bbox[ii+3]),int(bbox[ii+4]),float(bbox[ii])])\n            ii += 5\n        boxes = np.array(boxes)\n        for box in boxes.astype(float):\n            cv2.rectangle(image,(int(box[0]), int(box[1])),(int(box[0])+int(box[2]),  int(box[1])+int(box[3])),(255, 0, 0), 3)\n            cv2.putText(image, str(box[4]), (int(box[0]), int(box[1])+20), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,0,0), 2)\n        ax[i][j].imshow(image)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:54:50.409466Z","iopub.execute_input":"2022-11-26T08:54:50.410351Z","iopub.status.idle":"2022-11-26T08:54:55.046444Z","shell.execute_reply.started":"2022-11-26T08:54:50.410317Z","shell.execute_reply":"2022-11-26T08:54:55.043716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!rm -rf annotations checkpoints mmdetection\nsubmission_df.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-11-26T08:54:55.048524Z","iopub.execute_input":"2022-11-26T08:54:55.049455Z","iopub.status.idle":"2022-11-26T08:54:56.230360Z","shell.execute_reply.started":"2022-11-26T08:54:55.049354Z","shell.execute_reply":"2022-11-26T08:54:56.229092Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}