Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

OpenCVで動画の背景と動体を分離してみる

More than 3 years have passed since last update.

はじめに

OpenCV(Open Source Computer Vision Library)はBSDライセンスの映像/画像処理ライブラリ集です。画像のフィルタ処理、テンプレートマッチング、物体認識、映像解析、機械学習などのアルゴリズムが多数用意されています。

OpenCVを使った動体追跡の例 (OpenCV Google Summer of Code 2015)
https://www.youtube.com/watch?v=OUbUFn71S4s

インストールと簡単な使い方はこちら
OpenCV 3(core + contrib)をPython 3の環境にインストール&OpenCV 2とOpenCV 3の違い&簡単な動作チェック

静止画像のフィルター処理についてはこちら
OpenCVでエッジ検出してみる

動画ファイルの処理についてはこちら
OpenCVで動画をリアルタイムに変換してみる
OpenCVでWebカメラ/ビデオカメラの動画をリアルタイムに変換してみる

今回は、定点カメラで撮影した動画を使って背景と動体を分離してみます。
監視カメラで通りがかりの人を追跡するときに、人を抽出する方法として利用することができます。

アルゴリズム

  • 背景の抽出方法

    フレームに重みづけをしながら重ねていくと、動かない部分が浮かび上がってきます。
    単純に足すのではなく、現在のフレームを足しこんだ分、背景のフレームから引き算します。
    OpenCVでは、cv2.absdiff()を使います。また、重みづけ計算の誤差を減らすため、浮動小数点(np.float32)を用いて計算します。
     

    1. ゼロ埋めした背景フレームを用意する
    2. 新しい背景 = 背景 × (1 - 重み) + 現在のフレーム × 重み
    3. ステップ2を繰り返す  
       
  • 動体の抽出方法
    差分の絶対値が動体フレームになります。
    OpenCVでは、sv2.absdiff()を使います。

      動体フレーム = |現在のフレーム - 背景|

プログラム

diff&accum.py
import cv2
import numpy as np

# 定数定義
ESC_KEY = 27     # Escキー
INTERVAL= 33     # インターバル
FRAME_RATE = 30  # fps

WINDOW_ORG = "org"
WINDOW_BACK = "back"
WINDOW_DIFF = "diff"

FILE_ORG = "org_768x576.avi"

# ウィンドウの準備
cv2.namedWindow(WINDOW_ORG)
cv2.namedWindow(WINDOW_BACK)
cv2.namedWindow(WINDOW_DIFF)

# 元ビデオファイル読み込み
mov_org = cv2.VideoCapture(FILE_ORG)

# 最初のフレーム読み込み
has_next, i_frame = mov_org.read()

# 背景フレーム
back_frame = np.zeros_like(i_frame, np.float32)

# 変換処理ループ
while has_next == True:
    # 入力画像を浮動小数点型に変換
    f_frame = i_frame.astype(np.float32)

    # 差分計算
    diff_frame = cv2.absdiff(f_frame, back_frame)

    # 背景の更新
    cv2.accumulateWeighted(f_frame, back_frame, 0.025)

    # フレーム表示
    cv2.imshow(WINDOW_ORG, i_frame)
    cv2.imshow(WINDOW_BACK, back_frame.astype(np.uint8))
    cv2.imshow(WINDOW_DIFF, diff_frame.astype(np.uint8))

    # Escキーで終了
    key = cv2.waitKey(INTERVAL)
    if key == ESC_KEY:
        break

    # 次のフレーム読み込み
    has_next, i_frame = mov_org.read()

# 終了処理
cv2.destroyAllWindows()
mov_org.release()

実行結果

org.png
                    元画像

diff.png
                    動体抽出画像

back.png
                    背景画像

歩いている人をうまく抽出することができました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away