如何在 C++ 中使用 ZeroMQ 通信多个图像?

Posted

技术标签:

【中文标题】如何在 C++ 中使用 ZeroMQ 通信多个图像?【英文标题】:How to communicate multiple images using ZeroMQ in C++? 【发布时间】:2019-10-24 02:39:15 【问题描述】:

我想实现可以通过 ZeroMQ 通信多个图像的程序。 我可以使用 OpenCV 在 C++ 上显示多个图像,但我无法以某种方式在 Python 上显示,尽管编译器没有输出错误。

如何将多个图像从 C++ 传递到 Python?

C++ 代码:

    Mat img0;
    Mat img1;
    Mat img2;

    img0 = imread("images.jpeg");
    img1 = imread("images1.jpeg");
    img2 = imread("image2.jpg");

    if(img0.empty()) return -1;
    if(img1.empty()) return -1;
    if(img2.empty()) return -1;
    ShowManyImages("IMAGE",3,img0,img1,img2);

// Let structure for zeroMQ
    int32_t info[3];

    info[0] = (int32_t)img0.rows;
    info[1] = (int32_t)img0.cols;
    info[2] = (int32_t)img0.type();

    info[3] = (int32_t)img1.rows;
    info[4] = (int32_t)img1.cols;
    info[5] = (int32_t)img1.type();

    info[6] = (int32_t)img2.rows;
    info[7] = (int32_t)img2.cols;
    info[8] = (int32_t)img2.type();


    zmq::context_t context (1);
    zmq::socket_t socket (context, ZMQ_REQ);
    socket.connect ("tcp://localhost:5555");

    for(i=0; i<9; i++ )
    
        zmq::message_t msg0 ( (void*)&info[i], sizeof(int32_t), NULL  );
        socket.send(msg0, ZMQ_SNDMORE);
    


    void* data0 = malloc(img0.total() * img0.elemSize());
    memcpy(data0, img0.data, img0.total() * img0.elemSize());

    zmq::message_t msg00(data0, img0.total() * img0.elemSize(), my_free, NULL);
    socket.send(msg00);


    void* data1 = malloc(img1.total() * img1.elemSize());
    memcpy(data1, img1.data, img1.total() * img1.elemSize());

    zmq::message_t msg11(data1, img1.total() * img1.elemSize(), my_free, NULL);
    socket.send(msg11);

    void* data2 = malloc(img2.total() * img2.elemSize());
    memcpy(data2, img2.data, img2.total() * img2.elemSize());

    zmq::message_t msg22(data2, img2.total() * img2.elemSize(), my_free, NULL);
    socket.send(msg22);

Python 代码:

import zmq
import cv2
import struct
import numpy as np

# Connection String
conn_str      = "tcp://*:5555"

# Open ZMQ Connection
ctx = zmq.Context()
sock = ctx.socket(zmq.REP)
sock.bind(conn_str)


while(True):
# Receve Data from C++ Program
    byte_rows, byte_cols, byte_mat_type, data=  sock.recv_multipart()

# Convert byte to integer
    rows = struct.unpack('i', byte_rows)
    cols = struct.unpack('i', byte_cols)
    mat_type = struct.unpack('i', byte_mat_type)

    if mat_type[0] == 0:
    # Gray Scale
        image = np.frombuffer(data, dtype=np.uint8).reshape((rows[0],cols[0]))
    else:
    # BGR Color
        image = np.frombuffer(data, dtype=np.uint8).reshape((rows[0],cols[0],3))

    cv2.imshow('Python',image)
    cv2.waitKey()

此致,

【问题讨论】:

您能否发布您从已发布的 C++/python 分布式计算代码模型中在 python 端收到的确切值? 我从REQ修改为XREQ。然后,我只能看到三个显示img0的窗口。但我想观看显示每张图片的每个窗口,例如img0img1img2。我认为这是sock.recv_multipart() 发生的,因为c++ 程序发送每个图像信息,我将Python 程序从byte_rows, byte_cols, byte_mat_type, data= sock.recv_multipart() 恢复到byte_rows0, byte_cols0, byte_mat_type0, byte_rows1, byte_cols1,byte_mat_type1, byte_rows2, byte_cols2, byte_mat_type2, data= sock.recv_multipart()。我该如何解决这个问题? 【参考方案1】:

如何将多个图像从 C++ 传递到 Python?

上面的代码使用 REQ/REP Scalable Formal Communication Pattern Archetype,但它不会尝试发送和 REP-ly 到任何已经交付的 REQ-est.

如果不尝试 .send()REP-ly,REQ-方将永远无法发送任何下一条消息。

如果这是您第一次使用 ZeroMQ, 在深入了解更多细节之前,您可以先看看“ZeroMQ Principles in less than Five Seconds


解决方案?

尽量使用 PUSH/PULL 原型,不需要响应。

接下来,尝试决定,仅使用第一张图像发送所有元数据是否是一个相当可靠的假设(我宁愿编写,最好使用符合struct.pack()/struct.unpack() 的 BLOB 和每个人中的所有byte_rows, byte_cols, byte_mat_type, data,单个-frame,微不足道的消息负载映射)。

Python 端只会一步读取单帧 BLOB,可能会测试签名,如果需要,.unpack() 前导几个字节来学习图像大小,.unpack() 图像的“其余部分”-数据,你就完成了。这不是很可爱吗?

