q3dviewer 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- q3dviewer/__init__.py +5 -0
- q3dviewer/base_glwidget.py +256 -0
- q3dviewer/base_item.py +57 -0
- q3dviewer/custom_items/__init__.py +9 -0
- q3dviewer/custom_items/axis_item.py +148 -0
- q3dviewer/custom_items/cloud_io_item.py +79 -0
- q3dviewer/custom_items/cloud_item.py +314 -0
- q3dviewer/custom_items/frame_item.py +194 -0
- q3dviewer/custom_items/gaussian_item.py +254 -0
- q3dviewer/custom_items/grid_item.py +88 -0
- q3dviewer/custom_items/image_item.py +172 -0
- q3dviewer/custom_items/line_item.py +120 -0
- q3dviewer/custom_items/text_item.py +63 -0
- q3dviewer/gau_io.py +0 -0
- q3dviewer/glwidget.py +131 -0
- q3dviewer/shaders/cloud_frag.glsl +28 -0
- q3dviewer/shaders/cloud_vert.glsl +72 -0
- q3dviewer/shaders/gau_frag.glsl +42 -0
- q3dviewer/shaders/gau_prep.glsl +249 -0
- q3dviewer/shaders/gau_vert.glsl +77 -0
- q3dviewer/shaders/sort_by_key.glsl +56 -0
- q3dviewer/tools/__init__.py +1 -0
- q3dviewer/tools/cloud_viewer.py +123 -0
- q3dviewer/tools/example_viewer.py +47 -0
- q3dviewer/tools/gaussian_viewer.py +60 -0
- q3dviewer/tools/lidar_calib.py +294 -0
- q3dviewer/tools/lidar_cam_calib.py +314 -0
- q3dviewer/tools/ros_viewer.py +85 -0
- q3dviewer/utils/__init__.py +4 -0
- q3dviewer/utils/cloud_io.py +323 -0
- q3dviewer/utils/convert_ros_msg.py +49 -0
- q3dviewer/utils/gl_helper.py +40 -0
- q3dviewer/utils/maths.py +168 -0
- q3dviewer/utils/range_slider.py +86 -0
- q3dviewer/viewer.py +58 -0
- q3dviewer-1.0.5.dist-info/LICENSE +21 -0
- {q3dviewer-1.0.3.dist-info → q3dviewer-1.0.5.dist-info}/METADATA +7 -4
- q3dviewer-1.0.5.dist-info/RECORD +41 -0
- q3dviewer-1.0.5.dist-info/top_level.txt +1 -0
- q3dviewer-1.0.3.dist-info/RECORD +0 -5
- q3dviewer-1.0.3.dist-info/top_level.txt +0 -1
- {q3dviewer-1.0.3.dist-info → q3dviewer-1.0.5.dist-info}/WHEEL +0 -0
- {q3dviewer-1.0.3.dist-info → q3dviewer-1.0.5.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
|
|
5
|
+
Distributed under MIT license. See LICENSE for more information.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from sensor_msgs.msg import PointCloud2, Image, CameraInfo
|
|
9
|
+
import rospy
|
|
10
|
+
import numpy as np
|
|
11
|
+
import q3dviewer as q3d
|
|
12
|
+
from PySide6.QtWidgets import QLabel, QLineEdit, QDoubleSpinBox, \
|
|
13
|
+
QSpinBox, QWidget, QVBoxLayout, QHBoxLayout, QCheckBox
|
|
14
|
+
from PySide6 import QtCore
|
|
15
|
+
import rospy
|
|
16
|
+
import cv2
|
|
17
|
+
import argparse
|
|
18
|
+
from q3dviewer.utils.convert_ros_msg import convert_pointcloud2_msg, convert_image_msg
|
|
19
|
+
|
|
20
|
+
clouds = []
|
|
21
|
+
remap_info = None
|
|
22
|
+
K = None
|
|
23
|
+
cloud_accum_color = None
|
|
24
|
+
cloud_accum = None
|
|
25
|
+
|
|
26
|
+
class CustomDoubleSpinBox(QDoubleSpinBox):
|
|
27
|
+
def __init__(self, decimals=4, *args, **kwargs):
|
|
28
|
+
self.decimals = decimals
|
|
29
|
+
super().__init__(*args, **kwargs)
|
|
30
|
+
self.setDecimals(self.decimals) # Set the number of decimal places
|
|
31
|
+
|
|
32
|
+
def textFromValue(self, value):
|
|
33
|
+
return f"{value:.{self.decimals}f}"
|
|
34
|
+
|
|
35
|
+
def valueFromText(self, text):
|
|
36
|
+
return float(text)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ViewerWithPanel(q3d.Viewer):
|
|
40
|
+
def __init__(self, **kwargs):
|
|
41
|
+
# b: camera body frame
|
|
42
|
+
# c: camera image frame
|
|
43
|
+
# l: lidar frame
|
|
44
|
+
self.Rbl = np.eye(3)
|
|
45
|
+
self.Rbl = q3d.euler_to_matrix(np.array([0.0, 0.0, 0.0]))
|
|
46
|
+
self.Rcb = np.array([[0, -1, 0],
|
|
47
|
+
[0, 0, -1],
|
|
48
|
+
[1, 0, 0]])
|
|
49
|
+
self.tbl = np.array([0.0, 0.0, 0.0])
|
|
50
|
+
self.tcl = self.Rcb @ self.tbl
|
|
51
|
+
self.Rcl = self.Rcb @ self.Rbl
|
|
52
|
+
self.psize = 2
|
|
53
|
+
self.cloud_num = 1
|
|
54
|
+
self.en_rgb = False
|
|
55
|
+
super().__init__(**kwargs)
|
|
56
|
+
|
|
57
|
+
def init_ui(self):
|
|
58
|
+
center_widget = QWidget()
|
|
59
|
+
self.setCentralWidget(center_widget)
|
|
60
|
+
main_layout = QHBoxLayout()
|
|
61
|
+
center_widget.setLayout(main_layout)
|
|
62
|
+
|
|
63
|
+
# Create a vertical layout for the settings
|
|
64
|
+
setting_layout = QVBoxLayout()
|
|
65
|
+
|
|
66
|
+
# Add a checkbox for RGB
|
|
67
|
+
self.checkbox_rgb = QCheckBox("Enable RGB Cloud")
|
|
68
|
+
self.checkbox_rgb.setChecked(False)
|
|
69
|
+
setting_layout.addWidget(self.checkbox_rgb)
|
|
70
|
+
self.checkbox_rgb.stateChanged.connect(
|
|
71
|
+
self.checkbox_changed)
|
|
72
|
+
|
|
73
|
+
# Add XYZ spin boxes
|
|
74
|
+
label_xyz = QLabel("Set XYZ:")
|
|
75
|
+
setting_layout.addWidget(label_xyz)
|
|
76
|
+
self.box_x = CustomDoubleSpinBox()
|
|
77
|
+
self.box_x.setSingleStep(0.01)
|
|
78
|
+
setting_layout.addWidget(self.box_x)
|
|
79
|
+
self.box_y = CustomDoubleSpinBox()
|
|
80
|
+
self.box_y.setSingleStep(0.01)
|
|
81
|
+
setting_layout.addWidget(self.box_y)
|
|
82
|
+
self.box_z = CustomDoubleSpinBox()
|
|
83
|
+
self.box_z.setSingleStep(0.01)
|
|
84
|
+
setting_layout.addWidget(self.box_z)
|
|
85
|
+
self.box_x.setRange(-100.0, 100.0)
|
|
86
|
+
self.box_y.setRange(-100.0, 100.0)
|
|
87
|
+
self.box_z.setRange(-100.0, 100.0)
|
|
88
|
+
self.box_x.setValue(self.tbl[0])
|
|
89
|
+
self.box_y.setValue(self.tbl[1])
|
|
90
|
+
self.box_z.setValue(self.tbl[2])
|
|
91
|
+
|
|
92
|
+
# Add RPY spin boxes
|
|
93
|
+
label_rpy = QLabel("Set Roll-Pitch-Yaw:")
|
|
94
|
+
setting_layout.addWidget(label_rpy)
|
|
95
|
+
self.box_roll = CustomDoubleSpinBox()
|
|
96
|
+
self.box_roll.setSingleStep(0.01)
|
|
97
|
+
setting_layout.addWidget(self.box_roll)
|
|
98
|
+
self.box_pitch = CustomDoubleSpinBox()
|
|
99
|
+
self.box_pitch.setSingleStep(0.01)
|
|
100
|
+
setting_layout.addWidget(self.box_pitch)
|
|
101
|
+
self.box_yaw = CustomDoubleSpinBox()
|
|
102
|
+
self.box_yaw.setSingleStep(0.01)
|
|
103
|
+
setting_layout.addWidget(self.box_yaw)
|
|
104
|
+
self.box_roll.setRange(-np.pi, np.pi)
|
|
105
|
+
self.box_pitch.setRange(-np.pi, np.pi)
|
|
106
|
+
self.box_yaw.setRange(-np.pi, np.pi)
|
|
107
|
+
rpy = q3d.matrix_to_euler(self.Rbl)
|
|
108
|
+
self.box_roll.setValue(rpy[0])
|
|
109
|
+
self.box_pitch.setValue(rpy[1])
|
|
110
|
+
self.box_yaw.setValue(rpy[2])
|
|
111
|
+
|
|
112
|
+
label_psize = QLabel("Set point size:")
|
|
113
|
+
setting_layout.addWidget(label_psize)
|
|
114
|
+
self.box_psize = QSpinBox()
|
|
115
|
+
self.box_psize.setValue(self.psize)
|
|
116
|
+
self.box_psize.setRange(0, 5)
|
|
117
|
+
self.box_psize.valueChanged.connect(self.update_point_size)
|
|
118
|
+
setting_layout.addWidget(self.box_psize)
|
|
119
|
+
|
|
120
|
+
label_cloud_num = QLabel("Set cloud frame number:")
|
|
121
|
+
setting_layout.addWidget(label_cloud_num)
|
|
122
|
+
self.box_cloud_num = QSpinBox()
|
|
123
|
+
self.box_cloud_num.setValue(self.cloud_num)
|
|
124
|
+
self.box_cloud_num.setRange(1, 100)
|
|
125
|
+
self.box_cloud_num.valueChanged.connect(self.update_cloud_num)
|
|
126
|
+
setting_layout.addWidget(self.box_cloud_num)
|
|
127
|
+
|
|
128
|
+
label_res = QLabel("The cam-lidar quat and translation:")
|
|
129
|
+
setting_layout.addWidget(label_res)
|
|
130
|
+
self.line_quat = QLineEdit()
|
|
131
|
+
self.line_quat.setReadOnly(True)
|
|
132
|
+
setting_layout.addWidget(self.line_quat)
|
|
133
|
+
self.line_trans = QLineEdit()
|
|
134
|
+
self.line_trans.setReadOnly(True)
|
|
135
|
+
setting_layout.addWidget(self.line_trans)
|
|
136
|
+
|
|
137
|
+
self.line_trans.setText(
|
|
138
|
+
f"[{self.tcl[0]:.6f}, {self.tcl[1]:.6f}, {self.tcl[2]:.6f}]")
|
|
139
|
+
quat = q3d.matrix_to_quaternion(self.Rcl)
|
|
140
|
+
self.line_quat.setText(
|
|
141
|
+
f"[{quat[0]:.6f}, {quat[1]:.6f}, {quat[2]:.6f}, {quat[3]:.6f}]")
|
|
142
|
+
|
|
143
|
+
# Connect spin boxes to methods
|
|
144
|
+
self.box_x.valueChanged.connect(self.update_xyz)
|
|
145
|
+
self.box_y.valueChanged.connect(self.update_xyz)
|
|
146
|
+
self.box_z.valueChanged.connect(self.update_xyz)
|
|
147
|
+
self.box_roll.valueChanged.connect(self.update_rpy)
|
|
148
|
+
self.box_pitch.valueChanged.connect(self.update_rpy)
|
|
149
|
+
self.box_yaw.valueChanged.connect(self.update_rpy)
|
|
150
|
+
|
|
151
|
+
# Add a stretch to push the widgets to the top
|
|
152
|
+
setting_layout.addStretch(1)
|
|
153
|
+
|
|
154
|
+
self.glwidget = q3d.GLWidget()
|
|
155
|
+
main_layout.addLayout(setting_layout)
|
|
156
|
+
main_layout.addWidget(self.glwidget, 1)
|
|
157
|
+
|
|
158
|
+
timer = QtCore.QTimer(self)
|
|
159
|
+
timer.setInterval(20) # period, in milliseconds
|
|
160
|
+
timer.timeout.connect(self.update)
|
|
161
|
+
self.glwidget.set_cam_position(distance=5)
|
|
162
|
+
self.glwidget.set_bg_color('#ffffff')
|
|
163
|
+
timer.start()
|
|
164
|
+
|
|
165
|
+
def update_point_size(self):
|
|
166
|
+
self.psize = self.box_psize.value()
|
|
167
|
+
|
|
168
|
+
def update_cloud_num(self):
|
|
169
|
+
self.cloud_num = self.box_cloud_num.value()
|
|
170
|
+
|
|
171
|
+
def update_xyz(self):
|
|
172
|
+
x = self.box_x.value()
|
|
173
|
+
y = self.box_y.value()
|
|
174
|
+
z = self.box_z.value()
|
|
175
|
+
self.tbl = np.array([x, y, z])
|
|
176
|
+
self.tcl = self.Rcb @ self.tbl
|
|
177
|
+
x, y, z = self.tcl
|
|
178
|
+
self.line_trans.setText(f"[{x:.6f}, {y:.6f}, {x:.6f}]")
|
|
179
|
+
|
|
180
|
+
def update_rpy(self):
|
|
181
|
+
roll = self.box_roll.value()
|
|
182
|
+
pitch = self.box_pitch.value()
|
|
183
|
+
yaw = self.box_yaw.value()
|
|
184
|
+
self.Rbl = q3d.euler_to_matrix(np.array([roll, pitch, yaw]))
|
|
185
|
+
self.Rcl = self.Rcb @ self.Rbl
|
|
186
|
+
quat = q3d.matrix_to_quaternion(self.Rcl)
|
|
187
|
+
self.line_quat.setText(
|
|
188
|
+
f"[{quat[0]:.6f}, {quat[1]:.6f}, {quat[2]:.6f}, {quat[3]:.6f}]")
|
|
189
|
+
|
|
190
|
+
def checkbox_changed(self, state):
|
|
191
|
+
if state == QtCore.Qt.Checked:
|
|
192
|
+
self.en_rgb = True
|
|
193
|
+
else:
|
|
194
|
+
self.en_rgb = False
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def camera_info_cb(data):
|
|
198
|
+
global remap_info, K
|
|
199
|
+
if remap_info is None: # Initialize only once
|
|
200
|
+
K = np.array(data.K).reshape(3, 3)
|
|
201
|
+
D = np.array(data.D)
|
|
202
|
+
rospy.loginfo("Camera intrinsic parameters set")
|
|
203
|
+
height = data.height
|
|
204
|
+
width = data.width
|
|
205
|
+
mapx, mapy = cv2.initUndistortRectifyMap(
|
|
206
|
+
K, D, None, K, (width, height), cv2.CV_32FC1)
|
|
207
|
+
remap_info = [mapx, mapy]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def scan_cb(data):
|
|
211
|
+
global viewer, clouds, cloud_accum, cloud_accum_color
|
|
212
|
+
cloud, _, _ = convert_pointcloud2_msg(data)
|
|
213
|
+
while len(clouds) > viewer.cloud_num:
|
|
214
|
+
clouds.pop(0)
|
|
215
|
+
clouds.append(cloud)
|
|
216
|
+
cloud_accum = np.concatenate(clouds)
|
|
217
|
+
|
|
218
|
+
if cloud_accum_color is not None and viewer.en_rgb:
|
|
219
|
+
viewer['scan'].set_data(data=cloud_accum_color)
|
|
220
|
+
viewer['scan'].set_color_mode('RGB')
|
|
221
|
+
else:
|
|
222
|
+
viewer['scan'].set_data(data=cloud_accum)
|
|
223
|
+
viewer['scan'].set_color_mode('I')
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def draw_larger_points(image, points, colors, radius):
|
|
227
|
+
for dx in range(-radius + 1, radius):
|
|
228
|
+
for dy in range(-radius + 1, radius):
|
|
229
|
+
distance = np.sqrt(dx**2 + dy**2)
|
|
230
|
+
if distance <= radius:
|
|
231
|
+
x_indices = points[:, 0] + dx
|
|
232
|
+
y_indices = points[:, 1] + dy
|
|
233
|
+
image[y_indices, x_indices] = colors
|
|
234
|
+
return image
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def image_cb(data):
|
|
238
|
+
global cloud_accum, cloud_accum_color, remap_info, K
|
|
239
|
+
if remap_info is None:
|
|
240
|
+
rospy.logwarn("Camera parameters not yet received.")
|
|
241
|
+
return
|
|
242
|
+
image, _ = convert_image_msg(data)
|
|
243
|
+
|
|
244
|
+
# Undistort the image
|
|
245
|
+
image_un = cv2.remap(image, remap_info[0], remap_info[1], cv2.INTER_LINEAR)
|
|
246
|
+
if cloud_accum is not None:
|
|
247
|
+
cloud_local = cloud_accum.copy()
|
|
248
|
+
tcl = viewer.tcl
|
|
249
|
+
Rcl = viewer.Rcl
|
|
250
|
+
pl = cloud_local['xyz']
|
|
251
|
+
pc = (Rcl @ pl.T + tcl[:, np.newaxis]).T
|
|
252
|
+
u = (K @ pc.T).T
|
|
253
|
+
u_mask = (u[:, 2] != 0) & (pc[:, 2] > 0.2)
|
|
254
|
+
u = u[:, :2][u_mask] / u[:, 2][u_mask][:, np.newaxis]
|
|
255
|
+
|
|
256
|
+
radius = viewer.psize # Radius of the points to be drawn
|
|
257
|
+
u = np.round(u).astype(np.int32)
|
|
258
|
+
valid_x = (u[:, 0] >= radius) & (u[:, 0] < image_un.shape[1]-radius)
|
|
259
|
+
valid_y = (u[:, 1] >= radius) & (u[:, 1] < image_un.shape[0]-radius)
|
|
260
|
+
valid_points = valid_x & valid_y
|
|
261
|
+
u = u[valid_points]
|
|
262
|
+
|
|
263
|
+
intensity = cloud_local['irgb'][u_mask][valid_points] >> 24
|
|
264
|
+
vmin = viewer['scan'].vmin
|
|
265
|
+
vmax = viewer['scan'].vmax
|
|
266
|
+
intensity_color = q3d.rainbow(intensity, scalar_min=vmin, scalar_max=vmax).astype(np.uint8)
|
|
267
|
+
draw_image = image_un.copy()
|
|
268
|
+
draw_image = draw_larger_points(draw_image, u, intensity_color, radius)
|
|
269
|
+
rgb = image_un[u[:, 1], u[:, 0]]
|
|
270
|
+
xyz = cloud_local['xyz'][u_mask][valid_points]
|
|
271
|
+
color = (intensity.astype(np.uint32) << 24) | \
|
|
272
|
+
(rgb[:, 0].astype(np.uint32) << 16) | \
|
|
273
|
+
(rgb[:, 1].astype(np.uint32) << 8) | \
|
|
274
|
+
(rgb[:, 2].astype(np.uint32) << 0)
|
|
275
|
+
cloud_accum_color = np.rec.fromarrays(
|
|
276
|
+
[xyz, color],
|
|
277
|
+
dtype=viewer['scan'].data_type)
|
|
278
|
+
viewer['img'].set_data(data=draw_image)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def main():
|
|
282
|
+
global viewer
|
|
283
|
+
# Set up argument parser
|
|
284
|
+
parser = argparse.ArgumentParser(
|
|
285
|
+
description="Configure topic names for LiDAR, image, and camera info.")
|
|
286
|
+
parser.add_argument(
|
|
287
|
+
'--lidar', type=str, default='lidar', help="Topic of LiDAR data")
|
|
288
|
+
parser.add_argument('--camera', type=str, default='image_raw',
|
|
289
|
+
help="Topic of camera image data")
|
|
290
|
+
parser.add_argument('--camera_info', type=str,
|
|
291
|
+
default='camera_info', help="Topic of camera info")
|
|
292
|
+
args = parser.parse_args()
|
|
293
|
+
|
|
294
|
+
app = q3d.QApplication(['LiDAR Cam Calib'])
|
|
295
|
+
viewer = ViewerWithPanel(name='LiDAR Cam Calib')
|
|
296
|
+
grid_item = q3d.GridItem(size=10, spacing=1, color=(0, 0, 0, 70))
|
|
297
|
+
scan_item = q3d.CloudItem(size=2, alpha=1, color_mode='I')
|
|
298
|
+
img_item = q3d.ImageItem(pos=np.array([0, 0]), size=np.array([800, 600]))
|
|
299
|
+
viewer.add_items({'scan': scan_item, 'grid': grid_item, 'img': img_item})
|
|
300
|
+
|
|
301
|
+
rospy.init_node('lidar_cam_calib', anonymous=True)
|
|
302
|
+
|
|
303
|
+
# Use topic names from arguments
|
|
304
|
+
rospy.Subscriber(args.lidar, PointCloud2, scan_cb, queue_size=1)
|
|
305
|
+
rospy.Subscriber(args.camera, Image, image_cb, queue_size=1)
|
|
306
|
+
rospy.Subscriber(args.camera_info, CameraInfo,
|
|
307
|
+
camera_info_cb, queue_size=1)
|
|
308
|
+
|
|
309
|
+
viewer.show()
|
|
310
|
+
app.exec()
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
if __name__ == '__main__':
|
|
314
|
+
main()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
|
|
5
|
+
Distributed under MIT license. See LICENSE for more information.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from q3dviewer.utils.maths import make_transform
|
|
9
|
+
import q3dviewer as q3d
|
|
10
|
+
import rospy
|
|
11
|
+
from nav_msgs.msg import Odometry
|
|
12
|
+
from sensor_msgs.msg import PointCloud2
|
|
13
|
+
import numpy as np
|
|
14
|
+
import random
|
|
15
|
+
from sensor_msgs.msg import Image
|
|
16
|
+
from q3dviewer.utils.convert_ros_msg import convert_pointcloud2_msg, convert_odometry_msg, convert_image_msg
|
|
17
|
+
|
|
18
|
+
viewer = None
|
|
19
|
+
point_num_per_scan = None
|
|
20
|
+
color_mode = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def odom_cb(data):
|
|
24
|
+
global viewer
|
|
25
|
+
transform, _ = convert_odometry_msg(data)
|
|
26
|
+
viewer['odom'].set_transform(transform)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def scan_cb(data):
|
|
30
|
+
global viewer
|
|
31
|
+
global point_num_per_scan
|
|
32
|
+
cloud, fields, _ = convert_pointcloud2_msg(data)
|
|
33
|
+
if (cloud.shape[0] > point_num_per_scan):
|
|
34
|
+
idx = random.sample(range(cloud.shape[0]), point_num_per_scan)
|
|
35
|
+
cloud = cloud[idx]
|
|
36
|
+
color_mode = 'FLAT'
|
|
37
|
+
if 'intensity' in fields:
|
|
38
|
+
color_mode = 'I'
|
|
39
|
+
if 'rgb' in fields:
|
|
40
|
+
color_mode = 'RGB'
|
|
41
|
+
viewer['map'].set_color_mode(color_mode)
|
|
42
|
+
viewer['map'].set_data(data=cloud, append=True)
|
|
43
|
+
viewer['scan'].set_data(data=cloud)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def image_cb(data):
|
|
47
|
+
image, _ = convert_image_msg(data)
|
|
48
|
+
viewer['img'].set_data(data=image)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
rospy.init_node('ros_viewer', anonymous=True)
|
|
53
|
+
|
|
54
|
+
global viewer
|
|
55
|
+
global point_num_per_scan
|
|
56
|
+
point_num_per_scan = 10000
|
|
57
|
+
map_item = q3d.CloudIOItem(size=1, alpha=0.1, color_mode='RGB')
|
|
58
|
+
scan_item = q3d.CloudItem(
|
|
59
|
+
size=2, alpha=1, color_mode='FLAT', color='#ffffff')
|
|
60
|
+
odom_item = q3d.AxisItem(size=0.5, width=5)
|
|
61
|
+
grid_item = q3d.GridItem(size=1000, spacing=20)
|
|
62
|
+
img_item = q3d.ImageItem(pos=np.array([0, 0]), size=np.array([800, 600]))
|
|
63
|
+
|
|
64
|
+
app = q3d.QApplication(['ROS Viewer'])
|
|
65
|
+
viewer = q3d.Viewer(name='ROS Viewer')
|
|
66
|
+
|
|
67
|
+
viewer.add_items({'map': map_item, 'scan': scan_item,
|
|
68
|
+
'odom': odom_item, 'grid': grid_item,
|
|
69
|
+
'img': img_item})
|
|
70
|
+
|
|
71
|
+
point_num_per_scan = rospy.get_param("scan_num", 100000)
|
|
72
|
+
print("point_num_per_scan: %d" % point_num_per_scan)
|
|
73
|
+
rospy.Subscriber(
|
|
74
|
+
"/cloud_registered", PointCloud2, scan_cb,
|
|
75
|
+
queue_size=1, buff_size=2**24)
|
|
76
|
+
rospy.Subscriber(
|
|
77
|
+
"/odometry", Odometry, odom_cb, queue_size=1, buff_size=2**24)
|
|
78
|
+
rospy.Subscriber('/image', Image, image_cb)
|
|
79
|
+
|
|
80
|
+
viewer.show()
|
|
81
|
+
app.exec()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
main()
|