こんにちはイチケンです。
OpenCVを使ってラズパイ+Webカメラでタイムラプス動画撮影をしていきますよ。
OpenCVを使わず定期的に撮影したJPEG画像をあとでMP4タイムラプスに変換する方法はこちら。
ラズパイで植物タイムラプス撮影【こどもちゃれんじ】CONTENTS [非表示]
この記事でわかること
ラズパイ、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ビデオとステレオオーディオLogicool(ロジクール)
ロジクール ウェブカメラ C920n ブラック フルHD 1080P ウェブカム ストリーミング 自動フォーカス ステレオマイク 国内正規品 2年間メーカー保証 ブラック
960-001261
サンプルコード
ではさっそくサンプルコードです。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 件のコメント:
コメントを投稿