🐶

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

に公開

【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 くらいに設定してちょうどよく動体検知してくれた。

GitHubで編集を提案

Discussion