cavargas10 commited on
Commit
ed6baf2
Β·
verified Β·
1 Parent(s): f17c864

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -205
app.py CHANGED
@@ -3,73 +3,29 @@ import spaces
3
  from gradio_litmodel3d import LitModel3D
4
  import os
5
  import shutil
6
- os.environ['SPCONV_ALGO'] = 'native'
7
- from typing import *
8
  import torch
9
  import numpy as np
10
  import imageio
11
  from easydict import EasyDict as edict
12
- from PIL import Image, ImageOps
13
  from trellis.pipelines import TrellisImageTo3DPipeline
14
  from trellis.representations import Gaussian, MeshExtractResult
15
  from trellis.utils import render_utils, postprocessing_utils
16
- from diffusers import ControlNetModel, StableDiffusionXLControlNetPipeline, AutoencoderKL
17
- from diffusers import EulerAncestralDiscreteScheduler
18
 
19
  MAX_SEED = np.iinfo(np.int32).max
20
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
21
  os.makedirs(TMP_DIR, exist_ok=True)
22
 
23
- # ConfiguraciΓ³n de estilos
24
- style_list = [
25
- {
26
- "name": "3D Model",
27
- "prompt": "professional 3d model {prompt} . octane render, highly detailed, volumetric, dramatic lighting",
28
- "negative_prompt": "ugly, deformed, noisy, low poly, blurry, painting",
29
- },
30
- # ... (puedes agregar mΓ‘s estilos del ejemplo original si es necesario)
31
- ]
32
- styles = {k["name"]: (k["prompt"], k["negative_prompt"]) for k in style_list}
33
- STYLE_NAMES = list(styles.keys())
34
- DEFAULT_STYLE_NAME = "3D Model"
35
-
36
  def start_session(req: gr.Request):
37
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
38
  os.makedirs(user_dir, exist_ok=True)
39
-
40
  def end_session(req: gr.Request):
41
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
42
- shutil.rmtree(user_dir)
43
 
44
- def apply_style(style_name: str, positive: str, negative: str = "") -> tuple[str, str]:
45
- p, n = styles.get(style_name, styles[DEFAULT_STYLE_NAME])
46
- return p.replace("{prompt}", positive), n + negative
47
-
48
- @spaces.GPU
49
- def preprocess_image(image: Image.Image, style_name: str, prompt: str) -> Image.Image:
50
- # Preprocesamiento con ControlNet
51
- width, height = image.size
52
- ratio = np.sqrt(1024. * 1024. / (width * height))
53
- new_size = (int(width * ratio), int(height * ratio))
54
-
55
- image = image.resize(new_size)
56
- image = ImageOps.invert(image.convert("L")).convert("RGB")
57
-
58
- prompt, negative_prompt = apply_style(style_name, prompt)
59
-
60
- # GeneraciΓ³n con ControlNet
61
- output = pipe_control(
62
- prompt=prompt,
63
- negative_prompt=negative_prompt,
64
- image=image,
65
- num_inference_steps=20,
66
- controlnet_conditioning_scale=0.85,
67
- guidance_scale=5.0,
68
- width=new_size[0],
69
- height=new_size[1]
70
- ).images[0]
71
-
72
- return output
73
 
74
  def pack_state(gs: Gaussian, mesh: MeshExtractResult) -> dict:
75
  return {
@@ -86,195 +42,84 @@ def pack_state(gs: Gaussian, mesh: MeshExtractResult) -> dict:
86
  'faces': mesh.faces.cpu().numpy(),
87
  },
88
  }
89
-
90
  def unpack_state(state: dict) -> Tuple[Gaussian, edict]:
91
- gs = Gaussian(**state['gaussian']['init_params'])
92
- gs._xyz = torch.tensor(state['gaussian']['_xyz']).cuda()
93
- gs._features_dc = torch.tensor(state['gaussian']['_features_dc']).cuda()
94
- gs._scaling = torch.tensor(state['gaussian']['_scaling']).cuda()
95
- gs._rotation = torch.tensor(state['gaussian']['_rotation']).cuda()
96
- gs._opacity = torch.tensor(state['gaussian']['_opacity']).cuda()
97
-
98
  mesh = edict(
99
- vertices=torch.tensor(state['mesh']['vertices']).cuda(),
100
- faces=torch.tensor(state['mesh']['faces']).cuda(),
101
  )