另外请注意,并非所有平台都共享相同类型的 Endian-ness(因此最好将您的 c 端代码设计为在“网络”中显式字节排序 -Endian 排序 :o)只是为了安全如果托管设备/平台进一步发展,下一次可能会变成不同的字节序。显式排序就是相同的显式排序,因为您的代码从第一天开始就坚持使用)


我明白了。所以你说我必须使用 PUSH/PULL 对吗?但我将我的程序从 C++ 中的 ZMQ_REQ 修改为 ZMQ_XREQ 并在 python 中从 zmq.REP 修改为 zmq.XREQ。然后,我可以在 python 中观看三个窗口,但只显示 img0。我知道这是因为 sock.recv_multipart() 而发生的。但我不知道如何修改它。我还应该使用 PUSH/PULL 来实现吗? – user36327 7 小时前

不,我没有告诉任何人必须使用PUSH/PULL,这是一个人的建议,他从 v2.1+ 开始就使用 ZeroMQ

XREQ/XREP 有效地短路了理论上无限链 REQ.send()s-REP.recv()s-REP.send()s-REQ.recv 的 dFSA 步进()s-REQ...-REP...-REP...-REQ...-REQ...-...-[... ad-infinitum-or-deadlock, 以两者为准第一个 :o) ]

问题在于期望事情一定会发生,而这实际上不需要像预期的那样发生:

MASK        = 'Python: The Image#0: was decoded to be 1:'
order_ORDER = 0
while( True ):  # THIS WILL RUN FOREVER, WON'T IT?
    #####################################################################################
    order_ORDER += 1
    #####################################################################################
    ( byte_rows,                                  # EXPECTS ALL THIS TO .recv() EACH TIME
      byte_cols,                                  # EXPECTS ALL THIS TO .recv() EACH TIME
      byte_mat_type,                              # EXPECTS ALL THIS TO .recv() EACH TIME
      data                                        # EXPECTS ALL THIS TO .recv() EACH TIME
      )           =  sock.recv_multipart()        # in Data from C++ Program
    #####################################################################################
    rows     = struct.unpack( 'i', byte_rows)     # EXPECTS THIS TO BE CORRECT  EACH TIME
    cols     = struct.unpack( 'i', byte_cols)     # EXPECTS THIS TO BE CORRECT  EACH TIME
    mat_type = struct.unpack( 'i', byte_mat_type) # EXPECTS THIS TO BE CORRECT  EACH TIME
    #####################################################################################
    if mat_type[0] == 0:                          # IF mat_type[0] WAS == 0 ?in 2nd, 3rd?
        # # # # # # # # # # # # # # # Gray Scale: # IF mat_type[0] WAS == 0 ?in 2nd, 3rd?
        image = np.frombuffer( data,
                               dtype = np.uint8
                               ).reshape( ( rows[0],
                                            cols[0]
                                            )
                                          )
        print "IMAGE WAS of TYPE == ZERO, HAVING SHAPE OF: ", image.shape, image.dtype
        imgType = 'Grayscale'
    else:                                         # IF mat_type[0] WAS != 0 ?in 2nd, 3rd?
        # # # # # # # # # # # # # # # # BGR Color # IF mat_type[0] WAS != 0 ?in 2nd, 3rd?
        image = np.frombuffer( data,
                               dtype = np.uint8
                               ).reshape( ( rows[0],
                                            cols[0],
                                            3
                                            )
                                          )
        print "IMAGE WAS of TYPE != ZERO, HAVING SHAPE OF: ", image.shape, image.dtype
        imgType = 'Blue-Green-Red'
    ###################################################################################
    cv2.imshow( MASK.format( imgType, order_ORDER ), image )
    cv2.waitKey()
    ###################################################################################
    #   LOOP AGAIN
    ###################################################################################

【讨论】:

我明白了。所以你说我必须使用PUSH/PULL 对吗?但我在 c++ 中将程序从 ZMQ_REQ 修改为 ZMQ_XREQ,在 python 中从 zmq.REP 修改为 zmq.XREQ。然后,我可以在 python 中观看三个窗口,但只显示img0。我知道这是因为sock.recv_multipart() 而发生的。但我不知道如何修改它。我还应该使用PUSH/PULL 来实现吗?【参考方案2】:

我发现了几个问题。

    您正在创建一个大小为 3 个整数的数组,然后将 9 个整数存储到其中 int32_t info[3];

    您发送和接收的消息部分不匹配。

    您按此顺序发送(末尾有 3 个数据) byte_rows, byte_cols, byte_mat_type, byte_rows, byte_cols, byte_mat_type,byte_rows, byte_cols, byte_mat_type, data, data, data 您按此顺序接收(每个元部分之后的数据) byte_rows, byte_cols, byte_mat_type, data, byte_rows, byte_cols, byte_mat_type, data, byte_rows, byte_cols, byte_mat_type, data

【讨论】:

以上是关于如何在 C++ 中使用 ZeroMQ 通信多个图像?的主要内容,如果未能解决你的问题,请参考以下文章

C++跨平台:ZeroMQ的简单示例

ZeroMQ C++案例

如何使用 ZeroMQ 从 C# 客户端向 C++ 服务器发送消息

如何在同一个进程中操作多个 ZeroMQ Socket 类型?

使用ZeroMQ(clrzmq)实现异步通信

如何将来自不同图像的多个描述符存储在 C++ 和 OpenCV 中的向量向量中?