Smart Home

ラズパイでカメラを使ってスマートホーム監視システムを構築しよう

2022年7月31日

ラズパイでカメラを使ってスマートホーム監視システムを構築しよう

こんにちは。ポン吉です。

自宅をスマートホーム化したいな~ と思っている方にオススメしたいのが「カメラの活用」です。ラズパイでは簡単にカメラを扱う事ができるので、安価で高機能な監視カメラが簡単に作れちゃいます。市販の監視カメラにはない「AI画像分析やAR拡張」といったアイデアを組み込むのも楽しいです。

ラズパイでカメラを扱う場合、カメラの種類からソフトウェアの種類まで多くのパターンがあります。初心者の方は何を使えばよいのか悩むところだと思います。

今回は監視システム5年の稼働実績から、カメラの選び方やソフトウェアのノウハウを共有したいと思います。

ラズパイ監視カメラ
MJPG-Streamer / Motionをつかった監視カメラの作り方
監視カメラmjpg-streamer motion
Pythonによる自作監視カメラアプリの作り方

 
ラズパイでカメラを使う場合のソフトウェアは「MJPG-Streamer」「Motion」の2つが主流です。MJPG-Streamerはネットの情報が古く、最新ラズパイOS(Bullseye)で動かせない人が多発している状況。Motionは設定項目が多く誤検知を防止するためにややコツがいります。

長期運用でタフで壊れないカメラを選びたい!
アプリは何使えばいいんだ??

読者
ポン吉

そんなカメラに関するいろいろなお悩みを解決します!

 

今回の記事を読めばラズパイの「カメラ選択」「ソフトウェア選択」ノウハウがすべて手に入ります!

今回のテーマはこちら!

  • ラズパイで使うカメラ機器の選び方のポイントを紹介
  • カメラアプリ「MJPG-Streamer」「Motion」「自作pythonアプリ」の使い方を紹介
  • スマートホームでの監視カメラトラブル事例についても紹介

 

下記記事で紹介したスマートホームのシステム構成図の監視カメラに関する内容になります。

 
今回の記事サマリは以下となります。

ラズパイでカメラを扱うときのポイント

それではやってみましょう~♪

ラズベリーパイで使うカメラやソフトウェアの選び方

ラズパイ監視カメラ

ラズパイで使うカメラの接続インタフェースは2種類あります。「ラズパイ用カメラモジュール」「USBカメラ」の2種類について、それぞれの特徴や使い分け方を紹介します。

うちのスマートホームでは駐車場の監視カメラ以外に様々な用途でカメラを使っています。ラズパイでカメラを扱う場合「これが一番!」というオールマイティなものはありません。用途に応じて必要なカメラを使い分けて頂くのが良いと思います。

ラズパイ用カメラモジュール

まずはラズパイ専用のカメラモジュールから紹介します。

スペック情報

  • 製品名: Raspberry Pi Camera V2 ラズベリーパイ用 カメラモジュール
  • 画像センサ: SONY製のIMX219PQ CMOS画像センサ 
  • 静止画撮影スペック: 3280 × 2464 8メガピクセル固定フォーカス
  • 動画撮影スペック: 1920×1080 (H.264 1080p@30fps、720p@60fps、640x480p@90fps)
  • 接続インタフェース: 15ピンリボンケーブル

ラズベーリパイ専用品のため相性問題もなく安心して利用できます
(Raspberry Pi 400はインタフェースがなく接続できません。 USBカメラを使いましょう)

接続時のリボンケーブルには向きがありますので注意してください。下記、公式サイトの説明がわかりやすかったので参考にしてみてください。

ラズパイカメラモジュールの接続方法
ラズベリーパイカメラモジュールのリボンケーブル接続方法
Raspberry Pi 公式サイトより引用

Good Point!

こんな用途がオススメ

  • Raspberry Pi専用のカメラなので相性問題もなく安心して使用可能♪ 
  • USBカメラとくらべて小型軽量! 電子工作として組み合わせて使うのがオススメ
  • 個人的にはRaspberry Pi Zeroと小型スパイカメラ の組み合わせが超小型でステキ♪ 

     ⇒「子守監視カメラ」「ペット監視カメラ」等の室内での用途がおすすめ

Bad Point!

ここがイケてない

  • ラズパイ本体から離れた場所に設置できず設置の自由度が低い
  • リボンケーブルの取り回しが面倒! スマートに隠蔽するのが難しいです…
  • USBカメラに比べて価格が高い!

  

