Skip to content

Commit 2c6c04d

Browse files
committed
Added cras_bag_tools plugin. Fixed a few C/Python bugs.
1 parent 52e398e commit 2c6c04d

File tree

6 files changed

+197
-54
lines changed

6 files changed

+197
-54
lines changed

include/robot_model_renderer/c_api.h

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <stdbool.h>
1313
#include <stddef.h>
14+
#include <stdint.h>
1415

1516
#ifdef __cplusplus
1617
extern "C"
@@ -145,39 +146,39 @@ struct robot_model_renderer_RobotModelRendererConfig
145146

146147
struct ros_Time
147148
{
148-
unsigned int sec;
149-
unsigned int nsec;
149+
uint32_t sec;
150+
uint32_t nsec;
150151
};
151152

152153
struct std_msgs_Header
153154
{
154-
unsigned int seq;
155+
uint32_t seq;
155156
ros_Time stamp;
156157
const char* frame_id;
157158
};
158159

159160
struct sensor_msgs_RegionOfInterest
160161
{
161-
unsigned int x_offset;
162-
unsigned int y_offset;
163-
unsigned int height;
164-
unsigned int width;
165-
bool do_rectify;
162+
uint32_t x_offset;
163+
uint32_t y_offset;
164+
uint32_t height;
165+
uint32_t width;
166+
uint8_t do_rectify;
166167
};
167168

168169
struct sensor_msgs_CameraInfo
169170
{
170171
std_msgs_Header header;
171-
unsigned int height;
172-
unsigned int width;
172+
uint32_t height;
173+
uint32_t width;
173174
const char* distortion_model;
174175
size_t D_count;
175176
double* D;
176177
double K[9];
177178
double R[9];
178179
double P[12];
179-
unsigned int binning_x;
180-
unsigned int binning_y;
180+
uint32_t binning_x;
181+
uint32_t binning_y;
181182
sensor_msgs_RegionOfInterest roi;
182183
};
183184

package.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ SPDX-FileCopyrightText: Czech Technical University in Prague
4747
<build_export_depend>libopencv-dev</build_export_depend>
4848

4949
<exec_depend>assimp</exec_depend>
50+
<exec_depend>cras_bag_tools</exec_depend>
51+
<exec_depend>cras_py_common</exec_depend>
5052
<exec_depend>cv_bridge</exec_depend>
5153
<exec_depend>glut</exec_depend>
5254
<exec_depend>image_transport</exec_depend>
@@ -71,6 +73,7 @@ SPDX-FileCopyrightText: Czech Technical University in Prague
7173
<doc_depend>rosdoc_lite</doc_depend>
7274

