davidvgilmore commited on
Commit
a831b0c
·
verified ·
1 Parent(s): 47b577f

Upload hy3dgen/texgen/pipelines.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. hy3dgen/texgen/pipelines.py +191 -0
hy3dgen/texgen/pipelines.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Source Model Licensed under the Apache License Version 2.0
2
+ # and Other Licenses of the Third-Party Components therein:
3
+ # The below Model in this distribution may have been modified by THL A29 Limited
4
+ # ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
5
+
6
+ # Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
7
+ # The below software and/or models in this distribution may have been
8
+ # modified by THL A29 Limited ("Tencent Modifications").
9
+ # All Tencent Modifications are Copyright (C) THL A29 Limited.
10
+
11
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
12
+ # except for the third-party components listed below.
13
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
14
+ # in the repsective licenses of these third-party components.
15
+ # Users must comply with all terms and conditions of original licenses of these third-party
16
+ # components and must ensure that the usage of the third party components adheres to
17
+ # all relevant laws and regulations.
18
+
19
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
20
+ # their software and algorithms, including trained model weights, parameters (including
21
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
22
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
23
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
24
+
25
+
26
+ import logging
27
+ import os
28
+
29
+ import numpy as np
30
+ import torch
31
+ from PIL import Image
32
+
33
+ from .differentiable_renderer.mesh_render import MeshRender
34
+ from .utils.dehighlight_utils import Light_Shadow_Remover
35
+ from .utils.multiview_utils import Multiview_Diffusion_Net
36
+ from .utils.uv_warp_utils import mesh_uv_wrap
37
+
38
+ logger = logging.getLogger(__name__)
39
+
40
+
41
+ class Hunyuan3DTexGenConfig:
42
+
43
+ def __init__(self, light_remover_ckpt_path, multiview_ckpt_path):
44
+ self.device = 'cuda'
45
+ self.light_remover_ckpt_path = light_remover_ckpt_path
46
+ self.multiview_ckpt_path = multiview_ckpt_path
47
+
48
+ self.candidate_camera_azims = [0, 90, 180, 270, 0, 180]
49
+ self.candidate_camera_elevs = [0, 0, 0, 0, 90, -90]
50
+ self.candidate_view_weights = [1, 0.1, 0.5, 0.1, 0.05, 0.05]
51
+
52
+ self.render_size = 2048
53
+ self.texture_size = 1024
54
+ self.bake_exp = 4
55
+ self.merge_method = 'fast'
56
+
57
+
58
+ class Hunyuan3DPaintPipeline:
59
+ @classmethod
60
+ def from_pretrained(cls, model_path):
61
+ original_model_path = model_path
62
+ if not os.path.exists(model_path):
63
+ # try local path
64
+ base_dir = os.environ.get('HY3DGEN_MODELS', '~/.cache/hy3dgen')
65
+ model_path = os.path.expanduser(os.path.join(base_dir, model_path))
66
+
67
+ delight_model_path = os.path.join(model_path, 'hunyuan3d-delight-v2-0')
68
+ multiview_model_path = os.path.join(model_path, 'hunyuan3d-paint-v2-0')
69
+
70
+ if not os.path.exists(delight_model_path) or not os.path.exists(multiview_model_path):
71
+ try:
72
+ import huggingface_hub
73
+ # download from huggingface
74
+ model_path = huggingface_hub.snapshot_download(repo_id=original_model_path)
75
+ delight_model_path = os.path.join(model_path, 'hunyuan3d-delight-v2-0')
76
+ multiview_model_path = os.path.join(model_path, 'hunyuan3d-paint-v2-0')
77
+ return cls(Hunyuan3DTexGenConfig(delight_model_path, multiview_model_path))
78
+ except ImportError:
79
+ logger.warning(
80
+ "You need to install HuggingFace Hub to load models from the hub."
81
+ )
82
+ raise RuntimeError(f"Model path {model_path} not found")
83
+ else:
84
+ return cls(Hunyuan3DTexGenConfig(delight_model_path, multiview_model_path))
85
+
86
+ raise FileNotFoundError(f"Model path {original_model_path} not found and we could not find it at huggingface")
87
+
88
+ def __init__(self, config):
89
+ self.config = config
90
+ self.models = {}
91
+ self.render = MeshRender(
92
+ default_resolution=self.config.render_size,
93
+ texture_size=self.config.texture_size)
94
+
95
+ self.load_models()
96
+
97
+ def load_models(self):
98
+ # empty cude cache
99
+ torch.cuda.empty_cache()
100
+ # Load model
101
+ self.models['delight_model'] = Light_Shadow_Remover(self.config)
102
+ self.models['multiview_model'] = Multiview_Diffusion_Net(self.config)
103
+
104
+ def render_normal_multiview(self, camera_elevs, camera_azims, use_abs_coor=True):
105
+ normal_maps = []
106
+ for elev, azim in zip(camera_elevs, camera_azims):
107
+ normal_map = self.render.render_normal(
108
+ elev, azim, use_abs_coor=use_abs_coor, return_type='pl')
109
+ normal_maps.append(normal_map)
110
+
111
+ return normal_maps
112
+
113
+ def render_position_multiview(self, camera_elevs, camera_azims):
114
+ position_maps = []
115
+ for elev, azim in zip(camera_elevs, camera_azims):
116
+ position_map = self.render.render_position(
117
+ elev, azim, return_type='pl')
118
+ position_maps.append(position_map)
119
+
120
+ return position_maps
121
+
122
+ def bake_from_multiview(self, views, camera_elevs,
123
+ camera_azims, view_weights, method='graphcut'):
124
+ project_textures, project_weighted_cos_maps = [], []
125
+ project_boundary_maps = []
126
+ for view, camera_elev, camera_azim, weight in zip(
127
+ views, camera_elevs, camera_azims, view_weights):
128
+ project_texture, project_cos_map, project_boundary_map = self.render.back_project(
129
+ view, camera_elev, camera_azim)
130
+ project_cos_map = weight * (project_cos_map ** self.config.bake_exp)
131
+ project_textures.append(project_texture)
132
+ project_weighted_cos_maps.append(project_cos_map)
133
+ project_boundary_maps.append(project_boundary_map)
134
+
135
+ if method == 'fast':
136
+ texture, ori_trust_map = self.render.fast_bake_texture(
137
+ project_textures, project_weighted_cos_maps)
138
+ else:
139
+ raise f'no method {method}'
140
+ return texture, ori_trust_map > 1E-8
141
+
142
+ def texture_inpaint(self, texture, mask):
143
+
144
+ texture_np = self.render.uv_inpaint(texture, mask)
145
+ texture = torch.tensor(texture_np / 255).float().to(texture.device)
146
+
147
+ return texture
148
+
149
+ @torch.no_grad()
150
+ def __call__(self, mesh, image):
151
+
152
+ if isinstance(image, str):
153
+ image_prompt = Image.open(image)
154
+ else:
155
+ image_prompt = image
156
+
157
+ image_prompt = self.models['delight_model'](image_prompt)
158
+
159
+ mesh = mesh_uv_wrap(mesh)
160
+
161
+ self.render.load_mesh(mesh)
162
+
163
+ selected_camera_elevs, selected_camera_azims, selected_view_weights = \
164
+ self.config.candidate_camera_elevs, self.config.candidate_camera_azims, self.config.candidate_view_weights
165
+
166
+ normal_maps = self.render_normal_multiview(
167
+ selected_camera_elevs, selected_camera_azims, use_abs_coor=True)
168
+ position_maps = self.render_position_multiview(
169
+ selected_camera_elevs, selected_camera_azims)
170
+
171
+ camera_info = [(((azim // 30) + 9) % 12) // {-20: 1, 0: 1, 20: 1, -90: 3, 90: 3}[
172
+ elev] + {-20: 0, 0: 12, 20: 24, -90: 36, 90: 40}[elev] for azim, elev in
173
+ zip(selected_camera_azims, selected_camera_elevs)]
174
+ multiviews = self.models['multiview_model'](image_prompt, normal_maps + position_maps, camera_info)
175
+
176
+ for i in range(len(multiviews)):
177
+ multiviews[i] = multiviews[i].resize(
178
+ (self.config.render_size, self.config.render_size))
179
+
180
+ texture, mask = self.bake_from_multiview(multiviews,
181
+ selected_camera_elevs, selected_camera_azims, selected_view_weights,
182
+ method=self.config.merge_method)
183
+
184
+ mask_np = (mask.squeeze(-1).cpu().numpy() * 255).astype(np.uint8)
185
+
186
+ texture = self.texture_inpaint(texture, mask_np)
187
+
188
+ self.render.set_texture(texture)
189
+ textured_mesh = self.render.save_mesh()
190
+
191
+ return textured_mesh