q3dviewer 1.0.7__py3-none-any.whl → 1.0.9__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/base_glwidget.py +53 -8
- q3dviewer/base_item.py +15 -0
- q3dviewer/custom_items/axis_item.py +33 -96
- q3dviewer/custom_items/cloud_item.py +42 -31
- q3dviewer/custom_items/frame_item.py +56 -36
- q3dviewer/custom_items/gaussian_item.py +3 -3
- q3dviewer/custom_items/grid_item.py +88 -37
- q3dviewer/custom_items/image_item.py +1 -2
- q3dviewer/custom_items/line_item.py +4 -5
- q3dviewer/gau_io.py +0 -168
- q3dviewer/glwidget.py +22 -17
- q3dviewer/test/test_interpolation.py +58 -0
- q3dviewer/test/test_rendering.py +72 -0
- q3dviewer/tools/cinematographer.py +367 -0
- q3dviewer/tools/film_maker.py +395 -0
- q3dviewer/tools/lidar_calib.py +11 -22
- q3dviewer/tools/lidar_cam_calib.py +9 -20
- q3dviewer/tools/ros_viewer.py +7 -8
- q3dviewer/utils/maths.py +155 -5
- q3dviewer/viewer.py +30 -7
- {q3dviewer-1.0.7.dist-info → q3dviewer-1.0.9.dist-info}/METADATA +8 -15
- q3dviewer-1.0.9.dist-info/RECORD +45 -0
- {q3dviewer-1.0.7.dist-info → q3dviewer-1.0.9.dist-info}/entry_points.txt +1 -1
- q3dviewer/basic_window.py +0 -228
- q3dviewer/cloud_viewer.py +0 -74
- q3dviewer/custom_items/camera_frame_item.py +0 -173
- q3dviewer/custom_items/trajectory_item.py +0 -79
- q3dviewer/utils.py +0 -71
- q3dviewer-1.0.7.dist-info/RECORD +0 -46
- {q3dviewer-1.0.7.dist-info → q3dviewer-1.0.9.dist-info}/LICENSE +0 -0
- {q3dviewer-1.0.7.dist-info → q3dviewer-1.0.9.dist-info}/WHEEL +0 -0
- {q3dviewer-1.0.7.dist-info → q3dviewer-1.0.9.dist-info}/top_level.txt +0 -0
q3dviewer/base_glwidget.py
CHANGED
|
@@ -25,6 +25,9 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
25
25
|
self.active_keys = set()
|
|
26
26
|
self.show_center = False
|
|
27
27
|
self.enable_show_center = True
|
|
28
|
+
self.view_need_update = True
|
|
29
|
+
self.view_matrix = self.get_view_matrix()
|
|
30
|
+
self.projection_matrix = self.get_projection_matrix()
|
|
28
31
|
|
|
29
32
|
def keyPressEvent(self, ev: QtGui.QKeyEvent):
|
|
30
33
|
if ev.key() == QtCore.Qt.Key_Up or \
|
|
@@ -87,15 +90,29 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
87
90
|
"""
|
|
88
91
|
for item in self.items:
|
|
89
92
|
item.initialize()
|
|
93
|
+
# initialize the projection matrix and model view matrix
|
|
94
|
+
self.projection_matrix = self.get_projection_matrix()
|
|
95
|
+
self.update_model_projection()
|
|
96
|
+
self.view_matrix = self.get_view_matrix()
|
|
97
|
+
self.update_model_view()
|
|
98
|
+
|
|
99
|
+
def set_view_matrix(self, view_matrix):
|
|
100
|
+
self.view_matrix = view_matrix
|
|
101
|
+
self.view_need_update = False
|
|
90
102
|
|
|
91
103
|
def mouseReleaseEvent(self, ev):
|
|
92
104
|
if hasattr(self, 'mousePos'):
|
|
93
105
|
delattr(self, 'mousePos')
|
|
94
106
|
|
|
107
|
+
def set_dist(self, dist):
|
|
108
|
+
self.dist = dist
|
|
109
|
+
self.view_need_update = True
|
|
110
|
+
|
|
95
111
|
def update_dist(self, delta):
|
|
96
112
|
self.dist += delta
|
|
97
113
|
if self.dist < 0.1:
|
|
98
114
|
self.dist = 0.1
|
|
115
|
+
self.view_need_update = True
|
|
99
116
|
|
|
100
117
|
def wheelEvent(self, ev):
|
|
101
118
|
delta = ev.angleDelta().x()
|
|
@@ -121,17 +138,32 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
121
138
|
dist = max(self.dist, 0.5)
|
|
122
139
|
self.center += Rwc @ Kinv @ np.array([-diff.x(), diff.y(), 0]) * dist
|
|
123
140
|
self.show_center = True
|
|
141
|
+
self.view_need_update = True
|
|
142
|
+
|
|
143
|
+
def set_center(self, center):
|
|
144
|
+
self.center = center
|
|
145
|
+
self.view_need_update = True
|
|
124
146
|
|
|
125
147
|
def paintGL(self):
|
|
126
|
-
|
|
127
|
-
self.
|
|
148
|
+
# if the camera is moved, update the model view matrix.
|
|
149
|
+
if self.view_need_update:
|
|
150
|
+
self.view_matrix = self.get_view_matrix()
|
|
151
|
+
self.view_need_update = False
|
|
128
152
|
self.update_model_view()
|
|
153
|
+
|
|
154
|
+
# set the background color
|
|
129
155
|
bgcolor = self.color
|
|
130
156
|
glClearColor(*bgcolor)
|
|
131
157
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
|
|
132
158
|
for item in self.items:
|
|
133
159
|
if not item.visible():
|
|
134
160
|
continue
|
|
161
|
+
if not item.is_initialized():
|
|
162
|
+
"""
|
|
163
|
+
The item may not be initialized if it is added
|
|
164
|
+
after the widget is shown, so we need to initialize it here.
|
|
165
|
+
"""
|
|
166
|
+
item.initialize()
|
|
135
167
|
glMatrixMode(GL_MODELVIEW)
|
|
136
168
|
glPushMatrix()
|
|
137
169
|
glPushAttrib(GL_ALL_ATTRIB_BITS)
|
|
@@ -189,11 +221,11 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
189
221
|
self.center += Rz @ np.array([-trans_speed, 0, 0])
|
|
190
222
|
if QtCore.Qt.Key_D in self.active_keys:
|
|
191
223
|
self.center += Rz @ np.array([trans_speed, 0, 0])
|
|
224
|
+
self.view_need_update = True
|
|
192
225
|
|
|
193
226
|
def update_model_view(self):
|
|
194
|
-
m = self.get_view_matrix()
|
|
195
227
|
glMatrixMode(GL_MODELVIEW)
|
|
196
|
-
glLoadMatrixf(
|
|
228
|
+
glLoadMatrixf(self.view_matrix.T)
|
|
197
229
|
|
|
198
230
|
def get_view_matrix(self):
|
|
199
231
|
two = self.center # the origin(center) in the world frame
|
|
@@ -209,9 +241,9 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
209
241
|
pos = kwargs.get('pos', None)
|
|
210
242
|
distance = kwargs.get('distance', None)
|
|
211
243
|
if pos is not None:
|
|
212
|
-
self.
|
|
244
|
+
self.set_center(pos)
|
|
213
245
|
if distance is not None:
|
|
214
|
-
self.
|
|
246
|
+
self.set_dist(distance)
|
|
215
247
|
|
|
216
248
|
def set_color(self, color):
|
|
217
249
|
self.color = color
|
|
@@ -221,9 +253,8 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
221
253
|
super().update()
|
|
222
254
|
|
|
223
255
|
def update_model_projection(self):
|
|
224
|
-
m = self.get_projection_matrix()
|
|
225
256
|
glMatrixMode(GL_PROJECTION)
|
|
226
|
-
glLoadMatrixf(
|
|
257
|
+
glLoadMatrixf(self.projection_matrix.T)
|
|
227
258
|
|
|
228
259
|
def get_projection_matrix(self):
|
|
229
260
|
w, h = self.current_width(), self.current_height()
|
|
@@ -259,3 +290,17 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
259
290
|
|
|
260
291
|
def change_show_center(self, state):
|
|
261
292
|
self.enable_show_center = state
|
|
293
|
+
|
|
294
|
+
def resizeEvent(self, event):
|
|
295
|
+
super().resizeEvent(event)
|
|
296
|
+
self.projection_matrix = self.get_projection_matrix()
|
|
297
|
+
self.update_model_projection()
|
|
298
|
+
|
|
299
|
+
def capture_frame(self):
|
|
300
|
+
self.makeCurrent() # Ensure the OpenGL context is current
|
|
301
|
+
width = self.current_width()
|
|
302
|
+
height = self.current_height()
|
|
303
|
+
pixels = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE)
|
|
304
|
+
frame = np.frombuffer(pixels, dtype=np.uint8).reshape(height, width, 3)
|
|
305
|
+
frame = np.flip(frame, 0)
|
|
306
|
+
return frame
|
q3dviewer/base_item.py
CHANGED
|
@@ -16,6 +16,7 @@ class BaseItem(QtCore.QObject):
|
|
|
16
16
|
self._glwidget = None
|
|
17
17
|
self._visible = True
|
|
18
18
|
self._initialized = False
|
|
19
|
+
self._disable_setting = False
|
|
19
20
|
|
|
20
21
|
def set_glwidget(self, v):
|
|
21
22
|
self._glwidget = v
|
|
@@ -38,6 +39,17 @@ class BaseItem(QtCore.QObject):
|
|
|
38
39
|
def initialize(self):
|
|
39
40
|
if not self._initialized:
|
|
40
41
|
self.initialize_gl()
|
|
42
|
+
self._initialized = True
|
|
43
|
+
|
|
44
|
+
def is_initialized(self):
|
|
45
|
+
return self._initialized
|
|
46
|
+
|
|
47
|
+
def add_setting(self, layout):
|
|
48
|
+
"""
|
|
49
|
+
Add setting widgets to the layout.
|
|
50
|
+
This method should be overridden by subclasses to add any necessary setting widgets to the layout.
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
41
53
|
|
|
42
54
|
def initialize_gl(self):
|
|
43
55
|
"""
|
|
@@ -53,5 +65,8 @@ class BaseItem(QtCore.QObject):
|
|
|
53
65
|
"""
|
|
54
66
|
pass
|
|
55
67
|
|
|
68
|
+
def disable_setting(self):
|
|
69
|
+
self._disable_setting = True
|
|
70
|
+
|
|
56
71
|
|
|
57
72
|
|
|
@@ -4,44 +4,9 @@ Distributed under MIT license. See LICENSE for more information.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from q3dviewer.base_item import BaseItem
|
|
7
|
-
from q3dviewer.utils import set_uniform
|
|
8
7
|
from OpenGL.GL import *
|
|
9
8
|
import numpy as np
|
|
10
|
-
from
|
|
11
|
-
from PySide6.QtWidgets import QLabel, QDoubleSpinBox
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# Vertex and Fragment shader source code
|
|
15
|
-
vertex_shader_source = """
|
|
16
|
-
#version 330 core
|
|
17
|
-
layout(location = 0) in vec3 position;
|
|
18
|
-
layout(location = 1) in vec3 color;
|
|
19
|
-
|
|
20
|
-
out vec3 ourColor;
|
|
21
|
-
|
|
22
|
-
uniform mat4 view_matrix;
|
|
23
|
-
uniform mat4 project_matrix;
|
|
24
|
-
uniform mat4 model_matrix;
|
|
25
|
-
uniform float size;
|
|
26
|
-
|
|
27
|
-
void main()
|
|
28
|
-
{
|
|
29
|
-
vec3 scaled_position = position * size;
|
|
30
|
-
gl_Position = project_matrix * view_matrix * model_matrix * vec4(scaled_position, 1.0);
|
|
31
|
-
ourColor = color;
|
|
32
|
-
}
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
fragment_shader_source = """
|
|
36
|
-
#version 330 core
|
|
37
|
-
in vec3 ourColor;
|
|
38
|
-
out vec4 color;
|
|
39
|
-
|
|
40
|
-
void main()
|
|
41
|
-
{
|
|
42
|
-
color = vec4(ourColor, 1.0);
|
|
43
|
-
}
|
|
44
|
-
"""
|
|
9
|
+
from PySide6.QtWidgets import QDoubleSpinBox
|
|
45
10
|
|
|
46
11
|
|
|
47
12
|
class AxisItem(BaseItem):
|
|
@@ -49,76 +14,45 @@ class AxisItem(BaseItem):
|
|
|
49
14
|
super().__init__()
|
|
50
15
|
self.size = size
|
|
51
16
|
self.width = width
|
|
52
|
-
self.
|
|
17
|
+
self.T = np.eye(4, dtype=np.float32)
|
|
53
18
|
self.need_update_setting = True
|
|
54
19
|
|
|
55
20
|
def initialize_gl(self):
|
|
56
|
-
# Axis vertices
|
|
21
|
+
# Axis vertices
|
|
57
22
|
self.vertices = np.array([
|
|
58
|
-
# positions
|
|
59
|
-
[0.0, 0.0, 0.0,
|
|
60
|
-
[
|
|
61
|
-
[0.0, 0.0, 0.0,
|
|
62
|
-
[0.0, 1.0, 0.0, 0.0, 1.0, 0.0],
|
|
63
|
-
[0.0, 0.0, 0.0, 0.0, 0.0, 1.0], # Z axis (blue)
|
|
64
|
-
[0.0, 0.0, 1.0, 0.0, 0.0, 1.0],
|
|
23
|
+
# positions
|
|
24
|
+
[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], # X axis
|
|
25
|
+
[0.0, 0.0, 0.0], [0.0, 1.0, 0.0], # Y axis
|
|
26
|
+
[0.0, 0.0, 0.0], [0.0, 0.0, 1.0], # Z axis
|
|
65
27
|
], dtype=np.float32)
|
|
66
28
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
|
|
74
|
-
|
|
75
|
-
# Vertex positions
|
|
76
|
-
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
|
|
77
|
-
glEnableVertexAttribArray(0)
|
|
78
|
-
|
|
79
|
-
# Vertex colors
|
|
80
|
-
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
|
|
81
|
-
glEnableVertexAttribArray(1)
|
|
82
|
-
|
|
83
|
-
# Compile shaders and create shader program
|
|
84
|
-
self.program = shaders.compileProgram(
|
|
85
|
-
shaders.compileShader(vertex_shader_source, GL_VERTEX_SHADER),
|
|
86
|
-
shaders.compileShader(fragment_shader_source, GL_FRAGMENT_SHADER),
|
|
87
|
-
)
|
|
29
|
+
# Axis colors
|
|
30
|
+
self.colors = np.array([
|
|
31
|
+
[1.0, 0.0, 0.0], [1.0, 0.0, 0.0], # X axis (red)
|
|
32
|
+
[0.0, 1.0, 0.0], [0.0, 1.0, 0.0], # Y axis (green)
|
|
33
|
+
[0.0, 0.0, 1.0], [0.0, 0.0, 1.0], # Z axis (blue)
|
|
34
|
+
], dtype=np.float32)
|
|
88
35
|
|
|
89
|
-
glBindVertexArray(0)
|
|
90
36
|
|
|
91
37
|
def add_setting(self, layout):
|
|
92
|
-
label_size = QLabel("Set size:")
|
|
93
|
-
layout.addWidget(label_size)
|
|
94
38
|
spinbox_size = QDoubleSpinBox()
|
|
39
|
+
spinbox_size.setPrefix("Size: ")
|
|
95
40
|
spinbox_size.setSingleStep(0.1)
|
|
96
|
-
layout.addWidget(spinbox_size)
|
|
97
41
|
spinbox_size.setValue(self.size)
|
|
98
|
-
spinbox_size.valueChanged.connect(self.set_size)
|
|
99
42
|
spinbox_size.setRange(0.0, 100)
|
|
43
|
+
spinbox_size.valueChanged.connect(self.set_size)
|
|
44
|
+
layout.addWidget(spinbox_size)
|
|
100
45
|
|
|
101
|
-
label_width = QLabel("Set width:")
|
|
102
|
-
layout.addWidget(label_width)
|
|
103
46
|
spinbox_width = QDoubleSpinBox()
|
|
104
|
-
|
|
47
|
+
spinbox_width.setPrefix("Width: ")
|
|
105
48
|
spinbox_width.setSingleStep(0.1)
|
|
106
49
|
spinbox_width.setValue(self.width)
|
|
107
|
-
spinbox_width.valueChanged.connect(self.set_width)
|
|
108
50
|
spinbox_width.setRange(0, 1000)
|
|
51
|
+
spinbox_width.valueChanged.connect(self.set_width)
|
|
52
|
+
layout.addWidget(spinbox_width)
|
|
109
53
|
|
|
110
54
|
def set_size(self, size):
|
|
111
55
|
self.size = size
|
|
112
|
-
self.need_update_setting = True
|
|
113
|
-
|
|
114
|
-
def update_setting(self):
|
|
115
|
-
if not self.need_update_setting:
|
|
116
|
-
return
|
|
117
|
-
glUseProgram(self.program)
|
|
118
|
-
set_uniform(self.program, float(self.size), 'size')
|
|
119
|
-
set_uniform(self.program, self.model_matrix, 'model_matrix')
|
|
120
|
-
glUseProgram(0)
|
|
121
|
-
self.need_update_setting = False
|
|
122
56
|
|
|
123
57
|
def set_width(self, width):
|
|
124
58
|
self.width = width
|
|
@@ -127,22 +61,25 @@ class AxisItem(BaseItem):
|
|
|
127
61
|
"""
|
|
128
62
|
Set the transformation matrix for the axis item.
|
|
129
63
|
"""
|
|
130
|
-
self.
|
|
64
|
+
self.T = transform
|
|
131
65
|
self.need_update_setting = True
|
|
132
66
|
|
|
133
67
|
def paint(self):
|
|
134
|
-
self.update_setting()
|
|
135
68
|
glLineWidth(self.width)
|
|
136
|
-
glUseProgram(self.program)
|
|
137
|
-
glBindVertexArray(self.vao)
|
|
138
69
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
70
|
+
glPushMatrix()
|
|
71
|
+
glMultMatrixf(self.T.T)
|
|
72
|
+
|
|
73
|
+
glEnableClientState(GL_VERTEX_ARRAY)
|
|
74
|
+
glVertexPointer(3, GL_FLOAT, 0, self.vertices * self.size)
|
|
75
|
+
|
|
76
|
+
glEnableClientState(GL_COLOR_ARRAY)
|
|
77
|
+
glColorPointer(3, GL_FLOAT, 0, self.colors)
|
|
78
|
+
|
|
79
|
+
glDrawArrays(GL_LINES, 0, len(self.vertices))
|
|
143
80
|
|
|
144
|
-
|
|
81
|
+
glDisableClientState(GL_VERTEX_ARRAY)
|
|
82
|
+
glDisableClientState(GL_COLOR_ARRAY)
|
|
83
|
+
glPopMatrix()
|
|
145
84
|
|
|
146
|
-
glBindVertexArray(0)
|
|
147
|
-
glUseProgram(0)
|
|
148
85
|
glLineWidth(1)
|
|
@@ -20,7 +20,11 @@ from PySide6.QtGui import QRegularExpressionValidator
|
|
|
20
20
|
|
|
21
21
|
# draw points with color (x, y, z, color)
|
|
22
22
|
class CloudItem(BaseItem):
|
|
23
|
-
def __init__(self, size, alpha,
|
|
23
|
+
def __init__(self, size, alpha,
|
|
24
|
+
color_mode='I',
|
|
25
|
+
color='#ffffff',
|
|
26
|
+
point_type='PIXEL',
|
|
27
|
+
depth_test=False):
|
|
24
28
|
super().__init__()
|
|
25
29
|
self.STRIDE = 16 # stride of cloud array
|
|
26
30
|
self.valid_buff_top = 0
|
|
@@ -42,11 +46,11 @@ class CloudItem(BaseItem):
|
|
|
42
46
|
self.need_update_setting = True
|
|
43
47
|
self.max_cloud_size = 300000000
|
|
44
48
|
# Enable depth test when full opaque
|
|
45
|
-
self.
|
|
49
|
+
self.depth_test = depth_test
|
|
46
50
|
self.path = os.path.dirname(__file__)
|
|
47
51
|
|
|
48
52
|
def add_setting(self, layout):
|
|
49
|
-
label_ptype = QLabel("
|
|
53
|
+
label_ptype = QLabel("Point Type:")
|
|
50
54
|
layout.addWidget(label_ptype)
|
|
51
55
|
combo_ptype = QComboBox()
|
|
52
56
|
combo_ptype.addItem("pixels")
|
|
@@ -55,26 +59,25 @@ class CloudItem(BaseItem):
|
|
|
55
59
|
combo_ptype.setCurrentIndex(self.point_type_table[self.point_type])
|
|
56
60
|
combo_ptype.currentIndexChanged.connect(self._on_point_type_selection)
|
|
57
61
|
layout.addWidget(combo_ptype)
|
|
58
|
-
|
|
59
|
-
layout.addWidget(self.label_size)
|
|
62
|
+
|
|
60
63
|
self.box_size = QDoubleSpinBox()
|
|
64
|
+
self.box_size.setPrefix("Size: ")
|
|
61
65
|
self.box_size.setSingleStep(1)
|
|
62
66
|
self.box_size.setDecimals(0)
|
|
63
|
-
layout.addWidget(self.box_size)
|
|
64
67
|
self.box_size.setValue(self.size)
|
|
65
|
-
self.box_size.valueChanged.connect(self.set_size)
|
|
66
68
|
self.box_size.setRange(0, 100)
|
|
69
|
+
self.box_size.valueChanged.connect(self.set_size)
|
|
70
|
+
layout.addWidget(self.box_size)
|
|
67
71
|
|
|
68
|
-
label_alpha = QLabel("Set Alpha:")
|
|
69
|
-
layout.addWidget(label_alpha)
|
|
70
72
|
box_alpha = QDoubleSpinBox()
|
|
71
|
-
|
|
73
|
+
box_alpha.setPrefix("Alpha: ")
|
|
72
74
|
box_alpha.setSingleStep(0.01)
|
|
73
75
|
box_alpha.setValue(self.alpha)
|
|
74
|
-
box_alpha.valueChanged.connect(self.set_alpha)
|
|
75
76
|
box_alpha.setRange(0, 1)
|
|
77
|
+
box_alpha.valueChanged.connect(self.set_alpha)
|
|
78
|
+
layout.addWidget(box_alpha)
|
|
76
79
|
|
|
77
|
-
label_color = QLabel("
|
|
80
|
+
label_color = QLabel("Color Mode:")
|
|
78
81
|
layout.addWidget(label_color)
|
|
79
82
|
self.combo_color = QComboBox()
|
|
80
83
|
self.combo_color.addItem("flat color")
|
|
@@ -84,6 +87,8 @@ class CloudItem(BaseItem):
|
|
|
84
87
|
self.combo_color.currentIndexChanged.connect(self._on_color_mode)
|
|
85
88
|
layout.addWidget(self.combo_color)
|
|
86
89
|
|
|
90
|
+
label_rgb = QLabel("Color:")
|
|
91
|
+
layout.addWidget(label_rgb)
|
|
87
92
|
self.edit_rgb = QLineEdit()
|
|
88
93
|
self.edit_rgb.setToolTip("Hex number, i.e. #FF4500")
|
|
89
94
|
self.edit_rgb.setText(f"#{self.flat_rgb:06x}")
|
|
@@ -96,14 +101,11 @@ class CloudItem(BaseItem):
|
|
|
96
101
|
self.slider_v = RangeSlider()
|
|
97
102
|
self.slider_v.setRange(0, 255)
|
|
98
103
|
self.slider_v.rangeChanged.connect(self._on_range)
|
|
99
|
-
|
|
100
104
|
layout.addWidget(self.slider_v)
|
|
101
|
-
self.combo_color.setCurrentIndex(self.color_mode)
|
|
102
105
|
|
|
103
|
-
# Add a checkbox for enabling/disabling depth test
|
|
104
106
|
self.checkbox_depth_test = QCheckBox(
|
|
105
107
|
"Show front points first (Depth Test)")
|
|
106
|
-
self.checkbox_depth_test.setChecked(self.
|
|
108
|
+
self.checkbox_depth_test.setChecked(self.depth_test)
|
|
107
109
|
self.checkbox_depth_test.stateChanged.connect(self.set_depthtest)
|
|
108
110
|
layout.addWidget(self.checkbox_depth_test)
|
|
109
111
|
|
|
@@ -124,20 +126,25 @@ class CloudItem(BaseItem):
|
|
|
124
126
|
|
|
125
127
|
def set_color_mode(self, color_mode):
|
|
126
128
|
if color_mode in {'FLAT', 'RGB', 'I'}:
|
|
127
|
-
|
|
129
|
+
try:
|
|
130
|
+
self.combo_color.setCurrentIndex(self.mode_table[color_mode])
|
|
131
|
+
except RuntimeError:
|
|
132
|
+
pass
|
|
133
|
+
except ValueError:
|
|
134
|
+
pass
|
|
128
135
|
else:
|
|
129
136
|
print(f"Invalid color mode: {color_mode}")
|
|
130
137
|
|
|
131
138
|
def _on_point_type_selection(self, index):
|
|
132
139
|
self.point_type = list(self.point_type_table.keys())[index]
|
|
133
140
|
if self.point_type == 'PIXEL':
|
|
134
|
-
self.
|
|
141
|
+
self.box_size.setPrefix("Set size (pixel): ")
|
|
135
142
|
self.box_size.setDecimals(0)
|
|
136
143
|
self.box_size.setSingleStep(1)
|
|
137
144
|
self.box_size.setValue(1)
|
|
138
145
|
self.size = 1
|
|
139
146
|
else:
|
|
140
|
-
self.
|
|
147
|
+
self.box_size.setPrefix("Set size (meter): ")
|
|
141
148
|
self.box_size.setDecimals(2)
|
|
142
149
|
self.box_size.setSingleStep(0.01)
|
|
143
150
|
self.box_size.setValue(0.01)
|
|
@@ -167,7 +174,7 @@ class CloudItem(BaseItem):
|
|
|
167
174
|
self.need_update_setting = True
|
|
168
175
|
|
|
169
176
|
def set_depthtest(self, state):
|
|
170
|
-
self.
|
|
177
|
+
self.depth_test = state
|
|
171
178
|
|
|
172
179
|
def clear(self):
|
|
173
180
|
data = np.empty((0), self.data_type)
|
|
@@ -236,13 +243,17 @@ class CloudItem(BaseItem):
|
|
|
236
243
|
self.buff = new_buff
|
|
237
244
|
|
|
238
245
|
# if exceed the maximum cloud size, randomly select half of the points
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
+
exceed_flag = False
|
|
247
|
+
while new_buff.shape[0] > self.max_cloud_size:
|
|
248
|
+
exceed_flag = True
|
|
249
|
+
new_buff_half = new_buff[:new_buff_top:2]
|
|
250
|
+
new_buff_top = new_buff_half.shape[0]
|
|
251
|
+
new_buff = new_buff_half
|
|
252
|
+
if exceed_flag:
|
|
253
|
+
print("[Cloud Item] Exceed maximum cloud size %d, reduce the data size" % self.max_cloud_size)
|
|
254
|
+
self.buff = self.buff[:self.max_cloud_size]
|
|
255
|
+
self.buff[:new_buff_top] = new_buff
|
|
256
|
+
|
|
246
257
|
|
|
247
258
|
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
|
248
259
|
glBufferData(GL_ARRAY_BUFFER, self.buff.nbytes,
|
|
@@ -276,7 +287,7 @@ class CloudItem(BaseItem):
|
|
|
276
287
|
glEnable(GL_BLEND)
|
|
277
288
|
glEnable(GL_PROGRAM_POINT_SIZE)
|
|
278
289
|
glEnable(GL_POINT_SPRITE)
|
|
279
|
-
if self.
|
|
290
|
+
if self.depth_test:
|
|
280
291
|
glEnable(GL_DEPTH_TEST)
|
|
281
292
|
else:
|
|
282
293
|
glDisable(GL_DEPTH_TEST)
|
|
@@ -291,9 +302,9 @@ class CloudItem(BaseItem):
|
|
|
291
302
|
glEnableVertexAttribArray(0)
|
|
292
303
|
glEnableVertexAttribArray(1)
|
|
293
304
|
|
|
294
|
-
view_matrix = self.glwidget().
|
|
305
|
+
view_matrix = self.glwidget().view_matrix
|
|
295
306
|
set_uniform(self.program, view_matrix, 'view_matrix')
|
|
296
|
-
project_matrix = self.glwidget().
|
|
307
|
+
project_matrix = self.glwidget().projection_matrix
|
|
297
308
|
set_uniform(self.program, project_matrix, 'projection_matrix')
|
|
298
309
|
width = self.glwidget().current_width()
|
|
299
310
|
focal = project_matrix[0, 0] * width / 2
|
|
@@ -309,5 +320,5 @@ class CloudItem(BaseItem):
|
|
|
309
320
|
glDisable(GL_POINT_SPRITE)
|
|
310
321
|
glDisable(GL_PROGRAM_POINT_SIZE)
|
|
311
322
|
glDisable(GL_BLEND)
|
|
312
|
-
if self.
|
|
323
|
+
if self.depth_test:
|
|
313
324
|
glDisable(GL_DEPTH_TEST) # Disable depth testing if it was enabled
|
|
@@ -42,7 +42,7 @@ void main()
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class FrameItem(BaseItem):
|
|
45
|
-
def __init__(self, T=np.eye(4), size=(1, 0.8), width=3, img=None):
|
|
45
|
+
def __init__(self, T=np.eye(4), size=(1, 0.8), width=3, img=None, color='#0000FF'):
|
|
46
46
|
BaseItem.__init__(self)
|
|
47
47
|
self.w, self.h = size
|
|
48
48
|
self.width = width
|
|
@@ -50,6 +50,7 @@ class FrameItem(BaseItem):
|
|
|
50
50
|
self.img = img
|
|
51
51
|
self.texture = None
|
|
52
52
|
self.need_updating = False
|
|
53
|
+
self.set_color(color)
|
|
53
54
|
|
|
54
55
|
def initialize_gl(self):
|
|
55
56
|
# Rectangle vertices and texture coordinates
|
|
@@ -57,11 +58,11 @@ class FrameItem(BaseItem):
|
|
|
57
58
|
hh = self.h / 2
|
|
58
59
|
self.vertices = np.array([
|
|
59
60
|
# positions # texture coords
|
|
60
|
-
[-hw,
|
|
61
|
-
[hw,
|
|
62
|
-
[hw,
|
|
63
|
-
[-hw,
|
|
64
|
-
[0, 0,
|
|
61
|
+
[-hw, hh, 0.0, 0.0, 0.0], # bottom-left
|
|
62
|
+
[ hw, hh, 0.0, 1.0, 0.0], # bottom-right
|
|
63
|
+
[ hw, -hh, 0.0, 1.0, 1.0], # top-right
|
|
64
|
+
[-hw, -hh, 0.0, 0.0, 1.0], # top-left
|
|
65
|
+
[ 0.0, 0.0, hh * 0.66, 0.0, 0.0], # center -Z is the front.
|
|
65
66
|
], dtype=np.float32)
|
|
66
67
|
|
|
67
68
|
self.indices = np.array([
|
|
@@ -123,23 +124,28 @@ class FrameItem(BaseItem):
|
|
|
123
124
|
|
|
124
125
|
glBindVertexArray(0)
|
|
125
126
|
|
|
126
|
-
def set_transform(self,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
def set_transform(self, Twc, is_opencv_coord=False):
|
|
128
|
+
if is_opencv_coord:
|
|
129
|
+
# convert the opencv camera coordinate to the opengl camera coordinate
|
|
130
|
+
M_conv = np.array([
|
|
131
|
+
[1, 0, 0, 0],
|
|
132
|
+
[0, -1, 0, 0],
|
|
133
|
+
[0, 0, -1, 0],
|
|
134
|
+
[0, 0, 0, 1]
|
|
135
|
+
])
|
|
136
|
+
Twc = Twc @ M_conv
|
|
137
|
+
self.Twc = Twc
|
|
138
|
+
|
|
139
|
+
def set_data(self, img=None, transform=None, is_opencv_coord=False):
|
|
130
140
|
if transform is not None:
|
|
131
|
-
self.
|
|
141
|
+
self.set_transform(transform, is_opencv_coord)
|
|
132
142
|
self.img = img
|
|
133
143
|
self.need_updating = True
|
|
134
144
|
|
|
135
145
|
def update_img_buffer(self):
|
|
136
|
-
if self.
|
|
146
|
+
if self.need_updating:
|
|
137
147
|
self.texture = glGenTextures(1)
|
|
138
148
|
glBindTexture(GL_TEXTURE_2D, self.texture)
|
|
139
|
-
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
|
|
140
|
-
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
|
|
141
|
-
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
142
|
-
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
143
149
|
if self.img.ndim == 2:
|
|
144
150
|
# Convert grayscale to RGBA
|
|
145
151
|
self.img = np.stack((self.img,) * 3 + (np.ones_like(self.img) * 255,), axis=-1)
|
|
@@ -147,44 +153,57 @@ class FrameItem(BaseItem):
|
|
|
147
153
|
# Add an alpha channel
|
|
148
154
|
alpha_channel = np.ones((self.img.shape[0], self.img.shape[1], 1), dtype=np.uint8) * 255
|
|
149
155
|
self.img = np.concatenate((self.img, alpha_channel), axis=2)
|
|
150
|
-
|
|
151
156
|
img = np.ascontiguousarray(self.img, dtype=np.uint8)
|
|
152
|
-
|
|
153
157
|
# Load image
|
|
154
158
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.shape[1],
|
|
155
159
|
img.shape[0], 0, GL_RGBA, GL_UNSIGNED_BYTE, img)
|
|
156
160
|
glGenerateMipmap(GL_TEXTURE_2D)
|
|
157
161
|
glBindTexture(GL_TEXTURE_2D, 0)
|
|
158
162
|
self.need_updating = False
|
|
163
|
+
|
|
164
|
+
def set_color(self, color):
|
|
165
|
+
if isinstance(color, str):
|
|
166
|
+
self.rgba = hex_to_rgba(color)
|
|
167
|
+
elif isinstance(color, list):
|
|
168
|
+
self.rgba = color
|
|
169
|
+
elif isinstance(color, tuple):
|
|
170
|
+
self.rgba = list(color)
|
|
171
|
+
else:
|
|
172
|
+
raise ValueError("Invalid color format")
|
|
173
|
+
|
|
174
|
+
def set_line_width(self, width):
|
|
175
|
+
self.width = width
|
|
159
176
|
|
|
160
177
|
def paint(self):
|
|
161
|
-
self.view_matrix = self.glwidget().
|
|
162
|
-
project_matrix = self.glwidget().
|
|
163
|
-
self.update_img_buffer()
|
|
178
|
+
self.view_matrix = self.glwidget().view_matrix
|
|
179
|
+
project_matrix = self.glwidget().projection_matrix
|
|
164
180
|
|
|
165
181
|
glEnable(GL_DEPTH_TEST)
|
|
166
182
|
glEnable(GL_BLEND)
|
|
167
183
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
168
184
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
185
|
+
if self.img is not None:
|
|
186
|
+
self.update_img_buffer()
|
|
187
|
+
glUseProgram(self.program)
|
|
188
|
+
set_uniform(self.program, self.view_matrix, 'view_matrix')
|
|
189
|
+
set_uniform(self.program, project_matrix, 'project_matrix')
|
|
190
|
+
set_uniform(self.program, self.T, 'model_matrix')
|
|
191
|
+
glBindVertexArray(self.vao)
|
|
192
|
+
glBindVertexArray(0)
|
|
193
|
+
glUseProgram(0)
|
|
194
|
+
|
|
195
|
+
# if self.texture is not None:
|
|
196
|
+
# glBindTexture(GL_TEXTURE_2D, self.texture)
|
|
197
|
+
# glMultMatrixf(self.T.T) # Apply the transformation matrix
|
|
198
|
+
# glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
|
|
199
|
+
# glBindTexture(GL_TEXTURE_2D, 0)
|
|
180
200
|
|
|
181
|
-
glBindVertexArray(0)
|
|
182
|
-
glUseProgram(0)
|
|
183
|
-
|
|
184
201
|
glLineWidth(self.width)
|
|
185
|
-
|
|
202
|
+
# set color to bule
|
|
203
|
+
glColor4f(*self.rgba)
|
|
186
204
|
glBindBuffer(GL_ARRAY_BUFFER, self.line_vbo)
|
|
187
205
|
glEnableClientState(GL_VERTEX_ARRAY)
|
|
206
|
+
glMultMatrixf(self.T.T)
|
|
188
207
|
glVertexPointer(3, GL_FLOAT, 0, None)
|
|
189
208
|
glDrawArrays(GL_LINES, 0, len(self.line_vertices))
|
|
190
209
|
glDisableClientState(GL_VERTEX_ARRAY)
|
|
@@ -192,3 +211,4 @@ class FrameItem(BaseItem):
|
|
|
192
211
|
|
|
193
212
|
glDisable(GL_DEPTH_TEST)
|
|
194
213
|
glDisable(GL_BLEND)
|
|
214
|
+
|