Raspberry Pi 4でRaspberry Pi Camera Module v2を動かしたいが難航している話
2020/02/25追記:
進展しました。
Raspberry Pi 4とPythonとOpenCVを使って、Raspberry Pi Camera Module v2を動かし、30fpsの動画を1分毎に連番で撮影したいのだが、思った通りに撮影ができずに沼にハマっている。
解像度をコード内で指定して撮影すると、出来上がった動画が早送りになってしまうのだ。具体的には、1分撮影したはずが、デスクトップPCに持っていって動画再生ソフト(VLCメディアプレーヤーなど)で動画を確認すると、15秒とかになっていたりする。
使用コード
#Python3.7.3 #OpenCV3.4.4 import numpy as np import cv2 import time windowname = "Camera View" i = 1 camera_number = input("カメラ番号を入力してください(通常は0)\n") cap = cv2.VideoCapture(int(camera_number)) if(cap==None): print("カメラが見つかりません") record = False cap.set(cv2.CAP_PROP_FRAME_WIDTH,640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT,480) #cap.set(cv2.CAP_PROP_FRAME_WIDTH,1280) #cap.set(cv2.CAP_PROP_FRAME_HEIGHT,720) cap.set(cv2.CAP_PROP_FPS, 30) size = (int(cap.get(3)), int(cap.get(4))) fourcc = cv2.VideoWriter_fourcc("D","I","V","X") cv2.namedWindow(windowname) print("sで録画開始、qで録画終了、eでプログラム終了\n") while(cap.isOpened()): # カメラ映像が取得できなくなったら止める if(cap==None): print("カメラが見つかりません\n") break ret, frame = cap.read() cv2.imshow(windowname, frame) if(record): rec.write(frame) keyvalue = cv2.waitKey(1) & 0xFF now_time = time.time() #録画開始から60秒経過したら動画を閉じて、新たな動画で書き込みを開始する if(now_time-start_time>=60): rec.release() i+=1 rec = cv2.VideoWriter('record{}.avi'.format(i), fourcc, 30, size) start_time = time.time() else: #録画停止中はカメラ映像を描画 cv2.imshow(windowname, frame) keyvalue = cv2.waitKey(1) & 0xFF if(keyvalue==ord("e")): #ord()で文字列をAsciiコードに変換 if(record): print("録画終了") break elif(keyvalue==ord("s") and not record): i = 1 #ビデオファイル書き込みの設定 rec = cv2.VideoWriter('record{}.avi'.format(i), fourcc, 30, size) start_time = time.time() print("録画開始") record = True elif(keyvalue==ord("q") and record): rec.release() print("録画終了") record = False # Release everything if job is finished if(record): rec.release() cv2.destroyAllWindows() print("プログラムを終了します")
解決法の模索
qiita.com まずこちらの記事のテストコードを試させて貰ったが、
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('H', '2', '6', '4'));
として実行すると砂嵐が表示されてカメラ映像が映らない。BGR3、MJPG、YUYVだと映る。なんでや…
H264コーデックが使えるように2点試したが、どちらも効果はなかった。
1. OpenCVをアンインストールし、
pip install opencv-contrib-python
で入れ直し。contribには商用利用時にライセンスが必要なモジュールも含まれているため、そこで解決しないかを狙った。
2. こちら(
python - OpenCV: FFMPEG: tag 0x34363268/'h264' is not supported with codec - Stack Overflow
)を参考に、
sudo apt-get install libx264-dev
のインストール。
この時点で、H264コーデックが動かない件は、今の知識で追いかけると無限にハマりそうな雰囲気を感じたため、ここで一旦放置することにした。
知る必要があるのは、どの条件なら望む動画が撮影できるのか。
他の人が書いた動画撮影プログラムでどう動作するか(自分が変なことを書いていないか)確認したかったので、試してみた。参考にしたのはこのページ。
「正しいコーデックと拡張子の組み合わせを見つけるのに時間がかかった。わからなかったらMJPGと.aviの組み合わせでまず試しなさい。」と書かれている。Raspberry Pi で動画保存が問題なくできることを確認しているらしい。問題は少しずつ減らすに限るため、以後はひとまずこの組み合わせをデフォルトと考えることにした。掲載されているコードを丸々コピーして試すことにする。このコードは、撮影した映像をRGB成分に分けて4分割して動画にするようになっているので、元動画を保存するように変更したものも作ってみた。
#Python3.7.3 #OpenCV3.4.4 # import the necessary packages from __future__ import print_function from imutils.video import VideoStream import numpy as np import argparse import imutils import time import cv2 # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-o", "--output", required=True, help="path to output video file") ap.add_argument("-p", "--picamera", type=int, default=-1, help="whether or not the Raspberry Pi camera should be used") ap.add_argument("-f", "--fps", type=int, default=20, help="FPS of output video") ap.add_argument("-c", "--codec", type=str, default="MJPG", help="codec of output video") args = vars(ap.parse_args()) # initialize the video stream and allow the camera # sensor to warmup print("[INFO] warming up camera...") vs = VideoStream(usePiCamera=args["picamera"] > 0).start() time.sleep(2.0) # initialize the FourCC, video writer, dimensions of the frame, and # zeros array fourcc = cv2.VideoWriter_fourcc(*args["codec"]) writer = None (h, w) = (None, None) # loop over frames from the video stream while True: # grab the frame from the video stream and resize it to have a # maximum width of 300 pixels frame = vs.read() frame = imutils.resize(frame, width=300) # check if the writer is None if writer is None: # store the image dimensions, initialize the video writer, # and construct the zeros array (h, w) = frame.shape[:2] writer = cv2.VideoWriter(args["output"], fourcc, args["fps"], (w, h), True) # write the output frame to file writer.write(frame) # show the frames cv2.imshow("Frame", frame) key = cv2.waitKey(1) & 0xFF # if the `q` key was pressed, break from the loop if key == ord("q"): break # do a bit of cleanup print("[INFO] cleaning up...") cv2.destroyAllWindows() vs.stop() writer.release()
状況整理
ここで一度状況を整理するため、各場合について動画を撮影し比較を行う。機械式のストップウォッチを30秒間撮影し、出来上がった動画のコマ数を調べて、撮影時のカメラのfpsを計算する。コマ数の集計は、こちらの動画を静止画に分解するプログラムを用い、ストップウォッチのスタート時と30秒経過時の静止画を目視で決定して行う。撮影以外の作業はデスクトップPCで行う。以後、自作コードは「自作」、pyimageserchの記事のコードは「四分割」、pyimageserchのコードを変更して作ったコードを「改変」と呼ぶ。また、自作コードは「30fps」を、四分割と改変コードは「20fps」を指定していることに注意する。
- 自作・DIVX・avi・解像度640×480指定の場合
- 自作・DIVX・avi・解像度1280×720指定の場合
- 自作・MJPG・avi・解像度640×480指定の場合
- 自作・MJPG・avi・解像度1280×720指定の場合
- 四分割・MJPG・avi・元コードそのまま(width=300)の場合
- 改変・MJPG・avi・width=300の場合
- 改変・MJPG・avi・width=640の場合
- 改変・MJPG・avi・width=1280の場合
これらの場合について撮影を行った。
結果
- 自作・DIVX・avi・解像度640×480指定の場合
30秒:36~933フレーム→30fps - 自作・DIVX・avi・解像度1280×720指定の場合
30秒:13~416フレーム→13fps - 自作・MJPG・avi・解像度640×480指定の場合
30秒:17~724フレーム→24fps - 自作・MJPG・avi・解像度1280×720指定の場合
30秒:10~273フレーム→9fps - 四分割・MJPG・avi・元コードそのまま(width=300)の場合
30秒:17~696フレーム→23fps - 改変・MJPG・avi・width=300の場合
30秒:57~1963フレーム→64fps - 改変・MJPG・avi・width=640の場合
30秒:26~746フレーム→24fps - 改変・MJPG・avi・width=1280の場合
30秒:7~223フレーム→7fps
自作コードでコーデックDIVX、保存形式aviを選択し、640×480の解像度を指定した時のみ、カメラが指定したfpsで動作することが分かった。
四分割と改変width=640の場合もおおよそ指定した通りのfpsだが、少し大きい値になった。動画再生ソフトで動画を再生してみるとやはり少し早送りに見える。
考察
結局どうやったら640×480以上の解像度で30fpsの動画が撮影できるのかは不明のまま。仕様上は1920×1080までは30fpsで撮影が可能らしいが…
Buy a Camera Module V2 – Raspberry Pi
考えられる原因で一番ありそうなのは、Raspberry Piが処理しきれなくなっている事だろうか。動画のリアルタイム表示をやめれば、負荷が軽減されてより大きな解像度でも30fpsで撮影可能かもしれない。
しかし、この問題に割ける時間がこれ以上取れなくなったため、ここで一端打ち止めとする。