ホームセキュリティカメラをRaspberryPi+Pythonで実現します。
カメラとマイクを使用して音声付き動画を取ります。
一定以上の動体検知もしくは音量となった場合にはGoogleDriveに自動的にアップロードすることで家の外から動画を確認できるようにします。
使用するもの
・Raspberry Pi3 ModelB
・Picamera(赤外線フィルタレス) hbvcam
・USBマイク サンワサプライ製 MM-MCU01BK
ソースコード
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
#coding: utf-8 import datetime import wave import numpy as np import subprocess import os import cv2 from pydrive.auth import GoogleAuth from pydrive.drive import GoogleDrive RecordTime = 120 #録画時間 sound_th = 0.5 #音量閾値 moment_th = 1000 #動体検知の閾値 moment_max = 0 moment = [] gauth = GoogleAuth() gauth.CommandLineAuth() drive = GoogleDrive(gauth) folder_id = 'XSYgAwGLsViGzp2PcZi9srUk2dXaxXjWa109' while True: #時刻取得 before = datetime.datetime.now() strtime = before.strftime('%Y%m%d_%H:%M:%S') FileNameH = strtime + ".h264" FileNameW = strtime + ".wav" moment_max = 0 max_sound = 0 cmd1 = 'raspivid -t ' +str(RecordTime*1000) + ' -w 640 -h 480 -fps 30 -o ' + FileNameH cmd2 = 'arecord -d ' +str(RecordTime) + ' -D plughw:1,0 -f cd ' + FileNameW print("録画開始 %s" %(strtime)) ps1 = subprocess.Popen(cmd1.split(),stdout=subprocess.PIPE) ps2 = subprocess.Popen(cmd2.split(),stdout=subprocess.PIPE) ps1.stdout.close() outs,errs = ps2.communicate() print(outs.decode('utf-8')) print("録画終了") #h264ファイルを読み込む cap = cv2.VideoCapture(FileNameH) # 背景差分の設定 fgbg = cv2.bgsegm.createBackgroundSubtractorMOG() # 背景オブジェクトを作成 # 動体検知 while True: ret, frame = cap.read() # フレームを取得 fgmask = fgbg.apply(frame) # 前景領域のマスクを取得する moment.append(cv2.countNonZero(fgmask)) # フレームが取得できない場合はループを抜ける if not ret: break # 撮影用オブジェクトとウィンドウの解放 cap.release() #降順で50番目の動体検知値を使用 list.sort(moment,reverse=True) moment_judge = moment[50] print("動体検知:%d"%(moment_judge)) #for n in moment[:100]: # print(n) moment.clear() #waveファイルを読み込む wfile = wave.open(FileNameW, 'r') numsamples = wfile.getnframes() data = wfile.readframes(numsamples) wfile.close() result = np.frombuffer(data,dtype="int16") / float(2**15) #閾値判定用に最大値を計算 max_sound = max(result) print('音量:%s \n' %(max_sound)) #判定 閾値を越えたらmp4を作成・保存 if(max_sound > sound_th or moment_judge > moment_th): FileNameMp4 = strtime + ".mp4" cmd3 = 'ffmpeg -y -i '+ FileNameW + ' -r 30 -i ' + FileNameH +' -vcodec copy ' + FileNameMp4 runcmd = subprocess.run(cmd3.split()) #print(runcmd) #print('%s \n' %(strtime)) #googleドライブへアップロード print('-----upload開始-----') f = drive.CreateFile({'title': FileNameMp4, 'mimeType': 'video/mp4', 'parents': [{'kind': 'drive#fileLink','id': folder_id}]}) f.SetContentFile(FileNameMp4) f.Upload() print('-----upload終了-----') else: pass moment.clear() os.remove(FileNameH) os.remove(FileNameW) |
解説
(1) GoogleDrive認証
1 2 3 4 |
gauth = GoogleAuth() gauth.CommandLineAuth() drive = GoogleDrive(gauth) folder_id = 'XSYgAwGLsViGzp2PcZi9srUk2dXaxXjWa109' |
GoogleDriveにアクセスするための認証作業です。
Pythonプログラムと同じディレクトリに下記の「settings.yaml」と「client_secret.json」の2つのファイルを置きます。
settings.yamlには下記を記載します。
1 2 3 4 5 6 7 |
client_config_file: client_secret.json save_credentials: True save_credentials_backend: file save_credentials_file: credentials.json get_refresh_token: True |
client_secret.jsonはGoogle Developer Consoleで新しいプロジェクトを作成した後、認証情報からダウンロードできます。
folder_idはGoogleDrive上で取得した動画を置きたいフォルダを指定します。
(2) 動画と音声を取得
1 2 3 4 5 6 7 8 9 10 |
cmd1 = 'raspivid -t ' +str(RecordTime*1000) + ' -w 640 -h 480 -fps 30 -o ' + FileNameH cmd2 = 'arecord -d ' +str(RecordTime) + ' -D plughw:1,0 -f cd ' + FileNameW print("録画開始 %s" %(strtime)) ps1 = subprocess.Popen(cmd1.split(),stdout=subprocess.PIPE) ps2 = subprocess.Popen(cmd2.split(),stdout=subprocess.PIPE) ps1.stdout.close() outs,errs = ps2.communicate() print(outs.decode('utf-8')) print("録画終了") |
カメラの動画とマイクの音声を取得します。
内容の詳細は下記の別記事に記載しました。
https://non-it-engineer.com/?p=199
(3) 動体検知
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 |
#h264ファイルを読み込む cap = cv2.VideoCapture(FileNameH) # 背景差分の設定 fgbg = cv2.bgsegm.createBackgroundSubtractorMOG() # 背景オブジェクトを作成 # 動体検知 while True: ret, frame = cap.read() # フレームを取得 fgmask = fgbg.apply(frame) # 前景領域のマスクを取得する moment.append(cv2.countNonZero(fgmask)) # フレームが取得できない場合はループを抜ける if not ret: break # 撮影用オブジェクトとウィンドウの解放 cap.release() #降順で50番目の動体検知値を使用 list.sort(moment,reverse=True) moment_judge = moment[50] print("動体検知:%d"%(moment_judge)) #for n in moment[:100]: # print(n) moment.clear() |
カメラの動画内で動きがあった時のみ記録するようにします。そのための動体検知にOpenCVライブラリを使用しました。
突発的な変化での記録を防ぐために今回は取得したフレームの降順で50番目の動体検知の値を使用しました。
(4) 判定及びGoogleDriveアップロード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#判定 閾値を越えたらmp4を作成・保存 if(max_sound > sound_th or moment_judge > moment_th): FileNameMp4 = strtime + ".mp4" cmd3 = 'ffmpeg -y -i '+ FileNameW + ' -r 30 -i ' + FileNameH +' -vcodec copy ' + FileNameMp4 runcmd = subprocess.run(cmd3.split()) #print(runcmd) #print('%s \n' %(strtime)) #googleドライブへアップロード print('-----upload開始-----') f = drive.CreateFile({'title': FileNameMp4, 'mimeType': 'video/mp4', 'parents': [{'kind': 'drive#fileLink','id': folder_id}]}) f.SetContentFile(FileNameMp4) f.Upload() print('-----upload終了-----') else: pass |
音量もしくは動体検知値が閾値以上の場合にmp4ファイルを作成し、GoogleDriveにアップロードしています。
結果
今回は2分間の動体検知・音量検知した動画を自動的にGoogleDriveにアップロードすることができました。
ただ動画を録画→動体検知値計算→音量計算→判定→GoogelDriveアップロードを順次処理しているため、録画2分、その他の処理で8分程かかってしまい、かなりの時間で抜け漏れておりセキュリティとしては物足りない出来です。
今後、並列処理による改善をはかりたいと思います。