【ラズベリーパイ】監視カメラの作り方|PythonでカメラモジュールV2を自在に操作

【PR】この記事には広告が含まれています。

【ラズベリーパイ】監視カメラの作り方|PythonでカメラモジュールV2を自在に操作
  • ラズベリーパイでカメラを使ってみたい
  • カメラの選び方が知りたい
  • 外出先からもカメラの画像を確認したい

ラズベリーパイは一般的なパソコンとは違い、プログラミングでのカメラ操作が必要です。

この記事ではカメラの選び方から基本的な操作方法、応用編までをわかりやすく解説します。使用する機器はカメラモジュールV2Raspberry Pi 4、プログラミング言語はPythonを使います。

そぞら
そぞら

本記事のゴールはAIを活用した物体検出により、防犯カメラや監視システムとして活用できる仕組みを構築することです。

カメラモジュールV3の使用方法は、以下の記事で解説しています。
≫ Raspberry Pi カメラモジュール V3をOpenCVで操作する方法

ラズベリーパイで使えるカメラ

ラズベリーパイでは専用に開発されたカメラモジュールや、一般的なPC周辺機器として販売されているUSB接続のカメラが使用可能です。それぞれの特徴を解説します。

ラズベリーパイで使用できるカメラの種類

コンパクトなカメラモジュール

ラズベリーパイ専用のカメラはカメラモジュールと呼ばれています。カメラモジュールとラズベリーパイはリボンケーブルというテープのような形状のケーブルで接続します。

リボンケーブル外観

カメラモジュールのメリットはサイズが小さいことです。このため、ラズパイ本体と組み合わせたときに、システム全体がコンパクトにまとまります。デメリットはリボンケーブルが扱いにくいことです。リボンケーブルは形状を保とうとする力が強いため、曲げるとすぐに戻ってしまいます。このため、思い通りの場所に取り付けたり、角度を調整したりするのが難しいです。カメラの固定には工夫が必要となります。

そぞら
そぞら

僕はレゴブロックを使ってカメラスタンドを作りました。

自作のカメラスタンド
自作のカメラスタンド

簡易的な固定方法として、洗濯ばさみを利用する方法もあります。

出典:エンジニアの電気屋さん

3Dプリンターを使って、カメラスタンドを作るという方法もあります。以下のスタンドは、3Dモデル共有サイトThingiverseで公開されているデータ(Raspberry Pi Camera Holder)をプリントしたものです。

3Dプリンターで作成したスタンド

カメラモジュールの種類

ラズベリーパイで使えるカメラモジュールは、さまざまなメーカーから発売されています。僕は公式のRaspberry Pi Camera V2を使っています。800万画素のソニー製カメラユニットを採用しており、信頼性が高いです。ラズベリーパイと接続するためのリボンケーブルも付属しています。

Raspberry Pi Camera V2
Raspberry Pi Camera V2

2023年1月にカメラモジュール V3が発売されました。V2との違いは以下の通りです。


Camera Module 2

Camera Module 3
画素数800万画素
(3280 × 2464) 
1200万画素
(4608 x 2592)
センサーSony IMX219Sony IMX708
フォーカス手動調節式
(固定フォーカス)
モーター駆動
(オートフォーカス)
HDR対応なしあり
参考価格
KSY
4,070円4,620円
詳細詳細を見る詳細を見る

以下の安価なカメラでも、本記事のプログラムが正常に動作することを確認しています。

カメラモジュールV3は使用方法が異なります。V3の使用方法は以下の記事で解説しています。
≫ Raspberry Pi カメラモジュール V3をOpenCVで操作する方法

USB接続のカメラも使える

WEB会議などで使用するUSB接続のカメラは、ラズベリーパイ専用品ではないので使用できない可能性もあります。すでにUSBカメラを持っている方は使えるか試してみるとよいでしょう。USBカメラのメリットはカメラモジュールのリボンケーブルよりケーブルの取り回しがしやすいことです。また、固定しやすい形状のものが多く、角度の微調整も簡単に行えるものが多い印象です。

以下のUSBカメラは、本記事で紹介するプログラムが正常に動作することを確認済みのものです。

ラズベリーパイでカメラを使う準備をする

この記事で使用しているOSはRaspberry Pi OS(Legacy, 64-bit)です。OSの準備方法は以下の記事で解説しています。
≫【2025年最新版】OSインストールから初期設定まで|開始手順のすべて

まずはカメラの接続方法とカメラを有効にする手順を解説します。

カメラモジュールの接続方法

コネクターへの接続はリボンケーブルを使います。接続手順は下記の通りです。

  1. 黒いロックを持ち上げる。
  2. リボンケーブルを挿入する
  3. ロックを下に押し込んで固定する

まず、カメラポートの黒いロック部分を上に持ち上げます。

カメラポートのロック解除方法
カメラ接続

リボンケーブルを差し込みます。向きに注意してください。端子部分がHDMIポート側に来るような向きにします。

リボンケーブルの差し込み方
リボンケーブルの向き

黒いロック部分を押して固定します。

リボンケーブルのロック方法
ケーブルのロック

上に引っ張ってもケーブルが外れないことを確認します。

リボンケーブルのロック確認方法
ケーブルのロック確認

これでカメラの取り付けは完了です。よく分からない方は下記の動画を参考にしてください。

カメラを有効にする(カメラモジュールの場合)

カメラを接続しただけでは使えません。カメラの設定を有効にするため、まずはターミナルを開きます。

ターミナルの開き方

「sudo raspi-config」をターミナルに入力してenterキーを押します。

sudo raspi-config

「Interface Options」を選択してenterを押します。

「Legacy Camera Enable/disble legacy camera support」を選択。

「はい」を選択。

了解を押す

Finishを選択

「はい」を選択すると、再起動してカメラが有効化されます。

USBカメラの接続方法

USBカメラの場合はラズパイのUSBポートに差し込むだけです。どこのポートでも問題ありません。

USBカメラの接続方法
USBカメラの接続方法

USBカメラを使用する場合は、カメラを有効にする必要はありません。そのまま使用可能です。

ライブラリをインストールする

Pythonで画像を扱うためのライブラリであるOpenCVをラズパイにインストールします。ライブラリとはよく使う機能をまとめて保管している箱のようなものです。OpenCVをインストールすると、「カメラの画像を表示する」などの機能を「箱」から取り出して使えるようになります。

ターミナルを開いて、以下のコマンドを一行ずつ実行していきます。

ターミナルの開き方

まず、pipを最新のバージョンにします。下記のコマンドをターミナルに入力してEnterキーを押してください。

sudo python -m pip install --upgrade pip
そぞら
そぞら

sudo管理者権限で実行するときに使います。

OpenCVをバージョン指定でインストールします。

sudo pip3 install opencv-python==4.5.1.48

numpyというライブラリもバージョン指定でインストールします。

pip install numpy==1.23.1

パッケージリストを最新にします。

sudo apt update

「libatlas3-base」をインストールします。libatlas3-baseはOpenCVやNumPyで行列演算を高速化するために必要な数値計算用ライブラリ(ATLAS: Automatically Tuned Linear Algebra Software)の基本パッケージです。

sudo apt install libatlas3-base

以上でカメラを使用するための準備は完了です。

ラズベリーパイで監視カメラ【基礎編】

ラズベリーパイで監視カメラを作る方法を以下の順序で解説します。

  1. ラズベリーパイの画面にカメラの映像を表示する。
  2. カメラ映像が映ったラズパイの画面をスマホに表示する。

ラズパイとスマホはWi-Fiのネットワーク上に接続する必要があります。外出先からカメラの映像を確認することはできません。外出先へカメラの画像を送る方法は後ほど解説します。

Pythonプログラムでカメラ映像(動画)を表示する

カメラの映像を表示するためのPythonプログラムを作ります。プログラミングをするときはThonnyというソフトを使うのがおすすめです。

Thonnyの使い方は以下の記事で詳しく解説しています。
≫【ラズベリーパイ入門】Pythonプログラミングの始め方

まず、「Thonny」を開きます。

以下のプログラムをコピペして「camera_test.py」という名前で保存します。

import cv2

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))

while True:
  ret, frame = cap.read()
  if not ret:
    break

  cv2.imshow("Frame", frame)
  key = cv2.waitKey(1)
  
  # Escキーを入力されたら画面を閉じる
  if key == 27:
    break

cap.release()
cv2.destroyAllWindows()

プログラムの解説

プログラムの解説図解

まず1行目のimport cv2でOpenCVを使えるようにします。

cv2.VideoCapture でカメラチャンネル0を指定して、capという変数に代入します。

そぞら
そぞら

カメラモジュールとUSBカメラ、どちらもチャンネル0で大丈夫です。

チェックポイント

カメラが複数接続されている場合はカメラチャンネルを-1や1などに指定して、カメラを切り替えます。

while True繰り返し処理です。 while True 以下の文字が下がっている部分の処理を繰り返します。繰り返し処理の中身を解説します。

まず、camera.read1コマ分のカメラ画像を読み込みます。

if not ret: の部分は映像が正しく読み込めなかった場合にプログラムを終了させる処理です。

cv2.imshow でカメラ画像を画面に表示します。 “Frame” はフレーム画面上部に表示されるタイトルなので他の文字に変更しても構いません。文字列は必ず ” “(ダブルクォーテーション)で囲む必要があります。

そぞら
そぞら

1コマ分の画像表示を高速で繰り返すことで、リアルタイム映像(動画)になります。

OpenCVは waitKey 関数でキー操作を待つことができます。 Escキーが入力されたら繰り返し処理を抜けます。

