nyanpyou Note

主な目的は調べたり作ったりしたプログラミング備忘録(予定)

OpenCVで静止画を連続撮影した時に起こる「コマ落ち」について

例えば以下のように、Raspberry PiRaspberry 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した画像は全て同じものになってしまう(全く同じ画像が複数枚保存される)ため、別途対処の必要がある。