网络摄像头 cv2.VideoCapture 的延迟更低

Yon*_*arp 4 python webcam opencv video-capture low-latency

我正在构建一个使用网络摄像头来控制视频游戏的应用程序(有点像 kinect)。它使用网络摄像头 (cv2.VideoCapture(0))、AI 姿势估计 ( mediapipe ) 和自定义逻辑将输入传输到 dolphin 模拟器中。

问题是延迟。我使用手机的高速摄像头记录自己的抓拍,发现我的手和屏幕上的帧之间存在约 32 帧~133 毫秒的延迟。这是在任何附加代码之前,只是一个视频循环readcv2.imshow大约 15 毫秒)

有什么办法可以减少这个延迟吗?

我已经在单独的线程中抓取帧,将CAP_PROP_BUFFERSIZE设置为 0,并降低 CAP_PROP_FRAME_HEIGHT 和 CAP_PROP_FRAME_WIDTH,但我仍然得到约 133 毫秒的延迟。我还有什么可以做的吗?

下面是我的代码:

class WebcamStream:
    def __init__(self, src=0):
        self.stopped = False

        self.stream = cv2.VideoCapture(src)
        self.stream.set(cv2.CAP_PROP_BUFFERSIZE, 0)
        self.stream.set(cv2.CAP_PROP_FRAME_HEIGHT, 400)
        self.stream.set(cv2.CAP_PROP_FRAME_WIDTH, 600)

        (self.grabbed, self.frame) = self.stream.read()
    
        self.hasNew = self.grabbed
        self.condition = Condition()

    def start(self):

        Thread(target=self.update, args=()).start()
        return self

    def update(self,):
        while True:
            if self.stopped: return
            
            (self.grabbed, self.frame) = self.stream.read()
            with self.condition:
                self.hasNew = True
                self.condition.notify_all()
            

    def read(self):
        if not self.hasNew:
            with self.condition:
                self.condition.wait()

        self.hasNew = False
        return self.frame

    def stop(self):
        self.stopped = True
Run Code Online (Sandbox Code Playgroud)

应用程序需要尽可能接近实时地运行,因此延迟的任何减少,无论多小都是很好的。目前,在网络摄像头延迟(约 133 毫秒)、姿势估计和逻辑(约 25 毫秒)以及移动到正确姿势所需的实际时间之间,延迟高达约 350-400 毫秒。当我尝试玩游戏时绝对不理想。

编辑:这是我用来测试延迟的代码(在我的笔记本电脑上运行代码,记录我的手和屏幕,并计算捕捉中的帧差异):

if __name__ == "__main__":
    cap = WebcamStream().start()
    while(True):
        frame = cap.read()
        cv2.imshow('frame', frame)
        cv2.waitKey(1)
Run Code Online (Sandbox Code Playgroud)

use*_*197 9

欢迎来到延迟之战(削减延迟)

\n

您上面描述的经验是一个很好的例子,累积的延迟可能会破坏任何保持控制环路足够紧的机会,从而真正控制一些有意义的稳定的东西,就像在我们希望保持的人机接口系统中一样:

\n

用户动议| CAM-捕捉| IMG-加工| GUI-显示| 用户视觉皮层场景捕捉| 用户的决定+行动| 环形

\n

显示 OpenCV 分析的真实情况,以“感知”我们在各个采集-存储-转换-后处理-GUI 管道实际阶段花费了多少时间(根据需要放大)

\n

在此输入图像描述

\n
\n

我们采取哪些导致延迟的步骤?

\n

请原谅我们在哪里累计每个特定的延迟相关成本的原始草图:

\n
\n   CAM \\____/                                     python code GIL-awaiting ~ 100 [ms] chopping\n        |::|                                      python code calling a cv2.<function>()\n        |::|   __________________________________________-----!!!!!!!-----------\n        |::|    ^     2x                                 NNNNN!!!!!!!MOVES DATA!\n        |::|    | per-call                               NNNNN!!!!!!!    1.THERE\n        |::|    |     COST                               NNNNN!!!!!!!    2.BACK\n        |::|    |           TTTT-openCV::MAT into python numpy.array\n        |::|    |          ////       forMAT TRANSFORMER TRANSFORMATIONS\n        USBx    |         ////                           TRANSFORMATIONS\n        |::|    |        ////                            TRANSFORMATIONS\n        |::|    |       ////                             TRANSFORMATIONS\n        |::|    |      ////                              TRANSFORMATIONS\n        |::|    |     ////                               TRANSFORMATIONS\n    H/W oooo   _v____TTTT in-RAM openCV::MAT storage     TRANSFORMATIONS\n       /    \\        oooo ------ openCV::MAT object-mapper\n       \\    /        xxxx\n O/S--- \xc2\xb0\xc2\xb0\xc2\xb0\xc2\xb0         xxxx\n driver """" _____   xxxx\n         \\\\\\\\    ^   xxxx ...... openCV {signed|unsigned}-{size}-{N-channels}\n _________\\\\\\\\___|___++++ __________________________________________\n openCV I/O      ^   PPPP                                 PROCESSING\n            as F |   ....                                 PROCESSING\n               A |   ...                                  PROCESSING\n               S |   ..                                   PROCESSING\n               T |   .                                    PROCESSING\n            as   |   PPPP                                 PROCESSING\n      possible___v___PPPP _____ openCV::MAT NATIVE-object PROCESSING\n\n
Run Code Online (Sandbox Code Playgroud)\n
\n

