動画での物体追跡

映像関連のアプリケーションを作っていると、物体追跡の知識が役立つ場面があります。
オープンソースのコンピュータビジョンライブラリであるOpenCVを使うと実現できるので、簡単なサンプルで説明します。

コード(追跡function)

Python
import cv2


# 物体を追跡するfunction ↓↓↓
def track_object(tracker, frame):
    # 現在のフレームから対象を探す
    found, box = tracker.update(frame)

    # 対象を見失った場合
    if not found:
        return None

    # 座標を整数に変換して返す
    x, y, width, height = map(int, box)
    return x, y, width, height

呼び出し例

以下は、functionの呼び出し方だけを示したコードです。first_frame、initial_box、video_frames の取得処理は省略しているため、この部分だけでは実行できません。

Python
# functionを呼び出す部分
tracker = cv2.TrackerKCF_create()

# 最初の画像と対象範囲を設定する
tracker.init(first_frame, initial_box)

# フレームごとにfunctionを呼び出す
for frame in video_frames:
    position = track_object(tracker, frame)

    if position is None:
        continue

    # 取得した座標を後続の処理で使用する
    x, y, width, height = position

このコードでは、比較的処理の軽いKCFトラッカー(高速フーリエ変換(FFT)と巡回行列(Circulant Matrix)の数学的特性を利用し、計算量を大幅に削減してリアルタイム処理を実現しています。なんのこっちゃですが…)を使用しています。
物体検出のように毎フレーム画像全体を調べるのではなく、最初に指定された対象の周辺を中心に追跡するため、処理量を抑えられます。

さらに軽くしたい場合は

  • 入力画像を640×360などへ縮小(映像の縦横比を保ったまま縮小)してから追跡する
  • 同時に追跡する物体を1つに限定する
  • 2~3フレームに1回だけ追跡処理を実行する
  • 追跡に不要な画像加工や画面描画を減らす
  • カメラの解像度やフレームレートを下げる

画像を小さくしすぎたり処理するフレームを減らしすぎたりすると、小さな対象や動きの速い対象を見失いやすくなります。実際の映像に合わせて、処理速度と追跡精度のバランスを調整します。

実際に使う

上記のコードには入出力処理がないため、Webカメラを入力として使用する簡単なサンプルを作成します。

必要なライブラリのインストール

python -m pip install opencv-contrib-python

続いて、入力・表示部分
以下のコードを実行する場合は、先ほどのtrack_object()をon_mouse()より前に追加してください。

Python
import cv2

CAMERA_INDEX = 0
TARGET_SIZE = 120
HALF_TARGET_SIZE = TARGET_SIZE // 2
WAIT_TIME_MS = 1
QUIT_KEY = ord("q")
BOX_COLOR = (0, 255, 0)
BOX_THICKNESS = 2
WINDOW_NAME = "Object Tracking"

# この位置に[def track_object(tracker, frame)]をまるごと挿入する #

def on_mouse(event, mouse_x, mouse_y, _flags, _param):
    global tracker

    if event == cv2.EVENT_LBUTTONDOWN:
        left = mouse_x - HALF_TARGET_SIZE
        top = mouse_y - HALF_TARGET_SIZE
        box = (left, top, TARGET_SIZE, TARGET_SIZE)

        tracker = cv2.TrackerKCF_create()
        tracker.init(frame, box)


camera = cv2.VideoCapture(CAMERA_INDEX)
_, frame = camera.read()
tracker = None

cv2.namedWindow(WINDOW_NAME)
cv2.setMouseCallback(WINDOW_NAME, on_mouse)

while True:
    display_frame = frame.copy()

    if tracker is not None:
        position = track_object(tracker, frame)

        if position is not None:
            left, top, width, height = position
            cv2.rectangle(
                display_frame,
                (left, top),
                (left + width, top + height),
                BOX_COLOR,
                BOX_THICKNESS,
            )

    cv2.imshow(WINDOW_NAME, display_frame)

    if cv2.waitKey(WAIT_TIME_MS) == QUIT_KEY:
        break

    _, frame = camera.read()

camera.release()
cv2.destroyAllWindows()

メインコードの説明

 このサンプルでは、Webカメラの映像をOpenCVのウィンドウに表示し、クリックした場所を中心とする120×120ピクセルの範囲を追跡対象として登録します。
 on_mouse() は、映像上でマウスがクリックされたときに呼び出されます。クリック座標から追跡範囲を計算し、KCFトラッカーへ最初の画像と対象範囲を設定します。
 その後、映像を1フレームずつ取得し、先ほど作成した track_object() を呼び出します。追跡に成功すると、返された座標を使って対象の周囲に緑色の四角枠を表示します。
 別の場所をクリックすると、新しい位置を対象として追跡をやり直します。Qキーを押すと処理を終了します。
 なお、KCFは顔や人物の種類を認識しているわけではありません。クリックした範囲内の画像的な特徴を記憶し、次のフレームから似た領域を探しています。

使い方

コードを実行すると、Webカメラの映像が表示されます。追跡したい対象の中心をクリックすると、その周囲の120×120ピクセルが追跡対象として登録され、緑色の四角枠が表示されます。
追跡対象を変更する場合は、新しい対象をもう一度クリックしてください。Qキーを押すと終了します。

Web版Demo

一応、webアプリ版も作ってみました

ご利用上の注意

 このコードは、OpenCVによる物体追跡の基本的な仕組みを説明するための簡易サンプルです。実用アプリとして必要になる詳細なエラー処理や設定機能は省略しています。
 カメラを取得できない場合や、画像の端をクリックした場合には、正常に動作しないことがあります。また、対象が画面外へ移動した場合、ほかの物体に隠れた場合、見た目や大きさが大きく変化した場合には、追跡に失敗することがあります。
 本サンプルは動作や追跡精度を保証するものではありません。使用するカメラ、OpenCVのバージョン、撮影環境などに応じて調整してください。カメラ映像を取得・保存・公開する場合は、被写体のプライバシーや利用場所の規則にもご注意ください。

← 技術記事一覧へ戻る