Files changed (1) hide show
  1. app.py +38 -136
app.py CHANGED
@@ -2,22 +2,11 @@ import os
2
  import shlex
3
  import spaces
4
  import subprocess
5
-
6
- # --------------------------------------------------------------------------
7
- # 1. ENVIRONMENT AND DEPENDENCY INSTALLATION
8
- # This section is crucial for the Hugging Face Space to work.
9
- # Installs the CUDA toolkit and compiles the necessary C++/CUDA extensions.
10
- # --------------------------------------------------------------------------
11
  def install_cuda_toolkit():
12
- """Installs the CUDA toolkit required to compile extensions."""
13
  CUDA_TOOLKIT_URL = "https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run"
14
  CUDA_TOOLKIT_FILE = "/tmp/%s" % os.path.basename(CUDA_TOOLKIT_URL)
15
-
16
- print("Downloading CUDA Toolkit...")
17
  subprocess.call(["wget", "-q", CUDA_TOOLKIT_URL, "-O", CUDA_TOOLKIT_FILE])
18
  subprocess.call(["chmod", "+x", CUDA_TOOLKIT_FILE])
19
-
20
- print("Installing CUDA Toolkit...")
21
  subprocess.call([CUDA_TOOLKIT_FILE, "--silent", "--toolkit"])
22
 
23
  os.environ["CUDA_HOME"] = "/usr/local/cuda"
@@ -27,21 +16,14 @@ def install_cuda_toolkit():
27
  "" if "LD_LIBRARY_PATH" not in os.environ else os.environ["LD_LIBRARY_PATH"],
28
  )
29
  os.environ["TORCH_CUDA_ARCH_LIST"] = "8.0;8.6"
30
- print("CUDA environment configuration completed.")
31
-
32
  install_cuda_toolkit()
33
-
34
- print("Verifying PyTorch installation and NVCC version:")
35
  os.system("pip list | grep torch")
36
  os.system('nvcc -V')
37
-
38
- print("Compiling differentiable renderer extension...")
39
  os.system("cd /home/user/app/step1x3d_texture/differentiable_renderer/ && python setup.py install")
40
 
41
- print("Installing custom rasterizer...")
42
  subprocess.run(shlex.split("pip install custom_rasterizer-0.1-cp310-cp310-linux_x86_64.whl"), check=True)
43
- print("Installation and compilation completed.")
44
-
45
  import uuid
46
  import torch
47
  import trimesh
@@ -54,10 +36,7 @@ from step1x3d_texture.pipelines.step1x_3d_texture_synthesis_pipeline import (
54
  )
55
  from step1x3d_geometry.models.pipelines.pipeline_utils import reduce_face, remove_degenerate_face
56
 
57
- # --------------------------------------------------------------------------
58
- # 2. MODEL CONFIGURATION AND LOADING
59
- # Here we define the models to be used and load them into memory.
60
- # --------------------------------------------------------------------------
61
  parser = argparse.ArgumentParser()
