LoginSignup
7
7

More than 5 years have passed since last update.

OpenCVでVideoCaptureしてreadしたものが墨塗になる問題

Last updated at Posted at 2019-02-14

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()
7
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7