OpenCV: Optical Flowで物体の動きをトラックする
先週に引き続き、OpenCVを使っていきます。
Optical Flow
Optical Flowを使って、物体の動きを検出していきます。OpenCVのチュートリアルと↓の記事を参考にしました。
- OpenCVでとらえる画像の躍動、Optical Flow - Qiita
- OpenCVでオプティカルフローをリアルタイムに描画する(Shi-Tomasi法、Lucas-Kanade法) - Qiita
Optical Flowは動画から切り出されたフレーム画像間の特徴点の差分 (= 動き) をベクトル化することで、主に動画内の物体の動きのトラッキングなどに用いられます。
車が走っている動画1から2枚のフレームを切り出したとき、車の動きを左下へのベクトルとして表現します。
Optical Flowには2つのポイントがあります。
- 特徴点をどうやって検出するか
- フレーム間の特徴点の一致をどうやって判別するか
Optical Flowの特徴点の検出では、Shi-Tomasiのコーナ検出がよく使われるそうです。 (提案論文2のタイトルもそのものズバリの"Good Features to Track"です。ストレート。)
ベースとなっているのはHarris3のコーナ検出です (Harrisのコーナ検出についてはこちらのPDFの解説が詳しいです) 。Harrisのコーナ検出では最後に得られる2つの固有値が大きい画素 (複数のエッジ = コーナ) として判定しています。Shi-Tomasiはこの閾値の計算方法が異なるだけです。
特徴点の一致を判定する方法として、今回はLucas-Kanade法4を使いました。以下の条件に基づいて、フレーム前後で特徴点をトラックします。こちらのスライドが参考になります。
- 特徴点の周囲の画素も同様に動く (空間的整合性) と仮定している
- ピラミッド (解像度が1, 1/2, 1/4, ... となる画像の集合) を使って、大きな動きも小さな動きに変換することで追随させる
実装
それでは実装していきます。最初に動画をロードします。
- OpenCVではVideoCaptureクラスで動画ファイルを扱います
import numpy as np import matplotlib.pyplot as plt import cv2 from google.colab.patches import cv2_imshow # Google Colaboratory用 # NHKクリエイティブライブラリの動画 # https://www2.nhk.or.jp/archives/creative/material/view.cgi?m=D0002060316_00000 file_path = "D0002060316_00000_V_000.mp4" # 動画ファイルのロード video = cv2.VideoCapture(file_path)
特徴点抽出
最初のフレームで特徴点を抽出します。動画は150フレーム目から210フレーム目まで使用します (合計60フレーム) 。
- setメソッドで、1つ目の引数に定数
CAP_PROP_POS_FRAMES
、2つ目の引数に取得したいフレームインデックスを指定 - Shi-TomashiのアルゴリズムはgoodFeaturesToTrackメソッドで実装されてます
- パラメータはコメントのとおりです
# 150フレームから210フレームまで5フレームごとに切り出す start_frame = 150 end_frame = 210 interval_frames = 5 i = start_frame + interval_frames # 最初のフレームに移動して取得 video.set(cv2.CAP_PROP_POS_FRAMES, start_frame) ret, prev_frame = video.read() # グレースケールにしてコーナ特徴点を抽出 prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_RGB2GRAY) feature_params = { "maxCorners": 200, # 特徴点の上限数 "qualityLevel": 0.2, # 閾値 (高いほど特徴点数は減る) "minDistance": 12, # 特徴点間の距離 (近すぎる点は除外) "blockSize": 12 # } p0 = cv2.goodFeaturesToTrack(prev_gray, mask=None, **feature_params) # 特徴点をプロットして可視化 for p in p0: x,y = p.ravel() cv2.circle(prev_frame, (x, y), 5, (0, 255, 255) , -1) cv2_imshow(prev_frame)
抽出後の画像を見ると傾向がわかります。
- 文字 (左上) や木や枝はコーナーが多いので、そういった部分は特徴点として抽出されやすくなってます
- 自動車もボンネットとバンパーの間、フェンダの折目など、カーブしている部分が抽出されてます
Optical Flow
最後にOptical Flowの実装です。
- Lucas-Kanade法はcalcOpticalFlowPyrLKで実装されてます
- 前後のフレームと前のフレームの特徴点を渡すと、後のフレームにおいて対応する各特徴点のインデックスが返されます
- 対応する特徴点の有無は2つ目の返り値で判定 (0:無い, 1:ある)
- パラメータはコメントのとおりです
- 前後のフレームと前のフレームの特徴点を渡すと、後のフレームにおいて対応する各特徴点のインデックスが返されます
# OpticalFlowのパラメータ lk_params = { "winSize": (15, 15), # 特徴点の計算に使う周辺領域サイズ "maxLevel": 2, # ピラミッド数 (デフォルト0で、2の場合は1/4の画像まで使われる) "criteria": (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03) # 探索アルゴリズムの終了条件 } # 可視化用 color = np.random.randint(0, 255, (200, 3)) mask = np.zeros_like(prev_frame) for i in range(start_frame + interval_frames, end_frame + 1, interval_frames): # 次のフレームを取得してグレースケールにする video.set(cv2.CAP_PROP_POS_FRAMES, i) ret, frame = video.read() frame_gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) # OpticalFlowの計算 p1, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, frame_gray, p0, None, **lk_params) # フレーム前後でトラックが成功した特徴点のみを identical_p1 = p1[status==1] identical_p0 = p0[status==1] # 可視化用 for i, (p1, p0) in enumerate(zip(identical_p1, identical_p0)): p1_x, p1_y = p1.ravel() p0_x, p0_y = p0.ravel() mask = cv2.line(mask, (p1_x, p1_y), (p0_x, p0_y), color[i].tolist(), 2) frame = cv2.circle(frame, (p1_x, p1_y), 5, color[i].tolist(), -1) # 可視化用の線・円を重ねて表示 image = cv2.add(frame, mask) cv2_imshow(image) # トラックが成功した特徴点のみを引き継ぐ prev_gray = frame_gray.copy() p0 = identical_p1.reshape(-1, 1, 2)
トラックした結果は以下です。
180フレーム目 (特徴点抽出から30フレーム経過後) を見ると、自動車の特徴点は自動車とともに動いていますが、風景の特徴点は動かずそのままであることがわかります。
さらに30フレームが経過した210フレーム目を見ると、今度は自動車と風景の特徴点が重なり、異なるところにトラックされている点が多いことがわかります。また自動車の動きから外れてしまった特徴点なども見られます。
ある程度のフレーム数が経過したら特徴点を取り直す、といった工夫が必要なようです。
まとめ
今回はOpenCVを使ってOptical Flowで物体の動きをトラッキングしました。
-
NHKクリエイティブ・ライブラリーの動画を使用しました。https://www2.nhk.or.jp/archives/creative/material/view.cgi?m=D0002060316_00000↩
-
Shi, Jianbo & Tomasi, Carlo. (2000). Good Features to Track. Proceedings / CVPR, IEEE Computer Society Conference on Computer Vision and Pattern Recognition. IEEE Computer Society Conference on Computer Vision and Pattern Recognition. 600. 10.1109/CVPR.1994.323794. ↩
-
Chris Harris and Mike Stephens. A combined corner and edge detector. In Proc. of Fourth Alvey Vision Conference. 1988.↩
-
B. D. Lucas, T. Kanade, et al. An iterative image registration technique with an application to stereo vision. In IJCAI, 1981.↩