q3dviewer 1.0.8__py3-none-any.whl → 1.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,80 +9,131 @@ from PySide6.QtWidgets import QLabel, QDoubleSpinBox, QLineEdit
9
9
  from PySide6.QtCore import QRegularExpression
10
10
  from PySide6.QtGui import QRegularExpressionValidator
11
11
  import numpy as np
12
+ from q3dviewer.utils.maths import hex_to_rgba
12
13
 
13
14
 
14
15
  class GridItem(BaseItem):
15
- def __init__(self, size, spacing, color=np.array([255, 255, 255, 76.5]), offset=np.array([0, 0, 0])):
16
+ def __init__(self, size=100, spacing=20, color='#ffffff40', offset=np.array([0., 0., 0.])):
16
17
  super().__init__()
17
18
  self.size = size
18
19
  self.spacing = spacing
19
- self.color = color
20
20
  self.offset = offset
21
+ self.need_update_grid = True
22
+ self.set_color(color)
23
+ self.vertices = self.generate_grid_vertices()
24
+
25
+ def set_color(self, color):
26
+ try:
27
+ self.rgba = hex_to_rgba(color)
28
+ except ValueError:
29
+ pass
30
+
31
+ def generate_grid_vertices(self):
32
+ vertices = []
33
+ x, y, z = self.offset
34
+ half_size = self.size / 2 # Keep as float
35
+ for i in np.arange(-half_size, half_size + self.spacing, self.spacing):
36
+ vertices.extend([i + x, -half_size + y, z, i + x, half_size + y, z]) # Grid lines parallel to Y axis
37
+ vertices.extend([-half_size + x, i + y, z, half_size + x, i + y, z]) # Grid lines parallel to X axis
38
+ vertices = np.array(vertices, dtype=np.float32)
39
+ return vertices
40
+
41
+ def initialize_gl(self):
42
+ self.vao = glGenVertexArrays(1)
43
+ vbo = glGenBuffers(1)
44
+
45
+ glBindVertexArray(self.vao)
46
+
47
+ glBindBuffer(GL_ARRAY_BUFFER, vbo)
48
+ glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
49
+
50
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
51
+ glEnableVertexAttribArray(0)
52
+
53
+ glBindVertexArray(0)
21
54
 
22
55
  def add_setting(self, layout):
23
- label_size = QLabel("Set size:")
24
- layout.addWidget(label_size)
25
56
  spinbox_size = QDoubleSpinBox()
57
+ spinbox_size.setPrefix("Size: ")
26
58
  spinbox_size.setSingleStep(1.0)
27
- layout.addWidget(spinbox_size)
28
59
  spinbox_size.setValue(self.size)
29
- spinbox_size.valueChanged.connect(self.set_size)
30
60
  spinbox_size.setRange(0, 100000)
61
+ spinbox_size.valueChanged.connect(self.set_size)
62
+ layout.addWidget(spinbox_size)
31
63
 
32
- label_spacing = QLabel("Set spacing:")
33
- layout.addWidget(label_spacing)
34
64
  spinbox_spacing = QDoubleSpinBox()
35
- layout.addWidget(spinbox_spacing)
65
+ spinbox_spacing.setPrefix("Spacing: ")
36
66
  spinbox_spacing.setSingleStep(0.1)
37
67
  spinbox_spacing.setValue(self.spacing)
38
- spinbox_spacing.valueChanged.connect(self._on_spacing)
39
68
  spinbox_spacing.setRange(0, 1000)
69
+ spinbox_spacing.valueChanged.connect(self._on_spacing)
70
+ layout.addWidget(spinbox_spacing)
40
71
 