62
  parser.add_argument(
63
  "--geometry_model", type=str, default="Step1X-3D-Geometry-Label-1300m"
@@ -69,37 +48,19 @@ parser.add_argument("--cache_dir", type=str, default="cache")
69
  args = parser.parse_args()
70
 
71
  os.makedirs(args.cache_dir, exist_ok=True)
72
- device = "cuda" if torch.cuda.is_available() else "cpu"
73
 
74
- print(f"Loading geometry model: {args.geometry_model}...")
75
  geometry_model = Step1X3DGeometryPipeline.from_pretrained(
76
  "stepfun-ai/Step1X-3D", subfolder=args.geometry_model
77
- ).to(device)
78
- print("Geometry model loaded.")
79
 
80
- print(f"Loading texture model: {args.texture_model}...")
81
  texture_model = Step1X3DTexturePipeline.from_pretrained("stepfun-ai/Step1X-3D", subfolder=args.texture_model)
82
- print("Texture model loaded.")
83
-
84
 
85
- # --------------------------------------------------------------------------
86
- # 3. SEPARATE GENERATION FUNCTIONS
87
- # The logic is split into two functions: one for geometry and one for textures.
88
- # --------------------------------------------------------------------------
89
 
90
- @spaces.GPU(duration=180)
91
- def generate_geometry(
92
- input_image_path, guidance_scale, inference_steps, max_facenum, symmetry, edge_type, progress=gr.Progress(track_tqdm=True)
93
  ):
94
- """
95
- Function that generates geometry only from the input image.
96
- """
97
- if input_image_path is None:
98
- raise gr.Error("Please upload an image to start.")
99
-
100
- print("Starting geometry generation...")
101
-
102
- # Choose the appropriate pipeline based on the model name
103
  if "Label" in args.geometry_model:
104
  symmetry_values = ["x", "asymmetry"]
105
  out = geometry_model(
@@ -118,90 +79,49 @@ def generate_geometry(
118
  max_facenum=int(max_facenum),
119
  )
120
 
121
- # Save the result to a temporary file
122
  save_name = str(uuid.uuid4())
 
123
  geometry_save_path = f"{args.cache_dir}/{save_name}.glb"
124
  geometry_mesh = out.mesh[0]
125
  geometry_mesh.export(geometry_save_path)
126
-
127
- torch.cuda.empty_cache()
128
- print(f"Geometry saved at: {geometry_save_path}")
129
-
130
- # Return the path for display in the viewer and to store in state
131
- return geometry_save_path, geometry_save_path
132
-
133
- @spaces.GPU(duration=120)
134
- def generate_texture(input_image_path, geometry_path, progress=gr.Progress(track_tqdm=True)):
135
- """
136
- Function that applies texture to an already generated geometry.
137
- """
138
- if not geometry_path or not os.path.exists(geometry_path):
139
- raise gr.Error("Please generate the geometry first before texturing.")
140
-
141
- print(f"Starting texturing for mesh: {geometry_path}")
142
- geometry_mesh = trimesh.load(geometry_path)
143
-
144
- # Optional post-processing of the mesh before texturing
145
  geometry_mesh = remove_degenerate_face(geometry_mesh)
146
  geometry_mesh = reduce_face(geometry_mesh)
147
-
148
- # Call the texturing pipeline
149
  textured_mesh = texture_model(input_image_path, geometry_mesh)
150
-
151
- # Save the final result
152
- save_name = os.path.basename(geometry_path).replace(".glb", "")
153
  textured_save_path = f"{args.cache_dir}/{save_name}-textured.glb"
154
  textured_mesh.export(textured_save_path)
155
-
156
- torch.cuda.empty_cache()
157
- print(f"Textured mesh saved at: {textured_save_path}")
158
-
159
- return textured_save_path
160
 
 
 
 
161
 
162
- # --------------------------------------------------------------------------
163
- # 4. GRADIO USER INTERFACE
164
- # Defines the look and behavior of the web app.
165
- # --------------------------------------------------------------------------
166
 
167
  with gr.Blocks(title="Step1X-3D demo") as demo:
168
  gr.Markdown("# Step1X-3D")
169
- gr.Markdown("### Demo for generating 3D models from a single image")
170
-
171
- # State component: stores the geometry path between steps
172
- geometry_path_state = gr.State()
173
-
174
  with gr.Row():
175
  with gr.Column(scale=2):
176
- input_image = gr.Image(label="Input Image", type="filepath")
177
-
178
- with gr.Accordion(label="Generation Parameters", open=True):
179
- guidance_scale = gr.Number(label="Guidance Scale", value="7.5")
180
- inference_steps = gr.Slider(
181
- label="Inference Steps", minimum=1, maximum=100, value=50, step=1
182
- )
183
- max_facenum = gr.Number(label="Max. Number of Faces", value="400000")
184
- symmetry = gr.Radio(
185
- choices=["symmetry", "asymmetry"],
186
- label="Symmetry Type",
187
- value="symmetry",
188
- type="index",
189
- )
190
- edge_type = gr.Radio(
191
- choices=["sharp", "normal", "smooth"],
192
- label="Edge Type",
193
- value="sharp",
194
- type="value",
195
- )
196
-
197
- with gr.Row():
198
- btn_geo = gr.Button("1. Generate Geometry", variant="primary")
199
- btn_tex = gr.Button("2. Generate Texture", visible=False, variant="primary")
200
-
201
  with gr.Column(scale=4):
202
- textured_preview = gr.Model3D(label="Textured Model", height=380, clear_color=[0.0, 0.0, 0.0, 0.0])
203
- geometry_preview = gr.Model3D(label="Model (geometry only)", height=380, clear_color=[0.0, 0.0, 0.0, 0.0])
204
-
205
  with gr.Column(scale=1):
206
  gr.Examples(
207
  examples=[
@@ -218,11 +138,8 @@ with gr.Blocks(title="Step1X-3D demo") as demo:
218
  cache_examples=False,
219
  )
220
 
221
- # --- Button logic and interface flow ---
222
-
223
- # 1. When the user clicks "Generate Geometry"
224
- btn_geo.click(
225
- fn=generate_geometry,
226
  inputs=[
227
  input_image,
228
  guidance_scale,
@@ -231,22 +148,7 @@ with gr.Blocks(title="Step1X-3D demo") as demo:
231
  symmetry,
232
  edge_type,
233
  ],
234
- outputs=[geometry_preview, geometry_path_state]
235
- ).then(
236
- # 2. When geometry is done, run this part
237
- fn=lambda: {
238
- btn_tex: gr.update(visible=True), # Show texture button
239
- textured_preview: gr.update(value=None) # Clear previous texture preview
240
- },
241
- outputs=[btn_tex, textured_preview]
242
- )
243
-
244
- # 3. When the user clicks "Generate Texture"
245
- btn_tex.click(
246
- fn=generate_texture,
247
- inputs=[input_image, geometry_path_state],
248
- outputs=[textured_preview],
249
  )
250
 
251
- # Launch the app
252
- demo.launch(ssr_mode=False)
 
2
  import shlex
3
  import spaces
4
  import subprocess
 
 
 
 
 
 
5
  def install_cuda_toolkit():
 
6
  CUDA_TOOLKIT_URL = "https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run"
7
  CUDA_TOOLKIT_FILE = "/tmp/%s" % os.path.basename(CUDA_TOOLKIT_URL)
 
 
8
  subprocess.call(["wget", "-q", CUDA_TOOLKIT_URL, "-O", CUDA_TOOLKIT_FILE])
9
  subprocess.call(["chmod", "+x", CUDA_TOOLKIT_FILE])
 
 
10
  subprocess.call([CUDA_TOOLKIT_FILE, "--silent", "--toolkit"])
11
 
12
  os.environ["CUDA_HOME"] = "/usr/local/cuda"
 
16
  "" if "LD_LIBRARY_PATH" not in os.environ else os.environ["LD_LIBRARY_PATH"],
17
  )
18
  os.environ["TORCH_CUDA_ARCH_LIST"] = "8.0;8.6"
 
 
19
  install_cuda_toolkit()
 
 
20
  os.system("pip list | grep torch")
21
  os.system('nvcc -V')
22
+ print("cd /home/user/app/step1x3d_texture/differentiable_renderer/ && python setup.py install")
 
23
  os.system("cd /home/user/app/step1x3d_texture/differentiable_renderer/ && python setup.py install")
24
 
 
25
  subprocess.run(shlex.split("pip install custom_rasterizer-0.1-cp310-cp310-linux_x86_64.whl"), check=True)
26
+ import time
 
27
  import uuid
28
  import torch
29
  import trimesh
 
36
  )
37
  from step1x3d_geometry.models.pipelines.pipeline_utils import reduce_face, remove_degenerate_face
38
 
39
+
 
 
 
40
  parser = argparse.ArgumentParser()
41
  parser.add_argument(
42
  "--geometry_model", type=str, default="Step1X-3D-Geometry-Label-1300m"
 
48
  args = parser.parse_args()
49
 
50
  os.makedirs(args.cache_dir, exist_ok=True)
 
51
 
 
52
  geometry_model = Step1X3DGeometryPipeline.from_pretrained(
53
  "stepfun-ai/Step1X-3D", subfolder=args.geometry_model
54
+ ).to("cuda")
 
55
 
 
56
  texture_model = Step1X3DTexturePipeline.from_pretrained("stepfun-ai/Step1X-3D", subfolder=args.texture_model)
 
 
57
 
 
 
 
 
58
 
59
+ @spaces.GPU(duration=240)
60
+ def generate_func(
61
+ input_image_path, guidance_scale, inference_steps, max_facenum, symmetry, edge_type
62
  ):
63
+ # geometry_model = geometry_model.to("cuda")
 
 
 
 
 
 
 
 
64
  if "Label" in args.geometry_model:
65
  symmetry_values = ["x", "asymmetry"]
66
  out = geometry_model(
 
79
  max_facenum=int(max_facenum),
80
  )
81
 
 
82
  save_name = str(uuid.uuid4())
83
+ print(save_name)
84
  geometry_save_path = f"{args.cache_dir}/{save_name}.glb"
85
  geometry_mesh = out.mesh[0]
86
  geometry_mesh.export(geometry_save_path)
87
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  geometry_mesh = remove_degenerate_face(geometry_mesh)
89
  geometry_mesh = reduce_face(geometry_mesh)
 
 
90
  textured_mesh = texture_model(input_image_path, geometry_mesh)
 
 
 
91
  textured_save_path = f"{args.cache_dir}/{save_name}-textured.glb"
92
  textured_mesh.export(textured_save_path)
 
 
 
 
 
93
 
94
+ torch.cuda.empty_cache()
95
+ print("Generate finish")
96
+ return geometry_save_path, textured_save_path
97
 
 
 
 
 
98
 
99
  with gr.Blocks(title="Step1X-3D demo") as demo:
100
  gr.Markdown("# Step1X-3D")
 
 
 
 
 
101
  with gr.Row():
102
  with gr.Column(scale=2):
103
+ input_image = gr.Image(label="Image", type="filepath")
104
+ guidance_scale = gr.Number(label="Guidance Scale", value="7.5")
105
+ inference_steps = gr.Slider(
106
+ label="Inferece Steps", minimum=1, maximum=100, value=50
107
+ )
108
+ max_facenum = gr.Number(label="Max Face Num", value="400000")
109
+ symmetry = gr.Radio(
110
+ choices=["symmetry", "asymmetry"],
111
+ label="Symmetry Type",
112
+ value="symmetry",
113
+ type="index",
114
+ )
115
+ edge_type = gr.Radio(
116
+ choices=["sharp", "normal", "smooth"],
117
+ label="Edge Type",
118
+ value="sharp",
119
+ type="value",
120
+ )
121
+ btn = gr.Button("Start")
 
 
 
 
 
 
122
  with gr.Column(scale=4):
123
+ textured_preview = gr.Model3D(label="Textured", height=380)
124
+ geometry_preview = gr.Model3D(label="Geometry", height=380)
 
125
  with gr.Column(scale=1):
126
  gr.Examples(
127
  examples=[
 
138
  cache_examples=False,
139
  )
140
 
141
+ btn.click(
142
+ generate_func,
 
 
 
143
  inputs=[
144
  input_image,
145
  guidance_scale,
 
148
  symmetry,
149
  edge_type,
150
  ],
151
+ outputs=[geometry_preview, textured_preview],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  )
153
 
154
+ demo.launch(ssr_mode=False)