7375
<export>
76+
<cras_bag_tools filters="robot_model_renderer.cras_bag_tools_plugin" />
7477
<nodelet plugin="${prefix}/nodelet.xml" />
7578
<rosdoc config="rosdoc.yaml"/>
7679
</export>
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
# SPDX-FileCopyrightText: Czech Technical University in Prague
3+
4+
import copy
5+
import os
6+
import sys
7+
8+
import rospy
9+
from cras import image_encodings
10+
from cras_bag_tools import DeserializedMessageFilter
11+
from cras_bag_tools.message_filter import fix_connection_header
12+
from sensor_msgs.msg import Image
13+
from robot_model_renderer import RobotModelRenderer, RobotModelRendererConfig, RenderingMode
14+
15+
16+
class RenderCameraMask(DeserializedMessageFilter):
17+
def __init__(self, camera_info_topic, mask_topic=None, description_param=None, description_file=None,
18+
encoding=image_encodings.MONO8, tf_timeout=1.5,
19+
min_stamp=None, max_stamp=None, include_time_ranges=None, exclude_time_ranges=None, **kwargs):
20+
super(RenderCameraMask, self).__init__(
21+
include_topics=[camera_info_topic, "/tf", "/tf_static"], min_stamp=min_stamp, max_stamp=max_stamp,
22+
include_time_ranges=include_time_ranges, exclude_time_ranges=exclude_time_ranges)
23+
24+
self.camera_info_topic = camera_info_topic
25+
self.mask_topic = mask_topic
26+
if mask_topic is None:
27+
self.mask_topic = camera_info_topic.replace("/camera_info", "/image_raw/mask")
28+
self.tf_timeout = rospy.Duration(tf_timeout)
29+
self.description_param = description_param
30+
31+
self._model_from_file = None
32+
if description_file is not None:
33+
if os.path.exists(description_file):
34+
try:
35+
with open(description_file, "r") as f:
36+
self._model_from_file = f.read()
37+
print("Loaded URDF model from file " + description_file)
38+
except Exception as e:
39+
print('Could not read URDF model from file %s: %s' % (description_file, str(e)), file=sys.stderr)
40+
else:
41+
print('URDF model file "%s" does not exist.' % (description_file,), file=sys.stderr)
42+
43+
self._config = RobotModelRendererConfig()
44+
self._config.renderingMode = RenderingMode.MASK
45+
self._config.drawOutline = True
46+
self._config.outlineColor = [1.0, 1.0, 1.0, 1.0]
47+
self._config.outlineWidth = 10.0
48+
self._config.renderImageScale = 0.25
49+
self._config.maxRenderImageSize = 2048
50+
for k, v in kwargs.items():
51+
setattr(self._config, k, v)
52+
self._encoding = encoding
53+
54+
self.renderer = None
55+
self._failed_msgs = []
56+
57+
def set_bag(self, bag):
58+
super(RenderCameraMask, self).set_bag(bag)
59+
60+
model = self._model_from_file
61+
if model is None:
62+
if self.description_param is None:
63+
self.description_param = "robot_description"
64+
model = self._get_param(self.description_param)
65+
if model is not None:
66+
print("Read URDF model from ROS parameter " + self.description_param)
67+
68+
if model is None:
69+
raise RuntimeError("RenderCameraMask requires robot URDF either as a file or ROS parameter.")
70+
71+
self.renderer = RobotModelRenderer(model, self._config, self._encoding)
72+
73+
def filter(self, topic, msg, stamp, header):
74+
if self.renderer is None:
75+
return None
76+
77+
to_try = []
78+
for m, s, h, e in self._failed_msgs:
79+
if stamp - s < self.tf_timeout:
80+
to_try.append((m, s, h))
81+
else:
82+
print("Failed transforming robot for mask rendering at time %s: %r" % (m.header.stamp, e),
83+
file=sys.stderr)
84+
self._failed_msgs = []
85+
86+
if topic.lstrip('/') == 'tf':
87+
for transform in msg.transforms:
88+
self.renderer.setTransform(transform, "robot_model_renderer.cras_bag_tools_plugin", False)
89+
elif topic.lstrip('/') == 'tf_static':
90+
for transform in msg.transforms:
91+
self.renderer.setTransform(transform, "robot_model_renderer.cras_bag_tools_plugin", True)
92+
else:
93+
to_try.append((msg, stamp, header))
94+
result = [(topic, msg, stamp, header)]
95+
96+
for msg, stamp, header in to_try:
97+
img, err, link_err = self.renderer.render(msg)
98+
99+
may_succeed_later = any([e.maySucceedLater for e in link_err]) if link_err is not None else False
100+
if may_succeed_later:
101+
self._failed_msgs.append((msg, stamp, header, err))
102+
elif img is not None:
103+
header = fix_connection_header(copy.copy(header), self.mask_topic, Image._type, Image._md5sum, Image)
104+
result.append((self.mask_topic, img, stamp, header))
105+
else:
106+
print("Failed rendering robot mask for time %s: %r" % (msg.header.stamp, err), file=sys.stderr)
107+
108+
return result
109+
110+
def reset(self):
111+
self.renderer = None
112+
self._failed_msgs = []
113+
super(RenderCameraMask, self).reset()
114+
115+
def _str_params(self):
116+
parts = [
117+
'camera_info_topic=' + self.camera_info_topic,
118+
'mask_topic=' + self.mask_topic
119+
]
120+
if self.description_param != "robot_description":
121+
parts.append('description_param=' + self.description_param)
122+
parent_params = super(RenderCameraMask, self)._str_params()
123+
if len(parent_params) > 0:
124+
parts.append(parent_params)
125+
return ", ".join(parts)

src/robot_model_renderer/msg.py

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# SPDX-License-Identifier: BSD-3-Clause
22
# SPDX-FileCopyrightText: Czech Technical University in Prague
3-
4-
from ctypes import c_bool, c_char_p, c_double, c_size_t, c_uint, c_uint32
3+
import ctypes
4+
from ctypes import c_bool, c_char_p, c_double, c_size_t, c_uint, c_uint32, c_uint8
55
from ctypes import cast, POINTER, Structure
66
import sys
77

