ZhiyuanthePony commited on
Commit
460c964
·
1 Parent(s): ceb06ae
Files changed (1) hide show
  1. app.py +149 -65
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 argparse
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
- # Initialize configuration and parameters
22
- prompt = "a beautiful girl"
23
- output_dir = "output"
24
- adapter_name_or_path = "pretrained/triplane_turbo_sd_v1.pth"
25
- num_results_per_prompt = 1
26
- seed = 42
27
- device = "cuda"
28
- max_obj_files = 100
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
- # Initialize the TriplaneTurbo pipeline
42
- triplane_turbo_pipeline = TriplaneTurboTextTo3DPipeline.from_pretrained(adapter_name_or_path)
43
- triplane_turbo_pipeline.to(device)
 
 
 
 
 
44
 
45
  @spaces.GPU
46
- def generate_3d_model(prompt, num_results_per_prompt=1, seed=42, device="cuda"):
47
- """
48
- Generate 3D models using TriplaneTurbo pipeline.
49
 
50
- Args:
51
- prompt (str): Text prompt for the 3D model
52
- num_results_per_prompt (int): Number of results to generate
53
- seed (int): Random seed for generation
54
- device (str): Device to use for computation
55
-
56
- Returns:
57
- dict: Output from the pipeline
58
- """
59
- output = triplane_turbo_pipeline(
60
  prompt=prompt,
61
- num_results_per_prompt=num_results_per_prompt,
62
- generator=torch.Generator(device=device).manual_seed(seed),
63
- device=device,
64
  )
65
- # Initialize a deque with maximum length of 100 to store obj file paths
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
- # Save obj file and add its path to the queue
107
- name = f"{prompt.replace(' ', '_')}_{seed}_{i}"
108
  save_paths = export_obj(mesh, f"{output_dir}/{name}.obj")
109
- obj_file_queue.append(save_paths[0])
110
 
111
- # If an old file needs to be removed (queue is at max length)
112
- # and the file exists, delete it
113
- if len(obj_file_queue) == max_obj_files and os.path.exists(obj_file_queue[0]):
114
- old_file = obj_file_queue[0]
115
- try:
116
- os.remove(old_file)
117
- except OSError as e:
118
- print(f"Error deleting file {old_file}: {e}")
119
-
120
-
 
 
 
121
 
122
- # Run the pipeline
123
- output = generate_3d_model(
124
- prompt=prompt,
125
- num_results_per_prompt=num_results_per_prompt,
126
- seed=seed,
127
- device=device
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()