q3dviewer 1.0.7__tar.gz → 1.1.1__tar.gz
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-1.1.1/LICENSE +21 -0
- q3dviewer-1.1.1/PKG-INFO +205 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/README.md +54 -25
- q3dviewer-1.1.1/pyproject.toml +26 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/base_glwidget.py +71 -10
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/base_item.py +15 -0
- q3dviewer-1.1.1/q3dviewer/custom_items/axis_item.py +85 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/cloud_item.py +43 -31
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/frame_item.py +56 -36
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/gaussian_item.py +3 -3
- q3dviewer-1.1.1/q3dviewer/custom_items/grid_item.py +139 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/image_item.py +1 -2
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/line_item.py +4 -5
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/glwidget.py +22 -17
- q3dviewer-1.1.1/q3dviewer/shaders/cloud_frag.glsl +28 -0
- q3dviewer-1.1.1/q3dviewer/shaders/cloud_vert.glsl +72 -0
- q3dviewer-1.1.1/q3dviewer/shaders/gau_frag.glsl +42 -0
- q3dviewer-1.1.1/q3dviewer/shaders/gau_prep.glsl +249 -0
- q3dviewer-1.1.1/q3dviewer/shaders/gau_vert.glsl +77 -0
- q3dviewer-1.1.1/q3dviewer/shaders/sort_by_key.glsl +56 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/tools/cloud_viewer.py +2 -2
- q3dviewer-1.1.1/q3dviewer/tools/film_maker.py +421 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/tools/lidar_calib.py +11 -22
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/tools/lidar_cam_calib.py +9 -20
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/tools/ros_viewer.py +7 -8
- q3dviewer-1.1.1/q3dviewer/utils/maths.py +318 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/viewer.py +30 -7
- q3dviewer-1.1.1/q3dviewer.egg-info/PKG-INFO +205 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer.egg-info/SOURCES.txt +9 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer.egg-info/entry_points.txt +1 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer.egg-info/requires.txt +2 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/setup.py +15 -2
- q3dviewer-1.0.7/PKG-INFO +0 -10
- q3dviewer-1.0.7/q3dviewer/custom_items/axis_item.py +0 -148
- q3dviewer-1.0.7/q3dviewer/custom_items/grid_item.py +0 -88
- q3dviewer-1.0.7/q3dviewer/utils/maths.py +0 -168
- q3dviewer-1.0.7/q3dviewer.egg-info/PKG-INFO +0 -10
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/__init__.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/__init__.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/cloud_io_item.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/custom_items/text_item.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/tools/__init__.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/tools/example_viewer.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/tools/gaussian_viewer.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/utils/__init__.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/utils/cloud_io.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/utils/convert_ros_msg.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/utils/gl_helper.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer/utils/range_slider.py +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer.egg-info/dependency_links.txt +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/q3dviewer.egg-info/top_level.txt +0 -0
- {q3dviewer-1.0.7 → q3dviewer-1.1.1}/setup.cfg +0 -0
q3dviewer-1.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright 2024 Panasonic Advanced Technology Development Co.,Ltd. (Liu Yang)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
q3dviewer-1.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: q3dviewer
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: A library designed for quickly deploying a 3D viewer.
|
|
5
|
+
Home-page: https://github.com/scomup/q3dviewer
|
|
6
|
+
Author: Liu Yang
|
|
7
|
+
License: UNKNOWN
|
|
8
|
+
Description: ## q3dviewer
|
|
9
|
+
|
|
10
|
+
`q3dviewer` is a library designed for quickly deploying a 3D viewer. It is based on Qt (PySide6) and provides efficient OpenGL items for displaying 3D objects (e.g., point clouds, cameras, and 3D Gaussians). You can use it to visualize your 3D data or set up an efficient viewer application. It is inspired by PyQtGraph but focuses more on efficient 3D rendering.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
To install `q3dviewer`, execute the following command in your terminal on either Linux or Windows:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install q3dviewer
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Note for Windows Users
|
|
21
|
+
|
|
22
|
+
- Ensure that you have a Python 3 environment set up:
|
|
23
|
+
- Download and install Python 3 from the [official Python website](https://www.python.org/downloads/).
|
|
24
|
+
- During installation, make sure to check the "Add Python to PATH" option.
|
|
25
|
+
|
|
26
|
+
### Note for Linux Users
|
|
27
|
+
|
|
28
|
+
If you encounter an error related to loading the shared library `libxcb-cursor.so.0` on Ubuntu 20.04 or 22.04, please install `libxcb-cursor0`:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
sudo apt-get install libxcb-cursor0
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Tools
|
|
35
|
+
|
|
36
|
+
Once installed, you can directly use the following tools:
|
|
37
|
+
|
|
38
|
+
### 1. Cloud Viewer
|
|
39
|
+
|
|
40
|
+
A tool for visualizing point cloud files. Launch it by executing the following command in your terminal:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
cloud_viewer # The viewer will be displayed
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
*Alternatively*, if the path is not set (though it's not recommended):
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
python3 -m q3dviewer.tools.cloud_viewer
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
After the viewer launches, you can drag and drop files onto the window to display the point clouds. Multiple files can be dropped simultaneously to view them together. Supported formats include LAS, PCD, PLY, and E57.
|
|
53
|
+
|
|
54
|
+
For example, you can download and view point clouds of Tokyo in LAS format from the following link:
|
|
55
|
+
|
|
56
|
+
[Tokyo Point Clouds](https://www.geospatial.jp/ckan/dataset/tokyopc-23ku-2024/resource/7807d6d1-29f3-4b36-b0c8-f7aa0ea2cff3)
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+
Press `M` on your keyboard to display a menu on the screen, where you can modify visualization settings for each item. For example, you can adjust various settings such as shape, size, color, and transparency for `CloudItem`.
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
### 2. ROS Viewer
|
|
65
|
+
|
|
66
|
+
A high-performance SLAM viewer compatible with ROS, serving as an alternative to RVIZ.
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
roscore &
|
|
70
|
+
ros_viewer
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. Film Maker
|
|
74
|
+
|
|
75
|
+
Would you like to create a video from point cloud data? With Film Maker, you can easily create videos with simple operations. Just edit keyframes using the user-friendly GUI, and the software will automatically interpolate the keyframes to generate the video.
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
film_maker # drag and drop your cloud file to the window
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
* Space key to add a keyframe.
|
|
82
|
+
* Delete key to remove a keyframe.
|
|
83
|
+
|
|
84
|
+
Film Maker GUI:
|
|
85
|
+
|
|
86
|
+

|
|
87
|
+
|
|
88
|
+
### 4. Gaussian Viewer
|
|
89
|
+
|
|
90
|
+
A simple viewer for 3D Gaussians. See [EasyGaussianSplatting](https://github.com/scomup/EasyGaussianSplatting) for more information.
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
gaussian_viewer # Drag and drop your Gaussian file onto the window
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+

|
|
97
|
+
|
|
98
|
+
### 5. LiDAR-LiDAR Calibration Tools
|
|
99
|
+
|
|
100
|
+
A tool to compute the relative pose between two LiDARs. It allows for both manual adjustment in the settings screen and automatic calibration.
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
lidar_calib --lidar0=/YOUR_LIDAR0_TOPIC --lidar1=/YOUR_LIDAR1_TOPIC
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+

|
|
107
|
+
|
|
108
|
+
### 6. LiDAR-Camera Calibration Tools
|
|
109
|
+
|
|
110
|
+
A tool for calculating the relative pose between a LiDAR and a camera. It allows for manual adjustment in the settings screen and real-time verification of LiDAR point projection onto images.
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
lidar_cam_calib --lidar=/YOUR_LIDAR_TOPIC --camera=/YOUR_CAMERA_TOPIC --camera_info=/YOUR_CAMERA_INFO_TOPIC
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+

|
|
117
|
+
|
|
118
|
+
## Using as a Library
|
|
119
|
+
|
|
120
|
+
Using the examples above, you can easily customize and develop your own 3D viewer with `q3dviewer`. Below is a coding example.
|
|
121
|
+
|
|
122
|
+
### Custom 3D Viewer
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
#!/usr/bin/env python3
|
|
126
|
+
|
|
127
|
+
import q3dviewer as q3d # Import q3dviewer
|
|
128
|
+
|
|
129
|
+
def main():
|
|
130
|
+
# Create a Qt application
|
|
131
|
+
app = q3d.QApplication([])
|
|
132
|
+
|
|
133
|
+
# Create various 3D items
|
|
134
|
+
axis_item = q3d.AxisItem(size=0.5, width=5)
|
|
135
|
+
grid_item = q3d.GridItem(size=10, spacing=1)
|
|
136
|
+
|
|
137
|
+
# Create a viewer
|
|
138
|
+
viewer = q3d.Viewer(name='example')
|
|
139
|
+
|
|
140
|
+
# Add items to the viewer
|
|
141
|
+
viewer.add_items({
|
|
142
|
+
'grid': grid_item,
|
|
143
|
+
'axis': axis_item,
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
# Show the viewer & run the Qt application
|
|
147
|
+
viewer.show()
|
|
148
|
+
app.exec()
|
|
149
|
+
|
|
150
|
+
if __name__ == '__main__':
|
|
151
|
+
main()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
`q3dviewer` provides the following 3D items:
|
|
155
|
+
|
|
156
|
+
- **AxisItem**: Displays coordinate axes or the origin position.
|
|
157
|
+
- **CloudItem**: Displays point clouds.
|
|
158
|
+
- **CloudIOItem**: Displays point clouds with input/output capabilities.
|
|
159
|
+
- **GaussianItem**: Displays 3D Gaussians.
|
|
160
|
+
- **GridItem**: Displays grids.
|
|
161
|
+
- **ImageItem**: Displays 2D images.
|
|
162
|
+
- **Text2DItem**: Displays 2D text.
|
|
163
|
+
- **LineItem**: Displays lines or trajectories.
|
|
164
|
+
|
|
165
|
+
### Developing Custom Items
|
|
166
|
+
|
|
167
|
+
In addition to the standard 3D items provided, you can visualize custom 3D items with simple coding. Below is a sample:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from OpenGL.GL import *
|
|
171
|
+
import numpy as np
|
|
172
|
+
import q3dviewer as q3d
|
|
173
|
+
from PySide6.QtWidgets import QLabel, QSpinBox
|
|
174
|
+
|
|
175
|
+
class YourItem(q3d.BaseItem):
|
|
176
|
+
def __init__(self):
|
|
177
|
+
super(YourItem, self).__init__()
|
|
178
|
+
# Necessary initialization
|
|
179
|
+
|
|
180
|
+
def add_setting(self, layout):
|
|
181
|
+
# Initialize the settings screen
|
|
182
|
+
label = QLabel("Add your setting:")
|
|
183
|
+
layout.addWidget(label)
|
|
184
|
+
box = QSpinBox()
|
|
185
|
+
layout.addWidget(box)
|
|
186
|
+
|
|
187
|
+
def set_data(self, data):
|
|
188
|
+
# Obtain the data you want to visualize
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
def initialize_gl(self):
|
|
192
|
+
# OpenGL initialization settings (if needed)
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
def paint(self):
|
|
196
|
+
# Visualize 3D objects using OpenGL
|
|
197
|
+
pass
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Enjoy using `q3dviewer`!
|
|
201
|
+
Platform: UNKNOWN
|
|
202
|
+
Classifier: Programming Language :: Python :: 3
|
|
203
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
204
|
+
Classifier: Operating System :: OS Independent
|
|
205
|
+
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## q3dviewer
|
|
2
2
|
|
|
3
|
-
`q3dviewer` is a library designed for quickly deploying a 3D viewer. It is based on Qt (
|
|
3
|
+
`q3dviewer` is a library designed for quickly deploying a 3D viewer. It is based on Qt (PySide6) and provides efficient OpenGL items for displaying 3D objects (e.g., point clouds, cameras, and 3D Gaussians). You can use it to visualize your 3D data or set up an efficient viewer application. It is inspired by PyQtGraph but focuses more on efficient 3D rendering.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -16,6 +16,14 @@ pip install q3dviewer
|
|
|
16
16
|
- Download and install Python 3 from the [official Python website](https://www.python.org/downloads/).
|
|
17
17
|
- During installation, make sure to check the "Add Python to PATH" option.
|
|
18
18
|
|
|
19
|
+
### Note for Linux Users
|
|
20
|
+
|
|
21
|
+
If you encounter an error related to loading the shared library `libxcb-cursor.so.0` on Ubuntu 20.04 or 22.04, please install `libxcb-cursor0`:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
sudo apt-get install libxcb-cursor0
|
|
25
|
+
```
|
|
26
|
+
|
|
19
27
|
## Tools
|
|
20
28
|
|
|
21
29
|
Once installed, you can directly use the following tools:
|
|
@@ -26,44 +34,61 @@ A tool for visualizing point cloud files. Launch it by executing the following c
|
|
|
26
34
|
|
|
27
35
|
```sh
|
|
28
36
|
cloud_viewer # The viewer will be displayed
|
|
29
|
-
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
*Alternatively*, if the path is not set (though it's not recommended):
|
|
40
|
+
|
|
41
|
+
```sh
|
|
30
42
|
python3 -m q3dviewer.tools.cloud_viewer
|
|
31
43
|
```
|
|
32
44
|
|
|
33
45
|
After the viewer launches, you can drag and drop files onto the window to display the point clouds. Multiple files can be dropped simultaneously to view them together. Supported formats include LAS, PCD, PLY, and E57.
|
|
34
46
|
|
|
35
|
-
For example, you can download and
|
|
47
|
+
For example, you can download and view point clouds of Tokyo in LAS format from the following link:
|
|
36
48
|
|
|
37
49
|
[Tokyo Point Clouds](https://www.geospatial.jp/ckan/dataset/tokyopc-23ku-2024/resource/7807d6d1-29f3-4b36-b0c8-f7aa0ea2cff3)
|
|
38
50
|
|
|
39
|
-

|
|
51
|
+

|
|
40
52
|
|
|
41
|
-
Press `M` on your keyboard to display a menu on the screen, where you can modify visualization settings for each item.
|
|
53
|
+
Press `M` on your keyboard to display a menu on the screen, where you can modify visualization settings for each item. For example, you can adjust various settings such as shape, size, color, and transparency for `CloudItem`.
|
|
42
54
|
|
|
43
|
-

|
|
44
56
|
|
|
45
57
|
### 2. ROS Viewer
|
|
46
58
|
|
|
47
|
-
A high-performance SLAM viewer
|
|
59
|
+
A high-performance SLAM viewer compatible with ROS, serving as an alternative to RVIZ.
|
|
48
60
|
|
|
49
61
|
```sh
|
|
50
62
|
roscore &
|
|
51
63
|
ros_viewer
|
|
52
64
|
```
|
|
53
65
|
|
|
54
|
-
### 3.
|
|
66
|
+
### 3. Film Maker
|
|
67
|
+
|
|
68
|
+
Would you like to create a video from point cloud data? With Film Maker, you can easily create videos with simple operations. Just edit keyframes using the user-friendly GUI, and the software will automatically interpolate the keyframes to generate the video.
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
film_maker # drag and drop your cloud file to the window
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
* Space key to add a keyframe.
|
|
75
|
+
* Delete key to remove a keyframe.
|
|
76
|
+
|
|
77
|
+
Film Maker GUI:
|
|
78
|
+
|
|
79
|
+

|
|
55
80
|
|
|
56
|
-
|
|
57
|
-
see: https://github.com/scomup/EasyGaussianSplatting
|
|
81
|
+
### 4. Gaussian Viewer
|
|
58
82
|
|
|
83
|
+
A simple viewer for 3D Gaussians. See [EasyGaussianSplatting](https://github.com/scomup/EasyGaussianSplatting) for more information.
|
|
59
84
|
|
|
60
85
|
```sh
|
|
61
86
|
gaussian_viewer # Drag and drop your Gaussian file onto the window
|
|
62
87
|
```
|
|
63
88
|
|
|
64
|
-

|
|
89
|
+

|
|
65
90
|
|
|
66
|
-
###
|
|
91
|
+
### 5. LiDAR-LiDAR Calibration Tools
|
|
67
92
|
|
|
68
93
|
A tool to compute the relative pose between two LiDARs. It allows for both manual adjustment in the settings screen and automatic calibration.
|
|
69
94
|
|
|
@@ -73,7 +98,7 @@ lidar_calib --lidar0=/YOUR_LIDAR0_TOPIC --lidar1=/YOUR_LIDAR1_TOPIC
|
|
|
73
98
|
|
|
74
99
|

|
|
75
100
|
|
|
76
|
-
###
|
|
101
|
+
### 6. LiDAR-Camera Calibration Tools
|
|
77
102
|
|
|
78
103
|
A tool for calculating the relative pose between a LiDAR and a camera. It allows for manual adjustment in the settings screen and real-time verification of LiDAR point projection onto images.
|
|
79
104
|
|
|
@@ -104,6 +129,7 @@ def main():
|
|
|
104
129
|
|
|
105
130
|
# Create a viewer
|
|
106
131
|
viewer = q3d.Viewer(name='example')
|
|
132
|
+
|
|
107
133
|
# Add items to the viewer
|
|
108
134
|
viewer.add_items({
|
|
109
135
|
'grid': grid_item,
|
|
@@ -120,14 +146,14 @@ if __name__ == '__main__':
|
|
|
120
146
|
|
|
121
147
|
`q3dviewer` provides the following 3D items:
|
|
122
148
|
|
|
123
|
-
- **AxisItem**:
|
|
124
|
-
- **CloudItem**:
|
|
125
|
-
- **CloudIOItem**:
|
|
126
|
-
- **GaussianItem**:
|
|
127
|
-
- **GridItem**:
|
|
128
|
-
- **ImageItem**:
|
|
129
|
-
- **Text2DItem**:
|
|
130
|
-
- **LineItem**:
|
|
149
|
+
- **AxisItem**: Displays coordinate axes or the origin position.
|
|
150
|
+
- **CloudItem**: Displays point clouds.
|
|
151
|
+
- **CloudIOItem**: Displays point clouds with input/output capabilities.
|
|
152
|
+
- **GaussianItem**: Displays 3D Gaussians.
|
|
153
|
+
- **GridItem**: Displays grids.
|
|
154
|
+
- **ImageItem**: Displays 2D images.
|
|
155
|
+
- **Text2DItem**: Displays 2D text.
|
|
156
|
+
- **LineItem**: Displays lines or trajectories.
|
|
131
157
|
|
|
132
158
|
### Developing Custom Items
|
|
133
159
|
|
|
@@ -142,7 +168,7 @@ from PySide6.QtWidgets import QLabel, QSpinBox
|
|
|
142
168
|
class YourItem(q3d.BaseItem):
|
|
143
169
|
def __init__(self):
|
|
144
170
|
super(YourItem, self).__init__()
|
|
145
|
-
|
|
171
|
+
# Necessary initialization
|
|
146
172
|
|
|
147
173
|
def add_setting(self, layout):
|
|
148
174
|
# Initialize the settings screen
|
|
@@ -152,13 +178,16 @@ class YourItem(q3d.BaseItem):
|
|
|
152
178
|
layout.addWidget(box)
|
|
153
179
|
|
|
154
180
|
def set_data(self, data):
|
|
155
|
-
|
|
181
|
+
# Obtain the data you want to visualize
|
|
182
|
+
pass
|
|
156
183
|
|
|
157
184
|
def initialize_gl(self):
|
|
158
|
-
|
|
185
|
+
# OpenGL initialization settings (if needed)
|
|
186
|
+
pass
|
|
159
187
|
|
|
160
188
|
def paint(self):
|
|
161
|
-
|
|
189
|
+
# Visualize 3D objects using OpenGL
|
|
190
|
+
pass
|
|
162
191
|
```
|
|
163
192
|
|
|
164
193
|
Enjoy using `q3dviewer`!
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "q3dviewer"
|
|
3
|
+
version = "1.1.0"
|
|
4
|
+
description = "A library designed for quickly deploying a 3D viewer."
|
|
5
|
+
authors = ["Liu Yang <scomup@gmail.com>"]
|
|
6
|
+
|
|
7
|
+
[tool.poetry.dependencies]
|
|
8
|
+
python = "^3.8"
|
|
9
|
+
numpy = "^1.21"
|
|
10
|
+
pyside6 = "^6.2"
|
|
11
|
+
PyOpenGL = "^3.1"
|
|
12
|
+
pillow = "^8.3"
|
|
13
|
+
meshio = "^4.4"
|
|
14
|
+
pypcd4 = "^0.1"
|
|
15
|
+
pye57 = "^0.1"
|
|
16
|
+
laspy = "^2.0"
|
|
17
|
+
imageio = "^2.9"
|
|
18
|
+
|
|
19
|
+
[tool.poetry.scripts]
|
|
20
|
+
cloud_viewer = "q3dviewer.tools.cloud_viewer:main"
|
|
21
|
+
ros_viewer = "q3dviewer.tools.ros_viewer:main"
|
|
22
|
+
mesh_viewer = "q3dviewer.tools.mesh_viewer:main"
|
|
23
|
+
gaussian_viewer = "q3dviewer.tools.gaussian_viewer:main"
|
|
24
|
+
lidar_cam_calib = "q3dviewer.tools.lidar_cam_calib:main"
|
|
25
|
+
lidar_calib = "q3dviewer.tools.lidar_calib:main"
|
|
26
|
+
film_maker = "q3dviewer.tools.film_maker:main"
|
|
@@ -25,6 +25,9 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
25
25
|
self.active_keys = set()
|
|
26
26
|
self.show_center = False
|
|
27
27
|
self.enable_show_center = True
|
|
28
|
+
self.view_need_update = True
|
|
29
|
+
self.view_matrix = self.get_view_matrix()
|
|
30
|
+
self.projection_matrix = self.get_projection_matrix()
|
|
28
31
|
|
|
29
32
|
def keyPressEvent(self, ev: QtGui.QKeyEvent):
|
|
30
33
|
if ev.key() == QtCore.Qt.Key_Up or \
|
|
@@ -87,15 +90,29 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
87
90
|
"""
|
|
88
91
|
for item in self.items:
|
|
89
92
|
item.initialize()
|
|
93
|
+
# initialize the projection matrix and model view matrix
|
|
94
|
+
self.projection_matrix = self.get_projection_matrix()
|
|
95
|
+
self.update_model_projection()
|
|
96
|
+
self.view_matrix = self.get_view_matrix()
|
|
97
|
+
self.update_model_view()
|
|
98
|
+
|
|
99
|
+
def set_view_matrix(self, view_matrix):
|
|
100
|
+
self.view_matrix = view_matrix
|
|
101
|
+
self.view_need_update = False
|
|
90
102
|
|
|
91
103
|
def mouseReleaseEvent(self, ev):
|
|
92
104
|
if hasattr(self, 'mousePos'):
|
|
93
105
|
delattr(self, 'mousePos')
|
|
94
106
|
|
|
107
|
+
def set_dist(self, dist):
|
|
108
|
+
self.dist = dist
|
|
109
|
+
self.view_need_update = True
|
|
110
|
+
|
|
95
111
|
def update_dist(self, delta):
|
|
96
112
|
self.dist += delta
|
|
97
113
|
if self.dist < 0.1:
|
|
98
114
|
self.dist = 0.1
|
|
115
|
+
self.view_need_update = True
|
|
99
116
|
|
|
100
117
|
def wheelEvent(self, ev):
|
|
101
118
|
delta = ev.angleDelta().x()
|
|
@@ -121,17 +138,32 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
121
138
|
dist = max(self.dist, 0.5)
|
|
122
139
|
self.center += Rwc @ Kinv @ np.array([-diff.x(), diff.y(), 0]) * dist
|
|
123
140
|
self.show_center = True
|
|
141
|
+
self.view_need_update = True
|
|
142
|
+
|
|
143
|
+
def set_center(self, center):
|
|
144
|
+
self.center = center
|
|
145
|
+
self.view_need_update = True
|
|
124
146
|
|
|
125
147
|
def paintGL(self):
|
|
126
|
-
|
|
127
|
-
self.
|
|
148
|
+
# if the camera is moved, update the model view matrix.
|
|
149
|
+
if self.view_need_update:
|
|
150
|
+
self.view_matrix = self.get_view_matrix()
|
|
151
|
+
self.view_need_update = False
|
|
128
152
|
self.update_model_view()
|
|
153
|
+
|
|
154
|
+
# set the background color
|
|
129
155
|
bgcolor = self.color
|
|
130
156
|
glClearColor(*bgcolor)
|
|
131
157
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)
|
|
132
158
|
for item in self.items:
|
|
133
159
|
if not item.visible():
|
|
134
160
|
continue
|
|
161
|
+
if not item.is_initialized():
|
|
162
|
+
"""
|
|
163
|
+
The item may not be initialized if it is added
|
|
164
|
+
after the widget is shown, so we need to initialize it here.
|
|
165
|
+
"""
|
|
166
|
+
item.initialize()
|
|
135
167
|
glMatrixMode(GL_MODELVIEW)
|
|
136
168
|
glPushMatrix()
|
|
137
169
|
glPushAttrib(GL_ALL_ATTRIB_BITS)
|
|
@@ -163,37 +195,46 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
163
195
|
# Handle rotation keys
|
|
164
196
|
if QtCore.Qt.Key_Up in self.active_keys:
|
|
165
197
|
self.rotate(radians(rot_speed), 0, 0)
|
|
198
|
+
self.view_need_update = True
|
|
166
199
|
if QtCore.Qt.Key_Down in self.active_keys:
|
|
167
200
|
self.rotate(radians(-rot_speed), 0, 0)
|
|
201
|
+
self.view_need_update = True
|
|
168
202
|
if QtCore.Qt.Key_Left in self.active_keys:
|
|
169
203
|
self.rotate(0, 0, radians(rot_speed))
|
|
204
|
+
self.view_need_update = True
|
|
170
205
|
if QtCore.Qt.Key_Right in self.active_keys:
|
|
171
206
|
self.rotate(0, 0, radians(-rot_speed))
|
|
207
|
+
self.view_need_update = True
|
|
172
208
|
# Handle zoom keys
|
|
173
209
|
xz_keys = {QtCore.Qt.Key_Z, QtCore.Qt.Key_X}
|
|
174
210
|
if self.active_keys & xz_keys:
|
|
175
211
|
Rwc = euler_to_matrix(self.euler)
|
|
176
212
|
if QtCore.Qt.Key_Z in self.active_keys:
|
|
177
213
|
self.center += Rwc @ np.array([0, 0, -trans_speed])
|
|
214
|
+
self.view_need_update = True
|
|
178
215
|
if QtCore.Qt.Key_X in self.active_keys:
|
|
179
216
|
self.center += Rwc @ np.array([0, 0, trans_speed])
|
|
217
|
+
self.view_need_update = True
|
|
180
218
|
# Handle translation keys on the z plane
|
|
181
219
|
dir_keys = {QtCore.Qt.Key_W, QtCore.Qt.Key_S, QtCore.Qt.Key_A, QtCore.Qt.Key_D}
|
|
182
220
|
if self.active_keys & dir_keys:
|
|
183
221
|
Rz = euler_to_matrix([0, 0, self.euler[2]])
|
|
184
222
|
if QtCore.Qt.Key_W in self.active_keys:
|
|
185
223
|
self.center += Rz @ np.array([0, trans_speed, 0])
|
|
224
|
+
self.view_need_update = True
|
|
186
225
|
if QtCore.Qt.Key_S in self.active_keys:
|
|
187
226
|
self.center += Rz @ np.array([0, -trans_speed, 0])
|
|
227
|
+
self.view_need_update = True
|
|
188
228
|
if QtCore.Qt.Key_A in self.active_keys:
|
|
189
229
|
self.center += Rz @ np.array([-trans_speed, 0, 0])
|
|
230
|
+
self.view_need_update = True
|
|
190
231
|
if QtCore.Qt.Key_D in self.active_keys:
|
|
191
232
|
self.center += Rz @ np.array([trans_speed, 0, 0])
|
|
233
|
+
self.view_need_update = True
|
|
192
234
|
|
|
193
235
|
def update_model_view(self):
|
|
194
|
-
m = self.get_view_matrix()
|
|
195
236
|
glMatrixMode(GL_MODELVIEW)
|
|
196
|
-
glLoadMatrixf(
|
|
237
|
+
glLoadMatrixf(self.view_matrix.T)
|
|
197
238
|
|
|
198
239
|
def get_view_matrix(self):
|
|
199
240
|
two = self.center # the origin(center) in the world frame
|
|
@@ -206,12 +247,19 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
206
247
|
return Tcw
|
|
207
248
|
|
|
208
249
|
def set_cam_position(self, **kwargs):
|
|
209
|
-
|
|
250
|
+
center = kwargs.get('center', None)
|
|
210
251
|
distance = kwargs.get('distance', None)
|
|
211
|
-
|
|
212
|
-
|
|
252
|
+
euler = kwargs.get('euler', None)
|
|
253
|
+
if center is not None:
|
|
254
|
+
self.set_center(center)
|
|
213
255
|
if distance is not None:
|
|
214
|
-
self.
|
|
256
|
+
self.set_dist(distance)
|
|
257
|
+
if euler is not None:
|
|
258
|
+
self.set_euler(euler)
|
|
259
|
+
|
|
260
|
+
def set_euler(self, euler):
|
|
261
|
+
self.euler = euler
|
|
262
|
+
self.view_need_update = True
|
|
215
263
|
|
|
216
264
|
def set_color(self, color):
|
|
217
265
|
self.color = color
|
|
@@ -221,9 +269,8 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
221
269
|
super().update()
|
|
222
270
|
|
|
223
271
|
def update_model_projection(self):
|
|
224
|
-
m = self.get_projection_matrix()
|
|
225
272
|
glMatrixMode(GL_PROJECTION)
|
|
226
|
-
glLoadMatrixf(
|
|
273
|
+
glLoadMatrixf(self.projection_matrix.T)
|
|
227
274
|
|
|
228
275
|
def get_projection_matrix(self):
|
|
229
276
|
w, h = self.current_width(), self.current_height()
|
|
@@ -259,3 +306,17 @@ class BaseGLWidget(QtOpenGLWidgets.QOpenGLWidget):
|
|
|
259
306
|
|
|
260
307
|
def change_show_center(self, state):
|
|
261
308
|
self.enable_show_center = state
|
|
309
|
+
|
|
310
|
+
def resizeEvent(self, event):
|
|
311
|
+
super().resizeEvent(event)
|
|
312
|
+
self.projection_matrix = self.get_projection_matrix()
|
|
313
|
+
self.update_model_projection()
|
|
314
|
+
|
|
315
|
+
def capture_frame(self):
|
|
316
|
+
self.makeCurrent() # Ensure the OpenGL context is current
|
|
317
|
+
width = self.current_width()
|
|
318
|
+
height = self.current_height()
|
|
319
|
+
pixels = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE)
|
|
320
|
+
frame = np.frombuffer(pixels, dtype=np.uint8).reshape(height, width, 3)
|
|
321
|
+
frame = np.flip(frame, 0)
|
|
322
|
+
return frame
|
|
@@ -16,6 +16,7 @@ class BaseItem(QtCore.QObject):
|
|
|
16
16
|
self._glwidget = None
|
|
17
17
|
self._visible = True
|
|
18
18
|
self._initialized = False
|
|
19
|
+
self._disable_setting = False
|
|
19
20
|
|
|
20
21
|
def set_glwidget(self, v):
|
|
21
22
|
self._glwidget = v
|
|
@@ -38,6 +39,17 @@ class BaseItem(QtCore.QObject):
|
|
|
38
39
|
def initialize(self):
|
|
39
40
|
if not self._initialized:
|
|
40
41
|
self.initialize_gl()
|
|
42
|
+
self._initialized = True
|
|
43
|
+
|
|
44
|
+
def is_initialized(self):
|
|
45
|
+
return self._initialized
|
|
46
|
+
|
|
47
|
+
def add_setting(self, layout):
|
|
48
|
+
"""
|
|
49
|
+
Add setting widgets to the layout.
|
|
50
|
+
This method should be overridden by subclasses to add any necessary setting widgets to the layout.
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
41
53
|
|
|
42
54
|
def initialize_gl(self):
|
|
43
55
|
"""
|
|
@@ -53,5 +65,8 @@ class BaseItem(QtCore.QObject):
|
|
|
53
65
|
"""
|
|
54
66
|
pass
|
|
55
67
|
|
|
68
|
+
def disable_setting(self):
|
|
69
|
+
self._disable_setting = True
|
|
70
|
+
|
|
56
71
|
|
|
57
72
|
|