102
-
103
  return gs, mesh
104
 
 
 
 
105
  @spaces.GPU
106
- def image_to_3d(
107
- image: Image.Image,
108
- seed: int,
109
- ss_guidance: float,
110
- ss_steps: int,
111
- slat_guidance: float,
112
- slat_steps: int,
113
- req: gr.Request,
114
- ) -> Tuple[dict, str]:
115
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
116
  outputs = pipeline.run(
117
  image,
118
  seed=seed,
119
  formats=["gaussian", "mesh"],
120
- sparse_structure_sampler_params={
121
- "steps": ss_steps,
122
- "cfg_strength": ss_guidance,
123
- },
124
- slat_sampler_params={
125
- "steps": slat_steps,
126
- "cfg_strength": slat_guidance,
127
- },
128
  )
129
-
130
  video = render_utils.render_video(outputs['gaussian'][0], num_frames=120)['color']
 
 
131
  video_path = os.path.join(user_dir, 'sample.mp4')
132
  imageio.mimsave(video_path, video, fps=15)
133
-
134
  state = pack_state(outputs['gaussian'][0], outputs['mesh'][0])
135
  torch.cuda.empty_cache()
136
  return state, video_path
137
 
138
  @spaces.GPU(duration=90)
139
- def extract_glb(
140
- state: dict,
141
- simplify: float,
142
- texture_size: int,
143
- req: gr.Request,
144
- ) -> str:
145
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
146
  gs, mesh = unpack_state(state)
147
-
148
- glb = postprocessing_utils.to_glb(
149
- gs,
150
- mesh,
151
- simplify=simplify,
152
- texture_size=texture_size,
153
- verbose=False
154
- )
155
-
156
- glb_path = os.path.join(user_dir, 'model.glb')
157
  glb.export(glb_path)
158
  torch.cuda.empty_cache()
159
- return glb_path
160
 
161
  with gr.Blocks() as demo:
162
- gr.Markdown("# UTPL - ConversiΓ³n de Imagen a 3D")
163
- gr.Markdown("## Tesis: Modelado 3D automΓ‘tico con IA")
164
-
165
- with gr.Row():
166
- # Columna de entrada
167
- with gr.Column(scale=2):
168
- image_input = gr.ImageMask(
169
- label="Sube tu boceto",
170
- type="pil",
171
- image_mode="RGB",
172
- height=512,
173
- value={"background": Image.new("RGB", (512, 512), (255,255,255))}
174
- )
175
-
176
- with gr.Accordion("ConfiguraciΓ³n de GeneraciΓ³n", open=False):
177
- style = gr.Dropdown(
178
- STYLE_NAMES,
179
- value=DEFAULT_STYLE_NAME,
180
- label="Estilo"
181
- )
182
- prompt = gr.Textbox(
183
- label="Prompt",
184
- placeholder="Describe tu modelo 3D"
185
- )
186
- seed = gr.Slider(0, MAX_SEED, label="Seed", value=0, step=1)
187
- randomize_seed = gr.Checkbox(True, label="Semilla Aleatoria")
188
-
189
- with gr.Group():
190
- gr.Markdown("#### ParΓ‘metros 3D")
191
- ss_guidance = gr.Slider(0, 10, 7.5, label="GuΓ­a Estructura")
192
- ss_steps = gr.Slider(1, 50, 12, step=1, label="Pasos Estructura")
193
- slat_guidance = gr.Slider(0, 10, 3.0, label="GuΓ­a Detalle")
194
- slat_steps = gr.Slider(1, 50, 12, step=1, label="Pasos Detalle")
195
-
196
- generate_btn = gr.Button("Generar 3D", variant="primary")
197
-
198
- with gr.Accordion("Exportar GLB", open=False):
199
- mesh_simplify = gr.Slider(0.9, 0.98, 0.95, label="SimplificaciΓ³n")
200
- texture_size = gr.Slider(512, 2048, 1024, step=512, label="TamaΓ±o Textura")
201
- export_glb_btn = gr.Button("Exportar GLB", interactive=False)
202
-
203
- # Columna de salida
204
- with gr.Column(scale=3):
205
- video_preview = gr.Video(label="Vista Previa 3D", height=400)
206
- model_viewer = LitModel3D(label="Visor 3D", height=400)
207
- download_glb = gr.DownloadButton("Descargar GLB", interactive=False)
208
-
209
- output_state = gr.State()
210
-
211
- # Carga de pipelines
212
- pipe_control = None
213
- pipeline = None
214
 