ラズパイZero用に更に小さなカメラモジュールがあります。スペックは落ちますが電子工作への組み込みする場合は、こちらのカメラがオススメです。ただ、フラットケーブル部分がやや弱いのでケースに無理やり入れたりすると破損しやすいので注意。(1つ壊しました…

フリスクケース改にラズパイZero+カメラ+Lipoバッテリー搭載すれば、コードレスなハンディカメラも実現できます。

 

USBカメラ

続いてUSBカメラの紹介です。USBカメラの動作OSを確認し「Linuxで動作可能」となっているものを選びましょう。基本的にはUSBに接続するだけで使えるはずです。ただし、カメラによっては相性問題があるので人柱になるリスクを背負うことになります。

以下ご紹介するUSBカメラは、ラズパイで動作確認済みですので安心して使って頂けます。

Good Point!

特徴とおすすめの用途

  • USB接続のためラズパイ本体から離れた箇所に設置可能!自由度が高い! 
  • 小型から大型までカメラの種類が豊富!(高解像度、完全防水、暗視モード自動切換え等)
  • Raspberry Piカメラモジュールと比べて安価なカメラが多い! (価格はピンキリです)

    ⇒ 「駐車場監視カメラ」「玄関監視カメラ」など屋外での用途がおすすめ

Bad Point!

ここがイケてない

  • 種類は多いものの小型のカメラがあまりないため組み込み用にはやや使いづらい!
  • ラズパイで動くのかわからない。人柱リスクを背負う必要がある

 

うちの監視システムとして屋外に設置しているUSBカメラを紹介します。

スペック情報

  • 製品名: ELP 1080Pウェブカメラ 200万画素赤外線ナイトビジョン 防犯カメラ
  • 画像形式: YUY2 および MJPEG
  • 動画撮影スペック: 30fpsで1920x1080、60fpsで1280x720、120fpsで640x480
  • 接続インタフェース: USBにてラズパイに接続 USBケーブル5m

防水仕様なので屋外でも安全に使えます。夜間はオートで赤外線暗視モードに切り替わります。

スマートホームの屋外監視カメラとしての稼働実績は4年間フル稼働。故障やトラブルは発生していません。USBケーブルが5mと長いため、室内に設置したラズパイから屋外への設置も可能。

本格的に屋外設置の監視カメラを作りたいと検討されている方は、是非こちらのUSBカメラを購入することをお勧めします!これがH264のフォーマットに対応してくれていればパーフェクトなのですが未対応。惜しい!MJPEG形式で使っています。

ポン吉

同じELP社の製品で、解像度は100万画と落ちるものもあり
こちらはH264に対応しているんですけどね~

 

こちらは定番の安価なUSBカメラを紹介。安くてお手軽でオススメです。(室内用途では)

無謀にも屋外設置して耐久力を調べていた結果も共有します。カメラの隙間をコーキングした状態で屋外設置するも1年で故障。これで諦めず二代目ロジクール君にはこれでもかっ!ってくらいコーキングメガ盛りにして設置したのですが同じく1年ほどで故障

ポン吉

うーん 基本的に室内で使いましょう…

あたりまえだろ アホなの?

読者

ロジクールのカメラはUSBケーブルが短いんですよね。USB延長ケーブル接続部の防水が甘かったのが故障の原因かもしれません。

MJPG-Streamer による監視カメラの作り方

監視カメラmjpg-streamer motion

ラズパイで監視カメラを作る場合に使うソフトウェアを紹介します。

MJPG-Streamerとは?

MJPG-Streamerは、Linux互換カメラの映像をWeb配信するためのアプリケーションです。カメラから直接JPEGデータを生成し、リアルタイムに高速なMJPGストリームを出力します。

MJPG-Streamerにより出力される動画は、ネットワーク経由でChrome、Safari、FirefoxなどのブラウザーやVLC、mplayerのアプリで簡単に見ることができます。

もともと、CPUやメモリリソースの少ない組み込みデバイス向けに作成されたアプリです。ラズパイZeroのように小リソース機器で動画配信するにはこちらがオススメ。

mjpg-streamer-home

動作が軽量なぶん動体検知などの機能はありません。動体検知が不要な「ペット監視カメラ」「子守カメラ」などの用途で使いましょう。

MJPG-Streamerの詳細記事は下記にて記載しています。セットアップ・チューニング・トラブルシュート情報などをまとめていますので、是非参考にしてみてください。

 

Motion による監視カメラの作り方

監視カメラmjpg-streamer motion

ラズパイで監視カメラを作る場合に使うソフトウェアを紹介します。

Motionとは?

Motionはカメラ画像から動体検知をすることが出来るソフトウェアです。MJPG-Streamerのようにリアルタイムに動画配信をすることもできます。カメラで侵入者の検知などをしたい場合にMotionを使いましょう。 

MotionはMJPG-Streamerと異なり、小型組み込み機器を想定したアプリではありません。かなりCPUスペックを要求されるアプリなのでラズパイでの処理は重くキツイものになります。

FullHDのカメラ動画をデフォルト設定で使うとラズパイ4でも5fpsしか出ません。非常に遅い。また、設定オプションがわかりずらく初心者には取っつきにくいところが難点。

Motionの詳細記事は下記にて記載しています。セットアップ・チューニング・トラブルシュート情報などをまとめていますので、是非参考にしてみてください。

 

自作pythonアプリ(OpenCV) による監視カメラの作り方

監視カメラmjpg-streamer motion
ポン吉

難易度:★★★★

先ほど紹介したMotionは、Full HD(1920×1080)のカメラ動画で使おうとするとデフォルト設定では5fpsくらいしか出ません。設定チューニングしてもラズパイ4で15fpsあたりが限界かと思います。

防犯カメラ用途で使うのにフレームレート上げるために低解像度にするのではセキュリティ面でも不安ですよね。せっかくFull HDのカメラ買ったのに…

そんな人は、PythonプログラムでOpenCVを使って30fpsで動体検知できるよう自作しましょう。

Python(OpenCV)のセットアップ

セットアップ

ラズベリーパイにOpenCVをインストールする方法を紹介します。OpenCVをラズベリーパイ上でmakeすると3時間以上もかかってしまいます。そこで、インストール時間を最小限にしつつ、シンプルに新しめのパッケージを入れる方法を紹介します。

 

sudo apt update
sudo apt install -y python3-pip
sudo apt install -y libatlas-base-dev
sudo apt install -y libopencv-dev
pip install -U opencv-python
pip install -U numpy
pip install -U pillow

コピーボタンを押してコンソールに貼り付けてインストールしましょう。最後の行のpillowは画像処理をする際に必要になる場面が多いので、OpenCVと一緒に入れておきます。

所要インストール時間

初期セットアップ済みのRaspberry Pi 4 でデスクトップ(GUI版)で約6分デスクトップなし(CUI版)Liteで約10分ほどかかります。

インストール時に下記警告が出ますが無視してOK!「$HOME/.local/bin」にパスが通っていないが大丈夫か?」という警告。そもそもf2pyとか使いません。気になる方はパスを通してください。

WARNING: The scripts f2py, f2py3 and f2py3.9 are installed in '/home/ponkichi/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.

次にOpenCVが正しくインストールできているか動作確認します。下記のようにコンソールから「python」と入力してEnterキーを押しましょう。「>>>」と表示されPythonインタプリタモードの対話形式になります。

ponkichi@raspberrypi:~ $ python
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>>        ここに何も表示されなければ正常です
ponkichi@raspberrypi:~ $ 

ここで「import cv2」と入力してEnterキーを押します。ここでエラーが出なければインストール成功です。CTRL+Dを押して終了させます。

今後のパッケージ最新化に伴い、本手順通りに実施しても動かなくなる可能性もあると思います。エラー発生時の対処方法について下記にまとめておきますので参考にしてみてください。

import cv2 エラー発生時の対処方法

 

Python(OpenCV)による動体検知と動画ファイル保存

コーディング・チューニング

こちらはあくまでご参考までに。ややOpenCVの知識が必要になりますが、何も知らない人でもコピペで使えるよううちの監視システムで使っているソースを簡略化したものを公開します。(あまり綺麗に書いていません)

OpenCVの使い方は下記記事にて詳細な解説をしています。あわせて参考にしてみてください。

import cv2
import time
import datetime
import threading
import queue

# 画像が変化したと判定する動体検知の閾値
# 実際にコンソール出力結果を見ながら環境に合わせて調整してください
THRESHOLD = 2000

# 動画出力終了させるトリガ 動体検知されなくなった時間を秒で指定
EVENT_GAP_TIME = 3

# カメラデバイスを指定 (カメラデバイス接続状況により数字変更が必要)
# Mjpg-Streamerの動画ストリームURLを入力にすることも可能です
INPUT_DEVICE = 0
#INPUT_DEVICE = 1
#INPUT_DEVICE = "http://rasbperrypi.local:8080/?action=stream"

# カメラ動画入力側の定数
INPUT_W=1920
INPUT_H=1080
INPUT_FPS=30

# 動画ファイル出力側の定数
OUTPUT_W=1920
OUTPUT_H=1080
OUTPUT_FPS=30

# 動画エンコード種別
INPUT_FCC =  cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')
OUTPUT_FCC = cv2.VideoWriter_fourcc('h', '2', '6', '4')

# h.264で動画ファイルを書き込む場合[.mp4]拡張子はエラーになるので注意
OUTPUT_FILEPATH="/tmp/"
OUTPUT_FILE_EXTENSION=".avi"


# 出力エンコードをH.264以外にすると出力処理が間に合わず画像が滞留する
# 念のためキューの最大数を設定してOOM発生しないよう考慮しておく
q=queue.Queue(1000)

# カメラから取得した画像
image = None


# カメラ動画読み込み処理(スレッドで実行)
def video_read():
    global image

    # USB Camera / MJPG-Streamer Initialize
    cap = cv2.VideoCapture(INPUT_DEVICE)
    cap.set(cv2.CAP_PROP_FOURCC, INPUT_FCC)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, INPUT_W)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, INPUT_H)
    cap.set(cv2.CAP_PROP_FPS, INPUT_FPS)  # 入力のFPSはセットできないっぽい

    # カメラ画像をキューに入れまくる
    while True:
        ret, image=cap.read()
        q.put(image)

