Spaces:
Running
on
Zero
Running
on
Zero
Commit
·
460c964
1
Parent(s):
ceb06ae
update
Browse files
app.py
CHANGED
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
1 |
try:
|
2 |
import spaces
|
3 |
except ImportError:
|
@@ -5,68 +8,80 @@ except ImportError:
|
|
5 |
def GPU(func):
|
6 |
return func
|
7 |
spaces = type('spaces', (), {'GPU': GPU})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
-
import os
|
10 |
import torch
|
11 |
-
import
|
12 |
from typing import *
|
13 |
-
from diffusers import StableDiffusionPipeline
|
14 |
from collections import deque
|
|
|
15 |
|
|
|
16 |
from triplaneturbo_executable.utils.mesh_exporter import export_obj
|
17 |
-
from triplaneturbo_executable import TriplaneTurboTextTo3DPipeline, TriplaneTurboTextTo3DPipelineConfig
|
18 |
-
|
19 |
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
# download pretrained models if not exist
|
31 |
-
if not os.path.exists(adapter_name_or_path):
|
32 |
-
print(f"Downloading pretrained models from huggingface")
|
33 |
-
os.system(
|
34 |
-
f"huggingface-cli download --resume-download ZhiyuanthePony/TriplaneTurbo \
|
35 |
-
--include \"triplane_turbo_sd_v1.pth\" \
|
36 |
-
--local-dir ./pretrained \
|
37 |
-
--local-dir-use-symlinks False"
|
38 |
)
|
39 |
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
@spaces.GPU
|
46 |
-
def
|
47 |
-
"""
|
48 |
-
|
49 |
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
"""
|
59 |
-
output = triplane_turbo_pipeline(
|
60 |
prompt=prompt,
|
61 |
-
num_results_per_prompt=
|
62 |
-
generator=torch.Generator(device=
|
63 |
-
device=device,
|
64 |
)
|
65 |
-
|
66 |
-
obj_file_queue = deque(maxlen=max_obj_files)
|
67 |
-
|
68 |
# Save mesh
|
|
|
69 |
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
|
70 |
for i, mesh in enumerate(output["mesh"]):
|
71 |
vertices = mesh.v_pos
|
72 |
|
@@ -103,27 +118,96 @@ def generate_3d_model(prompt, num_results_per_prompt=1, seed=42, device="cuda"):
|
|
103 |
], dim=1)
|
104 |
mesh._v_nrm = normals
|
105 |
|
106 |
-
|
107 |
-
name = f"{prompt.replace(' ', '_')}_{seed}_{i}"
|
108 |
save_paths = export_obj(mesh, f"{output_dir}/{name}.obj")
|
109 |
-
|
110 |
|
111 |
-
#
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
|
|
|
|
|
|
121 |
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import subprocess
|
3 |
+
import sys
|
4 |
try:
|
5 |
import spaces
|
6 |
except ImportError:
|
|
|
8 |
def GPU(func):
|
9 |
return func
|
10 |
spaces = type('spaces', (), {'GPU': GPU})
|
11 |
+
|
12 |
+
# Check if setup has been run
|
13 |
+
setup_marker = ".setup_complete"
|
14 |
+
if not os.path.exists(setup_marker):
|
15 |
+
print("First run detected, installing dependencies...")
|
16 |
+
try:
|
17 |
+
subprocess.check_call(["bash", "setup.sh"])
|
18 |
+
# Create marker file to indicate setup is complete
|
19 |
+
with open(setup_marker, "w") as f:
|
20 |
+
f.write("Setup completed")
|
21 |
+
print("Setup completed successfully!")
|
22 |
+
except subprocess.CalledProcessError as e:
|
23 |
+
print(f"Setup failed with error: {e}")
|
24 |
+
sys.exit(1)
|
25 |
|
|
|
26 |
import torch
|
27 |
+
import gradio as gr
|
28 |
from typing import *
|
|
|
29 |
from collections import deque
|
30 |
+
from diffusers import StableDiffusionPipeline
|
31 |
|
32 |
+
from triplaneturbo_executable import TriplaneTurboTextTo3DPipeline
|
33 |
from triplaneturbo_executable.utils.mesh_exporter import export_obj
|
|
|
|
|
34 |
|
35 |
+
# Initialize global variables
|
36 |
+
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
37 |
+
ADAPTER_PATH = "pretrained/triplane_turbo_sd_v1.pth" #"/home/user/app/pretrained/triplane_turbo_sd_v1.pth"
|
38 |
+
PIPELINE = None # Will hold our pipeline instance
|
39 |
+
OBJ_FILE_QUEUE = deque(maxlen=100) # Queue to store OBJ file paths
|
40 |
|
41 |
+
def download_model():
|
42 |
+
"""Download the pretrained model if not exists"""
|
43 |
+
if not os.path.exists(ADAPTER_PATH):
|
44 |
+
print("Downloading pretrained models from huggingface")
|
45 |
+
os.system(
|
46 |
+
f"huggingface-cli download --resume-download ZhiyuanthePony/TriplaneTurbo \
|
47 |
+
--include \"triplane_turbo_sd_v1.pth\" \
|
48 |
+
--local-dir ./pretrained \
|
49 |
+
--local-dir-use-symlinks False"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
)
|
51 |
|
52 |
+
def initialize_pipeline():
|
53 |
+
"""Initialize the pipeline once and keep it in memory"""
|
54 |
+
global PIPELINE
|
55 |
+
if PIPELINE is None:
|
56 |
+
print("Initializing pipeline...")
|
57 |
+
PIPELINE = TriplaneTurboTextTo3DPipeline.from_pretrained(ADAPTER_PATH)
|
58 |
+
PIPELINE.to(DEVICE)
|
59 |
+
print("Pipeline initialized!")
|
60 |
+
return PIPELINE
|
61 |
|
62 |
@spaces.GPU
|
63 |
+
def generate_3d_mesh(prompt: str) -> Tuple[str, str]:
|
64 |
+
"""Generate 3D mesh from text prompt"""
|
65 |
+
global PIPELINE, OBJ_FILE_QUEUE
|
66 |
|
67 |
+
# Use the global pipeline instance
|
68 |
+
pipeline = initialize_pipeline()
|
69 |
+
|
70 |
+
# Use fixed seed value
|
71 |
+
seed = 42
|
72 |
+
|
73 |
+
# Generate mesh
|
74 |
+
output = pipeline(
|
|
|
|
|
75 |
prompt=prompt,
|
76 |
+
num_results_per_prompt=1,
|
77 |
+
generator=torch.Generator(device=DEVICE).manual_seed(seed),
|
|
|
78 |
)
|
79 |
+
|
|
|
|
|
80 |
# Save mesh
|
81 |
+
output_dir = "outputs"
|
82 |
os.makedirs(output_dir, exist_ok=True)
|
83 |
+
|
84 |
+
mesh_path = None
|
85 |
for i, mesh in enumerate(output["mesh"]):
|
86 |
vertices = mesh.v_pos
|
87 |
|
|
|
118 |
], dim=1)
|
119 |
mesh._v_nrm = normals
|
120 |
|
121 |
+
name = f"{prompt.replace(' ', '_')}"
|
|
|
122 |
save_paths = export_obj(mesh, f"{output_dir}/{name}.obj")
|
123 |
+
mesh_path = save_paths[0]
|
124 |
|
125 |
+
# Add new file path to queue
|
126 |
+
OBJ_FILE_QUEUE.append(mesh_path)
|
127 |
+
|
128 |
+
# If queue is at max length, remove oldest file
|
129 |
+
if len(OBJ_FILE_QUEUE) == OBJ_FILE_QUEUE.maxlen:
|
130 |
+
old_file = OBJ_FILE_QUEUE[0] # Get oldest file (will be automatically removed from queue)
|
131 |
+
if os.path.exists(old_file):
|
132 |
+
try:
|
133 |
+
os.remove(old_file)
|
134 |
+
except OSError as e:
|
135 |
+
print(f"Error deleting file {old_file}: {e}")
|
136 |
+
|
137 |
+
return mesh_path, mesh_path # Return the path twice - once for 3D preview, once for download
|
138 |
|
139 |
+
with gr.Blocks(css=".output-image, .input-image, .image-preview {height: 512px !important}") as demo:
|
140 |
+
# Download model if needed
|
141 |
+
download_model()
|
142 |
+
|
143 |
+
# Initialize pipeline at startup
|
144 |
+
initialize_pipeline()
|
145 |
+
|
146 |
+
gr.Markdown(
|
147 |
+
"""
|
148 |
+
# 🌟 Text to 3D Mesh Generation with TriplaneTurbo
|
149 |
+
|
150 |
+
Demo of the paper "Progressive Rendering Distillation: Adapting Stable Diffusion for Instant Text-to-Mesh Generation beyond 3D Training Data" [CVPR 2025]
|
151 |
+
|
152 |
+
[GitHub Repository](https://github.com/theEricMa/TriplaneTurbo)
|
153 |
+
|
154 |
+
## Instructions
|
155 |
+
1. Enter a text prompt describing what 3D object you want to generate
|
156 |
+
2. Click "Generate" and wait for the model to create your 3D mesh
|
157 |
+
3. View the result in the 3D viewer or download the OBJ file
|
158 |
+
"""
|
159 |
+
)
|
160 |
+
|
161 |
+
with gr.Row():
|
162 |
+
with gr.Column(scale=1):
|
163 |
+
prompt = gr.Textbox(
|
164 |
+
label="Text Prompt",
|
165 |
+
placeholder="Enter your text description...",
|
166 |
+
value="Armor dress style of outsiderzone fantasy helmet",
|
167 |
+
lines=2
|
168 |
+
)
|
169 |
+
|
170 |
+
generate_btn = gr.Button("Generate", variant="primary")
|
171 |
+
|
172 |
+
examples = gr.Examples(
|
173 |
+
examples=[
|
174 |
+
["Armor dress style of outsiderzone fantasy helmet"],
|
175 |
+
["Gandalf the grey riding a camel in a rock concert, victorian newspaper article, hyperrealistic"],
|
176 |
+
["A DSLR photo of a bald eagle"],
|
177 |
+
["A goblin riding a lawnmower in a hospital, victorian newspaper article, 4k hd"],
|
178 |
+
["An imperial stormtrooper, highly detailed"],
|
179 |
+
],
|
180 |
+
inputs=[prompt],
|
181 |
+
label="Example Prompts"
|
182 |
+
)
|
183 |
+
|
184 |
+
with gr.Column(scale=1):
|
185 |
+
output_model = gr.Model3D(
|
186 |
+
label="Generated 3D Mesh",
|
187 |
+
camera_position=(90, 90, 3),
|
188 |
+
clear_color=(0.5, 0.5, 0.5, 1),
|
189 |
+
)
|
190 |
+
output_file = gr.File(label="Download OBJ file")
|
191 |
+
|
192 |
+
generate_btn.click(
|
193 |
+
fn=generate_3d_mesh,
|
194 |
+
inputs=[prompt],
|
195 |
+
outputs=[output_model, output_file]
|
196 |
+
)
|
197 |
+
|
198 |
+
gr.Markdown(
|
199 |
+
"""
|
200 |
+
## About
|
201 |
+
|
202 |
+
This demo uses TriplaneTurbo, which adapts Stable Diffusion for instant text-to-mesh generation.
|
203 |
+
The model can generate high-quality 3D meshes from text descriptions without requiring 3D training data.
|
204 |
+
|
205 |
+
### Limitations
|
206 |
+
- Generation is deterministic with a fixed seed
|
207 |
+
- Complex prompts may produce unpredictable results
|
208 |
+
- Generated meshes may require clean-up for professional use
|
209 |
+
"""
|
210 |
+
)
|
211 |
|
212 |
+
if __name__ == "__main__":
|
213 |
+
demo.launch()
|