人や動物、自動車が通過したら画像を撮影するプログラムを作ったので紹介します。最近流行りのDEEP LEARNING やAIを作るためには、膨大な画像が必要となるため、自動撮影した方が効率的だと思ったので、自動撮影プログラムを作りました。これを使えば、家の前を通過する車や、動物を撮影することができます。最終的には、どんな車、どんな動物が来ているかなど写真を撮ることができます。
画像撮影プログラムってどうやって作るの?家の前を通過したものを自動で撮影するプログラムを作りたいです。
PythonとopenCVがあれば簡単に作れます。例えば、動体を検知したら撮影を行う、ものが入ったら撮影を行うなどです。
前回の記事で画像を撮影する方法はわかったから、自動で撮影する方法を教えてください。
わかりました。今回は自動で撮影、画像ファイルを保存するプログラムを作っていきましょう!
【この記事の対象者】
・PythonとopenCVを学びたい。
・openCVを使って写真をとりたい。
・機械学習用の画像を準備したい。
PythonとOpenCVで自動撮影する方法
前回の投稿でカメラを撮影する方法を紹介しました。今回は動体検知して、撮影するプログラムを作りたいと思います。撮影する際には何かのトリガーが必要となります。今回は注目する領域に動体が入ったら、撮影を行うといったプログラムを作っていきたいと思います。
OpenCV の基礎
OpenCVとはIntelが開発したライブラリで誰でも使用することができるオープンソースのライブラリです。インターネット上でもPythonのプログラムでたくさん公開されているので、画像処理の基礎を学ぶためには非常におすすめです。現在では、画像処理や機械学習、人工知能など多様なことに使えるので、技術者としては覚えておくべきと思います。
今回のプログラムで使用するクラス(基本的なもの)
VideoCapture(0)
これを使用すると、()内の数値を0または1に変更すると、現在接続されているビデオデバイスを変更できます。(0)であれば、内臓カメラと接続されます。また、動画ファイルのPath(ディレクトリ)を指定すれば、動画ファイルを読み込むこともできます。
cap = cv2.VideoCapture(0)
cv2.threshold(img, しきい値, 最大値, しきい値処理の方法)
こちらを使用すると、しきい値の処理ができます。一般的な画像処理方法である、2値化処理、適応的しきい値処理、大津の二値化など処理画像に合わせて、選択すると、適切な前処理を行うことができます。前処理をしっかりすることで、正確に行いたい処理ができます。OpenCVのチュートリアルは下記のURLに記載しております。チュートリアルには様々な処理方法やプログラム例があるので非常に参考になります。
thresh = cv2.threshold(frameDelta, 10, 255, cv2.THRESH_BINARY)
cv2.findContours(入力画像、抽出モード、輪郭検出方法)
1つ目の戻り値は輪郭のリスト、2つ目の戻り値は輪郭の階層情報(hierarchy)を返します。また、引数には、入力画像、抽出モード、輪郭検出方法や、色などを指定することができます。cv2.CHAIN_APPROX_SIMPLEを輪郭検出方法に使用することで、抽出する座標の数が減り、メモリの消費量が減ります。基本的には、こちらで大丈夫と思います。
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
実際のプログラミングコード
実際に動体検知を行うためのコードとしては、現在の画像と現在までの移動平均の差分をとることで、動体検知を行っております。具体的には、移動平均を算出するため、cv2.accumulateWeighted(img, avg, 0.60)を用いて、現在のフレームとの差分を計算しています。プログラム上では下記のコードで計算しています。
avg=cv2.accumulateWeighted(gray, avg, 0.60)
frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
下記のファイルを実行すると、画面が表示され、赤枠内にて動体が発生すると、画像を撮影するプログラムとなっています。動体が確認されると、カウントアップされるプログラムになります。参考に動画を載せておきます。動体が赤枠内に表示されると、カウント・写真撮影が行われ、画像フォルダに画像が撮影されます。
import cv2
import pandas as pd
import glob
cap = cv2.VideoCapture(0)
# 初期設定#####################################
fps=int(cap.get(cv2.CAP_PROP_FPS))
W = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
H = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
size = (W, H)
avg = None
frameNo=0
P_count=0
list=[0,0,0,0,0]
pointX=200 #動体検知範囲の設定
pointY=300 #動体検知範囲の設定
widthX=200 #動体検知範囲の設定
widthY=200 #動体検知範囲の設定
##################################################
while True:
# 1フレームずつ取得する。
ret, frame = cap.read()
if not ret:
break
frameNo=frameNo+1
fps=cap.get(cv2.CAP_PROP_FPS)
# グレースケールに変換
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 比較用のフレームを取得する
if avg is None:
avg = gray.copy().astype("float")
continue
bin_frame = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY)
# 現在のフレームと移動平均との差を計算
avg=cv2.accumulateWeighted(gray, avg, 0.60)
frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
frameDelta=frameDelta[pointY:pointY+widthY,pointX:pointX+widthX]#動体領域を設定
# デルタ画像を閾値処理を行う
thresh = cv2.threshold(frameDelta, 10, 255, cv2.THRESH_BINARY)
# 画像の閾値に輪郭線を入れる
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
thresh_con = frame.copy()
for contour in contours:
area=cv2.contourArea(contour)
if area>2000:
x, y, w, h = cv2.boundingRect(contour) #動体領域に枠線を入れる
cv2.rectangle(thresh_con, (x+pointX, y+pointY), (x+pointX + w, y+pointY + h), (0, 255, 0), cv2.LINE_4)
list.pop(0)
list.append(frameNo)
if list-list[0]>30 and list-list<5:
P_count+=1
cv2.imwrite('./img/img'+str(P_count)+'.jpg', frame)
cv2.putText(thresh_con,
text='area='+str(area),
org=(x+pointX-10, y+pointY-10),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=1,
color=(0, 255, 0),
thickness=2,
lineType=cv2.LINE_4)
cv2.putText(thresh_con,
text='Count='+str(P_count),
org=(10, 30),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.5,
color=(0, 255, 0),
thickness=2,
lineType=cv2.LINE_4)
cv2.rectangle(thresh_con, (pointX, pointY), (pointX+widthX, pointY+widthY), (0, 0, 255), cv2.LINE_4) #動体領域を枠線で表示(赤領域)
cv2.imshow("Frame3", thresh_con)
cv2.imshow("frame",bin_frame)
cv2.imshow("frame",frameDelta)
key = cv2.waitKey(1)
if key == 27:
break
cap.release()
cv2.destroyAllWindows()