# カメラ画像から動体検知処理(スレッドで実行)
def video_check():

    # 最後に動体検知した時間を格納する変数
    last_detected_time = 0

    while image is  None:
        # カメラから読み込みが始まるまでここで待つ
        pass

    # 起動直後のカメラ画像の乱れを考慮して1秒待つ
    time.sleep(1)
    img1 = img2 = img3 = image

    while True:
        # 動体検知チェックする間隔。出力動画のFPSには影響しない
        # CPU無駄に消費しないよう適度にSleep入れてまわす
        time.sleep(0.2)

        # 3つの画像の差分値を取得
        diff = check_image(img1, img2, img3)
        print("THRESHOLD=" + str(diff))
        #print("THRESHOLD=" + str(diff) + "  QueueSize:" + str(q.qsize()))

        if diff > THRESHOLD:
            # 画像変化が閾値を超えている場合
            if last_detected_time == 0:
                q.put("start")
            last_detected_time = int(time.time())
        else:
            # 画像変化が閾値を超えていない場合
            if last_detected_time > 0 and last_detected_time < int(time.time() - EVENT_GAP_TIME):
                q.put("end")
                last_detected_time = 0

        img1, img2, img3 = (img2, img3, image)


# 動体検知部分の動画ファイルを作成する処理(スレッドで実行)
def video_write():

    # VideoWrite
    video = None

    while True:
        item = q.get()
        if str(item) == "start":
            print("== Start writing video file ==")
            filename = OUTPUT_FILEPATH + datetime.datetime.now().strftime("%Y_%m%d_%H%M%S" + OUTPUT_FILE_EXTENSION)
            video = cv2.VideoWriter(filename , OUTPUT_FCC, OUTPUT_FPS, (OUTPUT_W, OUTPUT_H))
        elif str(item) == "end":
            print("== End writing video file ==")
            video.release()
            video = None

            # ★動画ファイルを書き終わった箇所★
            # ★他処理につなぐ場合はここに処理を記載する★

        else:
            if video != None:
                video.write(item)


