Как передать изображение (матрица opencv / массив numpy) от издателя c ++ отправителю python через ZeroMQ? - PullRequest
0 голосов
/ 10 апреля 2019

Я знаю, как отправить строковое сообщение из c ++ в python через zeromq.

Вот код для отправки известного мне строкового сообщения:

Код отправителя 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.

1 Ответ

1 голос
/ 10 апреля 2019

Отказ от ответственности: Отвечая на мой вопрос, чтобы другие не застряли там, где я.

Итак, начнем.

Что такое ноль MQ?

ZeroMQ - это высокопроизводительная библиотека асинхронных сообщений, предназначенная для использования в распределенных или параллельных приложениях. Он обеспечивает очередь сообщений, но в отличие от промежуточного программного обеспечения, ориентированного на сообщения, система ZeroMQ может работать без выделенного посредника сообщений.

Прежде, чем мы начнем, вот основы:

Используемый протокол / библиотека: ZeroMQ

Издатель: C ++ ориентированный

подписчик: Python-ориентированный


Отправка сообщения массива String / char через 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()

Отправка сообщения изображения / массива ndarray через ZeroMQ:

Для работы с изображениями 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);

В приведенном выше коде изображение читается как изображение в градациях серого, вы можете также прочитать 3-канальное (RGB) изображение, передав соответствующие параметры в методе imc opencv.

Также не забудьте изменить размер (третий параметр в вызове функции 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 ++ вместе с данными изображения. Таким образом, это изображение может быть соответствующим образом изменено на стороне питона.

Здесь пригодится флаг ZMQ_SNDMORE

Просто добавьте еще один оператор zmq_send на стороне c ++.

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 Matrix большого размера напрямую по сети может быть дорогостоящей. Лучшим подходом было бы закодировать изображение перед отправкой по сети.

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)))
...