神社 地域 にデジタルを

簡易で安価なカメラで防犯・外出対策を

2023年09月08日 | 最終更新日:2023年09月08日 | 情報・便利・考えなど |
cv2 pillow Fkask

在宅中でも外出先であっても、社務所の前の様子が確認ができる。そして、来訪者があった場合には、Lineに通知して素早く情報をキャッチできるようにしてみました。

USB接続のWebカメラを使った、お手軽で安価な方法になります。何かの参考になれば幸いです。


当方は神職一人であるため、基本、日中留守はできない原則です。

いつ来訪があるかもわからないため、休日の概念もありません。

ただ、四六時中激務が続くというわけではなく、待機中・空き時間・・・のような状態の場合も少なくはありません。


とはいえ、実際には出張祭典他、留守にせざるを得ない場合も多々あります。そこで、来訪者があったかどうか確認し、然るべき手が取れるようにしたいと考えました。

また、日常の防犯といった上でも有効だと考えています。


概要になりますが、以下に記します。



まずカメラを接続


ELP 1080Pウェブカメラ 200万画素赤外線ナイトビジョン 防犯カメラ 監視カメラHDのUSBドームカメラ セキュリティおよび監視家庭 事務所設置 【防水 暗視 (ブラック) ¥6,899 税込


暗視カメラ機能があり、屋外対応となっていたので、これにしました。

しかしはっきりいって、このカメラで屋外での暗視は全く機能しません。夜間は真っ暗です。ですので、もっと安いWEBカメラで十分だと思います。



Flaskで動かす

今回は、Webアプリの構築が簡易にできるFlaskを用いました。

動体検知のプログラムを記述し、来訪の動きをキャッチできるように工夫します。


<app.py>

import cv2
import time
from flask import Flask, render_template, Response

import datetime
import requests

token = 'Vxxxxx3JBxxxxxxpaytSxxxxxxxxxxaxxxqwz7bxx' # lineのアクセストークン

# Webcam 監視
video = cv2.VideoCapture(0)

def get_frame():
    
    red = (0, 0, 255) # 枠線の色
    before = None # 前回の画像を保存する変数
    fps = int(video.get(cv2.CAP_PROP_FPS)) #動画のFPSを取得    
    
    # 監視範囲
    xmin, xmax = 340,630
    ymin, ymax = 220,460
    
    # 背景差分の設定
    fgbg = cv2.bgsegm.createBackgroundSubtractorMOG() # 背景オブジェクト
    
    while True:
        success, frame = video.read()

        detframe = frame[ymin:ymax,xmin:xmax] # 背景差分する範囲を指定 
        cv2.rectangle(frame,(xmin,ymin),(xmax,ymax),(255,255,255),1)

        fgmask = fgbg.apply(detframe) # 前景領域のマスクを取得 
        moment = cv2.countNonZero(fgmask) # 動体検知した画素数を取得
        
        text = 'Motion:' + str(moment)
        font = cv2.FONT_HERSHEY_SIMPLEX 
        cv2.putText(frame,text,(20,440),font,1,(0,255,0),2,cv2.LINE_AA) #フレームに表示 
        
        # 全体の動体検知
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)        
        if before is None:
            before = gray.astype("float")
            continue
        #現在のフレームと移動平均との差を計算
        cv2.accumulateWeighted(gray, before, 0.5)
        frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(before))
        #frameDeltaの画像を2値化
        thresh = cv2.threshold(frameDelta, 3, 255, cv2.THRESH_BINARY)[1]
        #輪郭のデータを得る
        contours = cv2.findContours(thresh,
                        cv2.RETR_EXTERNAL,
                        cv2.CHAIN_APPROX_SIMPLE)[0]
        

        # 差分があった点を画面に描く
        for i, target in enumerate(contours):
            x, y, w, h = cv2.boundingRect(target)
            # print("width:",x+w, "height:", y+h)
            if w < 30: continue # 小さな変更点は無視
            cv2.rectangle(frame, (x, y), (x+w, y+h), red, 2)

            # 画像保存とLine通知の条件 
            if moment > 5000 and moment < 15500:
                if (x+w) >450 and (y+h) > 400:
                                   
                    print("動体検知しました。:", moment, "width:", (x+w), "height:", (y+h))
                    time_now = datetime.datetime.now()
                    strtime = time_now.strftime('%Y%m%d_%H_%M_%S')
                    print(strtime + '_' + str(i))
                    
                    # cv2.imwrite(f"./images/{FileName}", frame)
                    cv2.imwrite(
                        f'C:/Users/xxxo/Documents/ot_xxxn/APP_Flask/images/diff{strtime}_{str(i)}.jpg',frame)
                    
                    # Line
                    payload = {'message': '動体検知しました'}  # 送信メッセージ
                    url = 'https://notify-api.line.me/api/notify'
                    headers = {'Authorization': 'Bearer ' + token}
                    
                    files={'imageFile':open(
                        f"C:/Users/xxxo/Documents/ot_xxxn/APP_Flask/images/diff{strtime}_{str(i)}.jpg","rb")}
                    res = requests.post(url, data=payload, headers=headers,files=files,)  # LINE NotifyへPOST
                

        #ウィンドウでの再生速度を元動画と合わせる
        time.sleep(1/fps)
        # ウィンドウで表示
        # cv2.imshow('target_frame', frame)
        
        sc, encoded_image = cv2.imencode(".jpg", frame)
        frame = encoded_image.tobytes()
        yield (b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + \
            frame + b'\r\n')
    

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/video_feed_url')
def video_feed():
    return Response(get_frame(), mimetype="multipart/x-mixed-replace; boundary=frame")