camera.release() で画像データの取得を終了。 cv2.destroyAllWindows() でフレームの表示を終了させます。

プログラムの保存

プログラムは保存すると、実行できるようになります。

ファイル名は自由に付けて構いませんが、「opencv」などのライブラリ名は使わないでください。エラーの原因になります。Pythonスクリプトなので拡張子は.pyにします。

プログラムの実行

保存ができたら、プログラムを実行してみましょう。

カメラの映像が立ち上がれば成功です。

カメラ画像の確認

ピントの調整方法

ラズベリーパイのカメラモジュールはピント(焦点)を調整できます。文字がぼやけるなどピントが合っていないと感じたら調整してみましょう。調整方法はカメラのレンズの周りの黒い部分を左右に回します。近くのモノがはっきり見えないときは左方向に回します。逆に遠くのモノにフォーカスしたいときは右方向に回します。20~30°くらい回すだけでもピントは変わります。

そぞら
そぞら

映像を確認しながら少しずつ調整しましょう。

回しすぎるとレンズが外れるので要注意です。

ラズベリーパイカメラモジュールのピント調整方法
ラズベリーパイカメラのピント調整方法

ピント調整を手で行うのは厳しいです。ツマミが小さすぎてうまく回りません。下の動画のような専用の調整工具がカメラに同封されている場合があります。お手元にない方は工具のみ単品で購入することもできます。スイッチサイエンスで工具を見る

出典:adafruit.com

調整用工具が無い方は先の細いピンセットでも代用可能です。

ピンセットでカメラモジュールのピントを調整する方法
ピンセットでピント調整

スマホからカメラ映像を見る方法

VNC(Virtual Network Computing)を使えば他のパソコンやスマホからラズパイの画面が見れます。先ほど表示したカメラ映像をスマホに表示してみましょう。

スマートフォンにカメラ映像を出力したときの様子

ラズパイとスマホを接続するイメージは以下の通りです。

VNC接続の図解

ラズパイのWi-Fi機能を利用すると、無線でネットワークカメラが構築できるのでとても便利です。

設定手順は以下の通りです。

  1. ラズパイのVNCを有効にする
  2. スマホにVNCアプリをインストール
  3. ラズパイのIPアドレスとパスワードを入力して接続する
  4. ラズパイのデスクトップ画面がスマホに表示される

スマホにラズベリーパイの画面を表示する方法は以下の記事で解説しています。

ラズベリーパイで監視カメラ【応用編】

監視カメラといえば映像や静止画を記録する機能を有しているものもあります。今回はセンサーを利用して人が近付いた時に映像を保存するシステムを作ってみます。

とはいえ、いきなりセンサーを使ったプログラムを作るのはハードルが高いです。まずはセンサーを使わずに映像や静止画を保存する方法を解説します。

Pythonプログラムで静止画を保存する

カメラ映像を静止画として保存するプログラムは以下の通りです。

import cv2

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
ret, frame = cap.read()
cv2.imwrite('test.jpg', frame)
cap.release()

上記のプログラムを実行すると、同じフォルダにtest.jpgというファイルが保存されます。

しかし、このプログラムには少し問題があります。それはプログラムを実行するたびにファイルが上書きされることです。ファイルが上書きされると最新の画像だけが残り、過去の画像を見ることができません。

そぞら
そぞら

ファイルが上書きされないようにするには、ファイル名を都度変える必要があります。

ファイル名を時刻にして保存すると過去の画像も残る

Pythonでは現在時刻を簡単に取得できます。秒単位の現在時刻をファイル名にして保存すれば、ファイル名が毎回変わるので上書きされることはありません。

ファイル名を時刻にして画像を保存するプログラムは以下の通りです。

import cv2
import datetime

dt_now = datetime.datetime.now()
file_name = dt_now.strftime('%Y年%m月%d日%H時%M分%S秒')

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
ret, frame = cap.read()
cv2.imwrite(file_name + '.jpg', frame)
cap.release()

上記のプログラムを実行すると「2022年02月14日06時32分24秒.jpg」という名前のファイルが保存されます。

Pythonプログラムで動画を保存する

カメラで撮影した映像を動画として保存するプログラムです。

import cv2
import numpy as np
 
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
 
fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
fps = 20.0
size = (640, 360)
writer = cv2.VideoWriter('test.m4v', fmt, fps, size)
 
while True:
    _, frame = cap.read()
    frame = cv2.resize(frame, size)
    writer.write(frame)
     
    cv2.imshow('frame', frame)
    #escキーで終了
    if cv2.waitKey(1) == 27:
        break
 
writer.release()
cap.release()
cv2.destroyAllWindows()

プログラムを実行してescキー(エスケープキー)を押すまでの間の動画が保存されます。

動画はプログラムと同じフォルダーに保存されます。

動画の保存場所
動画の保存先

ダブルクリックして動画を再生してみましょう。

 

人感センサーが反応したら撮影する

ラズベリーパイにはGPIOピンがあるため電子工作が可能です。電子工作とカメラを組み合わせて人が近づいた時に撮影するシステムを組んでみましょう。

焦電型赤外線センサーを人感センサーとして使用します。
秋月電子で焦電型赤外線センサーを見る

焦電型赤外線センサー
焦電型赤外線センサー

人感センサーの接続

ラズベリーパイと人感センサーを以下のように接続します。

人感センサーとラズベリーパイの接続方法(回路図)
線の色センサー側ラズベリーパイ側
5V
オレンジOUTGPIO 18
GND
人感センサーの接続
GPIOピン配置
人感センサーとラズベリーパイの接続方法(写真)
焦電型赤外線センサーの配線

基板側の3つのツマミで感度などの調整が可能です。

感度調整方法など、焦電型赤外線センサーの使い方は以下の記事で詳しく解説しています。
≫ 焦電型赤外線センサーの使い方

僕は3Dプリンターでカメラとセンサーのマウントを作成しました。

カメラモジュールと人感センサー用マウント作成例

上記の写真ではセンサーの動作を可視化するため、LEDを使っています。本記事のプログラムではLED点灯の部分は省略しています。

人感センサーが反応したら静止画を保存するプログラム

import RPi.GPIO as GPIO
import cv2
import time
import datetime

GPIO_PIN = 18

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN,GPIO.IN)

while True:
    if(GPIO.input(GPIO_PIN) == GPIO.HIGH):
       
        print("1")
       
        dt_now = datetime.datetime.now()
        file_name = dt_now.strftime('%Y年%m月%d日%H時%M分%S秒')
        print(file_name)
       
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
        ret, frame = cap.read()
        cv2.imwrite(file_name + '.jpg', frame)
        cap.release()
       
        time.sleep( 10 )
       
    else:
        print("0")
        time.sleep( 1 )
        

上記のプログラムを実行すると、人が近づいた時にカメラモジュールで静止画を撮影して保存されます。

人感センサーが反応したら動画を保存するプログラム

import RPi.GPIO as GPIO
import cv2
import time
import datetime
import numpy as np

GPIO_PIN = 18

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN,GPIO.IN)

while True:
    if(GPIO.input(GPIO_PIN) == GPIO.HIGH):
        
        print("1")
        
        dt_now = datetime.datetime.now()
        file_name = dt_now.strftime('%Y年%m月%d日%H時%M分%S秒')
        print(file_name)
        
        #カメラ映像を保存する
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
        fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
        fps = 20.0
        size = (640, 360)
        writer = cv2.VideoWriter((file_name + '.m4v'), fmt, fps, size)
        
        i = 0
        #60フレーム撮影する
        while i < 60:
            _, frame = cap.read()
            frame = cv2.resize(frame, size)
            writer.write(frame)
     
            i = i + 1
             
        writer.release()
        cap.release()
        
        time.sleep( 10 )
        
    else:
        print("0")
        time.sleep( 1 )
GPIO.cleanup()

上記のプログラムを実行すると、3秒程度の動画が保存されます。録画時間を変更したいときは30行目の「60」の数値を変更してください。

外出先からカメラの映像を確認する方法

外出先からカメラの映像を確認する方法はいくつかあります。

Wi-Fiルーターの設定を変更する方法
ルーターの設定を変えれば、ネットワーク外からカメラの映像を見ることも可能ですが、セキュリティ上のリスクが高いためおすすめできません。
LINEやメールなどの既存サービスを利用する方法
安全に映像を確認する手段として、LINEやメールなどのサービスを活用する方法があります。たとえば、センサーが反応したときにカメラで撮影し、その画像を自動で送信する仕組みを作ることができます。ただし、この方法ではリアルタイムで映像を見るのには向きません。
Raspberry Pi Connect を使う方法
Raspberry Pi公式のリモートアクセスツール Raspberry Pi Connect を使えば、特別な設定なしで外出先からカメラの映像を確認できます。これにより、安全かつ手軽にリモート監視が可能になります。Raspberry Pi Connectの詳細は「外からラズパイを操作!Raspberry Pi Connectの設定方法」で紹介しています。
そぞら
そぞら

ここではセンサーが反応したときに画像をLINEで送るプログラムを組んでみましょう。

システムの概要は以下の画像のようなイメージとなります。

ラズベリーパイ監視カメラシステム図解

メッセージの送信にはLINE Notifyという、LINEアプリに通知を行うサービスを利用します。LINE Notifyを使うには自身のアカウントでトークンを取得する必要があります。

LINE Notifyのトークン取得方法やPyhtonプログラムでLINEにメッセージを送る方法について以下の記事で詳しく解説しています。
≫【Python入門】LINEに自動メッセージを送る

