福岡人データサイエンティストの部屋

データサイエンスを極めるため、日々の学習を綴っています。

【OpenCV】動画から動く物体を検出してみた【Python】



こんにちは!こーたろーです。


今回は、前回の動画から動く物体(実際には動かした物体)を検出するコードを書いてみます。


OpenCVのメソッドの多さには驚きですが、使いこなせればかなり便利かと思いますので、一緒に学んでいきましょう!


エラーがでたときは、各メソッドからの出力のサイズと型を考えましょう!


それでは早速まいりましょう!


ライブラリのインポート



今回もOpenCVを使います。また、フォルダコントロールのために、osもインポートしておきます。

import cv2
import os


保存先の定義及び保存用のフォルダの作成



ルートフォルダの中に、WebComというフォルダを作成します。


後々、このフォルダに動く物体を検出して、画像として保存していきます。

data_dir = "./WebCom"
os.mkdir(data_dir)



os.mkdirは、引数に直接文字列を入れてもいいですが、後でディレクトリ情報を使うため、わざわざ変数を使って入れています。

動画データの読み込みと、必要な変数の定義



動画の読み込みは、VideoCaptureで実施します。


前回保存したデータは、「"WebCam.m4v"」ですので、そちらを指定してあげます。

cap = cv2.VideoCapture("WebCam.m4v")
img_last = None
Frame_num = 0



img_lastは、動画の物体を検出するためのイメージデータを格納するarrayなります。


一つ前のフレーム画像を格納するものになります。


初期値としてNoneを入力しておきましょう。


Frame_numは、何番目のフレームを扱っているかの情報になります。


こちらの初期値は0にしておきます。

動画像から動いている物体を検出し、トリミングしながら画像データとして出力する



さて、ここからが難しいところです。


動画のフレームを順次読み込んでいき、前フレームとの差分を抽出することで、動きを捉えていきます。


動きを検出したら、動いた部分を「動く物体」として認識し、その物体部分のみを画像ファイルとして吐き出すような処理を行っていきます。


ベースは、動画を表示させる処理と一緒です。


while文がTrueの間、処理を実行し、cv.imwriteでフレーム中の物体を画像として保存しながら、繰り返していきます。


最初のif文ですが、これは動画がなかった場合にbreakするという文になります。


確認程度で入れています。

while True:
    rep, frame = cap.read()
    if not rep :
        print("data is null")
        break
    
    frame = cv2.resize(frame, (1000, 600))
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gray_frame = cv2.GaussianBlur(gray_frame, (15, 15), 0)
    retval, dst = cv2.threshold(gray_frame, 127, 255, cv2.THRESH_BINARY)

    if not img_last is None:
        frame_diff = cv2.absdiff(img_last, dst)
        contours, hierarchy = cv2.findContours(frame_diff, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        for cont in contours:
            x, y, w, h = cv2.boundingRect(cont)
            if w < 100 or w > 500:
                continue
            img_ex = frame[y:y+h, x:x+w]
            outfile = data_dir +"/" + str(Frame_num) + ".jpg"
            cv2.imwrite(outfile, img_ex)
            Frame_num += 1

    img_last = dst
cap.release()



フレームのリサイズを行った後は、差分抽出のために、グレースケールにしています。


また、GaussianBlurは、グレースケールにしたあとのぼかし処理になります。


グレースケールにした次は、白黒の2値化をしていきます。この時のメソッドがcv2.thresholdになります。


このthresholdの帰り値は、retval: 閾値とdst: 2値画像になっています。そのため、それぞれを変数に格納します。


こんかいはdstの2値画像のみを使っていきます。


その後、一つ前のフレームの2値画像と現在のフレームの2値画像を比較して、差分の輪郭を求めていきます。


差分の輪郭が求まると、その領域は物体が動いている状態になるため、その輪郭のエリアについて、画像を切り取って出力していきます。


if w < 100 or w > 500 は、動いた物体の輪郭の幅が小さいものや大きすぎるものを除外するためのものになります。


出力はこんな感じです。





今回は、OpenCVのメソッドの「cv2.GaussianBlur」「cv2.threshold」「cv2.absdiff」「cv2.findContours」「cv2.boundingRect」が初登場で、エラーが出るたびにこの辺の値を調べ回りました。


Google先生には本当にお世話になっております。


ではでは。