Detectron2にバッチ単位で処理させる方法

Detectron2を使ってバッチ処理を行う
======

Detectron2とは
---
[Detectron2](https://github.com/facebookresearch/detectron2)はfacebookが公開している物体検出ライブラリです。

- 情報源
  - [公式ドキュメント](https://detectron2.readthedocs.io/en/latest/index.html)
  - [Gooble colabチュートリアル](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5)
- Detectron2の簡単な使い方はgoogle colabのチュートリアルで説明されています。Detectorn2でバッチを処理を行う前に一度このチュートリアルに目を通すことをお勧めします。**このチュートリアルでは学習ループや評価コード内で呼び出す方法が説明されておらず公式リファレンスでしか説明されていません。**そのためDataloaderから読み出した画像のテンソルのバッチを学習ループ内で利用する場合は**チュートリアルの方法ではできません。**
- また、チュートリアルの方法では入力をuint8の`np.ndarry`で`(channel, height, width)`の形でないと**動きません**（実際はuint8でなくても動くそうですが、入力をfloatなどにして試したところ検出がうまくできていなかったのでuint8にすることをお勧めします）。



インストール方法
---
###必要環境
- Linuxのpython3.6以上
- Pytorch 1.7以上、Pytorchのバージョンに対応するtorchvision
- OpenCV（検出結果を可視化する場合）

###インストール
このコマンドを実行する

```bash
$ python -m pip install 'git+https://github.com/facebookresearch/detectron2.git'
```

詳しくは[公式ドキュメント](https://detectron2.readthedocs.io/en/latest/tutorials/install.html)でインストールについて説明されています。またDetectron2はmacOSも対応しているのでmacでのインストールをする場合も同様にリンクを参考にしてください。

モデルの生成
----
ライブラリをimportします．

```python:
import torch, torchvision

import detectron2
from detectron2.utils.logger import setup_logger
setup_logger()

import numpy as np
import os, json, cv2, random

from detectron2 import model_zoo
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
from detectron2.modeling import build_model
from detectron2.checkpoint import DectionCheckpointer
```


`detectron2.config .get_config`を用いて生成したいモデルの設定を決めるには次のようにします．

- `cfg.merge_from_file(model_zoo.get_config_file(好きなモデルのyamlファイル))`
- `cfg.MODEL.WEIGHTS = model_zoo.get_checkoutpoint_url(好きなモデルのyamlファイル)`

ここで生成したいモデルを決めます。yamlファイルは[githubのconfigのディレクトリ](https://github.com/facebookresearch/detectron2/tree/main/configs)にあるファイル名のものを使用してくだいさい。またこの２つは同じファイル名にしてください。

検出の閾値の設定
- `cfg.MODEL.RETINANET.SCORE_THRESH_TEST = 好きな閾値`
- `cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 好きな閾値`
- `cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = 好きな閾値`

モデルとタスクによって閾値を代入する変数名が変わるので使用するモデルに合わせて閾値の変数を決めてください


今回はCOCO_InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yamlを使った場合の例です

```python:detectron2の設定
cfg = get_cfg()
cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5
cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml")
```

cfgに設定したモデルを生成します。
今回は学習をしないので`eval()`を使います。

```python:モデルの作成
model = build_model(cfg)
model.eval()
checkpointer = DetectionCheckpointer(model)
checkpointer.load(cfg.MODEL.WEIGHTS)
```

モデルにはバッチのテンソルではなくリストを与える
---
今回はDataloaderによってバッチが呼び出されたものとします。

- このバッチは`torch.Tensor`(float32)の`(batch, channel, height, width)`の形になっていて、前処理として全て同じ大きさにリサイズされていて正規化されていないものとします。
- カラー画像のchannelの順序はOpenCVと同じ**BGR**とします。
- 生成されたモデルには`Dict["image":torch.Tensor, "height":int, "width":int]`の**リストを入力**とします。
  - `"image"`には画像の`torch.Tensor`、`"height"`と`"width"`には**出力の大きさ**を指定します。

このTensor中の画像を一つずつ取り出し，リストにして，モデルに入力します．

```python:
with torch.no_grad():
    input_list = []
    for image in batch:  # batchはDataloaderから取り出したバッチ
        input = {"image": image, 
                 "height": height,
                 "width": width}  # heightとwidthは出力の大きさ
        input_list.append(input)
    outputs = model(input_list)
```

出力された`outputs`は[公式ドキュメント](https://detectron2.readthedocs.io/en/latest/tutorials/models.html?highlight=output#model-output-format)で説明されているフォーマットの**リストが出力**されます．