Как я могу опубликовать двоичный файл изображения PIL через ROS без OpenCV?

В настоящее время я пытаюсь написать установку издателя/подписчика ROS, которая передает двоичный файл изображения, открытый PIL. Я бы не хотел использовать OpenCV из-за операционных ограничений, и мне было интересно, есть ли способ сделать это. Это мой текущий код:

#!/usr/bin/env python
import rospy
from PIL import Image
from sensor_msgs.msg import Image as sensorImage
from rospy.numpy_msg import numpy_msg
import numpy

def talker():
    pub = rospy.Publisher('image_stream', numpy_msg(sensorImage), queue_size=10)
    rospy.init_node('image_publisher', anonymous=False)
    rate = rospy.Rate(0.5)
    while not rospy.is_shutdown():
        im = numpy.array(Image.open('test.jpg'))
        pub.publish(im)
        rate.sleep()

if __name__ == '__main__'
    try:
        talker()
    except ROSInterruptException:
        pass

который при попытке pub.publish(im) выдает:

TypeError: Invalid number of arguments, args should be ['header', 'height', 'width', 'encoding', 'is_bigendian', 'step', 'data'] args are (array([[[***array data here***]]], dtype=uint8),)

Как мне преобразовать изображение в правильную форму, или есть метод преобразования/другой тип сообщения, который поддерживает только отправку необработанных двоичных файлов через соединение ROS?

Спасибо


person magicusingjunta    schedule 15.10.2020    source источник


Ответы (2)


Действительно, ответ Марка Сетчелла работает отлично (в этом примере игнорируется альфа-канал):

#!/usr/bin/env python
import rospy
import urllib2  # for downloading an example image
from PIL import Image
from sensor_msgs.msg import Image as SensorImage
import numpy as np

if __name__ == '__main__':
    pub = rospy.Publisher('/image', SensorImage, queue_size=10)

    rospy.init_node('image_publisher')

    im = Image.open(urllib2.urlopen('https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png'))
    im = im.convert('RGB')

    msg = SensorImage()
    msg.header.stamp = rospy.Time.now()
    msg.height = im.height
    msg.width = im.width
    msg.encoding = "rgb8"
    msg.is_bigendian = False
    msg.step = 3 * im.width
    msg.data = np.array(im).tobytes()
    pub.publish(msg)
person F1iX    schedule 23.10.2020
comment
Ааа, круто - отлично! Спасибо, что поделились более полным способом сделать это. - person Mark Setchell; 23.10.2020
comment
Испытаю ли я свою удачу, если спрошу, есть ли способ написать что-то на Python, ожидая получения этого сообщения и сохраняя его в файле, когда оно приходит? Я имею в виду только на моем Mac — у меня нет брокера, концентратора или чего-то еще, на котором работает ROS. - person Mark Setchell; 23.10.2020
comment
Я не уверен, правильно ли я понял ваш вопрос... у вас есть система, работающая под управлением ROS и публикующая там изображение, и теперь вы хотите подписаться на нее и сохранить ее на том же компьютере или, по крайней мере, в той же сети? Это был бы просто обычный подписчик ROS, слушающий тему (в моем примере /image)... (На нескольких машинах в одной сети просто укажите один и тот же ROS_MASTER_URI) - person F1iX; 23.10.2020
comment
Нет, я не задавал исходный вопрос, я просто дал ответ с некоторыми идеями о том, как отправить изображение в ROS. У меня нет аппаратного обеспечения/системы ROS, и я понятия не имею о ROS. Мне просто интересно, могу ли я запустить небольшой скрипт Python на моем Mac, ожидая получения изображения, которое нам удалось отправить. Это все. Это может быть глупый вопрос. Мне может понадобиться главный узел ROS и специальные кабели, но я ничего не знаю о ROS. Спасибо, если вы знаете, как я могу получить изображение, которое мы отправили. - person Mark Setchell; 23.10.2020
comment
@ F1iX спасибо за решение, теперь оно публикуется правильно. как мне прочитать np.array(im).tobytes() обратно в другое изображение pil на другой стороне? Я попытался использовать Image.open(msg.data), но он выдал несколько ошибок, предположительно из-за преобразования np.array().tobytes(). - person magicusingjunta; 26.10.2020
comment
@MarkSetchell, используйте библиотеку rospy и напишите подписчика, который сидит и ждет сообщений, он использует функцию обратного вызова при получении сообщения для обработки. - person magicusingjunta; 26.10.2020

Я ничего не знаю о ROS, но я часто использую PIL, поэтому, если кто-то знает лучше, отправьте мне запрос, и я удалю этот ответ наилучшее предположение.

Итак, кажется, вам нужно сделать что-то вроде это из PIL Image. Итак, вам нужно:

  • 'заголовок',
  • 'высота',
  • 'ширина',
  • 'кодирование',
  • 'is_bigendian',
  • 'шаг',
  • 'данные'

Итак, если вы сделаете это:

im = Image.open('test.jpg')

вы должны иметь возможность использовать:

  • что-то, что вам нужно, чтобы поработать
  • im.height из PIL Image
  • im.width из PIL Image
  • наверное const std::string RGB8 = "rgb8"
  • вероятно, не имеет значения, потому что данные 8-битные
  • вероятно im.width * 3 так как это 3 байта на пиксель RGB
  • np.array(im).tobytes()

Прежде чем кто-либо пометит этот ответ, никто не говорил, что ответы должны быть полными — они могут быть надеюсь полезными!

Обратите внимание, что если ваше входное изображение имеет формат PNG, вы должны проверить im.mode и, если это "P" (т.е. режим палитры), немедленно запустить:

im = im.convert('RGB')

чтобы убедиться, что это 3-канальный RGB.

Обратите внимание, что если ваше входное изображение имеет формат PNG и содержит альфа-канал, вы должны изменить encoding на "rgba8" и установить step = im.width * 4.

person Mark Setchell    schedule 15.10.2020
comment
Найден код для OpenCV-ROS Bridge, в котором могут быть полезные идеи... github.com/ros-perception/vision_opencv/blob/noetic/cv_bridge/ - person Mark Setchell; 15.10.2020