Python版OpenCVでLucas-Kanade法を実装し、物体追跡(オプティカルフローを計算)する方法をソースコード付きで解説します。
オプティカルフローで物体追跡
オプティカルフローとは、デジタル画像中の物体の動きを「ベクトル」で表したものです。
主に移動物体の検出や、その動作の解析などによく用いられています。以下は振り子のオプティカルフローを赤い線で描画しているものです。
【実行例】
しかしオプティカルフロー(=物体の移動ベクトル)を一意的に求めることは困難です。
一般的には推定によって動き(ベクトル)を求めます。
オプティカルフローを推定する手法は代表的なモノに「LucasKanade法」や「Horn-Schunk法」があります。今回は、Shi-Tomasi法で求めた特徴点を「LucasKanade法」で追跡してみます。
なお、オプティカルフローの原理については、以下ページで別途解説しています。

【オプティカルフローとは】推定の原理・特徴・計算式
オプティカルフローとは?移動量の推定方法、原理、計算式についてまとめました。
また、Shi-Tomasi法による特徴点(コーナー)の検出原理については以下ページで別途解説しています。

Shi-Tomasi法で特徴点を検出する原理・計算式
Shi-Tomasi法で特徴点を検出する原理や計算式についてまとめました。
サンプルコード
サンプルプログラムのソースコードです。
コード解説
サンプルコードの重要箇所を詳しく解説します。
- 指定されたビデオファイルを読み込みます。ここでは、
input2.mp4
というファイルを読み込んでいます。
cap = cv2.VideoCapture("/Users/github/sample/python/opencv/video/input2.mp4")
- Shi-Tomasi法のパラメータを設定しています。
ft_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
パラメータ | 説明 |
---|---|
maxCorners | 検出する最大の特徴点数を指定します。ここでは最大100個の特徴点を検出します。 |
qualityLevel | 特徴点の品質を決めるしきい値です。値が高いほど、品質の高い特徴点のみが選ばれます。 |
minDistance | 特徴点間の最小距離を指定します。これにより、近接する特徴点が除外されます。 |
blockSize | 特徴点を計算するためのブロックサイズを指定します。 |
以下では、Lucas-Kanade法のパラメータを設定します。
lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
パラメータ | 説明 |
---|---|
winSize | オプティカルフローを計算するためのウィンドウサイズを指定します。ここでは15×15ピクセルのウィンドウを使用します。 |
maxLevel | ピラミッドのレベル数を指定します。ここでは2レベルのピラミッドを使用します。 |
criteria | 探索アルゴリズムの終了条件を指定します。ここでは、10回の反復または0.03の精度に達するまで計算を続けます。 |
以下で、動画から最初のフレームを取得し、グレースケール変換しています。
ret, frame = cap.read()
gray1 = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
以下では、 Shi-Tomasi法を使用して、グレースケール画像から特徴点を検出します。
ft1 = cv2.goodFeaturesToTrack(gray1, mask=None, **ft_params)
以下では、フレームと同じサイズのゼロ配列(黒い画像)を生成します。これは特徴点の軌跡を描画するために使用します。
mask = np.zeros_like(frame)
以下では、動画からフレームを取得し、オプティカルフローを求めて画面に描画することを繰り返します。
# 動画終了まで繰り返し
while(cap.isOpened()):
# 次のフレームを取得し、グレースケールに変換
ret, frame = cap.read()
gray2 = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Lucas-Kanade法でフレーム間の特徴点のオプティカルフローwp計算
ft2, status, err = cv2.calcOpticalFlowPyrLK(
gray1, gray2, ft1, None, **lk_params)
# オプティカルフローを検出した特徴点を取得(1なら検出)
good1 = ft1[status == 1] # 1フレーム目
good2 = ft2[status == 1] # 2フレーム目
# 特徴点とオプティカルフローをフレーム・マスクに描画
for i, (pt2, pt1) in enumerate(zip(good2, good1)):
x1, y1 = pt1.ravel() # 1フレーム目の特徴点座標
x2, y2 = pt2.ravel() # 2フレーム目の特徴点座標
# 軌跡を描画(過去の軌跡も残すためにmaskに描く)
mask = cv2.line(mask, (x2, y2), (x1, y1), [0, 0, 200], 2)
# 現フレームにオプティカルフローを描画
frame = cv2.circle(frame, (x2, y2), 5, [0, 0, 200], -1)
# フレームとマスクの論理積(合成)
img = cv2.add(frame, mask)
# ウィンドウに表示
cv2.imshow('mask', img)
# 次のフレーム、ポイントの準備
gray1 = gray2.copy() # 次のフレームを最初のフレームに設定
ft1 = good2.reshape(-1, 1, 2) # 次の点を最初の点に設定
# qキーが押されたら途中終了
if cv2.waitKey(30) & 0xFF == ord('q'):
break
- cv2.calcOpticalFlowPyrLK
- Lucas-Kanade法を使用して、前のフレームと現在のフレーム間の特徴点のオプティカルフローを計算します。
- status
- 特徴点が追跡されたかどうかを示すステータス配列です。
- good1とgood2
- 追跡された特徴点の座標を取得します。
- cv2.line
- 特徴点の軌跡をマスクに描画します。
- cv2.circle
- 現在のフレームに特徴点を描画します。
- cv2.add
- フレームとマスクを合成します。
- cv2.imshow
- 合成画像をウィンドウに表示します。
- cv2.waitKey(30)
- 30ミリ秒待機し、’q’キーが押されたらループを終了します。
以下は終了処理です。すべてのウィンドウを閉じ、ビデオキャプチャーを解放します。
cv2.destroyAllWindows()
cap.release()
関連ページ

【PythonとOpenCVで画像処理超入門】使い方とサンプルコードを解説
Python版OpenCVで画像処理プログラミングを行う方法を入門者向けにソースコード付きで解説するページです。
コメント