ZMQ发布/订阅,用于传输base64图像

时间:2019-12-22 15:59:30

标签: python sockets image-processing base64 zeromq

我面临一个任务:使用zmq套接字发送和接收base64字符串(从800x600图像生成)。目前,我正在使用发布/订阅连接来执行此任务。但是看起来该消息很大,以致套接字无法立即传输它,而后面的消息则停留在网络缓冲区中。尽管我不想丢失太多消息,但必须限制HWM值,以便套接字正常工作。所以我有一些问题:

  1. 是否还有另一个有效的库/方法可以执行我的任务?还是应该使用zmq提供的其他连接类型(路由器/经销商和请求/回复)?
  2. 要传输图像(由OpenCV处理),除了转换为base64格式外,我是否可以使用一种方法来最小化发送图像的大小?
  3. 如果我必须继续使用zmq pub / sub连接,如何限制存储旧消息的时间(而不是数量)(例如3分钟)?

这是我的套接字的python代码:

发布者

import numpy as np
import zmq
import base64
context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.setsockopt(zmq.SNDHWM, 1)
footage_socket.connect(<tcp address>)

def send_func(frame, camera_link):
   height, width, _ = frame.shape
   frame = np.ascontiguousarray(frame)
   base64_img = base64.b64encode(frame)
   data = {"camera_link":camera_link,'base64_img':base64_img, "img_width":width, "img_height":height}
   footage_socket.send_json(data)

订阅者

footage_socket = context.socket(zmq.SUB)
footage_socket.bind(<tcp address>)
footage_socket.setsockopt(zmq.RCVHWM, 1)
def rcv_func(): 
    while True:
        print("run socket")
        try:
            framex = footage_socket.recv_string()
            data = json.loads(framex)
            frame = data['base64_img']
            img = np.frombuffer(base64.b64decode(frame), np.uint8)
            img = img.reshape(int(frame_height), int(frame_width), 3)

        except Exception as e:
            print(e)

1 个答案:

答案 0 :(得分:1)

在开始之前,请先注意以下几点:

-如果只是为了简化编码,请避免将数据重新打包到JSON 。 JSON重新序列化的数据“增大”了大小,而没有为您提供单个增值,即可实现超快速和资源高效的流处理。仅当专业系统有足够的时间并且几乎具有无限的备用CPU处理能力时,它们才会“重组”为JSON格式,从而浪费了将宝贵的数据重新打包到另一个数据框中-在另一个数据框中。在可行的情况下,他们可以支付所有成本和效率低下的费用-在这里,您将导致花费的CPU时钟没有任何交换,使重新包装自身所需的RAM增加了一倍以上,并且必须传输更大的数据

-查看,如果相机确实提供了“应得”的图像数据,则变为 8字节 / 64-有点“深”,如果没有的话,您将拥有无与伦比的第一个非凡的图像数据缩减功能

使用sys.getsizeof()可能会让您感到惊讶:

>>> aa = np.ones( 1000 )
>>> sys.getsizeof(  aa )
8096 <---------------------------- 8096 [B] object here "contains"
>>> (lambda array2TEST: array2TEST.itemsize * array2TEST.size )( aa )
8000 <---------------------------- 8000 [B] of data
>>> bb = aa.view()     # a similar effect happen in smart VECTORISED computing
>>> sys.getsizeof( bb )
96 <------------------------------   96 [B] object here "contains"
>>> (lambda array2TEST: array2TEST.itemsize * array2TEST.size )( bb )
8000 <---------------------------- 8000 [B] of data
>>> bb.flags
  C_CONTIGUOUS    : True
  F_CONTIGUOUS    : True
  OWNDATA         : False <-------------------------------||||||||||
  WRITEABLE       : True
  ALIGNED         : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY    : False
  >>> bb.dtype
  dtype('float64') <--------------    8 [B] per image-pixel even for {1|0} B/W
  

Q 有没有一种方法?我可以使用最小化发送图像的大小 ...?

是的,已经花费了数百万的 [man * years] 研发,致力于解决这一问题,并且仍在发展一流的方法来解决这一问题。

对于极端情况,需要最佳结果,就像任何人可能已经期望的那样-从远处的深处到家中进行卫星图像传输-例如当JAXA在第二次{ {3}},这次asteroid rendezvous mission

您的原样代码可以产生800x600的图像帧,而到目前为止尚未指定fps速率和色深。简短的视图显示,如果未对流程进行更多关注和适当注意,在 -3-分钟-内可以轻松生成多少数据:

>>> (lambda a2T: a2T.itemsize * a2T.size )( np.ones( ( 800, 600, 3 ) ) ) / 1E6
11.52 <---- each 800x600-RGB-FRAME handled/processed this way takes ~ 11.5 [MB]
                    @~30 fps                                        ~345.6 [MB/s]
                                                                    ~ 62.2 [GB/3min]

解决方案?汲取同类最佳技术的启发:

