Yolo v3+Windows 10でデスクトップのアイコンを識別してみる その１

#はじめに
[前回](https://qiita.com/yasunari_matsuo/items/8ab661ca4a449495703f)は、強化学習でデスクトップのアイコンをゴミ箱に移動するというものを作ってみました。
今回は、Yolo v3を使ってデスクトップのアイコンを識別して、それをゴミ箱に移動するというのを作ってみたいと思います。

#環境
Windows10 Pro
Visual Studio 2017
CUDA 10.0
cuDNN 7.3.0.29
OpenCV 4.1.1

#Yolo v3インストール
Yolo v3をWindowsにインストールするには、公式のものだといろいろ難しいようです。
そこで、Windows用にリポジトリしてくれたものがあるので、それを利用します。

```bash:github
git clone https://github.com/AlexeyAB/darknet.git
cd darknet
```

準備として、CUDAとcuDNNとOpenCVにパスを通します。

OpenCV
`[配置先]\opencv\build\x64\vc15\bin`

CUDA、CuDNN
`[配置先]\CUDA\v10.0\bin`

VSのソリューションを開きます。
ソリューションは、下記にあります。
`[配置先]\darknet\build\darknet\darknet.sln`

ソリューションプラットホームを`Release`と`x64`に変更します。Debugのままだとエラーになりました・・。

CUDAとOpenCVのインクルードディレクトリを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/38feb62a-a8eb-e647-95aa-b80ca534d8c5.png)


ライブラリにもディレクトリを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/d191e477-32ed-2023-ab95-42917adb4ecb.png)

リビルドしましょう！
ワーニングはたくさん出ますが、最終的に成功しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/555f1d4d-9441-b6d6-fbb8-086f85ecc951.png)

ウェイトファイルを以下からダウンロードして、先ほどビルドして出来上がった`darknet.exe`と同じ場所に保存します。
https://pjreddie.com/media/files/yolov3.weights

コマンドプロンプトで、darknet.exeの場所まで移動して、以下のコマンドを実行します。

```text:コマンドプロンプト
darknet.exe detect cfg\yolov3.cfg yolov3.weights data\dog.jpg
```
サンプル画像での認識結果が表示されたら、インストール成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/77ce7007-3143-57fc-435d-1ef1e698d4ea.png)

#テストデータを準備する
テストデータは、ネットの画像検索で取得してきます。
Goolgeで「デスクトップアイコン」というキーワードで画像検索すると、結構出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/d9014998-2638-77f7-adae-20f1ecea4ccb.png)

この画像たちをアノテーションしていくのですが、完全手作業だと手間がかかるので、labelImgというツールを使います。
Github：
https://github.com/tzutalin/labelImg

####labelImgをインストールする
Windows＋conda環境では、以下のようにインストールします。

```bash:conda_prompt
git clone https://github.com/tzutalin/labelImg.git
cd labelImg

conda install pyqt=5
pyrcc5 -o libs/resources.py resources.qrc
python labelImg.py
python labelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE]
```

`python lavelImg.py`を実行すると以下のエラーが発生しました。

```text:エラー発生
(labelImg) D:\Projects\python\labelImg>python labelImg.py
Traceback (most recent call last):
  File "labelImg.py", line 39, in <module>
    from libs.labelFile import LabelFile, LabelFileError
  File "D:\Projects\python\labelImg\libs\labelFile.py", line 10, in <module>
    from libs.pascal_voc_io import PascalVocWriter
  File "D:\Projects\python\labelImg\libs\pascal_voc_io.py", line 6, in <module>
    from lxml import etree
ModuleNotFoundError: No module named 'lxml'
```
そんな時は、`lxml`をインストールすれば解決です。

```text:conda_prompt
pip install lxml
```
そして、やり直すと、画面が出ました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/13d9168a-09d5-1546-7920-2c452e3de43f.png)

####アノテーションしてみる
画像にバウンディングボックスをつけることをアノテーションと呼びます。
先ほどのデスクトップアイコンの検索結果から一枚画像をダウンロードして、矩形をつけてみましょう。