215
- def initialize_pipelines():
216
- global pipe_control, pipeline
217
- device = "cuda" if torch.cuda.is_available() else "cpu"
218
-
219
- # ControlNet setup
220
- controlnet = ControlNetModel.from_pretrained(
221
- "xinsir/controlnet-scribble-sdxl-1.0",
222
- torch_dtype=torch.float16
223
- )
224
- vae = AutoencoderKL.from_pretrained(
225
- "madebyollin/sdxl-vae-fp16-fix",
226
- torch_dtype=torch.float16
227
- )
228
- pipe_control = StableDiffusionXLControlNetPipeline.from_pretrained(
229
- "sd-community/sdxl-flash",
230
- controlnet=controlnet,
231
- vae=vae,
232
- torch_dtype=torch.float16
233
- ).to(device)
234
- pipe_control.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe_control.scheduler.config)
235
-
236
- # TRELLIS pipeline
237
- pipeline = TrellisImageTo3DPipeline.from_pretrained("JeffreyXiang/TRELLIS-image-large").cuda()
238
 
239
- demo.load(initialize_pipelines)
240
- demo.load(start_session)
241
- demo.unload(end_session)
242
-
243
- # Flujo de generaciΓ³n
244
- image_processed = gr.Image(visible=False)
245
-
246
- image_input.upload(
247
- preprocess_image,
248
- inputs=[image_input, style, prompt],
249
- outputs=image_processed
250
- )
251
-
252
  generate_btn.click(
253
- lambda rnd, s: np.random.randint(0, MAX_SEED) if rnd else s,
254
- inputs=[randomize_seed, seed],
255
- outputs=seed
256
  ).then(
257
- image_to_3d,
258
- inputs=[image_processed, seed, ss_guidance, ss_steps, slat_guidance, slat_steps],
259
- outputs=[output_state, video_preview]
260
  ).then(
261
- lambda: gr.update(interactive=True),
262
- outputs=export_glb_btn
263
  )
264
-
265
- export_glb_btn.click(
266
- extract_glb,
267
- inputs=[output_state, mesh_simplify, texture_size],
268
- outputs=model_viewer
269
  ).then(
270
- lambda: gr.update(interactive=True),
271
- outputs=download_glb
272
- )
273
-
274
- model_viewer.change(
275
- lambda: gr.update(value=model_viewer.value),
276
- outputs=download_glb
277
  )
278
 
279
  if __name__ == "__main__":
280
- demo.launch()
 
 
3
  from gradio_litmodel3d import LitModel3D
4
  import os
5
  import shutil
 
 
6
  import torch
7
  import numpy as np
8
  import imageio
9
  from easydict import EasyDict as edict
10
+ from PIL import Image
11
  from trellis.pipelines import TrellisImageTo3DPipeline
12
  from trellis.representations import Gaussian, MeshExtractResult
13
  from trellis.utils import render_utils, postprocessing_utils
 
 
14
 
15
  MAX_SEED = np.iinfo(np.int32).max
16
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
17
  os.makedirs(TMP_DIR, exist_ok=True)
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def start_session(req: gr.Request):
20
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
21
  os.makedirs(user_dir, exist_ok=True)
22
+
23
  def end_session(req: gr.Request):
24
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
25
+ shutil.rmtree(user_dir, ignore_errors=True)
26
 
27
+ def preprocess_image(image: Image.Image) -> Image.Image:
28
+ return pipeline.preprocess_image(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  def pack_state(gs: Gaussian, mesh: MeshExtractResult) -> dict:
31
  return {
 
42
  'faces': mesh.faces.cpu().numpy(),
43
  },
44
  }
45
+
46
  def unpack_state(state: dict) -> Tuple[Gaussian, edict]:
