有名な動画処理に「動体検知」がありますが、PythonのOpenCVを使えば簡単に実装することができます。ここでは動画ファイルに対して行うPythonとOpenCVを使った動体検知のコードを紹介します。
こんにちは。wat(@watlablog)です。
この記事では、動画処理の王道も王道である「動体検知」のPythonプログラムを紹介します!
動体検知プログラムとは?
そもそも動体検知とは、動くものを検出するプログラムのことです。
具体的には動画で取得した映像に何の変化も無く完全に静止している場合は黒い画面(つまり画素の輝度値が0)になり、動きがある場合はその部分だけが白くなる(画素の輝度値が255)になるようなプログラムのことです。
このプログラムを実現するためには背景画像と動体を切り分けるということを行います。
最初から背景画像がある場合は比較的簡単なプログラムが想像されますが、通常は人混み等常に動きのある映像に使用される技術のため、初期値として背景が無い場合に適用されるケースがほとんどです。
背景画像が初期に無い場合は、映像のフレーム前後の変化の有無で動体かどうかを判断するアルゴリズムが使用されます。
動体検知プログラムで出来ること2選
①動きがあった瞬間の画像を保存できる
他にも色々あったかも知れませんが、僕がまず初めに思い浮かべたソリューションはこの「動きのあった瞬間の画像を保存できる」というものでした。
例えば、監視カメラというのは常時映像を記録しておき、後で何か事件や事故があった場合に映像を解析してヒントを得るという使い方がほとんどだと思います。
しかし、監視カメラの映像を記録するためのHDD等の記録媒体にも容量の限りがあります。
容量が一杯になってしまった時は古いデータを削除して新しいデータに上書きするというドライブレコーダのような仕様になっていると思います。
もし監視する対象がほとんど動きの無いもので、何か動きがあった時に限り映像を記録したいという用途であった場合(生物系の実験者に多いかも?)はこの仕様だと超ムダな映像が残ってしまいますね。
そんな時、動体を検知した時のみ画像を保存することで大部分のムダを省くことができます。
②動きのある物体のトラッキングができる
トラッキングとは、日本語で追跡を意味します。
ビデオカメラ等で撮影した映像の中で、動いている物体のトラッキングをすることで、その物体の速度や輪郭のリアルタイム検出ができます。
センサーの貼れない物体の位置や速度検出、機械学習のための動画解析等、工学分野の人であれば利用場面は多々あります。
動体検知は我々工学屋の大きな武器になってくれますね。
PythonとOpenCVで行う動体検知プログラムコード
サンプル動画
まず始めに、本記事で動体検知をお試しするサンプル動画を紹介します。
以下のYouTube動画が今回のサンプル動画です。馬型のヤジロベエがバランスを取ろうと振動している様子が確認できると思います。
今回はこのヤジロベエ部分だけを検出するということを行います。
全コード
以下が動画ファイルを読み込み動体検知をするプログラムの全コードです。詳細はその下に説明していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import cv2 # 動画読み込みの設定 movie = cv2.VideoCapture('video.mp4') # 動画ファイル保存用の設定 fps = int(movie.get(cv2.CAP_PROP_FPS)) # 動画のFPSを取得 w = int(movie.get(cv2.CAP_PROP_FRAME_WIDTH)) # 動画の横幅を取得 h = int(movie.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 動画の縦幅を取得 fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') # 動画保存時のfourcc設定(mp4用) video = cv2.VideoWriter('video_out.mp4', fourcc, fps, (w, h), False) # 動画の仕様(ファイル名、fourcc, FPS, サイズ) # 背景差分の設定 fgbg = cv2.bgsegm.createBackgroundSubtractorMOG() # 背景オブジェクトを作成 # ファイルからフレームを1枚ずつ取得して動画処理後に保存する while True: ret, frame = movie.read() # フレームを取得 fgmask = fgbg.apply(frame) # 前景領域のマスクを取得する video.write(fgmask) # 動画を保存する # フレームが取得できない場合はループを抜ける if not ret: break # 撮影用オブジェクトとウィンドウの解放 movie.release() |
動画読み込み/書き込み/背景差分の設定
importでOpenCVことcv2を読み込んでいる所は既に説明がいらないくらいだと思いますが、その下に記述している設定部分を最初に説明しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 動画読み込みの設定 movie = cv2.VideoCapture('video.mp4') # 動画ファイル保存用の設定 fps = int(movie.get(cv2.CAP_PROP_FPS)) # 動画のFPSを取得 w = int(movie.get(cv2.CAP_PROP_FRAME_WIDTH)) # 動画の横幅を取得 h = int(movie.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 動画の縦幅を取得 fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') # 動画保存時のfourcc設定(mp4用) video = cv2.VideoWriter('video_out.mp4', fourcc, fps, (w, h), False) # 動画の仕様(ファイル名、fourcc, FPS, サイズ) # 背景差分の設定 fgbg = cv2.bgsegm.createBackgroundSubtractorMOG() # 背景オブジェクトを作成 |
動画ファイルでもWebカメラでも、基本的にフレーム毎にデータを取得する関数にcv2.VideoCaptureを使います。今回は動画ファイルなので、引数にファイルへのパスを示しています。
fpsや動画の縦横サイズw, h、fourccは動画保存オブジェクトvideoで使う引数です。fourccは動画のフォーマットで、four-character codeの略です。今回はmp4ファイルを作成するので、データフォーマットを識別する4byteの文字列として「mp4v」を指定しています。
videoとして作成している動画保存オブジェクトはVideoWriterで作成していますが、最後の引数は今回保存する動画がカラー画像でないためFalseになっています。
カラー画像のフレームを保存する場合は空欄で良いのですが、取得した映像はカラーでも、動体検知をした後の黒と白の画像はカラー画像ではないのでこの設定になっています。
ちなみにこのFalseを指定しないと、エラーは発生しませんが動画は1kB程度の容量となり再生することもできません。
僕もしばらく悩まされましたが、過去記事でカラー画像とグレースケール画像のshapeの違いがあると学んだことに気付くことができ、リファレンスを調べたらわかりました…!初心者には結構厳しいのでは?
動画からフレームを取得して動体検知/ファイル保存する
設定が終了したら、続いて動画ファイルを読み込んで画像処理(動体検知)、そして動画ファイルへの保存を実行するwhileループです。
このwhileループはファイルからフレームが取得できている間は回り、取得できなかったらループを抜ける仕様です。このようにif not ret:と書くのが効率良さそうです。
1 2 3 4 5 6 7 8 9 |
# ファイルからフレームを1枚ずつ取得して動画処理後に保存する while True: ret, frame = movie.read() # フレームを取得 fgmask = fgbg.apply(frame) # 前景領域のマスクを取得する video.write(fgmask) # 動画を保存する # フレームが取得できない場合はループを抜ける if not ret: break |
ちなみに動体検知は.applyを行うことで前景領域でマスク画像を作ることができます。これは他の処理にも使えそうな関数ですね。
video.writeで動画を保存していますが、これはフレーム毎に1枚1枚ファイルに追加していっているイメージです。
実行結果
上記コードを実行すると以下の結果を得ます。
元動画にはチェック柄の背景等ありましたが、それらは全て黒く塗りつぶされ、動いているヤジロベエのみが白くなっています。
まとめ
本記事では動体検知プログラムの概要や、動体検知プログラムで実現できるソリューションを紹介しました。
また、実際にサンプルの動画に対して動体検知を実行し、しっかりと動いている部分のみを抽出することができることを確認しました。
動画処理の王道である動体検知を習得しました!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
コメント