Pythonプログラムで人感センサーが反応したとき写真をLINE通知する

import RPi.GPIO as GPIO
import cv2
import time
import datetime
import requests

#LINEメッセージ送信の関数
def send_message(Discovery_time):
    url = "https://notify-api.line.me/api/notify" 
    token = "トークンをここに入力"
    headers = {"Authorization" : "Bearer "+ token}
    files = {'imageFile': open("image.jpg", "rb")}
    message =  (Discovery_time,"侵入者あり")
    payload = {"message" :  message} 
    r = requests.post(url, headers = headers, params=payload, files=files)

#センサーを使う準備
GPIO_PIN = 18

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN,GPIO.IN)

while True:
    if(GPIO.input(GPIO_PIN) == GPIO.HIGH):
        #センサー検出時の処理
        print("1")
        
        #検出時間の取得
        dt_now = datetime.datetime.now()
        Discovery_time = dt_now.strftime('%Y年%m月%d日%H時%M分%S秒')
        print(Discovery_time)
        
        #カメラ画像を保存する
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
        ret, frame = cap.read()
        cv2.imwrite("image.jpg", frame)
        cap.release()
        
        #LINEメッセージ送信
        send_message(Discovery_time)
        
        #10秒待機
        time.sleep( 10 )
        
    else:
        #センサー未検出時の処理
        print("0")
        time.sleep( 1 )
        
GPIO.cleanup()


10行目のtoken =の部分には、ご自身のトークンをコピペしてください。

うまくいけば以下のようなメッセージが通知されるはずです。

チェックポイント

LINE Notifyでは動画の送信ができません。送信できるファイル形式はpngjpegです。

LINEが提供する「LINE Notify」は、2025年3月31日をもって、このサービスが終了することが発表されています。https://developers.line.biz/ja/news/2024/10/07/line-notify-will-be-discontinued/

顔認識の活用

OpenCVを活用すれば、顔認識などの本格的なAIカメラを作ることも可能です。

リアルタイムで顔を検出する

カメラ映像からリアルタイムで人の顔を探し出して、四角で囲むプログラムは以下の通りです。

import cv2 as cv

HAAR_FILE = \
"/usr/local/lib/python3.9/dist-packages/cv2/data/"\
"haarcascade_frontalface_default.xml"
cascade = cv.CascadeClassifier(HAAR_FILE)

cap = cv.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))

while(True):
    ret, frame = cap.read()
    
    face = cascade.detectMultiScale(frame)

    for x, y, w, h in face:
        cv.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),1)

    cv.imshow('Capture',frame)
    
    if cv.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv.destroyAllWindows()

プログラムを実行すると以下のように表示されます。※顔部分は後から加工により消しています。

顔認識と電子工作を組み合わせる

認識するだけでは面白くないので、電子工作と顔認識を組み合わせてみましょう。顔を認識したときにLEDを光らせてみます。

LEDとラズベリーパイは以下のように接続します。LEDに電流を流しすぎると壊れてしまうので100Ωの抵抗を使用しています。

ラズベリーパイでLEDを点滅させる方法について以下の記事で詳しく解説しています。
≫【ラズベリーパイ電子工作の始め方】失敗したくない初心者のための完全ガイド

プログラムは以下の通りです。

import cv2 as cv
from gpiozero import LED
import time

led = LED(18)

HAAR_FILE = \
"/usr/local/lib/python3.9/dist-packages/cv2/data/"\
"haarcascade_frontalface_default.xml"
cascade = cv.CascadeClassifier(HAAR_FILE)

cap = cv.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))

while(True):
    ret, frame = cap.read()
    
    face = cascade.detectMultiScale(frame)
    
    if len(face) > 0:
        led.on()

        for x, y, w, h in face:
            cv.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),1)
            
    else:
        led.off()
        
    cv.imshow('Capture',frame)
    
    if cv.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv.destroyAllWindows()

プログラムを実行すると、顔を認識したときにLEDが点灯します。

20行目のLED点灯の部分のプログラムを変更すれば、応用の幅が広がります。

顔認識によりLINE通知する

以下は顔を検出したときにLINE通知するスクリプトです。

import cv2 as cv
import requests
import datetime
import time

# LINEメッセージ送信の関数
def send_message(discovery_time):
    url = "https://notify-api.line.me/api/notify"
    token = "トークンをここに入力"
    headers = {"Authorization": "Bearer " + token}
    files = {'imageFile': open("image.jpg", "rb")}
    message = f"{discovery_time} 顔を検出しました。"
    payload = {"message": message}
    r = requests.post(url, headers=headers, params=payload, files=files)
    print("送信完了")

# 顔認識用のカスケード分類器のパス
HAAR_FILE = "/usr/local/lib/python3.9/dist-packages/cv2/data/haarcascade_frontalface_default.xml"
cascade = cv.CascadeClassifier(HAAR_FILE)

# メッセージ送信の間隔時間(秒)
wait_time = 30  # 30秒間隔

#連続して顔を検出する必要があるフレーム数
consecutive_frames_required = 2  # 2フレーム連続

cap = cv.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))

# 最後にメッセージを送信した時間を記録
last_notification_time = time.time() - wait_time  # 初回実行時にすぐ送信されるように調整

# 連続して顔が検出されたフレーム数を記録
consecutive_frames = 0

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

    # 顔を検出
    face = cascade.detectMultiScale(frame)

    # 顔を検出した場合
    if len(face) > 0:
        consecutive_frames += 1
        for x, y, w, h in face:
            cv.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 1)

        # 連続して顔が検出されたフレーム数が設定値を超えた場合
        if consecutive_frames >= consecutive_frames_required:
            current_time = time.time()
            # 30秒間隔でメッセージを送信する
            if current_time - last_notification_time > wait_time:
                cv.imwrite("image.jpg", frame)

                # 検出時間の取得
                dt_now = datetime.datetime.now()
                discovery_time = dt_now.strftime('%Y年%m月%d日%H時%M分%S秒')
                print(discovery_time)

                # LINEメッセージ送信
                send_message(discovery_time)

                last_notification_time = current_time
                consecutive_frames = 0  # カウントリセット

    else:
        consecutive_frames = 0  # 顔が検出されなかったらカウントリセット

    cv.imshow('Capture', frame)

    # 'q'キーで終了
    if cv.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv.destroyAllWindows()

9行目で入力するLINE Notifyのトークン取得方法は、以下の記事を参考にしてください。
≫【Python入門】LINEに自動メッセージを送る

21行目のwait_time変数は連続してLINE通知するのを防ぐために設定しました。メッセージ送信後、指定した時間が経過するまで次の送信を行いません。設定値は使用状況により適宜変更してください。

24行目のconsecutive_frames_requiredは誤検知での送信を減らすために設定した変数です。2の場合、2フレーム連続で顔を検知した場合にLINE通知します。この数値を増やすと誤検知でのLINE通知は減少しますが、即時性が悪くなります。こちらの数値も適宜調整してください。

カメラ映像からリアルタイムで物体を検出

ここから先のコードを実行するには、64ビット版のRaspberry Pi OSが必要です。

顔だけでなくさまざまな物体をリアルタイムで検出するプログラムをRaspberry Pi上で実行する方法を紹介します。MediaPipeのオブジェクト検出モデルを使用し、カメラ映像をもとに効率的な推論を行うものです。

そぞら
そぞら

MediaPipeはGoogleが提供するライブラリで、物体検出やジェスチャー認識などのビジョンタスクを簡単に実装できるツールです。

まずは、ターミナルを開き、以下のコマンドを実行していきます。

カメラ制御に必要な libcap ライブラリの開発用ヘッダーをインストールします。

sudo apt install libcap-dev

MediaPipeのサンプルコードを含むリポジトリをRaspberry Piにコピーします。

git clone https://github.com/googlesamples/mediapipe.git

Raspberry Pi用の物体検出のサンプルコードがあるディレクトリに移動します。

cd mediapipe/examples/object_detection/raspberry_pi

物体検出プログラムを実行するために必要な環境をセットアップします。32ビット版のRaspberry Pi OSでは、この段階でエラーが発生する場合があります。この場合は64ビット版の使用を推奨します

sh setup.sh

検出した物体を表示する

以下のコードを、/home/pi/mediapipe/examples/object_detection/raspberry_piに保存します。

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import sys
import time
import cv2
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
from utils import visualize

# FPSを計算し推論を制御するためのグローバル変数
COUNTER, FPS = 0, 0
START_TIME = time.time()
is_inference_in_flight = False  # 推論中かどうかを制御するフラグ
latest_detection_result = None  # 最新の検出結果を格納する変数


