🐶

【OpenCV + Python】LINE Notifyで動体検知を知らせる

2023/05/27に公開

【OpenCV + Python】LINE Notify で動体検知を知らせる

動作環境

  • OS: macOS Ventura Version13.2.1(M1)
  • Python 3.9.13
  • OpenCV 4.7.0

必要なライブラリのインストール

pip install opencv-python requests

ソースコード

import cv2
import time
import requests

# LINE Notify token (YOUR_LINE_NOTIFY_TOKENを置き換えてください)
LINE_NOTIFY_TOKEN = "YOUR_LINE_NOTIFY_TOKEN"

# 通知間隔に関する変数
notification_interval = 1  # 一度検知したら通知を制御する時間(秒)
last_notification_time = 0  # 最後に通知された時刻

# movie = cv2.VideoCapture('./movie/path.mp4') # 動画の読み込みの場合
movie = cv2.VideoCapture(0)
red = (0, 0, 255)  # 枠線の色
before = None  # 前回の画像を保存する変数
fps = int(movie.get(cv2.CAP_PROP_FPS))  # 動画のFPSを取得

while True:
    # 画像を取得
    ret, frame = movie.read()
    # 再生が終了したらループを抜ける
    if ret == False:
        break
    # 白黒画像に変換
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    if before is None:
        before = gray.astype("float")
        continue
    # 現在のフレームと移動平均との差を計算
    cv2.accumulateWeighted(gray, before, 0.95)
    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 target in contours:
        x, y, w, h = cv2.boundingRect(target)
        if w < 30:
            continue  # 小さな変更点は無視
        cv2.rectangle(frame, (x, y), (x + w, y + h), red, 2)

        # 通知間隔の確認
        current_time = time.time()
        if current_time - last_notification_time >= notification_interval:
            # 動体検知時にLINE通知を送る
            payload = {"message": "Motion detected!"}
            headers = {"Authorization": "Bearer " + LINE_NOTIFY_TOKEN}
            requests.post("https://notify-api.line.me/api/notify", data=payload, headers=headers)
            last_notification_time = current_time

    # ウィンドウでの再生速度を元動画と合わせる
    time.sleep(1 / fps)
    # ウィンドウで表示
    cv2.imshow('target_frame', frame)
    # Enterキーが押されたらループを抜ける
    if cv2.waitKey(1) == 13:
        break

# 内蔵カメラを解放する
movie.release()
cv2.destroyAllWindows()  # ウィンドウを破棄

YOUR_LINE_NOTIFY_TOKEN

に取得した LINE Notify のアクセストークンを代入します。

LINE Notify アクセストークンの発行方法はこちら

  • 通知間隔を設定しないと、一度検知したら連続で通知されてしまうので、通知間隔を設定しました。
movie = cv2.VideoCapture(0)
  • 引数で使用カメラを変更できます。(0 はデフォルトで内蔵カメラ)

結果

動体検知したらこんな感じに枠線でトラッキングして、LINE Notify で知らせてくれた。

感想

過剰に検出して通知してしまうのを程よく抑えるのが苦労した。

学習率を変更したり、フレーム間の差分の閾値を変えたりして、試行錯誤した。
自分の環境では、学習率を 0.975 くらいに設定してちょうどよく動体検知してくれた。

録画されない ZOOM の授業とかを動体検知して、スライドが動いたらスクショするプログラムとかも作れるかもしれない。

GitHubで編集を提案

Discussion