とその前に、dataフォルダ内にあるpredefined_classes.txtを編集し、今回分類したいカテゴリを事前に定義しておきます。
ゴミ箱とアイコンの二種類に分けたいので、以下のようにしました。

```text:predefined_classes.txt
recycle bin
icon
```

保存したらいったん起動しなおしましょう。そして、次に、画像を開きます。
OpenまたはOpen Dirでファイルを開いたら、Create RectBoxをクリックすると矩形をつけられるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/38dade1f-2933-41c9-89a4-fca4e0483663.png)

矩形を指定したら、クラスを選択するダイアログが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/50af62d6-6397-2016-e39f-5300f589a31d.png)
recycle binを指定してOKをクリックすると、右側に追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/4ca665f3-a624-ff89-34f0-fe91506011dc.png)

ということで、デスクトップのアイコンを指定してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/3563ab00-a0b4-8d70-182a-fb5ca9c83699.png)
っていうか、思った以上に面倒くさいです。やばいです。
保存の際は、「Pascal VOC」というところをクリックすると「Yolo」に変更されます。トグルになってるみたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/205cc560-7fc1-abfb-cba3-ed33b7b2ce30.png)
この状態で保存すると、以下のようなファイルが出来上がりました。
このファイルのフォーマットは以下の通りです。
`[クラス] [矩形の中心座標x] [矩形の中心座標y] [矩形のwidth] [矩形のheight]`

```text:Deskop001.txt
0 0.027083 0.037037 0.032292 0.072222
1 0.028906 0.135185 0.038021 0.087037
1 0.025521 0.225926 0.043750 0.083333
1 0.025781 0.311574 0.044271 0.071296
1 0.026302 0.407870 0.040104 0.087963
1 0.027344 0.496296 0.040104 0.068519
1 0.027344 0.590278 0.051562 0.084259
1 0.027604 0.678241 0.045833 0.069444
1 0.027604 0.767130 0.045833 0.069444
1 0.027344 0.866667 0.052604 0.090741
1 0.079167 0.043981 0.044792 0.086111
1 0.079948 0.131481 0.049479 0.077778
1 0.078646 0.229630 0.054167 0.090741
1 0.077083 0.312963 0.045833 0.068519
1 0.078906 0.407870 0.053646 0.076852
1 0.078906 0.500926 0.053646 0.085185
1 0.077865 0.587500 0.040104 0.071296
1 0.079687 0.680093 0.045833 0.080556
1 0.078646 0.768056 0.038542 0.071296
1 0.079427 0.858796 0.046354 0.080556
1 0.131771 0.038426 0.044792 0.075000
1 0.133333 0.124537 0.053125 0.084259
1 0.132292 0.221296 0.053125 0.083333
1 0.131771 0.312963 0.053125 0.083333
1 0.132292 0.402778 0.053125 0.083333
1 0.131250 0.496296 0.053125 0.083333
```

####yolov3-voc.cfgを編集する
yolov3-voc.cfgファイルを適当な位置にコピーし、中身を今回のものに書き換えます。
書き換える部分は、
`width=928`
`height=512`
ここでは、学習時の画像の幅と高さを指定します。
32の倍数を指定する必要があります。

ちな、これ以上の大きさにすると私のGTX1080ではメモリ不足になってしまいました。。
Teslaが欲しい・・。

あとは、下記を修正しました。
`605行目: filters=21
611行目: classes=2
689行目: filters=21
695行目: classes=2
773行目: filters=21
779行目: classes=2`

ちなみにfiletersは`(classes + 5) * 3`という計算式で算出します。今回は21です。

#トレーニングしてみる
5画像ほど準備してみたので、それでトレーニングしてみます。

が、その前に、訓練データを指定するファイル群を作成しないといけません。
最終的にこのようなフォルダ構成になります。

