MMDetectionで得られたInstance segmentationの結果を取り出す方法について

MMDetectionで`tools/test.py`を用いて得られたInstance segmentationの結果は`hoge.bbox.json`と`hoge.segm.json`に保存されます。(test.pyなどの詳しい使い方については[公式document](https://mmdetection.readthedocs.io/en/v2.21.0/1_exist_data_model.html)を参考にしてください。)
``` bash:MMDetectionでの推論例
$ python tools/test.py config/my_config.py checkpoints/my_config_weight.pth --options "jsonfile_prefix=results/hoge"
```

`hoge.segm.json`からsegmentation領域の情報を取り出すのに手間取ったためまとめておきます。
`inference_detector`を用いた場合も同様に取り出せます。

# 筆者環境
* WSL2 on windows11
* ubuntu 20.04
* docker
* pytorch 1.11.0+cu113
* MMDetection 2.23.0
* cuda 11.3
* GCC 7.3

# Instance segmentationの結果について
MMDetectionではInstance segmentationの結果は、`hoge.bbox.json`と`hoge.segm.json`といった`json`形式で出力されます。
Instance segmentationのtaskでは、segmentationした領域のピクセル数(≒面積)やそのポリゴンの形に興味がある場合があります。
`hoge.segm.json`にsegmentationした領域の情報が入っていることは明らかなので中身を見てみます。

```json:hoge.segm.json
{"image_id": 0, 
   "bbox": [587.4173583984375, 327.16925048828125, 326.24078369140625, 441.67816162109375], 
   "score": 0.9949996471405029, 
   "category_id": 0, 
   "segmentation": 
      {"size": [2048, 1323], 
       "counts": "f^nT1k1mm1c0@>D8H7I6I7J7I8H6J6K4L4L4L5K6J6J7I6I6K5J5L5K4L5K3N3L3N2M4M3M2N4L2N3M2N1O2M3N3M2M4M2M4M4L4L4L3M3M2O1N2N3M2N2N1O2N2O0O2N1O2O1N2N2O1N3M2O2M3N2M4M1N2O1O1N2O1O1N2O1O1N2O0O2N2O1N2N2N2O1N2N2O1N101N2O001N101O0O2O0O2O1O0O2O1O2N1N3N1O2N2N2M2O1O1O1N10001N10001O0O101O00001O00001O00010O00001O0000001O0O10001O00000O2O0001O01O000010O01O001O01O0001O0O100000000000000010O00001O001O001O001O00001O0O101O00001O000O2O00001N101O001N101O0O2O001N101O0O2O001O0O2O00001N101N101N2O0O2N101N2N101N2O0O2O1N101N2N101N2N2N2N2N3L3N2M4M2M4L4L3N2N2N2N2N101N1O2O1N2N101N2N102M2N2M4L4K6J6J5J7K5K3N3M2N2N3M2N2N3M2N3M3M4L4J7I6I6J6K5L4L4L5J5L5K7H8E:G8H9I6J8I7H8I8G8^Ob0F;Fg0VOY_ki0"}
}
{"image_id": 0, 
   "bbox": [509.74078369140625, 747.2927856445312, 271.965576171875, 288.40106201171875], 
   "score": 0.15585921704769135, 
   "category_id": 0, 
   "segmentation": 
      {"size": [2048, 1323], 
      "counts": "[k^Q16go1?A;E9D7J7M3M2M4N1N3L5L4K6J3K4M4M3N2O0O100O1O2O0O1O1O1N2O2M2O2M2O2M4L5L5K4L1O2O0O100O1O100O1O1O1N2N3M2N2N3N1O1O2N100O10000O10000O100O2O1N2N2M3M3N3L4M3L4N0O100O100O100O10000O3N1N2N101N101N100O2O0000000000000O1000001O0O2O001N101O1O00000O2O0000001O00001O1O100O1O1O1O2N1O000000000001O0O100000001O100O001O010O001O001O000O2O001O001N2O1N3M2N2N3M3L4M3L3N2O1O000O2O001O001O003L5K5L3L3N2M3N1N2SKWUNa4Uk1N2O0O100O2O0N2O2N1O2M3M3L5M2N3L3N3M3M3M2N2M3M3M3L4N2N2N2N2N2N1O2N2N2N1O2L3K6K5N2M4M6I9H9F^T[R1"}
},...
```
* image_id：画像を特定するためのid
* bbox：バウンディングボックスの形。 [中心のx座標, 中心のy座標, 幅, 高さ]の順
* score：バウンディングボックスが正しく判定されている確率
* category_id：検出した物体がどのカテゴリに属しているかを表すid
* segmentation：`RLE形式`で書かれたsegmentation領域の情報
  * size：segmentation領域の[幅,高さ]
  * counts：`RLE形式`にencodeされたポリゴン座標

image_isからcategory_idまではobject detectionのtaskでも同様に出力されます(`hoge.bbox.json`)。
今回興味があるのはsegmentationした領域なので`segmentation`の内容を読み解く必要があります。
しかしこのままでは`RLE形式`にエンコードされていて読み解けません。そのためデータ形式の変換が必要です。

# RLE形式からdecode
まず、`pycocotools`をインストールします。
```bash:pycocotoolsのインストール
$ pip install cython
$ git clone https://github.com/cocodataset/cocoapi.git
$ cd cocoapi/PythonAPI/
$ make install
$ python setup.py install
```
上記のコマンドで`pycocotools`をインストールできます。

次に`hoge.segm.json`を読み込みます。
```python:hoge.segm.jsonの読み込み
import json
json_open = open("hoge.segm.json")
json_load = json.load(json_open)
segm = json_load[0] #今回は1つだけ取り出します。
print(segm)
```
```json:segm
{"image_id": 0, 
   "bbox": [587.4173583984375, 327.16925048828125, 326.24078369140625, 441.67816162109375], 
   "score": 0.9949996471405029, 
   "category_id": 0, 
   "segmentation": 
      {"size": [2048, 1323], 
       "counts": "f^nT1k1mm1c0@>D8H7I6I7J7I8H6J6K4L4L4L5K6J6J7I6I6K5J5L5K4L5K3N3L3N2M4M3M2N4L2N3M2N1O2M3N3M2M4M2M4M4L4L4L3M3M2O1N2N3M2N2N1O2N2O0O2N1O2O1N2N2O1N3M2O2M3N2M4M1N2O1O1N2O1O1N2O1O1N2O0O2N2O1N2N2N2O1N2N2O1N101N2O001N101O0O2O0O2O1O0O2O1O2N1N3N1O2N2N2M2O1O1O1N10001N10001O0O101O00001O00001O00010O00001O0000001O0O10001O00000O2O0001O01O000010O01O001O01O0001O0O100000000000000010O00001O001O001O001O00001O0O101O00001O000O2O00001N101O001N101O0O2O001N101O0O2O001O0O2O00001N101N101N2O0O2N101N2N101N2O0O2O1N101N2N101N2N2N2N2N3L3N2M4M2M4L4L3N2N2N2N2N101N1O2O1N2N101N2N102M2N2M4L4K6J6J5J7K5K3N3M2N2N3M2N2N3M2N3M3M4L4J7I6I6J6K5L4L4L5J5L5K7H8E:G8H9I6J8I7H8I8G8^Ob0F;Fg0VOY_ki0"}
}
```
次に`pycocotools`を使ってdecodeします。

```python:pycocotoolを使ったdecode
import pycocotools.mask as mask
maskedArr = mask.decode(segm["segmentation"]) #segmentation部分のみがdecode対象のため
print(type(maskedArr)) 
print(maskedArr)
```
```:出力結果
<class `numpy.ndarray`>

[[0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 ...
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]]
```
`pycocotools.mask.decode`により`RLE形式`を`numpy形式`に変換することができました。
`maskedArr`にsegmentationした領域が描画された配列が格納されているので、あとは`opencv`などを使って面積などの特徴量を取得することができます。



## `inference_detector`を用いた場合
```python:MMDet_InstanceSeg_Tutorial.ipynb
# Use the detector to do inference
img = 'demo/demo.jpg'
result = inference_detector(model, img)
```
MMDetectionのチュートリアルノートブックでは上記のようにしてInstance segmentationを行います。`result`に結果が入っている状態です。
`result`はbboxとsegmの情報が格納されています。
```python
bbox, segm = results
print(bbox[0])
print(segm[0])
```
```:出力結果
array([[2.38843811e+02, 9.87134094e+01, 2.49885818e+02, 1.08494499e+02,
        1.03378251e-01], dtype=float32)
[array([[False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       ...,
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False]])]
```
* bbox：バウンディングボックスの内容+確率[中心のx座標, 中心のy座標, 幅, 高さ, 確率]
* segm：`bool型`で領域を描写

ここから`segm`を一度`RLE形式`にencodeします。
```python:RLE形式にencode
from mmdet.core import encode_mask_results
encode_result = (bbox, encode_mask_results(segm))
```
* encode_result[0]：bboxの配列
* encode_result[1]：`RLE形式`のsegmentation領域の情報

となるので、あとは`encode_result[1]`を上記の`pycocotools.mask.decode`を用いて変換してやれば終わりです。




# さいごに
今回はMMDetectionでのInstance segmentationの結果を取り出す方法についてまとめました。
間違いなどあればご指摘いただけると幸いです。
ありがとうございました。

# 参考URL
https://github.com/cocodataset/cocoapi/issues/476