q3dviewer 1.0.6__py3-none-any.whl → 1.0.7__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 +14 -9
- q3dviewer/basic_window.py +228 -0
- q3dviewer/cloud_viewer.py +74 -0
- q3dviewer/custom_items/camera_frame_item.py +173 -0
- q3dviewer/custom_items/trajectory_item.py +79 -0
- q3dviewer/gau_io.py +168 -0
- q3dviewer/utils/convert_ros_msg.py +2 -0
- q3dviewer/utils.py +71 -0
- {q3dviewer-1.0.6.dist-info → q3dviewer-1.0.7.dist-info}/METADATA +15 -7
- {q3dviewer-1.0.6.dist-info → q3dviewer-1.0.7.dist-info}/RECORD +14 -9
- {q3dviewer-1.0.6.dist-info → q3dviewer-1.0.7.dist-info}/entry_points.txt +1 -0
- {q3dviewer-1.0.6.dist-info → q3dviewer-1.0.7.dist-info}/LICENSE +0 -0
- {q3dviewer-1.0.6.dist-info → q3dviewer-1.0.7.dist-info}/WHEEL +0 -0
- {q3dviewer-1.0.6.dist-info → q3dviewer-1.0.7.dist-info}/top_level.txt +0 -0
q3dviewer/base_glwidget.py
CHANGED
|
@@ -123,6 +123,7 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
123
123
|
self.show_center = True
|
|
124
124
|
|
|
125
125
|
def paintGL(self):
|
|
126
|
+
pass
|
|
126
127
|
self.update_model_projection()
|
|
127
128
|
self.update_model_view()
|
|
128
129
|
bgcolor = self.color
|
|
@@ -134,10 +135,12 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
134
135
|
glMatrixMode(GL_MODELVIEW)
|
|
135
136
|
glPushMatrix()
|
|
136
137
|
glPushAttrib(GL_ALL_ATTRIB_BITS)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
try:
|
|
139
|
+
item.paint()
|
|
140
|
+
finally:
|
|
141
|
+
glPopAttrib()
|
|
142
|
+
glMatrixMode(GL_MODELVIEW)
|
|
143
|
+
glPopMatrix()
|
|
141
144
|
|
|
142
145
|
# Show center as a point if updated by mouse move event
|
|
143
146
|
if self.enable_show_center and self.show_center:
|
|
@@ -193,12 +196,14 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
193
196
|
glLoadMatrixf(m.T)
|
|
194
197
|
|
|
195
198
|
def get_view_matrix(self):
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
two = self.center # the origin(center) in the world frame
|
|
200
|
+
tco = np.array([0, 0, self.dist]) # the origin(center) in camera frame
|
|
198
201
|
Rwc = euler_to_matrix(self.euler)
|
|
199
|
-
twc =
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
twc = two + Rwc @ tco
|
|
203
|
+
Rcw = Rwc.T
|
|
204
|
+
tcw = -Rcw @ twc
|
|
205
|
+
Tcw = makeT(Rcw, tcw)
|
|
206
|
+
return Tcw
|
|
202
207
|
|
|
203
208
|
def set_cam_position(self, **kwargs):
|
|
204
209
|
pos = kwargs.get('pos', None)
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pyqtgraph.opengl as gl
|
|
5
|
+
from pyqtgraph.Qt import QtCore
|
|
6
|
+
from PyQt5.QtWidgets import QWidget, QComboBox, QVBoxLayout, QHBoxLayout, QSizePolicy,\
|
|
7
|
+
QSpacerItem, QMainWindow
|
|
8
|
+
from OpenGL.GL import *
|
|
9
|
+
from PyQt5.QtGui import QKeyEvent, QVector3D
|
|
10
|
+
from PyQt5.QtWidgets import QApplication, QWidget
|
|
11
|
+
import numpy as np
|
|
12
|
+
import signal
|
|
13
|
+
import sys
|
|
14
|
+
from PyQt5.QtWidgets import QLabel, QLineEdit, QDoubleSpinBox, QSpinBox
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SettingWindow(QWidget):
|
|
18
|
+
def __init__(self):
|
|
19
|
+
super().__init__()
|
|
20
|
+
self.combo_items = QComboBox()
|
|
21
|
+
self.combo_items.currentIndexChanged.connect(self.onComboboxSelection)
|
|
22
|
+
main_layout = QVBoxLayout()
|
|
23
|
+
self.stretch = QSpacerItem(
|
|
24
|
+
10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
|
|
25
|
+
main_layout.addWidget(self.combo_items)
|
|
26
|
+
self.layout = QVBoxLayout()
|
|
27
|
+
self.layout.addItem(self.stretch)
|
|
28
|
+
main_layout.addLayout(self.layout)
|
|
29
|
+
self.setLayout(main_layout)
|
|
30
|
+
self.setWindowTitle("Setting Window")
|
|
31
|
+
self.setGeometry(200, 200, 300, 200)
|
|
32
|
+
self.items = {}
|
|
33
|
+
|
|
34
|
+
def addSetting(self, name, item):
|
|
35
|
+
self.items.update({name: item})
|
|
36
|
+
self.combo_items.addItem("%s(%s)" % (name, item.__class__.__name__))
|
|
37
|
+
|
|
38
|
+
def clearSetting(self):
|
|
39
|
+
while self.layout.count():
|
|
40
|
+
child = self.layout.takeAt(0)
|
|
41
|
+
if child.widget():
|
|
42
|
+
child.widget().deleteLater()
|
|
43
|
+
|
|
44
|
+
def onComboboxSelection(self, index):
|
|
45
|
+
self.layout.removeItem(self.stretch)
|
|
46
|
+
# remove all setting of previous widget
|
|
47
|
+
self.clearSetting()
|
|
48
|
+
|
|
49
|
+
key = list(self.items.keys())
|
|
50
|
+
try:
|
|
51
|
+
item = self.items[key[index]]
|
|
52
|
+
item.addSetting(self.layout)
|
|
53
|
+
self.layout.addItem(self.stretch)
|
|
54
|
+
except AttributeError:
|
|
55
|
+
print("%s: No setting." % (item.__class__.__name__))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ViewWidget(gl.GLViewWidget):
|
|
59
|
+
def __init__(self):
|
|
60
|
+
self.followed_name = 'none'
|
|
61
|
+
self.named_items = {}
|
|
62
|
+
self.color = '#000000'
|
|
63
|
+
self.followable_item_name = ['none']
|
|
64
|
+
self.setting_window = SettingWindow()
|
|
65
|
+
super(ViewWidget, self).__init__()
|
|
66
|
+
|
|
67
|
+
def onFollowableSelection(self, index):
|
|
68
|
+
self.followed_name = self.followable_item_name[index]
|
|
69
|
+
|
|
70
|
+
def update(self):
|
|
71
|
+
if self.followed_name != 'none':
|
|
72
|
+
pos = self.named_items[self.followed_name].T[:3, 3]
|
|
73
|
+
self.opts['center'] = QVector3D(pos[0], pos[1], pos[2])
|
|
74
|
+
super().update()
|
|
75
|
+
|
|
76
|
+
def addSetting(self, layout):
|
|
77
|
+
label1 = QLabel("Set background color:")
|
|
78
|
+
label1.setToolTip("using '#xxxxxx', i.e. #FF4500")
|
|
79
|
+
box1 = QLineEdit()
|
|
80
|
+
box1.setToolTip("'using '#xxxxxx', i.e. #FF4500")
|
|
81
|
+
box1.setText(str(self.color))
|
|
82
|
+
box1.textChanged.connect(self.setBKColor)
|
|
83
|
+
layout.addWidget(label1)
|
|
84
|
+
layout.addWidget(box1)
|
|
85
|
+
label2 = QLabel("Set Focus:")
|
|
86
|
+
combo2 = QComboBox()
|
|
87
|
+
for name in self.followable_item_name:
|
|
88
|
+
combo2.addItem(name)
|
|
89
|
+
combo2.currentIndexChanged.connect(self.onFollowableSelection)
|
|
90
|
+
layout.addWidget(label2)
|
|
91
|
+
layout.addWidget(combo2)
|
|
92
|
+
|
|
93
|
+
def setBKColor(self, color):
|
|
94
|
+
if (type(color) != str):
|
|
95
|
+
return
|
|
96
|
+
if color.startswith("#"):
|
|
97
|
+
try:
|
|
98
|
+
self.setBackgroundColor(color)
|
|
99
|
+
self.color = color
|
|
100
|
+
except ValueError:
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
def addItem(self, name, item):
|
|
104
|
+
self.named_items.update({name: item})
|
|
105
|
+
if (item.__class__.__name__ == 'GLAxisItem'):
|
|
106
|
+
self.followable_item_name.append(name)
|
|
107
|
+
self.setting_window.addSetting(name, item)
|
|
108
|
+
super().addItem(item)
|
|
109
|
+
|
|
110
|
+
def mouseReleaseEvent(self, ev):
|
|
111
|
+
if hasattr(self, 'mousePos'):
|
|
112
|
+
delattr(self, 'mousePos')
|
|
113
|
+
|
|
114
|
+
def mouseMoveEvent(self, ev):
|
|
115
|
+
lpos = ev.localPos()
|
|
116
|
+
if not hasattr(self, 'mousePos'):
|
|
117
|
+
self.mousePos = lpos
|
|
118
|
+
diff = lpos - self.mousePos
|
|
119
|
+
self.mousePos = lpos
|
|
120
|
+
if ev.buttons() == QtCore.Qt.MouseButton.RightButton:
|
|
121
|
+
self.orbit(-diff.x(), diff.y())
|
|
122
|
+
elif ev.buttons() == QtCore.Qt.MouseButton.LeftButton:
|
|
123
|
+
pitch_abs = np.abs(self.opts['elevation'])
|
|
124
|
+
camera_mode = 'view-upright'
|
|
125
|
+
if(pitch_abs <= 45.0 or pitch_abs == 90):
|
|
126
|
+
camera_mode = 'view'
|
|
127
|
+
self.pan(diff.x(), diff.y(), 0, relative=camera_mode)
|
|
128
|
+
|
|
129
|
+
def keyPressEvent(self, ev: QKeyEvent):
|
|
130
|
+
step = 10
|
|
131
|
+
zoom_delta = 20
|
|
132
|
+
speed = 2
|
|
133
|
+
self.projectionMatrix().data()
|
|
134
|
+
|
|
135
|
+
pitch_abs = np.abs(self.opts['elevation'])
|
|
136
|
+
camera_mode = 'view-upright'
|
|
137
|
+
if(pitch_abs <= 45.0 or pitch_abs == 90):
|
|
138
|
+
camera_mode = 'view'
|
|
139
|
+
|
|
140
|
+
if ev.key() == QtCore.Qt.Key_M: # setting meun
|
|
141
|
+
print("Open setting windows")
|
|
142
|
+
self.openSettingWindow()
|
|
143
|
+
elif ev.key() == QtCore.Qt.Key_R:
|
|
144
|
+
print("Clear viewer")
|
|
145
|
+
for item in self.named_items.values():
|
|
146
|
+
try:
|
|
147
|
+
item.clear()
|
|
148
|
+
except:
|
|
149
|
+
pass
|
|
150
|
+
elif ev.key() == QtCore.Qt.Key_Up:
|
|
151
|
+
if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
|
|
152
|
+
self.pan(0, +step, 0, relative=camera_mode)
|
|
153
|
+
else:
|
|
154
|
+
self.orbit(azim=0, elev=-speed)
|
|
155
|
+
elif ev.key() == QtCore.Qt.Key_Down:
|
|
156
|
+
if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
|
|
157
|
+
self.pan(0, -step, 0, relative=camera_mode)
|
|
158
|
+
else:
|
|
159
|
+
self.orbit(azim=0, elev=speed)
|
|
160
|
+
elif ev.key() == QtCore.Qt.Key_Left:
|
|
161
|
+
if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
|
|
162
|
+
self.pan(+step, 0, 0, relative=camera_mode)
|
|
163
|
+
else:
|
|
164
|
+
self.orbit(azim=speed, elev=0)
|
|
165
|
+
|
|
166
|
+
elif ev.key() == QtCore.Qt.Key_Right:
|
|
167
|
+
if ev.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier:
|
|
168
|
+
self.pan(-step, 0, 0, relative=camera_mode)
|
|
169
|
+
else:
|
|
170
|
+
self.orbit(azim=-speed, elev=0)
|
|
171
|
+
|
|
172
|
+
elif ev.key() == QtCore.Qt.Key_Z:
|
|
173
|
+
self.opts['distance'] *= 0.999**(+zoom_delta)
|
|
174
|
+
elif ev.key() == QtCore.Qt.Key_X:
|
|
175
|
+
self.opts['distance'] *= 0.999**(-zoom_delta)
|
|
176
|
+
else:
|
|
177
|
+
super().keyPressEvent(ev)
|
|
178
|
+
|
|
179
|
+
def openSettingWindow(self):
|
|
180
|
+
if self.setting_window.isVisible():
|
|
181
|
+
self.setting_window.raise_()
|
|
182
|
+
|
|
183
|
+
else:
|
|
184
|
+
self.setting_window.show()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class Viewer(QMainWindow):
|
|
188
|
+
def __init__(self, name='Viewer', win_size=[1920, 1080], vw=ViewWidget):
|
|
189
|
+
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
190
|
+
super(Viewer, self).__init__()
|
|
191
|
+
self.vw = vw
|
|
192
|
+
self.setGeometry(0, 0, win_size[0], win_size[1])
|
|
193
|
+
self.initUI()
|
|
194
|
+
self.setWindowTitle(name)
|
|
195
|
+
|
|
196
|
+
def initUI(self):
|
|
197
|
+
centerWidget = QWidget()
|
|
198
|
+
self.setCentralWidget(centerWidget)
|
|
199
|
+
layout = QVBoxLayout()
|
|
200
|
+
centerWidget.setLayout(layout)
|
|
201
|
+
self.viewerWidget = self.vw()
|
|
202
|
+
layout.addWidget(self.viewerWidget, 1)
|
|
203
|
+
timer = QtCore.QTimer(self)
|
|
204
|
+
timer.setInterval(20) # period, in milliseconds
|
|
205
|
+
timer.timeout.connect(self.update)
|
|
206
|
+
self.viewerWidget.setCameraPosition(distance=40)
|
|
207
|
+
timer.start()
|
|
208
|
+
|
|
209
|
+
def addItems(self, named_items: dict):
|
|
210
|
+
for name, item in named_items.items():
|
|
211
|
+
self.viewerWidget.addItem(name, item)
|
|
212
|
+
|
|
213
|
+
def __getitem__(self, name: str):
|
|
214
|
+
if name in self.viewerWidget.named_items:
|
|
215
|
+
return self.viewerWidget.named_items[name]
|
|
216
|
+
else:
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
def update(self):
|
|
220
|
+
# force update by timer
|
|
221
|
+
self.viewerWidget.update()
|
|
222
|
+
|
|
223
|
+
def closeEvent(self, _):
|
|
224
|
+
sys.exit(0)
|
|
225
|
+
|
|
226
|
+
def show(self):
|
|
227
|
+
self.viewerWidget.setting_window.addSetting("main win", self.viewerWidget)
|
|
228
|
+
super().show()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from q3dviewer.custom_items import *
|
|
5
|
+
from q3dviewer.basic_window import *
|
|
6
|
+
from pypcd4 import PointCloud
|
|
7
|
+
|
|
8
|
+
class CloudViewer(Viewer):
|
|
9
|
+
def __init__(self):
|
|
10
|
+
super(CloudViewer, self).__init__(name="Cloud Viewer")
|
|
11
|
+
self.setAcceptDrops(True)
|
|
12
|
+
|
|
13
|
+
def dragEnterEvent(self, event):
|
|
14
|
+
if event.mimeData().hasUrls():
|
|
15
|
+
event.accept()
|
|
16
|
+
else:
|
|
17
|
+
event.ignore()
|
|
18
|
+
|
|
19
|
+
def dropEvent(self, event):
|
|
20
|
+
for url in event.mimeData().urls():
|
|
21
|
+
file_path = url.toLocalFile()
|
|
22
|
+
self.openCloudFile(file_path)
|
|
23
|
+
|
|
24
|
+
def openCloudFile(self, file):
|
|
25
|
+
cloud_item = self['cloud']
|
|
26
|
+
if cloud_item is None:
|
|
27
|
+
print("Can't find clouditem.")
|
|
28
|
+
return
|
|
29
|
+
if file.endswith('.pcd'):
|
|
30
|
+
pc = PointCloud.from_path(file).pc_data
|
|
31
|
+
if 'rgb' in pc.dtype.names:
|
|
32
|
+
color = pc["rgb"].astype(np.uint32)
|
|
33
|
+
cloud_item.setColorMode('RGB')
|
|
34
|
+
elif 'intensity' in pc.dtype.names:
|
|
35
|
+
color = pc["intensity"].astype(np.uint32)
|
|
36
|
+
cloud_item.setColorMode('I')
|
|
37
|
+
else:
|
|
38
|
+
color = pc['z'].astype(np.uint32)
|
|
39
|
+
cloud_item.setColorMode('#FFFFFF')
|
|
40
|
+
cloud = np.rec.fromarrays(
|
|
41
|
+
[np.stack([pc["x"], pc["y"], pc["z"]], axis=1), color],
|
|
42
|
+
dtype=cloud_item.data_type)
|
|
43
|
+
elif file.endswith('.npy'):
|
|
44
|
+
pc = np.load(file)
|
|
45
|
+
cloud = np.rec.fromarrays(
|
|
46
|
+
[np.stack([pc[:, 0], pc[:, 1], pc[:, 2]], axis=1), pc[:, 3].astype(np.uint32)],
|
|
47
|
+
dtype=cloud_item.data_type)
|
|
48
|
+
cloud_item.setData(data=cloud)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
import argparse
|
|
53
|
+
parser = argparse.ArgumentParser()
|
|
54
|
+
parser.add_argument("--pcd", help="the pcd path")
|
|
55
|
+
args = parser.parse_args()
|
|
56
|
+
app = QApplication([])
|
|
57
|
+
viewer = CloudViewer()
|
|
58
|
+
cloud_item = CloudIOItem(size=1, alpha=0.1)
|
|
59
|
+
axis_item = GLAxisItem(size=0.5, width=5)
|
|
60
|
+
gird_item = GridItem(size=1000, spacing=20)
|
|
61
|
+
# viewer.viewerWidget.setBackgroundColor(255, 255, 255, 255)
|
|
62
|
+
|
|
63
|
+
viewer.addItems({'cloud': cloud_item, 'grid': gird_item, 'axis': axis_item})
|
|
64
|
+
|
|
65
|
+
if args.pcd:
|
|
66
|
+
pcd_fn = args.pcd
|
|
67
|
+
viewer.openCloudFile(pcd_fn)
|
|
68
|
+
|
|
69
|
+
viewer.show()
|
|
70
|
+
app.exec_()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == '__main__':
|
|
74
|
+
main()
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import pyqtgraph.opengl as gl
|
|
2
|
+
from OpenGL.GL import *
|
|
3
|
+
import numpy as np
|
|
4
|
+
from OpenGL.GL import shaders
|
|
5
|
+
from PIL import Image
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Vertex and Fragment shader source code
|
|
9
|
+
vertex_shader_source = """
|
|
10
|
+
#version 330 core
|
|
11
|
+
layout(location = 0) in vec3 position;
|
|
12
|
+
layout(location = 1) in vec2 texCoord;
|
|
13
|
+
|
|
14
|
+
out vec2 TexCoord;
|
|
15
|
+
|
|
16
|
+
uniform mat4 view_matrix;
|
|
17
|
+
uniform mat4 project_matrix;
|
|
18
|
+
|
|
19
|
+
void main()
|
|
20
|
+
{
|
|
21
|
+
gl_Position = project_matrix * view_matrix * vec4(position, 1.0);
|
|
22
|
+
TexCoord = texCoord;
|
|
23
|
+
}
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
fragment_shader_source = """
|
|
27
|
+
#version 330 core
|
|
28
|
+
in vec2 TexCoord;
|
|
29
|
+
out vec4 color;
|
|
30
|
+
uniform sampler2D ourTexture;
|
|
31
|
+
void main()
|
|
32
|
+
{
|
|
33
|
+
color = texture(ourTexture, TexCoord);
|
|
34
|
+
}
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def set_uniform_mat4(shader, content, name):
|
|
39
|
+
content = content.T
|
|
40
|
+
glUniformMatrix4fv(
|
|
41
|
+
glGetUniformLocation(shader, name),
|
|
42
|
+
1,
|
|
43
|
+
GL_FALSE,
|
|
44
|
+
content.astype(np.float32)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class GLCameraFrameItem(gl.GLGraphicsItem.GLGraphicsItem):
|
|
49
|
+
def __init__(self, T=np.eye(4), size=1, width=3, path=None):
|
|
50
|
+
gl.GLGraphicsItem.GLGraphicsItem.__init__(self)
|
|
51
|
+
self.size = size
|
|
52
|
+
self.width = width
|
|
53
|
+
self.T = T
|
|
54
|
+
self.path = path
|
|
55
|
+
|
|
56
|
+
def initializeGL(self):
|
|
57
|
+
# Rectangle vertices and texture coordinates
|
|
58
|
+
hsize = self.size / 2
|
|
59
|
+
self.vertices = np.array([
|
|
60
|
+
# positions # texture coords
|
|
61
|
+
[-hsize, -hsize, 0.0, 0.0, 0.0], # bottom-left
|
|
62
|
+
[hsize, -hsize, 0.0, 1.0, 0.0], # bottom-right
|
|
63
|
+
[hsize, hsize, 0.0, 1.0, 1.0], # top-right
|
|
64
|
+
[-hsize, hsize, 0.0, 0.0, 1.0], # top-left
|
|
65
|
+
[0, 0, -hsize * 0.66, 0.0, 0.0], # top-left
|
|
66
|
+
], dtype=np.float32)
|
|
67
|
+
|
|
68
|
+
R = self.T[:3, :3]
|
|
69
|
+
t = self.T[:3, 3]
|
|
70
|
+
self.vertices[:, :3] = (
|
|
71
|
+
R @ self.vertices[:, :3].T + t[:, np.newaxis]).T
|
|
72
|
+
|
|
73
|
+
self.focal_p = np.array([0, 0, hsize * 0.66])
|
|
74
|
+
|
|
75
|
+
indices = np.array([
|
|
76
|
+
0, 1, 2, # first triangle
|
|
77
|
+
2, 3, 0 # second triangle
|
|
78
|
+
], dtype=np.uint32)
|
|
79
|
+
|
|
80
|
+
self.vao = glGenVertexArrays(1)
|
|
81
|
+
vbo = glGenBuffers(1)
|
|
82
|
+
ebo = glGenBuffers(1)
|
|
83
|
+
|
|
84
|
+
glBindVertexArray(self.vao)
|
|
85
|
+
|
|
86
|
+
glBindBuffer(GL_ARRAY_BUFFER, vbo)
|
|
87
|
+
glBufferData(GL_ARRAY_BUFFER, self.vertices.itemsize *
|
|
88
|
+
5 * 4, self.vertices, GL_STATIC_DRAW)
|
|
89
|
+
|
|
90
|
+
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
|
|
91
|
+
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
|
92
|
+
indices.nbytes, indices, GL_STATIC_DRAW)
|
|
93
|
+
|
|
94
|
+
# Vertex positions
|
|
95
|
+
|
|
96
|
+
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
|
|
97
|
+
20, ctypes.c_void_p(0))
|
|
98
|
+
glEnableVertexAttribArray(0)
|
|
99
|
+
# Texture coordinates
|
|
100
|
+
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
|
|
101
|
+
20, ctypes.c_void_p(12))
|
|
102
|
+
glEnableVertexAttribArray(1)
|
|
103
|
+
|
|
104
|
+
project_matrix = np.array(self._GLGraphicsItem__view.projectionMatrix().data(),
|
|
105
|
+
np.float32).reshape([4, 4]).T
|
|
106
|
+
# Compile shaders and create shader program
|
|
107
|
+
self.program = shaders.compileProgram(
|
|
108
|
+
shaders.compileShader(vertex_shader_source, GL_VERTEX_SHADER),
|
|
109
|
+
shaders.compileShader(fragment_shader_source, GL_FRAGMENT_SHADER),
|
|
110
|
+
)
|
|
111
|
+
glUseProgram(self.program)
|
|
112
|
+
set_uniform_mat4(self.program, project_matrix, 'project_matrix')
|
|
113
|
+
glUseProgram(0)
|
|
114
|
+
|
|
115
|
+
self.texture = glGenTextures(1)
|
|
116
|
+
glBindTexture(GL_TEXTURE_2D, self.texture)
|
|
117
|
+
|
|
118
|
+
# Load image
|
|
119
|
+
image = Image.open(self.path)
|
|
120
|
+
# image = image.transpose(Image.FLIP_TOP_BOTTOM)
|
|
121
|
+
img_data = image.convert("RGBA").tobytes()
|
|
122
|
+
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width,
|
|
123
|
+
image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data)
|
|
124
|
+
glGenerateMipmap(GL_TEXTURE_2D)
|
|
125
|
+
glBindTexture(GL_TEXTURE_2D, 0)
|
|
126
|
+
glBindVertexArray(0)
|
|
127
|
+
|
|
128
|
+
def setTransform(self, T):
|
|
129
|
+
self.T = T
|
|
130
|
+
|
|
131
|
+
def paint(self):
|
|
132
|
+
self.view_matrix = np.array(
|
|
133
|
+
self._GLGraphicsItem__view.viewMatrix().data(), np.float32).reshape([4, 4]).T
|
|
134
|
+
project_matrix = np.array(self._GLGraphicsItem__view.projectionMatrix(
|
|
135
|
+
).data(), np.float32).reshape([4, 4]).T
|
|
136
|
+
|
|
137
|
+
glEnable(GL_DEPTH_TEST)
|
|
138
|
+
glEnable(GL_BLEND)
|
|
139
|
+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
140
|
+
|
|
141
|
+
glUseProgram(self.program)
|
|
142
|
+
set_uniform_mat4(self.program, self.view_matrix, 'view_matrix')
|
|
143
|
+
set_uniform_mat4(self.program, project_matrix, 'project_matrix')
|
|
144
|
+
glBindVertexArray(self.vao)
|
|
145
|
+
glBindTexture(GL_TEXTURE_2D, self.texture)
|
|
146
|
+
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
|
|
147
|
+
glBindTexture(GL_TEXTURE_2D, 0)
|
|
148
|
+
glBindVertexArray(0)
|
|
149
|
+
glUseProgram(0)
|
|
150
|
+
|
|
151
|
+
glLineWidth(self.width)
|
|
152
|
+
glBegin(GL_LINES)
|
|
153
|
+
glColor4f(1, 1, 1, 1) # z is blue
|
|
154
|
+
glVertex3f(*self.vertices[0, :3])
|
|
155
|
+
glVertex3f(*self.vertices[1, :3])
|
|
156
|
+
glVertex3f(*self.vertices[1, :3])
|
|
157
|
+
glVertex3f(*self.vertices[2, :3])
|
|
158
|
+
glVertex3f(*self.vertices[2, :3])
|
|
159
|
+
glVertex3f(*self.vertices[3, :3])
|
|
160
|
+
glVertex3f(*self.vertices[3, :3])
|
|
161
|
+
glVertex3f(*self.vertices[0, :3])
|
|
162
|
+
glVertex3f(*self.vertices[4, :3])
|
|
163
|
+
glVertex3f(*self.vertices[0, :3])
|
|
164
|
+
glVertex3f(*self.vertices[4, :3])
|
|
165
|
+
glVertex3f(*self.vertices[1, :3])
|
|
166
|
+
glVertex3f(*self.vertices[4, :3])
|
|
167
|
+
glVertex3f(*self.vertices[2, :3])
|
|
168
|
+
glVertex3f(*self.vertices[4, :3])
|
|
169
|
+
glVertex3f(*self.vertices[3, :3])
|
|
170
|
+
glEnd()
|
|
171
|
+
|
|
172
|
+
glDisable(GL_DEPTH_TEST)
|
|
173
|
+
glDisable(GL_BLEND)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import pyqtgraph.opengl as gl
|
|
2
|
+
from OpenGL.GL import *
|
|
3
|
+
import numpy as np
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TrajectoryItem(gl.GLGridItem):
|
|
8
|
+
def __init__(self, width=1, color=(0, 1, 0, 1)):
|
|
9
|
+
super(TrajectoryItem, self).__init__()
|
|
10
|
+
self.width = width
|
|
11
|
+
self.buff = np.empty((0, 3), np.float32)
|
|
12
|
+
self.wait_add_data = None
|
|
13
|
+
self.mutex = threading.Lock()
|
|
14
|
+
self.CAPACITY = 100000
|
|
15
|
+
self.valid_buff_top = 0
|
|
16
|
+
self.color = color
|
|
17
|
+
|
|
18
|
+
def addSetting(self, layout):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def setData(self, data, append=True):
|
|
22
|
+
self.mutex.acquire()
|
|
23
|
+
data = data.astype(np.float32).reshape(-1, 3)
|
|
24
|
+
if (append is False):
|
|
25
|
+
self.wait_add_data = data
|
|
26
|
+
self.add_buff_loc = 0
|
|
27
|
+
else:
|
|
28
|
+
if (self.wait_add_data is None):
|
|
29
|
+
self.wait_add_data = data
|
|
30
|
+
else:
|
|
31
|
+
self.wait_add_data = np.concatenate([self.wait_add_data, data])
|
|
32
|
+
self.add_buff_loc = self.valid_buff_top
|
|
33
|
+
self.mutex.release()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def updateRenderBuffer(self):
|
|
37
|
+
if(self.wait_add_data is None):
|
|
38
|
+
return
|
|
39
|
+
self.mutex.acquire()
|
|
40
|
+
|
|
41
|
+
new_buff_top = self.add_buff_loc + self.wait_add_data.shape[0]
|
|
42
|
+
if new_buff_top > self.buff.shape[0]:
|
|
43
|
+
buff_capacity = self.buff.shape[0]
|
|
44
|
+
while (new_buff_top > buff_capacity):
|
|
45
|
+
buff_capacity += self.CAPACITY
|
|
46
|
+
self.buff = np.empty((buff_capacity, 3), np.float32)
|
|
47
|
+
self.buff[self.add_buff_loc:new_buff_top] = self.wait_add_data
|
|
48
|
+
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
|
49
|
+
glBufferData(GL_ARRAY_BUFFER, self.buff.nbytes, self.buff, GL_DYNAMIC_DRAW)
|
|
50
|
+
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
|
51
|
+
else:
|
|
52
|
+
self.buff[self.add_buff_loc:new_buff_top] = self.wait_add_data
|
|
53
|
+
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
|
54
|
+
glBufferSubData(GL_ARRAY_BUFFER, self.add_buff_loc * 12,
|
|
55
|
+
self.wait_add_data.shape[0] * 12, self.wait_add_data)
|
|
56
|
+
self.valid_buff_top = new_buff_top
|
|
57
|
+
self.wait_add_data = None
|
|
58
|
+
self.mutex.release()
|
|
59
|
+
|
|
60
|
+
def initializeGL(self):
|
|
61
|
+
self.vbo = glGenBuffers(1)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def paint(self):
|
|
65
|
+
self.setupGLState()
|
|
66
|
+
self.updateRenderBuffer()
|
|
67
|
+
glEnable(GL_BLEND)
|
|
68
|
+
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
|
69
|
+
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
|
|
70
|
+
glEnableClientState(GL_VERTEX_ARRAY)
|
|
71
|
+
glVertexPointer(3, GL_FLOAT, 0, None)
|
|
72
|
+
glLineWidth(self.width)
|
|
73
|
+
glColor4f(*self.color) # z is blue
|
|
74
|
+
|
|
75
|
+
glDrawArrays(GL_LINE_STRIP, 0, self.valid_buff_top)
|
|
76
|
+
glDisableClientState(GL_VERTEX_ARRAY)
|
|
77
|
+
|
|
78
|
+
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
|
79
|
+
glUseProgram(0)
|
q3dviewer/gau_io.py
CHANGED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from plyfile import PlyData
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def gsdata_type(sh_dim):
|
|
6
|
+
return [('pw', '<f4', (3,)),
|
|
7
|
+
('rot', '<f4', (4,)),
|
|
8
|
+
('scale', '<f4', (3,)),
|
|
9
|
+
('alpha', '<f4'),
|
|
10
|
+
('sh', '<f4', (sh_dim))]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def matrix_to_quaternion(matrices):
|
|
14
|
+
m00, m01, m02 = matrices[:, 0, 0], matrices[:, 0, 1], matrices[:, 0, 2]
|
|
15
|
+
m10, m11, m12 = matrices[:, 1, 0], matrices[:, 1, 1], matrices[:, 1, 2]
|
|
16
|
+
m20, m21, m22 = matrices[:, 2, 0], matrices[:, 2, 1], matrices[:, 2, 2]
|
|
17
|
+
t = 1 + m00 + m11 + m22
|
|
18
|
+
s = np.ones_like(m00)
|
|
19
|
+
w = np.ones_like(m00)
|
|
20
|
+
x = np.ones_like(m00)
|
|
21
|
+
y = np.ones_like(m00)
|
|
22
|
+
z = np.ones_like(m00)
|
|
23
|
+
|
|
24
|
+
t_positive = t > 0.0000001
|
|
25
|
+
s[t_positive] = 0.5 / np.sqrt(t[t_positive])
|
|
26
|
+
w[t_positive] = 0.25 / s[t_positive]
|
|
27
|
+
x[t_positive] = (m21[t_positive] - m12[t_positive]) * s[t_positive]
|
|
28
|
+
y[t_positive] = (m02[t_positive] - m20[t_positive]) * s[t_positive]
|
|
29
|
+
z[t_positive] = (m10[t_positive] - m01[t_positive]) * s[t_positive]
|
|
30
|
+
|
|
31
|
+
c1 = np.logical_and(m00 > m11, m00 > m22)
|
|
32
|
+
cond1 = np.logical_and(np.logical_not(t_positive), np.logical_and(m00 > m11, m00 > m22))
|
|
33
|
+
|
|
34
|
+
s[cond1] = 2.0 * np.sqrt(1.0 + m00[cond1] - m11[cond1] - m22[cond1])
|
|
35
|
+
w[cond1] = (m21[cond1] - m12[cond1]) / s[cond1]
|
|
36
|
+
x[cond1] = 0.25 * s[cond1]
|
|
37
|
+
y[cond1] = (m01[cond1] + m10[cond1]) / s[cond1]
|
|
38
|
+
z[cond1] = (m02[cond1] + m20[cond1]) / s[cond1]
|
|
39
|
+
|
|
40
|
+
c2 = np.logical_and(np.logical_not(c1), m11 > m22)
|
|
41
|
+
cond2 = np.logical_and(np.logical_not(t_positive), c2)
|
|
42
|
+
s[cond2] = 2.0 * np.sqrt(1.0 + m11[cond2] - m00[cond2] - m22[cond2])
|
|
43
|
+
w[cond2] = (m02[cond2] - m20[cond2]) / s[cond2]
|
|
44
|
+
x[cond2] = (m01[cond2] + m10[cond2]) / s[cond2]
|
|
45
|
+
y[cond2] = 0.25 * s[cond2]
|
|
46
|
+
z[cond2] = (m12[cond2] + m21[cond2]) / s[cond2]
|
|
47
|
+
|
|
48
|
+
c3 = np.logical_and(np.logical_not(c1), np.logical_not(c2))
|
|
49
|
+
cond3 = np.logical_and(np.logical_not(t_positive), c3)
|
|
50
|
+
s[cond3] = 2.0 * np.sqrt(1.0 + m22[cond3] - m00[cond3] - m11[cond3])
|
|
51
|
+
w[cond3] = (m10[cond3] - m01[cond3]) / s[cond3]
|
|
52
|
+
x[cond3] = (m02[cond3] + m20[cond3]) / s[cond3]
|
|
53
|
+
y[cond3] = (m12[cond3] + m21[cond3]) / s[cond3]
|
|
54
|
+
z[cond3] = 0.25 * s[cond3]
|
|
55
|
+
return np.array([w, x, y, z]).T
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def load_ply(path, T=None):
|
|
59
|
+
plydata = PlyData.read(path)
|
|
60
|
+
pws = np.stack((np.asarray(plydata.elements[0]["x"]),
|
|
61
|
+
np.asarray(plydata.elements[0]["y"]),
|
|
62
|
+
np.asarray(plydata.elements[0]["z"])), axis=1)
|
|
63
|
+
|
|
64
|
+
alphas = np.asarray(plydata.elements[0]["opacity"])
|
|
65
|
+
alphas = 1/(1 + np.exp(-alphas))
|
|
66
|
+
|
|
67
|
+
scales = np.stack((np.asarray(plydata.elements[0]["scale_0"]),
|
|
68
|
+
np.asarray(plydata.elements[0]["scale_1"]),
|
|
69
|
+
np.asarray(plydata.elements[0]["scale_2"])), axis=1)
|
|
70
|
+
|
|
71
|
+
rots = np.stack((np.asarray(plydata.elements[0]["rot_0"]),
|
|
72
|
+
np.asarray(plydata.elements[0]["rot_1"]),
|
|
73
|
+
np.asarray(plydata.elements[0]["rot_2"]),
|
|
74
|
+
np.asarray(plydata.elements[0]["rot_3"])), axis=1)
|
|
75
|
+
|
|
76
|
+
rots /= np.linalg.norm(rots, axis=1)[:, np.newaxis]
|
|
77
|
+
|
|
78
|
+
sh_dim = len(plydata.elements[0][0])-14
|
|
79
|
+
shs = np.zeros([pws.shape[0], sh_dim])
|
|
80
|
+
shs[:, 0] = np.asarray(plydata.elements[0]["f_dc_0"])
|
|
81
|
+
shs[:, 1] = np.asarray(plydata.elements[0]["f_dc_1"])
|
|
82
|
+
shs[:, 2] = np.asarray(plydata.elements[0]["f_dc_2"])
|
|
83
|
+
|
|
84
|
+
sh_rest_dim = sh_dim - 3
|
|
85
|
+
for i in range(sh_rest_dim):
|
|
86
|
+
name = "f_rest_%d" % i
|
|
87
|
+
shs[:, 3 + i] = np.asarray(plydata.elements[0][name])
|
|
88
|
+
|
|
89
|
+
shs[:, 3:] = shs[:, 3:].reshape(-1, 3, sh_rest_dim//3).transpose([0, 2, 1]).reshape(-1, sh_rest_dim)
|
|
90
|
+
|
|
91
|
+
pws = pws.astype(np.float32)
|
|
92
|
+
rots = rots.astype(np.float32)
|
|
93
|
+
scales = np.exp(scales)
|
|
94
|
+
scales = scales.astype(np.float32)
|
|
95
|
+
alphas = alphas.astype(np.float32)
|
|
96
|
+
shs = shs.astype(np.float32)
|
|
97
|
+
|
|
98
|
+
dtypes = gsdata_type(sh_dim)
|
|
99
|
+
|
|
100
|
+
gs = np.rec.fromarrays(
|
|
101
|
+
[pws, rots, scales, alphas, shs], dtype=dtypes)
|
|
102
|
+
|
|
103
|
+
return gs
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def rotate_gaussian(T, gs):
|
|
107
|
+
# Transform to world
|
|
108
|
+
pws = (T @ gs['pw'].T).T
|
|
109
|
+
w = gs['rot'][:, 0]
|
|
110
|
+
x = gs['rot'][:, 1]
|
|
111
|
+
y = gs['rot'][:, 2]
|
|
112
|
+
z = gs['rot'][:, 3]
|
|
113
|
+
R = np.array([
|
|
114
|
+
[1.0 - 2*(y**2 + z**2), 2*(x*y - z*w), 2*(x * z + y * w)],
|
|
115
|
+
[2*(x*y + z*w), 1.0 - 2*(x**2 + z**2), 2*(y*z - x*w)],
|
|
116
|
+
[2*(x*z - y*w), 2*(y*z + x*w), 1.0 - 2*(x**2 + y**2)]
|
|
117
|
+
]).transpose(2, 0, 1)
|
|
118
|
+
R_new = T @ R
|
|
119
|
+
rots = matrix_to_quaternion(R_new)
|
|
120
|
+
gs['pw'] = pws
|
|
121
|
+
gs['rot'] = rots
|
|
122
|
+
return gs
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def load_gs(fn):
|
|
126
|
+
if fn.endswith('.ply'):
|
|
127
|
+
return load_ply(fn)
|
|
128
|
+
elif fn.endswith('.npy'):
|
|
129
|
+
return np.load(fn)
|
|
130
|
+
else:
|
|
131
|
+
print("%s is not a supported file." % fn)
|
|
132
|
+
exit(0)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def save_gs(fn, gs):
|
|
136
|
+
np.save(fn, gs)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_example_gs():
|
|
140
|
+
gs_data = np.array([[0., 0., 0., # xyz
|
|
141
|
+
1., 0., 0., 0., # rot
|
|
142
|
+
0.05, 0.05, 0.05, # size
|
|
143
|
+
1.,
|
|
144
|
+
1.772484, -1.772484, 1.772484],
|
|
145
|
+
[1., 0., 0.,
|
|
146
|
+
1., 0., 0., 0.,
|
|
147
|
+
0.2, 0.05, 0.05,
|
|
148
|
+
1.,
|
|
149
|
+
1.772484, -1.772484, -1.772484],
|
|
150
|
+
[0., 1., 0.,
|
|
151
|
+
1., 0., 0., 0.,
|
|
152
|
+
0.05, 0.2, 0.05,
|
|
153
|
+
1.,
|
|
154
|
+
-1.772484, 1.772484, -1.772484],
|
|
155
|
+
[0., 0., 1.,
|
|
156
|
+
1., 0., 0., 0.,
|
|
157
|
+
0.05, 0.05, 0.2,
|
|
158
|
+
1.,
|
|
159
|
+
-1.772484, -1.772484, 1.772484]
|
|
160
|
+
], dtype=np.float32)
|
|
161
|
+
dtypes = gsdata_type(3)
|
|
162
|
+
gs = np.frombuffer(gs_data.tobytes(), dtype=dtypes)
|
|
163
|
+
return gs
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
if __name__ == "__main__":
|
|
167
|
+
gs = load_gs("/home/liu/workspace/EasyGaussianSplatting/data/final.npy")
|
|
168
|
+
print(gs.shape)
|
|
@@ -26,6 +26,7 @@ def convert_pointcloud2_msg(msg):
|
|
|
26
26
|
stamp = msg.header.stamp.to_sec()
|
|
27
27
|
return cloud, fields, stamp
|
|
28
28
|
|
|
29
|
+
|
|
29
30
|
def convert_odometry_msg(msg):
|
|
30
31
|
pose = np.array(
|
|
31
32
|
[msg.pose.pose.position.x,
|
|
@@ -40,6 +41,7 @@ def convert_odometry_msg(msg):
|
|
|
40
41
|
stamp = msg.header.stamp.to_sec()
|
|
41
42
|
return transform, stamp
|
|
42
43
|
|
|
44
|
+
|
|
43
45
|
def convert_image_msg(msg):
|
|
44
46
|
image = np.frombuffer(msg.data, dtype=np.uint8).reshape(
|
|
45
47
|
msg.height, msg.width, -1)
|
q3dviewer/utils.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def matrix_to_quaternion(matrix):
|
|
6
|
+
trace = matrix[0, 0] + matrix[1, 1] + matrix[2, 2]
|
|
7
|
+
if trace > 0:
|
|
8
|
+
s = 0.5 / np.sqrt(trace + 1.0)
|
|
9
|
+
w = 0.25 / s
|
|
10
|
+
x = (matrix[2, 1] - matrix[1, 2]) * s
|
|
11
|
+
y = (matrix[0, 2] - matrix[2, 0]) * s
|
|
12
|
+
z = (matrix[1, 0] - matrix[0, 1]) * s
|
|
13
|
+
else:
|
|
14
|
+
if matrix[0, 0] > matrix[1, 1] and matrix[0, 0] > matrix[2, 2]:
|
|
15
|
+
s = 2.0 * np.sqrt(1.0 + matrix[0, 0] - matrix[1, 1] - matrix[2, 2])
|
|
16
|
+
w = (matrix[2, 1] - matrix[1, 2]) / s
|
|
17
|
+
x = 0.25 * s
|
|
18
|
+
y = (matrix[0, 1] + matrix[1, 0]) / s
|
|
19
|
+
z = (matrix[0, 2] + matrix[2, 0]) / s
|
|
20
|
+
elif matrix[1, 1] > matrix[2, 2]:
|
|
21
|
+
s = 2.0 * np.sqrt(1.0 + matrix[1, 1] - matrix[0, 0] - matrix[2, 2])
|
|
22
|
+
w = (matrix[0, 2] - matrix[2, 0]) / s
|
|
23
|
+
x = (matrix[0, 1] + matrix[1, 0]) / s
|
|
24
|
+
y = 0.25 * s
|
|
25
|
+
z = (matrix[1, 2] + matrix[2, 1]) / s
|
|
26
|
+
else:
|
|
27
|
+
s = 2.0 * np.sqrt(1.0 + matrix[2, 2] - matrix[0, 0] - matrix[1, 1])
|
|
28
|
+
w = (matrix[1, 0] - matrix[0, 1]) / s
|
|
29
|
+
x = (matrix[0, 2] + matrix[2, 0]) / s
|
|
30
|
+
y = (matrix[1, 2] + matrix[2, 1]) / s
|
|
31
|
+
z = 0.25 * s
|
|
32
|
+
return np.array([w, x, y, z])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def quaternion_to_matrix(quaternion):
|
|
36
|
+
_EPS = np.finfo(float).eps * 4.0
|
|
37
|
+
q = np.array(quaternion[:4], dtype=np.float64, copy=True)
|
|
38
|
+
nq = np.dot(q, q)
|
|
39
|
+
if nq < _EPS:
|
|
40
|
+
return np.identity(4)
|
|
41
|
+
q *= np.sqrt(2.0 / nq)
|
|
42
|
+
q = np.outer(q, q)
|
|
43
|
+
return np.array((
|
|
44
|
+
(1.0-q[1, 1]-q[2, 2], q[0, 1]-q[2, 3], q[0, 2]+q[1, 3], 0.0),
|
|
45
|
+
(q[0, 1]+q[2, 3], 1.0-q[0, 0]-q[2, 2], q[1, 2]-q[0, 3], 0.0),
|
|
46
|
+
(q[0, 2]-q[1, 3], q[1, 2]+q[0, 3], 1.0-q[0, 0]-q[1, 1], 0.0),
|
|
47
|
+
(0.0, 0.0, 0.0, 1.0)
|
|
48
|
+
), dtype=np.float64)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def make_transform(pose, rotation):
|
|
52
|
+
transform = np.matrix(np.identity(4, dtype=np.float64))
|
|
53
|
+
transform = quaternion_to_matrix(rotation)
|
|
54
|
+
transform[0:3, 3] = np.transpose(pose)
|
|
55
|
+
return transform
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class FPSMonitor():
|
|
59
|
+
def __init__(self):
|
|
60
|
+
self.stamp_record = []
|
|
61
|
+
|
|
62
|
+
def count(self):
|
|
63
|
+
cur_stamp = time.time()
|
|
64
|
+
self.stamp_record.append(cur_stamp)
|
|
65
|
+
while len(self.stamp_record) > 0:
|
|
66
|
+
if(cur_stamp - self.stamp_record[0] > 1.):
|
|
67
|
+
self.stamp_record.pop(0)
|
|
68
|
+
else:
|
|
69
|
+
break
|
|
70
|
+
return len(self.stamp_record)
|
|
71
|
+
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: q3dviewer
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Version: 1.0.7
|
|
4
|
+
Summary: UNKNOWN
|
|
5
|
+
Home-page: UNKNOWN
|
|
6
|
+
Author: UNKNOWN
|
|
7
|
+
Author-email: UNKNOWN
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
7
10
|
Requires-Dist: PyOpenGL
|
|
8
|
-
Requires-Dist:
|
|
11
|
+
Requires-Dist: laspy
|
|
9
12
|
Requires-Dist: meshio
|
|
10
|
-
Requires-Dist:
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
Requires-Dist: pillow
|
|
11
15
|
Requires-Dist: pye57
|
|
12
|
-
Requires-Dist:
|
|
16
|
+
Requires-Dist: pypcd4
|
|
17
|
+
Requires-Dist: pyside6
|
|
18
|
+
|
|
19
|
+
UNKNOWN
|
|
20
|
+
|
|
13
21
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
q3dviewer/__init__.py,sha256=rP5XX_x8g7hxIMqNHlU89BN4dt5MSvoYYwip68fCmhc,173
|
|
2
|
-
q3dviewer/base_glwidget.py,sha256=
|
|
2
|
+
q3dviewer/base_glwidget.py,sha256=i1GOuPJMd_HeOu6-Y0mMe73tM4U0z1gdM-XOJszXGnM,8916
|
|
3
3
|
q3dviewer/base_item.py,sha256=-6GogOKrjVRSTszio9ELPDhehBaUlTyNaag2bUl81l8,1337
|
|
4
|
-
q3dviewer/
|
|
4
|
+
q3dviewer/basic_window.py,sha256=CFErOPRMysFcCqq3vhDsQ-xZzLArO3m1yABCTIhq5do,7946
|
|
5
|
+
q3dviewer/cloud_viewer.py,sha256=IxxrB6Sl6aPCr9P9QzKHGkMcDP_DjsBWbkmIbsAoIM4,2358
|
|
6
|
+
q3dviewer/gau_io.py,sha256=S6NmqL5vSMgHVxKR-eu4CWCqgZeWBJKYRoOMAwr8Xbo,5890
|
|
5
7
|
q3dviewer/glwidget.py,sha256=ppkAJUCCJ4pnLX3j6jCFmJw8O-kCr7UphsEdkGSakpQ,4832
|
|
8
|
+
q3dviewer/utils.py,sha256=evF0d-v17hbTmquC24fmMIp9CsXpUnSQZr4MVy2sfao,2426
|
|
6
9
|
q3dviewer/viewer.py,sha256=Rv_YMo-N2kibh4Bnaz0g32hEtNyn8WXRmoh7GTSPcJg,1708
|
|
7
10
|
q3dviewer/custom_items/__init__.py,sha256=gOiAxdjDaAnFL8YbqSEWWWOwUrJfvzP9JLR34sCB9-4,434
|
|
8
11
|
q3dviewer/custom_items/axis_item.py,sha256=6dWGqBeA3DL-9xmsxdmTSIGLs6BcRbiysBKkkXVb5oI,4456
|
|
12
|
+
q3dviewer/custom_items/camera_frame_item.py,sha256=VBsr3Avly_YWXViIh4DJkGc_HJt227GeOYLpGtbYTOw,5605
|
|
9
13
|
q3dviewer/custom_items/cloud_io_item.py,sha256=gjK3n9WKB7JwxC93ijkweEHA5EezpgNJ8KO-PBaDKCs,2835
|
|
10
14
|
q3dviewer/custom_items/cloud_item.py,sha256=u9hHHFjK4Ls6ssFVbBFqdR2xg0QNmgsKOz9jGHSqrt8,12444
|
|
11
15
|
q3dviewer/custom_items/frame_item.py,sha256=YTBOEeRWh95trhAlKoZ9LZ4I2x-A5JjOhNPp4OvD5E0,6983
|
|
@@ -14,6 +18,7 @@ q3dviewer/custom_items/grid_item.py,sha256=U4nk16d5Dkbt6U9s23c9PIlG-wPwu15Ho0Hsa
|
|
|
14
18
|
q3dviewer/custom_items/image_item.py,sha256=tJryUj5qxaGx1j7W0lbzx-JJmehMPSEqJXPnSkG4FOI,5529
|
|
15
19
|
q3dviewer/custom_items/line_item.py,sha256=ujl78wBlvcotghauAy7ILTa22P_fdrFzrH6RD4TvY3M,4401
|
|
16
20
|
q3dviewer/custom_items/text_item.py,sha256=nuHMVMQrwy50lNk9hxB94criFxbJJK-SYiK2fSXWUMQ,2158
|
|
21
|
+
q3dviewer/custom_items/trajectory_item.py,sha256=uoKQSrTs_m_m1M8iNAm3peiXnZ9uVPsYQLYas3Gksjg,2754
|
|
17
22
|
q3dviewer/shaders/cloud_frag.glsl,sha256=tbCsDUp9YlPe0hRWlFS724SH6TtMeLO-GVYROzEElZg,609
|
|
18
23
|
q3dviewer/shaders/cloud_vert.glsl,sha256=Vxgw-Zrr0knAK0z4qMXKML6IC4EbffKMwYN2TMXROoI,2117
|
|
19
24
|
q3dviewer/shaders/gau_frag.glsl,sha256=5_UY84tWDts59bxP8x4I-wgnzY8aGeGuo28wX--LW7E,975
|
|
@@ -29,13 +34,13 @@ q3dviewer/tools/lidar_cam_calib.py,sha256=kYR41Eq3o7frHLIWqysHUvVbB29GC0sDLzUigw
|
|
|
29
34
|
q3dviewer/tools/ros_viewer.py,sha256=Arpy5Y89h_1aSb8aFbeHJEYFFUOxCNm4QWLh5tKcPQI,2504
|
|
30
35
|
q3dviewer/utils/__init__.py,sha256=irm8Z_bT8l9kzhoMlds2Dal8g4iw4vjmqNPZSs4W6e0,157
|
|
31
36
|
q3dviewer/utils/cloud_io.py,sha256=ttD8FJExdDhXB1Z0Ej9S939i8gcq4JfyqwLXVh8CEFw,11094
|
|
32
|
-
q3dviewer/utils/convert_ros_msg.py,sha256=
|
|
37
|
+
q3dviewer/utils/convert_ros_msg.py,sha256=sAoQfy3qLQKsIArBAVm8H--wlQXOcmkKK3-Ox9UCcrc,1686
|
|
33
38
|
q3dviewer/utils/gl_helper.py,sha256=dRY_kUqyPMr7NTcupUr6_VTvgnj53iE2C0Lk0-oFYsI,1435
|
|
34
39
|
q3dviewer/utils/maths.py,sha256=ghnZQYxQtAQonTqQOmfHAMdSluk0sO1Ps_9bL2vMeb8,5397
|
|
35
40
|
q3dviewer/utils/range_slider.py,sha256=jZJQL-uQgnpgLvtYSWpKTrJlLkt3aqNpaRQAePEpNd0,3174
|
|
36
|
-
q3dviewer-1.0.
|
|
37
|
-
q3dviewer-1.0.
|
|
38
|
-
q3dviewer-1.0.
|
|
39
|
-
q3dviewer-1.0.
|
|
40
|
-
q3dviewer-1.0.
|
|
41
|
-
q3dviewer-1.0.
|
|
41
|
+
q3dviewer-1.0.7.dist-info/LICENSE,sha256=81cMOyNfw8KLb1JnPYngGHJ5W83gSbZEBU9MEP3tl-E,1124
|
|
42
|
+
q3dviewer-1.0.7.dist-info/METADATA,sha256=IP5D90vA9PSGbJAuw2N8a0mceD8YOzx7rIze_lqZGXQ,349
|
|
43
|
+
q3dviewer-1.0.7.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
|
|
44
|
+
q3dviewer-1.0.7.dist-info/entry_points.txt,sha256=2vsxH5_1WhM-DnDgY0TDbiAGFPFMfI0Wmfs_6iXEf4Y,317
|
|
45
|
+
q3dviewer-1.0.7.dist-info/top_level.txt,sha256=HFFDCbGu28txcGe2HPc46A7EPaguBa_b5oH7bufmxHM,10
|
|
46
|
+
q3dviewer-1.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|