【物体検出】2万種類の物体検出ができるDeticを試してみる　〜デモからテストまで〜

Meta(旧 Facebook)が2022年1月に発表した新しい物体検出器であるDetic(Detecting Twenty-thousand Classes using Image-level Supervision)を試していきます。
今回は公式のチュートリアルに沿って実装方法を紹介します。
Google colabを使用して簡単に物体検出のモデルを実装することができますので、ぜひ最後までご覧ください。
# Deticとは
Detic(Detecting Twenty-thousand Classes using Image-level Supervision)とはMeta(旧 Facebook)が発表した新しい物体検出器です。
画像分類データセットを使った物体検出器のトレーニングを可能とする事で、物体検出の検出分類数(vocabulary)を大幅に拡張しました。
これにより、物体検出時にアンカーボックスを用いる必要がなく、画像分類のデータセットで物体検出のトレーニングが可能となりました。
出典と公式実装は以下のリンクよりご確認いただけます。

https://arxiv.org/abs/2201.02605

https://github.com/facebookresearch/Detic

# Deticの特徴
アノテーション済みのマスク画像データを提供し物体検出の学習に用いるデータセットは、画像中の検出対象物の座標を与える必要があります。このため画像分類に比べると、物体検出のデータセット作成の方が手間がかかります。
Deticでは画像分類のデータセットで物体検出のトレーニングが可能となったため、大量のデータセット（ImageNet）から大量の物体を学習しています。これにより、従来の手法よりも２万種類以上にも及ぶ多くの物体を検出することが可能となります。またCLIPを使用して、任意のクラス名に対して物体検出も実現しています。

# Deticの導入
早速Deticを使用していきましょう。
以下、Google colab環境で進めていきます。
まずはGPUを使用できるように設定をします。

「ランタイムのタイプを変更」→「ハードウェアアクセラレータ」をGPUに変更

```ruby:python
from google.colab import drive
drive.mount('/content/drive')
%cd ./drive/MyDrive
```

```ruby:python
import torch
TORCH_VERSION = ".".join(torch.__version__.split(".")[:2])
CUDA_VERSION = torch.__version__.split("+")[-1]
print("torch: ", TORCH_VERSION, "; cuda: ", CUDA_VERSION)

```

detectron2をインストールします。

```ruby:python
!pip install detectron2 -f https://dl.fbaipublicfiles.com/detectron2/wheels/$CUDA_VERSION/torch$TORCH_VERSION/index.html

```

公式よりcloneしてきます。




```ruby:python
!git clone https://github.com/facebookresearch/Detic.git --recurse-submodules
%cd Detic
!pip install -r requirements.txt
```

以上で導入が完了しました。

# 実際にテストしてみる
ここからは実際にサンプル画像を用いてテストをしてみます。

```ruby:python
import detectron2
from detectron2.utils.logger import setup_logger
setup_logger()

import sys
import numpy as np
import os, json, cv2, random
from google.colab.patches import cv2_imshow

from detectron2 import model_zoo
from detectron2.engine import DefaultPredictor
from detectron2.config import get_cfg
from detectron2.utils.visualizer import Visualizer
from detectron2.data import MetadataCatalog, DatasetCatalog


sys.path.insert(0, 'third_party/CenterNet2/projects/CenterNet2/')
from centernet.config import add_centernet_config
from detic.config import add_detic_config
from detic.modeling.utils import reset_cls_test
```
学習済モデルをロードします。


```ruby:python
cfg = get_cfg()
add_centernet_config(cfg)
add_detic_config(cfg)
cfg.merge_from_file("configs/Detic_LCOCOI21k_CLIP_SwinB_896b32_4x_ft4x_max-size.yaml")
cfg.MODEL.WEIGHTS = 'https://dl.fbaipublicfiles.com/detic/Detic_LCOCOI21k_CLIP_SwinB_896b32_4x_ft4x_max-size.pth'
cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5  # set threshold for this model
cfg.MODEL.ROI_BOX_HEAD.ZEROSHOT_WEIGHT_PATH = 'rand'
cfg.MODEL.ROI_HEADS.ONE_CLASS_PER_PROPOSAL = True # For better visualization purpose. Set to False for all classes.
predictor = DefaultPredictor(cfg)
```

まずはvocabularyをロードし、検出できる対象を全て検出してみましょう。

```ruby:python
BUILDIN_CLASSIFIER = {
    'lvis': 'datasets/metadata/lvis_v1_clip_a+cname.npy',
    'objects365': 'datasets/metadata/o365_clip_a+cnamefix.npy',
    'openimages': 'datasets/metadata/oid_clip_a+cname.npy',
    'coco': 'datasets/metadata/coco_clip_a+cname.npy',
}

BUILDIN_METADATA_PATH = {
    'lvis': 'lvis_v1_val',
    'objects365': 'objects365_v2_val',
    'openimages': 'oid_val_expanded',
    'coco': 'coco_2017_val',
}

vocabulary = 'lvis' # change to 'lvis', 'objects365', 'openimages', or 'coco'
metadata = MetadataCatalog.get(BUILDIN_METADATA_PATH[vocabulary])
classifier = BUILDIN_CLASSIFIER[vocabulary]
num_classes = len(metadata.thing_classes)
reset_cls_test(predictor.model, classifier, num_classes)
```

