JunlinHan commited on
Commit
68ae2ac
1 Parent(s): dd3ad5d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -0
app.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import gradio as gr
3
+ import os
4
+ import numpy as np
5
+ import trimesh
6
+ import mcubes
7
+ from torchvision.utils import save_image
8
+ from PIL import Image
9
+ from transformers import AutoModel, AutoConfig
10
+ from rembg import remove, new_session
11
+ from functools import partial
12
+ from kiui.op import recenter
13
+ import kiui
14
+
15
+
16
+ # we load the pre-trained model from HF
17
+ class LRMGeneratorWrapper:
18
+ def __init__(self):
19
+ self.config = AutoConfig.from_pretrained("jadechoghari/custom-llrm", trust_remote_code=True)
20
+ self.model = AutoModel.from_pretrained("jadechoghari/custom-llrm", trust_remote_code=True)
21
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
22
+ self.model.to(self.device)
23
+ self.model.eval()
24
+
25
+ def forward(self, image, camera):
26
+ return self.model(image, camera)
27
+
28
+ model_wrapper = LRMGeneratorWrapper()
29
+
30
+
31
+ def preprocess_image(image, source_size):
32
+ session = new_session("isnet-general-use")
33
+ rembg_remove = partial(remove, session=session)
34
+ image = np.array(image)
35
+ image = rembg_remove(image)
36
+ mask = rembg_remove(image, only_mask=True)
37
+ image = recenter(image, mask, border_ratio=0.20)
38
+ image = torch.tensor(image).permute(2, 0, 1).unsqueeze(0) / 255.0
39
+ if image.shape[1] == 4:
40
+ image = image[:, :3, ...] * image[:, 3:, ...] + (1 - image[:, 3:, ...])
41
+ image = torch.nn.functional.interpolate(image, size=(source_size, source_size), mode='bicubic', align_corners=True)
42
+ image = torch.clamp(image, 0, 1)
43
+ return image
44
+
45
+ def get_normalized_camera_intrinsics(intrinsics: torch.Tensor):
46
+ """
47
+ intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]
48
+ Return batched fx, fy, cx, cy
49
+ """
50
+ fx, fy = intrinsics[:, 0, 0], intrinsics[:, 0, 1]
51
+ cx, cy = intrinsics[:, 1, 0], intrinsics[:, 1, 1]
52
+ width, height = intrinsics[:, 2, 0], intrinsics[:, 2, 1]
53
+ fx, fy = fx / width, fy / height
54
+ cx, cy = cx / width, cy / height
55
+ return fx, fy, cx, cy
56
+
57
+
58
+ def build_camera_principle(RT: torch.Tensor, intrinsics: torch.Tensor):
59
+ """
60
+ RT: (N, 3, 4)
61
+ intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]
62
+ """
63
+ fx, fy, cx, cy = get_normalized_camera_intrinsics(intrinsics)
64
+ return torch.cat([
65
+ RT.reshape(-1, 12),
66
+ fx.unsqueeze(-1), fy.unsqueeze(-1), cx.unsqueeze(-1), cy.unsqueeze(-1),
67
+ ], dim=-1)
68
+
69
+
70
+ def _default_intrinsics():
71
+ fx = fy = 384
72
+ cx = cy = 256
73
+ w = h = 512
74
+ intrinsics = torch.tensor([
75
+ [fx, fy],
76
+ [cx, cy],
77
+ [w, h],
78
+ ], dtype=torch.float32)
79
+ return intrinsics
80
+
81
+ def _default_source_camera(batch_size: int = 1):
82
+ dist_to_center = 1.5
83
+ canonical_camera_extrinsics = torch.tensor([[
84
+ [0, 0, 1, 1],
85
+ [1, 0, 0, 0],
86
+ [0, 1, 0, 0],
87
+ ]], dtype=torch.float32)
88
+ canonical_camera_intrinsics = _default_intrinsics().unsqueeze(0)
89
+ source_camera = build_camera_principle(canonical_camera_extrinsics, canonical_camera_intrinsics)
90
+ return source_camera.repeat(batch_size, 1)
91
+
92
+
93
+ #Ref: https://github.com/jadechoghari/vfusion3d/blob/main/lrm/inferrer.py
94
+ def generate_mesh(image, source_size=512, render_size=384, mesh_size=512, export_mesh=True):
95
+ image = preprocess_image(image, source_size).to(model_wrapper.device)
96
+ source_camera = _default_source_camera(batch_size=1).to(model_wrapper.device)
97
+ # TODO: export video we need render_camera
98
+ # render_camera = _default_render_cameras(batch_size=1).to(model_wrapper.device)
99
+
100
+ with torch.no_grad():
101
+ planes = model_wrapper.forward(image, source_camera)
102
+
103
+ if export_mesh:
104
+ grid_out = model_wrapper.model.synthesizer.forward_grid(planes=planes, grid_size=mesh_size)
105
+ vtx, faces = mcubes.marching_cubes(grid_out['sigma'].float().squeeze(0).squeeze(-1).cpu().numpy(), 1.0)
106
+ vtx = vtx / (mesh_size - 1) * 2 - 1
107
+ vtx_tensor = torch.tensor(vtx, dtype=torch.float32, device=model_wrapper.device).unsqueeze(0)
108
+ vtx_colors = model_wrapper.model.synthesizer.forward_points(planes, vtx_tensor)['rgb'].float().squeeze(0).cpu().numpy()
109
+ vtx_colors = (vtx_colors * 255).astype(np.uint8)
110
+ mesh = trimesh.Trimesh(vertices=vtx, faces=faces, vertex_colors=vtx_colors)
111
+
112
+ mesh_path = "awesome_mesh.obj"
113
+ mesh.export(mesh_path, 'obj')
114
+ return mesh_path
115
+
116
+ # we will convert image to mesh
117
+ def step_1_generate_obj(image):
118
+ mesh_path = generate_mesh(image)
119
+ return mesh_path
120
+
121
+ # we will convert mesh to 3d-image
122
+ def step_2_display_3d_model(mesh_file):
123
+ return mesh_file
124
+
125
+ with gr.Blocks() as demo:
126
+ with gr.Row():
127
+ with gr.Column():
128
+ img_input = gr.Image(type="pil", label="Input Image")
129
+ generate_button = gr.Button("Generate and Visualize 3D Model")
130
+ obj_file_output = gr.File(label="Download .obj File")
131
+
132
+ with gr.Column():
133
+ model_output = gr.Model3D(clear_color=[0.0, 0.0, 0.0, 0.0], label="3D Model Visualization")
134
+
135
+ def generate_and_visualize(image):
136
+ mesh_path = step_1_generate_obj(image)
137
+ return mesh_path, mesh_path
138
+
139
+ generate_button.click(generate_and_visualize, inputs=img_input, outputs=[obj_file_output, model_output])
140
+
141
+ demo.launch()