YOLO V3に、VoTTでアノテーションを付けた独自のデータセットを学習させる


YOLO V3を試すだけであれば学習済みモデルを使えばよいのですが、「やっぱり独自の物体検出をやってみたい！」ということで、独自のデータセットを学習させてみました。その内容を紹介します。

今回はグーの手を検出するだけのモデルを作成することとし、自分のグーの手を撮影し、その動画にVoTTでアノテーションを付けて学習データにしました。
![yologu1.jpg](https://qiita-image-store.s3.amazonaws.com/0/250349/c451b54d-f841-7d88-602c-23d3d05eec1e.jpeg)


# 私が実行した環境

- Windows 10 Home
- NVIDIA GTX1080Ti
- CUDA 9.1.85.3
- CuDNN 7.1.3
- Python 3.6.5
- Tensorflow-gpu 1.12.0
- Keras 2.2.4
- opencv-python 4.0.0.21
- matplotlib 3.0.2
- Pillow 5.4.1
- VoTT 1.7.2

# まずはYOLO V3を動かす
以下のPythonライブラリをインストール
tensorflow、keras、opencv-python、matplotlib、pillow

Yolo V3のソース一式をダウンロードし、任意のパスに配置
https://github.com/qqwweee/keras-yolo3

学習済みモデルをダウンロードし、"keras-yolo3"フォルダに配置
https://pjreddie.com/media/files/yolov3.weights


weightsをkeras用に変換
`python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5`


#### YOLOの実行(静止画)
`python yolo_video.py --image`
"Input image filename:"と聞かれたらファイル名を入力します。

#### YOLOの実行(動画)
`python yolo_video.py --input hoge.mp4`

上記でYOLOが動くことを確認できたら、安心して独自データセットの収集に取り組みます。


# データの収集
今回は自分のグーの手をあらゆる角度からただひたすら動画撮影します。
後で1秒毎にアノテーションを付けるので、500枚ほどの学習データを集める想定で10分ほどiPhoneの前でグーの手をグリグリ動かします。


# データにアノテーションを付ける
十分な動画が撮れたらアノテーションを付けていきます。
マイクロソフトが公開しているVoTTというアノテーションツールだと、動画へのアノテーションが楽ちんです。

VoTTをダウンロードしてインストールします。
https://github.com/Microsoft/VoTT/releases

VoTTはmp4、oggの動画ファイルに対応しています。
今回はiPhoneで撮影した動画を使ったので[ffmpeg](https://www.ffmpeg.org/)でmov形式からmp4形式に変換しました。
また、私の環境ではFullHDだと学習中にメモリ不足でエラーが発生したので解像度を下げました。
`ffmpeg -i hoge.mov -s 416x234 hoge.mp4`

あとはVoTTでこの動画ファイルを読み込んで黙々とアノテーションをつけていきます。
途中で「こんな同じ様な画像ばっかりで意味があるのか…」という考えがよぎりましたが、それほど大変でもないので最後までやりきりました。

一通りアノテーションを付け終えたらExport Tagsを選択し、"Tensorflow Pascal VOC"形式でExportします。
![vott.jpg](https://qiita-image-store.s3.amazonaws.com/0/250349/eb405f45-1a2d-1b92-e463-af91b2a4d519.jpeg)


# アノテーションをYOLO V3形式にする
YOLO V3のソースに付属している"voc_annotation.py"を使い、VOC形式のアノテーションをYOLO V3で学習する形式に変換します。

#### voc_annotation.pyの処理に合わせたファイル構成にする

"keras-yolo3"フォルダに"VOCDevkit"というフォルダを作成します。
VoTTからExportした"hoge_output"フォルダの名称を"VOC2007"に変更し、先の"VOCDevkit"フォルダ内に移動します。

"keras-yolo3/VOCDevkit/VOC2007/ImageSets/Main"フォルダに"train.txt"を作成します。
テキストファイルには"keras-yolo3/VOCDevkit/VOC2007/JPEGImages"フォルダ内の全jpgファイルを拡張子なしで列挙します。

```text:このような感じ
hoge_frame_1
hoge_frame_2
...
```

"train.txt"をコピーして"test.txt"、"val.txt"を作成します。
学習用と評価用などでデータを振り分ける場合は振り分けておいても構いません。


#### voc_annotation.pyの処理を修正する

VoTTで出力したファイルには、アノテーション位置がfloatで書かれています。そのままでは"voc_annotation.py"で読み込めないので、下記のようにfloatからintに変換するようにソースを修正します。

```python:voc_annotation.py,21行目あたり
        b = (int(float(xmlbox.find('xmin').text)),
             int(float(xmlbox.find('ymin').text)),
             int(float(xmlbox.find('xmax').text)),
             int(float(xmlbox.find('ymax').text)))
```

また、6行目あたりのclassesのリストも自分で学習させる内容に合わせて修正します。今回は1クラスなので`classes = ["gu"]`としておきました。

#### voc_annotation.pyで変換する

あとは"voc_annotation.py"を実行して変換します。
`python voc_annotation.py`

出力された"2007_train.txt"がYOLO V3用のアノテーションの形式です。

```text:このような形式
～\keras-yolo3/VOCdevkit/VOC2007/JPEGImages/hoge_frame_1.jpg 100,200,250,300,0
～\keras-yolo3/VOCdevkit/VOC2007/JPEGImages/hoge_frame_2.jpg 200,100,300,400,0
...
```


# 学習

#### 独自のクラスの列挙

"keras-yolo3\model_data"フォルダに"my_classes.txt"を作成し、自分で学習させるクラスを列挙します。
今回は`gu`とだけ記載しました。


#### train.pyの修正

"train.py"が"2007_train.txt"、"my_classes.txt"を読み込むように修正します。
17行目の"annotation_path"に"2007_train.txt"を指定します。
19行目の"classes_path"に"model_data/my_classes.txt"を指定します。

また、私の環境ではデフォルトのbatch_sizeだと学習中にメモリ不足でエラーが発生したので減らしました。
57行目と76行目の"batch_size"を32から8にしています。


#### 学習用にweightsをコンバート
学習用にweightsをコンバートします。
`python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5`


#### 学習の実行
`python train.py`

学習済みモデルは"\keras-yolo3\logs\000\trained_weights_final.h5"として生成されます。


# 学習済みモデルで物体検出
学習済みモデル、クラスを指定して起動します。

#### YOLOの実行(静止画)
`python yolo_video.py --model logs/000/trained_weights_final.h5 --classes model_data/my_classes.txt --image`

#### YOLOの実行(動画)
`python yolo_video.py --model logs/000/trained_weights_final.h5 --classes model_data/my_classes.txt --input hoge.mp4`

学習に用いたのは自分のグーだけでしたが、うちの子のグーも検出できました。
![yologu2.jpg](https://qiita-image-store.s3.amazonaws.com/0/250349/0418c697-d21c-7027-1537-8e5e9d9bce5c.jpeg)


顔でもグーと検出される時もありましたが…