41
- label_offset = QLabel("Set offset (x;y;z):")
42
- layout.addWidget(label_offset)
43
- self.edit_offset = QLineEdit()
44
- regex = QRegularExpression(r"^-?\d+(\.\d+)?;-?\d+(\.\d+)?;-?\d+(\.\d+)?$")
45
- validator = QRegularExpressionValidator(regex)
46
- self.edit_offset.setValidator(validator)
47
- self.edit_offset.setText(f"{self.offset[0]};{self.offset[1]};{self.offset[2]}")
48
- self.edit_offset.textChanged.connect(self._on_offset)
49
- layout.addWidget(self.edit_offset)
72
+ spinbox_offset_x = QDoubleSpinBox()
73
+ spinbox_offset_x.setPrefix("Offset X: ")
74
+ spinbox_offset_x.setSingleStep(0.1)
75
+ spinbox_offset_x.setValue(self.offset[0])
76
+ spinbox_offset_x.valueChanged.connect(self._on_offset_x)
77
+ layout.addWidget(spinbox_offset_x)
78
+
79
+ spinbox_offset_y = QDoubleSpinBox()
80
+ spinbox_offset_y.setPrefix("Offset Y: ")
81
+ spinbox_offset_y.setSingleStep(0.1)
82
+ spinbox_offset_y.setValue(self.offset[1])
83
+ spinbox_offset_y.valueChanged.connect(self._on_offset_y)
84
+ layout.addWidget(spinbox_offset_y)
85
+
86
+ spinbox_offset_z = QDoubleSpinBox()
87
+ spinbox_offset_z.setPrefix("Offset Z: ")
88
+ spinbox_offset_z.setSingleStep(0.1)
89
+ spinbox_offset_z.setValue(self.offset[2])
90
+ spinbox_offset_z.valueChanged.connect(self._on_offset_z)
91
+ layout.addWidget(spinbox_offset_z)
50
92
 
51
93
  def set_size(self, size):
52
94
  self.size = size
95
+ self.need_update_grid = True
53
96
 
54
97
  def _on_spacing(self, spacing):
55
98
  if spacing > 0:
56
99
  self.spacing = spacing
100
+ self.need_update_grid = True
57
101
 
58
- def _on_offset(self, text):
59
- try:
60
- values = list(map(float, text.split(';')))
61
- if len(values) == 3:
62
- self.offset = np.array(values)
63
- else:
64
- raise ValueError("Offset must have 3 values separated by ';'")
65
- except ValueError:
66
- pass
102
+ def _on_offset_x(self, value):
103
+ self.offset[0] = value
104
+ print(self.offset)
105
+ self.need_update_grid = True
106
+
107
+ def _on_offset_y(self, value):
108
+ self.offset[1] = value
109
+ self.need_update_grid = True
110
+
111
+ def _on_offset_z(self, value):
112
+ self.offset[2] = value
113
+ self.need_update_grid = True
67
114
 
68
115
  def set_offset(self, offset):
69
116
  if isinstance(offset, np.ndarray) and offset.shape == (3,):
70
117
  self.offset = offset
71
- self.edit_offset.setText(f"{self.offset[0]};{self.offset[1]};{self.offset[2]}")
118
+ self.need_update_grid = True
72
119
  else:
73
120
  raise ValueError("Offset must be a numpy array with shape (3,)")
74
121
 
75
122
  def paint(self):
123
+ if self.need_update_grid:
124
+ self.vertices = self.generate_grid_vertices()
125
+ self.initialize_gl()
126
+ self.need_update_grid = False
127
+
76
128
  glEnable(GL_BLEND)
