0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberryのカメラモジュールで動画から動体検知

Last updated at Posted at 2025-01-05

Raspberry PI と言えば、色々な電子工作 という事で、前回の記事 ↓

において、カメラモジュールを使って静止画を扱ってます。
今回は動画を取得して、前回と似たような事を試してみます。

Step1:前回同様、カメラで静止画(JPG)を保存
カメラモジュールの接続確認や動作確認の為、以下の手順から静止画の取得で、問題が無い事を確認していきましょう。
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install libcap-dev
sudo apt-get install libcamera-dev
sudo apt-get install python3-picamera2
sudo mkdir /test
sudo mkdir /test/camera
sudo chmod 777 -R /test
cd /test/camera

準備が出来たらプログラムを作りましょう。
vi createjpg.py

createjpg.py
import picamera2
import time

# カメラの設定
picam2 = picamera2.Picamera2()
picam2.start_preview()
config = picam2.create_preview_configuration(main={"size": (1920, 1080)})
picam2.configure(config)
picam2.start()

# 写真を撮影して保存
time.sleep(2)  # プレビュー開始後、2秒待つ
picam2.capture_file("image.jpg")  # 画像を image.jpg として保存

# カメラを停止
picam2.stop()

プログラムが出来たら実行してみましょう。
python createjpg.py

実行後、撮影したJPGファイルを確認しましょう。
ls -l (image.jpg の存在を確認しましょう)
カメラ撮影により、JPGファイルが作成されたのが解ります。
ここまで正常であれば、カメラの調子や接続等に、問題が無い事が確認されます。

Step2:動画を撮影しましょう
必要なモジュールをインストールします

sudo apt-get install python3-opencv
sudo apt-get install ffmpeg

準備が出来たらプログラムを作りましょう。
vi createmv.py

createmv.py
import picamera2
import cv2
import time
import subprocess
import numpy as np

# カメラの設定
picam2 = picamera2.Picamera2()
config = picam2.create_preview_configuration(main={"size": (1920, 1080)})
picam2.configure(config)
picam2.start()

# 動画の保存先とファイル名
video_path = "output.mp4"

# FFmpegコマンドの作成
command = [
    'ffmpeg',
    '-y',
    '-f', 'rawvideo',
    '-vcodec','rawvideo',
    '-s', '1920x1080',
    '-pix_fmt', 'bgr24',
    '-r', '20',
    '-i', '-',
    '-an',
    '-vcodec', 'h264_v4l2m2m', # ハードウェアエンコーダを使用
    '-b:v', '2M', # ビットレートを設定(必要に応じて調整)
    video_path
]

# FFmpegプロセスを開始
process = subprocess.Popen(command, stdin=subprocess.PIPE)

# 5秒間動画をキャプチャ
start_time = time.time()
while time.time() - start_time < 5:
    frame = picam2.capture_array()
    # BGRに変換
    frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) # RGBからBGRへ変換
    process.stdin.write(frame.tobytes())

# 後処理
process.stdin.close()
process.wait()
picam2.stop()

※「ハードウェアエンコーダ」の部分は、RasPI4以降の推奨値です。RasPI3以前の推奨値は、「h264_omx」この辺りでしょうか?

プログラムが出来たら実行してみましょう。
python createmv.py

撮影したmp4ファイルを確認しましょう。
ls -l (output.mp4 の存在を確認しましょう)
カメラ撮影により、mp4ファイルが作成されたのが解ります。

Step3:動画に動体があるか検知します
必要なプログラムを用意しましょう。
vi chkmv.py

chkmv.py
import cv2
import time

def analyze_motion(video_path):

    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        print("Error: Could not open video.")
        return

    # 背景差分法の準備
    fgbg = cv2.createBackgroundSubtractorMOG2()

    # 各物体の情報を格納するリスト
    objects = []

    frame_rate = cap.get(cv2.CAP_PROP_FPS)
    frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

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

        # 前処理:グレースケール変換、ぼかし
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        gray = cv2.GaussianBlur(gray, (5, 5), 0)

        # 背景差分、二値化、輪郭抽出
        fgmask = fgbg.apply(gray)
        _, thresh = cv2.threshold(fgmask, 200, 255, cv2.THRESH_BINARY)
        contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 各輪郭に対して処理
        for contour in contours:
            # 小さすぎる輪郭は無視
            if cv2.contourArea(contour) < 500: # 500は閾値。必要に応じて調整
                continue

            x, y, w, h = cv2.boundingRect(contour)
            area = w * h

            # 新しい物体か既存の物体か判定
            matched = False
            for obj in objects:
                if abs(x - obj['x']) < 50 and abs(y - obj['y']) < 50: # 50は閾値。必要に応じて調整
                    obj['end_time'] = cap.get(cv2.CAP_PROP_POS_MSEC) / 1000 # 経過時間を更新
                    obj['max_area'] = max(obj['max_area'], area)
                    matched = True
                    break

            # 新しい物体の場合
            if not matched:
                objects.append({
                    'x': x,
                    'y': y,
                    'start_time': cap.get(cv2.CAP_PROP_POS_MSEC) / 1000,
                    'end_time': cap.get(cv2.CAP_PROP_POS_MSEC) / 1000,
                    'max_area': area
                })
    cap.release()

    # 結果の出力
    for i, obj in enumerate(objects):
        duration = abs(obj['end_time'] - obj['start_time'])
        if duration == 0:
            continue
        print(f"Object {i+1}: Max Area = {obj['max_area']} pixels, Duration = {duration:.2f} seconds")

if __name__ == "__main__":
    video_path = "output.mp4" # 動画ファイルのパスを指定
    analyze_motion(video_path)

※「cv2.contourArea(contour) < 500:」 輪郭の面積の閾値です。小さすぎるノイズを除去するために使用します。
※「abs(x - obj['x']) < 50 and abs(y - obj['y']) < 50:」 物体が同一か否かを判断する閾値です。物体の移動速度に合わせて調整します。

プログラムが出来たら実行してみましょう。
python chkmv.py
※ 動作の検知された一覧が表示されたら完了です。
(動画に動体が検知されない場合は、何も表示されませんよ~)
以上で終了です。 お疲れさまでした~

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?