【问题标题】:How to I transfer an image(opencv Matrix/numpy array) from c++ publisher to python sender via ZeroMQ?如何通过 ZeroMQ 将图像(opencv 矩阵/numpy 数组)从 c++ 发布者传输到 python 发送者?
【发布时间】:2019-04-10 19:20:27
【问题描述】:

我知道如何通过 zeromq 从 c++ 向 python 发送字符串消息。

这是我知道的发送字符串消息的代码:

C++ 发件人代码:

void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB);
int bind = zmq_bind(publisher, "tcp://localhost:5563");
std::string message = "Hello from sender";
const char *message_char = message.c_str();
zmq_send(publisher, message_char, strlen(message_char), ZMQ_NOBLOCK);

Python 接收器代码:

context = zmq.Context()
receiver = context.socket(zmq.SUB)
receiver.connect("tcp://*:5563")
receiver.setsockopt_string(zmq.SUBSCRIBE, "")
message = receiver.recv_string()

我想要的是将图像从 c++ zeromq 发布者发送到 python 接收者。

【问题讨论】:

  • 也许使用std::vector<uint8_t> 而不是std::string
  • 你如何将图像存储在 c++ 中?
  • @eyllanesc 我使用库 opencv 来处理图像。它以 Matrix 对象的形式读取图像。
  • @Rohit 您应该在问题的开头写下这些内容,因为 c++ 默认情况下不提供图像处理,因此您始终需要使用外部库。 :-)
  • @eyllanesc 感谢您的指出。我已经在问题本身中声明了使用 opencv 库。

标签: python c++ zeromq pyzmq


【解决方案1】:

免责声明: 回答我自己的问题,这样别人就不会卡在我做过的地方了。

让我们开始吧。

什么是零 MQ?

ZeroMQ 是一个高性能异步消息传递库,旨在用于分布式或并发应用程序。它提供了一个消息队列,但与面向消息的中间件不同,ZeroMQ 系统可以在没有专用消息代理的情况下运行。

在此之前,我们先从这里开始:

使用的协议/库:ZeroMQ

发布者:面向 C++

订阅者:面向 Python


通过 ZeroMQ 发送字符串/字符数组消息:

C++ 发布者:-

// Setting up ZMQ context & socket variables
void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB); 
int bind = zmq_bind(publisher, "tcp://*:9000");
std::string message = "Hello from sender";
const char *message_char = message.c_str(); // Converting c++ string to char array
// Sending char array via ZMQ
zmq_send(publisher, message_char, strlen(message_char), ZMQ_NOBLOCK);

Python 订阅者:-

// Setting up ZMQ context & socket variables
context = zmq.Context()
receiver = context.socket(zmq.SUB)
receiver.connect("tcp://localhost:9000")
// Subscribing to start receiving messages
receiver.setsockopt_string(zmq.SUBSCRIBE, "")
message = receiver.recv_string()

通过 ZeroMQ 发送 Image/ndarray 数组消息:

对于处理图像,opencv 是一个很棒的库。简单、易于编码且速度极快。

C++ 发布者:-

void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB);
int bind = zmq_bind(publisher, "tcp://*:9000");

// Reading the image through opencv package
cv::Mat image = cv::imread("C:/Users/rohit/Desktop/sample.bmp", CV_LOAD_IMAGE_GRAYSCALE );
int height = image.rows;
int width = image.cols;
zmq_send(publisher, image.data, (height*width*sizeof(UINT8)), ZMQ_NOBLOCK);

在上面的代码中,图像被读取为灰度图像,您也可以通过在 opencv 的 imread 方法中传递适当的参数来读取 3 通道(RGB)图像。

还要记得相应地修改大小(zmq_send 函数调用中的第三个参数)。

Python 订阅者:-

context = zmq.Context()
receiver = context.socket(zmq.SUB)
receiver.connect("tcp://localhost:9000")
receiver.setsockopt_string(zmq.SUBSCRIBE, "")
// Receiving image in bytes
image_bytes = receiver.recv()
int width = 4096; // My image width
int height = 4096; // My image height
// Converting bytes data to ndarray
image = numpy.frombuffer(image_byte, dtype=uint8).reshape((width, height))

要做/改进: 您还可以从 c++ 发布者传递图像大小以及图像数据。 因此,该图像可以在 python 端进行相应的重塑。

ZMQ_SNDMORE 标志在这里派上用场

只需在 c++ 端添加另一个 zmq_send 语句。

zmq_send(publisher, img_height, strlen(img_height), ZMQ_SNDMORE)
zmq_send(publisher, img_width, strlen(img_width), ZMQ_SNDMORE)
zmq_send(publisher, image.data, (height*width*sizeof(UINT8)), ZMQ_NOBLOCK);

同理,在python末尾添加相应的接收语句。

height = receiver.recv_string(ZMQ_RCVMORE)
width = receiver.recv_string(ZMQ_RCVMORE)
image_bytes = receiver.recv()

另一项改进

感谢@Mark Setchell 指出改进。

直接通过网络发送大尺寸的 opencv 矩阵可能会很昂贵。 更好的方法是在通过网络发送之前对图像进行编码。

C++ 发布者:-

void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB);
int bind = zmq_bind(publisher, "tcp://*:9000");

// Reading the image through opencv package
cv::Mat image = cv::imread("C:/Users/rohit/Desktop/sample.bmp", CV_LOAD_IMAGE_GRAYSCALE );
int height = image.rows;
int width = image.cols;
cv::vector<uchar> buffer;
cv::imencode(".jpg", image, buffer);
zmq_send(publisher, buffer.data(), buffer.size(), ZMQ_NOBLOCK);

Python 订阅者:-

context = zmq.Context()
receiver = context.socket(zmq.SUB)
receiver.connect("tcp://localhost:9000")
receiver.setsockopt_string(zmq.SUBSCRIBE, "")
// Receiving image in bytes
image_bytes = receiver.recv()
// Decoding the image -- Python's PIL.Image library is used for decoding
image = numpy.array(Image.open(io.BytesIO(image_byte)))

【讨论】:

  • 发送未压缩的 16MB 图像可能会非常浪费网络带宽!也许考虑在发送之前使用cv2.imencode()在内存中转换为JPEG或PNG,它可能是200kB而不是16MB。
  • @MarkSetchell 感谢 Mark 的指出,我已经更新了代码,在 c++ 和 python 端都集成了图像编码/解码。
  • 能否提供从 python 客户端向 c++ 服务器发送图像(编码)的逆向?
  • 发送前的编码示例见this answer
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-13
  • 2012-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多