Raspberry Pi AI Cameraで自宅モニタリングアプリを作ってみた

<!-----------------
記事本文は下記レビュー観点を意識したうえで、自由に記載してください。 
 - [ ] 非公開情報はないか
 - [ ] 社内用語が使われていないか
 - [ ] 共通ワーディングを利用しているか
 - [ ] 技術的間違いがないか
 - [ ] リンク切れを起こしていないか
 - [ ] 政治、宗教等センシティブな領域に言及していないか (CATOPE対応)
 - [ ] 文体の統一/誤字チェック（JustRight対応）
 - [ ] その他不適切な表現・内容が含まれていないか
 - [ ]  問い合わせに関する文言を記載しているか  　
------------------->  

こんにちは！  
ソニーセミコンダクタソリューションズ の西村と申します。  
Raspberry Pi AI Cameraを使った自宅モニタリングアプリを作ったので、記事を投稿します。

# この記事の要約
- Raspberry Pi AI Cameraを使うと自宅モニタリングアプリが簡単に作れます
- Raspberry Pi AI Cameraを使ったアプリ開発の一連の流れを説明します
- Raspberry Pi AI Camera、AITRIOSに興味がある～これから触る予定がある方をメインにした記事です

# はじめに
Raspberry Pi AI Cameraが発売されたので、色々遊んでみようと思い、アプリを開発してみました！  
何を作るかというところで真っ先に思いついたのが、最近ニュースでも色々怖い事件を耳にするので、自宅のモニタリングをしたいというところでした。  
(実家の近所で夜中に強盗が入ったという話も聞いたりしたので…)

# 本文
## 0. 今回の作業の流れ
プログラミングや AI の高度な知識などは不要ですが  GitやPythonなどを一部使用する点について、あらかじめご了承ください。

	1. Raspberry Pi AI Cameraのセットアップ
    2. Raspberry Pi AI Cameraから人物検知データの取得
    3. アプリ作成

## 1. Raspberry Pi AI Cameraのセットアップ
まずはRaspberry Pi AI Cameraのセットアップになります。  

