q3dviewer 1.0.8__py3-none-any.whl → 1.1.0__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 +62 -10
- q3dviewer/base_item.py +15 -0
- q3dviewer/custom_items/axis_item.py +31 -94
- q3dviewer/custom_items/cloud_item.py +36 -30
- 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 -16
- q3dviewer/test/test_interpolation.py +58 -0
- q3dviewer/test/test_rendering.py +72 -0
- q3dviewer/tools/cinematographer.py +367 -0
- q3dviewer/tools/cloud_viewer.py +2 -2
- q3dviewer/tools/film_maker.py +421 -0
- q3dviewer/tools/lidar_calib.py +11 -22
- q3dviewer/tools/lidar_cam_calib.py +9 -20
- q3dviewer/utils/maths.py +155 -5
- q3dviewer/viewer.py +30 -7
- q3dviewer-1.1.0.dist-info/METADATA +214 -0
- q3dviewer-1.1.0.dist-info/RECORD +45 -0
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.1.0.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.8.dist-info/METADATA +0 -21
- q3dviewer-1.0.8.dist-info/RECORD +0 -46
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.1.0.dist-info}/LICENSE +0 -0
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.1.0.dist-info}/WHEEL +0 -0
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.1.0.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
|
|
@@ -206,12 +238,19 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
206
238
|
return Tcw
|
|
207
239
|
|
|
208
240
|
def set_cam_position(self, **kwargs):
|
|
209
|
-
|
|
241
|
+
center = kwargs.get('center', None)
|
|
210
242
|
distance = kwargs.get('distance', None)
|
|
211
|
-
|
|
212
|
-
|
|
243
|
+
euler = kwargs.get('euler', None)
|
|
244
|
+
if center is not None:
|
|
245
|
+
self.set_center(center)
|
|
213
246
|
if distance is not None:
|
|
214
|
-
self.
|
|
247
|
+
self.set_dist(distance)
|
|
248
|
+
if euler is not None:
|
|
249
|
+
self.set_euler(euler)
|
|
250
|
+
|
|
251
|
+
def set_euler(self, euler):
|
|
252
|
+
self.euler = euler
|
|
253
|
+
self.view_need_update = True
|
|
215
254
|
|
|
216
255
|
def set_color(self, color):
|
|
217
256
|
self.color = color
|
|
@@ -221,9 +260,8 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
221
260
|
super().update()
|
|
222
261
|
|
|
223
262
|
def update_model_projection(self):
|
|
224
|
-
m = self.get_projection_matrix()
|
|
225
263
|
glMatrixMode(GL_PROJECTION)
|
|
226
|
-
glLoadMatrixf(
|
|
264
|
+
glLoadMatrixf(self.projection_matrix.T)
|
|
227
265
|
|
|
228
266
|
def get_projection_matrix(self):
|
|
229
267
|
w, h = self.current_width(), self.current_height()
|
|
@@ -259,3 +297,17 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
259
297
|
|
|
260
298
|
def change_show_center(self, state):
|
|
261
299
|
self.enable_show_center = state
|
|
300
|
+
|
|
301
|
+
def resizeEvent(self, event):
|
|
302
|
+
super().resizeEvent(event)
|
|
303
|
+
self.projection_matrix = self.get_projection_matrix()
|
|
304
|
+
self.update_model_projection()
|
|
305
|
+
|
|
306
|
+
def capture_frame(self):
|
|
307
|
+
self.makeCurrent() # Ensure the OpenGL context is current
|
|
308
|
+
width = self.current_width()
|
|
309
|
+
height = self.current_height()
|
|
310
|
+
pixels = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE)
|
|
311
|
+
frame = np.frombuffer(pixels, dtype=np.uint8).reshape(height, width, 3)
|
|
312
|
+
frame = np.flip(frame, 0)
|
|
313
|
+
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):
|
|
@@ -53,72 +18,41 @@ class AxisItem(BaseItem):
|
|
|
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.T, '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
|
|
@@ -131,18 +65,21 @@ class AxisItem(BaseItem):
|
|
|
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
|
|
|
@@ -136,13 +138,13 @@ class CloudItem(BaseItem):
|
|
|
136
138
|
def _on_point_type_selection(self, index):
|
|
137
139
|
self.point_type = list(self.point_type_table.keys())[index]
|
|
138
140
|
if self.point_type == 'PIXEL':
|
|
139
|
-
self.
|
|
141
|
+
self.box_size.setPrefix("Set size (pixel): ")
|
|
140
142
|
self.box_size.setDecimals(0)
|
|
141
143
|
self.box_size.setSingleStep(1)
|
|
142
144
|
self.box_size.setValue(1)
|
|
143
145
|
self.size = 1
|
|
144
146
|
else:
|
|
145
|
-
self.
|
|
147
|
+
self.box_size.setPrefix("Set size (meter): ")
|
|
146
148
|
self.box_size.setDecimals(2)
|
|
147
149
|
self.box_size.setSingleStep(0.01)
|
|
148
150
|
self.box_size.setValue(0.01)
|
|
@@ -172,7 +174,7 @@ class CloudItem(BaseItem):
|
|
|
172
174
|
self.need_update_setting = True
|
|
173
175
|
|
|
174
176
|
def set_depthtest(self, state):
|
|
175
|
-
self.
|
|
177
|
+
self.depth_test = state
|
|
176
178
|
|
|
177
179
|
def clear(self):
|
|
178
180
|
data = np.empty((0), self.data_type)
|
|
@@ -241,13 +243,17 @@ class CloudItem(BaseItem):
|
|
|
241
243
|
self.buff = new_buff
|
|
242
244
|
|
|
243
245
|
# if exceed the maximum cloud size, randomly select half of the points
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
|
|
251
257
|
|
|
252
258
|
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
|
253
259
|
glBufferData(GL_ARRAY_BUFFER, self.buff.nbytes,
|
|
@@ -281,7 +287,7 @@ class CloudItem(BaseItem):
|
|
|
281
287
|
glEnable(GL_BLEND)
|
|
282
288
|
glEnable(GL_PROGRAM_POINT_SIZE)
|
|
283
289
|
glEnable(GL_POINT_SPRITE)
|
|
284
|
-
if self.
|
|
290
|
+
if self.depth_test:
|
|
285
291
|
glEnable(GL_DEPTH_TEST)
|
|
286
292
|
else:
|
|
287
293
|
glDisable(GL_DEPTH_TEST)
|
|
@@ -296,9 +302,9 @@ class CloudItem(BaseItem):
|
|
|
296
302
|
glEnableVertexAttribArray(0)
|
|
297
303
|
glEnableVertexAttribArray(1)
|
|
298
304
|
|
|
299
|
-
view_matrix = self.glwidget().
|
|
305
|
+
view_matrix = self.glwidget().view_matrix
|
|
300
306
|
set_uniform(self.program, view_matrix, 'view_matrix')
|
|
301
|
-
project_matrix = self.glwidget().
|
|
307
|
+
project_matrix = self.glwidget().projection_matrix
|
|
302
308
|
set_uniform(self.program, project_matrix, 'projection_matrix')
|
|
303
309
|
width = self.glwidget().current_width()
|
|
304
310
|
focal = project_matrix[0, 0] * width / 2
|
|
@@ -314,5 +320,5 @@ class CloudItem(BaseItem):
|
|
|
314
320
|
glDisable(GL_POINT_SPRITE)
|
|
315
321
|
glDisable(GL_PROGRAM_POINT_SIZE)
|
|
316
322
|
glDisable(GL_BLEND)
|
|
317
|
-
if self.
|
|
323
|
+
if self.depth_test:
|
|
318
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
|
+
|
|
@@ -35,8 +35,8 @@ class GaussianItem(BaseItem):
|
|
|
35
35
|
self.sort = self.openg_sort
|
|
36
36
|
|
|
37
37
|
def add_setting(self, layout):
|
|
38
|
-
|
|
39
|
-
layout.addWidget(
|
|
38
|
+
label_render_mode = QLabel("Render Mode:")
|
|
39
|
+
layout.addWidget(label_render_mode)
|
|
40
40
|
combo = QComboBox()
|
|
41
41
|
combo.addItem("render normal guassian")
|
|
42
42
|
combo.addItem("render ball")
|
|
@@ -168,7 +168,7 @@ class GaussianItem(BaseItem):
|
|
|
168
168
|
|
|
169
169
|
def paint(self):
|
|
170
170
|
# get current view matrix
|
|
171
|
-
self.view_matrix = self.glwidget().
|
|
171
|
+
self.view_matrix = self.glwidget().view_matrix
|
|
172
172
|
|
|
173
173
|
# if gaussian data is update, renew vao, ssbo, etc...
|
|
174
174
|
self.updateGS()
|