drawing_tools.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 # BSD 3-Clause License
3 
4 # Copyright (c) 2019, Noam C. Golombek
5 # All rights reserved.
6 
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
9 
10 # 1. Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 
13 # 2. Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 
17 # 3. Neither the name of the copyright holder nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 
32 
33 
34 import logging
35 import colorsys
36 import numpy as np
37 import cv2
38 
39 import rospy
40 from cnn_bridge.srv import *
41 
42 
43 class DrawingTools(object):
44  """Class for image processing"""
45 
46  def __init__(self, mode, opacity=0.6):
47  self.metadata = None
48  self.opacity = opacity
49  self.mode = mode
50 
51  def get_cnn_metadata(self, mode):
52  rospy.wait_for_service(rospy.get_name() + '/get_cnn_metadata')
53  try:
54  get_metadata = None
55  if mode == 'segmentation':
56  get_metadata = rospy.ServiceProxy(
57  rospy.get_name() + '/get_cnn_metadata', getSegmentationMetadata)
58  elif mode == 'detection':
59  get_metadata = rospy.ServiceProxy(
60  rospy.get_name() + '/get_cnn_metadata', getDetectionMetadata)
61  self.metadata = get_metadata()
62  except rospy.ServiceException, e:
63  rospy.logerr("Could not get metadata: %e", e)
64 
65  def overlay_segmentation(self, image_to_overlay, segmentation):
66  """
67  Overlay an image with a segmentation result for multiple classes.
68 
69  Parameters
70  ----------
71  image_to_show : numpy.array
72  An image of shape [width, height, 3]
73  segmentation : numpy.array
74  Segmentation of shape [width, height]
75 
76  Returns
77  -------
78  cv2.image
79  The image overlayed with the segmentation
80  """
81 
82  # Check that the image has three channels
83  assert image_to_overlay.shape[2] == 3
84  # Check that the segmentation has a single channel
85  assert segmentation.ndim == 2 or segmentation.shape[2] == 1
86 
87  # This does not happen in the __init__ because the class
88  # is initialized before the service is published
89  if self.metadata is None:
90  self.get_cnn_metadata(self.mode)
91 
92  # Squeeze the segmentation down if it is too large
93  if segmentation.ndim == 3:
94  segmentation = segmentation.squeeze(axis=2)
95 
96  # Find the unique classes in the image
97  classes = np.unique(segmentation)
98  # Create an image to overlay on
99  img = np.zeros(np.shape(image_to_overlay), dtype=np.uint8)
100 
101  # Loop the classes and create an image to overlay
102  # TODO Improve efficiency
103  for classification in self.metadata.class_metadata:
104  id = classification.classification.data
105  color = (classification.color.r,
106  classification.color.g, classification.color.b)
107  if id in classes:
108  img[segmentation == id] = color
109 
110  # Overlay the image
111  image_to_show = cv2.addWeighted(
112  image_to_overlay, self.opacity, img, 1 - self.opacity, 0)
113 
114  return image_to_show
115 
116  def draw_detection(self, image, boxes, scores, labels, detection_size,
117  font=cv2.FONT_HERSHEY_SIMPLEX):
118  """
119  :param boxes, shape of [num, 4]
120  :param scores, shape of [num, ]
121  :param labels, shape of [num, ]
122  :param image,
123  :param classes, the return list from the function `read_coco_names`
124  """
125  # This does not happen in the __init__ because the class
126  # is initialized before the service is published
127  if self.metadata is None:
128  self.get_cnn_metadata(self.mode)
129 
130  # If no boxes return original image
131  if boxes is None:
132  return image
133  a = len(self.metadata.class_names)*1.0
134  hsv_tuples = [(x / a, 0.9, 1.0)
135  for x in range(len(self.metadata.class_names))]
136  colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
137  colors = list(
138  map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), colors))
139 
140  # Loop the detections and draw the bounding boxes
141  for i in range(len(labels)):
142  # Get current data
143  bbox, score, label = boxes[i], scores[i], self.metadata.class_names[labels[i]]
144  bbox_text = "%s %.2f" % (label.data, score)
145 
146  # Parameters setup
147  text_size = cv2.getTextSize(bbox_text, font, 1, 2)
148  # Ration between detection size and image size
149  ratio = np.array((self.metadata.image_width, self.metadata.image_height),
150  dtype=float) / np.array(detection_size, dtype=float)
151  bbox = list((bbox.reshape(2, 2) * ratio).reshape(-1))
152 
153  # Draw bounding box
154  cv2.rectangle(image, (int(bbox[0]), int(bbox[1])), (int(
155  bbox[2]), int(bbox[3])), colors[labels[i]], thickness=3)
156  text_origin = bbox[:2]-np.array([0, text_size[0][1]])
157  cv2.rectangle(image, (int(text_origin[0]), int(text_origin[1])), (int(
158  text_origin[0]+text_size[0][0]), int(text_origin[1]+text_size[0][1])), colors[labels[i]], thickness=-1)
159  cv2.putText(image, bbox_text, (int(bbox[0]), int(
160  bbox[1])), font, 1, (0, 0, 0), thickness=2)
161 
162  return image
163 
164 
165 def read_coco_names(class_file_name):
166  names = {}
167  with open(class_file_name, 'r') as data:
168  for ID, name in enumerate(data):
169  names[ID] = name.strip('\n')
170  return names
def read_coco_names(class_file_name)
def draw_detection(self, image, boxes, scores, labels, detection_size, font=cv2.FONT_HERSHEY_SIMPLEX)
def __init__(self, mode, opacity=0.6)
def overlay_segmentation(self, image_to_overlay, segmentation)


cnn_bridge
Author(s): Noam C. Golombek , Alexander Beringolts
autogenerated on Mon Jun 10 2019 12:53:26