# 引数で指定した3つの画像の差分情報を数値で返す
def check_image(img1, img2, img3):
    gray1 = cv2.cvtColor(img1, cv2.COLOR_RGB2GRAY)
    gray2 = cv2.cvtColor(img2, cv2.COLOR_RGB2GRAY)
    gray3 = cv2.cvtColor(img3, cv2.COLOR_RGB2GRAY)
    diff1 = cv2.absdiff(gray1, gray2)
    diff2 = cv2.absdiff(gray2, gray3)
    diff_and = cv2.bitwise_and(diff1, diff2)
    _, diff_wb = cv2.threshold(diff_and, 30, 255, cv2.THRESH_BINARY)
    diff = cv2.medianBlur(diff_wb, 5)
    diff = cv2.countNonZero(diff)
    return diff


# メイン処理開始
if __name__ == '__main__':
    # 3つのスレッドを起動してマルチスレッドで並列処理させる
    t1 = threading.Thread(target=video_read)
    t1.start()
    t2 = threading.Thread(target=video_write)
    t2.start()
    t3 = threading.Thread(target=video_check)
    t3.start()

超雑なソース解説

マルチスレッド処理にて「動画読み込み処理」「動画書き込み処理」「動画分析処理」を並列処理させています。動体検知された部分を動画ファイルとして出力。★部分に「別プログラムを呼び出す処理」のトリガなど埋め込んで使ってみてください。

