【ラズパイ×Python】Discord Botで監視カメラシステムを作る

2024/07/30

Discord ラズパイ

  • B!

作りたいもの

DiscordBotで起動したり、停止したりできる動体検知カメラを作りたい。

DiscordBotを使用することで、外出先からでもコントロール可能なカメラが作成できる。

実際のプログラム

Discord Botの機能としては、監視カメラの起動、停止、画像の確認ができれば良いので、まずは監視カメラのプログラムを作成する。

detect.py
#!/usr/bin/python3
import datetime
import cv2 as cv
import os

save_dir  = '画像の保存先'
fn_suffix = '.jpg'

cap = cv.VideoCapture(0)
cap.set(cv.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv.CAP_PROP_FRAME_HEIGHT, 720)

DELTA_MAX = 255
DOT_TH = 10
MOTHON_FACTOR_TH = 0.10

avg = None

while True:
    ret, frame = cap.read()
    motion_detected = False

    dt_now = datetime.datetime.now()
    dt_format_string = dt_now.strftime('%Y-%m-%d %H:%M:%S')
    f_name = dt_now.strftime('%Y-%m-%d_%H-%M-%S') + fn_suffix
    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)

    if avg is None:
        avg = gray.copy().astype("float")
        continue

    cv.accumulateWeighted(gray, avg, 0.6)
    frameDelta = cv.absdiff(gray, cv.convertScaleAbs(avg))
    thresh = cv.threshold(frameDelta, DOT_TH, DELTA_MAX, cv.THRESH_BINARY)[1]
    motion_factor = thresh.sum() * 1.0 / thresh.size / DELTA_MAX
    motion_factor_str = '{:.08f}'.format(motion_factor)

    cv.putText(frame,dt_format_string,(25,50),cv.FONT_HERSHEY_SIMPLEX, 1.5,(0,0,255), 2)
    cv.putText(frame,motion_factor_str,(25,710),cv.FONT_HERSHEY_SIMPLEX, 1.5,(0,0,255), 2)

    if motion_factor > MOTHON_FACTOR_TH:
        motion_detected = True

    if motion_detected  == True:
        #print('DETECTED')
        cv.imwrite(save_dir + f_name, frame)

    #画像の最大保存数を決める(50枚)
    files = os.listdir(save_dir)
    if len(files) >= 51:
        #print('REMOVED')
        files.sort()
        os.remove(save_dir + files[0])

    k = cv.waitKey(1000)
    if k == 27:
        break

cap.release()
cv.destroyAllWindows()

次にdetect.pyをサービス化する。

念のためdetect.pyとDETECT.serviceには最高権限を与える。

/usr/lib/systemd/system/DETECT.service
[Unit]
Description=Motion Detect Camera

[Service]
ExecStart=/home/pi/python/detect.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

次に撮影した画像を確認できるプログラムを作成する。

全ての画像をDiscordに送信するのは時間と通信量が消費されすぎるので、一枚のコラージュ画像を作成して送信するようにする。

下記のプログラムは、すでに監視カメラ画像が50枚存在する事を前提にしているため、事前に監視カメラ画像を適当に増やしておく。

collage.py
import os
from PIL import Image, ImageDraw

directory = "/nas/motion_detection/" #監視カメラ画像の保存先

files = os.listdir(directory)
files.sort()
images = [i for i in files if i.endswith('.jpg') == True]

#画像の最大保存数50枚の場合
##最大横幅(3200)÷(320)=10
##最大縦幅(900)÷(180)=5
###コラージュ画像 => 監視カメラの画像(横10枚×縦5枚)
expected_size_collage = (3200, 900) #コラージュ画像のサイズ
expected_size_image = (320, 180) #監視カメラ画像のサイズ(リサイズする)

collage = Image.new("RGBA", expected_size_collage, color=(255,255,255,255))

file_count = 0
for h in range(0, expected_size_collage[1], expected_size_image[1]):
    for w in range(0, expected_size_collage[0], expected_size_image[0]):
        file_name = images[file_count]
        path = directory + file_name
        image = Image.open(path).convert("RGBA")

        image_width = image.size[0]
        image_height = image.size[1]

        width_factor = image_width / expected_size_image[0]
        height_factor = image_height / expected_size_image[1]

        if width_factor != height_factor:
            factor = min(width_factor, height_factor)

            expected_width = round(factor * expected_size_image[0])
            expected_height = round(factor * expected_size_image[1])

            start_width = round((image_width - expected_width) / 2)
            start_height = round((image_height - expected_height) / 2)
            end_width = expected_width + round((image_width - expected_width) / 2)
            end_height = expected_height + round((image_height - expected_height) / 2)

            image = image.crop((start_width, start_height, end_width, end_height))

        image = image.resize(expected_size_image)

        collage.paste(image, (w, h))
        file_count += 1

collage.save("コラージュ画像の保存先")

最後にDiscordBotのプログラムを作成する。

bot.py
#!/usr/bin/python3
import discord
from discord.ext import commands
import datetime
import requests
import datetime
import subprocess, shlex

webhook = "Discord Webhook URL"

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
    command_prefix=commands.when_mentioned_or("!"), debug_guilds=[サーバーID], intents=intents
)
client = discord.Client()
channel = チャンネルID


@bot.event
async def on_ready():
    print('[INFO] 	<' + str(datetime.datetime.now().replace(microsecond=0)) + '> Bot is active')

class detect_btn(discord.ui.View):
    def __init__(self, timeout=180):
        super().__init__(timeout=timeout)

    #監視カメラを起動する
    @discord.ui.button(label="Start", style=discord.ButtonStyle.green)
    async def start(self, button: discord.ui.Button, interaction: discord.Interaction):
        args = shlex.split("sudo systemctl start DETECT.service")
        ret = subprocess.call(args)
        await interaction.response.send_message("Start")

    #監視カメラを停止する
    @discord.ui.button(label="Stop", style=discord.ButtonStyle.red)
    async def stop(self, button: discord.ui.Button, interaction: discord.Interaction):
        args = shlex.split("sudo systemctl stop DETECT.service")
        ret = subprocess.call(args)
        await interaction.response.send_message("Stop")

    #保存された画像をコラージュして送信する
    @discord.ui.button(label="Callage", style=discord.ButtonStyle.blurple)
    async def collage(self, button: discord.ui.Button, interaction: discord.Interaction):
        await interaction.response.defer()
        args = shlex.split("sudo python3 python/collage.py")
        ret = subprocess.call(args)

        with open('コラージュ画像の保存先', 'rb') as f:
            file_bin = f.read()
        dht_img = {
            "favicon" : ("collage.jpg", file_bin),
        }
        res = requests.post(webhook, files=dht_img)

        await interaction.followup.send('Finish')


@bot.slash_command(name="detect")
async def help_slash(interaction: discord.Interaction):
    """motion detection camera"""
    await interaction.response.send_message(view=detect_btn())

bot.run("TOKEN")

実行結果

Writer

アイコン
Python×Raspi IoTシステム・Bot・ラズパイの記録
  • プログラミング
  • IoT
  • Python
\FOLLOW ME/ 𝕏

Ranking

Community

Search