def run(model: str, max_results: int, score_threshold: float, 
        camera_id: int, width: int, height: int) -> None:
    """カメラから取得した映像に対して継続的に推論を実行します。

    引数:
        model: TFLite物体検出モデルの名前。
        max_results: 検出結果の最大数。
        score_threshold: 検出結果のスコア閾値。
        camera_id: OpenCVに渡すカメラID。
        width: カメラから取得するフレームの幅。
        height: カメラから取得するフレームの高さ。
    """
    global is_inference_in_flight, latest_detection_result

    # カメラからの映像入力を開始
    cap = cv2.VideoCapture(camera_id)
    cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    # 表示用パラメータ
    row_size = 50  # ピクセル
    left_margin = 24  # ピクセル
    text_color = (0, 0, 0)  # 黒
    font_size = 1
    font_thickness = 1
    fps_avg_frame_count = 10

    def save_result(result: vision.ObjectDetectorResult, unused_output_image: mp.Image, timestamp_ms: int):
        """推論が完了した際に呼び出されるコールバック関数。"""
        global is_inference_in_flight, latest_detection_result, COUNTER, START_TIME, FPS

        # 推論が完了したことをマーク
        is_inference_in_flight = False

        # FPSを更新
        if COUNTER % fps_avg_frame_count == 0:
            current_time = time.time()
            FPS = fps_avg_frame_count / (current_time - START_TIME)
            START_TIME = current_time

        latest_detection_result = result
        COUNTER += 1

    # 物体検出モデルを初期化
    base_options = python.BaseOptions(model_asset_path=model)
    options = vision.ObjectDetectorOptions(
        base_options=base_options,
        running_mode=vision.RunningMode.LIVE_STREAM,
        max_results=max_results,
        score_threshold=score_threshold,
        result_callback=save_result
    )
    detector = vision.ObjectDetector.create_from_options(options)

    # カメラから継続的に映像を取得し推論を実行
    while cap.isOpened():
        success, image = cap.read()
        if not success:
            sys.exit(
                'エラー: ウェブカメラから映像を読み取れません。カメラ設定を確認してください。'
            )

        # TFLiteモデルが要求する形式に合わせて映像をBGRからRGBに変換
        rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_image)

        # 前回の推論が完了している場合のみ新しい推論を開始
        if not is_inference_in_flight:
            detector.detect_async(mp_image, time.time_ns() // 1_000_000)
            is_inference_in_flight = True

        # FPSを表示
        fps_text = 'FPS = {:.1f}'.format(FPS)
        text_location = (left_margin, row_size)
        current_frame = image
        cv2.putText(current_frame, fps_text, text_location, cv2.FONT_HERSHEY_DUPLEX,
                    font_size, text_color, font_thickness, cv2.LINE_AA)

        # 最新の検出結果がある場合は可視化
        if latest_detection_result is not None:
            current_frame = visualize(current_frame, latest_detection_result)

        cv2.imshow('object_detection', current_frame)

        # ESCキーが押されたらプログラムを終了
        if cv2.waitKey(1) == 27:
            break

    detector.close()
    cap.release()
    cv2.destroyAllWindows()


def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '--model',
        help='物体検出モデルのパス。',
        required=False,
        default='efficientdet.tflite')
    parser.add_argument(
        '--maxResults',
        help='検出結果の最大数。',
        required=False,
        default=5)
    parser.add_argument(
        '--scoreThreshold',
        help='検出結果のスコア閾値。',
        required=False,
        type=float,
        default=0.25)
    parser.add_argument(
        '--cameraId', help='カメラのID。', required=False, type=int, default=0)
    parser.add_argument(
        '--frameWidth',
        help='カメラから取得するフレームの幅。',
        required=False,
        type=int,
        default=800)
    parser.add_argument(
        '--frameHeight',
        help='カメラから取得するフレームの高さ。',
        required=False,
        type=int,
        default=600)
    args = parser.parse_args()

    run(args.model, int(args.maxResults),
        args.scoreThreshold, int(args.cameraId), args.frameWidth, args.frameHeight)


if __name__ == '__main__':
    main()

プログラムの保存場所が異なると、モジュールの読み込み時にエラーが発生するので注意が必要です。

プログラムの保存場所

上記のプログラムはRaspberry Piに接続されたカメラを使い、MediaPipeを利用してリアルタイムで物体を検出し、その結果を画面に表示するものです。具体的には、カメラから取得した映像をTFLite(TensorFlow Lite)モデルに入力し、物体の検出を行っています。TFLiteは軽量かつ効率的な機械学習モデルを動作させるためのプラットフォームで、Raspberry Piのようなリソースの限られた環境での使用に最適化されています。

まず、カメラから映像を取得し、それをBGR形式からRGB形式に変換します。これはTFLiteモデルがRGB形式を要求するために必要です。その後、映像が物体検出モデルに送られ、推論によって検出された物体の情報が返されます。この情報をもとに、画面上に物体を囲む枠や名前を描画し、何が検出されたのかを表示します。

dining table、bottle、cupを検出

このプログラムは、サンプルコードとして提供されているdetect.pyを改良したものです。元のdetect.pyは処理が重く、フリーズしてしまう問題がありました。そこで、「推論中に新しい推論を開始しない」という制御を追加することで、安定した動作を実現しています。

このプログラムで検出できる物体の一例を次に示します。

  • 人(person)
  • 自動車(car)
  • バイク(motorcycle)
  • 自転車(bicycle)
  • バス(bus)
  • トラック(truck)
  • ボトル(bottle)
  • テレビ(tv)
  • ノートパソコン(laptop)
  • 椅子(chair)
  • ベッド(bed)
  • マウス(mouse)
  • 携帯電話(cellphone)

検出した物体をカウントする

特定の物体を検出した場合にその数をカウントすることもできます。以下のコードを、/home/pi/mediapipe/examples/object_detection/raspberry_piに保存します。

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import sys
import time
import cv2
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
from utils import visualize

# 任意の物体名を指定する変数(ここで変更可能)
target_object = "car"  # ここを好きな物体名に変更できる

# FPSを計算し推論を制御するためのグローバル変数
COUNTER, FPS = 0, 0
START_TIME = time.time()
is_inference_in_flight = False  # 推論中かどうかを制御するフラグ
latest_detection_result = None  # 最新の検出結果を格納する変数


def run(model: str, max_results: int, score_threshold: float, 
        camera_id: int, width: int, height: int) -> None:
    """カメラから取得した映像に対して継続的に推論を実行します。

    引数:
        model: TFLite物体検出モデルの名前。
        max_results: 検出結果の最大数。
        score_threshold: 検出結果のスコア閾値。
        camera_id: OpenCVに渡すカメラID。
        width: カメラから取得するフレームの幅。
        height: カメラから取得するフレームの高さ。
    """
    global is_inference_in_flight, latest_detection_result

    # カメラからの映像入力を開始
    cap = cv2.VideoCapture(camera_id)
    cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    # 表示用パラメータ
    row_size = 50  # ピクセル
    left_margin = 24  # ピクセル
    text_color = (0, 0, 0)  # 黒
    font_size = 1
    font_thickness = 1
    fps_avg_frame_count = 10

    def save_result(result: vision.ObjectDetectorResult, unused_output_image: mp.Image, timestamp_ms: int):
        """推論が完了した際に呼び出されるコールバック関数。"""
        global is_inference_in_flight, latest_detection_result, COUNTER, START_TIME, FPS

        # 推論が完了したことをマーク
        is_inference_in_flight = False

        # FPSを更新
        if COUNTER % fps_avg_frame_count == 0:
            current_time = time.time()
            FPS = fps_avg_frame_count / (current_time - START_TIME)
            START_TIME = current_time
            
        # target_objectの数をカウント
        target_count = sum(1 for detection in result.detections
                           if detection.categories[0].category_name == target_object)
        
        print(f"検出された{target_object}の数: {target_count}")           

        latest_detection_result = result
        COUNTER += 1

    # 物体検出モデルを初期化
    base_options = python.BaseOptions(model_asset_path=model)
    options = vision.ObjectDetectorOptions(
        base_options=base_options,
        running_mode=vision.RunningMode.LIVE_STREAM,
        max_results=max_results,
        score_threshold=score_threshold,
        result_callback=save_result
    )
    detector = vision.ObjectDetector.create_from_options(options)

    # カメラから継続的に映像を取得し推論を実行
    while cap.isOpened():
        success, image = cap.read()
        if not success:
            sys.exit(
                'エラー: ウェブカメラから映像を読み取れません。カメラ設定を確認してください。'
            )

        # TFLiteモデルが要求する形式に合わせて映像をBGRからRGBに変換
        rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_image)

        # 前回の推論が完了している場合のみ新しい推論を開始
        if not is_inference_in_flight:
            detector.detect_async(mp_image, time.time_ns() // 1_000_000)
            is_inference_in_flight = True

        # FPSを表示
        fps_text = 'FPS = {:.1f}'.format(FPS)
        text_location = (left_margin, row_size)
        current_frame = image
        cv2.putText(current_frame, fps_text, text_location, cv2.FONT_HERSHEY_DUPLEX,
                    font_size, text_color, font_thickness, cv2.LINE_AA)

        # 最新の検出結果がある場合は可視化
        if latest_detection_result is not None:
            # 可視化処理を実行
            current_frame = visualize(current_frame, latest_detection_result)

            # target_objectのカウントを計算
            target_count = sum(1 for detection in latest_detection_result.detections
                               if detection.categories[0].category_name == target_object)

            # target_objectのカウントを画面に表示
            count_text = f'{target_object} = {target_count}'
            count_location = (left_margin, row_size * 2)
            cv2.putText(current_frame, count_text, count_location, cv2.FONT_HERSHEY_DUPLEX,
                        font_size, text_color, font_thickness, cv2.LINE_AA)            

        cv2.imshow('object_detection', current_frame)

        # ESCキーが押されたらプログラムを終了
        if cv2.waitKey(1) == 27:
            break

    detector.close()
    cap.release()
    cv2.destroyAllWindows()


def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '--model',
        help='物体検出モデルのパス。',
        required=False,
        default='efficientdet.tflite')
    parser.add_argument(
        '--maxResults',
        help='検出結果の最大数。',
        required=False,
        default=5)
    parser.add_argument(
        '--scoreThreshold',
        help='検出結果のスコア閾値。',
        required=False,
        type=float,
        default=0.25)
    parser.add_argument(
        '--cameraId', help='カメラのID。', required=False, type=int, default=0)
    parser.add_argument(
        '--frameWidth',
        help='カメラから取得するフレームの幅。',
        required=False,
        type=int,
        default=800)
    parser.add_argument(
        '--frameHeight',
        help='カメラから取得するフレームの高さ。',
        required=False,
        type=int,
        default=600)
    args = parser.parse_args()

    run(args.model, int(args.maxResults),
        args.scoreThreshold, int(args.cameraId), args.frameWidth, args.frameHeight)