47
+ gs = Gaussian(**state['gaussian'])
48
+ gs._xyz = torch.tensor(state['gaussian']['_xyz'], device='cuda')
49
+ gs._features_dc = torch.tensor(state['gaussian']['_features_dc'], device='cuda')
50
+ gs._scaling = torch.tensor(state['gaussian']['_scaling'], device='cuda')
51
+ gs._rotation = torch.tensor(state['gaussian']['_rotation'], device='cuda')
52
+ gs._opacity = torch.tensor(state['gaussian']['_opacity'], device='cuda')
53
+
54
  mesh = edict(
55
+ vertices=torch.tensor(state['mesh']['vertices'], device='cuda'),
56
+ faces=torch.tensor(state['mesh']['faces'], device='cuda'),
57
  )
 
58
  return gs, mesh
59
 
60
+ def get_seed(randomize_seed: bool, seed: int) -> int:
61
+ return np.random.randint(0, MAX_SEED) if randomize_seed else seed
62
+
63
  @spaces.GPU
64
+ def image_to_3d(image: Image.Image, seed: int, ss_guidance_strength: float, ss_sampling_steps: int, slat_guidance_strength: float, slat_sampling_steps: int, req: gr.Request) -> Tuple[dict, str]:
 
 
 
 
 
 
 
 
65
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
66
  outputs = pipeline.run(
67
  image,
68
  seed=seed,
69
  formats=["gaussian", "mesh"],
70
+ preprocess_image=False,
71
+ sparse_structure_sampler_params={"steps": ss_sampling_steps, "cfg_strength": ss_guidance_strength},
72
+ slat_sampler_params={"steps": slat_sampling_steps, "cfg_strength": slat_guidance_strength},
 
 
 
 
 
73
  )
74
+
75
  video = render_utils.render_video(outputs['gaussian'][0], num_frames=120)['color']
76
+ video_geo = render_utils.render_video(outputs['mesh'][0], num_frames=120)['normal']
77
+ video = [np.concatenate([video[i], video_geo[i]], axis=1) for i in range(len(video))]
78
  video_path = os.path.join(user_dir, 'sample.mp4')
79
  imageio.mimsave(video_path, video, fps=15)
 
80
  state = pack_state(outputs['gaussian'][0], outputs['mesh'][0])
81
  torch.cuda.empty_cache()
82
  return state, video_path
83
 
84
  @spaces.GPU(duration=90)
85
+ def extract_glb(state: dict, mesh_simplify: float, texture_size: int, req: gr.Request) -> Tuple[str, str]:
 
 
 
 
 
86
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
87
  gs, mesh = unpack_state(state)
88
+ glb = postprocessing_utils.to_glb(gs, mesh, simplify=mesh_simplify, texture_size=texture_size, verbose=False)
89
+ glb_path = os.path.join(user_dir, 'sample.glb')
 
 
 
 
 
 
 
 
90
  glb.export(glb_path)
91
  torch.cuda.empty_cache()
92
+ return glb_path, glb_path
93
 
94
  with gr.Blocks() as demo:
95
+ gr.Markdown("""
96
+ # ConversiΓ³n de ImΓ‘gen a 3D
97
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ image_prompt = gr.Image(label="Input Image", type="pil")
100
+ seed = gr.Slider(0, MAX_SEED, label="Seed", value=0)
101
+ randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
102
+ generate_btn = gr.Button("Generate 3D Asset")
103
+ video_output = gr.Video(label="3D Preview")
104
+ model_output = LitModel3D(label="3D Model Viewer")
105
+ extract_glb_btn = gr.Button("Export GLB")
106
+ download_glb = gr.DownloadButton(label="Download GLB")
107
+ output_buf = gr.State()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  generate_btn.click(
110
+ get_seed, inputs=[randomize_seed, seed], outputs=[seed]
 
 
111
  ).then(
112
+ image_to_3d, inputs=[image_prompt, seed, 7.5, 12, 3.0, 12], outputs=[output_buf, video_output]
 
 
113
  ).then(
114
+ lambda: gr.Button(interactive=True), outputs=[extract_glb_btn]
 
115
  )
116
+
117
+ extract_glb_btn.click(
118
+ extract_glb, inputs=[output_buf, 0.95, 1024], outputs=[model_output, download_glb]
 
 
119
  ).then(
120
+ lambda: gr.Button(interactive=True), outputs=[download_glb]
 
 
 
 
 
 
121
  )
122
 
123
  if __name__ == "__main__":
124
+ pipeline = TrellisImageTo3DPipeline.from_pretrained("cavargas10/TRELLIS").cuda()
125
+ demo.launch()