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
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
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
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
※ 動作の検知された一覧が表示されたら完了です。
(動画に動体が検知されない場合は、何も表示されませんよ~)
以上で終了です。 お疲れさまでした~