q3dviewer 1.0.6__py3-none-any.whl → 1.0.8__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.
@@ -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
- item.paint()
138
- glPopAttrib()
139
- glMatrixMode(GL_MODELVIEW)
140
- glPopMatrix()
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
- twc = self.center
197
- tcw = np.array([0, 0, self.dist])
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 = twc + Rwc @ tcw
200
- Twc = makeT(Rwc, twc)
201
- return np.linalg.inv(Twc)
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()
@@ -49,7 +49,7 @@ class AxisItem(BaseItem):
49
49
  super().__init__()
50
50
  self.size = size
51
51
  self.width = width
52
- self.model_matrix = np.eye(4, dtype=np.float32)
52
+ self.T = np.eye(4, dtype=np.float32)
53
53
  self.need_update_setting = True
54
54
 
55
55
  def initialize_gl(self):
@@ -116,7 +116,7 @@ class AxisItem(BaseItem):
116
116
  return
117
117
  glUseProgram(self.program)
118
118
  set_uniform(self.program, float(self.size), 'size')
119
- set_uniform(self.program, self.model_matrix, 'model_matrix')
119
+ set_uniform(self.program, self.T, 'model_matrix')
120
120
  glUseProgram(0)
121
121
  self.need_update_setting = False
122
122
 
@@ -127,7 +127,7 @@ class AxisItem(BaseItem):
127
127
  """
128
128
  Set the transformation matrix for the axis item.
129
129
  """
130
- self.model_matrix = transform
130
+ self.T = transform
131
131
  self.need_update_setting = True
132
132
 
133
133
  def paint(self):
@@ -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)
@@ -124,7 +124,12 @@ class CloudItem(BaseItem):
124
124
 
125
125
  def set_color_mode(self, color_mode):
126
126
  if color_mode in {'FLAT', 'RGB', 'I'}:
127
- self.combo_color.setCurrentIndex(self.mode_table[color_mode])
127
+ try:
128
+ self.combo_color.setCurrentIndex(self.mode_table[color_mode])
129
+ except RuntimeError:
130
+ pass
131
+ except ValueError:
132
+ pass
128
133
  else:
129
134
  print(f"Invalid color mode: {color_mode}")
130
135
 
@@ -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)
q3dviewer/glwidget.py CHANGED
@@ -73,8 +73,7 @@ class GLWidget(BaseGLWidget):
73
73
 
74
74
  def update(self):
75
75
  if self.followed_name != 'none':
76
- pos = self.named_items[self.followed_name].T[:3, 3]
77
- self.opts['center'] = QVector3D(pos[0], pos[1], pos[2])
76
+ self.center = self.named_items[self.followed_name].T[:3, 3]
78
77
  super().update()
79
78
 
80
79
  def add_setting(self, layout):
@@ -18,7 +18,7 @@ from q3dviewer.utils.convert_ros_msg import convert_pointcloud2_msg, convert_odo
18
18
  viewer = None
19
19
  point_num_per_scan = None
20
20
  color_mode = None
21
-
21
+ auto_set_color_mode = True
22
22
 
23
23
  def odom_cb(data):
24
24
  global viewer
@@ -29,16 +29,15 @@ def odom_cb(data):
29
29
  def scan_cb(data):
30
30
  global viewer
31
31
  global point_num_per_scan
32
+ global auto_set_color_mode
32
33
  cloud, fields, _ = convert_pointcloud2_msg(data)
33
34
  if (cloud.shape[0] > point_num_per_scan):
34
35
  idx = random.sample(range(cloud.shape[0]), point_num_per_scan)
35
36
  cloud = cloud[idx]
36
- color_mode = 'FLAT'
37
- if 'intensity' in fields:
38
- color_mode = 'I'
39
- if 'rgb' in fields:
40
- color_mode = 'RGB'
41
- viewer['map'].set_color_mode(color_mode)
37
+ if 'rgb' in fields and auto_set_color_mode:
38
+ print("Set color mode to RGB")
39
+ viewer['map'].set_color_mode('RGB')
40
+ auto_set_color_mode = False
42
41
  viewer['map'].set_data(data=cloud, append=True)
43
42
  viewer['scan'].set_data(data=cloud)
44
43
 
@@ -54,7 +53,7 @@ def main():
54
53
  global viewer
55
54
  global point_num_per_scan
56
55
  point_num_per_scan = 10000
57
- map_item = q3d.CloudIOItem(size=1, alpha=0.1, color_mode='RGB')
56
+ map_item = q3d.CloudIOItem(size=1, alpha=0.1, color_mode='I')
58
57
  scan_item = q3d.CloudItem(
59
58
  size=2, alpha=1, color_mode='FLAT', color='#ffffff')
60
59
  odom_item = q3d.AxisItem(size=0.5, width=5)
@@ -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.6
4
- License-File: LICENSE
5
- Requires-Dist: numpy
6
- Requires-Dist: pyside6
3
+ Version: 1.0.8
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: pillow
11
+ Requires-Dist: laspy
9
12
  Requires-Dist: meshio
10
- Requires-Dist: pypcd4
13
+ Requires-Dist: numpy
14
+ Requires-Dist: pillow
11
15
  Requires-Dist: pye57
12
- Requires-Dist: laspy
16
+ Requires-Dist: pypcd4
17
+ Requires-Dist: pyside6
18
+
19
+ UNKNOWN
20
+
13
21
 
@@ -1,19 +1,24 @@
1
1
  q3dviewer/__init__.py,sha256=rP5XX_x8g7hxIMqNHlU89BN4dt5MSvoYYwip68fCmhc,173
