Opencv: meanshift＆CamShiftで遊んでみた♬

ちょっとしたことから、すごく使えそうな気がしてきて、今更ながらmeanshiftで遊んでみた。

つまり、構想はこれでターゲット物体捕捉して、物体認識もして表示すれば単品だけど、使えるかなということ。
まずは、Roiできるかがポイント。
【参考】
とりあえず、まんまかもだけど、インスピレーションをもらえた
１．[Raspberry Pi サーボモーターとOpen CVで物体追跡カメラ（Meanshift）](https://qiita.com/PonDad/items/4d50ea908a0fc75f4cea)
ソースは以下の記事を参考にさせていただきました。
２．[Python3 OpenCV3を使って物体検出](https://www.blog.umentu.work/python3-opencv3%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E7%89%A9%E4%BD%93%E6%A4%9C%E5%87%BA/)

###コードは以下に置きました
[Video-Stream / meanshift.py ](https://github.com/MuAuan/Video-Stream/blob/master/meanshift.py)
現行版なので、本文と一部異なります。

###環境
構築は、参考２のようにやればいいのね。
---以下参考２引用
pipでインストールしたモジュール一覧

```
gnureadline==6.3.3
ipython==3.1.0
matplotlib==1.4.3
nose==1.3.7
numpy==1.9.2
Pillow==2.9.0
pyparsing==2.0.3
python-dateutil==2.4.2
pytz==2015.4
scipy==0.16.0
six==1.9.0
sympy==0.7.6
```
requirements.txtに上記の一覧を記述して、

```
$ pip install -r requirements.txt
```
とすればOpenCV以外の同じ環境が構築できる。
---ここまで引用

とはいえ、今回はいじらずに行けました。

```
>pip list
Package                            Version
---------------------------------- -----------
ipython                            5.1.0
matplotlib                         2.2.2
nose                               1.3.7
numpy                              1.14.2
Pillow                             3.3.1
pycparser                          2.14
python-dateutil                    2.5.3
pytz                               2016.6.1
scipy                              0.18.1
six                                1.10.0
sympy                              1.0
```

###コードと工夫したこと

```参考２のオリジナルコード.py
# -*- coding: utf-8 -*-
import numpy as np
import cv2
if __name__ == '__main__':
    cap = cv2.VideoCapture(0)
    # 追跡する枠の座標とサイズ
    x = 600
    y = 200
    w = 200
    h = 200
    track_window = (x, y, w, h)
    # フレームの取得
    ret,frame = cap.read()
    # 追跡する枠を決定
    roi = frame[y:y+h, x:x+w]
    # 追跡する枠の内部を切り抜いてHSV変換
    hsv_roi =  cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
    ## マスク画像の生成
    img_mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
    ## 正規化するためのヒストグラムの生成 
    roi_hist = cv2.calcHist([hsv_roi], [0], img_mask, [180], [0,180])
    ## ノルム正規化
    cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
    term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
    while(True):
        ret, frame = cap.read()
        if ret == True:
            # フレームをHSV変換する
            hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
            # 上で計算したヒストグラムを特徴量として、画像の類似度を求める
            dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180], 1)
            # 物体検出する
            ret, track_window = cv2.meanShift(dst, track_window, term_crit)
            #ret, track_window = cv2.CamShift(dst, track_window, term_crit)

            # 物体検出で取得した座標を元のフレームで囲う
            x,y,w,h = track_window
            img_dst = cv2.rectangle(frame, (x,y), (x+w, y+h), 255, 2)
            cv2.imshow('SHOW MEANSHIFT IMAGE', img_dst)
            # qを押したら終了。
            k = cv2.waitKey(1)
            if k == ord('q'):
                break
        else:
            break
```
```#ret, track_window = cv2.CamShift(dst, track_window, term_crit)```
はウワンが追記
【参考】
[Meanshift と Camshift](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_video/py_meanshift/py_meanshift.html)

これでも動くと思うが、ウワンのカメラだと初期動作が遅くて動いてくれなかった。
また、ターゲットを捕捉するのが難しくて、何度も失敗。
ということで、ターゲットを捕捉できるように以下の変更を加えた。

```
    # 追跡する枠の座標とサイズ
    x = 600
    y = 200
    w = 200
    h = 200
    track_window = (x, y, w, h)
    # フレームの取得
    ret,frame = cap.read()
    # 追跡する枠を決定
    roi = frame[y:y+h, x:x+w]
```
の部分を以下とした。

```
 　　# 追跡する枠の座標とサイズ
    x = 200
    y = 200
    w = 200
    h = 200
    track_window = (x, y, w, h)
    # フレームの取得
    ret,frame = cap.read()
    cv2.waitKey(2) 
    # 追跡する枠を決定
    while True:
        ret,frame = cap.read()
        cv2.imshow("org",frame)
        roi = frame[y:y+h, x:x+w]
        cv2.imshow("roi",roi)
        if cv2.waitKey(20)>0:
            break
```
つまり、一度初期起動させ、その後取得する。しかも動画を直接見ながらWhileでターゲットを確実に補足できたところで、何かキーを押すとroiが取得できるように変更した。これで、やると失敗はなくなった。

###結果
例は青い物体でのものであったが、赤いリンゴのお菓子袋でためしてみた。
以前の記事から青の範囲との比較
[OpenCV / read_save_doga.py ](https://github.com/MuAuan/OpenCV/blob/master/read_save_doga.py)

``` 
47     # define range of blue color in HSV 
48     lower_blue = np.array([110,50,50]) 
49     upper_blue = np.array([130,255,255]) 

#上記のコード
img_mask = cv2.inRange(hsv_roi, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
```
*上記はほぼ全色入っている*

ということで見事に捕捉してくれた。実は顔なんかも捕捉できているので、ほぼなんでも捕捉できるものと思わわれる。
![apple_meanshift.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/1f92e032-10e8-432c-9101-60c8fe9f5fe0.jpeg)

ちなみに、CamShiftはさらに捕捉するまで枠の大きさを変えた探索する。物体の見た目の大きさ（遠ざかったり、近づいたりで）が変わっても、その大きさに対応して捕捉する。
捕捉できなければ、撮影画像一面に広がる。。。
ただし、枠が横に長くなってしまったり、縦に長くなったりもして評価は難しいところです。

###まとめ
・meanshift＆CamShiftで物体捕捉して遊んでみた
・roiの使い方次第でいろいろ試せそう
　　　というか、SSPのRoiで物体認識が使えるような予感