我们/我们可以对抗哪些延迟此处对抗什么延迟?

\n

硬件延迟可能会有所帮助,但更改已购买的硬件可能会变得昂贵

\n

软件延迟是可能的,但越来越难

\n

设计效率低下是最后也是最常见的地方,延迟可以被削减

\n
\n

开放式CV?
这里没什么可做的。问题在于 OpenCV-Python 绑定细节:

\n
\n

...所以当你调用一个函数时,比如res = equalizeHist(img1,img2)在 Python 中,你传递两个 numpy 数组,并且你期望另一个numpy数组作为输出。所以这些numpy数组被转换为C++中的函数cv::Mat然后调用equalizeHist()。最终结果,res 将被转换回 Numpy 数组。简而言之,几乎所有操作都是在 C++ 中完成的,这给了我们几乎与 C++ 相同的速度。

\n
\n

这在控制循环“外部”工作得很好,但在我们的例子中则不然,在我们的例子中,两个传输成本、转换成本和任何新的或临时数据存储 RAM 分配成本都会导致我们的控制循环恶化TAT

\n

因此,请避免从 Python 端(在绑定的延迟额外里程后面)调用任何 OpenCV 原生函数,无论这些函数第一眼看起来多么诱人或甜蜜。

\n

忽视这一建议将[ms]带来相当惨痛的代价。

\n
\n

Python ?
是的,Python。使用 Python 解释器本身会带来延迟,而且会增加并发避免处理的问题,无论我们的硬件在多少个内核上运行(而最近的 Py3 尝试在解释器级软件下降低这些成本)。

\n

我们可以测试并挤出最大的(在 2022 年仍然不可避免的)GIL 锁交错 - 检查并sys.getswitchinterval()测试增加此数量以减少交错的 python 端处理(调整取决于您的其他 python 应用程序野心(GUI,任务、python 网络 I/O 工作负载、python-HW-I/O(如果适用)等)

\n
\n

RAM-内存-I/O成本?
我们的下一个主要敌人。使用MediaPipe可以使用的最不足够的图像数据格式是该领域的前进方向。

\n
\n

可避免的损失
所有其他(我们的)罪孽都属于这个部分。避免任何图像数据格式转换(参见上文,[us]仅将已获取并格式化并存储numpy.array为另一个颜色图的成本可能很容易增长到数十万)

\n

MediaPipe
列出了它可以使用的枚举格式:

\n
 // ImageFormat\n\n  SRGB: sRGB, interleaved:   one byte for R,\n                        then one byte for G,\n                        then one byte for B for each pixel.\n\n  SRGBA: sRGBA, interleaved: one byte for R,\n                             one byte for G,\n                             one byte for B,\n                             one byte for alpha or unused.\n\n  SBGRA: sBGRA, interleaved: one byte for B,\n                             one byte for G,\n                             one byte for R,\n                             one byte for alpha or unused.\n\n  GRAY8:        Grayscale,   one byte per pixel.\n\n  GRAY16:       Grayscale,   one uint16 per pixel.\n\n  SRGB48:  sRGB,interleaved, each component is a uint16.\n\n  SRGBA64: sRGBA,interleaved,each component is a uint16.\n\n  VEC32F1:                   One float per pixel.\n\n  VEC32F2:                   Two floats per pixel.\n
Run Code Online (Sandbox Code Playgroud)\n

因此,选择 MVF——最小可行格式——让手势识别发挥作用并尽可能缩小像素数量(400x600-GRAY8将是我的热门候选)

\n

预配置(不要遗漏cv.CAP_PROP_FOURCC 细节)本机端OpenCV::VideoCapture处理仅以 RAW 格式简单地将此 MVF 存储在采集和预处理链的本机端上,这样就不需要其他帖子- 进行格式化处理。

\n

如果确实被迫接触 python 端numpy.array对象,则更喜欢使用矢量化和跨步技巧驱动的操作而不是.view()-s 或.data-buffers,以避免任何不必要的附加延迟成本增加控制循环TAT

\n
\n

选项?

\n
    \n
  • 通过精确配置本机端 OpenCV 处理以匹配所需的 MediaPipe 数据格式,消除任何 Python 端调用(因为这些调用会花费您 2 倍的数据 I/O 成本 + 转换成本)

    \n
  • \n
  • 最小化,更好地避免任何阻塞,如果控制循环仍然过于倾斜,请尝试使用将原始数据移动到本地主机上或亚毫秒 LAN 域内的其他进程(不一定是 Python 解释器)(提供更多提示)这里

    \n
  • \n
  • 尝试调整热数据 RAM 占用空间以匹配您的 CPU 缓存层次结构缓存行的大小和关联性详细信息(请参阅

    \n
  • \n
\n