cv::imshow 通过 SSH x11 太慢了。备择方案?

cv::imshow over SSH x11 too slow. alternatives?

我有一个应用程序处理来自相机的实时帧,然后使用 imshow 输出结果帧。当 运行 在本地机器上运行时一切顺利。

但是,我需要通过 SSH 调用此应用程序并且仍能以某种方式看到视频(不需要通过 ssh)。目前该应用程序仅适用于 X11 转发,但帧速率非常糟糕并且存在巨大延迟。

有没有更好的方法可以最大限度地减少滞后和延迟?

我尝试了几种方法来提高 OpenCV imshow()ssh 连接上的性能。我使用 Mac 作为我的本地桌面,使用 Raspberry Pi4 作为我 ssh 进入的远程机器,它正在生成要在我的 Mac 上显示的图像。

ssh 连接上禁用压缩

我尝试使用:

ssh -o 'Compression no' pi4

这导致了大约 6-8% 的吞吐量提高,因此这可能还不够。

使用更轻量级的加密密码

我读到 arcfour 使用更少的资源并允许您实现更高的吞吐量。不幸的是,我试过了,它在 Raspbian 上不可用,似乎也没有任何其他轻量级密码。我使用此命令检查 Raspberry Pi:

上的可用密码
ssh -Q cipher

运行 X11 在 ssh 隧道外显示

由于找不到提高 ssh 吞吐量的方法,我决定发送未加密的 X11,而不是通过 ssh 隧道。这意味着我 运行 Mac 上的以下内容允许 X 服务器侦听直接连接并告诉它允许远程主机使用它:

defaults write org.macosforge.xquartz.X11 nolisten_tcp -bool false
xhost +

然后我 ssh 使用 ssh -Xssh -Y 和 运行 进入 Raspberry Pi 而没有 :

export DISPLAY=<MAC_IP_ADDRESS>:0
./opencvScript

这带来了更好的性能,但仍然不是开创性的。


然后我决定使用 Redis,这是一个非常高性能的内存数据结构服务器。在 Mac 或任何其他机器上安装都很简单。它允许您在网络中任意数量的客户端之间共享原子整数、字符串、列表、散列、队列、集合和有序集合。因此,我只是简单地进行 JPEG 编码并将每个视频帧从 Raspberry Pi 发送到 Redis,然后尽可能快地在我的 Mac 上拾取帧并显示它们。它工作起来如丝般顺畅,并在大约 3 秒内完成 256 帧 640x480 彩色视频,即 80 fps。

所以这是 DisplayServer 我 运行 在我的 Mac 上。它只是尽可能快地抓取最新图像并显示它:

#!/usr/bin/env python3

import ImageTransferService
import numpy as np
import cv2

if __name__ == "__main__":

    host = '0.0.0.0'
    src = ImageTransferService.ImageTransferService(host)

    # Check Redis is running 
    print(src.ping())

    while True:
        im = src.receiveImage()
        cv2.imshow('Image',im)
        cv2.waitKey(1)

这是我在 Raspberry Pi 上 运行 发送图片的代码:

#!/usr/local/bin/python3

import numpy as np
import ImageTransferService

if __name__ == "__main__":

    host = '192.168.0.8'
    RemoteDisplay = ImageTransferService.ImageTransferService(host)

    # Check remote display is up
    print(RemoteDisplay.ping())

    # Create BGR image
    w, h = 640, 480
    im = np.zeros((h,w,3),dtype=np.uint8)

    for c in range(256):
        im[:,:,0] = c
        RemoteDisplay.sendImage(im)

下面是隐藏 Redis 并提供发送、接收和缓冲图像的方法的粘合代码:

#!/usr/bin/env python3

import redis
import cv2
import numpy as np

class ImageTransferService:
    def __init__(self, host='localhost', port=6379):
        self.port = port
        self.host = host
        self.conn = redis.Redis(host,port)
        self.frameNum = 0

    def ping(self):
        return self.conn.ping()

    def sendImage(self,im, name='latest', Q=75):
        _, JPEG = cv2.imencode(".JPG", im, [int(cv2.IMWRITE_JPEG_QUALITY), Q])
        myDict = { 'frameNum': self.frameNum, 'Data':JPEG.tobytes() }
        self.conn.hmset(name, myDict)
        self.frameNum += 1

    def receiveImage(self,name='latest'):
        myDict  = self.conn.hgetall(name)
        Data = myDict.get(b'Data')
        im = cv2.imdecode(np.frombuffer(Data,dtype=np.uint8), cv2.IMREAD_COLOR)
        return im

由于 Redis 有很多绑定,您可以访问数据帧并查看 Redis 或从 PHP、Python、C/C++、PHP 甚至命令行。因此,下面的行,当 shell 中的 运行 时,将抓取视频的最新帧,例如:

redis-cli hgetall latest

这是一段代码的小视频,它只是将蓝色通道从 0 增加到 255 以产生 255 帧。如您所见,它从 Raspberry Pi(在较低的终端 window 中)到 Mac(在较高的终端 window 运行 中显示服务器代码) 大约 3 秒。


我还做了一个使用套接字传输 JPEG 编码图像的版本,速度几乎一样快,但有点丑陋,而且灵活性和无缓冲性也较差,也不能从外部控制或调试就像 Redis 一样。

关键字: Raspberry Pi, OpenCV, display, remote display, X11, tunnel, ssh, DISPLAY, Redis, buffer, image, image processing, performance, XQuartz , 听, 传入, 连接。