Upload 3 files
Browse files- README.md +7 -5
- app.py +219 -390
- requirements.txt +4 -29
README.md
CHANGED
@@ -1,12 +1,14 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: gradio
|
7 |
-
sdk_version:
|
8 |
app_file: app.py
|
9 |
pinned: false
|
|
|
|
|
10 |
---
|
11 |
|
12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
2 |
+
title: LLaMA Mesh
|
3 |
+
emoji: 👀
|
4 |
+
colorFrom: red
|
5 |
+
colorTo: green
|
6 |
sdk: gradio
|
7 |
+
sdk_version: 5.6.0
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
+
license: llama3.1
|
11 |
+
short_description: Create 3D mesh by chatting.
|
12 |
---
|
13 |
|
14 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
CHANGED
@@ -1,411 +1,240 @@
|
|
1 |
-
import os
|
2 |
-
import torch
|
3 |
-
import fire
|
4 |
import gradio as gr
|
5 |
-
from PIL import Image
|
6 |
-
from functools import partial
|
7 |
-
import spaces
|
8 |
-
import cv2
|
9 |
-
import time
|
10 |
-
import numpy as np
|
11 |
-
from rembg import remove
|
12 |
-
from segment_anything import sam_model_registry, SamPredictor
|
13 |
-
|
14 |
import os
|
15 |
-
import
|
16 |
-
|
17 |
-
from
|
18 |
-
from
|
19 |
-
from dataclasses import dataclass
|
20 |
-
from mvdiffusion.data.single_image_dataset import SingleImageDataset
|
21 |
-
from mvdiffusion.pipelines.pipeline_mvdiffusion_unclip import StableUnCLIPImg2ImgPipeline
|
22 |
-
from einops import rearrange
|
23 |
-
import numpy as np
|
24 |
-
import subprocess
|
25 |
-
from datetime import datetime
|
26 |
-
from icecream import ic
|
27 |
-
def save_image(tensor):
|
28 |
-
ndarr = tensor.mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to("cpu", torch.uint8).numpy()
|
29 |
-
# pdb.set_trace()
|
30 |
-
im = Image.fromarray(ndarr)
|
31 |
-
return ndarr
|
32 |
-
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
# pdb.set_trace()
|
37 |
-
im = Image.fromarray(ndarr)
|
38 |
-
im.save(fp)
|
39 |
-
return ndarr
|
40 |
|
41 |
|
42 |
-
|
43 |
-
im = Image.fromarray(ndarr)
|
44 |
-
im.save(fp)
|
45 |
-
|
46 |
-
|
47 |
-
weight_dtype = torch.float16
|
48 |
-
|
49 |
-
_TITLE = '''Era3D: High-Resolution Multiview Diffusion using Efficient Row-wise Attention'''
|
50 |
-
_DESCRIPTION = '''
|
51 |
<div>
|
52 |
-
|
53 |
-
</div>
|
54 |
<div>
|
55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
</div>
|
57 |
'''
|
58 |
-
_GPU_ID = 0
|
59 |
-
|
60 |
-
|
61 |
-
if not hasattr(Image, 'Resampling'):
|
62 |
-
Image.Resampling = Image
|
63 |
-
|
64 |
-
|
65 |
-
def sam_init():
|
66 |
-
sam_checkpoint = os.path.join(os.path.dirname(__file__), "sam_pt", "sam_vit_h_4b8939.pth")
|
67 |
-
model_type = "vit_h"
|
68 |
-
|
69 |
-
sam = sam_model_registry[model_type](checkpoint=sam_checkpoint).to(device=f"cuda:{_GPU_ID}")
|
70 |
-
predictor = SamPredictor(sam)
|
71 |
-
return predictor
|
72 |
-
|
73 |
-
@spaces.GPU
|
74 |
-
def sam_segment(predictor, input_image, *bbox_coords):
|
75 |
-
bbox = np.array(bbox_coords)
|
76 |
-
image = np.asarray(input_image)
|
77 |
-
|
78 |
-
start_time = time.time()
|
79 |
-
predictor.set_image(image)
|
80 |
-
|
81 |
-
masks_bbox, scores_bbox, logits_bbox = predictor.predict(box=bbox, multimask_output=True)
|
82 |
-
|
83 |
-
print(f"SAM Time: {time.time() - start_time:.3f}s")
|
84 |
-
out_image = np.zeros((image.shape[0], image.shape[1], 4), dtype=np.uint8)
|
85 |
-
out_image[:, :, :3] = image
|
86 |
-
out_image_bbox = out_image.copy()
|
87 |
-
out_image_bbox[:, :, 3] = masks_bbox[-1].astype(np.uint8) * 255
|
88 |
-
torch.cuda.empty_cache()
|
89 |
-
return Image.fromarray(out_image_bbox, mode='RGBA')
|
90 |
-
|
91 |
|
92 |
-
|
93 |
-
|
94 |
-
if width == height:
|
95 |
-
return pil_img
|
96 |
-
elif width > height:
|
97 |
-
result = Image.new(pil_img.mode, (width, width), background_color)
|
98 |
-
result.paste(pil_img, (0, (width - height) // 2))
|
99 |
-
return result
|
100 |
-
else:
|
101 |
-
result = Image.new(pil_img.mode, (height, height), background_color)
|
102 |
-
result.paste(pil_img, ((height - width) // 2, 0))
|
103 |
-
return result
|
104 |
|
|
|
|
|
|
|
105 |
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
)
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
#
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
pipeline.set_progress_bar_config(disable=True)
|
178 |
-
seed = int(seed)
|
179 |
-
generator = torch.Generator(device=pipeline.unet.device).manual_seed(seed)
|
180 |
-
|
181 |
-
|
182 |
-
imgs_in = torch.cat([batch['imgs_in']]*2, dim=0)
|
183 |
-
num_views = imgs_in.shape[1]
|
184 |
-
imgs_in = rearrange(imgs_in, "B Nv C H W -> (B Nv) C H W")# (B*Nv, 3, H, W)
|
185 |
-
|
186 |
-
normal_prompt_embeddings, clr_prompt_embeddings = batch['normal_prompt_embeddings'], batch['color_prompt_embeddings']
|
187 |
-
prompt_embeddings = torch.cat([normal_prompt_embeddings, clr_prompt_embeddings], dim=0)
|
188 |
-
prompt_embeddings = rearrange(prompt_embeddings, "B Nv N C -> (B Nv) N C")
|
189 |
-
|
190 |
|
191 |
-
|
192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
bsz = out.shape[0] // 2
|
207 |
-
normals_pred = out[:bsz]
|
208 |
-
images_pred = out[bsz:]
|
209 |
-
num_views = 6
|
210 |
-
if write_image:
|
211 |
-
VIEWS = ['front', 'front_right', 'right', 'back', 'left', 'front_left']
|
212 |
-
cur_dir = os.path.join(cfg.save_dir, f"cropsize-{int(crop_size)}-cfg{guidance_scale:.1f}")
|
213 |
-
|
214 |
-
scene = 'scene'+datetime.now().strftime('@%Y%m%d-%H%M%S')
|
215 |
-
scene_dir = os.path.join(cur_dir, scene)
|
216 |
-
os.makedirs(scene_dir, exist_ok=True)
|
217 |
-
|
218 |
-
for j in range(num_views):
|
219 |
-
view = VIEWS[j]
|
220 |
-
normal = normals_pred[j]
|
221 |
-
color = images_pred[j]
|
222 |
-
|
223 |
-
normal_filename = f"normals_{view}_masked.png"
|
224 |
-
color_filename = f"color_{view}_masked.png"
|
225 |
-
normal = save_image_to_disk(normal, os.path.join(scene_dir, normal_filename))
|
226 |
-
color = save_image_to_disk(color, os.path.join(scene_dir, color_filename))
|
227 |
-
|
228 |
-
|
229 |
-
normals_pred = [save_image(normals_pred[i]) for i in range(bsz)]
|
230 |
-
images_pred = [save_image(images_pred[i]) for i in range(bsz)]
|
231 |
-
|
232 |
-
out = images_pred + normals_pred
|
233 |
-
return images_pred, normals_pred
|
234 |
-
|
235 |
-
|
236 |
-
def process_3d(mode, data_dir, guidance_scale, crop_size):
|
237 |
-
dir = None
|
238 |
-
global scene
|
239 |
-
|
240 |
-
cur_dir = os.path.dirname(os.path.abspath(__file__))
|
241 |
-
|
242 |
-
subprocess.run(
|
243 |
-
f'cd instant-nsr-pl && bash run.sh 0 {scene} exp_demo && cd ..',
|
244 |
-
shell=True,
|
245 |
)
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
revision: Optional[str]
|
260 |
-
validation_dataset: Dict
|
261 |
-
save_dir: str
|
262 |
-
seed: Optional[int]
|
263 |
-
validation_batch_size: int
|
264 |
-
dataloader_num_workers: int
|
265 |
-
# save_single_views: bool
|
266 |
-
save_mode: str
|
267 |
-
local_rank: int
|
268 |
-
|
269 |
-
pipe_kwargs: Dict
|
270 |
-
pipe_validation_kwargs: Dict
|
271 |
-
unet_from_pretrained_kwargs: Dict
|
272 |
-
validation_guidance_scales: List[float]
|
273 |
-
validation_grid_nrow: int
|
274 |
-
camera_embedding_lr_mult: float
|
275 |
-
|
276 |
-
num_views: int
|
277 |
-
camera_embedding_type: str
|
278 |
-
|
279 |
-
pred_type: str # joint, or ablation
|
280 |
-
regress_elevation: bool
|
281 |
-
enable_xformers_memory_efficient_attention: bool
|
282 |
-
|
283 |
-
cond_on_normals: bool
|
284 |
-
cond_on_colors: bool
|
285 |
-
|
286 |
-
regress_elevation: bool
|
287 |
-
regress_focal_length: bool
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
def run_demo():
|
292 |
-
from utils.misc import load_config
|
293 |
-
from omegaconf import OmegaConf
|
294 |
-
|
295 |
-
# parse YAML config to OmegaConf
|
296 |
-
cfg = load_config("./configs/test_unclip-512-6view.yaml")
|
297 |
-
# print(cfg)
|
298 |
-
schema = OmegaConf.structured(TestConfig)
|
299 |
-
cfg = OmegaConf.merge(schema, cfg)
|
300 |
-
|
301 |
-
pipeline = load_era3d_pipeline(cfg)
|
302 |
-
torch.set_grad_enabled(False)
|
303 |
-
|
304 |
-
|
305 |
-
predictor = sam_init()
|
306 |
-
|
307 |
|
308 |
-
|
309 |
-
|
310 |
-
)
|
311 |
-
custom_css = '''#disp_image {
|
312 |
-
text-align: center; /* Horizontally center the content */
|
313 |
-
}'''
|
314 |
-
|
315 |
|
316 |
-
|
|
|
|
|
|
|
317 |
with gr.Row():
|
318 |
-
with gr.Column(scale=
|
319 |
-
gr.
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
)
|
337 |
-
|
338 |
-
# ## add 3D Model
|
339 |
-
# obj_3d = gr.Model3D(
|
340 |
-
# # clear_color=[0.0, 0.0, 0.0, 0.0],
|
341 |
-
# label="3D Model", height=320,
|
342 |
-
# # camera_position=[0,0,2.0]
|
343 |
-
# )
|
344 |
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
examples=example_fns,
|
351 |
-
inputs=[input_image],
|
352 |
-
outputs=[input_image],
|
353 |
-
cache_examples=False,
|
354 |
-
label='Examples (click one of the images below to start)',
|
355 |
-
examples_per_page=30,
|
356 |
)
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
input_processing = gr.CheckboxGroup(
|
362 |
-
['Background Removal'],
|
363 |
-
label='Input Image Preprocessing',
|
364 |
-
value=['Background Removal'],
|
365 |
-
info='untick this, if masked image with alpha channel',
|
366 |
-
)
|
367 |
-
with gr.Column():
|
368 |
-
with gr.Accordion('Advanced options', open=False):
|
369 |
-
output_processing = gr.CheckboxGroup(
|
370 |
-
['Write Results'], label='write the results in mv_res folder', value=['Write Results']
|
371 |
-
)
|
372 |
-
with gr.Row():
|
373 |
-
with gr.Column():
|
374 |
-
scale_slider = gr.Slider(1, 5, value=3, step=1, label='Classifier Free Guidance Scale')
|
375 |
-
with gr.Column():
|
376 |
-
steps_slider = gr.Slider(15, 100, value=40, step=1, label='Number of Diffusion Inference Steps')
|
377 |
-
with gr.Row():
|
378 |
-
with gr.Column():
|
379 |
-
seed = gr.Number(600, label='Seed', info='100 for digital portraits')
|
380 |
-
with gr.Column():
|
381 |
-
crop_size = gr.Number(420, label='Crop size', info='380 for digital portraits')
|
382 |
-
|
383 |
-
mode = gr.Textbox('train', visible=False)
|
384 |
-
data_dir = gr.Textbox('outputs', visible=False)
|
385 |
-
# with gr.Row():
|
386 |
-
# method = gr.Radio(choices=['instant-nsr-pl', 'NeuS'], label='Method (Default: instant-nsr-pl)', value='instant-nsr-pl')
|
387 |
-
run_btn = gr.Button('Generate Normals and Colors', variant='primary', interactive=True)
|
388 |
-
# recon_btn = gr.Button('Reconstruct 3D model', variant='primary', interactive=True)
|
389 |
-
# gr.Markdown("<span style='color:red'>First click Generate button, then click Reconstruct button. Reconstruction may cost several minutes.</span>")
|
390 |
-
|
391 |
-
with gr.Row():
|
392 |
-
view_gallery = gr.Gallery(label='Multiview Images')
|
393 |
-
normal_gallery = gr.Gallery(label='Multiview Normals')
|
394 |
-
|
395 |
-
print('Launching...')
|
396 |
-
run_btn.click(
|
397 |
-
fn=partial(preprocess, predictor), inputs=[input_image, input_processing], outputs=[processed_image_highres, processed_image], queue=True
|
398 |
-
).success(
|
399 |
-
fn=partial(run_pipeline, pipeline, cfg),
|
400 |
-
inputs=[processed_image_highres, scale_slider, steps_slider, seed, crop_size, output_processing],
|
401 |
-
outputs=[view_gallery, normal_gallery],
|
402 |
-
)
|
403 |
-
# recon_btn.click(
|
404 |
-
# process_3d, inputs=[mode, data_dir, scale_slider, crop_size], outputs=[obj_3d]
|
405 |
-
# )
|
406 |
-
|
407 |
-
demo.queue().launch(share=True, max_threads=80)
|
408 |
-
|
409 |
-
|
410 |
-
if __name__ == '__main__':
|
411 |
-
fire.Fire(run_demo)
|
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import os
|
3 |
+
import spaces
|
4 |
+
from transformers import GemmaTokenizer, AutoModelForCausalLM
|
5 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
|
6 |
+
from threading import Thread
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
+
# Set an environment variable
|
9 |
+
HF_TOKEN = os.environ.get("HF_TOKEN", None)
|
|
|
|
|
|
|
|
|
10 |
|
11 |
|
12 |
+
DESCRIPTION = '''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
<div>
|
14 |
+
<h1 style="text-align: center;">LLaMA-Mesh</h1>
|
|
|
15 |
<div>
|
16 |
+
<a style="display:inline-block" href="https://research.nvidia.com/labs/toronto-ai/LLaMA-Mesh/"><img src='https://img.shields.io/badge/public_website-8A2BE2'></a>
|
17 |
+
<a style="display:inline-block; margin-left: .5em" href="https://github.com/nv-tlabs/LLaMA-Mesh"><img src='https://img.shields.io/github/stars/nv-tlabs/LLaMA-Mesh?style=social'/></a>
|
18 |
+
</div>
|
19 |
+
<p>LLaMA-Mesh: Unifying 3D Mesh Generation with Language Models.<a style="display:inline-block" href="https://research.nvidia.com/labs/toronto-ai/LLaMA-Mesh/">[Project Page]</a> <a style="display:inline-block" href="https://github.com/nv-tlabs/LLaMA-Mesh">[Code]</a></p>
|
20 |
+
<p> Notice: (1) This demo supports up to 4096 tokens due to computational limits, while our full model supports 8k tokens. This limitation may result in incomplete generated meshes. To experience the full 8k token context, please run our model locally.</p>
|
21 |
+
<p>(2) We only support generating a single mesh per dialog round. To generate another mesh, click the "clear" button and start a new dialog.</p>
|
22 |
+
<p>(3) If the LLM refuses to generate a 3D mesh, try adding more explicit instructions to the prompt, such as "create a 3D model of a table <strong>in OBJ format</strong>." A more effective approach is to request the mesh generation at the start of the dialog.</p>
|
23 |
</div>
|
24 |
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
+
LICENSE = """
|
27 |
+
<p/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
+
---
|
30 |
+
Built with Meta Llama 3.1 8B
|
31 |
+
"""
|
32 |
|
33 |
+
PLACEHOLDER = """
|
34 |
+
<div style="padding: 30px; text-align: center; display: flex; flex-direction: column; align-items: center;">
|
35 |
+
<h1 style="font-size: 28px; margin-bottom: 2px; opacity: 0.55;">LLaMA-Mesh</h1>
|
36 |
+
<p style="font-size: 18px; margin-bottom: 2px; opacity: 0.65;">Create 3D meshes by chatting.</p>
|
37 |
+
</div>
|
38 |
+
"""
|
39 |
+
|
40 |
+
|
41 |
+
css = """
|
42 |
+
h1 {
|
43 |
+
text-align: center;
|
44 |
+
display: block;
|
45 |
+
}
|
46 |
+
|
47 |
+
#duplicate-button {
|
48 |
+
margin: auto;
|
49 |
+
color: white;
|
50 |
+
background: #1565c0;
|
51 |
+
border-radius: 100vh;
|
52 |
+
}
|
53 |
+
"""
|
54 |
+
# Load the tokenizer and model
|
55 |
+
model_path = "Zhengyi/LLaMA-Mesh"
|
56 |
+
tokenizer = AutoTokenizer.from_pretrained(model_path)
|
57 |
+
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto")
|
58 |
+
terminators = [
|
59 |
+
tokenizer.eos_token_id,
|
60 |
+
tokenizer.convert_tokens_to_ids("<|eot_id|>")
|
61 |
+
]
|
62 |
+
|
63 |
+
|
64 |
+
from trimesh.exchange.gltf import export_glb
|
65 |
+
import gradio as gr
|
66 |
+
import trimesh
|
67 |
+
import numpy as np
|
68 |
+
import tempfile
|
69 |
+
def apply_gradient_color(mesh_text):
|
70 |
+
"""
|
71 |
+
Apply a gradient color to the mesh vertices based on the Y-axis and save as GLB.
|
72 |
+
Args:
|
73 |
+
mesh_text (str): The input mesh in OBJ format as a string.
|
74 |
+
Returns:
|
75 |
+
str: Path to the GLB file with gradient colors applied.
|
76 |
+
"""
|
77 |
+
# Load the mesh
|
78 |
+
temp_file = tempfile.NamedTemporaryFile(suffix=f"", delete=False).name#"temp_mesh.obj"
|
79 |
+
with open(temp_file+".obj", "w") as f:
|
80 |
+
f.write(mesh_text)
|
81 |
+
# return temp_file
|
82 |
+
mesh = trimesh.load_mesh(temp_file+".obj", file_type='obj')
|
83 |
+
|
84 |
+
# Get vertex coordinates
|
85 |
+
vertices = mesh.vertices
|
86 |
+
y_values = vertices[:, 1] # Y-axis values
|
87 |
+
|
88 |
+
# Normalize Y values to range [0, 1] for color mapping
|
89 |
+
y_normalized = (y_values - y_values.min()) / (y_values.max() - y_values.min())
|
90 |
+
|
91 |
+
# Generate colors: Map normalized Y values to RGB gradient (e.g., blue to red)
|
92 |
+
colors = np.zeros((len(vertices), 4)) # RGBA
|
93 |
+
colors[:, 0] = y_normalized # Red channel
|
94 |
+
colors[:, 2] = 1 - y_normalized # Blue channel
|
95 |
+
colors[:, 3] = 1.0 # Alpha channel (fully opaque)
|
96 |
+
|
97 |
+
# Attach colors to mesh vertices
|
98 |
+
mesh.visual.vertex_colors = colors
|
99 |
+
|
100 |
+
# Export to GLB format
|
101 |
+
glb_path = temp_file+".glb"
|
102 |
+
with open(glb_path, "wb") as f:
|
103 |
+
f.write(export_glb(mesh))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
+
return glb_path
|
106 |
+
|
107 |
+
def visualize_mesh(mesh_text):
|
108 |
+
"""
|
109 |
+
Convert the provided 3D mesh text into a visualizable format.
|
110 |
+
This function assumes the input is in OBJ format.
|
111 |
+
"""
|
112 |
+
temp_file = "temp_mesh.obj"
|
113 |
+
with open(temp_file, "w") as f:
|
114 |
+
f.write(mesh_text)
|
115 |
+
return temp_file
|
116 |
+
|
117 |
+
@spaces.GPU(duration=120)
|
118 |
+
def chat_llama3_8b(message: str,
|
119 |
+
history: list,
|
120 |
+
temperature: float,
|
121 |
+
max_new_tokens: int
|
122 |
+
) -> str:
|
123 |
+
"""
|
124 |
+
Generate a streaming response using the llama3-8b model.
|
125 |
+
Args:
|
126 |
+
message (str): The input message.
|
127 |
+
history (list): The conversation history used by ChatInterface.
|
128 |
+
temperature (float): The temperature for generating the response.
|
129 |
+
max_new_tokens (int): The maximum number of new tokens to generate.
|
130 |
+
Returns:
|
131 |
+
str: The generated response.
|
132 |
+
"""
|
133 |
+
conversation = []
|
134 |
+
for user, assistant in history:
|
135 |
+
conversation.extend([{"role": "user", "content": user}, {"role": "assistant", "content": assistant}])
|
136 |
+
conversation.append({"role": "user", "content": message})
|
137 |
+
|
138 |
+
input_ids = tokenizer.apply_chat_template(conversation, return_tensors="pt").to(model.device)
|
139 |
|
140 |
+
streamer = TextIteratorStreamer(tokenizer, timeout=10.0, skip_prompt=True, skip_special_tokens=True)
|
141 |
+
# print(max_new_tokens)
|
142 |
+
max_new_tokens=4096
|
143 |
+
temperature=0.9
|
144 |
+
generate_kwargs = dict(
|
145 |
+
input_ids= input_ids,
|
146 |
+
streamer=streamer,
|
147 |
+
max_new_tokens=max_new_tokens,
|
148 |
+
do_sample=True,
|
149 |
+
temperature=temperature,
|
150 |
+
eos_token_id=terminators,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
)
|
152 |
+
# This will enforce greedy generation (do_sample=False) when the temperature is passed 0, avoiding the crash.
|
153 |
+
if temperature == 0:
|
154 |
+
generate_kwargs['do_sample'] = False
|
155 |
+
|
156 |
+
t = Thread(target=model.generate, kwargs=generate_kwargs)
|
157 |
+
t.start()
|
158 |
+
|
159 |
+
outputs = []
|
160 |
+
for text in streamer:
|
161 |
+
outputs.append(text)
|
162 |
+
#print(outputs)
|
163 |
+
yield "".join(outputs)
|
164 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
|
166 |
+
# Gradio block
|
167 |
+
chatbot=gr.Chatbot(height=450, placeholder=PLACEHOLDER, label='Gradio ChatInterface')
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
+
with gr.Blocks(fill_height=True, css=css) as demo:
|
170 |
+
with gr.Column():
|
171 |
+
gr.Markdown(DESCRIPTION)
|
172 |
+
# gr.DuplicateButton(value="Duplicate Space for private use", elem_id="duplicate-button")
|
173 |
with gr.Row():
|
174 |
+
with gr.Column(scale=3):
|
175 |
+
gr.ChatInterface(
|
176 |
+
fn=chat_llama3_8b,
|
177 |
+
chatbot=chatbot,
|
178 |
+
fill_height=True,
|
179 |
+
additional_inputs_accordion=gr.Accordion(label="⚙️ Parameters", open=False, render=False),
|
180 |
+
additional_inputs=[
|
181 |
+
gr.Slider(minimum=0,
|
182 |
+
maximum=1,
|
183 |
+
step=0.1,
|
184 |
+
value=0.9,
|
185 |
+
label="Temperature",
|
186 |
+
interactive = False,
|
187 |
+
render=False),
|
188 |
+
gr.Slider(minimum=128,
|
189 |
+
maximum=4096,
|
190 |
+
step=1,
|
191 |
+
value=4096,
|
192 |
+
label="Max new tokens",
|
193 |
+
interactive = False,
|
194 |
+
render=False),
|
195 |
+
],
|
196 |
+
examples=[
|
197 |
+
['Create a 3D model of a wooden hammer'],
|
198 |
+
['Create a 3D model of a pyramid in obj format'],
|
199 |
+
['Create a 3D model of a cabinet.'],
|
200 |
+
['Create a low poly 3D model of a coffe cup'],
|
201 |
+
['Create a 3D model of a table.'],
|
202 |
+
["Create a low poly 3D model of a tree."],
|
203 |
+
['Write a python code for sorting.'],
|
204 |
+
['How to setup a human base on Mars? Give short answer.'],
|
205 |
+
['Explain theory of relativity to me like I’m 8 years old.'],
|
206 |
+
['What is 9,000 * 9,000?'],
|
207 |
+
['Create a 3D model of a soda can.'],
|
208 |
+
['Create a 3D model of a sword.'],
|
209 |
+
['Create a 3D model of a wooden barrel'],
|
210 |
+
['Create a 3D model of a chair.']
|
211 |
+
],
|
212 |
+
cache_examples=False,
|
213 |
+
)
|
214 |
+
gr.Markdown(LICENSE)
|
215 |
+
|
216 |
+
with gr.Column(scale=2):
|
217 |
+
output_model = gr.Model3D(
|
218 |
+
label="3D Mesh Visualization",
|
219 |
+
interactive=False,
|
220 |
+
)
|
221 |
+
gr.Markdown("You can copy the generated 3d objects in the left and paste in the textbox below. Put the button and you will see the visualization of the 3D mesh.")
|
222 |
+
|
223 |
+
# Add the text box for 3D mesh input and button
|
224 |
+
mesh_input = gr.Textbox(
|
225 |
+
label="3D Mesh Input",
|
226 |
+
placeholder="Paste your 3D mesh in OBJ format here...",
|
227 |
+
lines=5,
|
228 |
)
|
229 |
+
visualize_button = gr.Button("Visualize 3D Mesh")
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
|
231 |
+
# Link the button to the visualization function
|
232 |
+
visualize_button.click(
|
233 |
+
fn=apply_gradient_color,
|
234 |
+
inputs=[mesh_input],
|
235 |
+
outputs=[output_model]
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
)
|
237 |
+
|
238 |
+
if __name__ == "__main__":
|
239 |
+
demo.launch()
|
240 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
@@ -1,29 +1,4 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
pytorch-lightning
|
6 |
-
omegaconf
|
7 |
-
nerfacc
|
8 |
-
trimesh
|
9 |
-
pyhocon
|
10 |
-
icecream
|
11 |
-
PyMCubes
|
12 |
-
accelerate
|
13 |
-
modelcards
|
14 |
-
einops
|
15 |
-
ftfy
|
16 |
-
piq
|
17 |
-
matplotlib
|
18 |
-
opencv-python
|
19 |
-
imageio
|
20 |
-
imageio-ffmpeg
|
21 |
-
scipy
|
22 |
-
pyransac3d
|
23 |
-
torch_efficient_distloss
|
24 |
-
tensorboard
|
25 |
-
rembg
|
26 |
-
segment_anything
|
27 |
-
gradio==4.31.5
|
28 |
-
kornia
|
29 |
-
fire
|
|
|
1 |
+
accelerate
|
2 |
+
transformers
|
3 |
+
trimesh
|
4 |
+
numpy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|