8+
import genpy
89
import rospy
910
from geometry_msgs.msg import Transform, TransformStamped
1011
from sensor_msgs.msg import CameraInfo, RegionOfInterest
@@ -29,7 +30,7 @@ def __init__(self, secs=0, nsecs=0):
2930
if isinstance(secs, _RosTime):
3031
c = secs
3132
super(RosTime, self).__init__(c.sec, c.nsec)
32-
elif isinstance(secs, rospy.Time):
33+
elif isinstance(secs, (rospy.Time, genpy.rostime.Time)):
3334
c = secs
3435
super(RosTime, self).__init__(c.secs, c.nsecs)
3536
else:
@@ -55,7 +56,7 @@ class std_msgs_Header(Header):
5556
def __init__(self, header=None, *args, **kwargs):
5657
if header is not None and isinstance(header, _std_msgs_Header):
5758
super(std_msgs_Header, self).__init__(header.seq, RosTime(header.stamp), _decode(header.frame_id))
58-
if header is not None and isinstance(header, Header):
59+
if header is not None:
5960
super(std_msgs_Header, self).__init__(header.seq, RosTime(header.stamp), header.frame_id)
6061
else:
6162
super(std_msgs_Header, self).__init__(*args, **kwargs)
@@ -65,23 +66,24 @@ def from_param(cls, obj):
6566
c = _std_msgs_Header()
6667
c.seq = obj.seq
6768
c.stamp = RosTime.from_param(obj.stamp)
68-
c.frame_id = obj.frame_id.encode("utf-8")
69+
obj._frame_id = obj.frame_id.encode("utf-8")
70+
c.frame_id = obj._frame_id
6971
return c
7072

7173

7274
class _sensor_msgs_RegionOfInterest(Structure):
7375
_fields_ = [
74-
("x_offset", c_uint),
75-
("y_offset", c_uint),
76-
("height", c_uint),
77-
("width", c_uint),
78-
("do_rectify", c_bool),
76+
("x_offset", c_uint32),
77+
("y_offset", c_uint32),
78+
("height", c_uint32),
79+
("width", c_uint32),
80+
("do_rectify", c_uint8),
7981
]
8082

8183

8284
class sensor_msgs_RegionOfInterest(RegionOfInterest):
8385
def __init__(self, roi=None, *args, **kwargs):
84-
if roi is not None and isinstance(roi, (_sensor_msgs_RegionOfInterest, RegionOfInterest)):
86+
if roi is not None:
8587
super(sensor_msgs_RegionOfInterest, self).__init__(
8688
roi.x_offset, roi.y_offset, roi.height, roi.width, roi.do_rectify)
8789
else:
@@ -101,16 +103,16 @@ def from_param(cls, obj):
101103
class _sensor_msgs_CameraInfo(Structure):
102104
_fields_ = [
103105
("header", _std_msgs_Header),
104-
("height", c_uint),
105-
("width", c_uint),
106+
("height", c_uint32),
107+
("width", c_uint32),
106108
("distortion_model", c_char_p),
107109
("D_count", c_size_t),
108110
("D", POINTER(c_double)),
109111
("K", c_double * 9),
110112
("R", c_double * 9),
111113
("P", c_double * 12),
112-
("binning_x", c_uint),
113-
("binning_y", c_uint),
114+
("binning_x", c_uint32),
115+
("binning_y", c_uint32),
114116
("roi", _sensor_msgs_RegionOfInterest),
115117
]
116118

@@ -119,17 +121,14 @@ class sensor_msgs_CameraInfo(CameraInfo):
119121
def __init__(self, camInfo=None, *args, **kwargs):
120122
if camInfo is not None and isinstance(camInfo, _sensor_msgs_CameraInfo):
121123
super(sensor_msgs_CameraInfo, self).__init__(
122-
std_msgs_Header(camInfo.header), camInfo.height, camInfo.width, _decode(camInfo.distortion_model),
123-
list(cast(camInfo.D, POINTER(c_double))), camInfo.K, camInfo.R, camInfo.P,
124-
camInfo.binning_x, camInfo.binning_y, sensor_msgs_RegionOfInterest(camInfo.roi))
125-
elif camInfo is not None and isinstance(camInfo, CameraInfo):
124+
std_msgs_Header(header=camInfo.header), camInfo.height, camInfo.width,
125+
_decode(camInfo.distortion_model), list(cast(camInfo.D, POINTER(c_double))), camInfo.K, camInfo.R,
126+
camInfo.P, camInfo.binning_x, camInfo.binning_y, sensor_msgs_RegionOfInterest(roi=camInfo.roi))
127+
elif camInfo is not None:
126128
super(sensor_msgs_CameraInfo, self).__init__(
127-
std_msgs_Header(camInfo.header), camInfo.height, camInfo.width, camInfo.distortion_model,
129+
std_msgs_Header(header=camInfo.header), camInfo.height, camInfo.width, camInfo.distortion_model,
128130
camInfo.D, camInfo.K, camInfo.R, camInfo.P, camInfo.binning_x, camInfo.binning_y,
129-
sensor_msgs_RegionOfInterest(camInfo.roi))
130-
elif camInfo is not None and isinstance(camInfo, CameraInfo):
131-
for f in CameraInfo.__slots__:
132-
setattr(self, f, getattr(camInfo, f))
131+
sensor_msgs_RegionOfInterest(roi=camInfo.roi))
133132
else:
134133
super(sensor_msgs_CameraInfo, self).__init__(*args, **kwargs)
135134