if __name__ == '__main__':
    main()

24行目の「target_object」という変数で、カウントする物体(例えば「car」など)を指定しています。この物体を検出したときに、その数をカウントして画面に表示します。この変数を変更することで、他の物体をターゲットにできます。

このコードの応用例を考えてみます。例えば、カメラに映る人の数が一定以上になった際に特定の処理を実行できます。店舗内の混雑状況を把握したり、その情報をデータ化するなど、さまざまな用途に活用できそうです。

刃物(ナイフ)を検出したらLINEに通知する

本項では応用として、カメラ映像内にナイフを検出した際にLINEに通知を送る方法を解説します。

LINEに通知を送るために、LINE Notifyを利用します。LINE Notifyトークンの発行方法については以下の記事を参考にしてください。

LINEに自動メッセージを送る

以下のコードを実装することで、カメラ映像内にナイフが映った際にLINEへ通知が送られるようになります。

そぞら
そぞら
35行目のLINE_TOKEN =の部分には、ご自身のLINEトークンを入力してください。
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import sys
import time
import cv2
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
from utils import visualize
import requests
import datetime

# FPSを計算し推論を制御するためのグローバル変数
COUNTER, FPS = 0, 0
START_TIME = time.time()
is_inference_in_flight = False  # 推論中かどうかを制御するフラグ
latest_detection_result = None  # 最新の検出結果を格納する変数
last_detection_time = 0  # 最後にナイフを検出した時間
DETECTION_COOLDOWN = 60  # 再検出を抑制する時間(秒)

def send_line_notification_with_image(image_path):
    """LINEに画像付きの通知を送信する"""
    LINE_TOKEN = "トークンをここに入力"
    url = "https://notify-api.line.me/api/notify"
    headers = {"Authorization": f"Bearer {LINE_TOKEN}"}
    message = f"{datetime.datetime.now().strftime('%Y年%m月%d日 %H時%M分%S秒')} - 警告: ナイフが検出されました!"
    payload = {"message": message}
    
    with open(image_path, "rb") as image_file:
        files = {"imageFile": image_file}
        requests.post(url, headers=headers, data=payload, files=files)
    
def run(model: str, max_results: int, score_threshold: float, 
        camera_id: int, width: int, height: int) -> None:
    """カメラから取得した映像に対して継続的に推論を実行します。

    引数:
        model: TFLite物体検出モデルの名前。
        max_results: 検出結果の最大数。
        score_threshold: 検出結果のスコア閾値。
        camera_id: OpenCVに渡すカメラID。
        width: カメラから取得するフレームの幅。
        height: カメラから取得するフレームの高さ。
    """
    global is_inference_in_flight, latest_detection_result

    # カメラからの映像入力を開始
    cap = cv2.VideoCapture(camera_id)
    cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    # 表示用パラメータ
    row_size = 50  # ピクセル
    left_margin = 24  # ピクセル
    text_color = (0, 0, 0)  # 黒
    font_size = 1
    font_thickness = 1
    fps_avg_frame_count = 10

    def save_result(result: vision.ObjectDetectorResult, unused_output_image: mp.Image, timestamp_ms: int):
        """推論が完了した際に呼び出されるコールバック関数。"""
        global is_inference_in_flight, latest_detection_result, COUNTER, START_TIME, FPS, last_detection_time

        # 推論が完了したことをマーク
        is_inference_in_flight = False

        # FPSを更新
        if COUNTER % fps_avg_frame_count == 0:
            current_time = time.time()
            FPS = fps_avg_frame_count / (current_time - START_TIME)
            START_TIME = current_time

        latest_detection_result = result
        COUNTER += 1
        
        # ナイフの検出をチェック
        current_time = time.time()
        if any("knife" in detection.categories[0].category_name.lower() for detection in result.detections):
            if current_time - last_detection_time > DETECTION_COOLDOWN:
                print("発見")
                
                # カメラ画像を保存
                image_path = "detected_knife.jpg"
                cv2.imwrite(image_path, current_frame)
                
                # LINEに通知(画像付き)を送信
                send_line_notification_with_image(image_path)                
                
                last_detection_time = current_time        
            else:
                print("再検出(通知待機)")
    # 物体検出モデルを初期化
    base_options = python.BaseOptions(model_asset_path=model)
    options = vision.ObjectDetectorOptions(
        base_options=base_options,
        running_mode=vision.RunningMode.LIVE_STREAM,
        max_results=max_results,
        score_threshold=score_threshold,
        result_callback=save_result
    )
    detector = vision.ObjectDetector.create_from_options(options)

    # カメラから継続的に映像を取得し推論を実行
    while cap.isOpened():
        success, image = cap.read()
        if not success:
            sys.exit(
                'エラー: ウェブカメラから映像を読み取れません。カメラ設定を確認してください。'
            )

        # TFLiteモデルが要求する形式に合わせて映像をBGRからRGBに変換
        rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_image)

        # 前回の推論が完了している場合のみ新しい推論を開始
        if not is_inference_in_flight:
            detector.detect_async(mp_image, time.time_ns() // 1_000_000)
            is_inference_in_flight = True

        # FPSを表示
        fps_text = 'FPS = {:.1f}'.format(FPS)
        text_location = (left_margin, row_size)
        current_frame = image
        cv2.putText(current_frame, fps_text, text_location, cv2.FONT_HERSHEY_DUPLEX,
                    font_size, text_color, font_thickness, cv2.LINE_AA)

        # 最新の検出結果がある場合は可視化
        if latest_detection_result is not None:
            current_frame = visualize(current_frame, latest_detection_result)

        cv2.imshow('object_detection', current_frame)

        # ESCキーが押されたらプログラムを終了
        if cv2.waitKey(1) == 27:
            break

    detector.close()
    cap.release()
    cv2.destroyAllWindows()


def main():
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        '--model',
        help='物体検出モデルのパス。',
        required=False,
        default='efficientdet.tflite')
    parser.add_argument(
        '--maxResults',
        help='検出結果の最大数。',
        required=False,
        default=5)
    parser.add_argument(
        '--scoreThreshold',
        help='検出結果のスコア閾値。',
        required=False,
        type=float,
        default=0.25)
    parser.add_argument(
        '--cameraId', help='カメラのID。', required=False, type=int, default=0)
    parser.add_argument(
        '--frameWidth',
        help='カメラから取得するフレームの幅。',
        required=False,
        type=int,
        default=800)
    parser.add_argument(
        '--frameHeight',
        help='カメラから取得するフレームの高さ。',
        required=False,
        type=int,
        default=600)
    args = parser.parse_args()

    run(args.model, int(args.maxResults),
        args.scoreThreshold, int(args.cameraId), args.frameWidth, args.frameHeight)


if __name__ == '__main__':
    main()

このプログラムでは、ナイフを検出した際に、カメラの映像を保存し、それをLINEに送信する機能を実装しています。ナイフが検出されると、現在のフレームが cv2.imwrite() を用いて detected_knife.jpg というファイル名で保存されます。この画像を send_line_notification_with_image() 関数を使用して、LINEに通知を送る仕組みになっています。

DETECTION_COOLDOWN = 60 はナイフを検出した後、次の通知を送るまで最低60秒の間隔を空けるための設定です。これにより、同じナイフが短時間で繰り返し検出された場合に、不要な通知が連続して送信されるのを防ぎます。

この方法を使えば、リアルタイムで物体検出を行い、特定の物体(今回の場合はナイフ)を検出した際にLINEに画像付きで通知するシステムを構築できます。不審な人物の検出や、特定のエリアへの侵入監視などにも活用が期待できます。

コメント一覧

夏は暑い

こんにちは。
2つほど質問です。
1.「Raspberry Pi Camera V2」ですが、Aサイトだと評価がいまいちのようです。
(梱包もそうですが、カメラが認識しないという評価)
当たりはずれがあるのでしょうか?
(値段は高くなりますが高評価の「Raspberry Pi Raspberry Pi NoIR Camera V2」はどう判断されますか?)
2.「カメラを有効にする」の項目でデスクトップの説明が出てきますが、デスクトップ自体はどうやって準備(ソフト※出来ればハードの説明も)されたのでしょうか(OSはLinux系?)?
宜しくお願い致します。

返信する
そぞら

お問い合わせありがとうございます。
回答させていただきます。

1.「Raspberry Pi Camera V2」の評価の件、確認しました。
  カメラが認識しないというレビューが2件ありますので、不安になることと思います。
  恐縮ですが、以下のサイトを検討いただければと思います。同商品の取り扱いがあります。  
  https://akizukidenshi.com/catalog/g/gM-10518/
  Amazonではありませんが、電子工作愛好家に人気のパーツショップです。
  申し訳ございません。「NoIR Camera」につきましては、使用経験がないため
  紹介させていただくことができないです。

2. デスクトップにつきましては、Raspberry Pi OSを使用しています。
  Raspberry Pi OSはLinux系のOSです。
  準備方法は以下の記事で解説していますので、参考にしていただければと思います。
  https://sozorablog.com/raspberrypi_initial_setting/

返信する
夏は暑い

