↓もし、良かったらSNSでの紹介よろしくお願いします。

OpenCV(Python)動体検知のサンプルコードで遊んでみた。

bootstrap

OpenCVで動体検出

昨日の記事「12行で動体検出!?OpenCV(Python)」でOpenCVでの動体検出が簡単であるということを紹介しました。今回はOpenCVのサンプルコードを書き換えて遊んでみました。サンプルコードはOpenCVのチュートリアルのページにあります。

注意

動体検出のコードはOpenCV2系では動かないのでOpenCV3系を使いましょう。PythonにOpenCV3系を入れる場合は「OpenCVをPython3にpipで入れてみた」を参考にしてください。

サンプルコードの実行結果

threading_sunflower

このように動体(人)が白、背景が黒、影が灰色といった具合に分けられています。ちなみにこのgifは圧縮のためカクカクになってます。実際の実行結果はなめらかです。

この実行結果で気になることが2つあります。1つは影(灰色部分)を背景にして表示したいことです。もう1つは前景にちり状のノイズがあることです。まず、この2点をOpenCVを使って解決します。

サンプルコード

OpenCVのサンプルコードを少し書きえたコードです。書き換えた内容は、コメントを追加、終了条件を追加、IF文の位置の移動です。今回はこのコードをベースにします。

import numpy as np
import cv2
cap = cv2.VideoCapture('vtest.avi')
#動体検出器の生成
fgbg = cv2.createBackgroundSubtractorMOG2()
while(1):
    #動画を1フレーム読み込む
    ret, frame = cap.read()
    #「Escが押される」または「動画フレームがない場合」終了
    k = cv2.waitKey(30) & 0xff
    if k == 27 or not ret:
        break
    #動体、背景、影に分ける
    fgmask = fgbg.apply(frame)
    cv2.imshow('frame',fgmask)
cap.release()
cv2.destroyAllWindows()

影を背景に

これは2値化を使って解決します。2値化処理については「OpenCV(Python)2値化の使い方」で解説してます。

    #動体、背景、影に分ける
fgmask = fgbg.apply(frame)

この部分に2値化処理を加える。

    #動体、背景、影に分ける
fgmask = fgbg.apply(frame)
_, fgmask = cv2.threshold(fgmask,127,255,cv2.THRESH_BINARY)

オープニング処理でちりを消す

オープニング処理については「OpenCVオープニング(opening)とクロージング(closing)でマスク画像を加工」で解説してます。

while文より前に下記のコードを入れましょう。

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))

kernelはざっくり説明すると、ちりの大きさを表しています。

    #動体、背景、影に分ける
fgmask = fgbg.apply(frame)
_, fgmask = cv2.threshold(fgmask,127,255,cv2.THRESH_BINARY)

この部分の後に下記のオープニング処理を追加する。

    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)

ここまでの結果を実行

おおむね上手くいってますね。

threa

マスクを元の動画に合成する

白黒だとつまらないので動画にマスクを合成します。マスク画像を0-1の行列にしたいので下記のように2値化の上限値を1にします。

    #動体、背景、影に分ける
fgmask = fgbg.apply(frame)
_, fgmask = cv2.threshold(fgmask,127,1,cv2.THRESH_BINARY)

この0-1のマスク行列を画像の各チャンネルの行列にかけて再合成すると前景だけが浮き上がります。この処理をコードで書くと次のようになります。

    #動体、背景、影に分ける
fgmask = fgbg.apply(frame)
_, fgmask = cv2.threshold(fgmask,127,1,cv2.THRESH_BINARY)
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
bgr = cv2.split(frame)
new_bgr = list(map(lambda x:x * fgmask, bgr))
result = cv2.merge(new_bgr)

実行結果

黒服の人が多くて見栄えは良くない…(白黒に見えますが、ちゃんとカラーになってます。)

threaa

サンプルコード(最終結果)

import numpy as np
import cv2
cap = cv2.VideoCapture('vtest.avi')

#動体検出器の生成
fgbg = cv2.createBackgroundSubtractorMOG2()
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
while(1):
    #動画を1フレーム読み込む
    ret, frame = cap.read()
    #「Escが押される」または「動画フレームがない場合」終了
    k = cv2.waitKey(30) & 0xff
    if k == 27 or not ret:
        break
    #動体、背景、影に分ける
    fgmask = fgbg.apply(frame)
    _, fgmask = cv2.threshold(fgmask,127,1,cv2.THRESH_BINARY)
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    bgr = cv2.split(frame)
    new_bgr = list(map(lambda x:x * fgmask, bgr))
    result = cv2.merge(new_bgr)
    cv2.imshow('frame', result)
cap.release()
cv2.destroyAllWindows()