OpenCVでオプティカルフローをリアルタイムに描画する（Shi-Tomasi法、Lucas-Kanade法）

# はじめに
OpenCV(Open Source Computer Vision Library)はBSDライセンスの映像/画像処理ライブラリ集です。画像のフィルタ処理、テンプレートマッチング、物体認識、映像解析、機械学習などのアルゴリズムが多数用意されています。

OpenCVを使った動体追跡の例 (OpenCV Google Summer of Code 2015)
[https://www.youtube.com/watch?v=OUbUFn71S4s](https://www.youtube.com/watch?v=OUbUFn71S4s)

インストールと簡単な使い方はこちら
[OpenCV 3（core + contrib）をPython 3の環境にインストール＆OpenCV 2とOpenCV 3の違い＆簡単な動作チェック](http://qiita.com/olympic2020/items/d5d475a446ec9c73261e)

静止画像のフィルター処理についてはこちら
[OpenCVでエッジ検出してみる](http://qiita.com/olympic2020/items/2c3a2bfefe73ab5c86a4)
[OpenCVで各種フィルター処理をする（グラディエント、ハイパス、ラプラシアン、ガウシアン）](http://qiita.com/olympic2020/items/93e01ef22e46b14a60a9)
[OpenCVで特徴点を抽出する（AgastFeature, FAST, GFTT, MSER, AKAZE, BRISK, KAZE, ORB, SimpleBlob）](http://qiita.com/olympic2020/items/62989573a30ec1d8180b)

動画ファイルの処理についてはこちら
[OpenCVで動画をリアルタイムに変換してみる](http://qiita.com/olympic2020/items/ce00fab38d829965db3b)
[OpenCVでWebカメラ/ビデオカメラの動画をリアルタイムに変換してみる](http://qiita.com/olympic2020/items/12a2eceaf65f142ec3df)

今回は、OpenCVを使ってオプティカルフローをリアルタイムに描画してみます。

# オプティカルフロー
オプティカルフローとは、動画において、特徴点のフレームとフレームの差分（ベクトル）を表現する方法です。フレームレートは、通常一定なので、特徴点の速度を表現しているとも言えます。
計算方法は、テンプレートマッチングを用いる方法や特徴点/特徴量を用いる方法があります。
今回は、特徴点を用いてオプティカルフローを描画してみます。流れは以下のようになります。

1. 前のフレームに対して特徴点を検出します。今回は、Shi-Tomashiの方法を用いて特徴点を検出します。  
    cv2.goodFeaturesToTrack() を利用します。
2. 前フレーム内の特徴点のオプティカルフローを、前フレーム、次フレームの情報を用いて、画像ピラミッド型Lucas-Kanade法で計算します。  
    cv2.calcOpticalFlowPyrLK() を利用します。
3. フレームにオプティカルフローを描画します。
4. 2と3を繰り返します。

# プログラム
+ 動作環境
    - python: 3.5.1
    - OpenCV: 3.1.0

+ 動画データ
    OpenCVに付属しているサンプル動画を利用しました。
    　　OpenCV\opencv\sources\samples\data\768x576.avi

```LucasKande.py
import numpy as np
import cv2

cap = cv2.VideoCapture('768x576.avi')

# Shi-Tomasiのコーナー検出パラメータ
feature_params = dict( maxCorners = 100,
                       qualityLevel = 0.3,
                       minDistance = 7,
                       blockSize = 7 )

# Lucas-Kanade法のパラメータ
lk_params = dict( winSize  = (15,15),
                  maxLevel = 2,
                  criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

# ランダムに色を１００個生成（値0～255の範囲で100行3列のランダムなndarrayを生成）
color = np.random.randint(0, 255, (100, 3))

# 最初のフレームの処理
end_flag, frame = cap.read()
gray_prev = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
feature_prev = cv2.goodFeaturesToTrack(gray_prev, mask = None, **feature_params)
mask = np.zeros_like(frame)

while(end_flag):
    # グレースケールに変換
    gray_next = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # オプティカルフロー検出
    feature_next, status, err = cv2.calcOpticalFlowPyrLK(gray_prev, gray_next, feature_prev, None, **lk_params)

    # オプティカルフローを検出した特徴点を選別（0：検出せず、1：検出した）
    good_prev = feature_prev[status == 1]
    good_next = feature_next[status == 1]

    # オプティカルフローを描画
    for i, (next_point, prev_point) in enumerate(zip(good_next, good_prev)):
        prev_x, prev_y = prev_point.ravel()
        next_x, next_y = next_point.ravel()
        mask = cv2.line(mask, (next_x, next_y), (prev_x, prev_y), color[i].tolist(), 2)
        frame = cv2.circle(frame, (next_x, next_y), 5, color[i].tolist(), -1)
    img = cv2.add(frame, mask)

    # ウィンドウに表示
    cv2.imshow('window', img)

    # ESCキー押下で終了
    if cv2.waitKey(30) & 0xff == 27:
        break

    # 次のフレーム、ポイントの準備
    gray_prev = gray_next.copy()
    feature_prev = good_next.reshape(-1, 1, 2)
    end_flag, frame = cap.read()

# 終了処理
cv2.destroyAllWindows()
cap.release()
```

OpenCVなら、特徴点の抽出、オプティカルフローの計算ともに、メソッドを１個呼ぶだけです。
なんて便利な世の中なんでしょう(^^;)

# 実行結果
実行すると、リアルタイムにオプティカルフローが描画されます。

![result1.png](https://qiita-image-store.s3.amazonaws.com/0/107056/9422f40f-2ca2-5542-d2ed-82a6418e272a.png)

![result2.png](https://qiita-image-store.s3.amazonaws.com/0/107056/855eeacb-0ef2-98c7-5a25-87eac130210e.png)

![result3.png](https://qiita-image-store.s3.amazonaws.com/0/107056/b633daa1-bf76-2585-82e3-c6185b2204fe.png)

![result4.png](https://qiita-image-store.s3.amazonaws.com/0/107056/6ecc01fd-5414-e8de-9749-57eae30687d8.png)