早々のお返事有難うございます。
ラズベリーパイと自作カメラは初めてなので、少しずつ試します。

返信する
なろす

こんにちは。
全くの初心者なので、頓珍漢な質問でしたら恐縮です。

このプログラムで動かしたところ、毎60秒ごとに静止画、もしくは動画が撮影されてしまいます。どうすればよいでしょうか。
ほかはちゃんと動いてます。
人感センサーが勝手に60秒ごとに反応しているかのとも思いましたが、そうでもないようです。

返信する
そぞら

お問い合わせありがとうございます。

60秒ごとというのが不思議なのですが、、、

人感センサー単体の動作は問題ないでしょうか。
以下のプログラムで正常に検知できることをご確認ください。

import RPi.GPIO as GPIO
import time

GPIO_PIN = 18

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN,GPIO.IN)

while True:
if(GPIO.input(GPIO_PIN) == GPIO.HIGH):
print(“反応あり!”)
time.sleep( 1 )

else:
print(“反応なし”)
time.sleep( 1 )

返信する
なろす

プログラムまでいただき、ありがとうございます。

このプログラムでは毎分ごとに検出するような挙動はしていません。
とりあえず、センサーの感度調整で望まない検出頻度は下がるようなので、原因はよくわかりませんが、それで対応いたします。

ありがとうございました。

返信する
夏は暑い

こんにちは。
改めて質問致します。
「Raspberry Pi 3 model B」のセットが手ごろな価格で売られているので、それで監視カメラに挑戦しようと検討中です。
理由は「Raspberry Pi 4」のセットがベストだと思いますが、高性能より何とか動く程度でいいことと、ずばり値段が高い&在庫も少ないためです。
別記事で「Raspberry Pi 3 model B」だと性能が劣ると拝見しましたが、映像がかくかくするのでしょうか?

お返事頂ければ幸いです。

返信する
そぞら

お問い合わせありがとうございます。

「Raspberry Pi 3 model B」でもカメラを扱ったことがありますが
問題なく動作します。
映像がカクカクすることはないです。
「Raspberry Pi 3 model B」は現在生産されていないという
話を聞いています。
手ごろな価格で販売されているのは貴重だと思います。

返信する
夏は暑い

お世話になります。
いつもお返事有難うございます。
部品が揃ってセットアップをしていますが、最初の設定で「?」な箇所があります。
出来るところから進めようと思いSDカードにOSはインストールしておきましたが、
詳細な設定(ユーザー名とかホスト名とか)は飛ばしました。
youtubeで設定動画を見たのですが、恐らく細かい設定は後回しでやる動画だと思っています(OSと他設定を同時に進めない)が、合っていますか?
というのは、今一度Imagerを起動しましたが設定する中のホスト名とユーザー名の違いが分からず(他設定が必要なとこと不要の個所が不明)、動画内の後から設定だとそういうのはないようなので、そっちが楽かなと思っています。
お返事頂ければ幸いです。

返信する
そぞら

お問い合わせありがとうございます。

Imagerを使ってSDカードに書き込むときに設定をしないことは問題ありません。
ホスト名はデフォルトのraspberrypiのままで大丈夫です。
同一ネットワーク上に複数のラズパイがある場合、識別するために
ホスト名を変更する場合がありますが、後からでも変更可能です。

ユーザー名とパスワードは起動時に設定する必要があります。
パソコンからリモート操作するときに使うのでメモしておいてください。

返信する
夏は暑い

どーもです。
応用編までは行けました。
有難うございます。

返信する
夏は暑い

お世話になります。
動画保存について質問です。
恐らく私のハードウェア(本体)の問題(メモリ1Gなので動画処理に耐えられない?)と思っていますが、「test.m4v」はvlcで起動しますが数秒だとただ暗く、10秒以上保存でようやくちらつきますが、画面上の方1/4に頑張ってるちらつきが出て終わりです。
別の方の動画で設定の「gl driver」を「full kms gl driver」に変えればとありましたが、本体をRaspberryPi4にしてメモリは最低でも4Gで試すのが手っ取り早いでしょうか?(上記の場合パフォーマンス設定からGPUメモリも上げる必要ありのようです)。

返信する
そぞら

お世話になっております。

当方でもRaspberry Pi 3で動画保存を実行したところ
同じ不具合が確認されました。
Raspberry Pi 3で動画保存する方法を模索してみます。
何かわかったら連絡させていただきます。

返信する
夏は暑い

こんばんは。
質問の件、半分解決しました(コードは未修正です)。
普段使っているPCで試しに再生したら普通でした。
動画処理となると4にしないと厳しいかもです。
お手間をおかけしました。

返信する
そぞら

おつかれさまです。
こちらこそ、お役に立てず申し訳ないです。

返信する
Mr.kouki

こんにちは、初心者ですが
Pythonのpip3のOpenCVとVideoCaptureでも監視カメラを作ることは可能ですか?
私は、CentOS7で作ろうと思ってますが、必ずラズベリーパイのCMDじゃないといけないんですか?
私が所持している素材:
microSDカード
Raspberry Pi3 ※Model B
Elecomのランケーブル
買おうと思ってる素材:
raspberryPi3 のケース
ヒートシンク
やはりケースは必要ですか?

返信する
そぞら

こんにちは。
ブログを読んでいただきありがとうございます。

申し訳ございません。
CentOS7は使ったことがないため、分からないです。
ケースは破損防止のため用意した方が良いと思います。

返信する
くろえっち

本ページの人感センサーが反応したら静止画を保存するプログラムをコピーし実行したところ
最初に撮影された画像が更新されずに表示されます。
例えば、1回目の反応、撮影後、カメラの角度を変えて2回目の反応が起きても1回目の画像が保存されます。この際、画像ファイルの名前はセンサー反応時刻でちゃんとついております。

私は、センサーが反応するごとに異なる画像を撮影したいです。どのように修正すれば良いでしょうか?

返信する
そぞら

申し訳ございません。プログラムに不備がありました。

1回目の撮影後はrelease()をして画像を初期状態にする必要があります。
また、release()をするとカメラから画像が取得できなくなるため
ループの中にcv2.VideoCapture(0)を移動しました。

記事内のプログラムを修正しております。
お手数ですが、詳細はそちらをご確認ください。

返信する
cheese

こんにちは。
質問宜しいでしょうか。
コマンドからカメラを有効にしたのですが、インターフェイスからカメラを有効にしようとしても、そもそも項目が出てきません。
なぜでしょうか?
教えてくださると幸いです。

返信する
そぞら

お問い合わせありがとうございます。

raspi-configでカメラを有効化していれば
すでにカメラが使える状態になっているはずです。

なお、有効化してもインターフェイスの部分には
カメラ項目は出ません。

よろしくお願いします。

返信する
宮崎ヒロキ

こんにちは。
初心者質問で恐縮なのですが、人感センサーで反応したときにLINEbotに写真を送るという流れをラズパイとスマホ1対1ではなく、ラズパイ1に対して複数とのやり取りにすることは可能でしょうか。
 自転車の防犯システムとして、そのラズパイ前に自転車を止め、QRコードなどでLINEを追加し異変があれば通知するといったシステムを考えています。教えていただけると幸いです。

返信する
そぞら

ブログを読んでいただきありがとうございます。

複数のLINEアカウントに対して
写真を同時送信することは可能です。
その場合はプログラムの9~15行目をコピーして
下に追加します。

返信する
九州ヒロキ

 返信ありがとうございます。
 防犯システムということで、複数のLINEアカウントに登録してもらう場合、異変検知で全員に送信ではなく、最後に登録した人のみに送信するなどは可能でしょうか。
 また、Androidアプリを作成してラズパイを遠隔操作や送受信するなど可能でしょうか。LINEbotを活用するか、Androidアプリを作成しようと考えています。
重ね重ねの質問で申し訳ありません。ご教授いただけると幸いです。

返信する
そぞら

まずLINEアカウントの登録についてですが
登録自体を自動化するのでしょうか?
自動メッセージを送信するためには
「LINE Notify」のトークン取得が必要であり
ここを自動化するのは難しいと思われます。

あらかじめトークン取得してある人に送信するのであれば
何らかの方法で人を識別して、
その人に送信することはできると思います。

申し訳ございません。
Androidアプリは作成経験がなく
お答えできないです。

返信する
平林

こんにちは!
こちらの記事を参考にラズパイでOpenCVが動かせました。
情報をまとめていただいてありがとうございます!

返信する
そぞら

ブログをご覧いただきありがとうございます。
今後も皆様のお役に立てるよう改善を進めてまいります。

返信する
ナス

2つ質問があります。
ラズパイを使って犬の様子をチャットアプリに動画として送るのを作ろうとしているんですが、動画で送る方法とかはありますか?
そしてカメラのモジュールをできれば、モジュールを使わずにできないでしょうか?

返信する
そぞら

すみません。動画の送信は未経験です。
カメラモジュールをモジュールを使わずに使用する方法も分からないです。

返信する
ナス

モジュールを自作することは可能でしょうか?

返信する
さの

こんにちは。
このサイトを参考にしてLINEに画像を送るのをできました。ありがとうございます。

一つ質問があります。

カメラ画像を一旦画像ファイルに保存してから、send_messageにてそのファイルを読み込んでいますが、ファイルに一旦保存することなく送信できるようにするにはどのようにコードを変更したら良いでしょうか?

SDカードに画像を繰り返し書き込むと、SDカードが早く劣化しそうな気がするので、改善したいと思っています。

よろしくお願いします。

返信する
そぞら