具体的な手順の説明については省略しますが、[こちらの記事](https://qiita.com/daiki0321/items/4223611adb453d6c7aa2)に詳細に記載されていますので、よろしければ参考にしてみてください。  
2章の最後まで終わっていれば、本記事用のセットアップとしては完了です。

## 2. Raspberry Pi AI Cameraから人物検知データの取得
つづいて、サンプルを動かしてRaspberry Pi AI Cameraで人物を検知できるか試してみましょう！  

0から人物検知&画面への表示を実装する場合、かなりの手間と時間が必要だと思います。  
ですが、つい最近、弊社から[Application Module Library](https://github.com/SonySemiconductorSolutions/aitrios-rpi-application-module-library/tree/6c775fb636d5934ed21c324b26e5947aeec25578)なるものがリリースされまして、それを使えば簡単にサンプルが作れちゃうそうです。  
折角ですので、使わせていただこうと思います！

まずはGitHubからリポジトリをクローンします。

    git clone https://github.com/SonySemiconductorSolutions/aitrios-rpi-application-module-library.git
    cd aitrios-rpi-application-module-library

&nbsp;  
つづいて、READMEの  
　1. Development environment setup.  
　2. Building the Python wheel.  
に記載されている下記のコマンドを実行してください。   

    # 1. Ensure that your Raspberry Pi runs the latest software:
    sudo apt update && sudo apt full-upgrade
    sudo apt install imx500-all
    sudo apt install python3-opencv python3-munkres python3-picamera2

    # 2. Create your project virtual environment and install modlib.
    python -m venv .venv --system-site-packages
    . .venv/bin/activate
    pip install modlib-<version>-py3-none-any.whl
 
&nbsp;  
つぎに、hello-world.pyファイルを新規作成して、READMEの  
　Basic example  
に記載されているpythonコードを丸ごとコピペします。  

```python:hello-world.py
    from modlib.apps import Annotator
    from modlib.devices import AiCamera
    from modlib.models.zoo import SSDMobileNetV2FPNLite320x320

    device = AiCamera()
    model = SSDMobileNetV2FPNLite320x320()
    device.deploy(model)

    annotator = Annotator(thickness=1, text_thickness=1, text_scale=0.4)

    with device as stream:
        for frame in stream:
            detections = frame.detections[frame.detections.confidence > 0.55]
            labels = [f"{model.labels[class_id]}: {score:0.2f}" for _, score, class_id, _ in detections]
            
            annotator.annotate_boxes(frame, detections, labels=labels)
            frame.display()
```
&nbsp; 
最後に、同じ章に載っている下記のコマンドを実行してみてください。

    . .venv/bin/activate && python hello_world.py

![PersonDetection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3993347/49f005b1-3b23-41b7-b132-0952f064e7c2.png)


いかがでしょう！  
シンプルなプログラムでしたが、しっかりと人物検知できている様子が分かると思います！  
マスクにネックウォーマー、フードを被っていても割と人間と認識できているので、防犯用途にもピッタリですね！  
(う～ん、我ながら見た目が怪しすぎる…)

## 3. アプリ作成
2章で触ったサンプルの一部を変更して、実際にモニタリングアプリとして使いやすいような形にしていきます！  
下記の流れで実装していきます。

    人物を検知  
    ↓  
    アラームを鳴らす  
    ↓  
    Raspberry Pi AI Cameraの画像を保存する  
    ↓  
    Slackに通知

実装途中でpygameやSlack等を使用していますが、他にも代替方法はございますのであくまで一例ということで、使用する際は使用者の責任で使用してください。

### 人物検知
こちらは2章のサンプルで既に実現できていますね。  
扱いやすいように、一部変更を加えてみました。  

```python:HomeCameraApp.py
    from modlib.apps import Annotator
    from modlib.devices import AiCamera
    from modlib.models.zoo import SSDMobileNetV2FPNLite320x320

    device = AiCamera()
    model = SSDMobileNetV2FPNLite320x320()
    device.deploy(model)

    annotator = Annotator(thickness=1, text_thickness=1, text_scale=0.4)

    with device as stream:
        for frame in stream:
            detections = frame.detections[frame.detections.confidence > 0.55]
            labels = [f"{model.labels[class_id]}: {score:0.2f}" for _, score, class_id, _ in detections]
+           names = [label.split(":")[0] for label in labels]

            annotator.annotate_boxes(frame, detections, labels=labels)
            frame.display()

+           if ("person" in names):
+                   print("Detection")        
```

### アラームを鳴らす
**※事前準備が必要なため、もしアラームを鳴らす必要がない場合は、こちらの章はSkipしてください。**  

#### 事前準備  
ラズパイから音声を鳴らすために、別途スピーカー・モニタなどを用意してください。  
また、アラームに使用する音声ファイルも用意してください。  

#### 実装
pythonから音を鳴らすことができれば何を使ってもよいのですが、今回は実装が簡単にできるpygameを使ってみます。  
もし同様にpygameを使用される場合は、下記コマンドで事前にモジュールをインポートしてください。  

    pip install pygame

soundについては、適当なアラーム音を準備いただき、そのパスを指定してください。  

    #アラーム音のパス
    ALERT_SOUND_PATH = "/your/file/path/hoge.wav"

最後に下記のように実装することで、ラズパイからアラームを鳴らすことができます！  

```python:HomeCameraApp.py    
    import pygame
    
    #アラーム音のパス
    ALERT_SOUND_PATH = "/your/file/path/hoge.wav"

    def initialize_pygame():
        pygame.init()
        pygame.mixer.init()
        print("[INFO] Pygame initialized.")

    def play_alert_sound():
        try:
            sound = pygame.mixer.Sound(ALERT_SOUND_PATH)
            sound.play()
            print("[INFO] Alert sound played successfully.")
        except pygame.error as e:
            print(f"[ERROR] Failed to play alert sound: {e}")
```

### Raspberry Pi AI Cameraの画像を保存する
つづいて、人物を検知した際のRaspberry Pi AI Cameraの画像を保存していきます。  
あとから証拠として必要になるかもしれないですからね…！  

実装方法は色々あると思いますが、今回はOpenCVを使って、Raspberry Pi AI Cameraから得られた画像を保存します。  
画像はカレントディレクトリに保存されると思いますが、その他の使い方を含めて、詳細についてはOpenCVの公式ドキュメントなどで確かめるようにしてください。  
もし同様にOpenCVを使用される場合は、下記コマンドで事前にモジュールをインポートしてください。  

    pip install opencv-python

引数としてimageに保存する画像は、人物検知のサンプルコード内に存在するframeから、frame.imageのような形でアクセスします。

```python:HomeCameraApp.py  
    import cv2
    
    def save_screenshot(filename, image):
        try:
            cv2.imwrite(filename, image)
            print(f"[INFO] Screenshot saved to {filename}.")
        except subprocess.CalledProcessError as e:
            print(f"[ERROR] Failed to save screenshot: {e}")
```

### Slackに通知
最後に、ユーザーへの通知を行うようにしていきたいと思います。  
今回は、手続きが楽&文献が多いという理由でSlackを使って通知を行います。  
もし同様にSlackを使用される場合は、下記コマンドで事前にモジュールをインポートしてください。  

    pip install slack-sdk requests lap aiohttp

SlackのOAuth Token発行やチャンネル設定などは色々と記事がありますので、今回は省略します。  
私は[こちらの記事](https://paiza.hatenablog.com/entry/2024/08/23/090000)を参考にさせていただきました…！
本記事では実際のSlack通知用のプログラムを載せておきます。

```python:HomeCameraApp.py 
    import slack

    OAUTH_TOKEN = 'SlackのOAuth Token'
    CHANNEL_ID = 'SlackのチャンネルID'

    def send_slack_message(filename):
        client = slack.WebClient(token=OAUTH_TOKEN)
        try:
            response = client.files_upload_v2(
                channel=CHANNEL_ID,
                file=filename,
                title="不審者を検出しました！"
            )
            print("[INFO] Slack message sent successfully.")
        except SlackApiError as e:
            print(f"[ERROR] Failed to send Slack message: {e.response['error']}")
```

send_slack_messageの引数に先程の画像を入れてあげれば、Slackの通知に画像が送られてくるようになります。

![SlackNotify.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3993347/709d8d99-827e-4e2c-84c4-654514338101.png)


### 全体のプログラム
最後に全体のプログラムを載せておきます。  
プログラムは[GitHub](https://github.com/SonySemiconductorSolutions/aitrios-rpi-sandbox)上にも公開されていますので、そちらも参考にしてください。

```python:HomeCameraApp.py 
    import time
    import pygame
    import cv2
    import slack
    from modlib.apps import Annotator
    from modlib.devices import AiCamera
    from modlib.models.zoo import SSDMobileNetV2FPNLite320x320

    # Definition of const
    ALERT_SOUND_PATH = "/your/file/path/hoge.wav"
    SCREENSHOT_FILENAME = "ScreenShot.png"
    OAUTH_TOKEN = 'Slack OAuth Token'
    CHANNEL_ID = 'Slack Channel ID'
    PAUSE_AFTER_DETECTION = 3  # (seconds)

    #Audio Output
    def initialize_pygame():
        pygame.init()
        pygame.mixer.init()
        print("[INFO] Pygame initialized.")

    def play_alert_sound():
        try:
            sound = pygame.mixer.Sound(ALERT_SOUND_PATH)
            sound.play()
            print("[INFO] Alert sound played successfully.")
        except pygame.error as e:
            print(f"[ERROR] Failed to play alert sound: {e}")

    # ScreenShot
    def save_screenshot(filename, image):
        try:
            cv2.imwrite(filename, image)
            print(f"[INFO] Screenshot saved to {filename}.")
        except subprocess.CalledProcessError as e:
            print(f"[ERROR] Failed to save screenshot: {e}")

    #Slack Message
    def send_slack_message(filename):
        client = slack.WebClient(token=OAUTH_TOKEN)
        try:
            response = client.files_upload_v2(
                channel=CHANNEL_ID,
                file=filename,
                title="Detect Person"
            )
            print("[INFO] Slack message sent successfully.")
        except SlackApiError as e:
            print(f"[ERROR] Failed to send Slack message: {e.response['error']}")


    #Detection by modlib
    def detect_person():
        print("[INFO] Starting person detection.")
        device = AiCamera()
        model = SSDMobileNetV2FPNLite320x320()
        device.deploy(model)

        annotator = Annotator(thickness=1, text_thickness=1, text_scale=0.4)

        last_detection_time = 0

        with device as stream:
            for frame in stream:
                detections = frame.detections[frame.detections.confidence > 0.55]
                labels = [f"{model.labels[class_id]}: {score:0.2f}" for _, score, class_id, _ in detections]
                names = [label.split(":")[0] for label in labels]

                annotator.annotate_boxes(frame, detections, labels=labels)
                frame.display()

                if "person" in names:
                    current_time = time.time()
                    if current_time - last_detection_time > PAUSE_AFTER_DETECTION:
                        print("[INFO] Detection action started.")
                        play_alert_sound()
                        save_screenshot(SCREENSHOT_FILENAME, frame.image)
                        send_slack_message(SCREENSHOT_FILENAME)
                        print("[INFO] Detection action completed.")
                        last_detection_time = time.time()


    #main処理
    if __name__ == "__main__":
        initialize_pygame()
        detect_person()
```

&nbsp; 
# 注意事項

「Slack」はSlack Technologies社が提供するサービスであり、これらの利用によって損害が発生した場合でも責任を負いかねます。  
お客様側でサービス内容を確認のうえで利用ください。


# 困った時は

もし、記事の途中でうまくいかなかった場合は、気軽にこの記事にコメントいただいたり、以下のサポートのページもご覧ください。  
コメントのお返事にはお時間を頂く可能性もありますがご了承ください。

<!-----------------RasPiに関する記事が含まれる場合は下記を追加してください。------------------->  
RaspberryPiに関連する疑問がある場合は、下記フォーラムをご確認、ご活用ください。
- [Raspberry Pi Forums](https://forums.raspberrypi.com/)

# 終わりに

今回の記事ではRaspberry Pi AI Cameraで自宅モニタリングアプリを作ってみました。  
実は私自身はRaspberry Pi AI Cameraを使うのは初めてでしたが、思ったよりも簡単に、しかも実用的なアプリが作れてしまい、かなり驚いています…！  
&nbsp;  
普通のモニタリング用カメラと違って、Raspberry Pi AI Cameraを使うと色々カスタマイズもできますし、自宅モニタリング以外の用途に活用できると思いますので、よろしければぜひ皆さんもお試しください！  
「こんなアプリもできるんじゃない？」などのアイデアもぜひぜひお待ちしています！