2
- q3dviewer/base_glwidget.py,sha256=lCYTT0ENrxZU31GRD0XeUOWFbDX_45zTQP8TH15PXTE,8742
2
+ q3dviewer/base_glwidget.py,sha256=i1GOuPJMd_HeOu6-Y0mMe73tM4U0z1gdM-XOJszXGnM,8916
3
3
  q3dviewer/base_item.py,sha256=-6GogOKrjVRSTszio9ELPDhehBaUlTyNaag2bUl81l8,1337
4
- q3dviewer/gau_io.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- q3dviewer/glwidget.py,sha256=ppkAJUCCJ4pnLX3j6jCFmJw8O-kCr7UphsEdkGSakpQ,4832
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
7
+ q3dviewer/glwidget.py,sha256=BwBxbok40cq705QBYjPbtckf-hbLxnKTxyA6nhaT1Ic,4772
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
- q3dviewer/custom_items/axis_item.py,sha256=6dWGqBeA3DL-9xmsxdmTSIGLs6BcRbiysBKkkXVb5oI,4456
11
+ q3dviewer/custom_items/axis_item.py,sha256=8ZIzBH9rYFD9DWNAlshpJFBpSyXfMcv-eewjqlJ9GIE,4423
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
- q3dviewer/custom_items/cloud_item.py,sha256=u9hHHFjK4Ls6ssFVbBFqdR2xg0QNmgsKOz9jGHSqrt8,12444
14
+ q3dviewer/custom_items/cloud_item.py,sha256=kkIpJky7-Bbry3yCUodsU08BCZqtRQD69-0EMCLKnUs,12571
11
15
  q3dviewer/custom_items/frame_item.py,sha256=YTBOEeRWh95trhAlKoZ9LZ4I2x-A5JjOhNPp4OvD5E0,6983
12
16
  q3dviewer/custom_items/gaussian_item.py,sha256=B0mYjlW83Kr6FUtZ6P_f40pjjiASPB8g3CSjMTnjsvY,9857
13
17
  q3dviewer/custom_items/grid_item.py,sha256=U4nk16d5Dkbt6U9s23c9PIlG-wPwu15Ho0HsamDuyqM,3436
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
@@ -26,16 +31,16 @@ q3dviewer/tools/example_viewer.py,sha256=yeVXT0k4-h1vTLKnGzWADZD3our6XUaYUTy0p5d
26
31
  q3dviewer/tools/gaussian_viewer.py,sha256=vIwWmiFhjNmknrEkBLzt2yiegeH7LP3OeNjnGM6GzaI,1633
27
32
  q3dviewer/tools/lidar_calib.py,sha256=beQP6Z9NyW7ME6zCRAxH1RRSm5zMalcmlmF_HzJPLi8,10811
28
33
  q3dviewer/tools/lidar_cam_calib.py,sha256=kYR41Eq3o7frHLIWqysHUvVbB29GC0sDLzUigwxG4n4,11539
29
- q3dviewer/tools/ros_viewer.py,sha256=Arpy5Y89h_1aSb8aFbeHJEYFFUOxCNm4QWLh5tKcPQI,2504
34
+ q3dviewer/tools/ros_viewer.py,sha256=ARB3I5wohY3maP8dCu0O0hxObd6JFKuK2y7AApVgMWA,2551
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=VJeleSqp4rMYGCOyrwoXiJveBanTQMM6MEzRMjyXpCI,1684
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.6.dist-info/LICENSE,sha256=81cMOyNfw8KLb1JnPYngGHJ5W83gSbZEBU9MEP3tl-E,1124
37
- q3dviewer-1.0.6.dist-info/METADATA,sha256=U3PKiEPPLKQWTa53XkcutQQtOuoKEHl72ZadqG4EJfM,252
38
- q3dviewer-1.0.6.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
39
- q3dviewer-1.0.6.dist-info/entry_points.txt,sha256=6zSxtJ5cjcpc32sR_wkRqOaeiJFpEuLnJQckZE3niU0,316
40
- q3dviewer-1.0.6.dist-info/top_level.txt,sha256=HFFDCbGu28txcGe2HPc46A7EPaguBa_b5oH7bufmxHM,10
41
- q3dviewer-1.0.6.dist-info/RECORD,,
41
+ q3dviewer-1.0.8.dist-info/LICENSE,sha256=81cMOyNfw8KLb1JnPYngGHJ5W83gSbZEBU9MEP3tl-E,1124
42
+ q3dviewer-1.0.8.dist-info/METADATA,sha256=36WLMFrKUX9e1GTT3s9QgIBgchCmF_7v2QzF4TiNJck,349
43
+ q3dviewer-1.0.8.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
44
+ q3dviewer-1.0.8.dist-info/entry_points.txt,sha256=2vsxH5_1WhM-DnDgY0TDbiAGFPFMfI0Wmfs_6iXEf4Y,317
45
+ q3dviewer-1.0.8.dist-info/top_level.txt,sha256=HFFDCbGu28txcGe2HPc46A7EPaguBa_b5oH7bufmxHM,10
46
+ q3dviewer-1.0.8.dist-info/RECORD,,
@@ -5,3 +5,4 @@ lidar_calib = q3dviewer.tools.lidar_calib:main
5
5
  lidar_cam_calib = q3dviewer.tools.lidar_cam_calib:main
6
6
  mesh_viewer = q3dviewer.tools.mesh_viewer:main
7
7
  ros_viewer = q3dviewer.tools.ros_viewer:main
8
+