RaspberryPi 3 Model B+でIoT監視カメラをつくる(その6 プログラムの修正) | そう備忘録

RaspberryPi 3 Model B+でIoT監視カメラをつくる(その6 プログラムの修正)

RaspberryPi(ラズパイ)でIoT監視カメラ

RaspberryPi 3 Model B+とカメラ(Raspberry Pi Camera Module V2)とモーションセンサー(HC-SR501)で動きがあった時だけ録画するIoT監視カメラを作成したときの備忘録の6回目。

今回は3回目の記事のプログラムに多少の変更を加えたので別記事として残しておくことにした。

ちなみに前回までのプログラムについてはこちらを参照。

過去関連記事

1回目はRaspberryPiとカメラ、モーションセンサーとの接続に関するこちらの記事を参照

2回目はGoogleDriveにアクセスする為のGoogle Developersの設定とLINEにメッセージを送信するためのLINE Notifyでのアクセストークンの発行に関する記事

3回目はPythonのプログラムの説明の記事

4回目はプログラムの起動環境、CLI起動とプログラムの自動起動に関する記事

5回目のケースの外箱を製作した時の記事はこちら

全体構成図

繰り返しになるがIoT監視カメラの全体構成図を載せておく。

モーションセンサーで動きを検知してカメラで撮影した映像をクラウド(GoogleDrive)にアップロード※してLINEで伝える仕組みになっている。

※今まではh364形式の映像ファイルをアップロードしていた。

監視カメラ全体構成図

ハードウェア

使用したハードウェアは以下の通り。

Raspberry Pi 3B+

尚、この時はRaspberry Pi 3B+ で作成したが今なら Raspberry Pi 4B で作成するのが良いと思う。

後述するプログラムはラズパイ4でも問題なく動作した。

カメラモジュール

モーションセンサー

変更点

プログラムの変更点は以下の通り。

  1. 映像の上部にタイムスタンプを表示するようにした
  2. ファイル形式をh264からmp4に変換した後にGoogle Driveにアップロードするようにした

今まではファイル名の一部に日付時刻を使っていたので映像に敢えて日付時刻を表示する必要は無いかと思ってのだが使っているうちにやはり映像にもタイムスタンプが合ったほうが分かりやすいと思い直したので追加表示することにした。

また映像ファイルはラズパイカメラでの標準的なファイル形式(拡張子がh264)で保存してGoogleDriveにアップロードしていたが再生できるプレイヤーが少なく、特にスマートフォンで再生させるためには別途アプリをインストールする必要があったので「それならばラズパイ側でmp4形式に変換してからアップロードしよう」と思い変更する事にした。

GPACのインストール

事前にラズパイにGPAC※をインストールする。

※GPACはオープンソースのマルチメディアフレームワーク。GPACのMP4Boxを使ってh264からmp4に変換を行う

sudo apt-get install gpac

GPACのインストール

プログラム

# -*- coding: utf-8 -*-
"""
Created on Sun Jul 28 09:32:20 2019
@author: Souichirou Kikuchi
2019/11/04 映像上部に日付・時刻を追加表示
2019/11/22 h264からmp4へファイル変換
"""

from picamera import PiCamera
from time import sleep
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from concurrent.futures import ThreadPoolExecutor # スレッド処理
import RPi.GPIO as GPIO
import datetime as dt
import os
import json
import requests # LINEメッセージ
import subprocess # MP4Boxコマンド実行の為

SC_CAMERA_PIN = 5 # ピンの名前を変数として定義
MAX_REC_TIME = 3600 # 最長録画時間(秒数)
SAVE_DIR = "./video/" # ファイル保存用ディレクトリ
INITIAL_FILE= "./cert/initial.json" # 初期設定ファイル
LINE_URL = "https://notify-api.line.me/api/notify" # LINEメッセージ用URL
DRIVE_LINK = "https://drive.google.com/open?id=" # LINEに表示するGoogleDriveへのリンク
INTERVAL = 0.5 # 監視間隔(秒)
AN_TEXT_SIZE = 24 # 録画画像上部に表示される注釈文字のサイズ