# if __name__ == '__main__':
#     app.run(debug=True)

# 別のコンピュータでアクセスできるようにする
if __name__ == '__main__':
    app.run(debug=False, host="0.0.0.0", port=x3x1x)

実行すると、こんなふうに動きます。


俊敏に動作をキャッチしますが、調整を施さないと、人物がいない状態での検知が多量になります。

そこで、白枠内の領域内限定で一定以上の大きなモージョンがあって、かつ、赤枠で捉える変化の幅と高さを一定レベル以上のものに限定しました。このダブルチェックで得た検知のみを、ローカルに保管・Lineに通知するように工夫してみました。

ただしこれでも、天気の移り変わり(曇から晴れ)や日の出・日の入りなど、画面変化が激しい場合、人がいなくとも検知がかなり生じます。

このあたりは、機械学習を採用するとより正確になるかと思います。

参考にさせていただきました。

参照:https://qiita.com/seri28/items/3ae4a2c87e352e976b46

参照:https://non-it-engineer.com/pythonopencv%E3%81%A7%E6%8C%87%E5%AE%9A%E7%AF%84%E5%9B%B2%E3%81%AE%E3%81%BF%E3%81%AB%E7%94%BB%E5%83%8F%E5%87%A6%E7%90%86%E3%82%92%E8%A1%8C%E3%81%86%E6%96%B9%E6%B3%95/

他のデバイスからも確認できるようにする

host="0.0.0.0"(112行目)とすることで、外部の接続が可能になるので、同じLANにあれば、どのデバイス(スマートフォンなど)からも接続して確認が可能になります。

(アドレスは、ipconfigコマンドでIPV4を探して設定します。192.xxx.xxx...)


検出した画像を保存

以下のような画像がフォルダに保存されます

画像は私です

外出先から確認

遠方のどこにいても、社務所の前を確認できるようにします。

ngrokというツールを活用します。

ngrokはローカルホスト接続を外部から見れるようにしてくれるもので、大変ありがたいものです。

ngrokのサイトでトークンを作成し、最初に以下コマンドを入力

ngrok config add-authtoken ここにコピーしたトークンを貼り付け

ポート設定のコマンドを入力します

ngrok http 設定したポート番号

外出先から確認できるURLが生成されています

Lineに通知

Lineのアクセストークンを取得しておきます。

(検索すると詳しい説明のあるサイトが多数ありますので割愛します)

<

画像とともに通知が送られてきます

日常的に使うために

画像を消去する

日々の画像がどんどんたまっていくので、Pythonコードを用意して、一括で削除できるようにしておきます。

(Lineは程よいあたりで、自分で手動でデータを削除します)


img_delete.py

from pathlib import Path

target_folder = Path("C:/Users/xxxo/Documents/oxxxon/APP_Flask/images")


for path in target_folder.glob("*.jpg"):
    with open(path, "wb") as f:
        f.write(b"")
    path.unlink()


Windows環境であれば

バッチファイルで一連の動作ができるようにしておくと便利です。

毎朝、ダブルクリックで起動するなどと決めておくと良いかもしれません。


motion_monitor.bat

"C:/Users/oxxxo/AppData/Local/pypoetry/Cache/virtualenvs/app-flask-zgkzrAnn-py3.9/Scripts/python.exe" c:/Users/oxxxo/Documents/oxxxxxhon/APP_Flask/img_delete.py
"C:/Users/oxxxo/AppData/Local/pypoetry/Cache/virtualenvs/app-flask-zgkzrAnn-py3.9/Scripts/python.exe" c:/Users/oxxxo/Documents/oxxxxhon/APP_Flask/app.py

あくまで簡易なものですが、拡張していくと賽銭泥をキャッチするのに有用な転用ができるかと思います。

私の願い

私は神社の宮司です。神社や地域を担う次世代の人々に対し、何かを残してお役に立ててもらいたいとの願いが、強く芽生えました。個業としての神社や、小規模な地域社会に、恩恵が届くのが遅くなりそうな「デジタル」の分野。門外漢として奮闘した実体験から得た経験則を、わずかずつでも残し未来につなぎたいと願うばかりです。

More

若宮 住吉神社

サイトへ

最近の投稿

Copyright ©All rights reserved | by omo fun

Made withby Toshio Omote