SSD(Keras / TensorFlow)でディープラーニングによる動画の物体検出を行う

# やったこと
[前回](http://qiita.com/yampy/items/7a705acf4c6899bc11c7)に引き続き、動画の物体検出を行いました。
今回はアルゴリズムを変えて、*SSD*という物体検出アルゴリズムを使用しています。

![3.gif](https://qiita-image-store.s3.amazonaws.com/0/172399/59dee41e-ebc4-c91e-2d50-a81daad8fc7f.gif)


（動画は[PIXELS VIDEOS](https://videos.pexels.com/)よりCCライセンスの動画を使用しました。）

# SSD(Single Shot MultiBox Detector)とは
論文は[こちら(2016年)](https://arxiv.org/abs/1512.02325)。

[前回](http://qiita.com/yampy/items/7a705acf4c6899bc11c7)使用した[YOLO(You only look once)](https://arxiv.org/abs/1612.08242)アルゴリズムと同じく、畳み込みニューラルネットワーク(CNN:Convolutional Neural Network)を用いた物体検出アルゴリズムです。

他のシングルステージ方式と比較して、入力画像サイズが小さくてもSSDの精度ははるかに優れています。 
> Compared to other single stage methods, SSD has much better accuracy, even with a smaller input image size. 

300×300入力の場合、58 FPSのVOC2007テストで72.1％mAP、500×500入力では75.1％mAPを達成。
同等の最速R-CNNモデルを凌駕しています。

> For 300×300 input, SSD achieves 72.1% mAP on VOC2007 test at 58 FPS on a Nvidia Titan X and for 500×500 input, SSD achieves 75.1% mAP, outperforming a comparable state of the art Faster R-CNN model. 


論文提出時のコードは[Caffe](http://caffe.berkeleyvision.org/)で書かれています。

> Code is available at: https://github.com/weiliu89/caffe/tree/ssd .

# SSDを動かす
今回は、[Keras/TensorFlowで実装されたソース](https://github.com/rykov8/ssd_keras)を利用します。
サンプルで提供されている学習済重みデータには、[Pascal VOC Challenges 2007年](http://host.robots.ox.ac.uk/pascal/VOC/)のデータを使用しているようです。

## 環境構築
今回使用するライブラリのバージョンは、以下の通りです。
- Keras v1.2.2
- Tensorflow v1.0.0
- OpenCV v3.1.0-dev

KerasやTensorflowが古いバージョンとなっています。
環境構築するにあたりpip等で直接ライブラリのバージョンを変更することも可能ですが、今回はAnacondaの[仮想環境管理機能](https://conda.io/docs/user-guide/tasks/manage-environments.html)をを用いて設定します。
（環境構築に慣れていない方は、[こちらの記事](http://qiita.com/yampy/items/4e89cd97179bbd20f726)にpip,condaの基本的なコマンドをまとめたので参照下さい）

### インストール

ソースコードをインストールします。

```
$ git clone https://github.com/rykov8/ssd_keras.git
$ cd ssd_keras
```
### 学習済み重みデータのダウンロード
重み付きデータは[こちら](https://mega.nz/#F!7RowVLCL!q3cEVRK9jyOSB9el3SssIA)に公開されていますので、プロジェクト直下にダウンロードしておきます。

### Anaconda仮想環境設定ファイルのダウンロード

Anaconda仮想環境設定ファイル[(ykov8_ssd_keras.yaml)](https://gist.github.com/yampy/4e0fc4aea37cf0f432ab243d76285e98)を作成しましたので、こちらをプロジェクト直下にダウンロードします。

### 仮想環境への切り替え

ダウンロードした設定ファイルを用いて仮想環境へ切り替えます。

```
conda env create -f ykov8_ssd_keras.yaml
source activate rykov8_ssd_keras
```

### ソースコードの一部修正

一部エラーコードの修正、及び検出結果の表示/書き出しの為、以下の通りソースコードを修正します。

```./testing_utils/videotest.py

# 87行目(修正)
vidw = vid.get(cv2.CAP_PROP_FRAME_WIDTH)
vidh = vid.get(cv2.CAP_PROP_FRAME_HEIGHT)

# 99行目(追加)
num_frame = 0

# 163行目(追加)
print(text)

# 183行目(追加)
print(text)
cv2.imwrite("frame_" + str('{0:04d}'.format(num_frame)) +".png", to_draw)

# 184行目(追加)
num_frame += 1
```

### 動画ファイルの指定

動画ファイルを用意して、パスを指定します。

```./testing_utils/videotest_example.py
# 24行目を修正して、自身の動画ファイルを指定
vid_test.run('./myvideo.mp4')

```

# 予測実行

`videotest_example.py`を実行します。

```
cd testing_utils/
python videotest_example.py 
```

コンソールの最後に`Done!`と表示されたら終了です。

```
（省略）
person 0.61
person 0.83
person 0.72
dog 0.78
person 0.61
person 0.75
dog 0.75
dog 0.73
Done!
```

### 検出結果をgif動画に変換

`videotest.py`に追加した183行目`cv2.imwrite()`にて出力した画像は、[ImageMagick](http://www.imagemagick.org/)のconvertコマンドでgifに変換しました。

```
convert -adjoin out/*.gif output.gif
```

# 実行結果

1. 暗い場所でも激しく動いても問題ないようです。
![3.gif](https://qiita-image-store.s3.amazonaws.com/0/172399/59dee41e-ebc4-c91e-2d50-a81daad8fc7f.gif)

2. 人混みもかなり良く認識できています。
![crowd.gif](https://qiita-image-store.s3.amazonaws.com/0/172399/fc34dad3-0327-eac5-d071-69a0676514b3.gif)

3. 水に入っているシーンでは精度が下がるようです（恐らく学習データに含まれていなかったのだと予想）
![4.gif](https://qiita-image-store.s3.amazonaws.com/0/172399/10737787-99a2-b7dc-40c0-f469e3272a3d.gif)

4. 被写体が小さすぎると認識できないようです。（こちらも学習データにおけるラベル付けの前提の問題かと）
![5.gif](https://qiita-image-store.s3.amazonaws.com/0/172399/e1f3d6d2-6671-4b17-e0f4-169f3cb401fe.gif)

# 感想
最初の環境構築に時間が取られてしまいましたが、Anacondaでの仮想環境切り替えが知見として溜まったので良かったです。
また、前回試した[YOLOの結果](http://qiita.com/yampy/items/7a705acf4c6899bc11c7)とは学習データも予測データも異なっていて比較できていないので、今度は条件を揃えた上で学習させて精度を比較していきたいと思います。
その際、学習に関してはローカルPCだと厳しいのでAWSのディープラーニング用AMIを利用したいと思います。


# 参考
- [(論文和訳) SSD:Single Shot Multibox Detector](http://qiita.com/de0ta/items/1ae60878c0e177fc7a3a)
- [本家Caffe版のGithub](https://github.com/weiliu89/caffe/tree/ssd)
- [SSD: Single Shot MultiBox Detector 高速リアルタイム物体検出デモをKerasで試す
](http://qiita.com/PonDad/items/6f9e6d9397951cadc6be)