您具有有限的功率(无论是在能量方面还是在处理方面-别忘了,这颗卫星内部的CPU已经制造了大约5至7年以前,在项目启动之前-没有人会敢发送带有明亮但又热门但未经验证的COTS芯片的任务,有限的RAM (同样,功率加重量限制,作为数量)每升克“有用的载重量”(“有用的载荷”)所产生的升空和飞行所需的燃料量就增加了),但最后但并非最不重要的-最大的限制因素-您的R / F-COMMs手段非常有限< / strong>-非常“ loooooooong”线(如果您尝试从“这里”应答/否应答,则需要将近半天才能从“那里”返回“这里” +相同内容) -request或在检测到错误后请求重新发送)。当前的DSN有效遥测数据传输速度约为6.4〜9.6 kbps(是的,不超过7000比特/秒)

在这里,最聪明的人将人类智慧的全部艺术都运用到了这一切中:

  • 图像的最终手段压缩-除非确实至关重要,否则绝不要发送任何内容
  • 添加了
  • 转码后图像数据的最终手段 错误自我纠正-如果值得添加任何内容,则错误检测不会(您将不得不等待几乎一天,以使其再次“重新传输”,希望那里没有其他错误)。在这里,我们需要一种方法(有限的,请参见上面发送单个比特的成本,因此这必须是非常经济的附件)自我校正,它确实可以修复有限范围的信号/数据传输错误,在R / F-COMMs信号从深空返回家乡的过程中可能会出现并确实出现。对于较大的错误,您必须等待几天才能得到重新计划的图像数据错误恢复,而另一种尝试发送较大的数据包解决了该问题,而该较大的数据包无法通过内置于设备中的功能从“损坏”的数据中恢复错误自校正。

从哪里开始?

如果您的用例没有大量的 “备用” CPU可用功率(确实需要具有足够的“免费” CPU + RAM资源来执行任何此类高级图像数据转码和错误恢复重新处理,两者均以 scale (用于转码和重新处理的附加数据-两者都很大-都比单个图像帧的大小大几个数量级),并且在时间(附加CPU的速度) -processing))没有获得最终图像数据压缩的魔术,您的故事到此结束。

如果您的用例可以提升CPU能力,那么下一个敌人就是时间。在设计合理的图像处理时间和使用工程图像数据转码处理每个图像帧的时间之前,都要在相当短的时间内将其发送到接收方。前者可以通过您的项目资源(通过财务-可以聘请合适的熟练工程师,也可以通过人们来执行(执行)实际设计和工程阶段的人员)进行管理。 )。后者是无法管理的,它取决于您的项目的需求-项目可以存活多长时间(fps)和承受什么延迟(延迟的累积多长时间[ms])才能执行预期的功能。

  • python 是一个简单的原型生态系统,一旦您需要提高吞吐量(参见上文),这很可能(30多年的经验使我对以下方面非常有信心)这样说-即使您加入附加类固醇,例如为了进行整个马戏团而进入cython + C延伸,的确确实需要一点点,但速度却要快一点,但必须付出巨大的附加成本(获得新技能(如果还没有加入,那么学习曲线的持续时间会很昂贵,并且那些熟练的技术人员的薪水会增加))。重新设计和重构目前为止原型良好的代码库)将是该节目的第一个阻碍因素< / p>

  • OpenCV 可以并且将为您提供一些基本的图像处理工具,

  • 必须遵循
  • 图像数据转码和普通或最终数据压缩,以减小数据大小

  • ZeroMQ 是问题最少的部分-既具有性能方面的可扩展性,又具有独特的低延迟吞吐量功能。没有任何细节,除非您一直防止并避免任何订阅列表处理,否则您可能会忘记 PUB/SUB (这样做的成本会给{中央节点|网络数据流+所有远程节点}-重载,对预期的快速和适当大小的图像数据管道处理没有实际影响。


  

Q 如果必须继续使用zmq pub / sub连接,如何限制存储旧消息的时间而不是3分钟,而不是数量? / em>

ZeroMQ是一种智能工具,但您必须了解它的功能-ZeroCopy将帮助您保持低RAM配置文件在生产中,但是如果您计划存储-3-分钟的图像数据流,< / strong>,您将需要大量的RAM和CPU能力,而这也很大程度上取决于 .recv() -ing对等方的实际数量。

ZeroMQ是一个无代理的系统,因此您实际上并没有“存储”消息,但是.send()方法只是告诉ZeroMQ基础架构,无论何时,提供的数据都是可以免费发送的。 ZeroMQ基础设施正在看到将它们分发给指定的对等收件人的机会(无论是本地的还是大西洋的,或通过卫星连接的)。这意味着,如果您打算让发送/接收端准备好入队/发送/接收/出队〜3分钟甚至最压缩的图像数据,则必须正确配置ZeroMQ。流,如果出现1:多方通信出现在生产中,则可能提供该流的倍数。

考虑到CPU,RAM和传输方式的先验限制,正确的分析和合理的设计决策是您项目满足所有这些要求的唯一机会。