77
129
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
78
- glColor4f(self.color[0] / 255.0, self.color[1] / 255.0, self.color[2] / 255.0, self.color[3] / 255.0)
79
- glBegin(GL_LINES)
80
- for i in np.arange(-self.size, self.size + self.spacing, self.spacing):
81
- glVertex3f(i + self.offset[0], -self.size + self.offset[1], self.offset[2])
82
- glVertex3f(i + self.offset[0], self.size + self.offset[1], self.offset[2])
83
- glVertex3f(-self.size + self.offset[0], i + self.offset[1], self.offset[2])
84
- glVertex3f(self.size + self.offset[0], i + self.offset[1], self.offset[2])
85
- glEnd()
130
+
131
+ glLineWidth(1)
132
+ glColor4f(*self.rgba)
133
+ glBindVertexArray(self.vao)
134
+ glDrawArrays(GL_LINES, 0, len(self.vertices) // 3)
135
+ glBindVertexArray(0)
136
+ glLineWidth(1)
86
137
  glDisable(GL_BLEND)
87
138
 
88
139
 
@@ -154,9 +154,8 @@ class ImageItem(BaseItem):
154
154
  glDisable(GL_BLEND)
155
155
 
156
156
  def add_setting(self, layout):
157
- alpha_label = QLabel("Set Alpha:")
158
- layout.addWidget(alpha_label)
159
157
  spinbox_alpha = QSpinBox()
158
+ spinbox_alpha.setPrefix("Alpha: ")
160
159
  spinbox_alpha.setSingleStep(1)
161
160
  spinbox_alpha.setRange(0, 255)
162
161
  spinbox_alpha.setValue(self.alpha)
@@ -27,7 +27,7 @@ class LineItem(BaseItem):
27
27
  self.line_type = GL_LINE_STRIP if line_type == 'LINE_STRIP' else GL_LINES
28
28
 
29
29
  def add_setting(self, layout):
30
- label_color = QLabel("Set trajectory color:")
30
+ label_color = QLabel("Color:")
31
31
  layout.addWidget(label_color)
32
32
  self.color_edit = QLineEdit()
33
33
  self.color_edit.setToolTip("Hex number, i.e. #FF4500")
@@ -38,14 +38,13 @@ class LineItem(BaseItem):
38
38
  self.color_edit.setValidator(validator)
39
39
  layout.addWidget(self.color_edit)
40
40
 
41
- label_width = QLabel("Set width:")
42
- layout.addWidget(label_width)
43
41
  spinbox_width = QDoubleSpinBox()
42
+ spinbox_width.setPrefix("Width: ")
44
43
  spinbox_width.setSingleStep(0.1)
45
- layout.addWidget(spinbox_width)
46
44
  spinbox_width.setValue(self.width)
47
- spinbox_width.valueChanged.connect(self.set_width)
48
45
  spinbox_width.setRange(0.1, 10.0)
46
+ spinbox_width.valueChanged.connect(self.set_width)
47
+ layout.addWidget(spinbox_width)
49
48
 
50
49
  def _on_color(self, color):
51
50
  try:
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
@@ -4,7 +4,7 @@ Distributed under MIT license. See LICENSE for more information.
4
4
  """
5
5
 
6
6
  from PySide6 import QtCore
7
- from PySide6.QtWidgets import QWidget, QComboBox, QVBoxLayout, QHBoxLayout, QSizePolicy, QSpacerItem, QLabel, QLineEdit, QCheckBox
7
+ from PySide6.QtWidgets import QWidget, QComboBox, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QCheckBox, QGroupBox
8
8
  from PySide6.QtGui import QKeyEvent, QVector3D, QRegularExpressionValidator
9
9
  from PySide6.QtCore import QRegularExpression
10
10
  from OpenGL.GL import *
@@ -17,11 +17,9 @@ class SettingWindow(QWidget):
17
17
  self.combo_items = QComboBox()
18
18
  self.combo_items.currentIndexChanged.connect(self.on_combo_selection)
19
19
  main_layout = QVBoxLayout()
20
- self.stretch = QSpacerItem(
21
- 10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
20
+ main_layout.setAlignment(QtCore.Qt.AlignTop)
22
21
  main_layout.addWidget(self.combo_items)
23
22
  self.layout = QVBoxLayout()
24
- self.layout.addItem(self.stretch)
25
23
  main_layout.addLayout(self.layout)
26
24
  self.setLayout(main_layout)
27
25
  self.setWindowTitle("Setting Window")
@@ -39,17 +37,15 @@ class SettingWindow(QWidget):
39
37
  child.widget().deleteLater()
40
38
 
41
39
  def on_combo_selection(self, index):
42
- self.layout.removeItem(self.stretch)
43
40
  # remove all setting of previous widget
44
41
  self.clear_setting()
45
-
46
42
  key = list(self.items.keys())
47
43
  item = self.items[key[index]]
48
- if hasattr(item, "add_setting"):
49
- item.add_setting(self.layout)
50
- self.layout.addItem(self.stretch)
51
- else:
52
- print("%s: No setting." % (item.__class__.__name__))
44
+ group_box = QGroupBox()
45
+ group_layout = QVBoxLayout()
46
+ item.add_setting(group_layout)
47
+ group_box.setLayout(group_layout)
48
+ self.layout.addWidget(group_box)
53
49
 
54
50
 
55
51
  class GLWidget(BaseGLWidget):
@@ -57,7 +53,7 @@ class GLWidget(BaseGLWidget):
57
53
  self.followed_name = 'none'
58
54
  self.named_items = {}
59
55
  self.color_str = '#000000'
60
- self.followable_item_name = ['none']
56
+ self.followable_item_name = None
61
57
  self.setting_window = SettingWindow()
62
58
  self.enable_show_center = True
63
59
  super(GLWidget, self).__init__()
@@ -73,7 +69,8 @@ class GLWidget(BaseGLWidget):
73
69
 
74
70
  def update(self):
75
71
  if self.followed_name != 'none':
76
- self.center = self.named_items[self.followed_name].T[:3, 3]
72
+ new_center = self.named_items[self.followed_name].T[:3, 3]
73
+ self.set_center(new_center)
77
74
  super().update()
78
75
 
79
76
  def add_setting(self, layout):
@@ -90,6 +87,10 @@ class GLWidget(BaseGLWidget):
90
87
 
91
88
  label_focus = QLabel("Set Focus:")
92
89
  combo_focus = QComboBox()
90
+
91
+ if self.followable_item_name is None:
92
+ self.initial_followable()
93
+
93
94
  for name in self.followable_item_name:
94
95
  combo_focus.addItem(name)
95
96
  combo_focus.currentIndexChanged.connect(self.on_followable_selection)
@@ -101,6 +102,12 @@ class GLWidget(BaseGLWidget):
101
102
  checkbox_show_center.stateChanged.connect(self.change_show_center)
102
103
  layout.addWidget(checkbox_show_center)
103
104
 
105
+ def initial_followable(self):
106
+ self.followable_item_name = ['none']
107
+ for name, item in self.named_items.items():
108
+ if item.__class__.__name__ == 'AxisItem' and not item._disable_setting:
109
+ self.followable_item_name.append(name)
110
+
104
111
  def set_bg_color(self, color_str):
105
112
  try:
106
113
  color_flat = int(color_str[1:], 16)
@@ -114,9 +121,8 @@ class GLWidget(BaseGLWidget):
114
121
 
115
122
  def add_item_with_name(self, name, item):
116
123
  self.named_items.update({name: item})
117
- if (item.__class__.__name__ == 'AxisItem'):
118
- self.followable_item_name.append(name)
119
- self.setting_window.add_setting(name, item)
124
+ if not item._disable_setting:
125
+ self.setting_window.add_setting(name, item)
120
126
  super().add_item(item)
121
127
 
122
128
  def open_setting_window(self):
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
5
+ Distributed under MIT license. See LICENSE for more information.
6
+ """
7
+
8
+ """
9
+ this script tests interpolation of 3D poses.
10
+ """
11
+
12
+
13
+ import numpy as np
14
+ from q3dviewer.utils.maths import expSO3, logSO3, makeT, makeRt
15
+
16
+
17
+ def interpolate_pose(T1, T2, v_max, omega_max, dt=0.1):
18
+ R1, t1 = makeRt(T1)
19
+ R2, t2 = makeRt(T2)
20
+
21
+ # Get transform time based on linear velocity
22
+ d = np.linalg.norm(t2 - t1)
23
+ t_lin = d / v_max
24
+
25
+ # Get transform time based on angular velocity
26
+ omega = logSO3(R2 @ R1.T)
27
+ theta = np.linalg.norm(omega)
28
+ t_ang = theta / omega_max
29
+
30
+ # Get total time based on the linear and angular time
31
+ t_total = max(t_lin, t_ang)
32
+ num_steps = int(np.ceil(t_total / dt))
33
+
34
+ # Generate interpolated transforms
35
+ interpolated_Ts = []
36
+ for i in range(num_steps + 1):
37
+ s = i / num_steps
38
+ t_interp = (1 - s) * t1 + s * t2
39
+ # Interpolate rotation using SO3
40
+ R_interp = expSO3(s * omega) @ R1
41
+ T_interp = makeT(R_interp, t_interp)
42
+ interpolated_Ts.append(T_interp)
43
+
44
+ return interpolated_Ts
45
+
46
+ if __name__ == "__main__":
47
+ T1 = np.eye(4) # Identity transformation
48
+ T2 = np.array([[0, -1, 0, 1], [1, 0, 0, 2], [0, 0, 1, 3], [0, 0, 0, 1]]) # Target transformation
49
+
50
+ v_max = 1.0 # Maximum linear velocity (m/s)
51
+ omega_max = np.pi / 4 # Maximum angular velocity (rad/s)
52
+
53
+ # Perform interpolation
54
+ interpolated_poses = interpolate_pose(T1, T2, v_max, omega_max)
55
+ for i, T in enumerate(interpolated_poses):
56
+ print(f"Step {i}:\n{T}\n")
57
+
58
+
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
5
+ Distributed under MIT license. See LICENSE for more information.
6
+ """
7
+
8
+ """
9
+ this script tests the rendering of a cloud in a camera frame based on the
10
+ camera pose and intrinsic matrix
11
+ """
12
+
13
+ import numpy as np
14
+ import q3dviewer as q3d
15
+ import cv2
16
+
17
+ cloud, _ = q3d.load_pcd('/home/liu/lab.pcd')
18
+
19
+
20
+ Tcw = np.array([[7.07106781e-01, 7.07106781e-01, 0.00000000e+00,
21
+ 0.00000000e+00],
22
+ [-3.53553391e-01, 3.53553391e-01, 8.66025404e-01,
23
+ 3.55271368e-15],
24
+ [6.12372436e-01, -6.12372436e-01, 5.00000000e-01,
25
+ -4.00000000e+01],
26
+ [0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
27
+ 1.00000000e+00]])
28
+ # convert the opengl camera coordinate to the opencv camera coordinate
29
+ Tconv = np.array([[1, 0, 0, 0],
30
+ [0, -1, 0, 0],
31
+ [0, 0, -1, 0],
32
+ [0, 0, 0, 1]])
33
+
34
+ Tcw = Tconv @ Tcw
35
+
36
+ K = np.array([[1.64718029e+03, 0.00000000e+00, 9.51000000e+02],
37
+ [0.00000000e+00, 1.64718036e+03, 5.31000000e+02],
38
+ [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
39
+
40
+
41
+ def render_frame(cloud, Tcw, K, width, height):
42
+ image = np.zeros((height, width, 3), dtype=np.uint8)
43
+ Rcw, tcw = Tcw[:3, :3], Tcw[:3, 3]
44
+ pc = (Rcw @ cloud['xyz'].T).T + tcw
45
+ uv = (K @ pc.T).T
46
+ uv = uv[:, :2] / uv[:, 2][:, np.newaxis]
47
+ mask = (pc[:, 2] > 0) & (uv[:, 0] > 0) & (
48
+ uv[:, 0] < width) & (uv[:, 1] > 0) & (uv[:, 1] < height)
49
+ uv = uv[mask]
50
+ u = uv[:, 0].astype(int)
51
+ v = uv[:, 1].astype(int)
52
+ rgb = cloud['irgb'][mask]
53
+ r = rgb >> 16 & 0xff
54
+ g = rgb >> 8 & 0xff
55
+ b = rgb & 0xff
56
+
57
+ # Sort by depth to ensure front points are drawn first
58
+ depth = pc[mask, 2]
59
+ sorted_indices = np.argsort(depth)
60
+ u = u[sorted_indices]
61
+ v = v[sorted_indices]
62
+ r = r[sorted_indices]
63
+ g = g[sorted_indices]
64
+ b = b[sorted_indices]
65
+
66
+ image[v, u] = np.stack([b, g, r], axis=1)
67
+ return image
68
+
69
+
70
+ image = render_frame(cloud, Tcw, K, 1902, 1062)
71
+ cv2.imshow('image', image)
72
+ cv2.waitKey(0)