Windows10環境で標識に反応するOpenCVのcascade分類器を作成する。

こんにちは。
今回、Windows10環境でOpenCVのcascade分類器を作成しました。
分類器作成に関してWindows10環境での情報が少なく非常に苦労しました。
備忘録もかねて分類器作成のポイントについてまとめます。
ポジティブ画像にいろいろな標識を使って分類器を作ればいろいろな標識に反応する分類器ができるのではないかと思って作ってみました。
もうちょっと調整が必要だけど狙いの分類器を作成できました。

目的
---
OpenCVを利用してcascade分類器を自作します。今回は標識を判別させることに挑戦しました。
ポイントとしてはいろいろな標識の画像を学習することで複数種類の標識を認識する分類器を作成すること。

環境
---
・Windows10
・OpenCV 3.4.5

使用方法
---
・OpenCVをインストール
　[OpenCV 3.4.5のリンク](https://sourceforge.net/projects/opencvlibrary/)からWin packをインストールする。
　（OpenCVのwebサイトは[こちら](https://opencv.org/)）
詳しいcascade分類器の製作マニュアルは[こちら](https://docs.opencv.org/3.3.1/dc/d88/tutorial_traincascade.html)（英語版）
フォルダ構成
---
OpenCVはフォルダパスに日本語があるとエラーが出るらしいので
インストール先や画像ファイル名には日本語を使用しないでください。

pos（正解画像を保存するフォルダ）
vec（正解画像ベクトルファイル用フォルダ）
neg（不正解画像を保存するフォルダ）
cascade（作成したcascade分類器を保存するフォルダ）

```ruby:フォルダ構成
<DIR>          cascade
<DIR>          neg
<DIR>          pos
<DIR>          vec
               opencv_annotation.exe
               opencv_createsamples.exe
               opencv_ffmpeg343_64.dll
               opencv_interactive-calibration.exe
               opencv_traincascade.exe
               opencv_version.exe
               opencv_version_win32.exe
               opencv_visualisation.exe
               opencv_world343.dll
               opencv_world343d.dll
```
画像ファイルの保存
---
pos/の中に、正解を含む画像（以下、ポジティブ画像）を保存
　-アノテーションするのが面倒であれば正解そのものの画像でいいです。
　　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/339914/5eaa34df-329f-06ce-eae2-f43ce87672c1.png)　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/339914/63f63835-242e-3453-d6d3-46629c4d3e97.png)　　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/339914/4374e5ca-5a13-6139-399f-01f0b5ec4aa5.png)


