BETA

OpenCVで動体検知をトライ(フレーム間差分法)

投稿日:2019-10-10
最終更新:2019-11-01

OpenCVで動体検知をフレーム間差分方法でやってみた
使ったツールバージョンは以下

  • MacOS Mojave / python3.5.6 / Opencv3.4.2 / VS code1.38.1

コメント

サンプルコードを参考に一部カスタマイズしてトライしてみたけど、なんとなく動いてそう。
掲載したソースは「Mac」で動作確認済み(ラズパイでも動作するはず)
FPS表示値は少し嘘っぽいなー
動画イメージを掲載したかったが方法がわからず断念
途中の画像のみ掲載しました

簡単な概要

  • 3画像で差分検出し変化した箇所を動体検出としている
  • 画像は単に配列データとして扱う
  • カラーの配列データをグレースケール画像の配列データに変換
  • 差分箇所を論理積演算で特定する
  • 差分箇所の外接を矩形で囲む
  • 動体検知すると「Motion Detected」と表示
  • 変化量を「Change Vol 数値」で表示

イメージ画像(その1)

イメージ画像(その2)

ファイル構成

  • motion_detect.py : 動体検知ソースファイル(下のソースコード)

ソースコード

# -*- coding: UTF-8 -*-  
import cv2  

cap_camera = None  
getimg1 = None  
getimg2 = None  
getimg3 = None  
view_frame = None  
before = None  

# Device0のcamera  
cap_camera = cv2.VideoCapture(0)  
## 画像サイズをVGAにする  
cap_camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)   
cap_camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)   

########################################  
# 動体検出差分取得メソッド[フレーム間差分法]  #  
########################################  
def motion_check(img1,img2,img3):  
    ## グレースケール画像に変換   
    gray1 = cv2.cvtColor(img1, cv2.COLOR_RGB2GRAY)  
    gray2 = cv2.cvtColor(img2, cv2.COLOR_RGB2GRAY)  
    gray3 = cv2.cvtColor(img3, cv2.COLOR_RGB2GRAY)  
    # 第一画像と第二の画像差の絶対差分値  
    diff_12 = cv2.absdiff(gray1, gray2)  
    # 第二画像と第三の画像差の絶対差分値  
    diff_23 = cv2.absdiff(gray2, gray3)  
    ############################################################  
    # 同じ画像データの同じ箇所を1 違う箇所を0にする。                 #  
    # 論理積ANDををとることで変化があるところの差分を検出する           #  
    ############################################################  
    bitwise_diff_and = cv2.bitwise_and(diff_12, diff_23)  
    ## 色の二値化 ###  
    ret, diff_thresholding = cv2.threshold(bitwise_diff_and, 30, 255, cv2.THRESH_BINARY)  
    ## ノイズ除去##  
    diff = cv2.medianBlur(diff_thresholding, 5)  
    return diff  

# 最初の画像を取得  
getimg1 = getimg2 = getimg3= cap_camera.read()[1]  

while True:  
    #FPS算出のため、事前に時間を取得  
    tick = cv2.getTickCount()  
    # 「フレーム間差分法」を使って3画像から差分を検出する  
    diff = motion_check(getimg1, getimg2, getimg3)  
    # 差分箇所が0でdiffに入っているので、0の数を数える  
    cnt = cv2.countNonZero(diff)  
    ## 最新画像をグレースケールに変換 ##  
    gray = cv2.cvtColor(getimg3,cv2.COLOR_BGR2GRAY)  
    if before is None :  
        before = gray.copy().astype('float')  
        continue    

    # 差分箇所が500以上である程度変化するが多い時だけ動体検知とする  
    if cnt > 500:  
        # 加重平均  
        cv2.accumulateWeighted(gray,before,0.7)  
        # 差分検出  
        diff_frame = cv2.absdiff(gray, cv2.convertScaleAbs(before))  
        # 画素値が閾値より大きければある値(白色)を割り当て,閾値より小さい別の値(黒色)を割り当てる  
        thl = cv2.threshold(diff_frame,5,255,cv2.THRESH_BINARY)[1]  
        image, contours, hierarchy = cv2.findContours(thl.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  
        max_area = 0  
        target = None  
        for i in contours:  
            area = cv2.contourArea(i)  
            if max_area < area and area < 30000 and area > 3000:  
                max_area = area  
                target = i  

        # 動いているエリアが500以上を外接矩形で囲む  
        if max_area >= 500:  
            # 輪郭に外接する長方形を取得する  
            x,y,w,h = cv2.boundingRect(target)  
            # 矩形で囲んだ画像を作成  
            areaframe = cv2.rectangle(getimg3,(x,y),(x+w,y+h),(0,255,0),2)  
            # 検出文字列を表示用データ作成  
            cv2.putText(areaframe, "Motion Detected", (10,405), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)  
            # 表示用に載せ替え  
            view_frame = areaframe  
    else:  
        # 表示用に載せ替え  
        view_frame = getimg3  

    # 画像データを移動し最新データをgetimg3に保存する  
    getimg1 = getimg2  
    getimg2 = getimg3  
    getimg3 = cap_camera.read()[1]  

    # FPS計算  
    fps = cv2.getTickFrequency() / (cv2.getTickCount() - tick)  
    # FPSを左下に表示  
    cv2.putText(view_frame, "FPS:{} ".format(int(fps)),(10, 430), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)  
    # 変化量を左下に表示  
    cv2.putText(view_frame, "Change Vol:{}".format(int(cnt)),(10,450), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)  
    cv2.imshow('Motion Detect Image', view_frame)  

    # ESCキー  
    k = cv2.waitKey(1)  
    if k == 27:  
        break  

cap_camera.release()  
cv2.destroyAllWindows()   
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@主にプログラム技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!