
動画の中を移動する物体の動きをトラッキング(追跡)することが出来れば、その物体の変位や速度、加速度といった物理量を計算することが出来ます。ここでは、Python/OpenCVを使って動画内物体トラッキングコードの例を紹介します。
こんにちは。wat(@watlablog)です。本日は動画処理の例題として、物体トラッキングの方法を紹介します!
目次(項目クリックでジャンプできます)
物体トラッキング処理で得る情報
トラッキングとは?
トラッキング(Tracking)とは、日本語で「追跡」を意味する単語です。
言葉の使われ方は分野によって様々ですが、例えばマーケティング分野では、インターネットを閲覧しているユーザがどのページを見てどのリンクを辿ったかを調べることもトラッキングと呼びます。
また信号処理の分野では、回転体のFFT分析でわかる回転次数の変化を追うこともトラッキングと呼びます。
このページでは動画内の物体の移動を追跡する内容を扱い、同じくトラッキングと呼びます。
物体トラッキングでわかること
軌跡(変位)がわかる
動画から物体の動きを追跡するという処理は、そのままで軌跡情報が得られていることになります。
物理や工学の実験では着目している物体がどの方向にどれだけ動いたかを知ることは、事象の理解にとても重要です。
変位センサ、GPSセンサを付けられる規模であれば良いのですが、微小範囲を取り扱ってセンサを付けることが出来なかったり、そもそもセンサを取付けることで事象の変化が懸念される場合等は動画で分析することが多々あります。
速度や加速度がわかる
動画で軌跡や変位が数値としてわかるということは、速度や加速度といった情報も同時に得ることが可能です。
動画撮影や動画ファイルにはFPS(Frames Per Second)、フレームレートとも呼ばれる時間情報を持っています。
変位と速度、加速度はそれぞれ時間による微積分関係にあるため、FPS情報が取得できれば算出可能となります。
物体トラッキング手順の例
ここで、物体トラッキング手順の例を紹介していきます。「~例」としているのは、このトラッキング技術はケースによって様々な手法を使い分ける必要があり、万能な方法がないからです。
ここでは物体の特徴量として輪郭情報を検出し、その情報から位置座標を取得する方法を紹介します。
あくまで一例ですので、異なる物体特徴量検出の方法を覚えたらそれは物体トラッキングの手段が増えたことにもなります。
他の例では、ディープニューラルネットワークを使った検出が最新技術でしょうか。CNNやYOLOといったキーワードで検索すると色々出て来ますね。これらはAI技術ですが、ご興味があればAIカテゴリのページも是非見てみて下さい!
動画内物体の輪郭から位置を特定するために必要な画像処理技術
動画のフレーム処理
動画内の物体トラッキングをするためには、まずは動画ファイルを扱う方法を知る必要があります。
動画といってもパラパラ漫画のような多数のフレームで構成されています。そのため、基本はフレームに対する画像処理をする方法を覚えておけば問題ありません。
以下の記事はそれぞれ「動画のフレームに対しある画像処理をする」という内容ですので、必要であれば是非ご覧下さい。
「Python/OpenCVで動体検知!動画の動いている部分を検出」
「Python動画編集!動画に位置制御したテロップを入れる方法」
「Pythonでブログの広告が目立つかどうか「動的」に評価する方法」
グレースケール化と二値化
今回は動画の各フレーム画像に対し、物体の輪郭を検出する手法を使います。
輪郭を始めとする様々な特徴量の抽出には、カラー画像をそのまま扱うのではなく、グレースケール化したり二値化したりといった前処理を行うことが一般的です。
以下の記事はグレースケール化や二値化に関する内容ですので、こちらも必要に応じてご覧下さい。
「Python/OpenCVで画像の二値化をする方法」
「Python/OpenCVの適応的閾値処理で綺麗な二値化!」
輪郭検出処理
本題の輪郭抽出方法ですが、輪郭は上記グレースケール化と二値化処理を使って行います。
詳しくは以下の記事に記載しましたので、併せてご覧下さい。
「Python/OpenCVで画像内オブジェクトの輪郭抽出をする」
輪郭から物体トラッキングをするPythonコード
サンプル動画
今回コードのテストで使用するサンプル動画を以下のYoutube動画に示します。
…かなり適当感はありますが、当WATLABブログのロゴマークを黒い背景の中で動かしてみました。
ロゴマークは背景の中を振幅100[px]の正弦波で動いています。
全コード
先ほどのサンプル動画に対し、物体トラッキングをする全コードを以下に示します。ポイントは輪郭検出を行っているdef関数です。
輪郭検出で得られたcontoursには輪郭の座標情報が格納されています。この座標情報をx, y軸それぞれで平均をとったものを物体の座標としています。
その他は既に紹介した記事内容と同じです。
しかし、今回はカラー画像を扱っているので、VideoWriterの最後の引数がTrueになっている点が注意点です(これをFalseにすると動画が正常に保存されない)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import numpy as np import cv2 from matplotlib import pyplot as plt # 画像から輪郭を検出する関数 def contours(img): img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # グレースケール化 ret, img_binary = cv2.threshold(img_gray, # 二値化 60, 255, # 二値化のための閾値60(調整要) cv2.THRESH_BINARY) contours, hierarchy = cv2.findContours(img_binary, # 輪郭検出 cv2.RETR_EXTERNAL, # 外側の輪郭のみ抽出 cv2.CHAIN_APPROX_SIMPLE) contours = np.array(contours) # 輪郭情報をndarrayに変換 x = np.mean(contours[0].T[0, 0]) # 輪郭のx方向平均値を算出 y = np.mean(contours[0].T[1, 0]) # 輪郭のy方向平均値を算出 return x, y movie = cv2.VideoCapture('video.mp4') # 動画ファイルの読み込み # 動画ファイル保存用の設定 fps = int(movie.get(cv2.CAP_PROP_FPS)) # 動画のFPSを取得 w = int(movie.get(cv2.CAP_PROP_FRAME_WIDTH)) # 動画の横幅を取得 h = int(movie.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 動画の縦幅を取得 fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') # 動画保存時のfourcc設定(mp4用) video = cv2.VideoWriter('video_out.mp4', fourcc, fps, (w, h), True) # 動画の仕様(ファイル名、fourcc, FPS, サイズ, カラー) # ファイルからフレームを1枚ずつ取得して動画処理後に保存する x_list = [] y_list = [] while True: ret, frame = movie.read() # フレームを取得 # フレームが取得できない場合はループを抜ける if not ret: break x, y = contours(frame) # 輪郭検出から物体中心を算出 frame = cv2.circle(frame, (int(x), int(y)), 30, (0, 255, 0), 3) # 検出した位置にサークル描画 video.write(frame) # 動画を保存する x_list.append(x) y_list.append(y) # 動画オブジェクト解放 movie.release() |
実行結果
以下の動画が実行結果です。
移動する物体に対しcv2.circleで円を描画していますが、見事にトラッキング出来ている結果を得ました。
おまけ:軌跡情報の取得
以下はおまけですが、このコードを先ほどのコードの一番下に追加することで、グラフ表示が出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# ここからグラフ描画 # フォントの種類とサイズを設定する。 plt.rcParams['font.size'] = 14 plt.rcParams['font.family'] = 'Times New Roman' # 目盛を内側にする。 plt.rcParams['xtick.direction'] = 'in' plt.rcParams['ytick.direction'] = 'in' # グラフの上下左右に目盛線を付ける。 fig = plt.figure() ax1 = fig.add_subplot(111) ax1.yaxis.set_ticks_position('both') ax1.xaxis.set_ticks_position('both') # スケール設定 ax1.set_xlim(0, 800) ax1.set_ylim(0, 400) # 軸のラベルを設定する。 ax1.set_xlabel('x') ax1.set_ylabel('y') # データプロット ax1.scatter(x_list, y_list, label='Tracking result') plt.legend() fig.tight_layout() # グラフを表示する。 plt.show() plt.close() |
以下のグラフが軌跡です。綺麗な正弦波を得ることが出来ました。
縦軸は画像の場合下が正ですが、このグラフは上が正になっていることに注意が必要です。

このように軌跡情報が数値であれば、後は既に得ているFPS情報を使って時間の関数にすることも可能ですね。
まとめ
本記事では動画から各フレームを取得し、フレームに対しグレースケール化と二値化処理を施し輪郭抽出を行い、輪郭情報から位置座標を連続的に取得することで軌跡を得るPythonコードを紹介しました。
動画処理は基本を習得すればどれも同じような流れなので、今後も何か画像処理を覚えたら動画へも応用してみたいと思います。
今回は輪郭情報からトラッキングをしてみましたが、他にも色々な手法がありそうですね!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
コメント