Ruicheng commited on
Commit
239102c
1 Parent(s): 728b726

use trimesh

Browse files
Files changed (2) hide show
  1. app.py +57 -27
  2. requirements.txt +4 -5
app.py CHANGED
@@ -4,14 +4,18 @@ from pathlib import Path
4
  import uuid
5
  import tempfile
6
  from typing import Union
7
- import spaces
8
  import atexit
 
9
  from concurrent.futures import ThreadPoolExecutor
10
 
11
  import gradio as gr
12
  import cv2
13
  import torch
14
  import numpy as np
 
 
 
 
15
 
16
  from moge.model import MoGeModel
17
  from moge.utils.vis import colorize_depth
@@ -33,47 +37,68 @@ def delete_later(path: Union[str, os.PathLike], delay: int = 300):
33
  thread_pool_executor.submit(_wait_and_delete)
34
  atexit.register(_delete)
35
 
 
36
  @spaces.GPU
37
- def run(image: np.ndarray, remove_edge: bool = True):
 
 
 
 
 
 
 
38
  run_id = str(uuid.uuid4())
39
 
40
  larger_size = max(image.shape[:2])
41
- if larger_size > 1024:
42
- scale = 1024 / larger_size
43
  image = cv2.resize(image, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
44
 
45
- image_tensor = torch.tensor(image, dtype=torch.float32, device=torch.device('cuda')).permute(2, 0, 1) / 255
46
- output = model.infer(image_tensor, resolution_level=9, apply_mask=True)
47
- points, depth, mask = output['points'].cpu().numpy(), output['depth'].cpu().numpy(), output['mask'].cpu().numpy()
 
48
 
49
  if remove_edge:
50
  mask = mask & ~utils3d.numpy.depth_edge(depth, mask=mask, rtol=0.02)
51
  mask = mask & (depth > 0)
52
 
53
- _, faces, indices = utils3d.numpy.image_mesh(width=image.shape[1], height=image.shape[0], mask=mask)
54
- faces = utils3d.numpy.triangulate(faces)
 
 
 
 
 
 
55
 
56
  tempdir = Path(tempfile.gettempdir(), 'moge')
57
  tempdir.mkdir(exist_ok=True)
58
 
59
  output_glb_path = Path(tempdir, f'{run_id}.glb')
60
  output_glb_path.parent.mkdir(exist_ok=True)
61
- tempfile.TemporaryFile()
62
- utils3d.io.write_glb(
63
- output_glb_path,
64
- vertices=points.reshape(-1, 3)[indices] * [-1, -1, 1],
65
- faces=faces,
66
- vertex_colors=image.reshape(-1, 3)[indices] / 255,
67
- )
 
 
 
 
 
 
68
 
69
  output_ply_path = Path(tempdir, f'{run_id}.ply')
70
  output_ply_path.parent.mkdir(exist_ok=True)
71
- utils3d.io.write_ply(
72
- output_ply_path,
73
- vertices=points.reshape(-1, 3)[indices] * [-1, -1, 1],
74
- faces=faces,
75
- vertex_colors=image.reshape(-1, 3)[indices] / 255,
76
- )
77
 
78
  colorized_depth = colorize_depth(depth)
79
 
@@ -84,15 +109,16 @@ def run(image: np.ndarray, remove_edge: bool = True):
84
 
85
 
86
  DESCRIPTION = """
87
- Turns 2D images into 3D point maps with MoGe
88
 
89
  NOTE:
90
- * If the image is too large (> 1024px), it will be resized accordingly.
91
  * The color in the 3D viewer may look dark due to rendering of 3D viewer. You may download the 3D model as .glb or .ply file to view it in other 3D viewers.
92
  """
93
 
94
- if __name__ == '__main__':
95
-
 
96
  gr.Interface(
97
  fn=run,
98
  inputs=[
@@ -109,4 +135,8 @@ if __name__ == '__main__':
109
  clear_btn=None,
110
  allow_flagging="never",
111
  theme=gr.themes.Soft()
112
- ).launch()
 
 
 
 
 
4
  import uuid
5
  import tempfile
6
  from typing import Union
 
7
  import atexit
8
+ import spaces
9
  from concurrent.futures import ThreadPoolExecutor
10
 
11
  import gradio as gr
12
  import cv2
13
  import torch
14
  import numpy as np
15
+ import click
16
+ import trimesh
17
+ import trimesh.visual
18
+ from PIL import Image
19
 
20
  from moge.model import MoGeModel
21
  from moge.utils.vis import colorize_depth
 
37
  thread_pool_executor.submit(_wait_and_delete)
38
  atexit.register(_delete)
39
 
40
+
41
  @spaces.GPU
42
+ def run_with_gpu(image: np.ndarray):
43
+ image_tensor = torch.tensor(image, dtype=torch.float32, device=torch.device('cuda')).permute(2, 0, 1) / 255
44
+ output = model.infer(image_tensor, resolution_level=9, apply_mask=True)
45
+ output = {k: v.cpu().numpy() for k, v in output.items()}
46
+ return output
47
+
48
+
49
+ def run(image: np.ndarray, remove_edge: bool = True, max_size: int = 800):
50
  run_id = str(uuid.uuid4())
51
 
52
  larger_size = max(image.shape[:2])
53
+ if larger_size > max_size:
54
+ scale = max_size / larger_size
55
  image = cv2.resize(image, (0, 0), fx=scale, fy=scale, interpolation=cv2.INTER_AREA)
56
 
57
+ height, width = image.shape[:2]
58
+
59
+ output = run_with_gpu(image)
60
+ points, depth, mask = output['points'], output['depth'], output['mask']
61
 
62
  if remove_edge:
63
  mask = mask & ~utils3d.numpy.depth_edge(depth, mask=mask, rtol=0.02)
64
  mask = mask & (depth > 0)
65
 
66
+ faces, vertices, vertex_colors, vertex_uvs = utils3d.numpy.image_mesh(
67
+ points,
68
+ image.astype(np.float32) / 255,
69
+ utils3d.numpy.image_uv(width=width, height=height),
70
+ mask=mask & ~utils3d.numpy.depth_edge(depth, rtol=0.02, mask=mask),
71
+ tri=True
72
+ )
73
+ vertices, vertex_uvs = vertices * [1, -1, -1], vertex_uvs * [1, -1] + [0, 1]
74
 
75
  tempdir = Path(tempfile.gettempdir(), 'moge')
76
  tempdir.mkdir(exist_ok=True)
77
 
78
  output_glb_path = Path(tempdir, f'{run_id}.glb')
79
  output_glb_path.parent.mkdir(exist_ok=True)
80
+ trimesh.Trimesh(
81
+ vertices=vertices * [-1, 1, -1], # No idea why Gradio 3D Viewer' default camera is flipped
82
+ faces=faces,
83
+ visual = trimesh.visual.texture.TextureVisuals(
84
+ uv=vertex_uvs,
85
+ material=trimesh.visual.material.PBRMaterial(
86
+ baseColorTexture=Image.fromarray(image),
87
+ metallicFactor=0.5,
88
+ roughnessFactor=1.0
89
+ )
90
+ ),
91
+ process=False
92
+ ).export(output_glb_path)
93
 
94
  output_ply_path = Path(tempdir, f'{run_id}.ply')
95
  output_ply_path.parent.mkdir(exist_ok=True)
96
+ trimesh.Trimesh(
97
+ vertices=vertices,
98
+ faces=faces,
99
+ vertex_colors=vertex_colors,
100
+ process=False
101
+ ).export(output_ply_path)
102
 
103
  colorized_depth = colorize_depth(depth)
104
 
 
109
 
110
 
111
  DESCRIPTION = """
112
+ ## Turn a 2D image into a 3D point map with [MoGe](https://wangrc.site/MoGePage/)
113
 
114
  NOTE:
115
+ * The maximum size is set to 800px for efficiency purpose. Oversized images will be downsampled.
116
  * The color in the 3D viewer may look dark due to rendering of 3D viewer. You may download the 3D model as .glb or .ply file to view it in other 3D viewers.
117
  """
118
 
119
+ @click.command()
120
+ @click.option('--share', is_flag=True, help='Whether to run the app in shared mode.')
121
+ def main(share: bool):
122
  gr.Interface(
123
  fn=run,
124
  inputs=[
 
135
  clear_btn=None,
136
  allow_flagging="never",
137
  theme=gr.themes.Soft()
138
+ ).launch(share=share)
139
+
140
+
141
+ if __name__ == '__main__':
142
+ main()
requirements.txt CHANGED
@@ -1,6 +1,5 @@
1
  opencv-python
2
- plyfile
3
- pygltflib
4
- transformers
5
- scikit-learn
6
- matplotlib
 
1
  opencv-python
2
+ scipy
3
+ matplotlib
4
+ huggingface_hub
5
+ trimesh