以下のコードを試してみてください。
画像は一時的にメモリ上(RAM)に保存されますが、SDカードには保存されません。
このコードでは、cv2.imencode()を使用して画像データをJPEG形式にエンコードし、
そのバイト列を直接メモリ上のBytesIOオブジェクトに保存しています。
そして、そのBytesIOオブジェクトをsend_message関数に渡して、一時ファイルとしてLINEに送信します。

import RPi.GPIO as GPIO
import cv2
import time
import datetime
import requests
import numpy as np
from io import BytesIO

# LINEメッセージ送信の関数
def send_message(Discovery_time, img_bytes):
url = “https://notify-api.line.me/api/notify”
token = “トークンをここに入力”
headers = {“Authorization” : “Bearer “+ token}
files = {‘imageFile’: img_bytes}
message = (Discovery_time,”侵入者あり”)
payload = {“message” : message}
r = requests.post(url, headers = headers, params=payload, files=files)

# 検出時間の取得
dt_now = datetime.datetime.now()
Discovery_time = dt_now.strftime(‘%Y年%m月%d日%H時%M分%S秒’)
print(Discovery_time)

# カメラ画像を取得する
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
cap.release()

# 画像をメモリに保存する
is_success, buffer = cv2.imencode(“.jpg”, frame)
img_bytes = BytesIO(buffer)

# LINEメッセージ送信
send_message(Discovery_time, img_bytes)

返信する
そぞら

すみません。
def send_message(Discovery_time, img_bytes):
の関数部分のインデントがおかしくなっていますので、ご注意ください。

返信する
さの

早速ご回答いただきましてありがとうございます😊

無事、ファイル書き込みなしでのLINE送信ができました。cv2.imencode、io.BytesIOを使うんですね。ググってもやり方を見つけられなかったので助かりました。ありがとうございました。

返信する
パセリ

こんにちは。
「Pythonプログラムでカメラ映像(動画)を表示する」について、プログラムをコピペして実行しましたが、エラーになり、numpyを最新にしてもダメです。
ラズパイ純正のカメラでないことと、他のmotion機能を自動起動に設定していることが疑われますが、解決方法を教えて頂けないでしょうか?

そぞら

ご質問いただきありがとうございます。

OpenCVを使ったカメラアクセスで問題が発生しているとのこと、大変お困りのことと思います。
ご指摘の通り、motionがカメラデバイスを占有していることが原因の可能性が高いです。

以下の手順で問題が解決するかもしれませんので、お試しください。

まず、motionを停止します。
ターミナルを開き、以下のコマンドを実行してください。
sudo service motion stop

次に、OpenCVを使ったプログラムを再実行してみてください。
motionが停止している間は、他のプログラムがカメラデバイスにアクセスできるはずです。

OpenCVの使用が終わったら、以下のコマンドでmotionを再開できます。
sudo service motion start

motionがカメラデバイスを占有していると、他のプログラムからはカメラへのアクセスが制限されるため、この手順で問題が解決する可能性があります。

もし問題が解決しない場合、エラー内容をコピペして返信していただけると、お手伝いできるかもしれません。
よろしくお願いいたします。

パセリ

そぞらさん、アドバイスありがとうございます。
motionをストップして、OpenCVを試してみましたが、以下のようなエラーでした。
初心者で理解できないエラーのため、アドバイス頂けると助かります。
※送信エラーのため、一部のエラーのみ貼り付けました。