H.264 エンコーダについて

ラズパイにはH.264ハードウェアエンコーダが備わっています。これを使うことで、CPUパワーを使うことなく動画書き込み処理が可能。出力するフォーマットは「mp4」だとエラーになるので注意してください。

 

上記のソースをコピペボタンを押して「camera.py」というファイルで保存。以下のように実行してみてください。

python camera.py

以下のように画像変化を示すTHRESHOLD値が出力されます。カメラ画像に手などをかざせば、数値が上がって動体検知すると思います。その後動体検知が3秒無ければ動画ファイル出力を閉じる処理となります。

実際に監視カメラとして使う環境に合わせ、画面に出力されるTHRESHOLD値の設定は変更してください。

THRESHOLD=0
THRESHOLD=0
THRESHOLD=41860
== Start writing video file ==
THRESHOLD=146639
THRESHOLD=180250
THRESHOLD=170382
THRESHOLD=176917
THRESHOLD=216624
THRESHOLD=126701
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
THRESHOLD=0
== End writing video file ==
THRESHOLD=0

サンプルソースとして改変して使い倒してください。出力される動画が若干早めになっていると思うので、気になる人は出力FPSを20程度にするとちょうどよいかも。パラメタ調整してみてください。

ラズパイでフルHD(1920×1080)の動画を処理させるのは正直キツイです。HD(1280×720)くらいが妥当なラインだと感じます。防犯カメラなので不審者の顔がわかるくらい解像度欲しい人は、自作プログラムで頑張りましょう!

また、MJPG-Streamerで出力したストリームを別のラズパイからOpenCVで読み込んで動体検知することも可能。うちのスマートホームではラズパイZeroでMJPG-Streamerで動画配信させ、他のラズパイ4のOpenCVで読み込みディープラーニング処理を実施するような活用方法が多いです。

動体検知後の処理として「AI画像分析で人物検出処理」を加えると監視システムとして完全体になると思います。興味のある方は下記リンク先の記事も参考にしてみてください。

ポン吉

MJPG-Streamerで中継してOpenCV処理するとちょっとフレーム抜けが発生します
この解決策がないものか…

 

まとめ

ラズパイでカメラを扱うときのポイント

うちのスマートホームで使っているカメラ4種類。更に複数購入してあちこち監視させています。ラズパイでカメラを使う場合、下記4台のカメラを選べば後悔はしないと思います。どのカメラ買おうかお悩みの方がいましたら参考にしてみてください。

ラズパイ自体も品薄で購入が難しい状況が続いています。ラズパイの在庫と価格をリアルタイム確認できるページを作りました。ラズパイを購入しようとしている方は是非参考にしてみてください。

 

まとめ

  • 小型機器に組み込むなら「ラズパイ専用カメラモジュール」それ以外は「USBカメラ」がオススメ
  • お手軽に動画ストリーム配信可能したい場合は「MJPG-Streamer」を使おう
  • 監視カメラとして動体検知したい場合は「Motion」を使おう
  • Motionの遅さにゲンナリしている方は自作Pythonアプリにチャレンジしよう!
ポン吉

ラズパイでカメラを活用したアイデアは∞です!
MJPG-Streamer / Motion / OpenCV を使いこなしていこう

カメラとOpenCVとAI

カメラとAIの組み合わせにより、ラズパイならではの面白いアイデアの具現化が出来ます。そこで必要になる技術がOpenCV!これを避けては何もできません。ラズパイの場合PythonからOpenCVを使うのがお手軽でオススメです。OpenCVについての解説記事も今後増やしていきたいと思います。

-Smart Home