tile_image.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 import collections
5 try: # for python3.10+
6  from collections.abc import Sequence as collections_Sequence
7 except ImportError:
8  from collections import Sequence as collections_Sequence
9 import sys
10 import cv2
11 import cv_bridge
12 import jsk_recognition_utils
13 from jsk_topic_tools import ConnectionBasedTransport
14 import message_filters
15 import numpy as np
16 import rospy
17 from sensor_msgs.msg import CompressedImage
18 from sensor_msgs.msg import Image
19 import itertools, pkg_resources
20 from distutils.version import StrictVersion
21 from threading import Lock
22 
23 
24 def draw_text_box(img, text, font_scale=0.8, thickness=2,
25  color=(0, 255, 0), fg_color=(0, 0, 0), loc='ltb'):
26  font_face = cv2.FONT_HERSHEY_SIMPLEX
27  size, baseline = cv2.getTextSize(text, font_face, font_scale, thickness)
28 
29  H, W = img.shape[:2]
30 
31  if loc == 'ltb': # left + top + below
32  # pt: (x, y)
33  pt1 = (0, 0)
34  pt2 = (size[0], size[1] + baseline)
35  pt3 = (0, size[1])
36  elif loc == 'rba': # right + bottom + above
37  pt1 = (W - size[0], H - size[1] - baseline)
38  pt2 = (W, H)
39  pt3 = (W - size[0], H - baseline)
40  else:
41  raise ValueError
42  if color is not None:
43  cv2.rectangle(img, pt1, pt2, color=color, thickness=-1)
44  cv2.putText(img, text, pt3, font_face, font_scale, fg_color, thickness)
45 
46 
47 class TileImages(ConnectionBasedTransport):
48  def __init__(self):
49  super(TileImages, self).__init__()
50  self.lock = Lock()
51  self.input_topics = rospy.get_param('~input_topics', [])
52  if not self.input_topics:
53  rospy.logerr('need to specify input_topics')
54  sys.exit(1)
55  self._shape = rospy.get_param('~shape', None)
56  if self._shape:
57  if not (isinstance(self._shape, collections_Sequence) and
58  len(self._shape) == 2):
59  rospy.logerr('~shape must be a list of 2 float values.')
60  sys.exit(1)
61  if (self._shape[0] * self._shape[1]) < len(self.input_topics):
62  rospy.logerr('Tile size must be larger than # of input topics')
63  sys.exit(1)
64  self.cache_img = None
65  self.draw_topic_name = rospy.get_param('~draw_topic_name', False)
66  self.approximate_sync = rospy.get_param('~approximate_sync', True)
67  self.no_sync = rospy.get_param('~no_sync', False)
68  self.font_scale = rospy.get_param('~font_scale', 0.8)
69  if (not self.no_sync and
70  StrictVersion(pkg_resources.get_distribution('message_filters').version) < StrictVersion('1.11.4') and
71  self.approximate_sync):
72  rospy.logerr('hydro message_filters does not support approximate sync. Force to set ~approximate_sync=false')
73  self.approximate_sync = False
74  self.pub_img = self.advertise('~output', Image, queue_size=1)
75  self.pub_compressed_img = self.advertise('~output/compressed', CompressedImage, queue_size=1)
76 
77  def subscribe(self):
78  self.sub_img_list = []
79  if self.no_sync:
80  self.input_imgs = {}
81  self.sub_img_list = [rospy.Subscriber(topic, Image, self.simple_callback(topic), queue_size=1) for topic in self.input_topics]
82  rospy.Timer(rospy.Duration(0.1), self.timer_callback)
83  else:
84  queue_size = rospy.get_param('~queue_size', 10)
85  slop = rospy.get_param('~slop', 1)
86  for i, input_topic in enumerate(self.input_topics):
87  sub_img = message_filters.Subscriber(input_topic, Image)
88  self.sub_img_list.append(sub_img)
89  if self.approximate_sync:
90  sync = message_filters.ApproximateTimeSynchronizer(
91  self.sub_img_list, queue_size=queue_size, slop=slop)
92  sync.registerCallback(self._apply)
93  else:
95  self.sub_img_list, queue_size=queue_size)
96  sync.registerCallback(self._apply)
97 
98  def unsubscribe(self):
99  for sub in self.sub_img_list:
100  sub.sub.unregister()
101 
102  def timer_callback(self, event):
103  with self.lock:
104  imgs = [self.input_imgs[topic] for topic in self.input_topics
105  if topic in self.input_imgs]
106  self._append_images(imgs)
107 
108  def simple_callback(self, target_topic):
109  def callback(msg):
110  with self.lock:
111  bridge = cv_bridge.CvBridge()
112  img = bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')
113  self.input_imgs[target_topic] = img
114  if self.draw_topic_name:
115  draw_text_box(img, rospy.resolve_name(target_topic),
116  font_scale=self.font_scale)
117  return callback
118 
119  def _append_images(self, imgs):
120  if not imgs:
121  return
122  # convert tile shape: (Y, X) -> (X, Y)
123  # if None, shape is automatically decided to be square AMAP.
124  shape_xy = self._shape[::-1] if self._shape else None
125  if self.cache_img is None:
127  imgs, tile_shape=shape_xy)
128  self.cache_img = out_bgr
129  else:
130  try:
132  imgs, tile_shape=shape_xy, result_img=self.cache_img)
133  except ValueError: # cache miss
135  imgs, tile_shape=shape_xy)
136  self.cache_img = out_bgr
137 
138  encoding = 'bgr8'
139  stamp = rospy.Time.now()
140  if self.pub_img.get_num_connections() > 0:
141  bridge = cv_bridge.CvBridge()
142  imgmsg = bridge.cv2_to_imgmsg(out_bgr, encoding=encoding)
143  imgmsg.header.stamp = stamp
144  self.pub_img.publish(imgmsg)
145 
146  if self.pub_compressed_img.get_num_connections() > 0:
147  compressed_msg = CompressedImage()
148  compressed_msg.header.stamp = stamp
149  compressed_msg.format = encoding + '; jpeg compressed ' + encoding
150  compressed_msg.data = np.array(
151  cv2.imencode('.jpg', out_bgr)[1]).tostring()
152  self.pub_compressed_img.publish(compressed_msg)
153 
154  def _apply(self, *msgs):
155  if (self.pub_img.get_num_connections() == 0
156  and self.pub_compressed_img.get_num_connections() == 0):
157  return
158 
159  bridge = cv_bridge.CvBridge()
160  imgs = []
161  for msg, topic in zip(msgs, self.input_topics):
162  img = bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')
163  if self.draw_topic_name:
164  draw_text_box(img, rospy.resolve_name(topic),
165  font_scale=self.font_scale)
166  imgs.append(img)
167  self._append_images(imgs)
168 
169 
170 if __name__ == '__main__':
171  rospy.init_node('tile_image')
172  tile_image = TileImages()
173  rospy.spin()
node_scripts.tile_image.TileImages.__init__
def __init__(self)
Definition: tile_image.py:48
node_scripts.tile_image.TileImages.draw_topic_name
draw_topic_name
Definition: tile_image.py:65
node_scripts.tile_image.TileImages.sub_img_list
sub_img_list
Definition: tile_image.py:78
callback
void callback(const sensor_msgs::Image::ConstPtr &rgb_image, const sensor_msgs::Image::ConstPtr &depth_image, const sensor_msgs::CameraInfo::ConstPtr &rgb_camera_info, const sensor_msgs::CameraInfo::ConstPtr &depth_camera_info)
Definition: linemod.cpp:201
message_filters::Subscriber
node_scripts.tile_image.TileImages.subscribe
def subscribe(self)
Definition: tile_image.py:77
jsk_recognition_utils::get_tile_image
get_tile_image
node_scripts.tile_image.TileImages.cache_img
cache_img
Definition: tile_image.py:64
node_scripts.tile_image.TileImages.lock
lock
Definition: tile_image.py:50
node_scripts.tile_image.TileImages.no_sync
no_sync
Definition: tile_image.py:67
node_scripts.tile_image.TileImages.unsubscribe
def unsubscribe(self)
Definition: tile_image.py:98
node_scripts.tile_image.TileImages.pub_img
pub_img
Definition: tile_image.py:74
node_scripts.tile_image.TileImages._append_images
def _append_images(self, imgs)
Definition: tile_image.py:119
node_scripts.tile_image.TileImages.input_topics
input_topics
Definition: tile_image.py:51
node_scripts.tile_image.TileImages._apply
def _apply(self, *msgs)
Definition: tile_image.py:154
node_scripts.tile_image.TileImages._shape
_shape
Definition: tile_image.py:55
message_filters::TimeSynchronizer
node_scripts.tile_image.draw_text_box
def draw_text_box(img, text, font_scale=0.8, thickness=2, color=(0, 255, 0), fg_color=(0, 0, 0), loc='ltb')
Definition: tile_image.py:24
node_scripts.tile_image.TileImages.approximate_sync
approximate_sync
Definition: tile_image.py:66
node_scripts.tile_image.TileImages.simple_callback
def simple_callback(self, target_topic)
Definition: tile_image.py:108
node_scripts.tile_image.TileImages.font_scale
font_scale
Definition: tile_image.py:68
node_scripts.tile_image.TileImages.pub_compressed_img
pub_compressed_img
Definition: tile_image.py:75
node_scripts.tile_image.TileImages
Definition: tile_image.py:47
node_scripts.tile_image.TileImages.input_imgs
input_imgs
Definition: tile_image.py:80
node_scripts.tile_image.TileImages.timer_callback
def timer_callback(self, event)
Definition: tile_image.py:102


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