GPIO.setmode(GPIO.BCM) # ピンをGPIOの番号で指定
GPIO.setup(SC_CAMERA_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # GPIOセットアップ

class  SecurityCameraClass: # セキュリティカメラのクラス
    def __init__(self):
        with open(INITIAL_FILE) as f: # 初期設定ファイルの読み込み
            jsn = json.load(f)
            self.folder_id = jsn["folder_id"] # folder_idの読み込み
            self.token = jsn["line_token"] # LINE用tokenの読み込み
            self.location = jsn["location"] # 監視カメラ設置場所
        self.camera = PiCamera()
        # self.camera.annotate_text = self.location
        # self.camera.annotate_text_size = AN_TEXT_SIZE
        self.camera.rotation = 270 # カメラを回転
        gauth = GoogleAuth() # GoogleDriveへの認証
        gauth.LocalWebserverAuth()
        self.drive = GoogleDrive(gauth)

    def StartRecording(self): # 録画開始
        time_stamp_file =  "{0:%Y-%m-%d-%H-%M-%S}".format(dt.datetime.now()) # 日付時刻をセット
        time_stamp_disp =  "{0:%Y/%m/%d %H:%M:%S}".format(dt.datetime.now()) # 日付時刻をセット
        self.save_file_path = SAVE_DIR + "video" + time_stamp_file + ".h264" # ディレクトリ、ファイル名をセット
        self.camera.annotate_text = self.location + " " + time_stamp_disp # 映像上部に表示 
        self.camera.annotate_text_size = AN_TEXT_SIZE
        self.camera.start_recording(self.save_file_path) # 指定pathに録画
        return True 

    def StopRecording(self): # 録画終了
        self.camera.stop_recording() # 録画終了
        executor = ThreadPoolExecutor(max_workers=3) # 同時実行は3つまでスレッド実行
        executor.submit(self.OnTheread)
        return False 

    def OnTheread(self): # ファイルのGoogleDriveへのアップロードは時間がかかるので別スレッドで行う
        del_file_name = self.save_file_path # 対象pathを保存しておかないと別スレッドなので更新されてしまう
        mp4_file_path = os.path.splitext(del_file_name)[0] + '.mp4' # 拡張子をmp4にしたファイル名
        file_name = os.path.basename(mp4_file_path) # ファイル名部分を取り出し
        # h264形式からmp4に変換    
        res=subprocess.call("MP4Box -add " + self.save_file_path + " " + mp4_file_path, shell=True)
        if res == 0: # 変換が正常終了ならファイルをアップロード
            f = self.drive.CreateFile({"title": file_name, # GoogleDrive 
                                  "mimeType": "video/mp4",
                                  "parents": [{"kind": "drive#fileLink", "id":self.folder_id}]})
            f.SetContentFile(mp4_file_path) # ファイル名指定
            f.Upload() # アップロード
            os.remove(del_file_name) # アップロード後にファイルは削除
            os.remove(mp4_file_path) # アップロード後にファイルは削除
            sec_camera.LineMessage() # LINEにメッセージを送信

    def LineMessage(self): # LINEに録画検知しましたメッセージを送信する
        headers = {"Authorization" : "Bearer " + self.token}
        message = "録画検知しました " + DRIVE_LINK + self.folder_id
        payload = {"message" : message}
        requests.post(LINE_URL, headers=headers, params=payload)

    def CloseCamera(self): # カメラクローズ
        self.camera.close()

#main
try:
    if __name__ == "__main__":
        os.chdir(os.path.dirname(os.path.abspath(__file__))) # カレントディレクトリをプログラムのあるディレクトリに移動する
        sec_camera = SecurityCameraClass()
        rec = False # 録画中フラグ OFF
        start_detect = dt.datetime.now() # 検知開始時刻
        while True:
            rec_time = dt.timedelta(seconds=0)
            if GPIO.input(SC_CAMERA_PIN) == GPIO.HIGH: # 検知
                if rec == False: # 録画 OFFなら
                    start_detect = dt.datetime.now() # ビデオスタート時刻
                    rec = sec_camera.StartRecording() # 録画開始
                rec_time  = dt.datetime.now() - start_detect # 録画時間を計算
                if  rec_time.total_seconds() >= MAX_REC_TIME: # 録画最大時間を超えた時
                    rec = sec_camera.StopRecording() # 録画終了
                    start_detect = dt.datetime.now() # ビデオスタート時刻
                    rec = sec_camera.StartRecording() # 録画開始
            else: # 未検知
                if rec == True: # 録画 ON なら
                    rec = sec_camera.StopRecording() # 録画終了
                    start_detect = dt.datetime.now() # ビデオスタート時刻リセット
 #           print("GPIO",GPIO.input(SC_CAMERA_PIN)," rec=",rec," 録画秒数",rec_time.total_seconds())
            sleep(INTERVAL)
except KeyboardInterrupt:
    pass
GPIO.cleanup()
sec_camera.CloseCamera()

主な変更点

映像上部に日付時刻を表示

クラスのコンストラクタでアノテーション(注釈)テキストとサイズを指定していたのをコメントアウト(41行目)。

        # self.camera.annotate_text = self.location
        # self.camera.annotate_text_size = AN_TEXT_SIZE

代わりにStartRecording時にアノテーションテキストとサイズを指定する(49行目)。

        time_stamp_file =  "{0:%Y-%m-%d-%H-%M-%S}".format(dt.datetime.now()) # 日付時刻をセット
        time_stamp_disp =  "{0:%Y/%m/%d %H:%M:%S}".format(dt.datetime.now()) # 日付時刻をセット
        self.save_file_path = SAVE_DIR + "video" + time_stamp_file + ".h264" # ディレクトリ、ファイル名をセット
        self.camera.annotate_text = self.location + " " + time_stamp_disp # 映像上部に表示 
        self.camera.annotate_text_size = AN_TEXT_SIZE

h264からmp4に変換

拡張子をmp4にしてファイル名を生成してMP4Boxコマンドで変換する(65行目)。

        mp4_file_path = os.path.splitext(del_file_name)[0] + '.mp4' # 拡張子をmp4にしたファイル名
        file_name = os.path.basename(mp4_file_path) # ファイル名部分を取り出し
        # h264形式からmp4に変換    
        res=subprocess.call("MP4Box -add " + self.save_file_path + " " + mp4_file_path, shell=True)

修正は以上。

その他

その後、上記のプログラムに更に修正を加えた。

GoogleのTensorFlowを使って映像を機械学習でObject Detection(物体検出)してモノが写っている時だけ動画ファイルをクラウドの保存するようにした。

その時の記事はこちら

souichirou

やった事を忘れない為の備忘録 同じような事をやりたい人の参考になればと思ってブログにしてます。 主にレゴ、AWS(Amazon Web Services)、WordPress、Deep Learning、RaspberryPiに関するブログを書いています。 仕事では工場に協働ロボットの導入や中小企業へのAI/IoT導入のアドバイザーをやっています。 2019年7月にJDLA(一般社団法人 日本デイープラーニング協会)Deep Learning for GENERALに合格しました。 質問は記事一番下にあるコメントかメニュー上部の問い合わせからお願いします。

おすすめ

質問やコメントや励ましの言葉などを残す

名前、メール、サイト欄は任意です。
またメールアドレスは公開されません。