SSD: Single Shot MultiBox Detector 高速リアルタイム物体検出デモをKerasで試す

リアルタイムに物体検出するのってどうやるんだろう？と思い調べてみたら、想像以上に高機能なモデルが公開されていたので試してみました。こんな感じです。

![7.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/6a2b7ef2-7141-89ad-80b2-e1fed3f65afb.gif)


自動運転で良く見るようなリアルタイムの物体認識をしています。このモデルは「Single Shot MultiBox Detector（SSD）」という深層学習モデルで、Kerasで動いています。

環境さえ整えればレポジトリをクローンして簡単に実行できます。今回はデモの実行方法をまとめてみます。

## 環境

ちょっと古いiMacにUbuntu16.04を入れたものを使いました。詳しくは[このへん](http://qiita.com/PonDad/items/ae27364ce2e4734c7588)とか[このへん](http://qiita.com/PonDad/items/cbef5dca04a1c1a201b0)をご参照ください。

##SSD: Single Shot MultiBox Detector

深層学習を利用したリアルタイムの物体検出は次々と新しい技術が公開されているようです。ざっと調べたところ、R-CNN、Fast R-CNN、Faster R-CNN…。どれだけ早くなるねん。って感じですが、とにかくどんどん早くなっている様です。今回試してみたSSDというモデルはそれらと比較してももっと速い。というモデルだそうです。

[weiliu89/caffe - GitHub](https://github.com/weiliu89/caffe/tree/ssd)

もとはCaffeで実装されています。モデルに関しては、スライドを見ても全くわからなかったので解説動画をみてみました。

![Screenshot from 2017-02-13 22-19-53.png](https://qiita-image-store.s3.amazonaws.com/0/47128/a5b5c850-d716-121c-c347-4b439c74a7a9.png)

[SSD: Single Shot MultiBox Detector (How it works) -YouTube](https://www.youtube.com/watch?v=P8e-G-Mhx4k)

畳み込みニューラルネットワークと並行して

![Screenshot from 2017-02-13 22-26-53.png](https://qiita-image-store.s3.amazonaws.com/0/47128/66d481de-1e8d-6594-3b4d-6fe8186073ec.png)

[SSD: Single Shot MultiBox Detector (How it works) -YouTube](https://www.youtube.com/watch?v=P8e-G-Mhx4k)

別の処理をしているようです。画像全体の検出位置をスライドしながら物体認識を並行して行っているのでしょうか。

ここは「すごーい」とだけつぶやいて、まずはデモを試してみることにします。

## ssd_keras

上記のモデルをKerasで実装したものが公開されています。

[rykov8/ssd_keras](https://github.com/rykov8/ssd_keras)

こちらのレポジトリ、学習済みモデルがダウンロードすることが出来るので長い学習時間を掛けずにすぐにデモを試してみることが出来ます。

実際にやってみましょう。

```bash
$ git clone https://github.com/rykov8/ssd_keras.git
$ cd ssd_keras
```
まずレポジトリをクローンした後に、学習済みモデルをダウンロードします。

![Screenshot from 2017-02-13 21-40-16.png](https://qiita-image-store.s3.amazonaws.com/0/47128/8f569e4a-0f03-1d2c-0253-cd69e6984b66.png)

`weights_SSD300.hdf5`をダウンロードし、ホームディレクトリに置きます。

`pics`フォルダにサンプル画像があるので、自分の試してみたい画像を設置します。

```bash
$ jupyter notebook
```
私はジュピターノートブックを開いて、`SSD.ipynb`をコピペしながら試してみました。

![Screenshot from 2017-02-13 21-39-21.png](https://qiita-image-store.s3.amazonaws.com/0/47128/3f67c1e7-6ec0-43a6-a1fb-9981d40815d3.png)

ラベルは21種類ですね。画像のパスを自分の認識させたいものに変更して実行してきます。

![Screenshot from 2017-02-13 21-38-47.png](https://qiita-image-store.s3.amazonaws.com/0/47128/9595eb4e-c3c8-89b7-1111-36e2869e5ed7.png)

飛行機とかバスをきちんと認識しています。

![Screenshot from 2017-02-13 21-39-01.png](https://qiita-image-store.s3.amazonaws.com/0/47128/9ce0e4dd-f1d4-26a8-d412-3f0ab5436c4c.png)

椅子とか、ダイニングテーブル、モニターなんかも認識しています。

## リアルタイム物体検出

OpenCV3.1を利用したリアルタイム物体検出のサンプルも試すことが出来ます。

```bash
$ cd testing_utils
$ python3 videotest_example.py
```
で実行できます。1箇所エラーコードがあるのでそこは修正してください。

```python:testing_utils/videotest.py
# 87行目
vidw = vid.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)
vidh = vid.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)

# 以下に修正してください
vidw = vid.get(cv2.CAP_PROP_FRAME_WIDTH)
vidh = vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
```
これで動くはずです。物体検出は同じファイルの65行目を見ると、60%以上で正と判断しています。

物体検出結果をコンソールでも確認したい場合は

```python:testing_utils/videotest.py
# 162行目に以下を追加
print(text)
```
これで確認することが出来ます。

動画ファイルの指定は`videotest_example.py`で行えます。

```python:testing_utils/videotest_example.py
# 24行目
vid_test.run('path/to/your/video.mkv')
```
このビデオパスを任意のものに指定します。OpenCVはffmpegを有効化して動画の読み取りが出来るようにしておきます。

## やってみた

以下のフリー動画サイトよりいくつか動画をダウンロードして試してみました。

[PEXELS VIDEOS](https://videos.pexels.com/)

![2.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/032c9a36-cbc3-0de3-3615-dd8e6eb5158b.gif)

重なりあう人に紛れた車をきちんと検出しています。

![3.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/ec722152-3c5f-07e2-d97d-3bb7ab9588ff.gif)

こちらも重なりあう自転車と、ランニングする人を検出しています。よく足だけで人と判断できるなと思いますが。

![5.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/8381b698-081e-9538-0079-7d1024944d70.gif)

ダイニングテーブル、椅子、手から人物を検出しています。

![6.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/14655400-433e-1612-7835-11febab25790.gif)

モニター、椅子の後ろに見える人物も検出しています。学習モデルは画像サイズ300x300で学習している様ですが、かなりの精度だと思います。

## まとめ

「高速」といったものの、画像認識は2~3FPS（毎秒2~3フレーム）と少々残念な結果ですが、これは私の環境（iMacのGPUを使用）での結果なので、高性能なGPUを使えばもっと速くなると思います。

転移学習を使って自分の用意した画像も学習させてみたいですね。ではまた。

---

## 追記（2018/2/22）

メインPCをMacからWindowsに変えたので改めて試してみました。

- Ubuntu 16.04
- GeForce® GTX 1080 Ti 
- cuda-9.0
- cuDNN v7.0.5 
- Python 3.5.2 
- tensorflow-gpu 1.5.0
- keras 2.1.4

@yabuchin_y　さんのコメントにある様に

[keras 2.0 concat error in ssd.train ipython #60](https://github.com/rykov8/ssd_keras/issues/60)

を参照してメソッド名を変更しました。

![output-2.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/501f43ad-65fd-7a7d-b826-2d12e9d2a058.gif)

iMacのGPUで2〜3FPSだったのですが、20FPS位とほぼリアルタイムに物体検出が出来ています。

ようやく「高速リアルタイム物体検出」を実行できてちょっと満足です。（1年後しになりましたが）

エッジデバイスでどれくらい速度が出せるかまだ分からないので、Movidius™NCS＆RaspberryPiで今度試してみたいと思います。では。