今回はOpenCVを使用してフレーム間差分法を行います。
フレーム間差分法は、動体検知でも使用されている方法です。
その原理をプログラミングのコードを交えて紹介していきたいと思います。
1.使用する画像
今回の使用する材料は以下の動画をフレームで分割した画像を使用します。
元のデータはこちらを参照してください。
また動画を各フレームごとに分割するコードは以下の通りです。
#ライブラリのインポート
import cv2
#import numpy as np
import os
#動画のインポート
def save_all_frame(video_path, dir_path, basename, ext='jpg'):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return
os.makedirs(dir_path, exist_ok=True)
base_path = os.path.join(dir_path,basename)
digit = len(str(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))))
n = 0
while True:
ret, frame = cap.read()
if ret:
cv2.imwrite('{}_{}.{}'.format(base_path, str(n).zfill(digit),ext),frame)
n += 1
else:
return
save_all_frame('vtest.avi', 'C:\\Users\User名\作業ディレクトリ名', 'sample_video_img')
2.処理の流れ
A. 連続する3枚(画像1・画像2・画像3)の画像を用意
B. 3枚の画像をグレースケールに変換
C. それぞれの画像(画像2-画像1・画像3-画像2)の差分画像を作成する
D. 2枚の差分画像の論理積を計算し、論理積画像を作成する
E. 論理積画像に二値化処理を行い、背景と前景に分けたマスク画像を作成する
3.ソースコード
import cv2
import numpy as np
#画像の読み込み
I1 = cv2.imread('sample_video_img_000.jpg', cv2.IMREAD_GRAYSCALE)
I2 = cv2.imread('sample_video_img_005.jpg', cv2.IMREAD_GRAYSCALE)
I3 = cv2.imread('sample_video_img_010.jpg', cv2.IMREAD_GRAYSCALE)
#絶対値の求めたのち、背景差分を求める
img_diff1 = cv2.absdiff(I2,I1)
img_diff2 = cv2.absdiff(I3,I2)
#論理積を算出するには、bitwise_and()関数
Im = cv2.bitwise_and(img_diff1, img_diff2)
#二値化処理
img_th = cv2.threshold(Im, 10, 255,cv2.THRESH_BINARY)[1]
#膨張処理・収縮処理を施してマスク画像を生成
operator = np.ones((3,3), np.uint8)
img_dilate = cv2.dilate(img_th, operator, iterations=4)
img_mask = cv2.erode(img_dilate,operator,iterations=4)
#マスク画像を使って対象を切り出す
img_dst = cv2.bitwise_and(I3, img_mask)
#表示
cv2.imshow("Show BACKGROUND SUBSTRACTION image",img_dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.フレーム間差分とは(ソースコード詳細)
まずフレーム間差分とは、背景差分とは違い背景画像を用意する必要がなく、連続画像の差分から動体検知をすることができる。
ものすごく簡単に言うと、連続する画像から変化量を抽出するということです。
import cv2
import numpy as np
#画像の読み込み
I1 = cv2.imread('sample_video_img_000.jpg', cv2.IMREAD_GRAYSCALE)
I2 = cv2.imread('sample_video_img_005.jpg', cv2.IMREAD_GRAYSCALE)
I3 = cv2.imread('sample_video_img_010.jpg', cv2.IMREAD_GRAYSCALE)
画像をグレースケールとして読み込みます。
グレースケールは、cv2.imread()関数の読み込み方法の指定によって、グレースケールに変換することができます。
使用した画像は変化がわかりやすいように、画像名が000、005、010と5フレーム刻みで読み込みをしました。
読み込んだ画像は以下の通りになります。
#絶対値の求めたのち、背景差分を求める
img_diff1 = cv2.absdiff(I2,I1)
img_diff2 = cv2.absdiff(I3,I2)
次にそれぞれの画像の差分の絶対値を求めていきます。
差分の絶対値を求めるには、cv2.absdiff()を使用します。使い方は、、、
変数名(今回はimg_diff)= cv2,absdiff(変化後の画像,変化前の画像)
イメージは以下の通りです。
取得した差分画像を見ると、それぞれ移動前と移動後の人のシルエットが表示されていますね。
#論理積を算出するには、bitwise_and()関数
Im = cv2.bitwise_and(img_diff1, img_diff2)
論理積を算出するには、bitwise_and()関数を使用します。
bitwise_and()関数は画像の算術演算の1つです。例えば、画像の足し算は、opencvの関数cv2.add()関数を使用するか、Numpyのビット数の計算をするかの2種類あります。opencvを使用した演算ではビット単位で処理をする場合、AND.OR.NOT.XORがある。これらの手法は、特定の領域を抽出したり、特定の領域にのみ処理を行いたいときに有効な処理になっています。AND.OR.NOT.XORの演算方法について以下で紹介します。
論理積画像は以下の通りです。左の画像がそれぞれの差分画像で、右の画像が論理積画像になります。
#二値化処理
img_th = cv2.threshold(Im, 10, 255,cv2.THRESH_BINARY)[1]
#膨張処理・収縮処理を施してマスク画像を生成
operator = np.ones((3,3), np.uint8)
img_dilate = cv2.dilate(img_th, operator, iterations=4)
img_mask = cv2.erode(img_dilate,operator,iterations=4)
#マスク画像を使って対象を切り出す
img_dst = cv2.bitwise_and(I3, img_mask)
次に論理演算画像に二値化処理を行います。
二値化処理にはcv2.threshold()関数を使い、関数の使い方は以下の通りです。
img_th(変数名) = cv2.threshold(使用数画像,閾値最小値,閾値最大値,閾値処理の種類)
閾値処理の種類は以下の5種類になります。
- cv2.THRESH_BINARY
- cv2.THRESH_BINARY_INV
- cv2.THRESH_TRUNC
- cv2.THRESH_TOZERO
- cv2.THRESH_TOZERO_INV
最後に膨張処理と収縮処理を施しマスク画像を生成します。
膨張処理:二値画像の白色の部分を増やす処理・注目画素の周囲(上下左右)に白い画素が1画素でもあれば注目画素を白に置き換える処理
収縮処理:二値画像の白色の部分を減らす処理・注目画素の周囲(上下左右)に黒い画素が1画素でもあれば注目画素を黒に置き換える処理
そしてこの処理を施した画像は以下の通りになります。
以上がフレーム間差分法の流れになります。
これを連続的に処理すると動体検知や、監視カメラ等にも使用されます。
5.おわりに
以上でフレーム間差分法の説明は終了です。
これを発展させて、次回はwebカメラを使用して監視カメラの構築を紹介していきたいと思います。
コメント