Traceback (most recent call last):
File “/home/tama/.local/lib/python3.9/site-packages/numpy/core/__init__.py”, line 24, in
from . import multiarray
File “/usr/lib/python3/dist-packages/thonny/plugins/cpython_backend/cp_back.py”, line 263, in _custom_import
module = self._original_import(*args, **kw)
File “/home/tama/.local/lib/python3.9/site-packages/numpy/core/multiarray.py”, line 10, in
from . import overrides
File “/usr/lib/python3/dist-packages/thonny/plugins/cpython_backend/cp_back.py”, line 263, in _custom_import
module = self._original_import(*args, **kw)
File “/home/tama/.local/lib/python3.9/site-packages/numpy/core/overrides.py”, line 8, in
from numpy.core._multiarray_umath import (
File “/usr/lib/python3/dist-packages/thonny/plugins/cpython_backend/cp_back.py”, line 263, in _custom_import
module = self._original_import(*args, **kw)
ImportError: libopenblas.so.0: cannot open shared object file: No such file or directory

返信する
そぞら

エラーメッセージを拝見すると、numpy関連のエラーが発生しているようです。
ImportError: libopenblas.so.0: cannot open shared object file: No such file or directoryは、
OpenBLASという数学ライブラリが見つからない、または正しくインストールされていないことを示しています。
これを解決するためには、OpenBLASライブラリをシステムにインストールする必要があります。

以下の手順を試してみてください:

ターミナルを開き、以下のコマンドを実行します。

sudo apt install libopenblas-dev

変更を有効にするために、ラズパイを再起動してください。

返信する
パセリ

そぞらさん、アドバイスありがとうございます。OpenBLASをインストールすると、OpecCVをテストしてまエラーが無くなりましたが、画像が起動せず、固まったままです。
ターミナルでbumpyを最新にしても、ターミナルに以下のエラーです。何かアドバイスあればよろしくお願いします。
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: numpy in /usr/local/lib/python3.9/dist-packages (1.26.3)
WARNING: Running pip as the ‘root’ user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

返信する
そぞら

おそらく最新のOS「Bookworm」を使用されていることが原因と思われます。
デスクトップ画面の壁紙が鵜飼いのようなデザインではないでしょうか?
本記事が最新OSに対応しておらず申し訳ございません。

Bookwormでpipをする場合、仮想環境で実行しないとエラーが出ます。
※仮想環境はプロジェクトごとに異なるPythonのバージョンやライブラリを分けて管理できる、独立した作業スペースです。

この場合の対策は、

①OSを1世代古いものBullseye(Legacy)にインストールし直す(データは消えてしまいます)
②今のOSのまま仮想環境を作る

以上のどちらかになります。

仮想環境を作成しその中でライブラリーをインストールし、camera_test.pyを実行する手順を説明します。

ターミナルを開きます。
仮想環境を作成します。

myenvという名前で作成しますが、任意の名前に変更可能です。

python3 -m venv myenv

以下のコマンドを実行して、仮想環境をアクティブにします。

source myenv/bin/activate

pipを最新バージョンにアップグレードします。

python -m pip install –upgrade pip

OpenCVをインストールします。

pip install opencv-python==4.5.1.48

numpyをアップグレードします。

pip install -U numpy

camera_test.pyがあるディレクトリに移動します。

cdコマンドを使用して、そのファイルがあるディレクトリに移動します。
例えば、camera_test.pyがDocumentsフォルダにある場合は以下のようにします。

cd ~/Documents

以下のコマンドを実行して、camera_test.pyを実行します。

python camera_test.py

長くなってしまいましたが、よろしくお願いします。
また何かあればご質問いただければと思います。

返信する
パセリ

さぞらさん、アドバイスありがとうございます。最新OSでしたので仮想環境をトライしましたが、うまくいかなかったので、旧OSに再インストールしました。
OpenCVもエラーなく動作し、Line通知までたどり着きました。ありがとうございます。

返信する

こんにちは。
sudo pip3 install opencv-python==4.5.1.48
でopenCVを入れようとしたところ
WARNING: Running pip as the ‘root’ user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
という文が出てきてしまいました。仮想環境を作れということなのでしょうか?
OSはBullseyeです。

返信する
そぞら

お問い合わせいただきありがとうございます。

先ほど、最新のBullseyeでsudo pip3 install opencv-python==4.5.1.48を実行してみましたが
エラーが出ることなくインストールできました。

可能であれば、OSの再インストールをお試しください。
よろしくお願いいたします。

返信する
みみみ

こんにちは
大学の研究で参考にさせていただいています
電子工作の接続をなしで顔認識をした画像をLINEに送るようにプログラムをしたいのですが知識が足りずどのように変更すればいいのかわらないです
教えていただければ幸いです

返信する
そぞら

ブログを読んでいただいてありがとうございます。
カメラで顔を検出した際に、画像をLINEに送るプログラムを記事に追加しました。
不明点がありましたら、お気軽にお問い合わせください。
よろしくお願いいたします。

返信する
ぐら

コメント失礼致します。
そぞらさんのブログを参考にさせていただいております、大学生です。

「Pythonプログラムでカメラ映像(動画)を表示する」の段階で躓いています。
実行すると、以下のようなエラーが表示されるのですが、どう修正すればいいかわからないのでご享受いただきたいです。

Traceback (most recent call last):
File “”, line 9, in
cv2.error: OpenCV(4.5.1) /tmp/pip-req-build-zatnrcbe/opencv/modules/imgcodecs/src/loadsave.cpp:753: error: (-215:Assertion failed) !_img.empty() in function ‘imwrite’

他の質問者の犬さんと同じ警告が出ているのが問題なのでしょうか?以下サイトにて問題ない的なことが書かれており、現状は警告を無視しています。プログラム初心者なので常識的なことを質問しているかもしれませんが、ご回答のほどよろしくお願いいたします。
https://teratail.com/9uestions/358301

本体:Raspberry-Pi v4
OS:Raspberry-Pi OS(64-bit) Bullseye (2024/6/24インストール)
カメラモジュール:v3
Python:3.9.4
opencv:https://sozorablog.com/camera-module-v3/ を参考にインストール(上記と同じ警告が出る)

返信する
そぞら

連絡が遅くなり、申し訳ございません。

「【ラズベリーパイ】監視カメラの作り方|Pythonでカメラモジュールを自在に操作」で紹介しているプログラムは
カメラモジュールV3に対応していません。

カメラモジュールV3の場合は、Picamera2を使って画像を取得してからOpenCVで表示させる
という手順が必要になります。

https://sozorablog.com/camera-module-v3/
の内容を参考にしてもらえればと思います。

不明点がありましたら、お気軽にお問い合わせください。
よろしくお願いいたします。

返信する
グラン

初めまして。
コメント失礼いたします。
こちらの記事のラズベリーパイで監視カメラ【基礎編】からコードを入力して実行したところ

ModulNotFoundError: No module named ‘cv2’と出てしまいます。

改善点を教えて頂いてもよろしいでしょうか。
使用しているものは、
カメラモジュールV2
Raspberry Pi4 ModelB 4GB
です。

返信する
そぞら

初めまして。
コメントありがとうございます。

「ModulNotFoundError: No module named ‘cv2’」というエラーが出る場合、PythonにOpenCV(cv2)がインストールされていない可能性が高いです。これを解決するには、以下のコマンドをターミナルに入力してOpenCVをインストールしてみてください。
sudo pip3 install opencv-python==4.5.1.48
これでも解決しない場合は、「lsb_release -a」コマンドを実行してOSのバージョンを確認してみてください。
「Codename: bookworm」が表示される場合は、仮想環境の作成が必要です。

返信する
グラン

返信ありがとうございます。
OSを調べたところ「Codename: bookworm」が表示されました。

素人で恐縮ですが、仮想環境とはどのようなものでしょうか?

返信する
そぞら

bookworm で使用されているPython 3.11では、仮想環境を作成しないとパッケージのインストール時にエラーが発生します。
これはシステム全体に影響を与えないようにするための制限です。
ただし、カメラモジュールV2を使用されるのであれば、Raspberry Pi OS(Legacy, 32-bit)への再インストールを検討された方が良いかもしれません。
bookworm では仮想環境だけでなく、カメラを表示するプログラムも変更する必要があるためです。
先ほどの返信内容と矛盾する部分があることをお詫び申し上げます。

返信する
グラン

Raspberry Pi OS(Legacy, 32-bit)への再インストールをしたところ、OSがBullseyeになったので記事の通りに実行することが出来ました。

ご丁寧に解説していただきありがとうございました。

返信する
すなふきん おじん

Pythonプログラムで静止画を保存するを実行すると
cv2.error: OpenCV(4.5.1) /tmp/pip-req-build-zatnrcbe/opencv/modules/imgcodecs/src/loadsave.cpp:753: error: (-215:Assertion failed) !_img.empty() in function ‘imwrite’
がでてしまいます。

Pythonプログラムでカメラ映像(動画)を表示するはうまくいきました。
OSのVerは Bullseye ラズパイは p3 Model b+です。カメラモジュールは V3です。
インストールは当該サイトの手順どうりにしています。
但し、以下が違います。
raspi-configで「Glamor」を有効にする
/boot/config.txtの最下行に「dtoverlay=imx708」を追加
/boot/config.txtで「dtoverlay=vc4-kms-v3d」のコメントアウトを外す は libcameraコマンドが動作しなかったのでやっていません。

なにか わかることありましたら 教えて頂けますでしょうか。

ただ 64bit版の Bullseyeです。 32bit版でないといけないのでしょうか。

返信する
そぞら

カメラモジュールV3は、新しいIMX708センサーを使用しており、従来の方法(cv2.VideoCaptureを使用したOpenCVのコード)では正常に動作しません。
V3を使用するには、公式のライブラリであるPicamera2を使用する必要があります。
以下をお試しください。Raspberry Pi 3 Model A+で動作確認しました。
from picamera2 import Picamera2
# Picamera2のインスタンスを作成
picam2 = Picamera2()
# カメラ画像を保存
picam2.start_and_capture_file(“test.jpg”)

返信する
グラン

コメント失礼いたします。
本記事⑦の「検出した物体を表示する」のコードを実行すると、以下のようなエラーが表示されるのですが、どう修正すればいいかわからないのでご享受いただだいてもよろしいでしょうか。

Traceback (most recent call last):
File “”, line 21, in
ImportError: cannot import name ‘visualize’ from ‘utils’ (/home/guran/.local/lib/python3.9/site-packages/utils/__init__.py)

とエラーが出てしまいました。
Raspberry Pi OS(Legacy, 64-bit)を使用しています。

返信する
そぞら

コードを/home/pi/mediapipe/examples/object_detection/raspberry_piに保存されているでしょうか?
プログラムの保存場所が異なると、モジュールの読み込み時にエラーが発生します。
よろしくお願いいたします。

返信する
ゴロ助

はじめまして。コメント失礼します。
「カメラ映像からリアルタイムで物体を検出」で以下の手順を実施したのち、
もともと動作していた「Pythonプログラムでカメラ映像(動画)を表示する」で作成したプログラムで、cv2.imshow(“Frame”, frame)でフリーズするようになってしまいました。
何か考えられることがありましたが、教えていただけないでしょうか。

sudo apt install libcap-dev
git clone https://github.com/googlesamples/mediapipe.git
cd mediapipe/examples/object_detection/raspberry_pi
sh setup.sh

返信する
そぞら

お問い合わせいただき、ありがとうございます。
お手数ですが、以下の情報を教えていただけますか?
・お使いのラズパイのモデル名
・カメラのモデル名
・Raspberry Pi OSの種類(BullseyeまたはBookworm)
・もしお分かりになれば、OSのインストール時期
よろしくお願いいたします。

返信する
ゴロ助

返信が遅くなり、申し訳ございませんでした。
環境は、Raspberry Pi 4B、カメラのジュールV2、Bullseyeになります。
OSのインストール時期は、2024年7月頃になります。

「カメラ映像からリアルタイムで物体を検出」は実行すると下記のエラーが発生しており、調査中ですが、こちらの手順ミスか何かで、カメラ映像(動画)に影響を与えてしまったのかもしれません。

Error in cpuinfo: prctl(PR_SVE_GET_VL) failed
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
terminate called after throwing an instance of ‘cv::Exception’
what(): OpenCV(4.5.5) /tmp/bazel_build/opencv/modules/imgproc/src/imgwarp.cpp:1724: error: (-215:Assertion failed) dst.cols < SHRT_MAX && dst.rows < SHRT_MAX && src.cols < SHRT_MAX && src.rows < SHRT_MAX in function 'remap'
Process ended with exit code -6.

返信する
そぞら

まずはターミナルで以下のコマンドを実行して
カメラのプレビュー画面が表示できるか確認してみてください。
libcamera-hello –timeout 0

エラーについて
error: (-215:Assertion failed) dst.cols~の部分は
カメラ映像のサイズが大きすぎるために発生するようです。

カメラ映像(動画)を表示するコードにおいて
カメラの解像度を 640×480 に設定してみましょう。

camera = cv2.VideoCapture(0)の下に次の2行を追加してみてください。
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

ちなみに、物体検出のコードは正常に動きますか?

返信する
ゴロ助

返信ありがとうございます。
「libcamera-hello –timeout 0」を実施したところ、
カメラが認識されていませんでした(ERROR: *** no cameras available ***)
もう一度、OSのインストールからやり直したのですが、
「sh setup.sh」コマンドを実行するまではカメラ撮影ができているのですが、「sh setup.sh」コマンドを実行した以降は、カメラ撮影のプログラムを実行すると、フリーズしてしまいます。
cv2.imshow(“Frame”, frame)
key = cv2.waitKey(1)
ちなみに、下記の2行を追加しても、物体検出は正常に動作しませんでした。エラー内容も同じものでした。
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

そぞら

何らかのライブラリのアップデートの影響かもしれませんが、「cv2.VideoCapture(0)」のあとに
以下のコードを加えることで解決できるようです。
camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*’MJPG’))
または
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*’MJPG’))
上記の追加により、カメラの映像フォーマットを MJPG(Motion JPEG) に設定し
解像度の適用を確実にすることができます。
お手数ですが、こちらを追加して試していただければと思います。
また、ブログ本文も動作確認済みのコードに修正しました。
対応が遅くなり、ご迷惑をおかけしました。
問題をご報告いただいたおかげで、より正確な情報を提供できるようになりました。
ありがとうございました。

ゴロ助

ご丁寧にありがうございます。
カメラ撮影は正常に動作しました。

返信する
ABXY

初めまして。
コメント失礼いたします、初心者の学生です。
そぞらさんの分かりやすい説明に助けられています。本当にありがとうございます。

現在授業の課題に取り組んでいるのですが、
こちらのサイトの「カメラ映像からリアルタイムで物体を検出」を応用して、防犯カメラを作ろうと考えています。
具体的には、刃物を検知すると写真を撮って、LINEに通知を送れるようにしたいです。
この場合、プログラムをどのように書き換えればよいでしょうか。

お手数をおかけしますが、ご教示いただけますと幸いです。

返信する
そぞら

はじめまして。
とても興味深い企画ですね。記事に追加させていただきたいと思いますので、少々お待ちください。
また、LINE通知のサービスが3月31日で終了予定のため、Gmail通知への変更を検討しておりますが、LINE通知をご希望されますでしょうか?

返信する
ABXY

ご返信いただきありがとうございます。
Gmail通知で全く問題ありません。

返信する
ABXY

追加記事を拝見しました。
ご多忙の中本当にありがとうございます。
よろしければ今後も参考にさせていただきます。

返信する

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です