neg/の中に、正解を含まない画像（ネガティブ画像）を保存
　-正解を含まなければ何でもよいらしい
　　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/339914/8790d8a9-53f2-f712-c5ae-b2997d1bede0.png)　　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/339914/d00f268f-dab3-8596-4165-f8fe8423029e.png)　　　![image.png](https://qiita-image-store.s3.amazonaws.com/0/339914/a848f8a8-85a1-316b-650d-436717b5707a.png)


ちゃんと作ろうとすると正解データ7000枚、不正解データ3000枚ほど必要らしいです。

画像ファイルリストの作成
---
cascade分類器の作成のためには画像ファイルのリストの作成が必要です。
正解画像と不正解画像のリストファイルを作成しましょう。
**＜ネガティブ画像リスト＞**
コマンドプロンプトでnegフォルダまで移動し、
下記コマンドで簡単にリストファイルが生成できます。 
	
```ruby:コマンドプロンプト
'dir *.jpg /b > neglist.txt'
```
neglist.txtファイルはフルパスじゃないとエラーが出るらしいのですべてフルパスを追記します。
画像ファイル数だけテキストを編集しなきゃいけませんが頑張ってやりましょう。
こんな感じ
`C:\cascade\build\x64\vc15\bin\neg\1.jpg`
`C:\cascade\build\x64\vc15\bin\neg\10.jpg`

**＜ポジティブ画像リスト＞**
コマンドプロンプトでposフォルダまで移動し、
下記コマンドで簡単にリストファイルが生成できます。 
	
```ruby:コマンドプロンプト
'dir *.jpg /b > poslist.txt'
```
poslist.txtにはファイル名と正解の場所を記入する必要があります。
こんな感じ
`pic01.jpg 1 0 0 30 30`
`pic02.jpg 1 0 0 30 30`
2つ正解が含まれている場合はこんな感じ
`pic02.jpg 2 0 0 10 10 20 20 30 30`

今回用いた画像ファイルは30x30ピクセルで正解だけの画像なので始点と終点を画像ファイルの大きさをそのまま指定しています。
ここでアノテーションが必要で、正解画像の位置によってテキストを編集する必要があります。
本気でやろうとすると7000ファイルありますので気が遠くなる作業になります。
だれか簡単にできる方法を教えてください。

モデル構築（ベクトルファイルの作成）
---
コマンドプロンプトでopencv_createsamples.exeのある場所まで移動して実行します。

```ruby:コマンドプロンプト
'opencv_createsamples.exe -info pos/poslist.txt -vec vec/positive.vec -num 1000 -maxidev 40 -maxxangle 0.8 -maxyangle 0.8 -maxzangle 0.5'
```
**＜opencv_createsamplesのオプション＞**
  [-info  <collection_file_name>]...正解画像のリストファイル（自力で集めた場合）
  [-img  <image_file_name>]...1枚の画像（変形させてベクトルファイルを作成する場合）
  [-vec  <vec_file_name>]...出力されるベクトルファイルの名前（～.vec）
  [-bg  <background_file_name>]...-imgの背景になる画像のリストファイル
  [-num  <number_of_samples = 1000>]...作成する正解画像の枚数
  [-bgcolor  <background_color = 0>]...正解画像の背景になっている色を指定
  [-inv] [-randinv]...色の反転をする場合に指定
  [-bgthresh  <background_color_threshold = 80>]...背景とみなす範囲
  [-maxidev  <max_intensity_deviation = 40>]...最大明度差
  [-maxxangle  <max_x_rotation_angle = 1.100000>]...最大回転角度rad
  [-maxyangle  <max_y_rotation_angle = 1.100000>]
  [-maxzangle  <max_z_rotation_angle = 0.500000>]
  [-show [ <scale = 4.000000>]]...指定すると変形させた正解画像を確認できるウィンドウが表示される。
  [-w  <sample_width = 24>]...ベクトルの横幅
  [-h  <sample_height = 24>]...ベクトルの高さ

今回はポジティブ画像のposlist.txtからベクトルを作成していますが一つのファイルからも作成できるみたいです。

cascade分類器を作成
---
いよいよcascade分類器を作成します。

```ruby:コマンドプロンプト
'opencv_traincascade.exe -data cascade/ooo -vec vec/positive.vec -bg neg/neglist.txt -numPos 6000 -numNeg 1500'
```
**＜opencv_traincascadeのオプション＞**
  -vec <vec_file_name>...作成したベクトルファイル（～.vec）
  -bg <background_file_name>...不正解画像のリストファイル
  [-numPos <number_of_positive_samples = 2000>]...（※）作成した正解画像の枚数より少なくする
  [-numNeg <number_of_negative_samples = 1000>]...用意した不正解画像の枚数
  [-numStages <number_of_stages = 20>]
  [-precalcValBufSize <precalculated_vals_buffer_size_in_Mb = 256>]...特徴量用のメモリサイズ
  [-precalcIdxBufSize <precalculated_idxs_buffer_size_in_Mb = 256>]...特徴量用のバッファメモリー　多いと計算速度が速くなる
  [-baseFormatSave]
--cascadeParams--
  [-stageType <BOOST(default)>]
  [-featureType <{HAAR(default), LBP, HOG}>]...特徴量の見つけ方を決める
  [-w <sampleWidth = 24>]...正解画像を作成したときと同じものを指定
  [-h <sampleHeight = 24>]...正解画像を作成したときと同じものを指定
--boostParams--
  [-bt <{DAB, RAB, LB, GAB(default)}>]...boost分類器のタイプ
    DAB - Discrete AdaBoost,
    RAB - Real AdaBoost,
    LB - LogitBoost,
    GAB - Gentle AdaBoost.
  [-minHitRate <min_hit_rate> = 0.995>]...各ステージでの最小ヒット率
  [-maxFalseAlarmRate <max_false_alarm_rate = 0.5>]...半分を超えない識別器は意味が無い
  [-weightTrimRate <weight_trim_rate = 0.95>]
  [-maxDepth <max_depth_of_weak_tree = 1>]...弱検出器の最大の深さ
  [-maxWeakCount <max_weak_tree_count = 100>]
--haarFeatureParams--
  [-mode <BASIC(default) | CORE | ALL>]
--lbpFeatureParams--
--HOGFeatureParams--

numPosとnumNegは実際の画像ファイルの8～9割程度の数にしておかないとエラーが出ます。
cascade分類器を作成する間もポジティブ画像の判定をしているらしくすべてがポジティブ画像にならないとのことです。

しばらくするとcascate.xmlが指定したフォルダに作成されます。
私のPC環境では16時間ほどかかりました…

Tips
---
・opencvに日本語は通じない、ファイル名、パスに日本語表記がないこと。
・createsamplesで正解画像を用意するときは、できるだけ縮小させておく。
（traincascadeでNG画像を正解画像のサイズに縮小させるため、小さくないと学習効率が悪い）
・traincascadeで使うNG画像のリストはフルパスで記入。じゃないとエラーが出る。
・traincascadeで使う画像ファイル数は無効になる数を考慮してサンプル数の8～9割程度にしておく

まとめ
---
cascade分類器を作成することには成功したがポジティブ画像とほぼ同じものしか反応しない。
ステージ数などを調整してcascade.xmlをいろいろ作って分類器を調整していきたい。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/339914/f8cfaf2a-5487-3a89-a0db-f94c72b075d9.png)


参考にしたサイト
---
OpenCVで物体検出器を作成する① ～基礎知識～　**←これ一番わかりやすいです。**
https://www.pro-s.co.jp/engineerblog/opencv/post_6202.html
[Python] OpenCVを使ってカスケード分類器を自作してみた
https://workout-engineer.com/python-opencv-cascade/
世界一いらない人工知能？？OpenCVを用いたカワウソ分類器作成奮闘記
https://blog.aidemy.net/entry/2018/05/21/210100
OpenCVでSplatoonのイカ分類器を作ってみた
https://qiita.com/haseshin/items/31fc5b42c4d73b574cd0
OpenCV 物体検出、分類器作成と検出テスト　その２
http://mirai-links.com/2018/06/11/opencv-%E7%89%A9%E4%BD%93%E6%A4%9C%E5%87%BA%E3%80%81%E5%88%86%E9%A1%9E%E5%99%A8%E4%BD%9C%E6%88%90%E3%81%A8%E6%A4%9C%E5%87%BA%E3%83%86%E3%82%B9%E3%83%88%E3%80%80%E3%81%9D%E3%81%AE%EF%BC%92/
opencvによるカスケード分類器作成
https://qiita.com/orenodinner/items/dac9e69144b00d5a7c2e