はじめまして。技術研究所の堀越と申します。
皆さんは動画から物体を検知し、その移動軌跡を取得する方法をご存じでしょうか?
今回は、Optical Flowを使用して物体の移動軌跡を描画するPythonプログラムを作成したので紹介します。
オプティカルフロー(Optical Flow)とは、フレーム間の物体の物体の動きを検出して速度をベクトルで表示する手法のことをいいます。
オプティカルフローは次のような仮定に基づいて計算を行っています。
- 連続フレーム間で物体の画像上の明るさは変わらない
- 隣接する画素は似たような動きをする
オプティカルフローの推定法としては、勾配法とブロックマッチング法があります。
【 勾配法 】
画像の時空間微分の拘束方程式による条件からフローベクトルを推定する手法。 短い処理時間で検出できるが、検出の精度が低い。
【 ブロックマッチング法 】
画像をある大きさの領域で分割して次のフレームの画像中を検索し、前フレームの注目領域との類似度が最も高い領域を検出する手法。 処理時間が長いが濃度値の変化やノイズに強く高い精度で検出を行うことが出来る。
今回のPythonプログラムでは、勾配法の一種であるLucas-Kanade法という方法を使用して実装を行いました。
Optical Flowの処理の流れは以下のようになります
ST_param = dict( maxCorners = 50, #特徴点の最大数 |
qualityLevel = 0.1, #特徴点を選択する閾値 |
minDistance = 7, #特徴点間の最小距離 |
blockSize = 7) #特徴点の計算に使う周辺領域のサイズ |
maxCorners:特徴点の最大数(値が大きいほど多くの特徴点を見つけます) qualityLevel:特徴点を選択する閾値(値が大きいほど特徴点が厳選されます) minDistance:特徴点間の最小距離(他の特徴点がこの距離より近い場合は特徴点としない)blockSize:特徴点の計算に使う周辺領域のサイズ(値が大きいほど計算に使う領域が大きくなる
gray1 = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) |
ft1 = cv2.goodFeaturesToTrack(gray1, mask = None, **ST_param ) |
画像の特徴点がgoodFeaturesToTrackの戻り値としてft1に格納されます。
# 軌跡描画用のマスクを生成 |
mask = np.zeros_like(frame) |
LK_param = dict( winSize = (15, 15), #オプティカルフローの推定の計算に使う周辺領域サイズ(小:敏感、大:鈍感) |
maxLevel = 2, #ピラミッド数 |
criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) #繰り返しの終了条件 |
【 各パラメータの説明 】
winSize:オプティカルフローの推定の計算に使う周辺領域サイズ(小さくするとノイズに敏感にな り、大きな動きを見逃す可能性がある) maxLevel:ピラミッド数(0の場合、ピラミッドを使用しない。ピラミッドを使用ことで、画像のさまざまな解像度でオプティカルフローを見つけることができる) criteria:繰り返しの終了条件(条件が満たされたらアルゴリズムの計算を終了する。cv2.TERM_CRITERIA_EPS:指定した精度(epsilon)に到達したら計算を終了する。cv2.TERM_CRITERIA_COUNT:指定した繰り返しの最大回数(count)に到達したら計算を終了。 今回は、計算の繰り返し回数を10回、精度を0.03に設定。)
gray2 = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) |
ft2, status, err = cv2.calcOpticalFlowPyrLK(gray1, gray2, ft1, None, **LK_param ) |
ft2:画像の特徴点がcalcOpticalFlowPyrLKの戻り値としてft2に格納されます。 status:Optical Flowが検知できた場合は1、検知できなかった場合は0が格納されます。 err:移動前の特徴点の周辺領域と,移動後の特徴点の周辺領域との差を含む出力 vectorが格納されます。
#オプティカルフローが計算できた場合 |
if status is not None: |
#オプティカルフローを検出した特徴点を取得 |
#1フレーム目 |
g1 = ft1[status == 1] |
#2フレーム目 |
g2 = ft2[status == 1] |
#特徴点とオプティカルフローをマスクに描画する |
for i, (pt2, pt1) in enumerate(zip(g2, g1)): |
#1フレーム目の特徴点座標 |
x1, y1 = pt1.ravel() |
#2フレーム目の特徴点座標 |
x2, y2 = pt2.ravel() |
#軌跡をマスクに描画 |
mask = cv2.line(mask, (int(x2), int(y2)), (int(x1), int(y1)), [0, 0, 200], 2) |
#現フレームにオプティカルフローを描画 |
frame = cv2.circle(frame, (int(x2), int(y2)), 5, [0, 0, 200], -1) |
#フレームとマスクを合成 |
img = cv2.add(frame, mask) |
オプティカルフローの処理結果を出力すると以下のような画像が得られます。
このように動画上の物体を検知し、その移動軌跡を描画することができます。
いかがだったでしょうか。今回は、オプティカルフローという計算方法を使用して動画上の物体移動軌跡の描画を行ってみました。オプティカルフローの計算方法は他にもありますので、それはまた別の機会でご紹介させていただきます。