![]() |
OpenCV 4.13.0
Open Source Computer Vision
|
前のチュートリアル: MeanshiftとCamshift
次のチュートリアル: カスケード分類器
本章では、
オプティカルフローとは、物体やカメラの動きによって生じる、連続する2枚のフレーム間での画像中の物体の見かけの動きのパターンである。これは2次元のベクトル場であり、各ベクトルは最初のフレームから2番目のフレームへの点の移動を示す変位ベクトルである。下の画像を見てほしい(画像提供: オプティカルフローに関する Wikipedia の記事)。
これは連続する5枚のフレーム中をボールが移動している様子を示している。矢印はその変位ベクトルを表す。オプティカルフローは次のような分野で多くの応用がある:
オプティカルフローはいくつかの仮定の上に成り立つ:
最初のフレーム中のピクセル \(I(x,y,t)\) を考える(ここで新たに時間という次元が追加されていることに注意。これまでは画像だけを扱っていたので時間は不要だった)。このピクセルは、\(dt\) 時間後に撮影された次のフレームで距離 \((dx,dy)\) だけ移動する。これらのピクセルは同一であり強度も変化しないので、次のように表せる。
\[I(x,y,t) = I(x+dx, y+dy, t+dt)\]
次に右辺をテイラー級数で近似し、共通項を消去して \(dt\) で割ると、次の式が得られる。
\[f_x u + f_y v + f_t = 0 \;\]
ここで:
\[f_x = \frac{\partial f}{\partial x} \; ; \; f_y = \frac{\partial f}{\partial y}\]
\[u = \frac{dx}{dt} \; ; \; v = \frac{dy}{dt}\]
上の式はオプティカルフロー方程式と呼ばれる。この式では \(f_x\) と \(f_y\) を求めることができ、これらは画像勾配である。同様に \(f_t\) は時間方向の勾配である。しかし \((u,v)\) は未知である。この1本の方程式を2つの未知変数について解くことはできない。そこでこの問題を解くためにいくつかの手法が提供されており、その1つが Lucas-Kanade である。
先ほど、近傍のピクセルはすべて同様の動きをするという仮定を見た。Lucas-Kanade 法では、対象点の周囲の 3x3 のパッチを取る。したがって9個の点はすべて同じ動きをする。これら9個の点について \((f_x, f_y, f_t)\) を求めることができる。こうして問題は、2つの未知変数を持つ9本の方程式を解くことになり、これは過剰決定系である。より良い解は最小二乗法によって得られる。以下が最終的な解であり、これは未知数2つ・方程式2つの問題となっていて、解いて答えを得る。
\[\begin{bmatrix} u \\ v \end{bmatrix} = \begin{bmatrix} \sum_{i}{f_{x_i}}^2 & \sum_{i}{f_{x_i} f_{y_i} } \\ \sum_{i}{f_{x_i} f_{y_i}} & \sum_{i}{f_{y_i}}^2 \end{bmatrix}^{-1} \begin{bmatrix} - \sum_{i}{f_{x_i} f_{t_i}} \\ - \sum_{i}{f_{y_i} f_{t_i}} \end{bmatrix}\]
(この逆行列が Harris コーナー検出器と類似していることに注目。これはコーナーが追跡に適した点であることを示している。)
そのため、ユーザの視点から見ると考え方は単純で、追跡したい点をいくつか与えると、それらの点のオプティカルフローベクトルが返ってくる。しかし、ここでもいくつか問題がある。これまでは小さな動きを扱ってきたので、大きな動きがあると失敗する。これに対処するために、ピラミッドを使う。ピラミッドを上っていくと、小さな動きは取り除かれ、大きな動きが小さな動きになる。そこで Lucas-Kanade を適用することで、スケールとともにオプティカルフローが得られる。
OpenCV はこれらすべてを1つの関数 cv.calcOpticalFlowPyrLK() で提供している。ここでは、動画中のいくつかの点を追跡する簡単なアプリケーションを作成する。追跡する点を決めるために cv.goodFeaturesToTrack() を使う。最初のフレームを取得し、その中で Shi-Tomasi コーナー点をいくつか検出し、それらの点を Lucas-Kanade オプティカルフローを使って反復的に追跡する。関数 cv.calcOpticalFlowPyrLK() には、前のフレーム、前の点、次のフレームを渡す。この関数は次の点を返すとともに、いくつかのステータス値を返す。ステータス値は、次の点が見つかった場合は 1、そうでない場合は 0 となる。これらの次の点を、次のステップで前の点として反復的に渡していく。以下のコードを参照してほしい。
(このコードは、次のキーポイントがどれだけ正確かをチェックしていない。そのため、ある特徴点が画像中で消えてしまっても、オプティカルフローがそれに近く見える次の点を見つけてしまう可能性がある。したがって、頑健な追跡のためには、特定の間隔でコーナー点を検出すべきである。OpenCV のサンプルには、5フレームごとに特徴点を見つけるサンプルが用意されている。これは、得られたオプティカルフローの点に対して逆方向のチェックも行い、良い点だけを選別している。samples/python/lk_track.py を参照してほしい。)
得られた結果を見てみよう。
Lucas-Kanade 法は、疎な特徴点集合(この例では Shi-Tomasi アルゴリズムで検出したコーナー)についてオプティカルフローを計算する。OpenCV は密なオプティカルフローを求める別のアルゴリズムも提供している。これはフレーム中のすべての点についてオプティカルフローを計算する。これは Gunnar Farneback のアルゴリズムに基づいており、2003年に Gunnar Farneback による "Two-Frame Motion Estimation Based on Polynomial Expansion" で説明されている。
以下のサンプルは、上記のアルゴリズムを使って密なオプティカルフローを求める方法を示している。オプティカルフローベクトル \((u,v)\) を持つ2チャンネルの配列が得られる。その大きさと方向を求める。結果を見やすくするために色分けする。方向は画像の色相 (Hue) 値に対応する。大きさは明度 (Value) 面に対応する。以下のコードを参照してほしい。
以下の結果を参照のこと。