物体検出SSD詳しく紹介(一)DefaultBoxes作成

### 背景
最近機械学習分野の物体検出を初めまして、知っているSSDモデルについて紹介したいとおもいます。ここはSSD300についてしょうかいします。
今回は第一編です、DefaultBoxesについて紹介したいとおもいます
### SSDとは
一枚の画像に含まれている複数の物体に対して、物体名と物体の領域を文字と枠から表示するものです

![SSD物体検出.014.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/beec980d-ef78-0cf1-3ef6-a0bcba178e7a.jpeg)

入力：
300*300*3の画像(3は色のチャンネル)

出力:
1. 物体を枠で囲むボックス(バウンディングボックスの座標と横幅、縦幅)
2. 物体はなんの種類なのか

###流れ
では、入力から出力まで、どのようなものが行われてるか、一言でいいます。
訓練:
![SSD物体検出.015.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/b234193e-83f0-5314-ccbc-cd25baac26d4.jpeg)

前提：検出したい物体の種類とバウンディングボックスの情報をすでに知っています。

```
1. 入力の画像(300*300*3)にたくさんのボックス(デフォルトボックス)をつけます
2. 1から用意したたくさんのデフォルトボックスからバウンディングボックスと結構重ねってあるボックスを抽出して残らせます。
3. 残らせたデフォルトボックスからバウンディングボックスへ変形させるためのoffset1情報を計算します
4. SSDモデルからoffset2を出力します
5. offset1情報とoffset2と損失関数で比較して、損失値を計算します。
6. 損失値がある程度小さくなるまで、1-4の流れをきうり返します
```

詳しくは、別の章で紹介しにいきます。ここはたくさんのデフォルトボックスをつくることが必要があることを見せたいです

推論:
![SSD物体検出.016.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/b0e785d1-ae36-0039-fee6-812cee0f41fd.jpeg)

前提：SSDもでるはすでに訓練が終わってます

```
1. 入力の画像(300*300*3)にたくさんのボックス(デフォルトボックス)をつけます
2. 1から用意したたくさんのデフォルトボックスからバウンディングボックスと結構重ねってあるボックスを抽出して残らせます。
3. SSDモデルから各デフォルトボックスの２１カテゴリに対する信頼度category2(8732,21)とOffset2(8732)情報出力します
4. 格カテゴリで、category2[i]の信頼度がある数値以上なデフォルトボックスをのこらせます
5. 各カテゴリで、残らせられたデフォルトボックスを重複チェックします
6. Offset2を使って、デフォルトボックスからバウンディングボックスに変形させます
```
詳しくは、別の章で紹介しにいきます。ここはたくさんのデフォルトボックスをつくることが必要があることを見せたいです

**つまり、SSDは最初にデフォルトボックスを用意します。SSDモデルが出力するのは、Offset情報と信頼度情報のみです。**
**デフォルトボックスの用意はSSDモデルの出力ではないです**
**訓練と推論のときに使っているデフォルトボックスが毎回同じものです**

###デフォルトボックスの生成

#### 38*38*4
まずは300*300の入力画像を３８*38分に均等にわけます、そして各分に４個のデフォルトボックスをつけます
![SSD物体検出.018.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/2f81ca46-712a-524b-424c-8d7912d9513b.jpeg)
4個のデフォルトボックスのサイズは異なります。それぞれの比例は1*1 min,1*1 big, 1*2,2*1のように理解していただけれは、大丈夫です。
このステップは38*38*4 = 5776個のデフォルトボックスを用意します

#### 19*19*6
つぎに300*300の入力画像を19*19分に均等にわけます、そして各分に６個のデフォルトボックスをつけます
![SSD物体検出.019.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/a5fcc957-1444-487c-01fb-54a41713ea4e.jpeg)
6個のデフォルトボックスのサイズは異なります。それぞれの比例は1*1 min,1*1 big, 1*2,2*1,1*3,3*1のように理解していただけれは、大丈夫です。
このステップは19*19*6 = 2166個のデフォルトボックスを用意します
感覚からみれば、19*19*6のデフォルトボックスは38*38*4デフォルトボックスより大きい、それは38*38*4のデフォルトボックスは小さいものを検出するため、19*19*6のデフォルトボックスはより大きな物体を検出できるわけです

