YOLOv3+pythonで車載動画中のバイクを認識し動画にする

リアルタイムに物体認識できる[YOLO](https://pjreddie.com/darknet/yolo/)ですが、最近YOLOv3が登場しました。
これを使って、試しに簡単な動画解析プログラムを組んでみます。

さて、私は、趣味でバイクに乗っていて、ドライブレコーダー代わりに、安価なアクションカメラを付けて、動画を撮影することがあります。
ただ、車載動画は撮影しても、長すぎて全部見返すことはほとんどありません。いいシーンだけ抜き出してくれればなぁと常々思っています。

ところで、バイク同士がすれ違うときに、軽く手を降るなど挨拶する文化があり、[YAEH(ヤエー)]((https://www.jmpsa.or.jp/bikegekkan/newsrelease/e11434.html))と呼ばれています。
市街地ではほとんどないですが、ツーリングスポットでお互い集団だと時々ヤエーされたりします。
youtubeには多くの[ヤエー動画](https://www.youtube.com/results?search_query=%E3%83%A4%E3%82%A8%E3%83%BC)がアップロードされてます。

YOLOを使って、バイクとのすれ違いシーンをうまく切り出すことができれば、「YAEH（っぽいところ）動画」を自動的に作ることができそうです。見返すのも楽しくなりそうですね。

というわけでやっていき。

## 結局どういうのができるの

こういう動画から、
[![元の動画](http://img.youtube.com/vi/BxplMQvY99U/0.jpg)](http://www.youtube.com/watch?v=BxplMQvY99U)

画像中のバイクの数を認識して、こういう動画を作ります。
[![自動編集した動画](http://img.youtube.com/vi/V2IZQlkRXF0/0.jpg)](http://www.youtube.com/watch?v=V2IZQlkRXF0)

すれ違ってない部分も多いですが、バイクとのすれ違いシーンはおおかた抜き出せてるのでは、と思います。
ただ、（私が）全くヤエーしてなくて、もうちょっといいサンプルが必要ですね…

## 環境

動画から物体認識するので、高性能のGPUは必須です。
それ以外は、特に制約はありませんが参考まで。

OS:Ubuntu 16.04
GPU:GTX1080 (cuda9.1)
メモリ:16GB
動画フォーマット:MOV, 1280*720 60FPS

python3（anaconda3-5.1.0)を使いました。
また、OpenCV, CUDA, cudnnは予めインストールしています。
ちなみに[OpenCV3.4.1だとdarknetのmakeできないIssue](https://github.com/pjreddie/darknet/issues/502)があるようで、ハマりました。
OpenCVは3.4.1ではなく、3.4.0を使いましょう。

## YOLOのインストール

YOLOは公式のdarknetでの実装を利用します。公式通りなので簡単に。

[公式サイトの手順通り](https://pjreddie.com/darknet/yolo/)、darknetをcloneし、YOLOv3のconfig fileをダウンロードします。
ただし、CPUだと1画像（1フレーム）十数秒～かかるので、動画の場合は[Compiling With CUDA](https://pjreddie.com/darknet/install/#cuda)を参考に、CUDAとOpenCVを有効にして、makeします。

makeが通り、detectが利用できれば問題ないと思います。

```
./darknet detect cfg/yolov3.cfg yolov3.weights data/dog.jpg
```

## pythonからdarknetを利用する

darknetにはpython用のAPIとサンプルコードが用意されています。

* python/darknet.py
* examples/detector-scipy-opencv.py

今回、python3を利用するので、2to3を使い、darknet.pyをpython3で動くようにしておきます。

```2to3 python/darknet.py -w```

また、python/darknet.py で libdarknet.so をフルパスで指定しておかないとうまく動かないことがあります。[darknet.py](https://github.com/pjreddie/darknet/blob/0f110834f4e18b30d5f101bf8f1724c34b7b83db/python/darknet.py#L47-L48)の L47-48あたりを修正しておきましょう。

```
#lib = CDLL("/home/pjreddie/documents/darknet/libdarknet.so", RTLD_GLOBAL)
lib = CDLL("libdarknet.so", RTLD_GLOBAL)
```

## 動画解析のコードを書く

サンプルコードを参考に実装していきます。
実装コードは[ここのgistに](https://gist.github.com/ppq200/02c61b79e227d5395b9717160bb09f31)あげました。 

動画の読み込み/書き込みには[scikit-video](http://www.scikit-video.org/stable/io.html)を使っています。

認識は0.18秒/1frame 程度で終わるので、デバッグ用のファイル出力をしなければ、1秒あたり2フレームを処理すると、10分の動画で、大体5分程度で終わります。全フレーム処理すると時間がかかるので間引いて処理しています。

実装では、YOLOで認識したバイク(motorbike)の認識結果をplotlyで可視化しています。
以下は、ある動画を解析した結果で、横軸がフレーム、縦軸がそれぞれのバイクの確率を表しています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/28360/564a9044-bd0a-2551-caed-c21373d6e175.png)

頻繁に1フレームだけバイクが検出されていて、ちょっとノイズが多いですね。より作りこむ場合には、この対策が必要です。
今回は、フレームごとに確率の総和を取り、適当なしきい値(1.8など)より大きい場合に、そのフレームを抜き出して、編集してみます。
すなわち1フレーム上に、複数台のバイクが存在していると、「バイクとすれ違った」と見なしています。

総和をとった結果はこちら。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/28360/10442a52-f099-713e-bfbb-a56d93542d7b.png)

ちょこちょこ、バイクを2台〜3台程度認識していることがわかります。
作成した結果は、上に上げたとおりです。

うまくいったり、いかなかったりした認識結果のフレームをいくつか参考に載せます。
バイクの認識結果だけを囲って表示しています。

18600フレーム目。いい感じですね。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/28360/f595f79d-8db2-5b35-4ca8-279ecf56e5b8.png)

33600フレーム目。なんか2台いることになってます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/28360/f2f59332-c6e1-f3a7-3939-1a04a71451d7.png)

32940フレーム目。遠くの自動車をバイクとして認識してしまっています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/28360/35148beb-4f83-9acd-ea88-dbb91c5a1bf3.png)

28320フレーム目。自動車をバイクとしている以外は、いい感じ！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/28360/79f542e8-5abb-6049-89d5-fc2d0b6b23be.png)

29160フレーム目。手前のバイクを認識してないですね…　奥の自動車をバイクとしています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/28360/443fd7fd-b68e-62bc-c71c-b1d6187ab996.png)

ざっと結果を見てみたところ、以下のような問題がありました。

* 真後ろ・真正面のバイクを認識できない（見逃す）
* 自動車をバイクと誤認識する（逆も多い）

しきい値を変えたりすることで緩和可能かと思います。

よりよい動画作成には、
* 誤認識（ノイズ）の扱い
* 自動車と認識した場合の対応、
* 認識したフレームの前後のフレームの利用
などが必要です。
もうちょっと作りこむ必要がありますね。

## まとめとfeature work

YOLO使って、お手軽に動画解析し、動画を編集・作成するシステムを作りました。
しっかり取れることもありますが、1フレームのみでは、誤認識も多いのでうまく扱う必要がありそうです。

今回は、画像中のバイクの台数を利用して動画を作成しましたが、マスツーリングの場合には前に走っているバイクも含めてしまうので、対象物が迫ってきているのか（相対速度）を判別して「すれ違ったバイク」のみを認識する必要があるでしょう。
また、「ヤエー」そのものも認識したいです。が、体がバイクに隠れるので難易度高そうです。ポーズ推定（[GoogleのPoseNet](https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5)など）を使ってみるのもありかもしれません。

動画解析サービス、例えば、[Google Cloud Video Intelligence](https://cloud.google.com/video-intelligence/?hl=ja)とか、[Azure Video Indexer](https://azure.microsoft.com/ja-jp/services/cognitive-services/video-indexer/)とか、[Amazon Rekognition Video](https://aws.amazon.com/jp/blogs/news/launch-welcoming-amazon-rekognition-video-service/)とかバシバシ登場してきてます。これらを用いればもっと簡単に・スケールするものが作れそうです。YOLOv3やTiny-YOLOv3との精度比較もしてみる価値はあると思います。