4 import dynamic_reconfigure.server
5 import geometry_msgs.msg
6 import jsk_recognition_msgs.msg
10 import shapely.geometry
11 from jsk_recognition_utils.cfg
import PolygonArrayToBoxArrayConfig
12 from jsk_topic_tools
import ConnectionBasedTransport
13 from tf.transformations
import quaternion_from_matrix
as matrix2quaternion
14 from tf.transformations
import unit_vector
as normalize_vector
18 return np.array([[0, -v[2], v[1]],
29 from distutils.version
import LooseVersion
30 if LooseVersion(shapely.__version__) < LooseVersion(
'1.6.0'):
32 from itertools
import islice
33 from shapely.affinity
import affine_transform
36 """Returns the general minimum bounding rectangle of
37 the geometry. Can possibly be rotated. If the convex hull
38 of the object is a degenerate (line or point) this same degenerate
42 hull = polygon.convex_hull
44 coords = hull.exterior.coords
45 except AttributeError:
48 edges = ((pt2[0]-pt1[0], pt2[1]-pt1[1])
for pt1, pt2
in zip(coords, islice(coords, 1,
None)))
50 def _transformed_rects():
53 length = math.sqrt(dx**2 + dy**2)
54 ux, uy = dx/length, dy/length
59 transf_rect = affine_transform(hull, (ux,uy,vx,vy,0,0)).envelope
62 yield (transf_rect, (ux,vx,uy,vy,0,0))
65 transf_rect, inv_matrix = min(_transformed_rects(), key=
lambda r : r[0].area)
66 return affine_transform(transf_rect, inv_matrix)
67 shapely.geometry.Polygon.minimum_rotated_rectangle = minimum_rotated_rectangle
70 first_axis=(1, 0, 0), second_axis=(0, 1, 0), axes=
'xy'):
71 if axes
not in [
'xy',
'yx',
'xz',
'zx',
'yz',
'zy']:
72 raise ValueError(
"Valid axes are 'xy', 'yx', 'xz', 'zx', 'yz', 'zy'.")
73 e1 = normalize_vector(first_axis)
74 e2 = normalize_vector(second_axis - np.dot(second_axis, e1) * e1)
75 if axes
in [
'xy',
'zx',
'yz']:
79 e3 = normalize_vector(
80 third_axis - np.dot(third_axis, e1) * e1 - np.dot(third_axis, e2) * e2)
81 first_index = ord(axes[0]) - ord(
'x')
82 second_index = ord(axes[1]) - ord(
'x')
83 third_index = ((first_index + 1) ^ (second_index + 1)) - 1
84 indices = [first_index, second_index, third_index]
85 return np.vstack([e1, e2, e3])[np.argsort(indices)].T
91 v1 = normalize_vector(v1)
92 v2 = normalize_vector(v2)
94 return np.arccos(np.clip(dot
if directed
else np.fabs(dot), -1.0, 1.0))
99 points = points[
None, :]
101 a = normalize_vector(a)
102 b = normalize_vector(b)
104 if np.isclose(angle, 0.0):
105 k = np.array([0, 0, 0],
'f')
107 k = normalize_vector(np.cross(a, b))
109 points_rot = points * np.cos(theta) \
110 + np.cross(k, points) * np.sin(theta) \
111 + k * np.dot(k, points.T).reshape(-1, 1) * (1 - np.cos(theta))
118 super(PolygonArrayToRectBoxArray, self).
__init__()
119 dynamic_reconfigure.server.Server(
120 PolygonArrayToBoxArrayConfig,
124 jsk_recognition_msgs.msg.PolygonArray,
127 '~output/coefficients',
128 jsk_recognition_msgs.msg.ModelCoefficientsArray,
132 jsk_recognition_msgs.msg.BoundingBoxArray,
140 queue_size = rospy.get_param(
'~queue_size', 10)
143 jsk_recognition_msgs.msg.PolygonArray,
146 '~input/coefficients',
147 jsk_recognition_msgs.msg.ModelCoefficientsArray,
149 self.
subs = [sub_polygon, sub_coefficients]
150 if rospy.get_param(
'~approximate_sync',
False):
151 slop = rospy.get_param(
'~slop', 0.1)
152 sync = message_filters.ApproximateTimeSynchronizer(
153 fs=self.
subs, queue_size=queue_size, slop=slop)
156 fs=self.
subs, queue_size=queue_size)
157 sync.registerCallback(self.
_cb)
163 def _cb(self, polygons_msg, coeffs_msg):
164 new_polygons_msg = jsk_recognition_msgs.msg.PolygonArray()
165 new_polygons_msg.header = polygons_msg.header
166 boxes_msg = jsk_recognition_msgs.msg.BoundingBoxArray(
167 header=polygons_msg.header)
168 base_normal = [0, 0, 1]
169 for coeff, polygon
in zip(
170 coeffs_msg.coefficients,
171 polygons_msg.polygons):
172 a, b, c, d = coeff.values
174 [[point.x, point.y, point.z]
175 for point
in polygon.polygon.points],
184 shapely_polygon = shapely.geometry.Polygon(
186 rect = shapely_polygon.minimum_rotated_rectangle
187 x, y = rect.exterior.xy
188 rect_polygon_2d = - np.ones((5, 3),
'f') * d
189 rect_polygon_2d[:, 0] = x
190 rect_polygon_2d[:, 1] = y
197 polygon = geometry_msgs.msg.PolygonStamped(
198 header=polygon.header)
199 for x, y, z
in rect_polygon:
200 polygon.polygon.points.append(
201 geometry_msgs.msg.Point32(x, y, z))
202 new_polygons_msg.polygons.append(polygon)
204 center_x, center_y, center_z = rect_polygon[:4].mean(axis=0)
206 box_msg = jsk_recognition_msgs.msg.BoundingBox(
207 header=polygon.header)
208 base_matrix = np.eye(4)
212 rect_polygon[1] - rect_polygon[0], normal,
'xz')
213 base_matrix[:3, :3] = matrix
214 q_x, q_y, q_z, q_w = matrix2quaternion(base_matrix)
215 dim_x = np.linalg.norm(rect_polygon[1] - rect_polygon[0])
216 dim_y = np.linalg.norm(rect_polygon[2] - rect_polygon[1])
217 box_msg.pose.position.x = center_x
218 box_msg.pose.position.y = center_y
219 box_msg.pose.position.z = center_z
220 box_msg.pose.orientation.w = q_w
221 box_msg.pose.orientation.x = q_x
222 box_msg.pose.orientation.y = q_y
223 box_msg.pose.orientation.z = q_z
224 box_msg.dimensions.x = dim_x
225 box_msg.dimensions.y = dim_y
227 boxes_msg.boxes.append(box_msg)
233 if __name__ ==
'__main__':
234 rospy.init_node(
'polygon_array_to_box_array')