#### 10*10*6
つぎに300*300の入力画像を10*10分に均等にわけます、そして各分に６個のデフォルトボックスをつけます
![SSD物体検出.020.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/01ca2a93-102c-2a83-5974-c7e25fbe053b.jpeg)
6個のデフォルトボックスのサイズは異なります。それぞれの比例は1*1 min,1*1 big, 1*2,2*1,1*3,3*1のように理解していただけれは、大丈夫です。
このステップは10*10*6 = 600個のデフォルトボックスを用意します
感覚からみれば、10*10*6のデフォルトボックスは38*38*4と19*19*6のデフォルトボックスより大きい、それは38*38*4と19*19*6のデフォルトボックスは小さいものを検出するため、10*10*6のデフォルトボックスはより大きな物体を検出できるわけです

#### 5*5*6
つぎに300*300の入力画像を5*5分に均等にわけます、そして各分に６個のデフォルトボックスをつけます
![SSD物体検出.021.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/b173b6da-db17-575e-628f-0b36a23f6302.jpeg)
6個のデフォルトボックスのサイズは異なります。それぞれの比例は1*1 min,1*1 big, 1*2,2*1,1*3,3*1のように理解していただけれは、大丈夫です。
このステップは5*5*6 = 150個のデフォルトボックスを用意します
さらに、感覚からみれば、5*5*6のデフォルトボックスは10*10*6のデフォルトボックスより大きい、10*10*6のデフォルトボックスより、もっと大きいな物体を検出可能なことをわかりました

#### 3*3*4
つぎに300*300の入力画像を3*3分に均等にわけます、そして各分に4個のデフォルトボックスをつけます
![SSD物体検出.022.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/f265dba1-fc88-7bec-bc15-30605a3c07db.jpeg)
4個のデフォルトボックスのサイズは異なります。それぞれの比例は1*1 min,1*1 big, 1*2,2*1のように理解していただけれは、大丈夫です。
このステップは3*3*4 = 36個のデフォルトボックスを用意します
ここからみれば、デフォルトボックスは相当大きい、相当大きいな物体を検出可能なことをわかりました。

#### 1*1*6
つぎに300*300の入力画像を1*1分に均等にわけます、そして唯一な均等分に6個のデフォルトボックスをつけます
![SSD物体検出.023.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/53de6bbb-6c86-035d-1c17-8d2c8d505b6a.jpeg)
6個のデフォルトボックスのサイズは異なります。それぞれの比例は1*1 min,1*1 big, 1*2,2*1,1*3,3*1のように理解していただけれは、大丈夫です。
このステップは1*1*6 = 6個のデフォルトボックスを用意します
ここの均等分はもとの画像のサイズと同じです、生成される６個のデフォルトボックスも大きいです。一番おおきいな物体を検出可能なことをわかりました。


**以上のように300*300の元画像を38*38,19*19,10*10,5*5,3*3,1*1に均等に分けて、各分けられた均等分から４個また６個のデフォルトボックスを生成しました**
**全体的にデフォルトボックスの数は38*38*4 + 19*19*6 + 10*10*6 + 5*5*6 + 3*3*4 + 1*1*6 = 8734個**
**各デフォルトボックス４の座標情報があります(cx,cy,width,height)**


# ソースコード
このコードは「PyTorchによる発展ディープラーニング」という本からさんこうにしました。

