OpenCVで静止画を連続撮影した時に起こる「コマ落ち」について
例えば以下のように、Raspberry PiとRaspberry Pi Camera Moduleを用いて、0.2秒の露出時間で5秒に1回カメラの取り込みを更新する(つまり途切れずに光を取り込み続ける)ように設定し、連続で画像の保存をしたいとする。
from imutils.video import VideoStream import signal import time import cv2 import sys import numpy as np def signal_handler(signum, frame): print("exit") sys.exit(0) vs = VideoStream(usePiCamera=True, resolution=(1920, 1280), framerate=5, awb_mode="fluorescent", shutter_speed=200000).start() time.sleep(2.0) count = 0 #ctl+cで終了するように設定 signal.signal(signal.SIGINT, signal_handler) file = open("delay_times.txt", mode="a") print("start") while True: s = time.time() frame = vs.read() filename = "{}.jpg".format(str(count)) cv2.imwrite(filename, frame) count += 1 """ cv2.imshow("Frame", frame) key = cv2.waitKey(1) & 0xFF if key == ord("q"): break """ time.sleep(0.2) f = time.time() #1ループにかかった時間をファイルに書き出す file.write(str(f-s)+"\n") #cv2.destroyAllWindows() vs.stop() file.close()
この設定で写真撮影し、保存された画像を一つずつ確認すると、途中のフレームが飛んでいることがある。
しばらくこの現象の原因が不明のままだったが、確からしい原因が判明したため書き残す。
原因
これはwhileループにかかる時間がframerateを超えていると起こってしまうようだ。
例えば今回の例ではframerateは5で設定しているため、VideoStreamは0.2秒ごとに更新される。しかし、0.2秒より多い時間で繰り返しが進むと、あるところでループの間隔がVideoStreamの更新間隔を超えてしまうことがあり、こうなった場合に途中が飛んでしまう、抜けが起きることを確認した。
生成されたテキストファイルを確認し、結果の平均(サンプル数68)を出したところ、0.31秒となった。自分の環境ではおよそ0.1秒ほど余分に時間がかかっている。もちろんループの所要時間はその時の状態に依存するため、画面に撮影した映像を表示する処理(コメントアウトされている部分)を入れたりすると、もっと時間がかかることになる。
解決策
簡単な解決策は、抜けが起こらないようにもっと細かい間隔でVideoStreamをreadしに行くこと。今回の場合だとtime.sleep()を0.05秒に設定すると、コマ落ちは解消された。
ただし、VideoStreamのframerateよりも細かい間隔でreadした場合、更新前にreadした画像は全て同じものになってしまう(全く同じ画像が複数枚保存される)ため、別途対処の必要がある。