forked from limacv/GaussianSplattingViewer
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmain.py
410 lines (348 loc) · 15.3 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
import glfw
import OpenGL.GL as gl
from imgui.integrations.glfw import GlfwRenderer
import imgui
import numpy as np
import util
import imageio
import util_gau
import util_3dgstream
import time
import tkinter as tk
from tkinter import filedialog
import os
import sys
import argparse
from renderer_ogl import OpenGLRenderer, GaussianRenderBase
# Add the directory containing main.py to the Python path
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(dir_path)
# Change the current working directory to the script's directory
os.chdir(os.path.dirname(os.path.abspath(__file__)))
g_camera = util.Camera(720, 1280)
BACKEND_OGL=0
BACKEND_CUDA=1
g_renderer_list = [
None, # ogl
]
g_renderer_idx = BACKEND_OGL
g_renderer = g_renderer_list[g_renderer_idx]
g_scale_modifier = 1.
g_auto_sort = False
g_show_control_win = True
g_show_help_win = True
g_show_camera_win = False
g_render_mode_tables = ["Gaussian Ball", "Flat Ball", "Billboard", "Depth", "SH:0", "SH:0~1", "SH:0~2", "SH:0~3 (default)"]
g_render_mode = 7
g_FVV_path=""
VIDEO_FPS = 30.0
VIDEO_INTERVAL = 1.0 / VIDEO_FPS
g_last_frame_time = 0.0
g_timestep = 0
g_paused = True
g_reset = False
g_total_frame = 300
def impl_glfw_init():
window_name = "Tiny 3DGStream Viewer"
if not glfw.init():
print("Could not initialize OpenGL context")
exit(1)
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
# glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE)
# Create a windowed mode window and its OpenGL context
global window
window = glfw.create_window(
g_camera.w, g_camera.h, window_name, None, None
)
glfw.make_context_current(window)
glfw.swap_interval(0)
# glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_NORMAL);
if not window:
glfw.terminate()
print("Could not initialize Window")
exit(1)
return window
def cursor_pos_callback(window, xpos, ypos):
if imgui.get_io().want_capture_mouse:
g_camera.is_leftmouse_pressed = False
g_camera.is_rightmouse_pressed = False
g_camera.process_mouse(xpos, ypos)
def mouse_button_callback(window, button, action, mod):
if imgui.get_io().want_capture_mouse:
return
pressed = action == glfw.PRESS
g_camera.is_leftmouse_pressed = (button == glfw.MOUSE_BUTTON_LEFT and pressed)
g_camera.is_rightmouse_pressed = (button == glfw.MOUSE_BUTTON_RIGHT and pressed)
def wheel_callback(window, dx, dy):
g_camera.process_wheel(dx, dy)
def key_callback(window, key, scancode, action, mods):
if action == glfw.REPEAT or action == glfw.PRESS:
if key == glfw.KEY_Q:
g_camera.process_roll_key(1)
elif key == glfw.KEY_E:
g_camera.process_roll_key(-1)
def update_camera_pose_lazy():
if g_camera.is_pose_dirty:
g_renderer.update_camera_pose(g_camera)
g_camera.is_pose_dirty = False
def update_camera_intrin_lazy():
if g_camera.is_intrin_dirty:
g_renderer.update_camera_intrin(g_camera)
g_camera.is_intrin_dirty = False
def update_activated_renderer_state(gaus: util_gau.GaussianData):
g_renderer.update_gaussian_data(gaus)
g_renderer.sort_and_update(g_camera)
g_renderer.set_scale_modifier(g_scale_modifier)
g_renderer.set_render_mod(g_render_mode - 3)
g_renderer.update_camera_pose(g_camera)
g_renderer.update_camera_intrin(g_camera)
g_renderer.set_render_reso(g_camera.w, g_camera.h)
def window_resize_callback(window, width, height):
gl.glViewport(0, 0, width, height)
g_camera.update_resolution(height, width)
g_renderer.set_render_reso(width, height)
def main():
global g_camera, g_renderer, g_renderer_list, g_renderer_idx, g_scale_modifier, g_auto_sort, \
g_show_control_win, g_show_help_win, g_show_camera_win, \
g_render_mode, g_render_mode_tables, \
g_FVV_path, g_paused, g_reset, g_timestep, g_last_frame_time, g_total_frame
imgui.create_context()
if args.hidpi:
imgui.get_io().font_global_scale = 1.5
window = impl_glfw_init()
impl = GlfwRenderer(window)
root = tk.Tk() # used for file dialog
root.withdraw()
glfw.set_cursor_pos_callback(window, cursor_pos_callback)
glfw.set_mouse_button_callback(window, mouse_button_callback)
glfw.set_scroll_callback(window, wheel_callback)
glfw.set_key_callback(window, key_callback)
glfw.set_window_size_callback(window, window_resize_callback)
# init renderer
g_renderer_list[BACKEND_OGL] = OpenGLRenderer(g_camera.w, g_camera.h)
try:
from renderer_cuda import CUDARenderer
g_renderer_list += [CUDARenderer(g_camera.w, g_camera.h)]
except ImportError:
pass
g_renderer_idx = BACKEND_CUDA
g_renderer = g_renderer_list[g_renderer_idx]
# gaussian data
gaussians = util_gau.naive_gaussian()
update_activated_renderer_state(gaussians)
g_last_frame_time=time.time()
#debug only
# gaussians = util_gau.load_ply("F:\\3dgstream\\flame_steak\\init_3dgs.ply")
# g_renderer.update_gaussian_data(gaussians)
# g_renderer.sort_and_update(g_camera)
# g_renderer.NTCs = util_3dgstream.load_NTCs("F:\\3dgstream\\flame_steak", g_renderer.gaussians, 300)
# g_renderer.additional_3dgs = util_3dgstream.load_Additions("F:\\3dgstream\\flame_steak", 300)
# settings
while not glfw.window_should_close(window):
glfw.poll_events()
impl.process_inputs()
imgui.new_frame()
gl.glClearColor(0, 0, 0, 1.0)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
update_camera_pose_lazy()
update_camera_intrin_lazy()
current_time=time.time()
if current_time - g_last_frame_time >= VIDEO_INTERVAL and not g_paused and g_timestep < g_total_frame-1:
g_timestep+=1
g_last_frame_time = current_time
if g_reset:
g_renderer.fvv_reset()
g_reset = False
g_last_frame_time = time.time()
g_renderer.draw(g_timestep)
# imgui ui
if imgui.begin_main_menu_bar():
if imgui.begin_menu("Window", True):
clicked, g_show_control_win = imgui.menu_item(
"Show Control", None, g_show_control_win
)
clicked, g_show_help_win = imgui.menu_item(
"Show Help", None, g_show_help_win
)
clicked, g_show_camera_win = imgui.menu_item(
"Show Camera Control", None, g_show_camera_win
)
imgui.end_menu()
imgui.end_main_menu_bar()
if g_show_control_win:
if imgui.begin("Control", True):
# rendering backend
changed, g_renderer_idx = imgui.combo("backend", g_renderer_idx, ["ogl", "cuda"][:len(g_renderer_list)])
if changed:
g_renderer = g_renderer_list[g_renderer_idx]
update_activated_renderer_state(gaussians)
imgui.text(f"# of Gaus = {len(gaussians)}")
imgui.text(f"Render FPS = {imgui.get_io().framerate:.1f}")
imgui.text(f"Video FPS = {VIDEO_FPS:.1f}")
imgui.text(f"FVV Dir:{g_FVV_path}")
imgui.text(f"Frame {g_timestep}")
imgui.text(f"#Frames: ")
imgui.same_line()
total_frame_changed, g_total_frame = imgui.slider_int(
"frames", g_total_frame, 1, 300
)
if imgui.button("Pause"):
g_paused = True
g_last_frame_time=time.time()
imgui.same_line()
if imgui.button("Play"):
g_paused = False
g_last_frame_time=time.time()
imgui.same_line()
if imgui.button("Reset"):
g_paused = True
g_reset = True
g_timestep=0
g_last_frame_time=time.time()
imgui.same_line()
if imgui.button("Step"):
g_timestep+=1
g_last_frame_time=time.time()
if imgui.button(label='load ply'):
file_path = filedialog.askopenfilename(title="load ply",
initialdir="C:\\Users",
filetypes=[('ply file', '.ply')]
)
if file_path:
try:
gaussians = util_gau.load_ply(file_path)
g_renderer.update_gaussian_data(gaussians)
g_renderer.sort_and_update(g_camera)
except RuntimeError as e:
pass
imgui.same_line()
if imgui.button(label='save ply'):
file_path = filedialog.asksaveasfilename(title="save ply",
initialdir="C:\\Users\\",
defaultextension=".txt",
filetypes=[('ply file', '.ply')]
)
if file_path:
try:
util_3dgstream.save_gau_cuda(g_renderer.gaussians, file_path)
except RuntimeError as e:
pass
imgui.same_line()
if imgui.button(label='load FVV'):
dir_path = filedialog.askdirectory(title="load FVV",
initialdir="C:\\Users"
)
if dir_path:
try:
g_FVV_path = dir_path
g_renderer.NTCs = util_3dgstream.load_NTCs(g_FVV_path, g_renderer.gaussians, g_total_frame)
g_renderer.additional_3dgs = util_3dgstream.load_Additions(g_FVV_path, g_total_frame)
except RuntimeError as e:
pass
# camera fov
changed, g_camera.fovy = imgui.slider_float(
"fov", g_camera.fovy, 0.001, np.pi - 0.001, "fov = %.3f"
)
g_camera.is_intrin_dirty = changed
update_camera_intrin_lazy()
# scale modifier
changed, g_scale_modifier = imgui.slider_float(
"", g_scale_modifier, 0.1, 10, "scale modifier = %.3f"
)
imgui.same_line()
if imgui.button(label="reset"):
g_scale_modifier = 1.
changed = True
if changed:
g_renderer.set_scale_modifier(g_scale_modifier)
# render mode
changed, g_render_mode = imgui.combo("shading", g_render_mode, g_render_mode_tables)
if changed:
g_renderer.set_render_mod(g_render_mode - 4)
# sort button
if imgui.button(label='sort Gaussians'):
g_renderer.sort_and_update(g_camera)
imgui.same_line()
changed, g_auto_sort = imgui.checkbox(
"auto sort", g_auto_sort,
)
if g_auto_sort:
g_renderer.sort_and_update(g_camera)
if imgui.button(label='save image'):
width, height = glfw.get_framebuffer_size(window)
nrChannels = 3;
stride = nrChannels * width;
stride += (4 - stride % 4) if stride % 4 else 0
gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 4)
gl.glReadBuffer(gl.GL_FRONT)
bufferdata = gl.glReadPixels(0, 0, width, height, gl.GL_RGB, gl.GL_UNSIGNED_BYTE)
img = np.frombuffer(bufferdata, np.uint8, -1).reshape(height, width, 3)
imageio.imwrite("save.png", img[::-1])
# save intermediate information
# np.savez(
# "save.npz",
# gau_xyz=gaussians.xyz,
# gau_s=gaussians.scale,
# gau_rot=gaussians.rot,
# gau_c=gaussians.sh,
# gau_a=gaussians.opacity,
# viewmat=g_camera.get_view_matrix(),
# projmat=g_camera.get_project_matrix(),
# hfovxyfocal=g_camera.get_htanfovxy_focal()
# )
# Add buttons directly in the main menu bar for control actions
imgui.end()
if g_show_camera_win:
if imgui.button(label='rot 180'):
g_camera.flip_ground()
changed, g_camera.target_dist = imgui.slider_float(
"t", g_camera.target_dist, 1., 8., "target dist = %.3f"
)
if changed:
g_camera.update_target_distance()
changed, g_camera.rot_sensitivity = imgui.slider_float(
"r", g_camera.rot_sensitivity, 0.002, 0.1, "rotate speed = %.3f"
)
imgui.same_line()
if imgui.button(label="reset r"):
g_camera.rot_sensitivity = 0.02
changed, g_camera.trans_sensitivity = imgui.slider_float(
"m", g_camera.trans_sensitivity, 0.001, 0.03, "move speed = %.3f"
)
imgui.same_line()
if imgui.button(label="reset m"):
g_camera.trans_sensitivity = 0.01
changed, g_camera.zoom_sensitivity = imgui.slider_float(
"z", g_camera.zoom_sensitivity, 0.001, 0.05, "zoom speed = %.3f"
)
imgui.same_line()
if imgui.button(label="reset z"):
g_camera.zoom_sensitivity = 0.01
changed, g_camera.roll_sensitivity = imgui.slider_float(
"ro", g_camera.roll_sensitivity, 0.003, 0.1, "roll speed = %.3f"
)
imgui.same_line()
if imgui.button(label="reset ro"):
g_camera.roll_sensitivity = 0.03
if g_show_help_win:
imgui.begin("Help", True)
imgui.text("Open Gaussian Splatting PLY file \n by click 'open ply' button")
imgui.text("Use left click & move to rotate camera")
imgui.text("Use right click & move to translate camera")
imgui.text("Press Q/E to roll camera")
imgui.text("Use scroll to zoom in/out")
imgui.text("Use control panel to change setting")
imgui.end()
imgui.render()
impl.render(imgui.get_draw_data())
glfw.swap_buffers(window)
impl.shutdown()
glfw.terminate()
if __name__ == "__main__":
global args
parser = argparse.ArgumentParser(description="Tiny 3DGStream Viewer.")
parser.add_argument("--hidpi", action="store_true", help="Enable HiDPI scaling for the interface.")
args = parser.parse_args()
main()