keras-yolov3を使用する

# 初めに
画像認識で物体を検出する際にyolov3を使用しました。その時の使用方法のメモを残そうと思います。

# 使用環境
- ubuntu20.04またはwindows10
- anaconda  
  - **tensorflow-gpu  1.13.1** 
  - **keras   2.3.1**
  - pillow  
  - matplotlib   
      - `conda install pillow matplotlib`で上の2つはインストールできます  
  - opencv    
      - ※ 注意 `pip install opencv-python`でインストールする。  
      - `conda install opencv`でインストールすると動かないと思います。 
      
環境構築については[参考](https://qiita.com/f-sk/items/6e2684ecc3fcb941bcb0)を見てください。(**tensorflow-gpu  1.13.1**,**keras   2.3.1**であることが違うので注意)  
※ この記事は[keras-yolo3](https://github.com/qqwweee/keras-yolo3)と[Windows 10 で YOLOv3 を自前画像で学習させる（物体検出編）【機械学習】](https://rightcode.co.jp/blog/information-technology/learn-yolov3-image-windows10-object-detection)の内容を主に参考にしています。

# モデルのダウンロード
## ubuntuの場合
```bash:ubuntu
cd ~/
#ソースコードをダウンロード
git clone https://github.com/qqwweee/keras-yolo3.git
cd keras-yolo3
#重みをダウンロード
wget https://pjreddie.com/media/files/yolov3.weights
#kerasようにモデルをconvert
#kerasなどインストールしたanaconda仮想環境内で行う
python convert.py yolov3.cfg yolov3.weights model_data/yolo_weights.h5
```


## windowsの場合
1. ソースコードをダウンロード
[keras-yolo3](https://github.com/qqwweee/keras-yolo3)からダウンロード(code→Download zip通すとダウンロードできます)し解凍する。ここでは`C:\test\`に解凍したとする。
2. 重みをダウンロード
[重み](https://pjreddie.com/media/files/yolov3.weights)からダウンロード。(クリックしただけでダウンロードが始まります)  
3. yolov3.weightsを`C:\test\keras-yolo3-master`に移動させる。
4. kerasようにモデルをconvert  
   1. anaconda promptから必要なものをインストールしたanaconda仮想環境に入る。   
   1. `C:\test\keras-yolo3-master`に移動する。例 `cd C:\test\keras-yolo3-master`  
   1. `python convert.py yolov3.cfg yolov3.weights model_data/yolo_weights.h5`
を実行する

# バグの修正
- [参考1](https://github.com/qqwweee/keras-yolo3/pull/588/commits/18811778288056c33bfe8e69977452f598400812)を参考にyolo_video.pyを修正する。  
- [参考2](https://github.com/qqwweee/keras-yolo3/issues/554)を参考にyolo.py(174行目あたり)を修正する。
- yolo.pyの最終行`yolo.close_session()`の前に  

   ``` 
   vid.release()
   cv2.destroyAllWindows()
   ```
   を追加する。(不要かもしれない)

# デモの実行 
  方法1. ファイル画像(.jpg,.mp4など)を認識する
  1. [このページ](https://qiita.com/toten_s/items/04d0744598e8c3d9bc9b)を参考に変更を行う
(行わないと `AttributeError: 'NoneType' object has no attribute  '__array_interface__'`となります)  
2. jpgファイルなどを認識する場合結果のウィンドウが一瞬で閉じてしまうので最後の`cv2.destroyAllWindows()`をする前に`time.sleep`などで停止する。(強引ですが…)
3. 以下のコマンドで起動する

```bash:demo
python yolo_video.py --model_path=model_data/yolo_weights.h5 --input=[ファイルのパス]
```


  方法2. webカメラで撮影した画像を認識する
  1. webカメラを接続する。
  2.  以下のコマンドで起動する

```bash:demo
#カメラ番号はopencvのcv2.VideoCapture(int(video_path))に渡されるのでカメラの番号を設定する
python yolo_video.py --model_path=model_data/yolo_weights.h5 --input=[カメラ番号]
#動画のウィンドウ上でqを押すと終了
 ```

※`InternalError (see above for traceback): Blas SGEMM launch failed : m=43264, n=32, k=64　…`のようなエラーが出る場合
[参考](https://github.com/qqwweee/keras-yolo3/issues/362)
GPUの種類やcudaのバージョンによっては上のようなエラーが出ることがあるらしい。tensorflow-gpuとkerasのバージョンを上のものとあっているか確認する。(tensorflow-gpu1.12.0 , keras2.2.4では上のようなエラーが出ました(cuda-toolkitのバージョンの問題のようだがtensorflow-gpuやkerasのバージョンを変えればanacondaが自動的に調整してくれる))
[参考(tensorflowとcudaのバージョン)](https://qiita.com/chin_self_driving_car/items/f00af2dbd022b65c9068)

# 自前のデータを用いる
1. データの画像を集める
2. [VoTTでアノテーションデータを作成する](https://qiita.com/f-sk/items/7db148abb6d2d312367d)を参考に、アノテーションデータを作成する。(今回はデータの画像はjpgのものを使用しています。
 - ここでproviderをPascal VOCにし、テストとトレーニング分割はすべて(100%)trainにしました。  
 (keras-yolo3の学習においてはtrainしか用いないようなので)  
 (また確認をwebカメラで行ったのでテストデータは不要かなと考えています)
3. `keras-yolo3(またはkeras-yolo3-master)`ディレクトリ以下に`VOCdevkit/VOC2007`を作成する。
4. [任意の名前]-PascalVOC-exportの中身をそのままVOC2007内部にコピペする。
5. `VOCdevkit/VOC2007/ImageSets/Main`内にクラスごとにファイルができている。これらのファイルは画像名と,1と-1で画像中にそのクラスが含まれているのかを表している(?)。ここでは画像名のみあればよいので1つのみ残してtrain.txtと名前を付け保存する。それ以外は削除してよい。またファイル中の各行末の1,-1と.jpgをすべて削除する。(行末の1は` 1`(半角スペースと1を空白に一括置換するとよいです))
5. voc_annotation.pyを以下のように編集する
    1. 4行目を`sets=[('2007', 'train')]`に
    2. 6行目classesを自分の使いたいクラスに(じゃんけんの例:`classes = ["gu","tyoki","pa"]`(VoTTでつけたタグのこと))
    3. 21行目をそれぞれ一度`float`にキャストする。`int(float(xmlbox.find('xmin').text))`など
5. python voc_annotation.pyを実行する。
6. `keras-yolo3(またはkeras-yolo3-master)` 内部に`2007_train.txt`ができる。
7. `model_data/voc_classes.txt`をコピーして、`my_classes.txt`を作り、クラスを書く（gu,tyoki,paなど）
8. `train.py`を以下のように編集する
    1. 17行目を`annotation_path = '2007_train.txt'`に
    1. 19行目を `classes_path = 'model_data/my_classes.txt’`に
    1. 57行目と76行目の“batch_size”を32から8にする。(これはGPUのメモリが不足するため(?)でエラーが起こらなければ32のままでもよく逆にエラーが出るなら4に下げるといいかもしれません)
9. `python train.py`で学習→重みが`logs/000/trained_weights_final.h5`にできる
 (`AttributeError: module 'keras.backend' has no attribute 'control_flow_ops'`と出る場合は[参考](https://qiita.com/sino20023/items/cdb8857badb4aac4d3e0)のようにして解決できます。)(kerasのバージョンによるらしい)
1. 認識の実行
    1. webカメラを接続する
    2.  以下を実行する     
     
```
python yolo_video.py --model_path=logs/000/trained_weights_final.h5 --classes_path=model_data/my_classes.txt --input=[カメラ番号]
```