こんにちはイチケンです。
OpenCVを使ってラズパイ+Webカメラでタイムラプス動画撮影をしていきますよ。
OpenCVを使わず定期的に撮影したJPEG画像をあとでMP4タイムラプスに変換する方法はこちら。
ラズパイで植物タイムラプス撮影【こどもちゃれんじ】この記事でわかること
ラズパイ、Webカメラ、OpenCVの組み合わせでタイムラプス動画を作る方法がわかります。自動削除は実装していませんが、それ以外はストレージが生きている限り撮り続けることができる作りになっています。
- OpenCVでWebカメラ動画取り込み
- 時刻を埋め込んで表示
- 1秒おきに動画として保存
- 一時間ごとに新しい動画ファイルに切り替え
- 一日ごとに新しい日付フォルダに切り替え
OpenCVでMP4を作る場合は、最後にきちんとファイルの終了処理をしないと動画が見れなくなります。なので不意のシャットダウンなどに備え、一時間に一回くらいファイルを切り替えるようにします。
前提条件
まず前提条件となる私の環境です。
Raspberry Pi | 4 Model B 8GB |
---|---|
OS | Raspbian GNU/Linux 10 (buster) |
VSCode | 1.55.2 |
Python | 3.7.3 |
OpenCV | 4.5.1 |
機器の準備はこちらから
【IoT×キャンプ|機器準備編】意地でもラズパイとキャンプへ!らずキャン△プロジェクト|第1回 【IoT×キャンプ|OpenCVインストール編】意地でもラズパイとキャンプへ!らずキャン△プロジェクト第10回USB-Webカメラ
今回使用しているカメラはこちら。オートフォーカスや自動HD光補正など、高品質で撮影だけでなくリモート会議にも最適。三脚対応などタイムラプスに必要な機能もそろっており、マルチな一台としてオススメです。
ロジクールC920 PRO HDウェブカメラ、1080pビデオとステレオオーディオサンプルコード
ではさっそくサンプルコードです。2つのpyファイルで構成しています。
- webcam.py
- log.py
ではまず「webcam.py」から。名前は何でもかまいません。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime
import cv2
import os
import time
import log #自作モジュール
from logging import getLogger
# Log設定
LOG_PATH = "./Log/" #Logファイル保存先
logger = getLogger(__name__)
log.set_log_config(logger, LOG_PATH, 'timelapse.log')
class WebCamTimeLapse:
def __init__(self, mp4_fps, resize_ratio):
# ファイルパス
self.VIDEO_PATH = "./Timelapse/" #Videoデータ保存先
# 時間ごとに処理するための比較用変数
dt_now = datetime.datetime.now()
self.previous_sec = dt_now.second
self.previous_min = dt_now.minute
self.previous_hour = dt_now.hour
# Fourcc (MP4)
self.fourcc = cv2.VideoWriter_fourcc("m","p","4","v")
# Video保存設定
self.mp4_fps = mp4_fps #FPS for timelapse
self.resize_ratio = resize_ratio # 縮小比率
def timelapse(self):
logger.info('Start timelapse')
try:
# MP4保存先
filename = self.__get_video_dir()
# Webカメラ用インスタンス
cap = cv2.VideoCapture(0) # 引数でカメラ指定
# Webカメラ情報取得
fps = int(cap.get(cv2.CAP_PROP_FPS)) # FPS
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # Width
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # Height
logger.info(f'FPS={fps} Width={w} Hight={h}')
# 設定にしたがってサイズ等分 1ならそのまま 2なら半分
mp4_w = int(w/self.resize_ratio)
mp4_h = int(h/self.resize_ratio)
# Video保存用インスタンス
video = cv2.VideoWriter(filename, self.fourcc, self.mp4_fps, (mp4_w,mp4_h))
# Webカメラ安定タイマー
time.sleep(2) #s
while True:
#1フレーム読み込み
ret, img = cap.read()
# Movie表示
dt_now = datetime.datetime.now()
dt_str = dt_now.strftime('%Y/%m/%d %H:%M:%S')
cv2.putText(img, dt_str,(0,30), cv2.FONT_HERSHEY_PLAIN, 2, (255,255,255), 2, cv2.LINE_AA)
cv2.imshow("image", img)
# 1秒ごとにムービー保存
if self.__compare_sec():
# 1時間ごとに保存ファイル切り替え
if self.__compare_hour():
# 日付変わったとき用にファイルパス更新
filename = self.__get_video_dir()
# Video保存用インスタンス更新
video.release()
video = cv2.VideoWriter(filename, self.fourcc, self.mp4_fps, (mp4_w,mp4_h))
video.write(cv2.resize(img,(mp4_w,mp4_h)))
# キー入力受付 & ループディレイ
k = cv2.waitKey(50) #ms
if k == 27: # ESCで終了
break
except KeyboardInterrupt: # ターミナルCtrl+C強制終了
logger.info(f"KeyboardInterrupt")
except Exception as e:
logger.exception(e)
finally:
#終了処理
video.release()
cap.release()
cv2.destroyAllWindows()
logger.info('Finished')
def __get_video_dir(self):
dt_now = datetime.datetime.now()
day_folder = dt_now.strftime("%Y%m%d")
video_dir = self.VIDEO_PATH + day_folder
os.makedirs(video_dir, exist_ok=True)
filename = video_dir +"/"+ dt_now.strftime("%Y%m%d_%H%M%S") + ".mp4"
logger.info(filename)
return filename
def __compare_sec(self):
dt_now = datetime.datetime.now()
now = dt_now.second
if self.previous_sec == now:
return False
else:
self.previous_sec = now
return True
def __compare_min(self):
dt_now = datetime.datetime.now()
now = dt_now.minute
if self.previous_min == now:
return False
else:
self.previous_min = now
return True
def __compare_hour(self):
dt_now = datetime.datetime.now()
now = dt_now.hour
if self.previous_hour == now:
return False
else:
self.previous_hour = now
return True
if __name__ == "__main__":
logger.info('App start')
mp4_fps = 60
resize_ratio = 1 # 2にするとサイズ半分
# タイムラプスのインスタンス
tl = WebCamTimeLapse(mp4_fps, resize_ratio)
tl.timelapse()
追記:ラズパイに上記ソフトを入れて屋外でcrontab自動立ち上げ時しようとするとimshowでエラーになります。エラーハンドリングするかコメントアウトしましょう。
次にログ用の「log.py」です。webcam.pyと同じフォルダに配置するのがラクです。こちらは名前を変えた場合はwebcam.pyの import log もあわせて変えてください。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from logging import StreamHandler, DEBUG, INFO, Formatter
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
import os
def set_log_config(logger, dir, filename):
# ---- Prepare log folder ----
os.makedirs(dir, exist_ok=True)
filepath = dir + filename
# ---- handler1: For terminal ----
handler1 = StreamHandler()
handler1.setLevel(DEBUG)
handler1.setFormatter(Formatter("[%(asctime)s][%(levelname)8s][%(name)s][%(funcName)s] %(message)s"))
# ---- handler2: For log file ----
#handler2 = TimedRotatingFileHandler(filename = filepath, when='midnight', backupCount=30, encoding='utf-8') # If you want to rotate file by time, use this
handler2 = RotatingFileHandler(filename = filepath, maxBytes = 1048576, backupCount = 10, encoding='utf-8', ) # 1MB, keep 10 files
handler2.setLevel(INFO)
handler2.setFormatter(Formatter("[%(asctime)s][%(levelname)8s][%(name)s][%(funcName)s] %(message)s"))
# ---- Set log config ----
logger.setLevel(DEBUG)
logger.addHandler(handler1)
logger.addHandler(handler2)
logger.propagate = False
あとはwebcam.pyを実行すればタイムラプス撮影開始します。終わる時は動画ウィンドウにフォーカスをあわせてESCキーです。
サクサク解説
では手短にサクサク解説いきます。ログについてはこちら。
結局どうする?ラクして無難にPython loggingを使うためのサンプルコードと使用例Webカメラ起動
VideoCaptureのインスタンス作った時点でWebカメラが起動します。大抵のWebカメラは起動後数フレームはピントや明るさ調整するので、取り込みをスキップします。
# Webカメラ用インスタンス
cap = cv2.VideoCapture(0) # 引数でカメラ指定
フレーム取り込み
readで1フレームを画像配列として取得できます。
#1フレーム読み込み
ret, img = cap.read()
現在時刻埋め込みと動画表示
そのままだと何時かよくわからないので時刻を画像として埋め込みます。
# Movie表示
dt_now = datetime.datetime.now()
dt_str = dt_now.strftime('%Y/%m/%d %H:%M:%S')
cv2.putText(img, dt_str,(0,30), cv2.FONT_HERSHEY_PLAIN, 2, (255,255,255), 2, cv2.LINE_AA)
cv2.imshow("image", img)
時間ごとに処理
表示は毎サイクル実施しますが、動画ファイルへの保存は1回/秒に間引いてます。
あと1ファイル/時間で切り替えていき、日付が変わると新しい日付フォルダに切り替えます。
それぞれ前回処理した秒や時間を覚えておき、比較して不一致なら実行するようにしています。
# 1秒ごとにムービー保存
if self.__compare_sec():
# 1時間ごとに保存ファイル切り替え
if self.__compare_hour():
# 日付変わったとき用にファイルパス更新
filename = self.__get_video_dir()
# Video保存用インスタンス更新
video.release()
video = cv2.VideoWriter(filename, self.fourcc, self.mp4_fps, (mp4_w,mp4_h))
video.write(cv2.resize(img,(mp4_w,mp4_h)))
解説は以上です。
最後に
いかがでしたか?
この記事が皆さんのお役に立てれば幸いです。別の手法でのタイムラプスはこちら。JPEG画像保存のほうがシャットダウンに対し堅牢ですね。
ラズパイで植物タイムラプス撮影【こどもちゃれんじ】
0 件のコメント:
コメントを投稿