テストする画像を用意します。
ここではチュートリアルに従ってテスト画像を準備します。

```ruby:python
!wget https://web.eecs.umich.edu/~fouhey/fun/desk/desk.jpg
im = cv2.imread("./desk.jpg")
```
この画像に対してテストをしてみます。
```ruby:python
outputs = predictor(im)
v = Visualizer(im[:, :, ::-1], metadata)
out = v.draw_instance_predictions(outputs["instances"].to("cpu"))
cv2_imshow(out.get_image()[:, :, ::-1])
```
テスト結果を表示してみます。

![result-1024x768.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1559455/d6e3e468-b6d6-b1ed-79cb-10c7fbc703c3.png)

画像の中にある多くの物体が検出できていますね。

# 検出対象を指定する
多くの物体を検出できることができる反面、検出対象を限定したいケースもあるかと思います。
ここでは、検出対象を指定してテストしてみます。

```ruby:python
from detic.modeling.text.text_encoder import build_text_encoder
def get_clip_embeddings(vocabulary, prompt='a '):
    text_encoder = build_text_encoder(pretrain=True)
    text_encoder.eval()
    texts = [prompt + x for x in vocabulary]
    emb = text_encoder(texts).detach().permute(1, 0).contiguous().cpu()
    return emb

vocabulary = 'custom'
metadata = MetadataCatalog.get("__unused")
metadata.thing_classes = ['headphone', 'webcam', 'paper', 'coffee'] # Change here to try your own vocabularies!
classifier = get_clip_embeddings(metadata.thing_classes)
num_classes = len(metadata.thing_classes)
reset_cls_test(predictor.model, classifier, num_classes)
output_score_threshold = 0.3
for cascade_stages in range(len(predictor.model.roi_heads.box_predictor)):
    predictor.model.roi_heads.box_predictor[cascade_stages].test_score_thresh = output_score_threshold

```
検出対象を「'headphone', 'webcam', 'paper', 'coffee'」に指定しました。
先ほどと同様にテストをしてみましょう。

```ruby:python
outputs = predictor(im)
v = Visualizer(im[:, :, ::-1], metadata)
out = v.draw_instance_predictions(outputs["instances"].to("cpu"))
cv2_imshow(out.get_image()[:, :, ::-1])
```
![result2-1024x768.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1559455/49af5d42-70b0-733c-4575-0d354264c3b1.png)
指定した対象物のみ検出することができました。

# コマンドからオリジナル画像をテスト

最後にオリジナル画像でテストをしてみます。
コマンドからテストを実行します。
まずは学習済モデルをダウンロードしましょう。

```ruby:python
!mkdir models
!wget https://dl.fbaipublicfiles.com/detic/Detic_LCOCOI21k_CLIP_SwinB_896b32_4x_ft4x_max-size.pth -O models/Detic_LCOCOI21k_CLIP_SwinB_896b32_4x_ft4x_max-size.pth
!wget https://web.eecs.umich.edu/~fouhey/fun/desk/desk.jpg
```

あとはコマンドから実行してみましょう。
「--input test.jpg 」の引数部分を実際に使用する画像に書き換えてください。




```ruby:python
!python demo.py --config-file configs/Detic_LCOCOI21k_CLIP_SwinB_896b32_4x_ft4x_max-size.yaml --input test.jpg --output out.jpg --vocabulary lvis --opts MODEL.WEIGHTS models/Detic_LCOCOI21k_CLIP_SwinB_896b32_4x_ft4x_max-size.pth
```

![out3-1-1024x575.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1559455/eabc9ec8-f247-1f58-19dc-296a5825480a.jpeg)
大きな物体だけでなく、小さな物体に対しても精度良く検出できていることがわかります。

なお、画像だけでなく動画でも検出ができます。

（参考）

https://youtu.be/Nr_BRW8s9iI

# まとめ
最後までご覧いただきありがとうございます。
今回はDeticの実装方法を紹介しました。
データセットの確保を容易にしつつ、精度良く物体検出が可能となりました。物体検出データセット作成のためのアノテーション作業はとても負荷が大きい作業です。アノテーションが不要となり、画像分類のデータセットでできるようになると、さらに物体検出が身近なものになりそうですね。

なお、今回のベースとなっているDetectron2の扱い方は以下の記事で紹介しております。

https://tt-tsukumochi.com/archives/1131

Deticの検出結果のスコアや座標の出力方法についても紹介しておりますので、ぜひご覧ください。

https://tt-tsukumochi.com/archives/1198