GStreamer画像ストリームをOpenCVで受け、それをVideoCaptureして、readしては処理するループを回します。その際、フレームが壊れて墨塗になってしまうことがありました。
readしたものに対する画像処理が重すぎて(あるいはシステムが同時にほかの処理を並行して実行していてこちらへの計算資源がたっぷりないとき)、次にreadするtimingが適当でないと、ストリームのバッファがオーバーフローして壊れてしまうようです。逆に、Gstreamerのストリームが途切れたときに、readし処理するループ側が待ってやる処理も必要です。
以下、バッファ機能を自前で作り、上記の問題を解決したコード例です。
【ここ】に、readを別スレッドにして画像処理サイクルに重ねると高速化できるという記事があり、ヒントになりました。そのコードより、もっと簡素にできます。また、データに追いつかない場合だけでなく、データが遅れた場合の処理も追加します。
import cv2
...
def reader(video):
# Reference: https://www.pyimagesearch.com/2017/02/06/faster-video-file-fps-with-cv2-videocapture-and-opencv/
while True:
# キューがいっぱいなら、最も古いフレームを捨てる
if video.Q.full(): video.Q.get()
# ここでVideoCaptureしたもののread
ret, frame = video.capture.read()
if not ret: continue
video.Q.put(frame) # FIFOキューにフレームを追加
class VideoReco():
def __init__(self):
# フレームのFIFOキューをバッファにする
# framerate 30くらいなら、1秒以上前の画像にはもう興味はないので 30
self.Q = Queue(maxsize=30)
def run(self):
self.capture = cv2.VideoCapture(GStreamerパイプライン記述)
# 別スレッドに読み込みとバッファリングをやらせる
sr = threading.Thread(target=reader, args=([self]))
sr.start()
while self.capture.isOpened():
if self.Q.qsize() > 0:
# ここで直接 capture.read()せずに、キューから取り出して処理
frame = self.Q.get()
else:
# データが遅れていたらキューにデータが入るまで処理側でidling
continue
# ...
# キューにデータがあれば、ここでframeに対する画像処理いろいろ
# ...
# VideoCaptureのループのおまじない
if cv2.waitKey(1) & 0xFF == ord('q'): break
def main():
video = VideoReco()
video.run()