image_publisher.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 import os, sys
4 from threading import Lock
5 
6 import cv2
7 import numpy as np
8 
9 import cv_bridge
10 from cv_bridge.boost.cv_bridge_boost import getCvType
11 import dynamic_reconfigure.server
12 import rospy
13 
14 from jsk_perception.cfg import ImagePublisherConfig
15 from sensor_msgs.msg import CameraInfo
16 from sensor_msgs.msg import Image, CompressedImage
17 
18 import re
19 from PIL import Image as PIL_Image
20 from PIL import ExifTags as PIL_ExifTags
21 from jsk_recognition_msgs.msg import ExifTags, ExifGPSInfo
22 import imghdr
23 
24 from jsk_recognition_utils.depth import depth_to_compressed_depth
25 
26 
28 
29  def __init__(self):
30  self.lock = Lock()
31  self.imgmsg = None
32  self.encoding = rospy.get_param('~encoding', 'bgr8')
33  self.frame_id = rospy.get_param('~frame_id', 'camera')
34  self.fovx = rospy.get_param('~fovx', None)
35  self.fovy = rospy.get_param('~fovy', None)
36  self.publish_exif = rospy.get_param('~publish_exif', False)
37  if (self.fovx is None) != (self.fovy is None):
38  rospy.logwarn('fovx and fovy should be specified, but '
39  'specified only {}'
40  .format('fovx' if self.fovx else 'fovy'))
41  dynamic_reconfigure.server.Server(
42  ImagePublisherConfig, self._cb_dyn_reconfig)
43  self.pub = rospy.Publisher('~output', Image, queue_size=1)
44  self.pub_compressed = rospy.Publisher('{}/compressed'.format(rospy.resolve_name('~output')), CompressedImage, queue_size=1)
45  if self.encoding == '32FC1':
46  self.pub_compressed = rospy.Publisher(
47  '{}/compressedDepth'.format(rospy.resolve_name('~output')),
48  CompressedImage, queue_size=1)
49  self.publish_info = rospy.get_param('~publish_info', True)
50  if self.publish_info:
51  self.pub_info = rospy.Publisher(
52  '~output/camera_info', CameraInfo, queue_size=1)
53  if self.publish_exif:
54  self.pub_exif = rospy.Publisher('~exif', ExifTags, queue_size=1)
55  rate = rospy.get_param('~rate', 1.)
56  rospy.Timer(rospy.Duration(1. / rate), self.publish)
57 
58  def _update_exif_data(self, data, tag, value, get = PIL_ExifTags.TAGS.get):
59  def encode_value(value, attr, data):
60  attr_type = type(attr)
61  value_type = type(value)
62  if attr_type == list and value_type == tuple:
63  for i in range(len(value)):
64  attr[i] = encode_value(value[i], attr[i], data)
65  return attr
66  else:
67  if attr_type == str and value_type == unicode:
68  # if type is unicode, conver to string
69  value = value.encode('ascii', 'ignore')
70  if attr_type == int and value_type == str:
71  # binary string to uint8
72  value = map(ord, value)[0]
73  elif attr_type == float and value_type == tuple:
74  # float tuple to tuple
75  value = float('{}.{}'.format(value[0], value[1]))
76  return value
77 
78  string_tag = get(tag, tag)
79  if tag == 0x927c:
80  pass # skip MakerNote
81  elif type(string_tag) == str:
82  decoded_tag = re.sub('([a-z])([A-Z])', '\\1_\\2', string_tag)
83  decoded_tag = re.sub('([a-zA-Z])([0-9])', '\\1_\\2', decoded_tag)
84  decoded_tag = re.sub('([XYZ])Resolution', '\\1_resolution', decoded_tag)
85  decoded_tag = re.sub('([F])Number', '\\1_number', decoded_tag)
86  decoded_tag = re.sub('(ISO)', '\\1_', decoded_tag)
87  decoded_tag = re.sub('(GPS)', '\\1_', decoded_tag)
88  decoded_tag = decoded_tag.lower()
89  if decoded_tag == 'gps_info':
90  gps_data = ExifGPSInfo()
91  for t in value:
92  self._update_exif_data(gps_data, t, value[t], get=PIL_ExifTags.GPSTAGS.get)
93  data.gps_info = gps_data
94  else:
95  try:
96  setattr(data, decoded_tag, encode_value(value, getattr(data, decoded_tag), data))
97  except AttributeError as e:
98  rospy.logwarn("Undefiend exif tags [{:x} {}({}) {}]".format(tag, string_tag, decoded_tag, value))
99  except Exception as e:
100  rospy.logerr("Wrong exif information {} [{:x} {}({}) {}]".format(e, tag, string_tag, decoded_tag, value))
101  else:
102  rospy.logwarn("Unknown exif tags [{:x} {} {}]".format(tag, string_tag, value))
103  return data
104 
105  def _get_exif_data(self, file_name):
106  exif_data =ExifTags()
107  self.info = PIL_Image.open(file_name)._getexif()
108  if self.info:
109  for tag, value in self.info.items():
110  self._update_exif_data(exif_data, tag, value)
111  return exif_data
112 
113  def _cb_dyn_reconfig(self, config, level):
114  file_name = config['file_name']
115  config['file_name'] = os.path.abspath(file_name)
116  self.dst_format = imghdr.what(file_name)
117  img = cv2.imread(file_name, cv2.IMREAD_UNCHANGED)
118  if img is None:
119  rospy.logwarn('Could not read image file: {}'.format(file_name))
120  with self.lock:
121  self.imgmsg = None
122  else:
123  rospy.loginfo('Read the image file: {}'.format(file_name))
124  # when file is gray scale but encoding is not grayscale,
125  # load the image again in color
126  if (len(img.shape) == 2 and
127  getCvType(self.encoding) not in [
128  cv2.CV_8UC1, cv2.CV_16UC1, cv2.CV_32FC1]):
129  img = cv2.imread(file_name, cv2.IMREAD_COLOR)
130  # read exif
131  if self.publish_exif:
132  self.exif_data = self._get_exif_data(file_name)
133  with self.lock:
134  self.imgmsg, self.compmsg = \
135  self.cv2_to_imgmsg(img, self.encoding)
136  return config
137 
138  def publish(self, event):
139  if self.imgmsg is None:
140  return
141  now = rospy.Time.now()
142  # setup ros message and publish
143  with self.lock:
144  self.imgmsg.header.stamp = \
145  self.compmsg.header.stamp = now
146  self.imgmsg.header.frame_id = \
147  self.compmsg.header.frame_id = self.frame_id
148  if self.pub.get_num_connections() > 0:
149  self.pub.publish(self.imgmsg)
150  if self.pub_compressed.get_num_connections() > 0:
151  self.pub_compressed.publish(self.compmsg)
152  if self.publish_info:
153  info = CameraInfo()
154  info.header.stamp = now
155  info.header.frame_id = self.frame_id
156  info.width = self.imgmsg.width
157  info.height = self.imgmsg.height
158  if self.fovx is not None and self.fovy is not None:
159  fx = self.imgmsg.width / 2.0 / \
160  np.tan(np.deg2rad(self.fovx / 2.0))
161  fy = self.imgmsg.height / 2.0 / \
162  np.tan(np.deg2rad(self.fovy / 2.0))
163  cx = self.imgmsg.width / 2.0
164  cy = self.imgmsg.height / 2.0
165  info.K = np.array([fx, 0, cx,
166  0, fy, cy,
167  0, 0, 1.0])
168  info.P = np.array([fx, 0, cx, 0,
169  0, fy, cy, 0,
170  0, 0, 1, 0])
171  info.R = [1, 0, 0, 0, 1, 0, 0, 0, 1]
172  self.pub_info.publish(info)
173  if self.publish_exif:
174  self.pub_exif.publish(self.exif_data)
175 
176  def cv2_to_imgmsg(self, img, encoding):
177  bridge = cv_bridge.CvBridge()
178  # resolve encoding
179  cv_type = getCvType(encoding)
180  dst_format = self.dst_format
181  if cv_type in [cv2.CV_8UC1, cv2.CV_16UC1, cv2.CV_32FC1]:
182  # mono8
183  if len(img.shape) == 3:
184  if img.shape[2] == 4:
185  code = cv2.COLOR_BGRA2GRAY
186  else:
187  code = cv2.COLOR_BGR2GRAY
188  img = cv2.cvtColor(img, code)
189  if cv_type == cv2.CV_16UC1:
190  # 16UC1
191  # png compression on 16-bit images
192  dst_format = 'png'
193  img = img.astype(np.float32)
194  img = np.clip(img / 255.0 * (2 ** 16 - 1), 0, 2 ** 16 - 1)
195  img = img.astype(np.uint16)
196  elif cv_type == cv2.CV_32FC1:
197  # 32FC1
198  img = img.astype(np.float32)
199  img /= 255.0
200  comp_depth_msg = depth_to_compressed_depth(img)
201  return bridge.cv2_to_imgmsg(img, encoding=encoding), comp_depth_msg
202  comp_img = img
203  target_format = encoding
204  elif cv_type == cv2.CV_8UC3 and len(img.shape) == 3:
205  # 8UC3
206  # BGRA, BGR -> BGR
207  img = comp_img = img[:, :, :3]
208  target_format = 'bgr8'
209  # BGR -> RGB
210  if encoding == 'rgb8':
211  img = img[:, :, ::-1]
212  elif cv_type == cv2.CV_16UC3 and len(img.shape) == 3:
213  # png compression on 16-bit images
214  dst_format = 'png'
215  # 16UC3
216  # BGRA, BGR -> BGR
217  img = img[:, :, :3]
218  # convert to 16UC3 image.
219  img = img.astype(np.float32)
220  img = np.clip(img / 255.0 * (2 ** 16 - 1), 0, 2 ** 16 - 1)
221  img = comp_img = img.astype(np.uint16)
222  target_format = 'bgr16'
223  # BGR -> RGB
224  if encoding == 'rgb16':
225  img = img[:, :, ::-1]
226  elif (cv_type == cv2.CV_8UC4 and
227  len(img.shape) == 3 and img.shape[2] == 4):
228  comp_img = img[:, :, :3]
229  target_format = 'bgr8'
230  # 8UC4
231  if encoding == 'rgba8':
232  # BGRA -> RGBA
233  img = img[:, :, [2, 1, 0, 3]]
234  elif (cv_type == cv2.CV_16UC4 and
235  len(img.shape) == 3 and img.shape[2] == 4):
236  # convert to 16UC4 image.
237  img = img.astype(np.float32)
238  img = np.clip(img / 255.0 * (2 ** 16 - 1), 0, 2 ** 16 - 1)
239  img = img.astype(np.uint16)
240  comp_img = img[:, :, :3]
241  target_format = 'bgr16'
242  if encoding == 'rgba16':
243  # BGRA -> RGBA
244  img = img[:, :, [2, 1, 0, 3]]
245  else:
246  rospy.logerr('unsupported encoding: {0}'.format(encoding))
247  return
248  compmsg = bridge.cv2_to_compressed_imgmsg(comp_img, dst_format=dst_format)
249  # compressed format is separated by ';'.
250  # https://github.com/ros-perception/image_transport_plugins/blob/f0afd122ed9a66ff3362dc7937e6d465e3c3ccf7/compressed_image_transport/src/compressed_publisher.cpp#L116-L128
251  compmsg.format = '{}; {} compressed {}'.format(
252  encoding, dst_format, target_format)
253  return bridge.cv2_to_imgmsg(img, encoding=encoding), compmsg
254 
255 
256 
257 if __name__ == '__main__':
258  rospy.init_node('image_publisher')
260  rospy.spin()
object
node_scripts.image_publisher.ImagePublisher.dst_format
dst_format
Definition: image_publisher.py:116
ssd_train_dataset.float
float
Definition: ssd_train_dataset.py:180
node_scripts.image_publisher.ImagePublisher.pub
pub
Definition: image_publisher.py:43
node_scripts.image_publisher.ImagePublisher.pub_compressed
pub_compressed
Definition: image_publisher.py:44
node_scripts.image_publisher.ImagePublisher.lock
lock
Definition: image_publisher.py:30
node_scripts.image_publisher.ImagePublisher._cb_dyn_reconfig
def _cb_dyn_reconfig(self, config, level)
Definition: image_publisher.py:113
node_scripts.image_publisher.ImagePublisher.pub_exif
pub_exif
Definition: image_publisher.py:54
node_scripts.image_publisher.ImagePublisher.publish_info
publish_info
Definition: image_publisher.py:49
node_scripts.image_publisher.ImagePublisher
Definition: image_publisher.py:27
node_scripts.image_publisher.ImagePublisher.frame_id
frame_id
Definition: image_publisher.py:33
node_scripts.image_publisher.ImagePublisher.publish
def publish(self, event)
Definition: image_publisher.py:138
node_scripts.image_publisher.ImagePublisher.imgmsg
imgmsg
Definition: image_publisher.py:31
node_scripts.image_publisher.ImagePublisher.compmsg
compmsg
Definition: image_publisher.py:134
node_scripts.image_publisher.ImagePublisher.fovy
fovy
Definition: image_publisher.py:35
node_scripts.image_publisher.ImagePublisher.info
info
Definition: image_publisher.py:107
node_scripts.image_publisher.ImagePublisher.cv2_to_imgmsg
def cv2_to_imgmsg(self, img, encoding)
Definition: image_publisher.py:176
node_scripts.image_publisher.ImagePublisher.publish_exif
publish_exif
Definition: image_publisher.py:36
node_scripts.image_publisher.ImagePublisher._get_exif_data
def _get_exif_data(self, file_name)
Definition: image_publisher.py:105
node_scripts.image_publisher.ImagePublisher.__init__
def __init__(self)
Definition: image_publisher.py:29
node_scripts.image_publisher.ImagePublisher.encoding
encoding
Definition: image_publisher.py:32
jsk_recognition_utils::depth
node_scripts.image_publisher.ImagePublisher.exif_data
exif_data
Definition: image_publisher.py:132
node_scripts.image_publisher.ImagePublisher._update_exif_data
def _update_exif_data(self, data, tag, value, get=PIL_ExifTags.TAGS.get)
Definition: image_publisher.py:58
node_scripts.image_publisher.ImagePublisher.fovx
fovx
Definition: image_publisher.py:34
node_scripts.image_publisher.ImagePublisher.pub_info
pub_info
Definition: image_publisher.py:51


jsk_perception
Author(s): Manabu Saito, Ryohei Ueda
autogenerated on Fri May 16 2025 03:11:17