カスタムデータセットにおけるbboxの最適化[COCO format]

# カスタムデータセットにおけるbboxの最適化

maskrcnnなどにおいて自己データを用いて推論することがある場合に、カテゴリーが１種類くらいであれば最適化が出来ます。

例えば縦と横があらかじめ決まっていたり、サイズが推論する前からおおよそ分かっているのであれば、アンカーボックスを生成する際に
その形に近いものをつくってあげることで計算コストと精度の向上につながります。

FPN層においてアンカーボックスが作成されるときに

初期のサイズは(0.5,1.0,2.0)
アスペクト比率は(32, 64, 128, 256, 512)

です。(facebook/maskrcnn-benchmarkの場合)

どのサイズがいいのかを推定するために、アノテーションしたmaskのポリゴンの情報を用いて面積とアスペクト比率をヒストグラムに直します。

```Python
import IPython
import os
import json
import random
import numpy as np
import requests
from io import BytesIO
from math import trunc
from PIL import Image as PILImage
from PIL import ImageDraw as PILImageDraw
import base64
import pandas as pd
import numpy as np


# Load the dataset json
class CocoDataset():
    def __init__(self, annotation_path, image_dir):
        self.annotation_path = annotation_path
        self.image_dir = image_dir
        self.colors = colors = ['blue', 'purple', 'red', 'green', 'orange', 'salmon', 'pink', 'gold',
                                'orchid', 'slateblue', 'limegreen', 'seagreen', 'darkgreen', 'olive',
                               'teal', 'aquamarine', 'steelblue', 'powderblue', 'dodgerblue', 'navy',
                               'magenta', 'sienna', 'maroon']

        json_file = open(self.annotation_path)
        self.coco = json.load(json_file)



        json_file.close()

        self.process_categories()
        self.process_images()
        self.process_segmentations()


    def display_info(self):
        print('Dataset Info:')
        print('=============')
        for key, item in self.info.items():
            print('  {}: {}'.format(key, item))

        requirements = [['description', str],
                        ['url', str],
                        ['version', str],
                        ['year', int],
                        ['contributor', str],
                        ['date_created', str]]
        for req, req_type in requirements:
            if req not in self.info:
                print('ERROR: {} is missing'.format(req))
            elif type(self.info[req]) != req_type:
                print('ERROR: {} should be type {}'.format(req, str(req_type)))
        print('')


    def display_licenses(self):
        print('Licenses:')
        print('=========')

        requirements = [['id', int],
                        ['url', str],
                        ['name', str]]
        for license in self.licenses:
            for key, item in license.items():
                print('  {}: {}'.format(key, item))
            for req, req_type in requirements:
                if req not in license:
                    print('ERROR: {} is missing'.format(req))
                elif type(license[req]) != req_type:
                    print('ERROR: {} should be type {}'.format(req, str(req_type)))
            print('')
        print('')

    def display_categories(self):
        print('Categories:')
        print('=========')
        for sc_key, sc_val in self.super_categories.items():
            print('  super_category: {}'.format(sc_key))
            for cat_id in sc_val:
                print('    id {}: {}'.format(cat_id, self.categories[cat_id]['name']))
            print('')

    def print_segmentation(self):
        self.segmentations = {}
        data = []
        for segmentation in self.coco['annotations']:
            area = segmentation['area']
            print(area)
            bbox = segmentation['bbox']
            aspect = bbox[2]/bbox[3]
            print(bbox)
            print(aspect)
            data.append([area,aspect])
        df = pd.DataFrame(data)
        df.to_csv('out.csv')
        print(df)


annotation_path = "アノテーションしたcocoのjsonファイルのパス"
image_dir = '画像が保存されているフォルダのパス'
coco_dataset = CocoDataset(annotation_path, image_dir)
coco_dataset.print_segmentation()
```

これを実行すると以下のように面積とアスペクト比率がまとまったcsvが手に入ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187470/6cc91233-fd49-1d66-5dc6-508fa68e4964.png)

あとはエクセルとかでグラフ化してあげます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187470/c5e997a7-9faf-2fe7-bbef-27ed9a1c1566.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187470/eac4e6b2-257a-597f-2106-7f6e51dcec22.png)

この場合面積は10000あたりが多いことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187470/d59d8cc8-f41f-04cb-2625-40b2710ed464.png)
なので今回だと128のアンカーサイズが重要だとわかります。

そのサイズよりおおきな256や512も出来ればあればいいこともわかるかと思います。
逆に64のサイズはほとんどないことがわかるので削除することができます。

アスペクト比率を同様に考えると、今回は縦:横が0.5のとこが非常に少ないことがわかるので
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187470/706dc155-ca44-8163-404e-b08b95c661a6.png)
あたりで設定すればいいことがわかります。

##まとめ
以上の方法でカテゴリが少ない場合などにアンカーボックスに最適なパラメータを見つけることができます。
ぜひ最適化できればいいと思います