Spaces:
Running
Running
Commit
·
ba6f051
1
Parent(s):
4d3c14b
Add spaces decorator for ZeroGPU
Browse files
app.py
CHANGED
|
@@ -2,6 +2,7 @@ import gradio as gr
|
|
| 2 |
import os
|
| 3 |
import struct
|
| 4 |
import numpy as np
|
|
|
|
| 5 |
|
| 6 |
# --- 1. Configuration ---
|
| 7 |
SCENES = [
|
|
@@ -47,10 +48,9 @@ def read_gaussian_ply(filepath):
|
|
| 47 |
vertex_count = int(line.split()[-1])
|
| 48 |
break
|
| 49 |
|
| 50 |
-
print(f"Reading {vertex_count:,} Gaussian splats...")
|
| 51 |
|
| 52 |
# 讀取二進制數據
|
| 53 |
-
# 每個 vertex: 17 個 float (x,y,z, nx,ny,nz, f_dc_0,f_dc_1,f_dc_2, opacity, scale*3, rot*4)
|
| 54 |
bytes_per_vertex = 17 * 4 # 17 floats * 4 bytes
|
| 55 |
data = f.read(vertex_count * bytes_per_vertex)
|
| 56 |
|
|
@@ -67,8 +67,6 @@ def read_gaussian_ply(filepath):
|
|
| 67 |
vertices.append([x, y, z])
|
| 68 |
|
| 69 |
# 提取顏色 (f_dc_0, f_dc_1, f_dc_2) 並轉換到 [0, 255]
|
| 70 |
-
# Gaussian Splatting 使用球諧函數,f_dc 是 DC 分量
|
| 71 |
-
# 轉換公式: color = (0.5 + SH_C0 * f_dc) * 255
|
| 72 |
SH_C0 = 0.28209479177387814
|
| 73 |
r = max(0, min(255, int((0.5 + SH_C0 * vertex_data[6]) * 255)))
|
| 74 |
g = max(0, min(255, int((0.5 + SH_C0 * vertex_data[7]) * 255)))
|
|
@@ -101,75 +99,52 @@ def convert_to_standard_ply(input_path, output_path):
|
|
| 101 |
|
| 102 |
print(f"✅ Converted to: {output_path}")
|
| 103 |
|
| 104 |
-
# --- 4.
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
converted_dir = "converted"
|
| 109 |
os.makedirs(converted_dir, exist_ok=True)
|
| 110 |
|
| 111 |
-
|
|
|
|
| 112 |
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
try:
|
| 120 |
-
convert_to_standard_ply(input_ply, output_ply)
|
| 121 |
-
except Exception as e:
|
| 122 |
-
print(f"❌ Error converting {input_ply}: {e}")
|
| 123 |
-
continue
|
| 124 |
-
else:
|
| 125 |
-
print(f"✅ Already converted: {output_ply}")
|
| 126 |
-
|
| 127 |
-
# 更新場景配置
|
| 128 |
-
converted_scenes.append({
|
| 129 |
-
**scene,
|
| 130 |
-
"converted_model": output_ply
|
| 131 |
-
})
|
| 132 |
-
|
| 133 |
-
return converted_scenes
|
| 134 |
-
|
| 135 |
-
# --- 5. 選擇處理 ---
|
| 136 |
-
def create_select_handler(converted_scenes):
|
| 137 |
-
"""創建選擇處理函數"""
|
| 138 |
-
def on_select(evt: gr.SelectData):
|
| 139 |
-
index = evt.index
|
| 140 |
-
scene = converted_scenes[index]
|
| 141 |
-
|
| 142 |
-
if "converted_model" not in scene or not os.path.exists(scene["converted_model"]):
|
| 143 |
return (
|
| 144 |
None,
|
| 145 |
scene["tracking_img"],
|
| 146 |
-
f"**Error
|
| 147 |
)
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
return (
|
| 156 |
-
|
| 157 |
scene["tracking_img"],
|
| 158 |
-
f"**{scene['name']}** ({file_size:.1f} MB) ✓"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
)
|
| 160 |
-
|
| 161 |
-
return on_select
|
| 162 |
-
|
| 163 |
-
# --- 6. UI ---
|
| 164 |
-
print("\n" + "="*60)
|
| 165 |
-
print("🚀 RGS-SLAM Viewer - Initializing...")
|
| 166 |
-
print("="*60)
|
| 167 |
-
|
| 168 |
-
# 轉換模型
|
| 169 |
-
converted_scenes = initialize_converted_models()
|
| 170 |
|
|
|
|
| 171 |
print("\n" + "="*60)
|
| 172 |
-
print("
|
| 173 |
print("="*60 + "\n")
|
| 174 |
|
| 175 |
with gr.Blocks(title="RGS-SLAM Demo") as demo:
|
|
@@ -177,7 +152,7 @@ with gr.Blocks(title="RGS-SLAM Demo") as demo:
|
|
| 177 |
gr.Markdown("# 🚀 RGS-SLAM Gaussian Splatting Viewer")
|
| 178 |
gr.Markdown(
|
| 179 |
"**3D Gaussian Splatting** reconstruction results with camera trajectories. "
|
| 180 |
-
"
|
| 181 |
)
|
| 182 |
|
| 183 |
with gr.Row():
|
|
@@ -199,7 +174,7 @@ with gr.Blocks(title="RGS-SLAM Demo") as demo:
|
|
| 199 |
""")
|
| 200 |
|
| 201 |
with gr.Column(scale=3):
|
| 202 |
-
status = gr.Markdown("**👈 Select a scene to begin**")
|
| 203 |
|
| 204 |
with gr.Tab("🎯 3D Reconstruction"):
|
| 205 |
gr.Markdown("### Point Cloud with Colors")
|
|
@@ -217,11 +192,9 @@ with gr.Blocks(title="RGS-SLAM Demo") as demo:
|
|
| 217 |
height=600
|
| 218 |
)
|
| 219 |
|
| 220 |
-
# Event binding
|
| 221 |
-
select_handler = create_select_handler(converted_scenes)
|
| 222 |
-
|
| 223 |
scene_gallery.select(
|
| 224 |
-
fn=
|
| 225 |
outputs=[model_viewer, trajectory_img, status]
|
| 226 |
)
|
| 227 |
|
|
|
|
| 2 |
import os
|
| 3 |
import struct
|
| 4 |
import numpy as np
|
| 5 |
+
import spaces # <--- 1. 新增這個 import
|
| 6 |
|
| 7 |
# --- 1. Configuration ---
|
| 8 |
SCENES = [
|
|
|
|
| 48 |
vertex_count = int(line.split()[-1])
|
| 49 |
break
|
| 50 |
|
| 51 |
+
print(f"Reading {vertex_count:,} Gaussian splats from {os.path.basename(filepath)}...")
|
| 52 |
|
| 53 |
# 讀取二進制數據
|
|
|
|
| 54 |
bytes_per_vertex = 17 * 4 # 17 floats * 4 bytes
|
| 55 |
data = f.read(vertex_count * bytes_per_vertex)
|
| 56 |
|
|
|
|
| 67 |
vertices.append([x, y, z])
|
| 68 |
|
| 69 |
# 提取顏色 (f_dc_0, f_dc_1, f_dc_2) 並轉換到 [0, 255]
|
|
|
|
|
|
|
| 70 |
SH_C0 = 0.28209479177387814
|
| 71 |
r = max(0, min(255, int((0.5 + SH_C0 * vertex_data[6]) * 255)))
|
| 72 |
g = max(0, min(255, int((0.5 + SH_C0 * vertex_data[7]) * 255)))
|
|
|
|
| 99 |
|
| 100 |
print(f"✅ Converted to: {output_path}")
|
| 101 |
|
| 102 |
+
# --- 4. 核心處理函式 (加上 @spaces.GPU) ---
|
| 103 |
+
# 注意:即使這只是檔案轉換,加上這個標籤可以解決 ZeroGPU 的檢查錯誤
|
| 104 |
+
# 並且將運算移到計算節點上執行
|
| 105 |
+
@spaces.GPU(duration=120)
|
| 106 |
+
def process_and_load_scene(evt: gr.SelectData):
|
| 107 |
+
index = evt.index
|
| 108 |
+
scene = SCENES[index]
|
| 109 |
|
| 110 |
converted_dir = "converted"
|
| 111 |
os.makedirs(converted_dir, exist_ok=True)
|
| 112 |
|
| 113 |
+
input_ply = scene["model"]
|
| 114 |
+
output_ply = f"{converted_dir}/{os.path.basename(input_ply)}"
|
| 115 |
|
| 116 |
+
# 檢查是否需要轉換 (Lazy Loading)
|
| 117 |
+
if not os.path.exists(output_ply):
|
| 118 |
+
print(f"\n🔄 Converting {scene['name']} on GPU Node...")
|
| 119 |
+
try:
|
| 120 |
+
convert_to_standard_ply(input_ply, output_ply)
|
| 121 |
+
except Exception as e:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
return (
|
| 123 |
None,
|
| 124 |
scene["tracking_img"],
|
| 125 |
+
f"**Error converting {scene['name']}: {str(e)}**"
|
| 126 |
)
|
| 127 |
+
else:
|
| 128 |
+
print(f"✅ Using cached model: {output_ply}")
|
| 129 |
+
|
| 130 |
+
# 檢查檔案大小
|
| 131 |
+
if os.path.exists(output_ply):
|
| 132 |
+
file_size = os.path.getsize(output_ply) / (1024 * 1024)
|
|
|
|
| 133 |
return (
|
| 134 |
+
output_ply,
|
| 135 |
scene["tracking_img"],
|
| 136 |
+
f"**{scene['name']}** ({file_size:.1f} MB) ✓ Loaded"
|
| 137 |
+
)
|
| 138 |
+
else:
|
| 139 |
+
return (
|
| 140 |
+
None,
|
| 141 |
+
scene["tracking_img"],
|
| 142 |
+
"**Error: File not found after conversion**"
|
| 143 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
+
# --- 5. UI ---
|
| 146 |
print("\n" + "="*60)
|
| 147 |
+
print("🚀 RGS-SLAM Viewer - Initializing UI...")
|
| 148 |
print("="*60 + "\n")
|
| 149 |
|
| 150 |
with gr.Blocks(title="RGS-SLAM Demo") as demo:
|
|
|
|
| 152 |
gr.Markdown("# 🚀 RGS-SLAM Gaussian Splatting Viewer")
|
| 153 |
gr.Markdown(
|
| 154 |
"**3D Gaussian Splatting** reconstruction results with camera trajectories. "
|
| 155 |
+
"Select a scene below to load the model."
|
| 156 |
)
|
| 157 |
|
| 158 |
with gr.Row():
|
|
|
|
| 174 |
""")
|
| 175 |
|
| 176 |
with gr.Column(scale=3):
|
| 177 |
+
status = gr.Markdown("**👈 Select a scene to begin (Conversion happens on click)**")
|
| 178 |
|
| 179 |
with gr.Tab("🎯 3D Reconstruction"):
|
| 180 |
gr.Markdown("### Point Cloud with Colors")
|
|
|
|
| 192 |
height=600
|
| 193 |
)
|
| 194 |
|
| 195 |
+
# Event binding (直接綁定到有 @spaces.GPU 的函式)
|
|
|
|
|
|
|
| 196 |
scene_gallery.select(
|
| 197 |
+
fn=process_and_load_scene,
|
| 198 |
outputs=[model_viewer, trajectory_img, status]
|
| 199 |
)
|
| 200 |
|