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