q3dviewer 1.1.7__py3-none-any.whl → 1.1.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.
Files changed (36) hide show
  1. q3dviewer/.vscode/c_cpp_properties.json +30 -0
  2. q3dviewer/.vscode/settings.json +10 -0
  3. q3dviewer/base_glwidget.py +56 -1
  4. q3dviewer/custom_items/__init__.py +1 -0
  5. q3dviewer/custom_items/cloud_io_item.py +16 -1
  6. q3dviewer/custom_items/cloud_item.py +31 -20
  7. q3dviewer/custom_items/text3d_item.py +120 -0
  8. q3dviewer/custom_items/text_item.py +19 -4
  9. q3dviewer/gau_io.py +0 -168
  10. q3dviewer/glwidget.py +23 -3
  11. q3dviewer/shaders/cloud_frag.glsl +1 -1
  12. q3dviewer/shaders/cloud_vert.glsl +4 -4
  13. q3dviewer/shaders/gau_frag.glsl +1 -1
  14. q3dviewer/shaders/gau_prep.glsl +1 -1
  15. q3dviewer/shaders/gau_vert.glsl +1 -1
  16. q3dviewer/shaders/sort_by_key.glsl +1 -1
  17. q3dviewer/test/test_interpolation.py +58 -0
  18. q3dviewer/test/test_rendering.py +73 -0
  19. q3dviewer/tools/cinematographer.py +367 -0
  20. q3dviewer/tools/cloud_viewer.py +79 -3
  21. q3dviewer/tools/example_viewer.py +8 -28
  22. q3dviewer/tools/film_maker.py +1 -1
  23. q3dviewer/tools/lidar_cam_calib.py +8 -8
  24. q3dviewer/utils/convert_ros_msg.py +49 -6
  25. {q3dviewer-1.1.7.dist-info → q3dviewer-1.1.8.dist-info}/METADATA +2 -1
  26. q3dviewer-1.1.8.dist-info/RECORD +50 -0
  27. {q3dviewer-1.1.7.dist-info → q3dviewer-1.1.8.dist-info}/WHEEL +1 -1
  28. q3dviewer/basic_window.py +0 -228
  29. q3dviewer/cloud_viewer.py +0 -74
  30. q3dviewer/custom_items/camera_frame_item.py +0 -173
  31. q3dviewer/custom_items/trajectory_item.py +0 -79
  32. q3dviewer/utils.py +0 -71
  33. q3dviewer-1.1.7.dist-info/RECORD +0 -49
  34. {q3dviewer-1.1.7.dist-info → q3dviewer-1.1.8.dist-info}/LICENSE +0 -0
  35. {q3dviewer-1.1.7.dist-info → q3dviewer-1.1.8.dist-info}/entry_points.txt +0 -0
  36. {q3dviewer-1.1.7.dist-info → q3dviewer-1.1.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,30 @@
1
+ {
2
+ "configurations": [
3
+ {
4
+ "browse": {
5
+ "databaseFilename": "${default}",
6
+ "limitSymbolsToIncludedHeaders": false
7
+ },
8
+ "includePath": [
9
+ "/home/liu/catkin_ws/devel/include/**",
10
+ "/opt/ros/noetic/include/**",
11
+ "/home/liu/catkin_ws/src/marker_test/apriltag_ros/apriltag_ros/include/**",
12
+ "/home/liu/catkin_ws/src/BALM/include/**",
13
+ "/home/liu/catkin_ws/src/FAST-LIVO/include/**",
14
+ "/home/liu/catkin_ws/src/grid2dlib/include/**",
15
+ "/home/liu/catkin_ws/src/lidar_cam_calib/include/**",
16
+ "/home/liu/catkin_ws/src/livox_laser_simulation/include/**",
17
+ "/home/liu/catkin_ws/src/usb_cam/include/**",
18
+ "/home/liu/catkin_ws/src/rpg_vikit/vikit_common/include/**",
19
+ "/home/liu/catkin_ws/src/rpg_vikit/vikit_ros/include/**",
20
+ "/usr/include/**"
21
+ ],
22
+ "name": "ROS",
23
+ "intelliSenseMode": "gcc-x64",
24
+ "compilerPath": "/usr/bin/gcc",
25
+ "cStandard": "gnu11",
26
+ "cppStandard": "c++14"
27
+ }
28
+ ],
29
+ "version": 4
30
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "python.autoComplete.extraPaths": [
3
+ "/home/liu/catkin_ws/devel/lib/python3/dist-packages",
4
+ "/opt/ros/noetic/lib/python3/dist-packages"
5
+ ],
6
+ "python.analysis.extraPaths": [
7
+ "/home/liu/catkin_ws/devel/lib/python3/dist-packages",
8
+ "/opt/ros/noetic/lib/python3/dist-packages"
9
+ ]
10
+ }
@@ -89,6 +89,9 @@ class BaseGLWidget(QOpenGLWidget):
89
89
  the method is herted from QOpenGLWidget,
90
90
  and it is called when the widget is first shown.
91
91
  """
92
+ glEnable(GL_DEPTH_TEST)
93
+ glDepthFunc(GL_LESS)
94
+
92
95
  for item in self.items:
93
96
  item.initialize()
94
97
  # initialize the projection matrix and model view matrix
@@ -302,7 +305,7 @@ class BaseGLWidget(QOpenGLWidget):
302
305
  near = dist * 0.001
303
306
  far = dist * 10000.
304
307
  r = near * tan(0.5 * radians(self._fov))
305
- t = r * h / w
308
+ t = r * h / max(w, 1)
306
309
  matrix = frustum(-r, r, -t, t, near, far)
307
310
  return matrix
308
311
 
@@ -349,3 +352,55 @@ class BaseGLWidget(QOpenGLWidget):
349
352
  frame = np.frombuffer(pixels, dtype=np.uint8).reshape(height, width, 3)
350
353
  frame = np.flip(frame, 0)
351
354
  return frame
355
+
356
+ def depth_to_meters(self, depth_buffer):
357
+ """
358
+ Convert normalized depth buffer values [0,1] to actual distances in meters.
359
+
360
+ Args:
361
+ depth_buffer: numpy array with depth values in range [0,1]
362
+
363
+ Returns:
364
+ numpy array with distances in meters
365
+ """
366
+ # Get near and far clipping planes
367
+ near = self.dist * 0.001
368
+ far = self.dist * 10000.
369
+
370
+ # Convert from normalized depth [0,1] to linear depth in meters
371
+ # OpenGL depth buffer formula: depth = (1/z - 1/near) / (1/far - 1/near)
372
+ # Solving for z: z = 1 / (depth * (1/far - 1/near) + 1/near)
373
+
374
+ # Avoid division by zero for depth = 1.0 (far plane)
375
+ depth_clamped = np.clip(depth_buffer, 0.0, 0.999999)
376
+
377
+ linear_depth = 1.0 / (depth_clamped * (1.0/far - 1.0/near) + 1.0/near)
378
+
379
+ return linear_depth
380
+
381
+
382
+ def get_point(self, x, y):
383
+ self.makeCurrent() # Ensure the OpenGL context is current
384
+ width = self.current_width()
385
+ height = self.current_height()
386
+
387
+ gl_y = height - y - 1
388
+ z = glReadPixels(x, gl_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
389
+ z = np.frombuffer(z, dtype=np.float32)[0]
390
+ if z == 1.0 or z == 0.0:
391
+ return None
392
+
393
+ # Retrieve OpenGL matrices (column-major), convert to numpy arrays and transpose
394
+ view = np.array(glGetFloatv(GL_MODELVIEW_MATRIX), dtype=np.float32).reshape((4,4)).T
395
+ proj = np.array(glGetFloatv(GL_PROJECTION_MATRIX), dtype=np.float32).reshape((4,4)).T
396
+
397
+ # Convert screen (x, y, z) to normalized device coordinates (NDC)
398
+ ndc_x = (x / width) * 2.0 - 1.0
399
+ ndc_y = (gl_y / height) * 2.0 - 1.0
400
+ ndc_z = 2.0 * z - 1.0
401
+ ndc = np.array([ndc_x, ndc_y, ndc_z, 1.0], dtype=np.float32)
402
+
403
+ inv_projview = np.linalg.inv(proj @ view)
404
+ world_p = inv_projview @ ndc
405
+ world_p /= world_p[3]
406
+ return world_p[:3]
@@ -7,3 +7,4 @@ from q3dviewer.custom_items.grid_item import GridItem
7
7
  from q3dviewer.custom_items.text_item import Text2DItem
8
8
  from q3dviewer.custom_items.image_item import ImageItem
9
9
  from q3dviewer.custom_items.line_item import LineItem
10
+ from q3dviewer.custom_items.text3d_item import Text3DItem
@@ -11,7 +11,22 @@ from q3dviewer.utils.cloud_io import save_pcd, save_ply, save_e57, save_las, loa
11
11
 
12
12
  class CloudIOItem(CloudItem):
13
13
  """
14
- add save/load function to CloudItem
14
+ A OpenGL point cloud item with input/output capabilities.
15
+ Attributes:
16
+ size (float): The size of each point. When `point_type` is 'PIXEL', this is the size in screen pixels.
17
+ When `point_type` is 'SQUARE' or 'SPHERE', this is the size in centimeters in 3D space.
18
+ alpha (float): The transparency of the points, in the range [0, 1], where 0 is fully transparent and 1 is fully opaque.
19
+ color_mode (str): The coloring mode for the points.
20
+ - 'FLAT': Single flat color for all points (uses the `color` attribute).
21
+ - 'I': Color by intensity.
22
+ - 'RGB': Per-point RGB color.
23
+ - 'GRAY': Per-point grayscale color.
24
+ color (str or tuple): The flat color to use when `color_mode` is 'FLAT'. Accepts any valid matplotlib color (e.g., 'red', '#FF4500', (1.0, 0.5, 0.0)).
25
+ point_type (str): The type/rendering style of each point:
26
+ - 'PIXEL': Draw each point as a square pixel on the screen.
27
+ - 'SQUARE': Draw each point as a square in 3D space.
28
+ - 'SPHERE': Draw each point as a sphere in 3D space.
29
+ depth_test (bool): Whether to enable depth testing. If True, points closer to the camera will appear in front of farther ones.
15
30
  """
16
31
  def __init__(self, **kwargs):
17
32
  super().__init__(**kwargs)
@@ -11,7 +11,7 @@ from OpenGL.GL import shaders
11
11
 
12
12
  import threading
13
13
  import os
14
- from q3dviewer.Qt.QtWidgets import QLabel, QLineEdit, QDoubleSpinBox, QComboBox, QCheckBox
14
+ from q3dviewer.Qt.QtWidgets import QLabel, QLineEdit, QDoubleSpinBox, QSpinBox, QComboBox, QCheckBox
15
15
  from q3dviewer.utils.range_slider import RangeSlider
16
16
  from q3dviewer.utils import set_uniform
17
17
  from q3dviewer.utils import text_to_rgba
@@ -20,6 +20,25 @@ from q3dviewer.Qt import Q3D_DEBUG
20
20
 
21
21
  # draw points with color (x, y, z, color)
22
22
  class CloudItem(BaseItem):
23
+ """
24
+ A OpenGL point cloud item.
25
+
26
+ Attributes:
27
+ size (float): The size of each point. When `point_type` is 'PIXEL', this is the size in screen pixels.
28
+ When `point_type` is 'SQUARE' or 'SPHERE', this is the size in centimeters in 3D space.
29
+ alpha (float): The transparency of the points, in the range [0, 1], where 0 is fully transparent and 1 is fully opaque.
30
+ color_mode (str): The coloring mode for the points.
31
+ - 'FLAT': Single flat color for all points (uses the `color` attribute).
32
+ - 'I': Color by intensity.
33
+ - 'RGB': Per-point RGB color.
34
+ - 'GRAY': Per-point grayscale color.
35
+ color (str or tuple): The flat color to use when `color_mode` is 'FLAT'. Accepts any valid matplotlib color (e.g., 'red', '#FF4500', (1.0, 0.5, 0.0)).
36
+ point_type (str): The type/rendering style of each point:
37
+ - 'PIXEL': Draw each point as a square pixel on the screen.
38
+ - 'SQUARE': Draw each point as a square in 3D space.
39
+ - 'SPHERE': Draw each point as a sphere in 3D space.
40
+ depth_test (bool): Whether to enable depth testing. If True, points closer to the camera will appear in front of farther ones.
41
+ """
23
42
  def __init__(self, size, alpha,
24
43
  color_mode='I',
25
44
  color='white',
@@ -65,11 +84,10 @@ class CloudItem(BaseItem):
65
84
  combo_ptype.currentIndexChanged.connect(self._on_point_type_selection)
66
85
  layout.addWidget(combo_ptype)
67
86
 
68
- self.box_size = QDoubleSpinBox()
87
+ self.box_size = QSpinBox()
69
88
  self.box_size.setPrefix("Size: ")
70
89
  self.box_size.setSingleStep(1)
71
- self.box_size.setDecimals(0)
72
- self.box_size.setValue(self.size)
90
+ self.box_size.setValue(int(self.size))
73
91
  self.box_size.setRange(0, 100)
74
92
  self.box_size.valueChanged.connect(self.set_size)
75
93
  self._on_point_type_selection(self.point_type_table[self.point_type])
@@ -147,17 +165,10 @@ class CloudItem(BaseItem):
147
165
  self.point_type = list(self.point_type_table.keys())[index]
148
166
  if self.point_type == 'PIXEL':
149
167
  self.box_size.setPrefix("Set size (pixel): ")
150
- self.box_size.setDecimals(0)
151
- self.box_size.setSingleStep(1)
152
- self.size = np.ceil(self.size)
153
- self.box_size.setValue(self.size)
154
168
  else:
155
- self.box_size.setPrefix("Set size (meter): ")
156
- self.box_size.setDecimals(2)
157
- self.box_size.setSingleStep(0.01)
158
- if self.size >= 1:
159
- self.size = self.size * 0.01
160
- self.box_size.setValue(self.size)
169
+ self.box_size.setPrefix("Set size (cm): ")
170
+ # self.size = 1
171
+ # self.box_size.setValue(self.size)
161
172
  self.need_update_setting = True
162
173
 
163
174
  def set_alpha(self, alpha):
@@ -226,7 +237,7 @@ class CloudItem(BaseItem):
226
237
  set_uniform(self.program, float(self.vmax), 'vmax')
227
238
  set_uniform(self.program, float(self.vmin), 'vmin')
228
239
  set_uniform(self.program, float(self.alpha), 'alpha')
229
- set_uniform(self.program, float(self.size), 'point_size')
240
+ set_uniform(self.program, int(self.size), 'point_size')
230
241
  set_uniform(self.program, int(self.point_type_table[self.point_type]), 'point_type')
231
242
  glUseProgram(0)
232
243
  self.need_update_setting = False
@@ -296,10 +307,12 @@ class CloudItem(BaseItem):
296
307
  glEnable(GL_BLEND)
297
308
  glEnable(GL_PROGRAM_POINT_SIZE)
298
309
  glEnable(GL_POINT_SPRITE)
299
- if self.depth_test:
300
- glEnable(GL_DEPTH_TEST)
310
+ glEnable(GL_DEPTH_TEST)
311
+
312
+ if not self.depth_test:
313
+ glDepthFunc(GL_ALWAYS) # Always pass depth test but still write depth
301
314
  else:
302
- glDisable(GL_DEPTH_TEST)
315
+ glDepthFunc(GL_LESS) # Normal depth testing
303
316
 
304
317
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
305
318
  glUseProgram(self.program)
@@ -329,5 +342,3 @@ class CloudItem(BaseItem):
329
342
  glDisable(GL_POINT_SPRITE)
330
343
  glDisable(GL_PROGRAM_POINT_SIZE)
331
344
  glDisable(GL_BLEND)
332
- if self.depth_test:
333
- glDisable(GL_DEPTH_TEST) # Disable depth testing if it was enabled
@@ -0,0 +1,120 @@
1
+ """
2
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
3
+ Distributed under MIT license. See LICENSE for more information.
4
+ """
5
+
6
+
7
+ from turtle import position
8
+ from q3dviewer.base_item import BaseItem
9
+ from OpenGL.GL import *
10
+ from OpenGL.GLUT import glutBitmapCharacter, glutInit
11
+ from OpenGL.GLUT import (
12
+ GLUT_BITMAP_HELVETICA_10,
13
+ GLUT_BITMAP_HELVETICA_12,
14
+ GLUT_BITMAP_HELVETICA_18,
15
+ # GLUT_BITMAP_TIMES_ROMAN_10,
16
+ GLUT_BITMAP_TIMES_ROMAN_24,
17
+ )
18
+
19
+ def get_glut_font(font_size):
20
+ # Map requested font_size to a GLUT font object
21
+ if font_size <= 10:
22
+ return GLUT_BITMAP_HELVETICA_10
23
+ elif font_size <= 12:
24
+ return GLUT_BITMAP_HELVETICA_12
25
+ elif font_size <= 18:
26
+ return GLUT_BITMAP_HELVETICA_18
27
+ elif font_size <= 24:
28
+ return GLUT_BITMAP_TIMES_ROMAN_24
29
+ else:
30
+ return GLUT_BITMAP_TIMES_ROMAN_24 # Largest available
31
+
32
+
33
+ # draw points with color (x, y, z, color)
34
+ class Text3DItem(BaseItem):
35
+ """
36
+ A OpenGL 3d text and mark item.
37
+
38
+ Attributes:
39
+ data: list storing text data. Each element is a dict with keys:
40
+ 'text': str, the text to display
41
+ 'position': (x, y, z), the 3D position of the text
42
+ 'color': (r, g, b, a), the color of the text
43
+ 'font_size': float, the font size of the text
44
+ 'point_size': float, size of point to draw at position (0 for no point)
45
+ 'line_width': float, width of line to draw between points (0 for no line)
46
+
47
+ """
48
+ def __init__(self, data=[]):
49
+ super().__init__()
50
+ self._disable_setting = True
51
+ self.data_list = data # map of {'text': str, 'position': (x,y,z), 'color': (r,g,b,a), 'size': float}
52
+
53
+ def add_setting(self, layout):
54
+ pass # No settings for Text3DItem
55
+
56
+ def set_data(self, data, append=False):
57
+ if not append:
58
+ self.data_list = []
59
+ self.data_list.extend(data)
60
+
61
+ def clear_data(self):
62
+ self.data_list = []
63
+
64
+
65
+ def initialize_gl(self):
66
+ glutInit()
67
+ # super().initialize_gl()
68
+
69
+
70
+ def paint(self):
71
+ for item in self.data_list:
72
+ # Handle both dictionary and string formats
73
+ if isinstance(item, dict):
74
+ text = item.get('text', '')
75
+ pos = item.get('position', (0.0, 0.0, 0.0))
76
+ font_size = item.get('font_size', 24)
77
+ color = item.get('color', (1.0, 1.0, 1.0, 1.0))
78
+ point_size = item.get('point_size', 0.0)
79
+ elif isinstance(item, str):
80
+ # If item is a string, treat it as text with default position and color
81
+ text = item
82
+ pos = (0.0, 0.0, 0.0)
83
+ color = (1.0, 1.0, 1.0, 1.0)
84
+ font_size = 24
85
+ point_size = 0.0
86
+ else:
87
+ print(f"Warning: Unsupported item type: {type(item)}")
88
+ continue
89
+
90
+ glColor4f(*color)
91
+ offset = 0.02
92
+ pos_text = (pos[0] + offset, pos[1]+ offset, pos[2]+ offset)
93
+ glRasterPos3f(*pos_text)
94
+
95
+ if point_size > 0.0:
96
+ # draw a point at the position
97
+ glPointSize(point_size)
98
+ glBegin(GL_POINTS)
99
+ glVertex3f(*pos)
100
+ glEnd()
101
+ font = get_glut_font(font_size)
102
+ for ch in text:
103
+ glutBitmapCharacter(font, ord(ch))
104
+
105
+ # draw lines between points
106
+ for i in range(len(self.data_list) - 1):
107
+ item1 = self.data_list[i]
108
+ item2 = self.data_list[i + 1]
109
+ if isinstance(item1, dict) and isinstance(item2, dict):
110
+ pos1 = item1.get('position', (0.0, 0.0, 0.0))
111
+ pos2 = item2.get('position', (0.0, 0.0, 0.0))
112
+ line_width = item1.get('line_width', 0.0)
113
+ color = item1.get('color', (1.0, 1.0, 1.0, 1.0))
114
+ if line_width > 0.0:
115
+ glColor4f(*color)
116
+ glLineWidth(line_width)
117
+ glBegin(GL_LINES)
118
+ glVertex3f(*pos1)
119
+ glVertex3f(*pos2)
120
+ glEnd()
@@ -9,7 +9,16 @@ from q3dviewer.utils import text_to_rgba
9
9
 
10
10
 
11
11
  class Text2DItem(BaseItem):
12
- """Draws text over opengl 3D."""
12
+ """
13
+ A OpenGL 2D text item.
14
+
15
+ Attributes:
16
+ pos: (x, y), the 2D position of the text in pixels
17
+ text: str, the text to display
18
+ font: QFont, the font of the text
19
+ color: str, the color of the text in matplotlib format
20
+ size: int, the font size of the text
21
+ """
13
22
 
14
23
  def __init__(self, **kwds):
15
24
  """All keyword arguments are passed to set_data()"""
@@ -17,11 +26,17 @@ class Text2DItem(BaseItem):
17
26
  self.pos = (20, 50)
18
27
  self.text = ''
19
28
  self.font = QtGui.QFont('Helvetica', 16)
20
-
21
- self.rgb = text_to_rgba('w')
29
+ self.rgb = text_to_rgba('white')
22
30
  if 'pos' in kwds:
23
31
  self.pos = kwds['pos']
24
- self.set_data(**kwds)
32
+ if 'text' in kwds:
33
+ self.text = kwds['text']
34
+ if 'font' in kwds:
35
+ self.font = kwds['font']
36
+ if 'color' in kwds:
37
+ self.set_color(kwds['color'])
38
+ if 'size' in kwds:
39
+ self.font.setPointSize(kwds['size'])
25
40
 
26
41
  def set_data(self, **kwds):
27
42
  args = ['pos', 'color', 'text', 'size', 'font']
q3dviewer/gau_io.py CHANGED
@@ -1,168 +0,0 @@
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
@@ -9,6 +9,7 @@ from q3dviewer.Qt.QtGui import QKeyEvent
9
9
  from q3dviewer.base_glwidget import BaseGLWidget
10
10
  from q3dviewer.utils import text_to_rgba
11
11
 
12
+
12
13
  class SettingWindow(QWidget):
13
14
  def __init__(self):
14
15
  super().__init__()
@@ -57,10 +58,11 @@ class GLWidget(BaseGLWidget):
57
58
  super(GLWidget, self).__init__()
58
59
 
59
60
  def keyPressEvent(self, ev: QKeyEvent):
60
- if ev.key() == QtCore.Qt.Key_M: # setting meun
61
+ if ev.key() == QtCore.Qt.Key_M: # setting menu
61
62
  print("Open setting windows")
62
- self.open_setting_window()
63
- super().keyPressEvent(ev)
63
+ self.open_setting_window()
64
+ else:
65
+ super().keyPressEvent(ev)
64
66
 
65
67
  def on_followable_selection(self, index):
66
68
  self.followed_name = self.followable_item_name[index]
@@ -126,3 +128,21 @@ class GLWidget(BaseGLWidget):
126
128
 
127
129
  def change_show_center(self, state):
128
130
  self.enable_show_center = state
131
+
132
+ def get_camera_pose(self):
133
+ """Get current camera pose parameters"""
134
+ camera_pose = {
135
+ 'center': self.center.tolist() if hasattr(self.center, 'tolist') else list(self.center),
136
+ 'euler': self.euler.tolist() if hasattr(self.euler, 'tolist') else list(self.euler),
137
+ 'distance': float(self.dist),
138
+ }
139
+ return camera_pose
140
+
141
+ def set_camera_pose(self, config):
142
+ """Set camera pose from parameters"""
143
+ if 'center' in config and 'euler' in config and 'distance' in config:
144
+ self.set_center(config['center'])
145
+ self.set_euler(config['euler'])
146
+ self.set_dist(config['distance'])
147
+ else:
148
+ print("Invalid camera pose config")
@@ -1,9 +1,9 @@
1
+ #version 330 core
1
2
  /*
2
3
  Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
3
4
  Distributed under MIT license. See LICENSE for more information.
4
5
  */
5
6
 
6
- #version 330 core
7
7
 
8
8
  uniform int point_type;
9
9
 
@@ -1,9 +1,9 @@
1
+ #version 330 core
1
2
  /*
2
3
  Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
3
4
  Distributed under MIT license. See LICENSE for more information.
4
5
  */
5
6
 
6
- #version 330 core
7
7
 
8
8
  layout (location = 0) in vec3 position;
9
9
  layout (location = 1) in uint value;
@@ -17,7 +17,7 @@ uniform float vmin = 0;
17
17
  uniform float vmax = 255;
18
18
  uniform float focal = 1000;
19
19
  uniform int point_type = 0; // 0 pixel, 1 flat square, 2 sphere
20
- uniform float point_size = 0.01; // World size for each point (meter)
20
+ uniform int point_size = 1; // World size for each point (pixel or cm)
21
21
  out vec4 color;
22
22
 
23
23
  vec3 getRainbowColor(uint value_raw) {
@@ -47,9 +47,9 @@ void main()
47
47
 
48
48
  // Calculate point size in pixels based on distance
49
49
  if (point_type == 0)
50
- gl_PointSize = int(point_size);
50
+ gl_PointSize = float(point_size);
51
51
  else
52
- gl_PointSize = point_size / gl_Position.w * focal;
52
+ gl_PointSize = (float(point_size) * 0.01) / gl_Position.w * focal;
53
53
  vec3 c = vec3(1.0, 1.0, 1.0);
54
54
  if (color_mode == 1)
55
55
  {
@@ -1,9 +1,9 @@
1
+ #version 430 core
1
2
  /*
2
3
  this file is modified from GaussianSplattingViewer licensed under the MIT License.
3
4
  see https://github.com/limacv/GaussianSplattingViewer/blob/main/shaders/gau_frag.glsl
4
5
  */
5
6
 
6
- #version 430 core
7
7
 
8
8
  in vec3 color;
9
9
  in float alpha;