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.
- q3dviewer/base_glwidget.py +53 -8
- q3dviewer/base_item.py +15 -0
- q3dviewer/custom_items/axis_item.py +31 -94
- q3dviewer/custom_items/cloud_item.py +36 -30
- q3dviewer/custom_items/frame_item.py +56 -36
- q3dviewer/custom_items/gaussian_item.py +3 -3
- q3dviewer/custom_items/grid_item.py +88 -37
- q3dviewer/custom_items/image_item.py +1 -2
- q3dviewer/custom_items/line_item.py +4 -5
- q3dviewer/gau_io.py +0 -168
- q3dviewer/glwidget.py +22 -16
- q3dviewer/test/test_interpolation.py +58 -0
- q3dviewer/test/test_rendering.py +72 -0
- q3dviewer/tools/cinematographer.py +367 -0
- q3dviewer/tools/film_maker.py +395 -0
- q3dviewer/tools/lidar_calib.py +11 -22
- q3dviewer/tools/lidar_cam_calib.py +9 -20
- q3dviewer/utils/maths.py +155 -5
- q3dviewer/viewer.py +30 -7
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.0.9.dist-info}/METADATA +8 -15
- q3dviewer-1.0.9.dist-info/RECORD +45 -0
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.0.9.dist-info}/entry_points.txt +1 -1
- q3dviewer/basic_window.py +0 -228
- q3dviewer/cloud_viewer.py +0 -74
- q3dviewer/custom_items/camera_frame_item.py +0 -173
- q3dviewer/custom_items/trajectory_item.py +0 -79
- q3dviewer/utils.py +0 -71
- q3dviewer-1.0.8.dist-info/RECORD +0 -46
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.0.9.dist-info}/LICENSE +0 -0
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.0.9.dist-info}/WHEEL +0 -0
- {q3dviewer-1.0.8.dist-info → q3dviewer-1.0.9.dist-info}/top_level.txt +0 -0
q3dviewer/viewer.py
CHANGED
|
@@ -6,7 +6,7 @@ Distributed under MIT license. See LICENSE for more information.
|
|
|
6
6
|
|
|
7
7
|
from q3dviewer.glwidget import *
|
|
8
8
|
import signal
|
|
9
|
-
from PySide6.QtWidgets import QMainWindow, QApplication
|
|
9
|
+
from PySide6.QtWidgets import QMainWindow, QApplication, QHBoxLayout
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def handler(signal, frame):
|
|
@@ -15,22 +15,45 @@ def handler(signal, frame):
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class Viewer(QMainWindow):
|
|
18
|
-
def __init__(self, name='Viewer', win_size=[1920, 1080]
|
|
18
|
+
def __init__(self, name='Viewer', win_size=[1920, 1080],
|
|
19
|
+
gl_widget_class=GLWidget, update_interval=20):
|
|
19
20
|
signal.signal(signal.SIGINT, handler)
|
|
20
21
|
super(Viewer, self).__init__()
|
|
21
22
|
self.setGeometry(0, 0, win_size[0], win_size[1])
|
|
23
|
+
self.gl_widget_class = gl_widget_class
|
|
22
24
|
self.init_ui()
|
|
25
|
+
self.update_interval = update_interval
|
|
26
|
+
self.add_update_timer()
|
|
23
27
|
self.setWindowTitle(name)
|
|
28
|
+
self.installEventFilter(self)
|
|
24
29
|
|
|
25
30
|
def init_ui(self):
|
|
26
31
|
center_widget = QWidget()
|
|
27
32
|
self.setCentralWidget(center_widget)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
self.
|
|
33
|
+
main_layout = QHBoxLayout()
|
|
34
|
+
self.add_control_panel(main_layout)
|
|
35
|
+
center_widget.setLayout(main_layout)
|
|
36
|
+
self.glwidget = self.gl_widget_class()
|
|
37
|
+
main_layout.addWidget(self.glwidget, 1)
|
|
38
|
+
self.default_gl_setting(self.glwidget)
|
|
39
|
+
|
|
40
|
+
def add_control_panel(self, main_layout):
|
|
41
|
+
"""
|
|
42
|
+
Override this function to add your own control panel to
|
|
43
|
+
the left side of the main window.
|
|
44
|
+
Don't forget add your own layout to the main_layout.
|
|
45
|
+
"""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def default_gl_setting(self, glwidget):
|
|
49
|
+
"""
|
|
50
|
+
Override this function to set the default opengl setting of the viewer.
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def add_update_timer(self):
|
|
32
55
|
timer = QtCore.QTimer(self)
|
|
33
|
-
timer.setInterval(
|
|
56
|
+
timer.setInterval(self.update_interval) # period, in milliseconds
|
|
34
57
|
timer.timeout.connect(self.update)
|
|
35
58
|
timer.start()
|
|
36
59
|
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: q3dviewer
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
|
|
5
|
-
Home-page: UNKNOWN
|
|
6
|
-
Author: UNKNOWN
|
|
7
|
-
Author-email: UNKNOWN
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
-
Requires-Dist: PyOpenGL
|
|
11
|
-
Requires-Dist: laspy
|
|
12
|
-
Requires-Dist: meshio
|
|
3
|
+
Version: 1.0.9
|
|
4
|
+
License-File: LICENSE
|
|
13
5
|
Requires-Dist: numpy
|
|
6
|
+
Requires-Dist: pyside6
|
|
7
|
+
Requires-Dist: PyOpenGL
|
|
14
8
|
Requires-Dist: pillow
|
|
15
|
-
Requires-Dist:
|
|
9
|
+
Requires-Dist: meshio
|
|
16
10
|
Requires-Dist: pypcd4
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
Requires-Dist: pye57
|
|
12
|
+
Requires-Dist: laspy
|
|
13
|
+
Requires-Dist: imageio
|
|
21
14
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
q3dviewer/__init__.py,sha256=rP5XX_x8g7hxIMqNHlU89BN4dt5MSvoYYwip68fCmhc,173
|
|
2
|
+
q3dviewer/base_glwidget.py,sha256=7CJKea2JSP78jsESZJP38yfLhhRINF-VQ9R8sMXwAo0,10697
|
|
3
|
+
q3dviewer/base_item.py,sha256=lzb04oRaS4rRJrAP6C1Bu4ugK237FgupMTB97zjNVFw,1768
|
|
4
|
+
q3dviewer/gau_io.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
q3dviewer/glwidget.py,sha256=im8hjVYEL0Zl7fOIHTQMJdWu7WNOHlvTdIDYjebz9WA,4940
|
|
6
|
+
q3dviewer/viewer.py,sha256=LH1INLFhi6pRjzazzQJ0AWT4hgyXI6GnmqoJFUwUZVE,2517
|
|
7
|
+
q3dviewer/custom_items/__init__.py,sha256=gOiAxdjDaAnFL8YbqSEWWWOwUrJfvzP9JLR34sCB9-4,434
|
|
8
|
+
q3dviewer/custom_items/axis_item.py,sha256=PTBSf5DmQI8ieSinYjY_aC7P8q1nzE-2Vc2GNd1O3Os,2568
|
|
9
|
+
q3dviewer/custom_items/cloud_io_item.py,sha256=gjK3n9WKB7JwxC93ijkweEHA5EezpgNJ8KO-PBaDKCs,2835
|
|
10
|
+
q3dviewer/custom_items/cloud_item.py,sha256=7r6s_j1qEUfLe70t-M68-oIrGRrPOUcZp18ML6jST0s,12542
|
|
11
|
+
q3dviewer/custom_items/frame_item.py,sha256=6BOM3MXi-Akv6KUXDC3QYEAXKxwk0Eo1KQn-F7jkqrQ,7559
|
|
12
|
+
q3dviewer/custom_items/gaussian_item.py,sha256=CZoXMmj2JPFfMqu7v4q4dLUI2cg_WfE1DHWGXjEYn6M,9869
|
|
13
|
+
q3dviewer/custom_items/grid_item.py,sha256=20n4TGm5YEaudhnEOCOk-HtsKwxVxaPr8YV36kO04yU,4802
|
|
14
|
+
q3dviewer/custom_items/image_item.py,sha256=ctNR81fVgxkdl2n3U_TPFL6yn086UhNI_A9fFHgkc-4,5491
|
|
15
|
+
q3dviewer/custom_items/line_item.py,sha256=u0oFN2iHzsRHtnbvyvC_iglEkCwEU2NnTuE536sKUAE,4348
|
|
16
|
+
q3dviewer/custom_items/text_item.py,sha256=nuHMVMQrwy50lNk9hxB94criFxbJJK-SYiK2fSXWUMQ,2158
|
|
17
|
+
q3dviewer/shaders/cloud_frag.glsl,sha256=tbCsDUp9YlPe0hRWlFS724SH6TtMeLO-GVYROzEElZg,609
|
|
18
|
+
q3dviewer/shaders/cloud_vert.glsl,sha256=Vxgw-Zrr0knAK0z4qMXKML6IC4EbffKMwYN2TMXROoI,2117
|
|
19
|
+
q3dviewer/shaders/gau_frag.glsl,sha256=5_UY84tWDts59bxP8x4I-wgnzY8aGeGuo28wX--LW7E,975
|
|
20
|
+
q3dviewer/shaders/gau_prep.glsl,sha256=eCT9nm65uz32w8NaDjeGKhyAZh42Aea-QTwr3yQVr9U,7218
|
|
21
|
+
q3dviewer/shaders/gau_vert.glsl,sha256=NNbVhv_JyqZDK9iXAyBAcIHAtim7G9yWbC9IaUfTL1w,1666
|
|
22
|
+
q3dviewer/shaders/sort_by_key.glsl,sha256=CA2zOcbyDGYAJSJEUvgjUqNshg9NAehf8ipL3Jsv4qE,1097
|
|
23
|
+
q3dviewer/test/test_interpolation.py,sha256=rR_CXsYFLpn0zO0mHf_jL-naluDBMSky--FviOQga0Q,1657
|
|
24
|
+
q3dviewer/test/test_rendering.py,sha256=gbTcu7-cg20DgC5Zoi17C1s5lBGLfAE1rW9biqPjRsA,2164
|
|
25
|
+
q3dviewer/tools/__init__.py,sha256=01wG7BGM6VX0QyFBKsqPmyf2e-vrmV_N3-mo-VQ1VBg,20
|
|
26
|
+
q3dviewer/tools/cinematographer.py,sha256=o_24SSQ4mF062QQ7Gv3i90v7fA79PcHLB03UHXufuEA,13950
|
|
27
|
+
q3dviewer/tools/cloud_viewer.py,sha256=lNvOD0XWDAfgdqc2aJGigcCJsaWUS4qxadjFwf59-OY,3861
|
|
28
|
+
q3dviewer/tools/example_viewer.py,sha256=yeVXT0k4-h1vTLKnGzWADZD3our6XUaYUTy0p5daTkE,959
|
|
29
|
+
q3dviewer/tools/film_maker.py,sha256=MtmmL1-iogSmHXCZc0MLk35_bP-RMBN8twaOtTkv77w,15180
|
|
30
|
+
q3dviewer/tools/gaussian_viewer.py,sha256=vIwWmiFhjNmknrEkBLzt2yiegeH7LP3OeNjnGM6GzaI,1633
|
|
31
|
+
q3dviewer/tools/lidar_calib.py,sha256=M01bGg2mT8LwVcYybolr4UW_UUaR-f-BFciEHtjeK-w,10488
|
|
32
|
+
q3dviewer/tools/lidar_cam_calib.py,sha256=SYNLDvi15MX7Q3aGn771fvu1cES9xeXgP0_WmDq33w4,11200
|
|
33
|
+
q3dviewer/tools/ros_viewer.py,sha256=ARB3I5wohY3maP8dCu0O0hxObd6JFKuK2y7AApVgMWA,2551
|
|
34
|
+
q3dviewer/utils/__init__.py,sha256=irm8Z_bT8l9kzhoMlds2Dal8g4iw4vjmqNPZSs4W6e0,157
|
|
35
|
+
q3dviewer/utils/cloud_io.py,sha256=ttD8FJExdDhXB1Z0Ej9S939i8gcq4JfyqwLXVh8CEFw,11094
|
|
36
|
+
q3dviewer/utils/convert_ros_msg.py,sha256=sAoQfy3qLQKsIArBAVm8H--wlQXOcmkKK3-Ox9UCcrc,1686
|
|
37
|
+
q3dviewer/utils/gl_helper.py,sha256=dRY_kUqyPMr7NTcupUr6_VTvgnj53iE2C0Lk0-oFYsI,1435
|
|
38
|
+
q3dviewer/utils/maths.py,sha256=5TmjWUX1K3UjygXxrUsydjbo7tPzu0gD-yy7qtQUGBU,10588
|
|
39
|
+
q3dviewer/utils/range_slider.py,sha256=jZJQL-uQgnpgLvtYSWpKTrJlLkt3aqNpaRQAePEpNd0,3174
|
|
40
|
+
q3dviewer-1.0.9.dist-info/LICENSE,sha256=81cMOyNfw8KLb1JnPYngGHJ5W83gSbZEBU9MEP3tl-E,1124
|
|
41
|
+
q3dviewer-1.0.9.dist-info/METADATA,sha256=zD6g_ySQ3hityOMXY5cKo1wpbaYk0whJEVzQIRReqL4,275
|
|
42
|
+
q3dviewer-1.0.9.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
|
|
43
|
+
q3dviewer-1.0.9.dist-info/entry_points.txt,sha256=aeUdGH7UIgMZEMFUc-0xPZWspY95GoPdZcZuLceq85g,361
|
|
44
|
+
q3dviewer-1.0.9.dist-info/top_level.txt,sha256=HFFDCbGu28txcGe2HPc46A7EPaguBa_b5oH7bufmxHM,10
|
|
45
|
+
q3dviewer-1.0.9.dist-info/RECORD,,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[console_scripts]
|
|
2
2
|
cloud_viewer = q3dviewer.tools.cloud_viewer:main
|
|
3
|
+
film_maker = q3dviewer.tools.film_maker:main
|
|
3
4
|
gaussian_viewer = q3dviewer.tools.gaussian_viewer:main
|
|
4
5
|
lidar_calib = q3dviewer.tools.lidar_calib:main
|
|
5
6
|
lidar_cam_calib = q3dviewer.tools.lidar_cam_calib:main
|
|
6
7
|
mesh_viewer = q3dviewer.tools.mesh_viewer:main
|
|
7
8
|
ros_viewer = q3dviewer.tools.ros_viewer:main
|
|
8
|
-
|
q3dviewer/basic_window.py
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
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()
|
q3dviewer/cloud_viewer.py
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
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()
|
|
@@ -1,173 +0,0 @@
|
|
|
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)
|