繁体   English   中英

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

[英]Lower latency from webcam cv2.VideoCapture

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

问题是延迟。 我用手机的高速摄像头记录了自己的抓拍,发现我的手和屏幕上的帧之间的延迟约为 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

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

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

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

欢迎来到延迟战争(剃须)

您上面描述的经验是一个很好的例子,累积的延迟如何破坏任何保持控制回路足够紧密、确实控制有意义的稳定的机会,就像我们希望保持的人机接口系统一样:

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

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

在此处输入图像描述


我们使用哪些导致延迟的步骤?

暂时原谅我们累积每个特定延迟相关成本的原始草图:


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


我们/我们可以在这里对抗什么延迟?

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

已经优化了延迟的工具箱的软件延迟是可能的,但越来越难

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


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

... So when you call a function, say res = equalizeHist(img1,img2) in Python, you pass two numpy arrays and you expect another numpy array as the output. So these numpy arrays are converted to cv::Mat and then calls the equalizeHist() function in C++. 最终结果, res 将被转换回 Numpy 数组。 所以简而言之,几乎所有的操作都在 C++ 中完成,这给了我们几乎与 C++ 相同的速度。

这在控制回路“外部”工作得很好,在我们的情况下,这两种运输成本、转换成本和任何新的或临时数据存储 RAM 分配成本都会导致我们的控制回路TAT恶化.

因此,请避免从 Python(在绑定的延迟额外英里之后)调用任何和所有 OpenCV 原生函数,无论这些函数乍一看多么诱人或甜蜜。

忽略这个建议是一个相当[ms]的代价。


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

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


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


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

媒体管道
列出了它可以使用的枚举格式:

 // ImageFormat

  SRGB: sRGB, interleaved:   one byte for R,
                        then one byte for G,
                        then one byte for B for each pixel.

  SRGBA: sRGBA, interleaved: one byte for R,
                             one byte for G,
                             one byte for B,
                             one byte for alpha or unused.

  SBGRA: sBGRA, interleaved: one byte for B,
                             one byte for G,
                             one byte for R,
                             one byte for alpha or unused.

  GRAY8:        Grayscale,   one byte per pixel.

  GRAY16:       Grayscale,   one uint16 per pixel.

  SRGB48:  sRGB,interleaved, each component is a uint16.

  SRGBA64: sRGBA,interleaved,each component is a uint16.

  VEC32F1:                   One float per pixel.

  VEC32F2:                   Two floats per pixel.

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

预配置(不缺少cv.CAP_PROP_FOURCC 详细信息)本机端OpenCV::VideoCapture处理,仅在采集-&-预处理的本机端以 RAW 格式简单存储此 MVF链,因此不会发生其他后处理格式化。

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


选项?

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

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

  • 尝试适应热数据 RAM 占用空间以匹配您的 CPU-Cache Hierarchy 缓存行的大小和关联性详细信息(请参阅

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM