泳いでいる鯉を認識してみる

今回は、これまでと趣向を変えて物体認識について取り扱ってみます。

動画の中から任意の個体を認識させて、それを追跡する検証をPythonベースのプログラムで行ってみます。

# 泳いでいる鯉の認識

今回扱う映像は、泳いでいる鯉の映像です。
![Goldfish.gif](https://qiita-image-store.s3.amazonaws.com/0/134453/0620d873-b62c-4b42-bbca-db2cd4935ba6.gif)
（※GIF動画にしたので画像が少々荒いです。）

画面を横切っていく、少し体の大きな赤色の鯉を認識させてみます。
![goldfish_tartget.jpg](https://qiita-image-store.s3.amazonaws.com/0/134453/37de5123-d7ec-6086-36db-d17535f7ff8f.jpeg)

# 機械学習ライブラリ「dlib」
これまでのブログでは、scikit-learnを使ってきましたが、今回は[dlib](http://dlib.net)という機械学習のライブラリを使ってみます。
オープンソースのC++ベースのライブラリで、Pythonからも使用することができます。

dlibをインストールする場合は、ライブラリのビルドをC++ライブラリの[boost](http://www.boost.org)を用いて行うので、それも合わせてインストールする必要があります。

ちなみに、Macの場合のインストールコマンドは以下の通り。

```
brew install boost
brew install boost-python --with-python3
pip install dlib
```

# サンプリング画像と矩形データの作成
動画から対象の鯉が写っているコマをいくつかキャプチャして、学習データを作成していきます。

今回は動画プレイヤーでコマを進めながら20ファイル分、サンプリングしてみます。
![sampling.png](https://qiita-image-store.s3.amazonaws.com/0/134453/f0e6717e-32b3-00c2-ee68-c2648b2ac765.png)

サンプリングした画像から、鯉が表示されている矩形をデータ化します。

dlibに矩形データを作成する、imglabというGUIツールが用意されていますので、それを使います。
矩形データはxml形式のファイルで生成されます。
今回は、training.xmlというファイルにしましょう。

サンプリング画像を内包するディレクトリで、以下のコマンドを
実行します。
サンプル画像はPNGファイルで、「IMG_01.png」のような連番で
用意しておきます。

```
~/dlib/tools/imglab/build/imglab training.xml
~/dlib/tools/imglab/build/imglab -c training.xml IMG_*.png
```

以下のような、GUIのウィンドウが表示されます。
各画像ファイルに対して、Shiftキーを押しながらドラッグすると、
赤い矩形範囲がマーキングできます。

![imglab.png](https://qiita-image-store.s3.amazonaws.com/0/134453/da7f2c24-1d0f-f910-43f6-f060e911fe7d.png)

全てのサンプリング画像に対して、鯉の部分を矩形でマーキングしたあとに
「File」メニューから「Save」コマンドで保存します。

サンプリング画像と同階層に生成された「training.xml」ファイルには
以下のように、画像ファイルと矩形のbox情報（top,left,width,height）が
記録されていることが確認できます。

![xml_shot.png](https://qiita-image-store.s3.amazonaws.com/0/134453/3392ab18-0113-0dbd-52a9-d7cf5651d439.png)

#学習データの作成
先ほどの矩形データのxmlファイルで、学習データを作成していきます。

dlibのsimple_object_detectorを用いて認識を行うため、
それ用の学習データを用意します。

pythonで以下のコードで「detector.svm」という学習データを
作成しています。

```py
import dlib

#学習用のオプション変数の生成.
options = dlib.simple_object_detector_training_options()

#認識はSVMアルゴリズムを使っている。それのコストパラメータを設定.
options.C = 3

#学習処理を行うときの実行スレッドの数.
options.num_threads = 8

#左右反転のイメージは生成しない（鯉が一方向に進むだけなので）
options.add_left_right_image_flips = False

#学習処理の経過を出力する（ターミナルで確認可能）
options.be_verbose = True

#矩形のXMLデータから学習データを作成.
dlib.train_simple_object_detector("training.xml", "detector.svm", options)

#学習データから認識オブジェクトの作成.
detector = dlib.simple_object_detector("detector.svm")
```

認識アルゴリズムは[サポートベクターマシン（SVM）](https://ja.wikipedia.org/wiki/サポートベクターマシン)を用いています。
options.CはSVMのコストパラメータと呼ばれるもので、値が大きいほど誤認識に対して厳しく判定するようになります。

# 動画の鯉を認識させてみる

上記で生成した鯉の認識オブジェクトを用いて、実際に動画中の鯉を認識させてみます。

動画の読み込みはOpenCVを用いて行います。
動画はmp4形式のファイルで扱っています。

OpenCVのVideoCaptureに動画ファイルを指定してやれば、簡単に動画のフレームを読み込み、解析処理を行うことができます。

以下のプログラムでは、VideoCaptureで読み込んだフレーム画像に対して、dlibの認識オブジェクト（detector）を通して、鯉の判定矩形を取得しています。

それをフレームに青枠で書き込んで、再生ウィンドウに出力しています。

```py
import cv2

#鯉の動画（input.mp4）をキャプチャーする.
cap = cv2.VideoCapture("input.mp4")

while(cap.isOpened()):
    #キャプチャーで動画のフレームを読み込む.
    ret, frame = cap.read()
    
    #フレームが出来なかった場合（動画が終了した場合など）はループを抜ける.
    if ret == False:
        break
    
    # 読み込んだフレームを鯉の矩形認識処理に通す.
    dets = detector(frame)

    # 返された矩形の数分、フレームに青色で矩形を書き込む.
    for det in dets:
        cv2.rectangle(frame, (det.left(), det.top()), (det.right(), det.bottom()), (255, 0, 0), 3)

    # 出力ウィンドウにフレームを表示する.    
    cv2.imshow("frame", frame)

    if cv2.waitKey(1) == 27: # ESCキーで終了
        break   

#キャプチャーの解放.        
cap.release()
#出力ウィンドウの破棄.
cv2.destroyAllWindows()
```

実行すると以下のような映像が出力されます。

![detect_frame.gif](https://qiita-image-store.s3.amazonaws.com/0/134453/21b488ad-cf93-96e9-a5c8-8d453870cad4.gif)

サンプリング画像は、鯉が全体で写っている画像で学習させているので、
見切れているときは、青枠は表示されませんね。

ただ、20ファイル程度のサンプリングデータで、動画中の対象物体の認識も
そこそこ出来ているようです。

今回はここまで。

---
参照動画URL：
https://pixabay.com/videos/id-3613/