@@ -139,9 +138,11 @@ def from_param(cls, obj):
139138
c.header = std_msgs_Header.from_param(obj.header)
140139
c.height = obj.height
141140
c.width = obj.width
142-
c.distortion_model = obj.distortion_model.encode('utf-8')
141+
obj._distortion_model = obj.distortion_model.encode('utf-8')
142+
c.distortion_model = obj._distortion_model
143143
c.D_count = len(obj.D)
144-
c.D = cast((c_double * len(obj.D))(*obj.D), POINTER(c_double))
144+
obj._D = cast((c_double * len(obj.D))(*obj.D), POINTER(c_double))
145+
c.D = obj._D
145146
c.K = (c_double * 9)(*obj.K)
146147
c.R = (c_double * 9)(*obj.R)
147148
c.P = (c_double * 12)(*obj.P)
@@ -169,8 +170,15 @@ def __init__(self, tf=None, *args, **kwargs):
169170
self.rotation.y = tf.rotation[1]
170171
self.rotation.z = tf.rotation[2]
171172
self.rotation.w = tf.rotation[3]
172-
elif tf is not None and isinstance(tf, Transform):
173-
super(geometry_msgs_Transform, self).__init__(tf.translation, tf.rotation)
173+
elif tf is not None:
174+
super(geometry_msgs_Transform, self).__init__()
175+
self.translation.x = tf.translation.x
176+
self.translation.y = tf.translation.y
177+
self.translation.z = tf.translation.z
178+
self.rotation.x = tf.rotation.x
179+
self.rotation.y = tf.rotation.y
180+
self.rotation.z = tf.rotation.z
181+
self.rotation.w = tf.rotation.w
174182
else:
175183
super(geometry_msgs_Transform, self).__init__(*args, **kwargs)
176184

@@ -194,22 +202,23 @@ class geometry_msgs_TransformStamped(TransformStamped):
194202
def __init__(self, tf=None, *args, **kwargs):
195203
if tf is not None and isinstance(tf, _geometry_msgs_TransformStamped):
196204
super(geometry_msgs_TransformStamped, self).__init__()
197-
self.header = std_msgs_Header(tf.header)
205+
self.header = std_msgs_Header(header=tf.header)
198206
self.child_frame_id = _decode(tf.child_frame_id)
199-
self.transform = geometry_msgs_Transform(tf.transform)
200-
if tf is not None and isinstance(tf, TransformStamped):
207+
self.transform = geometry_msgs_Transform(tf=tf.transform)
208+
elif tf is not None:
201209
super(geometry_msgs_TransformStamped, self).__init__()
202-
self.header = std_msgs_Header(tf.header)
210+
self.header = std_msgs_Header(header=tf.header)
203211
self.child_frame_id = tf.child_frame_id
204-
self.transform = geometry_msgs_Transform(tf.transform)
212+
self.transform = geometry_msgs_Transform(tf=tf.transform)
205213
else:
206214
super(geometry_msgs_TransformStamped, self).__init__(*args, **kwargs)
207215

208216
@classmethod
209217
def from_param(cls, obj):
210218
c = _geometry_msgs_TransformStamped()
211219
c.header = std_msgs_Header.from_param(obj.header)
212-
c.child_frame_id = obj.child_frame_id.encode('utf-8')
220+
obj._child_frame_id = obj.child_frame_id.encode('utf-8')
221+
c.child_frame_id = obj._child_frame_id
213222
c.transform = geometry_msgs_Transform.from_param(obj.transform)
214223
return c
215224

0 commit comments

Comments
 (0)