OpenCV VideoCapture 仅在读取 5 次后更新

0 投票
2 回答
4505 浏览
提问于 2025-04-18 10:47

我遇到了一个非常奇怪的错误,这个问题困扰了我几年的研究。我正在用Python和OpenCV2从网络摄像头读取图像数据。但问题是,图像总是滞后5帧。换句话说,每次调用read()时,得到的图像比实时画面慢了5帧。

我用的一个临时解决办法是每次抓取4帧,然后再读取第5帧来获取更新的图像,但这样做会严重影响我的性能。

这是我用来显示网络摄像头图像的代码:

    frame = self.getGoodFrame()

    if self.DEBUG:
        window = cv2.namedWindow("Angles")
    imgHSV = cv2.cvtColor(frame, cv2.cv.CV_BGR2HSV)

    ... Reseach specific code I shouldn't be giving out here ...
    ... It finds the center of a few bright colors in an image

    if self.DEBUG:
        debugImage = numpy.zeros((self.CAMERA_HEIGHT, self.CAMERA_WIDTH), numpy.uint8) #blank image

       ... Then we draw some stuff to the image to be displayed ...

        cv2.imshow("Angles", debugImage)
        cv2.waitKey(1)
        raw_input()

还有getGoodFrame()这个函数:

def getGoodFrame(self):
    MIN_READS_FOR_GOOD_FRAME = 4

    for i in xrange(MIN_READS_FOR_GOOD_FRAME):
        successful_read = self.capture.grab() 

    successful_read, frame = self.capture.read()

    if not successful_read:
        print "Unable to read from webcam, exiting."

    return frame

你会注意到我有一个raw_input()的调用。这让我可以通过在控制台按几次回车来查看需要读取多少帧。这显示出确实有5帧的延迟。

我觉得这不是硬件问题,因为我在多个摄像头和USB线缆上都遇到过这个问题。不过,我还没有在其他机器上尝试重现这个错误。

2 个回答

1

问题出在我的硬件处理画面的方式上。我没能完全搞明白,但我找到的解决办法非常简单,就是并行处理。我修改了代码,开了一个线程,不停地更新一个变量,这个变量用来存储当前的画面。然后,当我需要当前画面时,只需查看这个变量的值。

需要注意的是,由于缓冲的原因,这个方法仍然会落后5次读取的调用,但因为我在一个独立的线程里持续进行读取,所以更新速度非常快。这是因为我每秒可以进行很多次读取。得到的图像和实时画面几乎没有差别。

我写的这个Python类如下。虽然代码不算优雅,但确实能用。为了做好这件事,我会(也一定会)添加一些优雅的方式来退出这个无限循环。不过现在对我来说,这个方法已经让我的图像检测代码速度提高了超过100倍,这让我非常兴奋!:)

class webcamImageGetter:

    def __init__(self):
        self.currentFrame = None
        self.CAMERA_WIDTH = #webcam width
        self.CAMERA_HEIGHT = #webcam height
        self.CAMERA_NUM = 0

        self.capture = cv2.VideoCapture(0) #Put in correct capture number here
        #OpenCV by default gets a half resolution image so we manually set the correct resolution
        self.capture.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH,self.CAMERA_WIDTH)
        self.capture.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT,self.CAMERA_HEIGHT)

    #Starts updating the images in a thread
    def start(self):
        Thread(target=self.updateFrame, args=()).start()

    #Continually updates the frame
    def updateFrame(self):
        while(True):
            ret, self.currentFrame = self.capture.read()

            while (self.currentFrame == None): #Continually grab frames until we get a good one
                ret, frame = self.capture.read()

    def getFrame(self):
        return self.currentFrame

使用这个类时,你需要先初始化它,然后调用实例的start方法。这样,当你之后调用getFrame()时,就能得到来自摄像头的最新画面。太棒了!

2

默认的缓冲区设置为4.0,而不是1.0。

要解决这个问题:

cap.set(38,1)

参考链接: https://docs.opencv.org/4.x/d4/d15/group__videoio__flags__base.html#ga41c5cfa7859ae542b71b1d33bbd4d2b4

你可以在图片中看到参数编号38。

撰写回答