cronos3k commited on
Commit
abd8161
·
verified ·
1 Parent(s): 876b58d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -5
app.py CHANGED
@@ -1,4 +1,93 @@
1
- # [Previous imports and utility functions remain exactly the same until image_to_3d]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  def image_to_3d(
4
  image: Image.Image,
@@ -27,7 +116,6 @@ def image_to_3d(
27
  "cfg_strength": slat_guidance_strength,
28
  },
29
  )
30
-
31
  video = render_utils.render_video(outputs['gaussian'][0], num_frames=120)['color']
32
  video_geo = render_utils.render_video(outputs['mesh'][0], num_frames=120)['normal']
33
  video = [np.concatenate([video[i], video_geo[i]], axis=1) for i in range(len(video))]
@@ -35,7 +123,7 @@ def image_to_3d(
35
  video_path = os.path.join(user_dir, f"{trial_id}.mp4")
36
  imageio.mimsave(video_path, video, fps=15)
37
 
38
- # Generate full quality GLB
39
  glb = postprocessing_utils.to_glb(
40
  outputs['gaussian'][0],
41
  outputs['mesh'][0],
@@ -51,7 +139,94 @@ def image_to_3d(
51
  state = pack_state(outputs['gaussian'][0], outputs['mesh'][0], trial_id)
52
  return state, video_path, full_glb_path
53
 
54
- # [Rest of the code remains exactly the same, except for the event handler which needs to be updated]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  generate_btn.click(
57
  get_seed,
@@ -66,4 +241,20 @@ def image_to_3d(
66
  outputs=[download_full, extract_glb_btn, download_reduced],
67
  )
68
 
69
- # [Rest of the code remains exactly the same]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import shutil
4
+ os.environ['SPCONV_ALGO'] = 'native'
5
+ from typing import *
6
+ import torch
7
+ import numpy as np
8
+ import imageio
9
+ import uuid
10
+ from easydict import EasyDict as edict
11
+ from PIL import Image
12
+ from trellis.pipelines import TrellisImageTo3DPipeline
13
+ from trellis.representations import Gaussian, MeshExtractResult
14
+ from trellis.utils import render_utils, postprocessing_utils
15
+ from gradio_litmodel3d import LitModel3D
16
+
17
+
18
+ MAX_SEED = np.iinfo(np.int32).max
19
+ TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
20
+ os.makedirs(TMP_DIR, exist_ok=True)
21
+
22
+
23
+ def start_session(req: gr.Request):
24
+ user_dir = os.path.join(TMP_DIR, str(req.session_hash))
25
+ print(f'Creating user directory: {user_dir}')
26
+ os.makedirs(user_dir, exist_ok=True)
27
+
28
+ def end_session(req: gr.Request):
29
+ user_dir = os.path.join(TMP_DIR, str(req.session_hash))
30
+ print(f'Removing user directory: {user_dir}')
31
+ shutil.rmtree(user_dir)
32
+
33
+ def preprocess_image(image: Image.Image) -> Tuple[str, Image.Image]:
34
+ """
35
+ Preprocess the input image.
36
+
37
+ Args:
38
+ image (Image.Image): The input image.
39
+
40
+ Returns:
41
+ str: uuid of the trial.
42
+ Image.Image: The preprocessed image.
43
+ """
44
+ processed_image = pipeline.preprocess_image(image)
45
+ return processed_image
46
+
47
+ def pack_state(gs: Gaussian, mesh: MeshExtractResult, trial_id: str) -> dict:
48
+ return {
49
+ 'gaussian': {
50
+ **gs.init_params,
51
+ '_xyz': gs._xyz.cpu().numpy(),
52
+ '_features_dc': gs._features_dc.cpu().numpy(),
53
+ '_scaling': gs._scaling.cpu().numpy(),
54
+ '_rotation': gs._rotation.cpu().numpy(),
55
+ '_opacity': gs._opacity.cpu().numpy(),
56
+ },
57
+ 'mesh': {
58
+ 'vertices': mesh.vertices.cpu().numpy(),
59
+ 'faces': mesh.faces.cpu().numpy(),
60
+ },
61
+ 'trial_id': trial_id,
62
+ }
63
+
64
+ def unpack_state(state: dict) -> Tuple[Gaussian, edict, str]:
65
+ gs = Gaussian(
66
+ aabb=state['gaussian']['aabb'],
67
+ sh_degree=state['gaussian']['sh_degree'],
68
+ mininum_kernel_size=state['gaussian']['mininum_kernel_size'],
69
+ scaling_bias=state['gaussian']['scaling_bias'],
70
+ opacity_bias=state['gaussian']['opacity_bias'],
71
+ scaling_activation=state['gaussian']['scaling_activation'],
72
+ )
73
+ gs._xyz = torch.tensor(state['gaussian']['_xyz'], device='cuda')
74
+ gs._features_dc = torch.tensor(state['gaussian']['_features_dc'], device='cuda')
75
+ gs._scaling = torch.tensor(state['gaussian']['_scaling'], device='cuda')
76
+ gs._rotation = torch.tensor(state['gaussian']['_rotation'], device='cuda')
77
+ gs._opacity = torch.tensor(state['gaussian']['_opacity'], device='cuda')
78
+
79
+ mesh = edict(
80
+ vertices=torch.tensor(state['mesh']['vertices'], device='cuda'),
81
+ faces=torch.tensor(state['mesh']['faces'], device='cuda'),
82
+ )
83
+
84
+ return gs, mesh, state['trial_id']
85
+
86
+ def get_seed(randomize_seed: bool, seed: int) -> int:
87
+ """
88
+ Get the random seed.
89
+ """
90
+ return np.random.randint(0, MAX_SEED) if randomize_seed else seed
91
 
92
  def image_to_3d(
93
  image: Image.Image,
 
116
  "cfg_strength": slat_guidance_strength,
117
  },
118
  )
 
119
  video = render_utils.render_video(outputs['gaussian'][0], num_frames=120)['color']
120
  video_geo = render_utils.render_video(outputs['mesh'][0], num_frames=120)['normal']
121
  video = [np.concatenate([video[i], video_geo[i]], axis=1) for i in range(len(video))]
 
123
  video_path = os.path.join(user_dir, f"{trial_id}.mp4")
124
  imageio.mimsave(video_path, video, fps=15)
125
 
126
+ # Save full quality GLB
127
  glb = postprocessing_utils.to_glb(
128
  outputs['gaussian'][0],
129
  outputs['mesh'][0],
 
139
  state = pack_state(outputs['gaussian'][0], outputs['mesh'][0], trial_id)
140
  return state, video_path, full_glb_path
141
 
142
+ def extract_glb(
143
+ state: dict,
144
+ mesh_simplify: float,
145
+ texture_size: int,
146
+ req: gr.Request,
147
+ ) -> Tuple[str, str]:
148
+ """
149
+ Extract a GLB file from the 3D model.
150
+ """
151
+ user_dir = os.path.join(TMP_DIR, str(req.session_hash))
152
+ gs, mesh, trial_id = unpack_state(state)
153
+ glb = postprocessing_utils.to_glb(
154
+ gs, mesh,
155
+ simplify=mesh_simplify,
156
+ fill_holes=True,
157
+ fill_holes_max_size=0.04,
158
+ texture_size=texture_size,
159
+ verbose=False
160
+ )
161
+ glb_path = os.path.join(user_dir, f"{trial_id}_reduced.glb")
162
+ glb.export(glb_path)
163
+ return glb_path, glb_path
164
+
165
+ with gr.Blocks(delete_cache=(600, 600)) as demo:
166
+ gr.Markdown("""
167
+ ## Image to 3D Asset with [TRELLIS](https://trellis3d.github.io/)
168
+ * Upload an image and click "Generate" to create a 3D asset
169
+ * After generation:
170
+ * Download the full quality GLB immediately
171
+ * Or create a reduced size version with the extraction settings below
172
+ """)
173
+
174
+ with gr.Row():
175
+ with gr.Column():
176
+ image_prompt = gr.Image(label="Image Prompt", format="png", image_mode="RGBA", type="pil", height=300)
177
+
178
+ with gr.Accordion(label="Generation Settings", open=False):
179
+ seed = gr.Slider(0, MAX_SEED, label="Seed", value=0, step=1)
180
+ randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
181
+ gr.Markdown("Stage 1: Sparse Structure Generation")
182
+ with gr.Row():
183
+ ss_guidance_strength = gr.Slider(0.0, 10.0, label="Guidance Strength", value=7.5, step=0.1)
184
+ ss_sampling_steps = gr.Slider(1, 500, label="Sampling Steps", value=12, step=1)
185
+ gr.Markdown("Stage 2: Structured Latent Generation")
186
+ with gr.Row():
187
+ slat_guidance_strength = gr.Slider(0.0, 10.0, label="Guidance Strength", value=3.0, step=0.1)
188
+ slat_sampling_steps = gr.Slider(1, 500, label="Sampling Steps", value=12, step=1)
189
+
190
+ generate_btn = gr.Button("Generate")
191
+
192
+ with gr.Accordion(label="GLB Extraction Settings", open=False):
193
+ mesh_simplify = gr.Slider(0.0, 0.98, label="Simplify", value=0.95, step=0.01)
194
+ texture_size = gr.Slider(512, 2048, label="Texture Size", value=1024, step=512)
195
+
196
+ extract_glb_btn = gr.Button("Extract Reduced GLB", interactive=False)
197
+
198
+ with gr.Column():
199
+ video_output = gr.Video(label="Generated 3D Asset", autoplay=True, loop=True, height=300)
200
+ model_output = LitModel3D(label="3D Model Preview", exposure=20.0, height=300)
201
+ with gr.Row():
202
+ download_full = gr.DownloadButton(label="Download Full-Quality GLB", interactive=False)
203
+ download_reduced = gr.DownloadButton(label="Download Reduced GLB", interactive=False)
204
+
205
+ output_buf = gr.State()
206
+
207
+ # Example images at the bottom of the page
208
+ with gr.Row():
209
+ examples = gr.Examples(
210
+ examples=[
211
+ f'assets/example_image/{image}'
212
+ for image in os.listdir("assets/example_image")
213
+ ],
214
+ inputs=[image_prompt],
215
+ fn=preprocess_image,
216
+ outputs=[image_prompt],
217
+ run_on_click=True,
218
+ examples_per_page=64,
219
+ )
220
+
221
+ # Event handlers
222
+ demo.load(start_session)
223
+ demo.unload(end_session)
224
+
225
+ image_prompt.upload(
226
+ preprocess_image,
227
+ inputs=[image_prompt],
228
+ outputs=[image_prompt],
229
+ )
230
 
231
  generate_btn.click(
232
  get_seed,
 
241
  outputs=[download_full, extract_glb_btn, download_reduced],
242
  )
243
 
244
+ extract_glb_btn.click(
245
+ extract_glb,
246
+ inputs=[output_buf, mesh_simplify, texture_size],
247
+ outputs=[model_output, download_reduced],
248
+ ).then(
249
+ lambda: gr.Button(interactive=True),
250
+ outputs=[download_reduced],
251
+ )
252
+
253
+ if __name__ == "__main__":
254
+ pipeline = TrellisImageTo3DPipeline.from_pretrained("JeffreyXiang/TRELLIS-image-large")
255
+ pipeline.cuda()
256
+ try:
257
+ pipeline.preprocess_image(Image.fromarray(np.zeros((512, 512, 3), dtype=np.uint8)))
258
+ except:
259
+ pass
260
+ demo.launch()