```
 | - data
     | - desktop.data
     | - desktop-obj.names
     | - desktop-test.txt
     | - desktop-train.txt
     |- backup
     |- datasets
         |- Desktop001.png
         |- Desktop001.txt
         |- Desktop002.png
         |- Desktop002.txt
         |- Desktop003.png
         |- Desktop003.txt
         |- Desktop004.png
         |- Desktop004.txt
         |- Desktop005.png
         |- Desktop005.txt
 | - cfg
     |- yolov3-voc.cfg
 | - models
     |- darknet53.conv.74

```

まずは、`desktop.data`ファイルを作成します。

```text:desktop.data
classes= 2
train  = data/desktop-train.txt
valid  = data/desktop-test.txt
names = data/desktop-obj.names
backup = data/backup
```
今回は二つに分類するので`classes = 2`を指定します。あとは、それぞれの定義ファイルと、学習中のweightが保存されるbackupフォルダを指定します。

`desktop-obj.names`にはクラスの名前を指定します。

```text:desktop-obj.names
recycle bin
icon
```

`desktop-train.txt`には、学習用の画像ファイルパスを指定します。

```text:desktop-train.txt
/data/datasets/Desktop001.png
/data/datasets/Desktop002.png
/data/datasets/Desktop003.png
/data/datasets/Desktop004.png
/data/datasets/Desktop005.png
```
`desktop-test.txt`には、テスト用の画像ファイルパスを指定します。とりあえず、学習用と同じのを指定しておきます。

```text:desktop-test.txt
/data/datasets/Desktop001.png
/data/datasets/Desktop002.png
/data/datasets/Desktop003.png
/data/datasets/Desktop004.png
/data/datasets/Desktop005.png
```
下記サイトから、weightの初期値をダウンロードして配置します。

https://pjreddie.com/media/files/darknet53.conv.74


下記のコマンドを実行します。

```text
darknet.exe detector train data\desktop.data cfg\yolov3-voc.cfg models\darknet53.conv.74
```

訓練が無事開始されたら成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/0cb7de53-95ac-821f-1b1c-a4fb0f5402a0.png)

#テストしてみる
下記コマンドを実行すると、テスト対象の画像のパスを聞いてきます。

```
darknet.exe detector test data\desktop.data cfg\yolov3-voc.cfg data\backup\yolov3-voc_last.weights
```
まずは、訓練に使った画像を指定してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/be3697c7-a54b-2832-c8eb-8ae662476bcb.png)

アイコンの位置をずらしてみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/78ba01a1-af02-a04d-6818-5bb056334583.png)
す、すごい・・・。

背景をつけたらさすがに厳しいでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508288/7471ea48-fa39-7b0c-6089-11cdaff5e9f0.png)
二個だけしか認識されませんでした。
訓練画像が5枚だけなのと、背景のバリエーションが訓練データになかったのが原因だと思います。

#つづく
今回はいったん動作の実験だったので背景変更には対応できていませんが、そのあたりのデータバリエーションを増やして再訓練させてみたいと思います。
次回は、そのあたりの認識率向上と、アイコン削除プログラムへの組み込みをやっていきたいと思います。こうご期待

次の記事
「（おまけ）Yolo v3+Windows 10でデスクトップのアイコンを動的に識別してみる」
https://qiita.com/yasunari_matsuo/items/f0989523849f6ae40483#_reference-8747cd277d7aeccfdef1

####関連している記事
デスクトップのアイコンをすべて削除してくれるＡＩを強化学習（DQN)でつくってみた
https://qiita.com/yasunari_matsuo/items/8ab661ca4a449495703f

ＡＩでデスクトップアイコンを自動削除してくれるツールを作ったった（YOLOv3+Windows 10）
https://qiita.com/yasunari_matsuo/items/2ac2140e7ac250304d4a


#参考にしたサイト
https://blog-ryotaro-diary.hatenablog.com/entry/2018/12/26/002035
https://nmxi.hateblo.jp/entry/2019/02/28/104546
https://demura.net/misc/14458.html
https://github.com/pjreddie/darknet/issues/587
https://qiita.com/harmegiddo/items/c3db5fd567fa4c6cc9fb