```python
# デフォルトボックスを出力するクラス
class DBox(object):
    def __init__(self, cfg):
        super(DBox, self).__init__()

        # 初期設定
        self.image_size = cfg['input_size']  # 画像サイズの300
        # [38, 19, …] 各sourceの特徴量マップのサイズ
        self.feature_maps = cfg['feature_maps']
        self.num_priors = len(cfg["feature_maps"])  # sourceの個数=6
        self.steps = cfg['steps']  # [8, 16, …] DBoxのピクセルサイズ
        self.min_sizes = cfg['min_sizes']  # [30, 60, …] 小さい正方形のDBoxのピクセルサイズ
        self.max_sizes = cfg['max_sizes']  # [60, 111, …] 大きい正方形のDBoxのピクセルサイズ
        self.aspect_ratios = cfg['aspect_ratios']  # 長方形のDBoxのアスペクト比

    def make_dbox_list(self):
        '''DBoxを作成する'''
        mean = []
        # 'feature_maps': [38, 19, 10, 5, 3, 1]
        for k, f in enumerate(self.feature_maps):
            for i, j in product(range(f), repeat=2):  # fまでの数で2ペアの組み合わせを作る　f_P_2 個
                # 特徴量の画像サイズ
                # 300 / 'steps': [8, 16, 32, 64, 100, 300],
                f_k = self.image_size / self.steps[k]
                print(f_k)
                # DBoxの中心座標 x,y　ただし、0～1で規格化している
                cx = (j + 0.5) / f_k
                cy = (i + 0.5) / f_k

                # アスペクト比1の小さいDBox [cx,cy, width, height]
                # 'min_sizes': [30, 60, 111, 162, 213, 264]
                s_k = self.min_sizes[k]/self.image_size
                mean += [cx, cy, s_k, s_k]

                # アスペクト比1の大きいDBox [cx,cy, width, height]
                # 'max_sizes': [45, 99, 153, 207, 261, 315],
                s_k_prime = sqrt(s_k * (self.max_sizes[k]/self.image_size))
                mean += [cx, cy, s_k_prime, s_k_prime]

                # その他のアスペクト比のdefBox [cx,cy, width, height]
                for ar in self.aspect_ratios[k]:
                    mean += [cx, cy, s_k*sqrt(ar), s_k/sqrt(ar)]
                    mean += [cx, cy, s_k/sqrt(ar), s_k*sqrt(ar)]

        # DBoxをテンソルに変換 torch.Size([8732, 4])
        output = torch.Tensor(mean).view(-1, 4)

        # DBoxが画像の外にはみ出るのを防ぐため、大きさを最小0、最大1にする
        output.clamp_(max=1, min=0)

        return output

# 動作の確認

# SSD300の設定
ssd_cfg = {
    'num_classes': 21,  # 背景クラスを含めた合計クラス数
    'input_size': 300,  # 画像の入力サイズ
    'bbox_aspect_num': [4, 6, 6, 6, 4, 4],  # 出力するDBoxのアスペクト比の種類
    'feature_maps': [38, 19, 10, 5, 3, 1],  # 各sourceの画像サイズ
    'steps': [8, 16, 32, 64, 100, 300],  # DBOXの大きさを決める
    'min_sizes': [30, 60, 111, 162, 213, 264],  # DBOXの大きさを決める
    'max_sizes': [60, 111, 162, 213, 264, 315],  # DBOXの大きさを決める
    'aspect_ratios': [[2], [2, 3], [2, 3], [2, 3], [2], [2]],
}

# DBox作成
dbox = DBox(ssd_cfg)
dbox_list = dbox.make_dbox_list()

# DBoxの出力を確認する
pd.DataFrame(dbox_list.numpy())

```

出力

```
0	1	2	3
0	0.013333	0.013333	0.100000	0.100000
1	0.013333	0.013333	0.141421	0.141421
2	0.013333	0.013333	0.141421	0.070711
3	0.013333	0.013333	0.070711	0.141421
4	0.040000	0.013333	0.100000	0.100000
...	...	...	...	...
8727	0.833333	0.833333	0.502046	1.000000
8728	0.500000	0.500000	0.880000	0.880000
8729	0.500000	0.500000	0.961249	0.961249
8730	0.500000	0.500000	1.000000	0.622254
8731	0.500000	0.500000	0.622254	1.000000
8732 rows × 4 columns
```

デフォルトボックスの範囲は0-1に納めるように処理しました。

###まとめ###
物体検出モデルSSD300の訓練と推論ともの材料になる、デフォルトボックスを紹介しました
つぎの文章はデフォルトボックスを変形させるため、SSDモデルの出力offset情報を紹介したいとおもいます。