BladeSzaSza commited on
Commit
05ce3e0
·
1 Parent(s): 27cd716

refactor(3d): Remove duplicated Hunyuan3D code and use dynamic loading

Browse files

- Removes the vendored hunyuan3d_repo, hy3dpaint, and hy3dshape directories to rely on a single source of truth downloaded from Hugging Face.
- Modifies the 3D model generator to dynamically load the Hunyuan3D modules using importlib, making the integration more robust and maintainable.
- Adds a test script for verifying the 3D generation pipeline.

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. hy3dpaint/DifferentiableRenderer/MeshRender.py +0 -1414
  2. hy3dpaint/DifferentiableRenderer/__init__.py +0 -0
  3. hy3dpaint/DifferentiableRenderer/camera_utils.py +0 -107
  4. hy3dpaint/DifferentiableRenderer/compile_mesh_painter.sh +0 -1
  5. hy3dpaint/DifferentiableRenderer/mesh_inpaint_processor.cpp +0 -395
  6. hy3dpaint/DifferentiableRenderer/mesh_utils.py +0 -284
  7. hy3dpaint/LICENSE +0 -81
  8. hy3dpaint/README.md +0 -96
  9. hy3dpaint/cfgs/hunyuan-paint-pbr.yaml +0 -52
  10. hy3dpaint/convert_utils.py +0 -140
  11. hy3dpaint/demo.py +0 -35
  12. hy3dpaint/hunyuanpaintpbr/__init__.py +0 -39
  13. hy3dpaint/hunyuanpaintpbr/pipeline.py +0 -736
  14. hy3dpaint/hunyuanpaintpbr/unet/attn_processor.py +0 -839
  15. hy3dpaint/hunyuanpaintpbr/unet/model.py +0 -622
  16. hy3dpaint/hunyuanpaintpbr/unet/modules.py +0 -1102
  17. hy3dpaint/packages/custom_rasterizer/custom_rasterizer/__init__.py +0 -4
  18. hy3dpaint/packages/custom_rasterizer/custom_rasterizer/render.py +0 -32
  19. hy3dpaint/packages/custom_rasterizer/setup.py +0 -40
  20. hy3dpaint/src/__init__.py +0 -13
  21. hy3dpaint/src/utils/__init__.py +0 -13
  22. hy3dpaint/src/utils/train_util.py +0 -40
  23. hy3dpaint/textureGenPipeline.py +0 -192
  24. hy3dpaint/train.py +0 -401
  25. hy3dpaint/train_examples/001/render_cond/mesh.ply +0 -0
  26. hy3dpaint/train_examples/001/render_cond/transforms.json +0 -838
  27. hy3dpaint/train_examples/001/render_tex/transforms.json +0 -226
  28. hy3dpaint/train_examples/examples.json +0 -3
  29. hy3dpaint/utils/__init__.py +0 -13
  30. hy3dpaint/utils/image_super_utils.py +0 -41
  31. hy3dpaint/utils/multiview_utils.py +0 -128
  32. hy3dpaint/utils/pipeline_utils.py +0 -135
  33. hy3dpaint/utils/simplify_mesh_utils.py +0 -37
  34. hy3dpaint/utils/torchvision_fix.py +0 -111
  35. hy3dpaint/utils/uvwrap_utils.py +0 -32
  36. hy3dshape/.gitignore +0 -169
  37. hy3dshape/LICENSE +0 -81
  38. hy3dshape/NOTICE +0 -214
  39. hy3dshape/README-zh.md +0 -47
  40. hy3dshape/README.md +0 -54
  41. hy3dshape/configs/hunyuan3ddit-full-params-finetuning-flowmatching-dinog518-bf16-lr1e5-512.yaml +0 -174
  42. hy3dshape/configs/hunyuan3ddit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-512.yaml +0 -173
  43. hy3dshape/configs/hunyuandit-finetuning-flowmatching-dinog518-bf16-lr1e5-4096.yaml +0 -180
  44. hy3dshape/configs/hunyuandit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-4096.yaml +0 -180
  45. hy3dshape/configs/hunyuandit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-512.yaml +0 -180
  46. hy3dshape/hy3dshape/__init__.py +0 -17
  47. hy3dshape/hy3dshape/models/__init__.py +0 -28
  48. hy3dshape/hy3dshape/models/autoencoders/__init__.py +0 -20
  49. hy3dshape/hy3dshape/models/autoencoders/attention_blocks.py +0 -716
  50. hy3dshape/hy3dshape/models/autoencoders/attention_processors.py +0 -96
hy3dpaint/DifferentiableRenderer/MeshRender.py DELETED
@@ -1,1414 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import cv2
16
- import torch
17
- import trimesh
18
- import numpy as np
19
- from PIL import Image
20
- import torch.nn.functional as F
21
- from typing import Union, Optional, Tuple, List, Any, Callable
22
- from dataclasses import dataclass
23
- from enum import Enum
24
- from .camera_utils import (
25
- transform_pos,
26
- get_mv_matrix,
27
- get_orthographic_projection_matrix,
28
- get_perspective_projection_matrix,
29
- )
30
-
31
- try:
32
- from .mesh_utils import load_mesh, save_mesh
33
- except:
34
- print("Bpy IO CAN NOT BE Imported!!!")
35
-
36
- try:
37
- from .mesh_inpaint_processor import meshVerticeInpaint # , meshVerticeColor
38
- except:
39
- print("InPaint Function CAN NOT BE Imported!!!")
40
-
41
-
42
- class RenderMode(Enum):
43
- """Rendering mode enumeration."""
44
- NORMAL = "normal"
45
- POSITION = "position"
46
- ALPHA = "alpha"
47
- UV_POS = "uvpos"
48
-
49
-
50
- class ReturnType(Enum):
51
- """Return type enumeration."""
52
- TENSOR = "th"
53
- NUMPY = "np"
54
- PIL = "pl"
55
-
56
-
57
- class TextureType(Enum):
58
- """Texture type enumeration."""
59
- DIFFUSE = "diffuse"
60
- METALLIC_ROUGHNESS = "mr"
61
- NORMAL = "normal"
62
-
63
-
64
- @dataclass
65
- class RenderConfig:
66
- """Unified rendering configuration."""
67
- elev: float = 0
68
- azim: float = 0
69
- camera_distance: Optional[float] = None
70
- center: Optional[List[float]] = None
71
- resolution: Optional[Union[int, Tuple[int, int]]] = None
72
- bg_color: List[float] = None
73
- return_type: str = "th"
74
-
75
- def __post_init__(self):
76
- if self.bg_color is None:
77
- self.bg_color = [1, 1, 1]
78
-
79
-
80
- @dataclass
81
- class ViewState:
82
- """Camera view state for rendering pipeline."""
83
- proj_mat: torch.Tensor
84
- mv_mat: torch.Tensor
85
- pos_camera: torch.Tensor
86
- pos_clip: torch.Tensor
87
- resolution: Tuple[int, int]
88
-
89
-
90
- def stride_from_shape(shape):
91
- """
92
- Calculate stride values from a given shape for multi-dimensional indexing.
93
-
94
- Args:
95
- shape: Tuple or list representing tensor dimensions
96
-
97
- Returns:
98
- List of stride values for each dimension
99
- """
100
- stride = [1]
101
- for x in reversed(shape[1:]):
102
- stride.append(stride[-1] * x)
103
- return list(reversed(stride))
104
-
105
-
106
- def scatter_add_nd_with_count(input, count, indices, values, weights=None):
107
- """
108
- Perform scatter-add operation on N-dimensional tensors with counting.
109
-
110
- Args:
111
- input: Input tensor [..., C] with D dimensions + C channels
112
- count: Count tensor [..., 1] with D dimensions
113
- indices: Index tensor [N, D] of type long
114
- values: Value tensor [N, C] to scatter
115
- weights: Optional weight tensor [N, C], defaults to ones if None
116
-
117
- Returns:
118
- Tuple of (updated_input, updated_count) tensors
119
- """
120
- # input: [..., C], D dimension + C channel
121
- # count: [..., 1], D dimension
122
- # indices: [N, D], long
123
- # values: [N, C]
124
-
125
- D = indices.shape[-1]
126
- C = input.shape[-1]
127
- size = input.shape[:-1]
128
- stride = stride_from_shape(size)
129
-
130
- assert len(size) == D
131
-
132
- input = input.view(-1, C) # [HW, C]
133
- count = count.view(-1, 1)
134
-
135
- flatten_indices = (indices * torch.tensor(stride, dtype=torch.long, device=indices.device)).sum(-1) # [N]
136
-
137
- if weights is None:
138
- weights = torch.ones_like(values[..., :1])
139
-
140
- input.scatter_add_(0, flatten_indices.unsqueeze(1).repeat(1, C), values)
141
- count.scatter_add_(0, flatten_indices.unsqueeze(1), weights)
142
-
143
- return input.view(*size, C), count.view(*size, 1)
144
-
145
-
146
- def linear_grid_put_2d(H, W, coords, values, return_count=False):
147
- """
148
- Place values on a 2D grid using bilinear interpolation.
149
-
150
- Args:
151
- H: Grid height
152
- W: Grid width
153
- coords: Coordinate tensor [N, 2] with values in range [0, 1]
154
- values: Value tensor [N, C] to place on grid
155
- return_count: Whether to return count information
156
-
157
- Returns:
158
- 2D grid tensor [H, W, C] with interpolated values, optionally with count tensor
159
- """
160
- # coords: [N, 2], float in [0, 1]
161
- # values: [N, C]
162
-
163
- C = values.shape[-1]
164
-
165
- indices = coords * torch.tensor([H - 1, W - 1], dtype=torch.float32, device=coords.device)
166
- indices_00 = indices.floor().long() # [N, 2]
167
- indices_00[:, 0].clamp_(0, H - 2)
168
- indices_00[:, 1].clamp_(0, W - 2)
169
- indices_01 = indices_00 + torch.tensor([0, 1], dtype=torch.long, device=indices.device)
170
- indices_10 = indices_00 + torch.tensor([1, 0], dtype=torch.long, device=indices.device)
171
- indices_11 = indices_00 + torch.tensor([1, 1], dtype=torch.long, device=indices.device)
172
-
173
- h = indices[..., 0] - indices_00[..., 0].float()
174
- w = indices[..., 1] - indices_00[..., 1].float()
175
- w_00 = (1 - h) * (1 - w)
176
- w_01 = (1 - h) * w
177
- w_10 = h * (1 - w)
178
- w_11 = h * w
179
-
180
- result = torch.zeros(H, W, C, device=values.device, dtype=values.dtype) # [H, W, C]
181
- count = torch.zeros(H, W, 1, device=values.device, dtype=values.dtype) # [H, W, 1]
182
- weights = torch.ones_like(values[..., :1]) # [N, 1]
183
-
184
- result, count = scatter_add_nd_with_count(
185
- result, count, indices_00, values * w_00.unsqueeze(1), weights * w_00.unsqueeze(1)
186
- )
187
- result, count = scatter_add_nd_with_count(
188
- result, count, indices_01, values * w_01.unsqueeze(1), weights * w_01.unsqueeze(1)
189
- )
190
- result, count = scatter_add_nd_with_count(
191
- result, count, indices_10, values * w_10.unsqueeze(1), weights * w_10.unsqueeze(1)
192
- )
193
- result, count = scatter_add_nd_with_count(
194
- result, count, indices_11, values * w_11.unsqueeze(1), weights * w_11.unsqueeze(1)
195
- )
196
-
197
- if return_count:
198
- return result, count
199
-
200
- mask = count.squeeze(-1) > 0
201
- result[mask] = result[mask] / count[mask].repeat(1, C)
202
-
203
- return result
204
-
205
-
206
- def mipmap_linear_grid_put_2d(H, W, coords, values, min_resolution=128, return_count=False):
207
- """
208
- Place values on 2D grid using mipmap-based multiresolution interpolation to fill holes.
209
-
210
- Args:
211
- H: Grid height
212
- W: Grid width
213
- coords: Coordinate tensor [N, 2] with values in range [0, 1]
214
- values: Value tensor [N, C] to place on grid
215
- min_resolution: Minimum resolution for mipmap levels
216
- return_count: Whether to return count information
217
-
218
- Returns:
219
- 2D grid tensor [H, W, C] with filled values, optionally with count tensor
220
- """
221
- # coords: [N, 2], float in [0, 1]
222
- # values: [N, C]
223
-
224
- C = values.shape[-1]
225
-
226
- result = torch.zeros(H, W, C, device=values.device, dtype=values.dtype) # [H, W, C]
227
- count = torch.zeros(H, W, 1, device=values.device, dtype=values.dtype) # [H, W, 1]
228
-
229
- cur_H, cur_W = H, W
230
-
231
- while min(cur_H, cur_W) > min_resolution:
232
-
233
- # try to fill the holes
234
- mask = count.squeeze(-1) == 0
235
- if not mask.any():
236
- break
237
-
238
- cur_result, cur_count = linear_grid_put_2d(cur_H, cur_W, coords, values, return_count=True)
239
- result[mask] = (
240
- result[mask]
241
- + F.interpolate(
242
- cur_result.permute(2, 0, 1).unsqueeze(0).contiguous(), (H, W), mode="bilinear", align_corners=False
243
- )
244
- .squeeze(0)
245
- .permute(1, 2, 0)
246
- .contiguous()[mask]
247
- )
248
- count[mask] = (
249
- count[mask]
250
- + F.interpolate(cur_count.view(1, 1, cur_H, cur_W), (H, W), mode="bilinear", align_corners=False).view(
251
- H, W, 1
252
- )[mask]
253
- )
254
- cur_H //= 2
255
- cur_W //= 2
256
-
257
- if return_count:
258
- return result, count
259
-
260
- mask = count.squeeze(-1) > 0
261
- result[mask] = result[mask] / count[mask].repeat(1, C)
262
-
263
- return result
264
-
265
-
266
- # ============ Core utility functions for reducing duplication ============
267
-
268
- def _normalize_image_input(image: Union[np.ndarray, torch.Tensor, Image.Image]) -> Union[np.ndarray, torch.Tensor]:
269
- """Normalize image input to consistent format."""
270
- if isinstance(image, Image.Image):
271
- return np.array(image) / 255.0
272
- elif isinstance(image, torch.Tensor):
273
- return image.cpu().numpy() if image.is_cuda else image
274
- return image
275
-
276
-
277
- def _convert_texture_format(tex: Union[np.ndarray, torch.Tensor, Image.Image],
278
- texture_size: Tuple[int, int], device: str, force_set: bool = False) -> torch.Tensor:
279
- """Unified texture format conversion logic."""
280
- if not force_set:
281
- if isinstance(tex, np.ndarray):
282
- tex = Image.fromarray((tex * 255).astype(np.uint8))
283
- elif isinstance(tex, torch.Tensor):
284
- tex_np = tex.cpu().numpy()
285
- tex = Image.fromarray((tex_np * 255).astype(np.uint8))
286
-
287
- tex = tex.resize(texture_size).convert("RGB")
288
- tex = np.array(tex) / 255.0
289
- return torch.from_numpy(tex).to(device).float()
290
- else:
291
- if isinstance(tex, np.ndarray):
292
- tex = torch.from_numpy(tex)
293
- return tex.to(device).float()
294
-
295
-
296
- def _format_output(image: torch.Tensor, return_type: str) -> Union[torch.Tensor, np.ndarray, Image.Image]:
297
- """Convert output to requested format."""
298
- if return_type == ReturnType.NUMPY.value:
299
- return image.cpu().numpy()
300
- elif return_type == ReturnType.PIL.value:
301
- img_np = image.cpu().numpy() * 255
302
- return Image.fromarray(img_np.astype(np.uint8))
303
- return image
304
-
305
-
306
- def _ensure_resolution_format(resolution: Optional[Union[int, Tuple[int, int]]],
307
- default: Tuple[int, int]) -> Tuple[int, int]:
308
- """Ensure resolution is in (height, width) format."""
309
- if resolution is None:
310
- return default
311
- if isinstance(resolution, (int, float)):
312
- return (int(resolution), int(resolution))
313
- return tuple(resolution)
314
-
315
-
316
- def _apply_background_mask(content: torch.Tensor, visible_mask: torch.Tensor,
317
- bg_color: List[float], device: str) -> torch.Tensor:
318
- """Apply background color to masked regions."""
319
- bg_tensor = torch.tensor(bg_color, dtype=torch.float32, device=device)
320
- return content * visible_mask + bg_tensor * (1 - visible_mask)
321
-
322
-
323
- class MeshRender:
324
- def __init__(
325
- self,
326
- camera_distance=1.45,
327
- camera_type="orth",
328
- default_resolution=1024,
329
- texture_size=1024,
330
- use_antialias=True,
331
- max_mip_level=None,
332
- filter_mode="linear-mipmap-linear",
333
- bake_mode="back_sample",
334
- raster_mode="cr",
335
- shader_type="face",
336
- use_opengl=False,
337
- device="cuda",
338
- ):
339
- """
340
- Initialize mesh renderer with configurable parameters.
341
-
342
- Args:
343
- camera_distance: Distance from camera to object center
344
- camera_type: Type of camera projection ("orth" or "perspective")
345
- default_resolution: Default rendering resolution
346
- texture_size: Size of texture maps
347
- use_antialias: Whether to use antialiasing
348
- max_mip_level: Maximum mipmap level for texture filtering
349
- filter_mode: Texture filtering mode
350
- bake_mode: Texture baking method ("back_sample", "linear", "mip-map")
351
- raster_mode: Rasterization backend ("cr" for custom rasterizer)
352
- shader_type: Shading type ("face" or "vertex")
353
- use_opengl: Whether to use OpenGL backend (deprecated)
354
- device: Computing device ("cuda" or "cpu")
355
- """
356
-
357
- self.device = device
358
-
359
- self.set_default_render_resolution(default_resolution)
360
- self.set_default_texture_resolution(texture_size)
361
-
362
- self.camera_distance = camera_distance
363
- self.use_antialias = use_antialias
364
- self.max_mip_level = max_mip_level
365
- self.filter_mode = filter_mode
366
- self.bake_angle_thres = 75
367
- self.set_boundary_unreliable_scale(2)
368
- self.bake_mode = bake_mode
369
- self.shader_type = shader_type
370
-
371
- self.raster_mode = raster_mode
372
- if self.raster_mode == "cr":
373
- import custom_rasterizer as cr
374
-
375
- self.raster = cr
376
- else:
377
- raise f"No raster named {self.raster_mode}"
378
-
379
- if camera_type == "orth":
380
- self.set_orth_scale(1.2)
381
- elif camera_type == "perspective":
382
- self.camera_proj_mat = get_perspective_projection_matrix(
383
- 49.13, self.default_resolution[1] / self.default_resolution[0], 0.01, 100.0
384
- )
385
- else:
386
- raise f"No camera type {camera_type}"
387
-
388
- # Removed multiprocessing components for single-threaded version
389
-
390
- def _create_view_state(self, config: RenderConfig) -> ViewState:
391
- """Create unified view state for rendering pipeline."""
392
- proj = self.camera_proj_mat
393
- r_mv = get_mv_matrix(
394
- elev=config.elev,
395
- azim=config.azim,
396
- camera_distance=self.camera_distance if config.camera_distance is None else config.camera_distance,
397
- center=config.center,
398
- )
399
-
400
- pos_camera = transform_pos(r_mv, self.vtx_pos, keepdim=True)
401
- pos_clip = transform_pos(proj, pos_camera)
402
- resolution = _ensure_resolution_format(config.resolution, self.default_resolution)
403
-
404
- return ViewState(proj, r_mv, pos_camera, pos_clip, resolution)
405
-
406
- def _compute_face_normals(self, triangles: torch.Tensor) -> torch.Tensor:
407
- """Compute face normals from triangle vertices."""
408
- return F.normalize(
409
- torch.cross(
410
- triangles[:, 1, :] - triangles[:, 0, :],
411
- triangles[:, 2, :] - triangles[:, 0, :],
412
- dim=-1,
413
- ),
414
- dim=-1,
415
- )
416
-
417
- def _get_normals_for_shading(self, view_state: ViewState, use_abs_coor: bool = False) -> torch.Tensor:
418
- """Get normals based on shader type and coordinate system."""
419
- if use_abs_coor:
420
- mesh_triangles = self.vtx_pos[self.pos_idx[:, :3], :]
421
- else:
422
- pos_camera = view_state.pos_camera[:, :3] / view_state.pos_camera[:, 3:4]
423
- mesh_triangles = pos_camera[self.pos_idx[:, :3], :]
424
-
425
- face_normals = self._compute_face_normals(mesh_triangles)
426
-
427
- # Common rasterization
428
- rast_out, _ = self.raster_rasterize(view_state.pos_clip, self.pos_idx, resolution=view_state.resolution)
429
-
430
- if self.shader_type == "vertex":
431
- vertex_normals = trimesh.geometry.mean_vertex_normals(
432
- vertex_count=self.vtx_pos.shape[0],
433
- faces=self.pos_idx.cpu(),
434
- face_normals=face_normals.cpu(),
435
- )
436
- vertex_normals = torch.from_numpy(vertex_normals).float().to(self.device).contiguous()
437
- normal, _ = self.raster_interpolate(vertex_normals[None, ...], rast_out, self.pos_idx)
438
-
439
- elif self.shader_type == "face":
440
- tri_ids = rast_out[..., 3]
441
- tri_ids_mask = tri_ids > 0
442
- tri_ids = ((tri_ids - 1) * tri_ids_mask).long()
443
- normal = torch.zeros(rast_out.shape[0], rast_out.shape[1], rast_out.shape[2], 3).to(rast_out)
444
- normal.reshape(-1, 3)[tri_ids_mask.view(-1)] = face_normals.reshape(-1, 3)[tri_ids[tri_ids_mask].view(-1)]
445
-
446
- return normal, rast_out
447
-
448
- def _unified_render_pipeline(self, config: RenderConfig, mode: RenderMode, **kwargs) -> torch.Tensor:
449
- """Unified rendering pipeline for all render modes."""
450
- view_state = self._create_view_state(config)
451
-
452
- if mode == RenderMode.ALPHA:
453
- rast_out, _ = self.raster_rasterize(view_state.pos_clip, self.pos_idx, resolution=view_state.resolution)
454
- return rast_out[..., -1:].long()
455
-
456
- elif mode == RenderMode.UV_POS:
457
- return self.uv_feature_map(self.vtx_pos * 0.5 + 0.5)
458
-
459
- elif mode == RenderMode.NORMAL:
460
- use_abs_coor = kwargs.get('use_abs_coor', False)
461
- normalize_rgb = kwargs.get('normalize_rgb', True)
462
-
463
- normal, rast_out = self._get_normals_for_shading(view_state, use_abs_coor)
464
- visible_mask = torch.clamp(rast_out[..., -1:], 0, 1)
465
-
466
- result = _apply_background_mask(normal, visible_mask, config.bg_color, self.device)
467
-
468
- if normalize_rgb:
469
- result = (result + 1) * 0.5
470
-
471
- if self.use_antialias:
472
- result = self.raster_antialias(result, rast_out, view_state.pos_clip, self.pos_idx)
473
-
474
- return result[0, ...]
475
-
476
- elif mode == RenderMode.POSITION:
477
- rast_out, _ = self.raster_rasterize(view_state.pos_clip, self.pos_idx, resolution=view_state.resolution)
478
-
479
- tex_position = 0.5 - self.vtx_pos[:, :3] / self.scale_factor
480
- tex_position = tex_position.contiguous()
481
-
482
- position, _ = self.raster_interpolate(tex_position[None, ...], rast_out, self.pos_idx)
483
- visible_mask = torch.clamp(rast_out[..., -1:], 0, 1)
484
-
485
- result = _apply_background_mask(position, visible_mask, config.bg_color, self.device)
486
-
487
- if self.use_antialias:
488
- result = self.raster_antialias(result, rast_out, view_state.pos_clip, self.pos_idx)
489
-
490
- return result[0, ...]
491
-
492
- def set_orth_scale(self, ortho_scale):
493
- """
494
- Set the orthographic projection scale and update camera projection matrix.
495
-
496
- Args:
497
- ortho_scale: Scale factor for orthographic projection
498
- """
499
- self.ortho_scale = ortho_scale
500
- self.camera_proj_mat = get_orthographic_projection_matrix(
501
- left=-self.ortho_scale * 0.5,
502
- right=self.ortho_scale * 0.5,
503
- bottom=-self.ortho_scale * 0.5,
504
- top=self.ortho_scale * 0.5,
505
- near=0.1,
506
- far=100,
507
- )
508
-
509
- def raster_rasterize(self, pos, tri, resolution, ranges=None, grad_db=True):
510
- """
511
- Rasterize triangular mesh using the configured rasterization backend.
512
-
513
- Args:
514
- pos: Vertex positions in clip space
515
- tri: Triangle indices
516
- resolution: Rendering resolution [height, width]
517
- ranges: Optional rendering ranges (unused in current implementation)
518
- grad_db: Whether to compute gradients (unused in current implementation)
519
-
520
- Returns:
521
- Tuple of (rasterization_output, gradient_info)
522
- """
523
-
524
- if self.raster_mode == "cr":
525
- rast_out_db = None
526
- if pos.dim() == 2:
527
- pos = pos.unsqueeze(0)
528
-
529
- # 确保pos是float32类型
530
- if pos.dtype == torch.float64:
531
- pos = pos.to(torch.float32)
532
-
533
- # 确保tri是int32类型
534
- if tri.dtype == torch.int64:
535
- tri = tri.to(torch.int32)
536
-
537
- findices, barycentric = self.raster.rasterize(pos, tri, resolution)
538
- rast_out = torch.cat((barycentric, findices.unsqueeze(-1)), dim=-1)
539
- rast_out = rast_out.unsqueeze(0)
540
- else:
541
- raise f"No raster named {self.raster_mode}"
542
-
543
- return rast_out, rast_out_db
544
-
545
- def raster_interpolate(self, uv, rast_out, uv_idx):
546
- """
547
- Interpolate texture coordinates or vertex attributes across rasterized triangles.
548
-
549
- Args:
550
- uv: UV coordinates or vertex attributes to interpolate
551
- rast_out: Rasterization output containing barycentric coordinates
552
- uv_idx: UV or vertex indices for triangles
553
-
554
- Returns:
555
- Tuple of (interpolated_values, gradient_info)
556
- """
557
-
558
- if self.raster_mode == "cr":
559
- textd = None
560
- barycentric = rast_out[0, ..., :-1]
561
- findices = rast_out[0, ..., -1]
562
- if uv.dim() == 2:
563
- uv = uv.unsqueeze(0)
564
- textc = self.raster.interpolate(uv, findices, barycentric, uv_idx)
565
- else:
566
- raise f"No raster named {self.raster_mode}"
567
-
568
- return textc, textd
569
-
570
- def raster_antialias(self, color, rast, pos, tri, topology_hash=None, pos_gradient_boost=1.0):
571
- """
572
- Apply antialiasing to rendered colors (currently returns input unchanged).
573
-
574
- Args:
575
- color: Input color values
576
- rast: Rasterization output
577
- pos: Vertex positions
578
- tri: Triangle indices
579
- topology_hash: Optional topology hash for optimization
580
- pos_gradient_boost: Gradient boosting factor
581
-
582
- Returns:
583
- Antialiased color values
584
- """
585
-
586
- if self.raster_mode == "cr":
587
- color = color
588
- else:
589
- raise f"No raster named {self.raster_mode}"
590
-
591
- return color
592
-
593
- def set_boundary_unreliable_scale(self, scale):
594
- """
595
- Set the kernel size for boundary unreliable region detection during texture baking.
596
-
597
- Args:
598
- scale: Scale factor relative to 512 resolution baseline
599
- """
600
- self.bake_unreliable_kernel_size = int(
601
- (scale / 512) * max(self.default_resolution[0], self.default_resolution[1])
602
- )
603
-
604
- def load_mesh(
605
- self,
606
- mesh,
607
- scale_factor=1.15,
608
- auto_center=True,
609
- ):
610
- """
611
- Load mesh from file and set up rendering data structures.
612
-
613
- Args:
614
- mesh: Path to mesh file or mesh object
615
- scale_factor: Scaling factor for mesh normalization
616
- auto_center: Whether to automatically center the mesh
617
- """
618
- vtx_pos, pos_idx, vtx_uv, uv_idx, texture_data = load_mesh(mesh)
619
- self.set_mesh(
620
- vtx_pos, pos_idx, vtx_uv=vtx_uv, uv_idx=uv_idx, scale_factor=scale_factor, auto_center=auto_center
621
- )
622
- if texture_data is not None:
623
- self.set_texture(texture_data)
624
-
625
- def save_mesh(self, mesh_path, downsample=False):
626
- """
627
- Save current mesh with textures to file.
628
-
629
- Args:
630
- mesh_path: Output file path
631
- downsample: Whether to downsample textures by half
632
- """
633
-
634
- vtx_pos, pos_idx, vtx_uv, uv_idx = self.get_mesh(normalize=False)
635
- texture_data = self.get_texture()
636
- texture_metallic, texture_roughness = self.get_texture_mr()
637
- texture_normal = self.get_texture_normal()
638
- if downsample:
639
- texture_data = cv2.resize(texture_data, (texture_data.shape[1] // 2, texture_data.shape[0] // 2))
640
- if texture_metallic is not None:
641
- texture_metallic = cv2.resize(
642
- texture_metallic, (texture_metallic.shape[1] // 2, texture_metallic.shape[0] // 2)
643
- )
644
- if texture_roughness is not None:
645
- texture_roughness = cv2.resize(
646
- texture_roughness, (texture_roughness.shape[1] // 2, texture_roughness.shape[0] // 2)
647
- )
648
- if texture_normal is not None:
649
- texture_normal = cv2.resize(
650
- texture_normal, (texture_normal.shape[1] // 2, texture_normal.shape[0] // 2)
651
- )
652
-
653
- save_mesh(
654
- mesh_path,
655
- vtx_pos,
656
- pos_idx,
657
- vtx_uv,
658
- uv_idx,
659
- texture_data,
660
- metallic=texture_metallic,
661
- roughness=texture_roughness,
662
- normal=texture_normal,
663
- )
664
-
665
- def set_mesh(self, vtx_pos, pos_idx, vtx_uv=None, uv_idx=None, scale_factor=1.15, auto_center=True):
666
- """
667
- Set mesh geometry data and perform coordinate transformations.
668
-
669
- Args:
670
- vtx_pos: Vertex positions [N, 3]
671
- pos_idx: Triangle vertex indices [F, 3]
672
- vtx_uv: UV coordinates [N, 2], optional
673
- uv_idx: Triangle UV indices [F, 3], optional
674
- scale_factor: Scaling factor for mesh normalization
675
- auto_center: Whether to automatically center and scale the mesh
676
- """
677
- self.vtx_pos = torch.from_numpy(vtx_pos).to(self.device)
678
- self.pos_idx = torch.from_numpy(pos_idx).to(self.device)
679
-
680
- # 确保顶点位置是float32类型
681
- if self.vtx_pos.dtype == torch.float64:
682
- self.vtx_pos = self.vtx_pos.to(torch.float32)
683
-
684
- # 确保索引类型为int32
685
- if self.pos_idx.dtype == torch.int64:
686
- self.pos_idx = self.pos_idx.to(torch.int32)
687
-
688
- if (vtx_uv is not None) and (uv_idx is not None):
689
- self.vtx_uv = torch.from_numpy(vtx_uv).to(self.device)
690
- self.uv_idx = torch.from_numpy(uv_idx).to(self.device)
691
-
692
- # 确保UV坐标是float32类型
693
- if self.vtx_uv.dtype == torch.float64:
694
- self.vtx_uv = self.vtx_uv.to(torch.float32)
695
-
696
- # 确保UV索引类型为int32
697
- if self.uv_idx.dtype == torch.int64:
698
- self.uv_idx = self.uv_idx.to(torch.int32)
699
- else:
700
- self.vtx_uv = None
701
- self.uv_idx = None
702
-
703
- self.vtx_pos[:, [0, 1]] = -self.vtx_pos[:, [0, 1]]
704
- self.vtx_pos[:, [1, 2]] = self.vtx_pos[:, [2, 1]]
705
- if (vtx_uv is not None) and (uv_idx is not None):
706
- self.vtx_uv[:, 1] = 1.0 - self.vtx_uv[:, 1]
707
- pass
708
-
709
- if auto_center:
710
- max_bb = (self.vtx_pos - 0).max(0)[0]
711
- min_bb = (self.vtx_pos - 0).min(0)[0]
712
- center = (max_bb + min_bb) / 2
713
- scale = torch.norm(self.vtx_pos - center, dim=1).max() * 2.0
714
- self.vtx_pos = (self.vtx_pos - center) * (scale_factor / float(scale))
715
- self.scale_factor = scale_factor
716
- self.mesh_normalize_scale_factor = scale_factor / float(scale)
717
- self.mesh_normalize_scale_center = center.unsqueeze(0).cpu().numpy()
718
- else:
719
- self.scale_factor = 1.0
720
- self.mesh_normalize_scale_factor = 1.0
721
- self.mesh_normalize_scale_center = np.array([[0, 0, 0]])
722
-
723
- if uv_idx is not None:
724
- self.extract_textiles()
725
-
726
- def _set_texture_unified(self, tex: Union[np.ndarray, torch.Tensor, Image.Image],
727
- texture_type: TextureType, force_set: bool = False):
728
- """Unified texture setting method."""
729
- converted_tex = _convert_texture_format(tex, self.texture_size, self.device, force_set)
730
-
731
- if texture_type == TextureType.DIFFUSE:
732
- self.tex = converted_tex
733
- elif texture_type == TextureType.METALLIC_ROUGHNESS:
734
- self.tex_mr = converted_tex
735
- elif texture_type == TextureType.NORMAL:
736
- self.tex_normalMap = converted_tex
737
-
738
- def set_texture(self, tex, force_set=False):
739
- """Set the main diffuse texture for the mesh."""
740
- self._set_texture_unified(tex, TextureType.DIFFUSE, force_set)
741
-
742
- def set_texture_mr(self, mr, force_set=False):
743
- """Set metallic-roughness texture for PBR rendering."""
744
- self._set_texture_unified(mr, TextureType.METALLIC_ROUGHNESS, force_set)
745
-
746
- def set_texture_normal(self, normal, force_set=False):
747
- """Set normal map texture for surface detail."""
748
- self._set_texture_unified(normal, TextureType.NORMAL, force_set)
749
-
750
- def set_default_render_resolution(self, default_resolution):
751
- """
752
- Set the default resolution for rendering operations.
753
-
754
- Args:
755
- default_resolution: Resolution as int (square) or tuple (height, width)
756
- """
757
- if isinstance(default_resolution, int):
758
- default_resolution = (default_resolution, default_resolution)
759
- self.default_resolution = default_resolution
760
-
761
- def set_default_texture_resolution(self, texture_size):
762
- """
763
- Set the default texture resolution for UV mapping operations.
764
-
765
- Args:
766
- texture_size: Texture size as int (square) or tuple (height, width)
767
- """
768
- if isinstance(texture_size, int):
769
- texture_size = (texture_size, texture_size)
770
- self.texture_size = texture_size
771
-
772
- def get_face_num(self):
773
- """
774
- Get the number of triangular faces in the mesh.
775
-
776
- Returns:
777
- Number of faces as integer
778
- """
779
- return self.pos_idx.shape[0]
780
-
781
- def get_vertex_num(self):
782
- """
783
- Get the number of vertices in the mesh.
784
-
785
- Returns:
786
- Number of vertices as integer
787
- """
788
- return self.vtx_pos.shape[0]
789
-
790
- def get_face_areas(self, from_one_index=False):
791
- """
792
- Calculate the area of each triangular face in the mesh.
793
-
794
- Args:
795
- from_one_index: If True, insert zero at beginning for 1-indexed face IDs
796
-
797
- Returns:
798
- Numpy array of face areas
799
- """
800
- v0 = self.vtx_pos[self.pos_idx[:, 0], :]
801
- v1 = self.vtx_pos[self.pos_idx[:, 1], :]
802
- v2 = self.vtx_pos[self.pos_idx[:, 2], :]
803
-
804
- # 计算两个边向量
805
- edge1 = v1 - v0
806
- edge2 = v2 - v0
807
-
808
- # 计算叉积的模长的一半即为面积
809
- areas = torch.norm(torch.cross(edge1, edge2, dim=-1), dim=-1) * 0.5
810
-
811
- areas = areas.cpu().numpy()
812
-
813
- if from_one_index:
814
- # 在数组前面插入一个0,��为三角片索引是从1开始的
815
- areas = np.insert(areas, 0, 0)
816
-
817
- return areas
818
-
819
- def get_mesh(self, normalize=True):
820
- """
821
- Get mesh geometry with optional coordinate denormalization.
822
-
823
- Args:
824
- normalize: Whether to keep normalized coordinates (True) or restore original scale (False)
825
-
826
- Returns:
827
- Tuple of (vertex_positions, face_indices, uv_coordinates, uv_indices)
828
- """
829
- vtx_pos = self.vtx_pos.cpu().numpy()
830
- pos_idx = self.pos_idx.cpu().numpy()
831
- vtx_uv = self.vtx_uv.cpu().numpy()
832
- uv_idx = self.uv_idx.cpu().numpy()
833
-
834
- # 坐标变换的逆变换
835
- if not normalize:
836
- vtx_pos = vtx_pos / self.mesh_normalize_scale_factor
837
- vtx_pos = vtx_pos + self.mesh_normalize_scale_center
838
- vtx_pos[:, [1, 2]] = vtx_pos[:, [2, 1]]
839
- vtx_pos[:, [0, 1]] = -vtx_pos[:, [0, 1]]
840
-
841
- vtx_uv[:, 1] = 1.0 - vtx_uv[:, 1]
842
- return vtx_pos, pos_idx, vtx_uv, uv_idx
843
-
844
- def get_texture(self):
845
- """
846
- Get the current diffuse texture as numpy array.
847
-
848
- Returns:
849
- Texture as numpy array in range [0, 1]
850
- """
851
- return self.tex.cpu().numpy()
852
-
853
- def get_texture_mr(self):
854
- """
855
- Get metallic and roughness textures as separate channels.
856
-
857
- Returns:
858
- Tuple of (metallic_texture, roughness_texture) as numpy arrays, or (None, None) if not set
859
- """
860
- metallic, roughness = None, None
861
- if hasattr(self, "tex_mr"):
862
- mr = self.tex_mr.cpu().numpy()
863
- metallic = np.repeat(mr[:, :, 0:1], repeats=3, axis=2)
864
- roughness = np.repeat(mr[:, :, 1:2], repeats=3, axis=2)
865
- return metallic, roughness
866
-
867
- def get_texture_normal(self):
868
- """
869
- Get the normal map texture as numpy array.
870
-
871
- Returns:
872
- Normal map as numpy array, or None if not set
873
- """
874
- normal = None
875
- if hasattr(self, "tex_normalMap"):
876
- normal = self.tex_normalMap.cpu().numpy()
877
- return normal
878
-
879
- def to(self, device):
880
- """
881
- Move all tensor attributes to the specified device.
882
-
883
- Args:
884
- device: Target device ("cuda", "cpu", etc.)
885
- """
886
- self.device = device
887
-
888
- for attr_name in dir(self):
889
- attr_value = getattr(self, attr_name)
890
- if isinstance(attr_value, torch.Tensor):
891
- setattr(self, attr_name, attr_value.to(self.device))
892
-
893
- def color_rgb_to_srgb(self, image):
894
- """
895
- Convert RGB color values to sRGB color space using gamma correction.
896
-
897
- Args:
898
- image: Input image as PIL Image, numpy array, or torch tensor
899
-
900
- Returns:
901
- sRGB corrected image in same format as input
902
- """
903
- if isinstance(image, Image.Image):
904
- image_rgb = torch.tesnor(np.array(image) / 255.0).float().to(self.device)
905
- elif isinstance(image, np.ndarray):
906
- image_rgb = torch.tensor(image).float()
907
- else:
908
- image_rgb = image.to(self.device)
909
-
910
- image_srgb = torch.where(
911
- image_rgb <= 0.0031308, 12.92 * image_rgb, 1.055 * torch.pow(image_rgb, 1 / 2.4) - 0.055
912
- )
913
-
914
- if isinstance(image, Image.Image):
915
- image_srgb = Image.fromarray((image_srgb.cpu().numpy() * 255).astype(np.uint8))
916
- elif isinstance(image, np.ndarray):
917
- image_srgb = image_srgb.cpu().numpy()
918
- else:
919
- image_srgb = image_srgb.to(image.device)
920
-
921
- return image_srgb
922
-
923
- def extract_textiles(self):
924
- """
925
- Extract texture-space position and normal information by rasterizing
926
- the mesh in UV coordinate space. Creates texture-space geometry mappings.
927
- """
928
-
929
- vnum = self.vtx_uv.shape[0]
930
- vtx_uv = torch.cat(
931
- (self.vtx_uv, torch.zeros_like(self.vtx_uv[:, 0:1]), torch.ones_like(self.vtx_uv[:, 0:1])), axis=1
932
- )
933
- vtx_uv = vtx_uv.view(1, vnum, 4) * 2 - 1
934
-
935
- rast_out, rast_out_db = self.raster_rasterize(vtx_uv, self.uv_idx, resolution=self.texture_size)
936
- position, _ = self.raster_interpolate(self.vtx_pos, rast_out, self.pos_idx)
937
-
938
- v0 = self.vtx_pos[self.pos_idx[:, 0], :]
939
- v1 = self.vtx_pos[self.pos_idx[:, 1], :]
940
- v2 = self.vtx_pos[self.pos_idx[:, 2], :]
941
- face_normals = F.normalize(torch.cross(v1 - v0, v2 - v0, dim=-1), dim=-1)
942
- vertex_normals = trimesh.geometry.mean_vertex_normals(
943
- vertex_count=self.vtx_pos.shape[0],
944
- faces=self.pos_idx.cpu(),
945
- face_normals=face_normals.cpu(),
946
- )
947
- vertex_normals = torch.from_numpy(vertex_normals).to(self.vtx_pos).contiguous()
948
- position_normal, _ = self.raster_interpolate(vertex_normals[None, ...], rast_out, self.pos_idx)
949
- visible_mask = torch.clamp(rast_out[..., -1:], 0, 1)[0, ..., 0]
950
- position = position[0]
951
- position_normal = position_normal[0]
952
- tri_ids = rast_out[0, ..., 3]
953
- tri_ids_mask = tri_ids > 0
954
- tri_ids = ((tri_ids - 1) * tri_ids_mask).long()
955
- position_normal.reshape(-1, 3)[tri_ids_mask.view(-1)] = face_normals.reshape(-1, 3)[
956
- tri_ids[tri_ids_mask].view(-1)
957
- ]
958
-
959
- row = torch.arange(position.shape[0]).to(visible_mask.device)
960
- col = torch.arange(position.shape[1]).to(visible_mask.device)
961
- grid_i, grid_j = torch.meshgrid(row, col, indexing="ij")
962
-
963
- mask = visible_mask.reshape(-1) > 0
964
- position = position.reshape(-1, 3)[mask]
965
- position_normal = position_normal.reshape(-1, 3)[mask]
966
- position = torch.cat((position, torch.ones_like(position[:, :1])), axis=-1)
967
- grid = torch.stack((grid_i, grid_j), -1).reshape(-1, 2)[mask]
968
-
969
- texture_indices = (
970
- torch.ones(self.texture_size[0], self.texture_size[1], device=self.device, dtype=torch.long) * -1
971
- )
972
- texture_indices.view(-1)[grid[:, 0] * self.texture_size[1] + grid[:, 1]] = torch.arange(grid.shape[0]).to(
973
- device=self.device, dtype=torch.long
974
- )
975
-
976
- self.tex_position = position
977
- self.tex_normal = position_normal
978
- self.tex_grid = grid
979
- self.texture_indices = texture_indices
980
-
981
- def render_normal(self, elev, azim, camera_distance=None, center=None, resolution=None,
982
- bg_color=[1, 1, 1], use_abs_coor=False, normalize_rgb=True, return_type="th"):
983
- """Render surface normals of the mesh from specified viewpoint."""
984
- config = RenderConfig(elev, azim, camera_distance, center, resolution, bg_color, return_type)
985
- image = self._unified_render_pipeline(config, RenderMode.NORMAL,
986
- use_abs_coor=use_abs_coor, normalize_rgb=normalize_rgb)
987
- return _format_output(image, return_type)
988
-
989
- def convert_normal_map(self, image):
990
- """
991
- Convert normal map from standard format to renderer's coordinate system.
992
- Applies coordinate transformations for proper normal interpretation.
993
-
994
- Args:
995
- image: Input normal map as PIL Image or numpy array
996
-
997
- Returns:
998
- Converted normal map as PIL Image
999
- """
1000
- # blue is front, red is left, green is top
1001
- if isinstance(image, Image.Image):
1002
- image = np.array(image)
1003
- mask = (image == [255, 255, 255]).all(axis=-1)
1004
-
1005
- image = (image / 255.0) * 2.0 - 1.0
1006
-
1007
- image[..., [1]] = -image[..., [1]]
1008
- image[..., [1, 2]] = image[..., [2, 1]]
1009
- image[..., [0]] = -image[..., [0]]
1010
-
1011
- image = (image + 1.0) * 0.5
1012
-
1013
- image = (image * 255).astype(np.uint8)
1014
- image[mask] = [127, 127, 255]
1015
-
1016
- return Image.fromarray(image)
1017
-
1018
- def render_position(self, elev, azim, camera_distance=None, center=None, resolution=None,
1019
- bg_color=[1, 1, 1], return_type="th"):
1020
- """Render world-space positions of visible mesh surface points."""
1021
- config = RenderConfig(elev, azim, camera_distance, center, resolution, bg_color, return_type)
1022
- image = self._unified_render_pipeline(config, RenderMode.POSITION)
1023
-
1024
- if return_type == ReturnType.PIL.value:
1025
- image = image.squeeze(-1).cpu().numpy() * 255
1026
- return Image.fromarray(image.astype(np.uint8))
1027
- return _format_output(image, return_type)
1028
-
1029
- def render_uvpos(self, return_type="th"):
1030
- """Render vertex positions mapped to UV texture space."""
1031
- config = RenderConfig(return_type=return_type)
1032
- image = self._unified_render_pipeline(config, RenderMode.UV_POS)
1033
- return _format_output(image, return_type)
1034
-
1035
- def render_alpha(self, elev, azim, camera_distance=None, center=None, resolution=None, return_type="th"):
1036
- """Render binary alpha mask indicating visible mesh regions."""
1037
- config = RenderConfig(elev, azim, camera_distance, center, resolution, return_type=return_type)
1038
- image = self._unified_render_pipeline(config, RenderMode.ALPHA)
1039
-
1040
- if return_type == ReturnType.PIL.value:
1041
- raise Exception("PIL format not supported for alpha rendering")
1042
- return _format_output(image, return_type)
1043
-
1044
- def uv_feature_map(self, vert_feat, bg=None):
1045
- """
1046
- Map per-vertex features to UV texture space using mesh topology.
1047
-
1048
- Args:
1049
- vert_feat: Per-vertex feature tensor [N, C]
1050
- bg: Background value for unmapped regions (optional)
1051
-
1052
- Returns:
1053
- Feature map in UV texture space [H, W, C]
1054
- """
1055
- vtx_uv = self.vtx_uv * 2 - 1.0
1056
- vtx_uv = torch.cat([vtx_uv, torch.zeros_like(self.vtx_uv)], dim=1).unsqueeze(0)
1057
- vtx_uv[..., -1] = 1
1058
- uv_idx = self.uv_idx
1059
- rast_out, rast_out_db = self.raster_rasterize(vtx_uv, uv_idx, resolution=self.texture_size)
1060
- feat_map, _ = self.raster_interpolate(vert_feat[None, ...], rast_out, uv_idx)
1061
- feat_map = feat_map[0, ...]
1062
- if bg is not None:
1063
- visible_mask = torch.clamp(rast_out[..., -1:], 0, 1)[0, ...]
1064
- feat_map[visible_mask == 0] = bg
1065
- return feat_map
1066
-
1067
- def render_sketch_from_geometry(self, normal_image, depth_image):
1068
- """
1069
- Generate sketch-style edge image from rendered normal and depth maps.
1070
-
1071
- Args:
1072
- normal_image: Rendered normal map tensor
1073
- depth_image: Rendered depth map tensor
1074
-
1075
- Returns:
1076
- Binary edge sketch image as tensor
1077
- """
1078
- normal_image_np = normal_image.cpu().numpy()
1079
- depth_image_np = depth_image.cpu().numpy()
1080
-
1081
- normal_image_np = (normal_image_np * 255).astype(np.uint8)
1082
- depth_image_np = (depth_image_np * 255).astype(np.uint8)
1083
- normal_image_np = cv2.cvtColor(normal_image_np, cv2.COLOR_RGB2GRAY)
1084
-
1085
- normal_edges = cv2.Canny(normal_image_np, 80, 150)
1086
- depth_edges = cv2.Canny(depth_image_np, 30, 80)
1087
-
1088
- combined_edges = np.maximum(normal_edges, depth_edges)
1089
-
1090
- sketch_image = torch.from_numpy(combined_edges).to(normal_image.device).float() / 255.0
1091
- sketch_image = sketch_image.unsqueeze(-1)
1092
-
1093
- return sketch_image
1094
-
1095
- def render_sketch_from_depth(self, depth_image):
1096
- """
1097
- Generate sketch-style edge image from depth map using edge detection.
1098
-
1099
- Args:
1100
- depth_image: Input depth map tensor
1101
-
1102
- Returns:
1103
- Binary edge sketch image as tensor
1104
- """
1105
- depth_image_np = depth_image.cpu().numpy()
1106
- depth_image_np = (depth_image_np * 255).astype(np.uint8)
1107
- depth_edges = cv2.Canny(depth_image_np, 30, 80)
1108
- combined_edges = depth_edges
1109
- sketch_image = torch.from_numpy(combined_edges).to(depth_image.device).float() / 255.0
1110
- sketch_image = sketch_image.unsqueeze(-1)
1111
- return sketch_image
1112
-
1113
- def back_project(self, image, elev, azim, camera_distance=None, center=None, method=None):
1114
- """
1115
- Back-project a rendered image onto the mesh's UV texture space.
1116
- Handles visibility, viewing angle, and boundary detection for texture baking.
1117
-
1118
- Args:
1119
- image: Input image to back-project (PIL Image, numpy array, or tensor)
1120
- elev: Camera elevation angle in degrees used for rendering
1121
- azim: Camera azimuth angle in degrees used for rendering
1122
- camera_distance: Camera distance (uses default if None)
1123
- center: Camera focus center (uses origin if None)
1124
- method: Back-projection method ("linear", "mip-map", "back_sample", uses default if None)
1125
-
1126
- Returns:
1127
- Tuple of (texture, cosine_map, boundary_map) tensors in UV space
1128
- """
1129
-
1130
- if isinstance(image, Image.Image):
1131
- image = torch.tensor(np.array(image) / 255.0)
1132
- elif isinstance(image, np.ndarray):
1133
- image = torch.tensor(image)
1134
- if image.dim() == 2:
1135
- image = image.unsqueeze(-1)
1136
- image = image.float().to(self.device)
1137
- resolution = image.shape[:2]
1138
- channel = image.shape[-1]
1139
- texture = torch.zeros(self.texture_size + (channel,)).to(self.device)
1140
- cos_map = torch.zeros(self.texture_size + (1,)).to(self.device)
1141
-
1142
- proj = self.camera_proj_mat
1143
- r_mv = get_mv_matrix(
1144
- elev=elev,
1145
- azim=azim,
1146
- camera_distance=self.camera_distance if camera_distance is None else camera_distance,
1147
- center=center,
1148
- )
1149
- pos_camera = transform_pos(r_mv, self.vtx_pos, keepdim=True)
1150
- pos_clip = transform_pos(proj, pos_camera)
1151
- pos_camera = pos_camera[:, :3] / pos_camera[:, 3:4]
1152
-
1153
- v0 = pos_camera[self.pos_idx[:, 0], :]
1154
- v1 = pos_camera[self.pos_idx[:, 1], :]
1155
- v2 = pos_camera[self.pos_idx[:, 2], :]
1156
- face_normals = F.normalize(torch.cross(v1 - v0, v2 - v0, dim=-1), dim=-1)
1157
-
1158
- tex_depth = pos_camera[:, 2].reshape(1, -1, 1).contiguous()
1159
- rast_out, rast_out_db = self.raster_rasterize(pos_clip, self.pos_idx, resolution=resolution)
1160
- visible_mask = torch.clamp(rast_out[..., -1:], 0, 1)[0, ...]
1161
-
1162
- if self.shader_type == "vertex":
1163
- vertex_normals = trimesh.geometry.mean_vertex_normals(
1164
- vertex_count=self.vtx_pos.shape[0],
1165
- faces=self.pos_idx.cpu(),
1166
- face_normals=face_normals.cpu(),
1167
- )
1168
- vertex_normals = torch.from_numpy(vertex_normals).float().to(self.device).contiguous()
1169
- normal, _ = self.raster_interpolate(vertex_normals[None, ...], rast_out, self.pos_idx)
1170
- elif self.shader_type == "face":
1171
- tri_ids = rast_out[..., 3]
1172
- tri_ids_mask = tri_ids > 0
1173
- tri_ids = ((tri_ids - 1) * tri_ids_mask).long()
1174
- normal = torch.zeros(rast_out.shape[0], rast_out.shape[1], rast_out.shape[2], 3).to(rast_out)
1175
- normal.reshape(-1, 3)[tri_ids_mask.view(-1)] = face_normals.reshape(-1, 3)[tri_ids[tri_ids_mask].view(-1)]
1176
-
1177
- normal = normal[0, ...]
1178
- uv, _ = self.raster_interpolate(self.vtx_uv[None, ...], rast_out, self.uv_idx)
1179
- depth, _ = self.raster_interpolate(tex_depth, rast_out, self.pos_idx)
1180
- depth = depth[0, ...]
1181
-
1182
- depth_max, depth_min = depth[visible_mask > 0].max(), depth[visible_mask > 0].min()
1183
- depth_normalized = (depth - depth_min) / (depth_max - depth_min)
1184
- depth_image = depth_normalized * visible_mask # Mask out background.
1185
-
1186
- sketch_image = self.render_sketch_from_depth(depth_image)
1187
-
1188
- lookat = torch.tensor([[0, 0, -1]], device=self.device)
1189
- cos_image = torch.nn.functional.cosine_similarity(lookat, normal.view(-1, 3))
1190
- cos_image = cos_image.view(normal.shape[0], normal.shape[1], 1)
1191
-
1192
- cos_thres = np.cos(self.bake_angle_thres / 180 * np.pi)
1193
- cos_image[cos_image < cos_thres] = 0
1194
-
1195
- # shrink
1196
- if self.bake_unreliable_kernel_size > 0:
1197
- kernel_size = self.bake_unreliable_kernel_size * 2 + 1
1198
- kernel = torch.ones((1, 1, kernel_size, kernel_size), dtype=torch.float32).to(sketch_image.device)
1199
-
1200
- visible_mask = visible_mask.permute(2, 0, 1).unsqueeze(0).float()
1201
- visible_mask = F.conv2d(1.0 - visible_mask, kernel, padding=kernel_size // 2)
1202
- visible_mask = 1.0 - (visible_mask > 0).float() # 二值化
1203
- visible_mask = visible_mask.squeeze(0).permute(1, 2, 0)
1204
-
1205
- sketch_image = sketch_image.permute(2, 0, 1).unsqueeze(0)
1206
- sketch_image = F.conv2d(sketch_image, kernel, padding=kernel_size // 2)
1207
- sketch_image = (sketch_image > 0).float() # 二值化
1208
- sketch_image = sketch_image.squeeze(0).permute(1, 2, 0)
1209
- visible_mask = visible_mask * (sketch_image < 0.5)
1210
-
1211
- cos_image[visible_mask == 0] = 0
1212
-
1213
- method = self.bake_mode if method is None else method
1214
-
1215
- if method == "linear":
1216
- proj_mask = (visible_mask != 0).view(-1)
1217
- uv = uv.squeeze(0).contiguous().view(-1, 2)[proj_mask]
1218
- image = image.squeeze(0).contiguous().view(-1, channel)[proj_mask]
1219
- cos_image = cos_image.contiguous().view(-1, 1)[proj_mask]
1220
- sketch_image = sketch_image.contiguous().view(-1, 1)[proj_mask]
1221
-
1222
- texture = linear_grid_put_2d(self.texture_size[1], self.texture_size[0], uv[..., [1, 0]], image)
1223
- cos_map = linear_grid_put_2d(self.texture_size[1], self.texture_size[0], uv[..., [1, 0]], cos_image)
1224
- boundary_map = linear_grid_put_2d(self.texture_size[1], self.texture_size[0], uv[..., [1, 0]], sketch_image)
1225
- elif method == "mip-map":
1226
- proj_mask = (visible_mask != 0).view(-1)
1227
- uv = uv.squeeze(0).contiguous().view(-1, 2)[proj_mask]
1228
- image = image.squeeze(0).contiguous().view(-1, channel)[proj_mask]
1229
- cos_image = cos_image.contiguous().view(-1, 1)[proj_mask]
1230
-
1231
- texture = mipmap_linear_grid_put_2d(
1232
- self.texture_size[1], self.texture_size[0], uv[..., [1, 0]], image, min_resolution=128
1233
- )
1234
- cos_map = mipmap_linear_grid_put_2d(
1235
- self.texture_size[1], self.texture_size[0], uv[..., [1, 0]], cos_image, min_resolution=256
1236
- )
1237
-
1238
- if self.vtx_map is not None:
1239
- vertex_normals = vertex_normals[self.vtx_map, :]
1240
- normal_map = self.uv_feature_map(vertex_normals)
1241
- cos_map_uv = torch.nn.functional.cosine_similarity(lookat, normal_map.view(-1, 3)) # .abs()
1242
- cos_map_uv = cos_map_uv.view(1, 1, normal_map.shape[0], normal_map.shape[1])
1243
- cos_map_uv = torch.nn.functional.max_pool2d(cos_map_uv, kernel_size=3, stride=1, padding=1)
1244
- cos_map_uv = cos_map_uv.reshape(self.texture_size[0], self.texture_size[1], 1)
1245
- cos_map_uv[cos_map_uv < cos_thres] = 0
1246
- # cos_map = torch.min(cos_map, cos_map_uv)
1247
- cos_map[cos_map_uv < cos_thres] = 0
1248
- elif method == "back_sample":
1249
-
1250
- img_proj = torch.from_numpy(
1251
- np.array(((proj[0, 0], 0, 0, 0), (0, proj[1, 1], 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)))
1252
- ).to(self.tex_position)
1253
- w2c = torch.from_numpy(r_mv).to(self.tex_position)
1254
- v_proj = self.tex_position @ w2c.T @ img_proj
1255
- inner_mask = (v_proj[:, 0] <= 1.0) & (v_proj[:, 0] >= -1.0) & (v_proj[:, 1] <= 1.0) & (v_proj[:, 1] >= -1.0)
1256
- inner_valid_idx = torch.where(inner_mask)[0].long()
1257
- img_x = torch.clamp(
1258
- ((v_proj[:, 0].clamp(-1, 1) * 0.5 + 0.5) * (resolution[0])).long(), 0, resolution[0] - 1
1259
- )
1260
- img_y = torch.clamp(
1261
- ((v_proj[:, 1].clamp(-1, 1) * 0.5 + 0.5) * (resolution[1])).long(), 0, resolution[1] - 1
1262
- )
1263
-
1264
- indices = img_y * resolution[0] + img_x
1265
- sampled_z = depth.reshape(-1)[indices]
1266
- sampled_m = visible_mask.reshape(-1)[indices]
1267
- v_z = v_proj[:, 2]
1268
-
1269
- sampled_w = cos_image.reshape(-1)[indices]
1270
- depth_thres = 3e-3
1271
-
1272
- # valid_idx = torch.where((torch.abs(v_z - sampled_z) < depth_thres) * (sampled_m*sampled_w>0))[0]
1273
- valid_idx = torch.where((torch.abs(v_z - sampled_z) < depth_thres) & (sampled_m * sampled_w > 0))[0]
1274
-
1275
- intersection_mask = torch.isin(valid_idx, inner_valid_idx)
1276
- valid_idx = valid_idx[intersection_mask].to(inner_valid_idx)
1277
-
1278
- indices = indices[valid_idx]
1279
- sampled_b = sketch_image.reshape(-1)[indices]
1280
- sampled_w = sampled_w[valid_idx]
1281
-
1282
- # bilinear sampling rgb
1283
- wx = ((v_proj[:, 0] * 0.5 + 0.5) * resolution[0] - img_x)[valid_idx].reshape(-1, 1)
1284
- wy = ((v_proj[:, 1] * 0.5 + 0.5) * resolution[1] - img_y)[valid_idx].reshape(-1, 1)
1285
- img_x = img_x[valid_idx]
1286
- img_y = img_y[valid_idx]
1287
- img_x_r = torch.clamp(img_x + 1, 0, resolution[0] - 1)
1288
- img_y_r = torch.clamp(img_y + 1, 0, resolution[1] - 1)
1289
- indices_lr = img_y * resolution[0] + img_x_r
1290
- indices_rl = img_y_r * resolution[0] + img_x
1291
- indices_rr = img_y_r * resolution[0] + img_x_r
1292
- rgb = image.reshape(-1, channel)
1293
- sampled_rgb = (rgb[indices] * (1 - wx) + rgb[indices_lr] * wx) * (1 - wy) + (
1294
- rgb[indices_rl] * (1 - wx) + rgb[indices_rr] * wx
1295
- ) * wy
1296
-
1297
- # return sampled_rgb, sampled_w, sampled_b, valid_idx
1298
- texture = torch.zeros(self.texture_size[0], self.texture_size[1], channel, device=self.device).reshape(
1299
- -1, channel
1300
- )
1301
- cos_map = torch.zeros(self.texture_size[0], self.texture_size[1], 1, device=self.device).reshape(-1)
1302
- boundary_map = torch.zeros(self.texture_size[0], self.texture_size[1], 1, device=self.device).reshape(-1)
1303
-
1304
- valid_tex_indices = self.tex_grid[valid_idx, 0] * self.texture_size[1] + self.tex_grid[valid_idx, 1]
1305
- texture[valid_tex_indices, :] = sampled_rgb
1306
- cos_map[valid_tex_indices] = sampled_w
1307
- boundary_map[valid_tex_indices] = sampled_b
1308
-
1309
- texture = texture.view(self.texture_size[0], self.texture_size[1], channel)
1310
- cos_map = cos_map.view(self.texture_size[0], self.texture_size[1], 1)
1311
- # texture = torch.clamp(texture,0,1)
1312
-
1313
- else:
1314
- raise f"No bake mode {method}"
1315
- return texture, cos_map, boundary_map
1316
-
1317
- def bake_texture(self, colors, elevs, azims, camera_distance=None, center=None, exp=6, weights=None):
1318
- """
1319
- Bake multiple view images into a single UV texture using weighted blending.
1320
-
1321
- Args:
1322
- colors: List of input images (tensors, numpy arrays, or PIL Images)
1323
- elevs: List of elevation angles for each view
1324
- azims: List of azimuth angles for each view
1325
- camera_distance: Camera distance (uses default if None)
1326
- center: Camera focus center (uses origin if None)
1327
- exp: Exponent for cosine weighting (higher values favor front-facing views)
1328
- weights: Optional per-view weights (defaults to 1.0 for all views)
1329
-
1330
- Returns:
1331
- Tuple of (merged_texture, trust_map) tensors in UV space
1332
- """
1333
- if isinstance(colors, torch.Tensor):
1334
- colors = [colors[i, ...].float().permute(1, 2, 0) for i in range(colors.shape[0])]
1335
- else:
1336
- for i in range(len(colors)):
1337
- if isinstance(colors[i], Image.Image):
1338
- colors[i] = torch.tensor(np.array(colors[i]) / 255.0, device=self.device).float()
1339
- if weights is None:
1340
- weights = [1.0 for _ in range(len(colors))]
1341
- textures = []
1342
- cos_maps = []
1343
- for color, elev, azim, weight in zip(colors, elevs, azims, weights):
1344
- texture, cos_map, _ = self.back_project(color, elev, azim, camera_distance, center)
1345
- cos_map = weight * (cos_map**exp)
1346
- textures.append(texture)
1347
- cos_maps.append(cos_map)
1348
-
1349
- texture_merge, trust_map_merge = self.fast_bake_texture(textures, cos_maps)
1350
- return texture_merge, trust_map_merge
1351
-
1352
- @torch.no_grad()
1353
- def fast_bake_texture(self, textures, cos_maps):
1354
- """
1355
- Efficiently merge multiple textures using cosine-weighted blending.
1356
- Optimizes by skipping views that don't contribute new information.
1357
-
1358
- Args:
1359
- textures: List of texture tensors to merge
1360
- cos_maps: List of corresponding cosine weight maps
1361
-
1362
- Returns:
1363
- Tuple of (merged_texture, valid_mask) tensors
1364
- """
1365
-
1366
- channel = textures[0].shape[-1]
1367
- texture_merge = torch.zeros(self.texture_size + (channel,)).to(self.device)
1368
- trust_map_merge = torch.zeros(self.texture_size + (1,)).to(self.device)
1369
- for texture, cos_map in zip(textures, cos_maps):
1370
- view_sum = (cos_map > 0).sum()
1371
- painted_sum = ((cos_map > 0) * (trust_map_merge > 0)).sum()
1372
- if painted_sum / view_sum > 0.99:
1373
- continue
1374
- texture_merge += texture * cos_map
1375
- trust_map_merge += cos_map
1376
- texture_merge = texture_merge / torch.clamp(trust_map_merge, min=1e-8)
1377
-
1378
- return texture_merge, trust_map_merge > 1e-8
1379
-
1380
- @torch.no_grad()
1381
- def uv_inpaint(self, texture, mask, vertex_inpaint=True, method="NS", return_float=False):
1382
- """
1383
- Inpaint missing regions in UV texture using mesh-aware and traditional methods.
1384
-
1385
- Args:
1386
- texture: Input texture as tensor, numpy array, or PIL Image
1387
- mask: Binary mask indicating regions to inpaint (1 = keep, 0 = inpaint)
1388
- vertex_inpaint: Whether to use mesh vertex connectivity for inpainting
1389
- method: Inpainting method ("NS" for Navier-Stokes)
1390
- return_float: Whether to return float values (False returns uint8)
1391
-
1392
- Returns:
1393
- Inpainted texture as numpy array
1394
- """
1395
-
1396
- if isinstance(texture, torch.Tensor):
1397
- texture_np = texture.cpu().numpy()
1398
- elif isinstance(texture, np.ndarray):
1399
- texture_np = texture
1400
- elif isinstance(texture, Image.Image):
1401
- texture_np = np.array(texture) / 255.0
1402
-
1403
- if isinstance(mask, torch.Tensor):
1404
- mask = (mask.squeeze(-1).cpu().numpy() * 255).astype(np.uint8)
1405
-
1406
- if vertex_inpaint:
1407
- vtx_pos, pos_idx, vtx_uv, uv_idx = self.get_mesh()
1408
- texture_np, mask = meshVerticeInpaint(texture_np, mask, vtx_pos, vtx_uv, pos_idx, uv_idx)
1409
-
1410
- if method == "NS":
1411
- texture_np = cv2.inpaint((texture_np * 255).astype(np.uint8), 255 - mask, 3, cv2.INPAINT_NS)
1412
- assert return_float == False
1413
-
1414
- return texture_np
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/DifferentiableRenderer/__init__.py DELETED
File without changes
hy3dpaint/DifferentiableRenderer/camera_utils.py DELETED
@@ -1,107 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import math
16
-
17
- import numpy as np
18
- import torch
19
-
20
-
21
- def transform_pos(mtx, pos, keepdim=False):
22
- t_mtx = torch.from_numpy(mtx).to(pos.device) if isinstance(mtx, np.ndarray) else mtx
23
- if pos.shape[-1] == 3:
24
- posw = torch.cat([pos, torch.ones([pos.shape[0], 1]).to(pos.device)], axis=1)
25
- else:
26
- posw = pos
27
-
28
- if keepdim:
29
- return torch.matmul(posw, t_mtx.t())[...]
30
- else:
31
- return torch.matmul(posw, t_mtx.t())[None, ...]
32
-
33
-
34
- def get_mv_matrix(elev, azim, camera_distance, center=None):
35
- elev = -elev
36
- azim += 90
37
-
38
- elev_rad = math.radians(elev)
39
- azim_rad = math.radians(azim)
40
-
41
- camera_position = np.array(
42
- [
43
- camera_distance * math.cos(elev_rad) * math.cos(azim_rad),
44
- camera_distance * math.cos(elev_rad) * math.sin(azim_rad),
45
- camera_distance * math.sin(elev_rad),
46
- ]
47
- )
48
-
49
- if center is None:
50
- center = np.array([0, 0, 0])
51
- else:
52
- center = np.array(center)
53
-
54
- lookat = center - camera_position
55
- lookat = lookat / np.linalg.norm(lookat)
56
-
57
- up = np.array([0, 0, 1.0])
58
- right = np.cross(lookat, up)
59
- right = right / np.linalg.norm(right)
60
- up = np.cross(right, lookat)
61
- up = up / np.linalg.norm(up)
62
-
63
- c2w = np.concatenate([np.stack([right, up, -lookat], axis=-1), camera_position[:, None]], axis=-1)
64
-
65
- w2c = np.zeros((4, 4))
66
- w2c[:3, :3] = np.transpose(c2w[:3, :3], (1, 0))
67
- w2c[:3, 3:] = -np.matmul(np.transpose(c2w[:3, :3], (1, 0)), c2w[:3, 3:])
68
- w2c[3, 3] = 1.0
69
-
70
- return w2c.astype(np.float32)
71
-
72
-
73
- def get_orthographic_projection_matrix(left=-1, right=1, bottom=-1, top=1, near=0, far=2):
74
- """
75
- 计算正交投影矩阵。
76
-
77
- 参数:
78
- left (float): 投影区域左侧边界。
79
- right (float): 投影区域右侧边界。
80
- bottom (float): 投影区域底部边界。
81
- top (float): 投影区域顶部边界。
82
- near (float): 投影区域近裁剪面距离。
83
- far (float): 投影区域远裁剪面距离。
84
-
85
- 返回:
86
- numpy.ndarray: 正交投影矩阵。
87
- """
88
- ortho_matrix = np.eye(4, dtype=np.float32)
89
- ortho_matrix[0, 0] = 2 / (right - left)
90
- ortho_matrix[1, 1] = 2 / (top - bottom)
91
- ortho_matrix[2, 2] = -2 / (far - near)
92
- ortho_matrix[0, 3] = -(right + left) / (right - left)
93
- ortho_matrix[1, 3] = -(top + bottom) / (top - bottom)
94
- ortho_matrix[2, 3] = -(far + near) / (far - near)
95
- return ortho_matrix
96
-
97
-
98
- def get_perspective_projection_matrix(fovy, aspect_wh, near, far):
99
- fovy_rad = math.radians(fovy)
100
- return np.array(
101
- [
102
- [1.0 / (math.tan(fovy_rad / 2.0) * aspect_wh), 0, 0, 0],
103
- [0, 1.0 / math.tan(fovy_rad / 2.0), 0, 0],
104
- [0, 0, -(far + near) / (far - near), -2.0 * far * near / (far - near)],
105
- [0, 0, -1, 0],
106
- ]
107
- ).astype(np.float32)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/DifferentiableRenderer/compile_mesh_painter.sh DELETED
@@ -1 +0,0 @@
1
- c++ -O3 -Wall -shared -std=c++11 -fPIC `python -m pybind11 --includes` mesh_inpaint_processor.cpp -o mesh_inpaint_processor`python3-config --extension-suffix`
 
 
hy3dpaint/DifferentiableRenderer/mesh_inpaint_processor.cpp DELETED
@@ -1,395 +0,0 @@
1
- #include <pybind11/numpy.h>
2
- #include <pybind11/pybind11.h>
3
- #include <pybind11/stl.h>
4
-
5
- #include <algorithm>
6
- #include <cmath>
7
- #include <queue>
8
- #include <vector>
9
- #include <functional>
10
-
11
- namespace py = pybind11;
12
- using namespace std;
13
-
14
- namespace {
15
- // 内部数据结构,避免重复的buffer获取和指针设置
16
- struct MeshData {
17
- int texture_height, texture_width, texture_channel;
18
- int vtx_num;
19
- float* texture_ptr;
20
- uint8_t* mask_ptr;
21
- float* vtx_pos_ptr;
22
- float* vtx_uv_ptr;
23
- int* pos_idx_ptr;
24
- int* uv_idx_ptr;
25
-
26
- // 存储buffer以防止被销毁
27
- py::buffer_info texture_buf, mask_buf, vtx_pos_buf, vtx_uv_buf, pos_idx_buf, uv_idx_buf;
28
-
29
- MeshData(py::array_t<float>& texture, py::array_t<uint8_t>& mask,
30
- py::array_t<float>& vtx_pos, py::array_t<float>& vtx_uv,
31
- py::array_t<int>& pos_idx, py::array_t<int>& uv_idx) {
32
-
33
- texture_buf = texture.request();
34
- mask_buf = mask.request();
35
- vtx_pos_buf = vtx_pos.request();
36
- vtx_uv_buf = vtx_uv.request();
37
- pos_idx_buf = pos_idx.request();
38
- uv_idx_buf = uv_idx.request();
39
-
40
- texture_height = texture_buf.shape[0];
41
- texture_width = texture_buf.shape[1];
42
- texture_channel = texture_buf.shape[2];
43
- texture_ptr = static_cast<float*>(texture_buf.ptr);
44
- mask_ptr = static_cast<uint8_t*>(mask_buf.ptr);
45
-
46
- vtx_num = vtx_pos_buf.shape[0];
47
- vtx_pos_ptr = static_cast<float*>(vtx_pos_buf.ptr);
48
- vtx_uv_ptr = static_cast<float*>(vtx_uv_buf.ptr);
49
- pos_idx_ptr = static_cast<int*>(pos_idx_buf.ptr);
50
- uv_idx_ptr = static_cast<int*>(uv_idx_buf.ptr);
51
- }
52
- };
53
-
54
- // 公共函数:计算UV坐标
55
- pair<int, int> calculateUVCoordinates(int vtx_uv_idx, const MeshData& data) {
56
- int uv_v = round(data.vtx_uv_ptr[vtx_uv_idx * 2] * (data.texture_width - 1));
57
- int uv_u = round((1.0 - data.vtx_uv_ptr[vtx_uv_idx * 2 + 1]) * (data.texture_height - 1));
58
- return make_pair(uv_u, uv_v);
59
- }
60
-
61
- // 公共函数:计算距离权重
62
- float calculateDistanceWeight(const array<float, 3>& vtx_0, const array<float, 3>& vtx1) {
63
- float dist_weight = 1.0f / max(
64
- sqrt(
65
- pow(vtx_0[0] - vtx1[0], 2) +
66
- pow(vtx_0[1] - vtx1[1], 2) +
67
- pow(vtx_0[2] - vtx1[2], 2)
68
- ), 1E-4);
69
- return dist_weight * dist_weight;
70
- }
71
-
72
- // 公共函数:获取顶点位置
73
- array<float, 3> getVertexPosition(int vtx_idx, const MeshData& data) {
74
- return {data.vtx_pos_ptr[vtx_idx * 3],
75
- data.vtx_pos_ptr[vtx_idx * 3 + 1],
76
- data.vtx_pos_ptr[vtx_idx * 3 + 2]};
77
- }
78
-
79
- // 公共函数:构建图结构
80
- void buildGraph(vector<vector<int>>& G, const MeshData& data) {
81
- G.resize(data.vtx_num);
82
- for(int i = 0; i < data.uv_idx_buf.shape[0]; ++i) {
83
- for(int k = 0; k < 3; ++k) {
84
- G[data.pos_idx_ptr[i * 3 + k]].push_back(data.pos_idx_ptr[i * 3 + (k + 1) % 3]);
85
- }
86
- }
87
- }
88
-
89
- // 通用初始化函数:处理两种掩码类型(float和int)
90
- template<typename MaskType>
91
- void initializeVertexDataGeneric(const MeshData& data, vector<MaskType>& vtx_mask,
92
- vector<vector<float>>& vtx_color, vector<int>* uncolored_vtxs = nullptr,
93
- MaskType mask_value = static_cast<MaskType>(1)) {
94
- vtx_mask.assign(data.vtx_num, static_cast<MaskType>(0));
95
- vtx_color.assign(data.vtx_num, vector<float>(data.texture_channel, 0.0f));
96
-
97
- if(uncolored_vtxs) {
98
- uncolored_vtxs->clear();
99
- }
100
-
101
- for(int i = 0; i < data.uv_idx_buf.shape[0]; ++i) {
102
- for(int k = 0; k < 3; ++k) {
103
- int vtx_uv_idx = data.uv_idx_ptr[i * 3 + k];
104
- int vtx_idx = data.pos_idx_ptr[i * 3 + k];
105
- auto uv_coords = calculateUVCoordinates(vtx_uv_idx, data);
106
-
107
- if(data.mask_ptr[uv_coords.first * data.texture_width + uv_coords.second] > 0) {
108
- vtx_mask[vtx_idx] = mask_value;
109
- for(int c = 0; c < data.texture_channel; ++c) {
110
- vtx_color[vtx_idx][c] = data.texture_ptr[(uv_coords.first * data.texture_width +
111
- uv_coords.second) * data.texture_channel + c];
112
- }
113
- } else if(uncolored_vtxs) {
114
- uncolored_vtxs->push_back(vtx_idx);
115
- }
116
- }
117
- }
118
- }
119
-
120
- // 通用平滑算法:支持不同的掩码类型和检查函数
121
- template<typename MaskType>
122
- void performSmoothingAlgorithm(const MeshData& data, const vector<vector<int>>& G,
123
- vector<MaskType>& vtx_mask, vector<vector<float>>& vtx_color,
124
- const vector<int>& uncolored_vtxs,
125
- function<bool(MaskType)> is_colored_func,
126
- function<void(MaskType&)> set_colored_func) {
127
- int smooth_count = 2;
128
- int last_uncolored_vtx_count = 0;
129
-
130
- while(smooth_count > 0) {
131
- int uncolored_vtx_count = 0;
132
-
133
- for(int vtx_idx : uncolored_vtxs) {
134
- vector<float> sum_color(data.texture_channel, 0.0f);
135
- float total_weight = 0.0f;
136
-
137
- array<float, 3> vtx_0 = getVertexPosition(vtx_idx, data);
138
-
139
- for(int connected_idx : G[vtx_idx]) {
140
- if(is_colored_func(vtx_mask[connected_idx])) {
141
- array<float, 3> vtx1 = getVertexPosition(connected_idx, data);
142
- float dist_weight = calculateDistanceWeight(vtx_0, vtx1);
143
-
144
- for(int c = 0; c < data.texture_channel; ++c) {
145
- sum_color[c] += vtx_color[connected_idx][c] * dist_weight;
146
- }
147
- total_weight += dist_weight;
148
- }
149
- }
150
-
151
- if(total_weight > 0.0f) {
152
- for(int c = 0; c < data.texture_channel; ++c) {
153
- vtx_color[vtx_idx][c] = sum_color[c] / total_weight;
154
- }
155
- set_colored_func(vtx_mask[vtx_idx]);
156
- } else {
157
- uncolored_vtx_count++;
158
- }
159
- }
160
-
161
- if(last_uncolored_vtx_count == uncolored_vtx_count) {
162
- smooth_count--;
163
- } else {
164
- smooth_count++;
165
- }
166
- last_uncolored_vtx_count = uncolored_vtx_count;
167
- }
168
- }
169
-
170
- // 前向传播算法的通用实现
171
- void performForwardPropagation(const MeshData& data, const vector<vector<int>>& G,
172
- vector<float>& vtx_mask, vector<vector<float>>& vtx_color,
173
- queue<int>& active_vtxs) {
174
- while(!active_vtxs.empty()) {
175
- queue<int> pending_active_vtxs;
176
-
177
- while(!active_vtxs.empty()) {
178
- int vtx_idx = active_vtxs.front();
179
- active_vtxs.pop();
180
- array<float, 3> vtx_0 = getVertexPosition(vtx_idx, data);
181
-
182
- for(int connected_idx : G[vtx_idx]) {
183
- if(vtx_mask[connected_idx] > 0) continue;
184
-
185
- array<float, 3> vtx1 = getVertexPosition(connected_idx, data);
186
- float dist_weight = calculateDistanceWeight(vtx_0, vtx1);
187
-
188
- for(int c = 0; c < data.texture_channel; ++c) {
189
- vtx_color[connected_idx][c] += vtx_color[vtx_idx][c] * dist_weight;
190
- }
191
-
192
- if(vtx_mask[connected_idx] == 0) {
193
- pending_active_vtxs.push(connected_idx);
194
- }
195
- vtx_mask[connected_idx] -= dist_weight;
196
- }
197
- }
198
-
199
- while(!pending_active_vtxs.empty()) {
200
- int vtx_idx = pending_active_vtxs.front();
201
- pending_active_vtxs.pop();
202
-
203
- for(int c = 0; c < data.texture_channel; ++c) {
204
- vtx_color[vtx_idx][c] /= -vtx_mask[vtx_idx];
205
- }
206
- vtx_mask[vtx_idx] = 1.0f;
207
- active_vtxs.push(vtx_idx);
208
- }
209
- }
210
- }
211
-
212
- // 公共函数:创建输出数组
213
- pair<py::array_t<float>, py::array_t<uint8_t>> createOutputArrays(
214
- const MeshData& data, const vector<float>& vtx_mask,
215
- const vector<vector<float>>& vtx_color) {
216
-
217
- py::array_t<float> new_texture(data.texture_buf.size);
218
- py::array_t<uint8_t> new_mask(data.mask_buf.size);
219
-
220
- auto new_texture_buf = new_texture.request();
221
- auto new_mask_buf = new_mask.request();
222
-
223
- float* new_texture_ptr = static_cast<float*>(new_texture_buf.ptr);
224
- uint8_t* new_mask_ptr = static_cast<uint8_t*>(new_mask_buf.ptr);
225
-
226
- // Copy original texture and mask to new arrays
227
- copy(data.texture_ptr, data.texture_ptr + data.texture_buf.size, new_texture_ptr);
228
- copy(data.mask_ptr, data.mask_ptr + data.mask_buf.size, new_mask_ptr);
229
-
230
- for(int face_idx = 0; face_idx < data.uv_idx_buf.shape[0]; ++face_idx) {
231
- for(int k = 0; k < 3; ++k) {
232
- int vtx_uv_idx = data.uv_idx_ptr[face_idx * 3 + k];
233
- int vtx_idx = data.pos_idx_ptr[face_idx * 3 + k];
234
-
235
- if(vtx_mask[vtx_idx] == 1.0f) {
236
- auto uv_coords = calculateUVCoordinates(vtx_uv_idx, data);
237
-
238
- for(int c = 0; c < data.texture_channel; ++c) {
239
- new_texture_ptr[
240
- (uv_coords.first * data.texture_width + uv_coords.second) *
241
- data.texture_channel + c
242
- ] = vtx_color[vtx_idx][c];
243
- }
244
- new_mask_ptr[uv_coords.first * data.texture_width + uv_coords.second] = 255;
245
- }
246
- }
247
- }
248
-
249
- // Reshape the new arrays to match the original texture and mask shapes
250
- new_texture.resize({data.texture_height, data.texture_width, 3});
251
- new_mask.resize({data.texture_height, data.texture_width});
252
-
253
- return make_pair(new_texture, new_mask);
254
- }
255
-
256
- // 创建顶点颜色输出数组的专用函数
257
- pair<py::array_t<float>, py::array_t<uint8_t>> createVertexColorOutput(
258
- const MeshData& data, const vector<int>& vtx_mask,
259
- const vector<vector<float>>& vtx_color) {
260
-
261
- py::array_t<float> py_vtx_color({data.vtx_num, data.texture_channel});
262
- py::array_t<uint8_t> py_vtx_mask({data.vtx_num});
263
-
264
- auto py_vtx_color_buf = py_vtx_color.request();
265
- auto py_vtx_mask_buf = py_vtx_mask.request();
266
-
267
- float* py_vtx_color_ptr = static_cast<float*>(py_vtx_color_buf.ptr);
268
- uint8_t* py_vtx_mask_ptr = static_cast<uint8_t*>(py_vtx_mask_buf.ptr);
269
-
270
- for(int i = 0; i < data.vtx_num; ++i) {
271
- py_vtx_mask_ptr[i] = vtx_mask[i];
272
- for(int c = 0; c < data.texture_channel; ++c) {
273
- py_vtx_color_ptr[i * data.texture_channel + c] = vtx_color[i][c];
274
- }
275
- }
276
-
277
- return make_pair(py_vtx_color, py_vtx_mask);
278
- }
279
-
280
- } // anonymous namespace
281
-
282
- // 重构后的 meshVerticeInpaint_smooth 函数
283
- pair<py::array_t<float>, py::array_t<uint8_t>> meshVerticeInpaint_smooth(
284
- py::array_t<float> texture, py::array_t<uint8_t> mask, py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
285
- py::array_t<int> pos_idx, py::array_t<int> uv_idx) {
286
-
287
- MeshData data(texture, mask, vtx_pos, vtx_uv, pos_idx, uv_idx);
288
-
289
- vector<float> vtx_mask;
290
- vector<vector<float>> vtx_color;
291
- vector<int> uncolored_vtxs;
292
- vector<vector<int>> G;
293
-
294
- initializeVertexDataGeneric(data, vtx_mask, vtx_color, &uncolored_vtxs, 1.0f);
295
- buildGraph(G, data);
296
-
297
- // 使用通用平滑算法
298
- performSmoothingAlgorithm<float>(data, G, vtx_mask, vtx_color, uncolored_vtxs,
299
- [](float mask_val) { return mask_val > 0; }, // 检查是否着色
300
- [](float& mask_val) { mask_val = 1.0f; } // 设置为已着色
301
- );
302
-
303
- return createOutputArrays(data, vtx_mask, vtx_color);
304
- }
305
-
306
- // 重构后的 meshVerticeInpaint_forward 函数
307
- pair<py::array_t<float>, py::array_t<uint8_t>> meshVerticeInpaint_forward(
308
- py::array_t<float> texture, py::array_t<uint8_t> mask, py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
309
- py::array_t<int> pos_idx, py::array_t<int> uv_idx) {
310
-
311
- MeshData data(texture, mask, vtx_pos, vtx_uv, pos_idx, uv_idx);
312
-
313
- vector<float> vtx_mask;
314
- vector<vector<float>> vtx_color;
315
- vector<vector<int>> G;
316
- queue<int> active_vtxs;
317
-
318
- // 使用通用初始化(不需要 uncolored_vtxs)
319
- initializeVertexDataGeneric(data, vtx_mask, vtx_color, nullptr, 1.0f);
320
- buildGraph(G, data);
321
-
322
- // 收集活跃顶点
323
- for(int i = 0; i < data.vtx_num; ++i) {
324
- if(vtx_mask[i] == 1.0f) {
325
- active_vtxs.push(i);
326
- }
327
- }
328
-
329
- // 使用通用前向传播算法
330
- performForwardPropagation(data, G, vtx_mask, vtx_color, active_vtxs);
331
-
332
- return createOutputArrays(data, vtx_mask, vtx_color);
333
- }
334
-
335
- // 主接口函数
336
- pair<py::array_t<float>, py::array_t<uint8_t>> meshVerticeInpaint(
337
- py::array_t<float> texture, py::array_t<uint8_t> mask, py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
338
- py::array_t<int> pos_idx, py::array_t<int> uv_idx, const string& method = "smooth") {
339
-
340
- if(method == "smooth") {
341
- return meshVerticeInpaint_smooth(texture, mask, vtx_pos, vtx_uv, pos_idx, uv_idx);
342
- } else if(method == "forward") {
343
- return meshVerticeInpaint_forward(texture, mask, vtx_pos, vtx_uv, pos_idx, uv_idx);
344
- } else {
345
- throw invalid_argument("Invalid method. Use 'smooth' or 'forward'.");
346
- }
347
- }
348
-
349
- //============================
350
-
351
- // 重构后的 meshVerticeColor_smooth 函数
352
- pair<py::array_t<float>, py::array_t<uint8_t>> meshVerticeColor_smooth(
353
- py::array_t<float> texture, py::array_t<uint8_t> mask, py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
354
- py::array_t<int> pos_idx, py::array_t<int> uv_idx) {
355
-
356
- MeshData data(texture, mask, vtx_pos, vtx_uv, pos_idx, uv_idx);
357
-
358
- vector<int> vtx_mask;
359
- vector<vector<float>> vtx_color;
360
- vector<int> uncolored_vtxs;
361
- vector<vector<int>> G;
362
-
363
- initializeVertexDataGeneric(data, vtx_mask, vtx_color, &uncolored_vtxs, 1);
364
- buildGraph(G, data);
365
-
366
- // 使用通用平滑算法
367
- performSmoothingAlgorithm<int>(data, G, vtx_mask, vtx_color, uncolored_vtxs,
368
- [](int mask_val) { return mask_val > 0; }, // 检查是否着色
369
- [](int& mask_val) { mask_val = 2; } // 设置为已着色(值为2)
370
- );
371
-
372
- return createVertexColorOutput(data, vtx_mask, vtx_color);
373
- }
374
-
375
- // meshVerticeColor 主接口函数
376
- pair<py::array_t<float>, py::array_t<uint8_t>> meshVerticeColor(
377
- py::array_t<float> texture, py::array_t<uint8_t> mask, py::array_t<float> vtx_pos, py::array_t<float> vtx_uv,
378
- py::array_t<int> pos_idx, py::array_t<int> uv_idx, const string& method = "smooth") {
379
-
380
- if(method == "smooth") {
381
- return meshVerticeColor_smooth(texture, mask, vtx_pos, vtx_uv, pos_idx, uv_idx);
382
- } else {
383
- throw invalid_argument("Invalid method. Use 'smooth' or 'forward'.");
384
- }
385
- }
386
-
387
- // Python绑定
388
- PYBIND11_MODULE(mesh_inpaint_processor, m) {
389
- m.def("meshVerticeInpaint", &meshVerticeInpaint, "A function to process mesh",
390
- py::arg("texture"), py::arg("mask"), py::arg("vtx_pos"), py::arg("vtx_uv"),
391
- py::arg("pos_idx"), py::arg("uv_idx"), py::arg("method") = "smooth");
392
- m.def("meshVerticeColor", &meshVerticeColor, "A function to process mesh",
393
- py::arg("texture"), py::arg("mask"), py::arg("vtx_pos"), py::arg("vtx_uv"),
394
- py::arg("pos_idx"), py::arg("uv_idx"), py::arg("method") = "smooth");
395
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/DifferentiableRenderer/mesh_utils.py DELETED
@@ -1,284 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import os
16
- import cv2
17
- import bpy
18
- import math
19
- import numpy as np
20
- from io import StringIO
21
- from typing import Optional, Tuple, Dict, Any
22
-
23
-
24
- def _safe_extract_attribute(obj: Any, attr_path: str, default: Any = None) -> Any:
25
- """Extract nested attribute safely from object."""
26
- try:
27
- for attr in attr_path.split("."):
28
- obj = getattr(obj, attr)
29
- return obj
30
- except AttributeError:
31
- return default
32
-
33
-
34
- def _convert_to_numpy(data: Any, dtype: np.dtype) -> Optional[np.ndarray]:
35
- """Convert data to numpy array with specified dtype, handling None values."""
36
- if data is None:
37
- return None
38
- return np.asarray(data, dtype=dtype)
39
-
40
-
41
- def load_mesh(mesh):
42
- """Load mesh data including vertices, faces, UV coordinates and texture."""
43
- # Extract vertex positions and face indices
44
- vtx_pos = _safe_extract_attribute(mesh, "vertices")
45
- pos_idx = _safe_extract_attribute(mesh, "faces")
46
-
47
- # Extract UV coordinates (reusing face indices for UV indices)
48
- vtx_uv = _safe_extract_attribute(mesh, "visual.uv")
49
- uv_idx = pos_idx # Reuse face indices for UV mapping
50
-
51
- # Convert to numpy arrays with appropriate dtypes
52
- vtx_pos = _convert_to_numpy(vtx_pos, np.float32)
53
- pos_idx = _convert_to_numpy(pos_idx, np.int32)
54
- vtx_uv = _convert_to_numpy(vtx_uv, np.float32)
55
- uv_idx = _convert_to_numpy(uv_idx, np.int32)
56
-
57
- texture_data = None
58
- return vtx_pos, pos_idx, vtx_uv, uv_idx, texture_data
59
-
60
-
61
- def _get_base_path_and_name(mesh_path: str) -> Tuple[str, str]:
62
- """Get base path without extension and mesh name."""
63
- base_path = os.path.splitext(mesh_path)[0]
64
- name = os.path.basename(base_path)
65
- return base_path, name
66
-
67
-
68
- def _save_texture_map(
69
- texture: np.ndarray,
70
- base_path: str,
71
- suffix: str = "",
72
- image_format: str = ".jpg",
73
- color_convert: Optional[int] = None,
74
- ) -> str:
75
- """Save texture map with optional color conversion."""
76
- path = f"{base_path}{suffix}{image_format}"
77
- processed_texture = (texture * 255).astype(np.uint8)
78
-
79
- if color_convert is not None:
80
- processed_texture = cv2.cvtColor(processed_texture, color_convert)
81
- cv2.imwrite(path, processed_texture)
82
- else:
83
- cv2.imwrite(path, processed_texture[..., ::-1]) # RGB to BGR
84
-
85
- return os.path.basename(path)
86
-
87
-
88
- def _write_mtl_properties(f, properties: Dict[str, Any]):
89
- """Write material properties to MTL file."""
90
- for key, value in properties.items():
91
- if isinstance(value, (list, tuple)):
92
- f.write(f"{key} {' '.join(map(str, value))}\n")
93
- else:
94
- f.write(f"{key} {value}\n")
95
-
96
-
97
- def _create_obj_content(
98
- vtx_pos: np.ndarray, vtx_uv: np.ndarray, pos_idx: np.ndarray, uv_idx: np.ndarray, name: str
99
- ) -> str:
100
- """Create OBJ file content."""
101
- buffer = StringIO()
102
-
103
- # Write header and vertices
104
- buffer.write(f"mtllib {name}.mtl\no {name}\n")
105
- np.savetxt(buffer, vtx_pos, fmt="v %.6f %.6f %.6f")
106
- np.savetxt(buffer, vtx_uv, fmt="vt %.6f %.6f")
107
- buffer.write("s 0\nusemtl Material\n")
108
-
109
- # Write faces
110
- pos_idx_plus1 = pos_idx + 1
111
- uv_idx_plus1 = uv_idx + 1
112
- face_format = np.frompyfunc(lambda *x: f"{int(x[0])}/{int(x[1])}", 2, 1)
113
- faces = face_format(pos_idx_plus1, uv_idx_plus1)
114
- face_strings = [f"f {' '.join(face)}" for face in faces]
115
- buffer.write("\n".join(face_strings) + "\n")
116
-
117
- return buffer.getvalue()
118
-
119
-
120
- def save_obj_mesh(mesh_path, vtx_pos, pos_idx, vtx_uv, uv_idx, texture, metallic=None, roughness=None, normal=None):
121
- """Save mesh as OBJ file with textures and material."""
122
- # Convert inputs to numpy arrays
123
- vtx_pos = _convert_to_numpy(vtx_pos, np.float32)
124
- vtx_uv = _convert_to_numpy(vtx_uv, np.float32)
125
- pos_idx = _convert_to_numpy(pos_idx, np.int32)
126
- uv_idx = _convert_to_numpy(uv_idx, np.int32)
127
-
128
- base_path, name = _get_base_path_and_name(mesh_path)
129
-
130
- # Create and save OBJ content
131
- obj_content = _create_obj_content(vtx_pos, vtx_uv, pos_idx, uv_idx, name)
132
- with open(mesh_path, "w") as obj_file:
133
- obj_file.write(obj_content)
134
-
135
- # Save texture maps
136
- texture_maps = {}
137
- texture_maps["diffuse"] = _save_texture_map(texture, base_path)
138
-
139
- if metallic is not None:
140
- texture_maps["metallic"] = _save_texture_map(metallic, base_path, "_metallic", color_convert=cv2.COLOR_RGB2GRAY)
141
- if roughness is not None:
142
- texture_maps["roughness"] = _save_texture_map(
143
- roughness, base_path, "_roughness", color_convert=cv2.COLOR_RGB2GRAY
144
- )
145
- if normal is not None:
146
- texture_maps["normal"] = _save_texture_map(normal, base_path, "_normal")
147
-
148
- # Create MTL file
149
- _create_mtl_file(base_path, texture_maps, metallic is not None)
150
-
151
-
152
- def _create_mtl_file(base_path: str, texture_maps: Dict[str, str], is_pbr: bool):
153
- """Create MTL material file."""
154
- mtl_path = f"{base_path}.mtl"
155
-
156
- with open(mtl_path, "w") as f:
157
- f.write("newmtl Material\n")
158
-
159
- if is_pbr:
160
- # PBR material properties
161
- properties = {
162
- "Kd": [0.800, 0.800, 0.800],
163
- "Ke": [0.000, 0.000, 0.000], # 鐜鍏夐伄钄�
164
- "Ni": 1.500, # 鎶樺皠绯绘暟
165
- "d": 1.0, # 閫忔槑搴�
166
- "illum": 2, # 鍏夌収妯″瀷
167
- "map_Kd": texture_maps["diffuse"],
168
- }
169
- _write_mtl_properties(f, properties)
170
-
171
- # Additional PBR maps
172
- map_configs = [("metallic", "map_Pm"), ("roughness", "map_Pr"), ("normal", "map_Bump -bm 1.0")]
173
-
174
- for texture_key, mtl_key in map_configs:
175
- if texture_key in texture_maps:
176
- f.write(f"{mtl_key} {texture_maps[texture_key]}\n")
177
- else:
178
- # Standard material properties
179
- properties = {
180
- "Ns": 250.000000,
181
- "Ka": [0.200, 0.200, 0.200],
182
- "Kd": [0.800, 0.800, 0.800],
183
- "Ks": [0.500, 0.500, 0.500],
184
- "Ke": [0.000, 0.000, 0.000],
185
- "Ni": 1.500,
186
- "d": 1.0,
187
- "illum": 3,
188
- "map_Kd": texture_maps["diffuse"],
189
- }
190
- _write_mtl_properties(f, properties)
191
-
192
-
193
- def save_mesh(mesh_path, vtx_pos, pos_idx, vtx_uv, uv_idx, texture, metallic=None, roughness=None, normal=None):
194
- """Save mesh using OBJ format."""
195
- save_obj_mesh(
196
- mesh_path, vtx_pos, pos_idx, vtx_uv, uv_idx, texture, metallic=metallic, roughness=roughness, normal=normal
197
- )
198
-
199
-
200
- def _setup_blender_scene():
201
- """Setup Blender scene for conversion."""
202
- if "convert" not in bpy.data.scenes:
203
- bpy.data.scenes.new("convert")
204
- bpy.context.window.scene = bpy.data.scenes["convert"]
205
-
206
-
207
- def _clear_scene_objects():
208
- """Clear all objects from current Blender scene."""
209
- for obj in bpy.context.scene.objects:
210
- obj.select_set(True)
211
- bpy.data.objects.remove(obj, do_unlink=True)
212
-
213
-
214
- def _select_mesh_objects():
215
- """Select all mesh objects in scene."""
216
- bpy.ops.object.select_all(action="DESELECT")
217
- for obj in bpy.context.scene.objects:
218
- if obj.type == "MESH":
219
- obj.select_set(True)
220
-
221
-
222
- def _merge_vertices_if_needed(merge_vertices: bool):
223
- """Merge duplicate vertices if requested."""
224
- if not merge_vertices:
225
- return
226
-
227
- for obj in bpy.context.selected_objects:
228
- if obj.type == "MESH":
229
- bpy.context.view_layer.objects.active = obj
230
- bpy.ops.object.mode_set(mode="EDIT")
231
- bpy.ops.mesh.select_all(action="SELECT")
232
- bpy.ops.mesh.remove_doubles()
233
- bpy.ops.object.mode_set(mode="OBJECT")
234
-
235
-
236
- def _apply_shading(shade_type: str, auto_smooth_angle: float):
237
- """Apply shading to selected objects."""
238
- shading_ops = {
239
- "SMOOTH": lambda: bpy.ops.object.shade_smooth(),
240
- "FLAT": lambda: bpy.ops.object.shade_flat(),
241
- "AUTO_SMOOTH": lambda: _apply_auto_smooth(auto_smooth_angle),
242
- }
243
-
244
- if shade_type in shading_ops:
245
- shading_ops[shade_type]()
246
-
247
-
248
- def _apply_auto_smooth(auto_smooth_angle: float):
249
- """Apply auto smooth based on Blender version."""
250
- angle_rad = math.radians(auto_smooth_angle)
251
-
252
- if bpy.app.version < (4, 1, 0):
253
- bpy.ops.object.shade_smooth(use_auto_smooth=True, auto_smooth_angle=angle_rad)
254
- elif bpy.app.version < (4, 2, 0):
255
- bpy.ops.object.shade_smooth_by_angle(angle=angle_rad)
256
- else:
257
- bpy.ops.object.shade_auto_smooth(angle=angle_rad)
258
-
259
-
260
- def convert_obj_to_glb(
261
- obj_path: str,
262
- glb_path: str,
263
- shade_type: str = "SMOOTH",
264
- auto_smooth_angle: float = 60,
265
- merge_vertices: bool = False,
266
- ) -> bool:
267
- """Convert OBJ file to GLB format using Blender."""
268
- try:
269
- _setup_blender_scene()
270
- _clear_scene_objects()
271
-
272
- # Import OBJ file
273
- bpy.ops.wm.obj_import(filepath=obj_path)
274
- _select_mesh_objects()
275
-
276
- # Process meshes
277
- _merge_vertices_if_needed(merge_vertices)
278
- _apply_shading(shade_type, auto_smooth_angle)
279
-
280
- # Export to GLB
281
- bpy.ops.export_scene.gltf(filepath=glb_path, use_active_scene=True)
282
- return True
283
- except Exception:
284
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/LICENSE DELETED
@@ -1,81 +0,0 @@
1
- TENCENT HUNYUAN 3D 2.1 COMMUNITY LICENSE AGREEMENT
2
- Tencent Hunyuan 3D 2.1 Release Date: June 13, 2025
3
- THIS LICENSE AGREEMENT DOES NOT APPLY IN THE EUROPEAN UNION, UNITED KINGDOM AND SOUTH KOREA AND IS EXPRESSLY LIMITED TO THE TERRITORY, AS DEFINED BELOW.
4
- By clicking to agree or by using, reproducing, modifying, distributing, performing or displaying any portion or element of the Tencent Hunyuan 3D 2.1 Works, including via any Hosted Service, You will be deemed to have recognized and accepted the content of this Agreement, which is effective immediately.
5
- 1. DEFINITIONS.
6
- a. “Acceptable Use Policy” shall mean the policy made available by Tencent as set forth in the Exhibit A.
7
- b. “Agreement” shall mean the terms and conditions for use, reproduction, distribution, modification, performance and displaying of Tencent Hunyuan 3D 2.1 Works or any portion or element thereof set forth herein.
8
- c. “Documentation” shall mean the specifications, manuals and documentation for Tencent Hunyuan 3D 2.1 made publicly available by Tencent.
9
- d. “Hosted Service” shall mean a hosted service offered via an application programming interface (API), web access, or any other electronic or remote means.
10
- e. “Licensee,” “You” or “Your” shall mean a natural person or legal entity exercising the rights granted by this Agreement and/or using the Tencent Hunyuan 3D 2.1 Works for any purpose and in any field of use.
11
- f. “Materials” shall mean, collectively, Tencent’s proprietary Tencent Hunyuan 3D 2.1 and Documentation (and any portion thereof) as made available by Tencent under this Agreement.
12
- g. “Model Derivatives” shall mean all: (i) modifications to Tencent Hunyuan 3D 2.1 or any Model Derivative of Tencent Hunyuan 3D 2.1; (ii) works based on Tencent Hunyuan 3D 2.1 or any Model Derivative of Tencent Hunyuan 3D 2.1; or (iii) any other machine learning model which is created by transfer of patterns of the weights, parameters, operations, or Output of Tencent Hunyuan 3D 2.1 or any Model Derivative of Tencent Hunyuan 3D 2.1, to that model in order to cause that model to perform similarly to Tencent Hunyuan 3D 2.1 or a Model Derivative of Tencent Hunyuan 3D 2.1, including distillation methods, methods that use intermediate data representations, or methods based on the generation of synthetic data Outputs by Tencent Hunyuan 3D 2.1 or a Model Derivative of Tencent Hunyuan 3D 2.1 for training that model. For clarity, Outputs by themselves are not deemed Model Derivatives.
13
- h. “Output” shall mean the information and/or content output of Tencent Hunyuan 3D 2.1 or a Model Derivative that results from operating or otherwise using Tencent Hunyuan 3D 2.1 or a Model Derivative, including via a Hosted Service.
14
- i. “Tencent,” “We” or “Us” shall mean THL Q Limited.
15
- j. “Tencent Hunyuan 3D 2.1” shall mean the 3D generation models and their software and algorithms, including trained model weights, parameters (including optimizer states), machine-learning model code, inference-enabling code, training-enabling code, fine-tuning enabling code and other elements of the foregoing made publicly available by Us at [ https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1].
16
- k. “Tencent Hunyuan 3D 2.1 Works” shall mean: (i) the Materials; (ii) Model Derivatives; and (iii) all derivative works thereof.
17
- l. “Territory” shall mean the worldwide territory, excluding the territory of the European Union, United Kingdom and South Korea.
18
- m. “Third Party” or “Third Parties” shall mean individuals or legal entities that are not under common control with Us or You.
19
- n. “including” shall mean including but not limited to.
20
- 2. GRANT OF RIGHTS.
21
- We grant You, for the Territory only, a non-exclusive, non-transferable and royalty-free limited license under Tencent’s intellectual property or other rights owned by Us embodied in or utilized by the Materials to use, reproduce, distribute, create derivative works of (including Model Derivatives), and make modifications to the Materials, only in accordance with the terms of this Agreement and the Acceptable Use Policy, and You must not violate (or encourage or permit anyone else to violate) any term of this Agreement or the Acceptable Use Policy.
22
- 3. DISTRIBUTION.
23
- You may, subject to Your compliance with this Agreement, distribute or make available to Third Parties the Tencent Hunyuan 3D 2.1 Works, exclusively in the Territory, provided that You meet all of the following conditions:
24
- a. You must provide all such Third Party recipients of the Tencent Hunyuan 3D 2.1 Works or products or services using them a copy of this Agreement;
25
- b. You must cause any modified files to carry prominent notices stating that You changed the files;
26
- c. You are encouraged to: (i) publish at least one technology introduction blogpost or one public statement expressing Your experience of using the Tencent Hunyuan 3D 2.1 Works; and (ii) mark the products or services developed by using the Tencent Hunyuan 3D 2.1 Works to indicate that the product/service is “Powered by Tencent Hunyuan”; and
27
- d. All distributions to Third Parties (other than through a Hosted Service) must be accompanied by a “Notice” text file that contains the following notice: “Tencent Hunyuan 3D 2.1 is licensed under the Tencent Hunyuan 3D 2.1 Community License Agreement, Copyright © 2025 Tencent. All Rights Reserved. The trademark rights of “Tencent Hunyuan” are owned by Tencent or its affiliate.”
28
- You may add Your own copyright statement to Your modifications and, except as set forth in this Section and in Section 5, may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Model Derivatives as a whole, provided Your use, reproduction, modification, distribution, performance and display of the work otherwise complies with the terms and conditions of this Agreement (including as regards the Territory). If You receive Tencent Hunyuan 3D 2.1 Works from a Licensee as part of an integrated end user product, then this Section 3 of this Agreement will not apply to You.
29
- 4. ADDITIONAL COMMERCIAL TERMS.
30
- If, on the Tencent Hunyuan 3D 2.1 version release date, the monthly active users of all products or services made available by or for Licensee is greater than 1 million monthly active users in the preceding calendar month, You must request a license from Tencent, which Tencent may grant to You in its sole discretion, and You are not authorized to exercise any of the rights under this Agreement unless or until Tencent otherwise expressly grants You such rights.
31
- Subject to Tencent's written approval, you may request a license for the use of Tencent Hunyuan 3D 2.1 by submitting the following information to [email protected]:
32
- a. Your company’s name and associated business sector that plans to use Tencent Hunyuan 3D 2.1.
33
- b. Your intended use case and the purpose of using Tencent Hunyuan 3D 2.1.
34
- c. Your plans to modify Tencent Hunyuan 3D 2.1 or create Model Derivatives.
35
- 5. RULES OF USE.
36
- a. Your use of the Tencent Hunyuan 3D 2.1 Works must comply with applicable laws and regulations (including trade compliance laws and regulations) and adhere to the Acceptable Use Policy for the Tencent Hunyuan 3D 2.1 Works, which is hereby incorporated by reference into this Agreement. You must include the use restrictions referenced in these Sections 5(a) and 5(b) as an enforceable provision in any agreement (e.g., license agreement, terms of use, etc.) governing the use and/or distribution of Tencent Hunyuan 3D 2.1 Works and You must provide notice to subsequent users to whom You distribute that Tencent Hunyuan 3D 2.1 Works are subject to the use restrictions in these Sections 5(a) and 5(b).
37
- b. You must not use the Tencent Hunyuan 3D 2.1 Works or any Output or results of the Tencent Hunyuan 3D 2.1 Works to improve any other AI model (other than Tencent Hunyuan 3D 2.1 or Model Derivatives thereof).
38
- c. You must not use, reproduce, modify, distribute, or display the Tencent Hunyuan 3D 2.1 Works, Output or results of the Tencent Hunyuan 3D 2.1 Works outside the Territory. Any such use outside the Territory is unlicensed and unauthorized under this Agreement.
39
- 6. INTELLECTUAL PROPERTY.
40
- a. Subject to Tencent’s ownership of Tencent Hunyuan 3D 2.1 Works made by or for Tencent and intellectual property rights therein, conditioned upon Your compliance with the terms and conditions of this Agreement, as between You and Tencent, You will be the owner of any derivative works and modifications of the Materials and any Model Derivatives that are made by or for You.
41
- b. No trademark licenses are granted under this Agreement, and in connection with the Tencent Hunyuan 3D 2.1 Works, Licensee may not use any name or mark owned by or associated with Tencent or any of its affiliates, except as required for reasonable and customary use in describing and distributing the Tencent Hunyuan 3D 2.1 Works. Tencent hereby grants You a license to use “Tencent Hunyuan” (the “Mark”) in the Territory solely as required to comply with the provisions of Section 3(c), provided that You comply with any applicable laws related to trademark protection. All goodwill arising out of Your use of the Mark will inure to the benefit of Tencent.
42
- c. If You commence a lawsuit or other proceedings (including a cross-claim or counterclaim in a lawsuit) against Us or any person or entity alleging that the Materials or any Output, or any portion of any of the foregoing, infringe any intellectual property or other right owned or licensable by You, then all licenses granted to You under this Agreement shall terminate as of the date such lawsuit or other proceeding is filed. You will defend, indemnify and hold harmless Us from and against any claim by any Third Party arising out of or related to Your or the Third Party’s use or distribution of the Tencent Hunyuan 3D 2.1 Works.
43
- d. Tencent claims no rights in Outputs You generate. You and Your users are solely responsible for Outputs and their subsequent uses.
44
- 7. DISCLAIMERS OF WARRANTY AND LIMITATIONS OF LIABILITY.
45
- a. We are not obligated to support, update, provide training for, or develop any further version of the Tencent Hunyuan 3D 2.1 Works or to grant any license thereto.
46
- b. UNLESS AND ONLY TO THE EXTENT REQUIRED BY APPLICABLE LAW, THE TENCENT HUNYUAN 3D 2.1 WORKS AND ANY OUTPUT AND RESULTS THEREFROM ARE PROVIDED “AS IS” WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND INCLUDING ANY WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, COURSE OF DEALING, USAGE OF TRADE, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, REPRODUCING, MODIFYING, PERFORMING, DISPLAYING OR DISTRIBUTING ANY OF THE TENCENT HUNYUAN 3D 2.1 WORKS OR OUTPUTS AND ASSUME ANY AND ALL RISKS ASSOCIATED WITH YOUR OR A THIRD PARTY’S USE OR DISTRIBUTION OF ANY OF THE TENCENT HUNYUAN 3D 2.1 WORKS OR OUTPUTS AND YOUR EXERCISE OF RIGHTS AND PERMISSIONS UNDER THIS AGREEMENT.
47
- c. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL TENCENT OR ITS AFFILIATES BE LIABLE UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, NEGLIGENCE, PRODUCTS LIABILITY, OR OTHERWISE, FOR ANY DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR LOST PROFITS OF ANY KIND ARISING FROM THIS AGREEMENT OR RELATED TO ANY OF THE TENCENT HUNYUAN 3D 2.1 WORKS OR OUTPUTS, EVEN IF TENCENT OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF ANY OF THE FOREGOING.
48
- 8. SURVIVAL AND TERMINATION.
49
- a. The term of this Agreement shall commence upon Your acceptance of this Agreement or access to the Materials and will continue in full force and effect until terminated in accordance with the terms and conditions herein.
50
- b. We may terminate this Agreement if You breach any of the terms or conditions of this Agreement. Upon termination of this Agreement, You must promptly delete and cease use of the Tencent Hunyuan 3D 2.1 Works. Sections 6(a), 6(c), 7 and 9 shall survive the termination of this Agreement.
51
- 9. GOVERNING LAW AND JURISDICTION.
52
- a. This Agreement and any dispute arising out of or relating to it will be governed by the laws of the Hong Kong Special Administrative Region of the People’s Republic of China, without regard to conflict of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement.
53
- b. Exclusive jurisdiction and venue for any dispute arising out of or relating to this Agreement will be a court of competent jurisdiction in the Hong Kong Special Administrative Region of the People’s Republic of China, and Tencent and Licensee consent to the exclusive jurisdiction of such court with respect to any such dispute.
54
-
55
- EXHIBIT A
56
- ACCEPTABLE USE POLICY
57
-
58
- Tencent reserves the right to update this Acceptable Use Policy from time to time.
59
- Last modified: November 5, 2024
60
-
61
- Tencent endeavors to promote safe and fair use of its tools and features, including Tencent Hunyuan 3D 2.1. You agree not to use Tencent Hunyuan 3D 2.1 or Model Derivatives:
62
- 1. Outside the Territory;
63
- 2. In any way that violates any applicable national, federal, state, local, international or any other law or regulation;
64
- 3. To harm Yourself or others;
65
- 4. To repurpose or distribute output from Tencent Hunyuan 3D 2.1 or any Model Derivatives to harm Yourself or others;
66
- 5. To override or circumvent the safety guardrails and safeguards We have put in place;
67
- 6. For the purpose of exploiting, harming or attempting to exploit or harm minors in any way;
68
- 7. To generate or disseminate verifiably false information and/or content with the purpose of harming others or influencing elections;
69
- 8. To generate or facilitate false online engagement, including fake reviews and other means of fake online engagement;
70
- 9. To intentionally defame, disparage or otherwise harass others;
71
- 10. To generate and/or disseminate malware (including ransomware) or any other content to be used for the purpose of harming electronic systems;
72
- 11. To generate or disseminate personal identifiable information with the purpose of harming others;
73
- 12. To generate or disseminate information (including images, code, posts, articles), and place the information in any public context (including –through the use of bot generated tweets), without expressly and conspicuously identifying that the information and/or content is machine generated;
74
- 13. To impersonate another individual without consent, authorization, or legal right;
75
- 14. To make high-stakes automated decisions in domains that affect an individual’s safety, rights or wellbeing (e.g., law enforcement, migration, medicine/health, management of critical infrastructure, safety components of products, essential services, credit, employment, housing, education, social scoring, or insurance);
76
- 15. In a manner that violates or disrespects the social ethics and moral standards of other countries or regions;
77
- 16. To perform, facilitate, threaten, incite, plan, promote or encourage violent extremism or terrorism;
78
- 17. For any use intended to discriminate against or harm individuals or groups based on protected characteristics or categories, online or offline social behavior or known or predicted personal or personality characteristics;
79
- 18. To intentionally exploit any of the vulnerabilities of a specific group of persons based on their age, social, physical or mental characteristics, in order to materially distort the behavior of a person pertaining to that group in a manner that causes or is likely to cause that person or another person physical or psychological harm;
80
- 19. For military purposes;
81
- 20. To engage in the unauthorized or unlicensed practice of any profession including, but not limited to, financial, legal, medical/health, or other professional practices.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/README.md DELETED
@@ -1,96 +0,0 @@
1
- # Hunyuan3D-Paint 2.1
2
-
3
- Hunyuan3D-Paint 2.1 is a high quality PBR texture generation model for 3D meshes, powered by [RomanTex](https://github.com/oakshy/RomanTex) and [MaterialMVP](https://github.com/ZebinHe/MaterialMVP/).
4
-
5
-
6
- ## Quick Inference
7
- You need to manually download the RealESRGAN weight to the `ckpt` folder using the following command:
8
- ```bash
9
- wget https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth -P ckpt
10
- ```
11
-
12
- Given a 3D mesh `mesh.glb` and a reference image `image.png`, you can run inference using the following code. The result will be saved as `textured_mesh.glb`.
13
-
14
- ```bash
15
- python3 demo.py
16
- ```
17
- **Optional arguments in `demo.py`:**
18
-
19
- - `max_num_view` : Maximum number of views, adaptively selected by the model (integer between 6 to 9)
20
-
21
- - `resolution` : Resolution for generated PBR textures (512 or 768)
22
-
23
- **Memory Recommendation:** For `max_num_view=6` and `resolution=512`, we recommend using a GPU with at least **21GB VRAM**.
24
-
25
- ## Training
26
-
27
- ### Data Prepare
28
- We provide a piece of data in `train_examples` for the overfitting training test. The data structure should be organized as follows:
29
-
30
- ```
31
- train_examples/
32
- ├── examples.json
33
- └── 001/
34
- ├── render_tex/ # Rendered generated PBR images
35
- │ ├── 000.png # Rendered views (RGB images)
36
- │ ├── 000_albedo.png # Albedo maps for each view
37
- │ ├── 000_mr.png # Metallic-Roughness maps for each view, R and G channels
38
- │ ├── 000_normal.png # Normal maps
39
- │ ├── 000_normal.png # Normal maps
40
- │ ├── 000_pos.png # Position maps
41
- │ ├── 000_pos.png # Position maps
42
- │ ├── 001.png # Additional views...
43
- │ ├── 001_albedo.png
44
- │ ├── 001_mr.png
45
- │ ├── 001_normal.png
46
- │ ├── 001_pos.png
47
- │ └── ... # More views (002, 003, 004, 005, ...)
48
- └── render_cond/ # Rendered reference images (at least two light conditions should be rendered to facilitate consistency loss)
49
- ├── 000_light_AL.png # Light condition 1 (Area Light)
50
- ├── 000_light_ENVMAP.png # Light condition 2 (Environment map)
51
- ├── 000_light_PL.png # Light condition 3 (Point lighting)
52
- ├── 001_light_AL.png
53
- ├── 001_light_ENVMAP.png
54
- ├── 001_light_PL.png
55
- └── ... # More lighting conditions (002-005, ...)
56
- ```
57
-
58
- Each training example contains:
59
- - **render_tex/**: Multi-view renderings with PBR material properties
60
- - Main RGB images (`XXX.png`)
61
- - Albedo maps (`XXX_albedo.png`)
62
- - Metallic-Roughness maps (`XXX_mr.png`)
63
- - Normal maps (`XXX_normal.png/jpg`)
64
- - Position maps (`XXX_pos.png/jpg`)
65
- - Camera transforms (`transforms.json`)
66
- - **render_cond/**: Lighting condition maps for each view
67
- - Ambient lighting (`XXX_light_AL.png`)
68
- - Environment map lighting (`XXX_light_ENVMAP.png`)
69
- - Point lighting (`XXX_light_PL.png`)
70
-
71
- ### Launch Training
72
-
73
-
74
- ```bash
75
- python3 train.py --base 'cfgs/hunyuan-paint-pbr.yaml' --name overfit --logdir logs/
76
- ```
77
-
78
- ## BibTeX
79
-
80
- If you found Hunyuan3D-Paint 2.1 helpful, please cite our papers:
81
-
82
- ```bibtex
83
- @article{feng2025romantex,
84
- title={RomanTex: Decoupling 3D-aware Rotary Positional Embedded Multi-Attention Network for Texture Synthesis},
85
- author={Feng, Yifei and Yang, Mingxin and Yang, Shuhui and Zhang, Sheng and Yu, Jiaao and Zhao, Zibo and Liu, Yuhong and Jiang, Jie and Guo, Chunchao},
86
- journal={arXiv preprint arXiv:2503.19011},
87
- year={2025}
88
- }
89
-
90
- @article{he2025materialmvp,
91
- title={MaterialMVP: Illumination-Invariant Material Generation via Multi-view PBR Diffusion},
92
- author={He, Zebin and Yang, Mingxin and Yang, Shuhui and Tang, Yixuan and Wang, Tao and Zhang, Kaihao and Chen, Guanying and Liu, Yuhong and Jiang, Jie and Guo, Chunchao and Luo, Wenhan},
93
- journal={arXiv preprint arXiv:2503.10289},
94
- year={2025}
95
- }
96
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/cfgs/hunyuan-paint-pbr.yaml DELETED
@@ -1,52 +0,0 @@
1
- model:
2
- base_learning_rate: 5.0e-05
3
- target: hunyuanpaintpbr.model.HunyuanPaint
4
- params:
5
- num_view: 6
6
- view_size: 512
7
- drop_cond_prob: 0.1
8
-
9
- noise_in_channels: 12
10
-
11
- stable_diffusion_config:
12
- pretrained_model_name_or_path: stabilityai/stable-diffusion-2-1
13
- custom_pipeline: ./hunyuanpaintpbr
14
-
15
-
16
- data:
17
- target: src.data.objaverse_hunyuan.DataModuleFromConfig
18
- params:
19
- batch_size: 1
20
- num_workers: 4
21
- train:
22
- -
23
- target: src.data.dataloader.objaverse_loader_forTexturePBR.TextureDataset
24
- params:
25
- num_view: 6
26
- json_path: train_examples/examples.json
27
- validation:
28
- -
29
- target: src.data.dataloader.objaverse_loader_forTexturePBR.TextureDataset
30
- params:
31
- num_view: 6
32
- json_path: train_examples/examples.json
33
-
34
- lightning:
35
- modelcheckpoint:
36
- params:
37
- every_n_train_steps: 10000
38
- save_top_k: -1
39
- save_last: true
40
- callbacks: {}
41
-
42
- trainer:
43
- benchmark: true
44
- max_epochs: -1
45
- gradient_clip_val: 1.0
46
- val_check_interval: 1000
47
- num_sanity_val_steps: 0
48
- accumulate_grad_batches: 1
49
- check_val_every_n_epoch: null # if not set this, validation does not run
50
-
51
- init_control_from:
52
- resume_from:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/convert_utils.py DELETED
@@ -1,140 +0,0 @@
1
- import trimesh
2
- import pygltflib
3
- import numpy as np
4
- from PIL import Image
5
- import base64
6
- import io
7
-
8
-
9
- def combine_metallic_roughness(metallic_path, roughness_path, output_path):
10
- """
11
- 将metallic和roughness贴图合并为一张贴图
12
- GLB格式要求metallic在B通道,roughness在G通道
13
- """
14
- # 加载贴图
15
- metallic_img = Image.open(metallic_path).convert("L") # 转为灰度
16
- roughness_img = Image.open(roughness_path).convert("L") # 转为灰度
17
-
18
- # 确保尺寸一致
19
- if metallic_img.size != roughness_img.size:
20
- roughness_img = roughness_img.resize(metallic_img.size)
21
-
22
- # 创建RGB图像
23
- width, height = metallic_img.size
24
- combined = Image.new("RGB", (width, height))
25
-
26
- # 转为numpy数组便于操作
27
- metallic_array = np.array(metallic_img)
28
- roughness_array = np.array(roughness_img)
29
-
30
- # 创建合并的数组 (R, G, B) = (AO, Roughness, Metallic)
31
- combined_array = np.zeros((height, width, 3), dtype=np.uint8)
32
- combined_array[:, :, 0] = 255 # R通道:AO (如果没有AO贴图,设为白色)
33
- combined_array[:, :, 1] = roughness_array # G通道:Roughness
34
- combined_array[:, :, 2] = metallic_array # B通道:Metallic
35
-
36
- # 转回PIL图像并保存
37
- combined = Image.fromarray(combined_array)
38
- combined.save(output_path)
39
- return output_path
40
-
41
-
42
- def create_glb_with_pbr_materials(obj_path, textures_dict, output_path):
43
- """
44
- 使用pygltflib创建包含完整PBR材质的GLB文件
45
-
46
- textures_dict = {
47
- 'albedo': 'path/to/albedo.png',
48
- 'metallic': 'path/to/metallic.png',
49
- 'roughness': 'path/to/roughness.png',
50
- 'normal': 'path/to/normal.png', # 可选
51
- 'ao': 'path/to/ao.png' # 可选
52
- }
53
- """
54
- # 1. 加载OBJ文件
55
- mesh = trimesh.load(obj_path)
56
-
57
- # 2. 先导出为临时GLB
58
- temp_glb = "temp.glb"
59
- mesh.export(temp_glb)
60
-
61
- # 3. 加载GLB文件进行材质编辑
62
- gltf = pygltflib.GLTF2().load(temp_glb)
63
-
64
- # 4. 准备纹理数据
65
- def image_to_data_uri(image_path):
66
- """将图像转换为data URI"""
67
- with open(image_path, "rb") as f:
68
- image_data = f.read()
69
- encoded = base64.b64encode(image_data).decode()
70
- return f"data:image/png;base64,{encoded}"
71
-
72
- # 5. 合并metallic和roughness
73
- if "metallic" in textures_dict and "roughness" in textures_dict:
74
- mr_combined_path = "mr_combined.png"
75
- combine_metallic_roughness(textures_dict["metallic"], textures_dict["roughness"], mr_combined_path)
76
- textures_dict["metallicRoughness"] = mr_combined_path
77
-
78
- # 6. 添加图像到GLTF
79
- images = []
80
- textures = []
81
-
82
- texture_mapping = {
83
- "albedo": "baseColorTexture",
84
- "metallicRoughness": "metallicRoughnessTexture",
85
- "normal": "normalTexture",
86
- "ao": "occlusionTexture",
87
- }
88
-
89
- for tex_type, tex_path in textures_dict.items():
90
- if tex_type in texture_mapping and tex_path:
91
- # 添加图像
92
- image = pygltflib.Image(uri=image_to_data_uri(tex_path))
93
- images.append(image)
94
-
95
- # 添加纹理
96
- texture = pygltflib.Texture(source=len(images) - 1)
97
- textures.append(texture)
98
-
99
- # 7. 创建PBR材质
100
- pbr_metallic_roughness = pygltflib.PbrMetallicRoughness(
101
- baseColorFactor=[1.0, 1.0, 1.0, 1.0], metallicFactor=1.0, roughnessFactor=1.0
102
- )
103
-
104
- # 设置纹理索引
105
- texture_index = 0
106
- if "albedo" in textures_dict:
107
- pbr_metallic_roughness.baseColorTexture = pygltflib.TextureInfo(index=texture_index)
108
- texture_index += 1
109
-
110
- if "metallicRoughness" in textures_dict:
111
- pbr_metallic_roughness.metallicRoughnessTexture = pygltflib.TextureInfo(index=texture_index)
112
- texture_index += 1
113
-
114
- # 创建材质
115
- material = pygltflib.Material(name="PBR_Material", pbrMetallicRoughness=pbr_metallic_roughness)
116
-
117
- # 添加法线贴图
118
- if "normal" in textures_dict:
119
- material.normalTexture = pygltflib.NormalTextureInfo(index=texture_index)
120
- texture_index += 1
121
-
122
- # 添加AO贴图
123
- if "ao" in textures_dict:
124
- material.occlusionTexture = pygltflib.OcclusionTextureInfo(index=texture_index)
125
-
126
- # 8. 更新GLTF
127
- gltf.images = images
128
- gltf.textures = textures
129
- gltf.materials = [material]
130
-
131
- # 确保mesh使用材质
132
- if gltf.meshes:
133
- for primitive in gltf.meshes[0].primitives:
134
- primitive.material = 0
135
-
136
- # 9. 保存最终GLB
137
- gltf.save(output_path)
138
- print(f"PBR GLB文件已保存: {output_path}")
139
-
140
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/demo.py DELETED
@@ -1,35 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- from textureGenPipeline import Hunyuan3DPaintPipeline, Hunyuan3DPaintConfig
16
-
17
- try:
18
- from utils.torchvision_fix import apply_fix
19
-
20
- apply_fix()
21
- except ImportError:
22
- print("Warning: torchvision_fix module not found, proceeding without compatibility fix")
23
- except Exception as e:
24
- print(f"Warning: Failed to apply torchvision fix: {e}")
25
-
26
-
27
- if __name__ == "__main__":
28
-
29
- max_num_view = 6 # can be 6 to 9
30
- resolution = 512 # can be 768 or 512
31
-
32
- conf = Hunyuan3DPaintConfig(max_num_view, resolution)
33
- paint_pipeline = Hunyuan3DPaintPipeline(conf)
34
- output_mesh_path = paint_pipeline(mesh_path="./assets/case_1/mesh.glb", image_path="./assets/case_1/image.png")
35
- print(f"Output mesh path: {output_mesh_path}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/hunyuanpaintpbr/__init__.py DELETED
@@ -1,39 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- from .pipeline import HunyuanPaintPipeline
16
- from .unet.model import HunyuanPaint
17
- from .unet.modules import (
18
- Dino_v2,
19
- Basic2p5DTransformerBlock,
20
- ImageProjModel,
21
- UNet2p5DConditionModel,
22
- )
23
- from .unet.attn_processor import (
24
- PoseRoPEAttnProcessor2_0,
25
- SelfAttnProcessor2_0,
26
- RefAttnProcessor2_0,
27
- )
28
-
29
- __all__ = [
30
- 'HunyuanPaintPipeline',
31
- 'HunyuanPaint',
32
- 'Dino_v2',
33
- 'Basic2p5DTransformerBlock',
34
- 'ImageProjModel',
35
- 'UNet2p5DConditionModel',
36
- 'PoseRoPEAttnProcessor2_0',
37
- 'SelfAttnProcessor2_0',
38
- 'RefAttnProcessor2_0',
39
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/hunyuanpaintpbr/pipeline.py DELETED
@@ -1,736 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- from typing import Any, Dict, Optional
16
- from diffusers.models import AutoencoderKL, UNet2DConditionModel
17
- from diffusers.schedulers import KarrasDiffusionSchedulers
18
-
19
- import numpy
20
- import torch
21
- import torch.utils.checkpoint
22
- import torch.distributed
23
- import numpy as np
24
- import transformers
25
- from PIL import Image
26
- from einops import rearrange
27
- from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer, CLIPVisionModelWithProjection
28
- from typing import Any, Callable, Dict, List, Optional, Union, Tuple
29
-
30
- import diffusers
31
- from diffusers import (
32
- AutoencoderKL,
33
- DiffusionPipeline,
34
- UNet2DConditionModel,
35
- )
36
- from diffusers.image_processor import VaeImageProcessor
37
-
38
- from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion import (
39
- StableDiffusionPipeline,
40
- retrieve_timesteps,
41
- rescale_noise_cfg,
42
- )
43
-
44
- from diffusers.utils import deprecate
45
- from diffusers.callbacks import MultiPipelineCallbacks, PipelineCallback
46
- from diffusers.image_processor import PipelineImageInput
47
- from diffusers.pipelines.stable_diffusion.pipeline_output import StableDiffusionPipelineOutput
48
- from .unet.modules import UNet2p5DConditionModel
49
- from .unet.attn_processor import SelfAttnProcessor2_0, RefAttnProcessor2_0, PoseRoPEAttnProcessor2_0
50
-
51
- __all__ = [
52
- "HunyuanPaintPipeline",
53
- "UNet2p5DConditionModel",
54
- "SelfAttnProcessor2_0",
55
- "RefAttnProcessor2_0",
56
- "PoseRoPEAttnProcessor2_0",
57
- ]
58
-
59
-
60
- def to_rgb_image(maybe_rgba: Image.Image):
61
- if maybe_rgba.mode == "RGB":
62
- return maybe_rgba
63
- elif maybe_rgba.mode == "RGBA":
64
- rgba = maybe_rgba
65
- img = numpy.random.randint(127, 128, size=[rgba.size[1], rgba.size[0], 3], dtype=numpy.uint8)
66
- img = Image.fromarray(img, "RGB")
67
- img.paste(rgba, mask=rgba.getchannel("A"))
68
- return img
69
- else:
70
- raise ValueError("Unsupported image type.", maybe_rgba.mode)
71
-
72
-
73
- class HunyuanPaintPipeline(StableDiffusionPipeline):
74
-
75
- """Custom pipeline for multiview PBR texture generation.
76
-
77
- Extends Stable Diffusion with:
78
- - Material-specific conditioning
79
- - Multiview processing
80
- - Position-aware attention
81
- - 2.5D UNet integration
82
- """
83
-
84
- def __init__(
85
- self,
86
- vae: AutoencoderKL,
87
- text_encoder: CLIPTextModel,
88
- tokenizer: CLIPTokenizer,
89
- unet: UNet2DConditionModel,
90
- scheduler: KarrasDiffusionSchedulers,
91
- feature_extractor: CLIPImageProcessor,
92
- safety_checker=None,
93
- use_torch_compile=False,
94
- ):
95
- DiffusionPipeline.__init__(self)
96
-
97
- safety_checker = None
98
- self.register_modules(
99
- vae=torch.compile(vae) if use_torch_compile else vae,
100
- text_encoder=text_encoder,
101
- tokenizer=tokenizer,
102
- unet=unet,
103
- scheduler=scheduler,
104
- safety_checker=safety_checker,
105
- feature_extractor=torch.compile(feature_extractor) if use_torch_compile else feature_extractor,
106
- )
107
-
108
- self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1)
109
- self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
110
-
111
- if isinstance(self.unet, UNet2DConditionModel):
112
- self.unet = UNet2p5DConditionModel(self.unet, None, self.scheduler)
113
-
114
- def eval(self):
115
- self.unet.eval()
116
- self.vae.eval()
117
-
118
- def set_pbr_settings(self, pbr_settings: List[str]):
119
- self.pbr_settings = pbr_settings
120
-
121
- def set_learned_parameters(self):
122
-
123
- """Configures parameter freezing strategy.
124
-
125
- Freezes:
126
- - Standard attention layers
127
- - Dual-stream reference UNet
128
-
129
- Unfreezes:
130
- - Material-specific parameters
131
- - DINO integration components
132
- """
133
-
134
- freezed_names = ["attn1", "unet_dual"]
135
- added_learned_names = ["albedo", "mr", "dino"]
136
-
137
- for name, params in self.unet.named_parameters():
138
- if any(freeze_name in name for freeze_name in freezed_names) and all(
139
- learned_name not in name for learned_name in added_learned_names
140
- ):
141
- params.requires_grad = False
142
- else:
143
- params.requires_grad = True
144
-
145
- def prepare(self):
146
- if isinstance(self.unet, UNet2DConditionModel):
147
- self.unet = UNet2p5DConditionModel(self.unet, None, self.scheduler).eval()
148
-
149
- @torch.no_grad()
150
- def encode_images(self, images):
151
-
152
- """Encodes multiview image batches into latent space.
153
-
154
- Args:
155
- images: Input images [B, N_views, C, H, W]
156
-
157
- Returns:
158
- torch.Tensor: Latent representations [B, N_views, C, H_latent, W_latent]
159
- """
160
-
161
- B = images.shape[0]
162
- images = rearrange(images, "b n c h w -> (b n) c h w")
163
-
164
- dtype = next(self.vae.parameters()).dtype
165
- images = (images - 0.5) * 2.0
166
- posterior = self.vae.encode(images.to(dtype)).latent_dist
167
- latents = posterior.sample() * self.vae.config.scaling_factor
168
-
169
- latents = rearrange(latents, "(b n) c h w -> b n c h w", b=B)
170
- return latents
171
-
172
- @torch.no_grad()
173
- def __call__(
174
- self,
175
- images=None,
176
- prompt=None,
177
- negative_prompt="watermark, ugly, deformed, noisy, blurry, low contrast",
178
- *args,
179
- num_images_per_prompt: Optional[int] = 1,
180
- guidance_scale=3.0,
181
- output_type: Optional[str] = "pil",
182
- width=512,
183
- height=512,
184
- num_inference_steps=15,
185
- return_dict=True,
186
- sync_condition=None,
187
- **cached_condition,
188
- ):
189
-
190
- """Main generation method for multiview PBR textures.
191
-
192
- Steps:
193
- 1. Input validation and preparation
194
- 2. Reference image encoding
195
- 3. Condition processing (normal/position maps)
196
- 4. Prompt embedding setup
197
- 5. Classifier-free guidance preparation
198
- 6. Diffusion sampling loop
199
-
200
- Args:
201
- images: List of reference PIL images
202
- prompt: Text prompt (overridden by learned embeddings)
203
- cached_condition: Dictionary containing:
204
- - images_normal: Normal maps (PIL or tensor)
205
- - images_position: Position maps (PIL or tensor)
206
-
207
- Returns:
208
- List[PIL.Image]: Generated multiview PBR textures
209
- """
210
-
211
- self.prepare()
212
- if images is None:
213
- raise ValueError("Inputting embeddings not supported for this pipeline. Please pass an image.")
214
- assert not isinstance(images, torch.Tensor)
215
-
216
- if not isinstance(images, List):
217
- images = [images]
218
-
219
- images = [to_rgb_image(image) for image in images]
220
- images_vae = [torch.tensor(np.array(image) / 255.0) for image in images]
221
- images_vae = [image_vae.unsqueeze(0).permute(0, 3, 1, 2).unsqueeze(0) for image_vae in images_vae]
222
- images_vae = torch.cat(images_vae, dim=1)
223
- images_vae = images_vae.to(device=self.vae.device, dtype=self.unet.dtype)
224
-
225
- batch_size = images_vae.shape[0]
226
- N_ref = images_vae.shape[1]
227
-
228
- assert batch_size == 1
229
- assert num_images_per_prompt == 1
230
-
231
- if self.unet.use_ra:
232
- ref_latents = self.encode_images(images_vae)
233
- cached_condition["ref_latents"] = ref_latents
234
-
235
- def convert_pil_list_to_tensor(images):
236
- bg_c = [1.0, 1.0, 1.0]
237
- images_tensor = []
238
- for batch_imgs in images:
239
- view_imgs = []
240
- for pil_img in batch_imgs:
241
- img = numpy.asarray(pil_img, dtype=numpy.float32) / 255.0
242
- if img.shape[2] > 3:
243
- alpha = img[:, :, 3:]
244
- img = img[:, :, :3] * alpha + bg_c * (1 - alpha)
245
- img = torch.from_numpy(img).permute(2, 0, 1).unsqueeze(0).contiguous().half().to("cuda")
246
- view_imgs.append(img)
247
- view_imgs = torch.cat(view_imgs, dim=0)
248
- images_tensor.append(view_imgs.unsqueeze(0))
249
-
250
- images_tensor = torch.cat(images_tensor, dim=0)
251
- return images_tensor
252
-
253
- if "images_normal" in cached_condition:
254
- if isinstance(cached_condition["images_normal"], List):
255
- cached_condition["images_normal"] = convert_pil_list_to_tensor(cached_condition["images_normal"])
256
-
257
- cached_condition["embeds_normal"] = self.encode_images(cached_condition["images_normal"])
258
-
259
- if "images_position" in cached_condition:
260
-
261
- if isinstance(cached_condition["images_position"], List):
262
- cached_condition["images_position"] = convert_pil_list_to_tensor(cached_condition["images_position"])
263
-
264
- cached_condition["position_maps"] = cached_condition["images_position"]
265
- cached_condition["embeds_position"] = self.encode_images(cached_condition["images_position"])
266
-
267
- if self.unet.use_learned_text_clip:
268
-
269
- all_shading_tokens = []
270
- for token in self.unet.pbr_setting:
271
- all_shading_tokens.append(
272
- getattr(self.unet, f"learned_text_clip_{token}").unsqueeze(dim=0).repeat(batch_size, 1, 1)
273
- )
274
- prompt_embeds = torch.stack(all_shading_tokens, dim=1)
275
- negative_prompt_embeds = torch.stack(all_shading_tokens, dim=1)
276
- # negative_prompt_embeds = torch.zeros_like(prompt_embeds)
277
-
278
- else:
279
- if prompt is None:
280
- prompt = "high quality"
281
- if isinstance(prompt, str):
282
- prompt = [prompt for _ in range(batch_size)]
283
- device = self._execution_device
284
- prompt_embeds, _ = self.encode_prompt(
285
- prompt, device=device, num_images_per_prompt=num_images_per_prompt, do_classifier_free_guidance=False
286
- )
287
-
288
- if isinstance(negative_prompt, str):
289
- negative_prompt = [negative_prompt for _ in range(batch_size)]
290
- if negative_prompt is not None:
291
- negative_prompt_embeds, _ = self.encode_prompt(
292
- negative_prompt,
293
- device=device,
294
- num_images_per_prompt=num_images_per_prompt,
295
- do_classifier_free_guidance=False,
296
- )
297
- else:
298
- negative_prompt_embeds = torch.zeros_like(prompt_embeds)
299
-
300
- if guidance_scale > 1:
301
- if self.unet.use_ra:
302
- cached_condition["ref_latents"] = cached_condition["ref_latents"].repeat(
303
- 3, *([1] * (cached_condition["ref_latents"].dim() - 1))
304
- )
305
- cached_condition["ref_scale"] = torch.as_tensor([0.0, 1.0, 1.0]).to(cached_condition["ref_latents"])
306
-
307
- if self.unet.use_dino:
308
- zero_states = torch.zeros_like(cached_condition["dino_hidden_states"])
309
- cached_condition["dino_hidden_states"] = torch.cat(
310
- [zero_states, zero_states, cached_condition["dino_hidden_states"]]
311
- )
312
-
313
- del zero_states
314
- if "embeds_normal" in cached_condition:
315
- cached_condition["embeds_normal"] = cached_condition["embeds_normal"].repeat(
316
- 3, *([1] * (cached_condition["embeds_normal"].dim() - 1))
317
- )
318
-
319
- if "embeds_position" in cached_condition:
320
- cached_condition["embeds_position"] = cached_condition["embeds_position"].repeat(
321
- 3, *([1] * (cached_condition["embeds_position"].dim() - 1))
322
- )
323
-
324
- if "position_maps" in cached_condition:
325
- cached_condition["position_maps"] = cached_condition["position_maps"].repeat(
326
- 3, *([1] * (cached_condition["position_maps"].dim() - 1))
327
- )
328
-
329
- images = self.denoise(
330
- None,
331
- *args,
332
- cross_attention_kwargs=None,
333
- guidance_scale=guidance_scale,
334
- num_images_per_prompt=num_images_per_prompt,
335
- prompt_embeds=prompt_embeds,
336
- negative_prompt_embeds=negative_prompt_embeds,
337
- num_inference_steps=num_inference_steps,
338
- output_type=output_type,
339
- width=width,
340
- height=height,
341
- return_dict=return_dict,
342
- **cached_condition,
343
- )
344
-
345
- return images
346
-
347
- def denoise(
348
- self,
349
- prompt: Union[str, List[str]] = None,
350
- height: Optional[int] = None,
351
- width: Optional[int] = None,
352
- num_inference_steps: int = 50,
353
- timesteps: List[int] = None,
354
- sigmas: List[float] = None,
355
- guidance_scale: float = 7.5,
356
- negative_prompt: Optional[Union[str, List[str]]] = None,
357
- num_images_per_prompt: Optional[int] = 1,
358
- eta: float = 0.0,
359
- generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
360
- latents: Optional[torch.Tensor] = None,
361
- prompt_embeds: Optional[torch.Tensor] = None,
362
- negative_prompt_embeds: Optional[torch.Tensor] = None,
363
- ip_adapter_image: Optional[PipelineImageInput] = None,
364
- ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
365
- output_type: Optional[str] = "pil",
366
- return_dict: bool = True,
367
- cross_attention_kwargs: Optional[Dict[str, Any]] = None,
368
- guidance_rescale: float = 0.0,
369
- clip_skip: Optional[int] = None,
370
- callback_on_step_end: Optional[
371
- Union[Callable[[int, int, Dict], None], PipelineCallback, MultiPipelineCallbacks]
372
- ] = None,
373
- callback_on_step_end_tensor_inputs: List[str] = ["latents"],
374
- **kwargs,
375
- ):
376
- r"""
377
- The call function to the pipeline for generation.
378
-
379
- Args:
380
- prompt (`str` or `List[str]`, *optional*):
381
- The prompt or prompts to guide image generation. If not defined, you need to pass `prompt_embeds`.
382
- height (`int`, *optional*, defaults to `self.unet.config.sample_size * self.vae_scale_factor`):
383
- The height in pixels of the generated image.
384
- width (`int`, *optional*, defaults to `self.unet.config.sample_size * self.vae_scale_factor`):
385
- The width in pixels of the generated image.
386
- num_inference_steps (`int`, *optional*, defaults to 50):
387
- The number of denoising steps. More denoising steps usually lead to a higher quality image at the
388
- expense of slower inference.
389
- timesteps (`List[int]`, *optional*):
390
- Custom timesteps to use for the denoising process with schedulers which support a `timesteps` argument
391
- in their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is
392
- passed will be used. Must be in descending order.
393
- sigmas (`List[float]`, *optional*):
394
- Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
395
- their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
396
- will be used.
397
- guidance_scale (`float`, *optional*, defaults to 7.5):
398
- A higher guidance scale value encourages the model to generate images closely linked to the text
399
- `prompt` at the expense of lower image quality. Guidance scale is enabled when `guidance_scale > 1`.
400
- negative_prompt (`str` or `List[str]`, *optional*):
401
- The prompt or prompts to guide what to not include in image generation. If not defined, you need to
402
- pass `negative_prompt_embeds` instead. Ignored when not using guidance (`guidance_scale < 1`).
403
- num_images_per_prompt (`int`, *optional*, defaults to 1):
404
- The number of images to generate per prompt.
405
- eta (`float`, *optional*, defaults to 0.0):
406
- Corresponds to parameter eta (η) from the [DDIM](https://arxiv.org/abs/2010.02502) paper. Only applies
407
- to the [`~schedulers.DDIMScheduler`], and is ignored in other schedulers.
408
- generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
409
- A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
410
- generation deterministic.
411
- latents (`torch.Tensor`, *optional*):
412
- Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
413
- generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
414
- tensor is generated by sampling using the supplied random `generator`.
415
- prompt_embeds (`torch.Tensor`, *optional*):
416
- Pre-generated text embeddings. Can be used to easily tweak text inputs (prompt weighting). If not
417
- provided, text embeddings are generated from the `prompt` input argument.
418
- negative_prompt_embeds (`torch.Tensor`, *optional*):
419
- Pre-generated negative text embeddings. Can be used to easily tweak text inputs (prompt weighting). If
420
- not provided, `negative_prompt_embeds` are generated from the `negative_prompt` input argument.
421
- ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
422
- ip_adapter_image_embeds (`List[torch.Tensor]`, *optional*):
423
- Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
424
- IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. It should
425
- contain the negative image embedding if `do_classifier_free_guidance` is set to `True`. If not
426
- provided, embeddings are computed from the `ip_adapter_image` input argument.
427
- output_type (`str`, *optional*, defaults to `"pil"`):
428
- The output format of the generated image. Choose between `PIL.Image` or `np.array`.
429
- return_dict (`bool`, *optional*, defaults to `True`):
430
- Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a
431
- plain tuple.
432
- cross_attention_kwargs (`dict`, *optional*):
433
- A kwargs dictionary that if specified is passed along to the [`AttentionProcessor`] as defined in
434
- [`self.processor`]
435
- (https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
436
- guidance_rescale (`float`, *optional*, defaults to 0.0):
437
- Guidance rescale factor from [Common Diffusion Noise Schedules and Sample Steps are
438
- Flawed](https://arxiv.org/pdf/2305.08891.pdf). Guidance rescale factor should fix overexposure when
439
- using zero terminal SNR.
440
- clip_skip (`int`, *optional*):
441
- Number of layers to be skipped from CLIP while computing the prompt embeddings. A value of 1 means that
442
- the output of the pre-final layer will be used for computing the prompt embeddings.
443
- callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
444
- A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
445
- each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
446
- DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
447
- list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
448
- callback_on_step_end_tensor_inputs (`List`, *optional*):
449
- The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
450
- will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
451
- `._callback_tensor_inputs` attribute of your pipeline class.
452
-
453
- Examples:
454
-
455
- Returns:
456
- [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] or `tuple`:
457
- If `return_dict` is `True`, [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] is returned,
458
- otherwise a `tuple` is returned where the first element is a list with the generated images and the
459
- second element is a list of `bool`s indicating whether the corresponding generated image contains
460
- "not-safe-for-work" (nsfw) content.
461
-
462
- Core denoising procedure for multiview PBR texture generation.
463
-
464
- Handles the complete diffusion process including:
465
- - Input validation and preparation
466
- - Timestep scheduling
467
- - Latent noise initialization
468
- - Iterative denoising with specialized guidance
469
- - Output decoding and post-processing
470
-
471
- Key innovations:
472
- 1. Triple-batch classifier-free guidance:
473
- - Negative (unconditional)
474
- - Reference-conditioned
475
- - Full-conditioned
476
- 2. View-dependent guidance scaling:
477
- - Adjusts influence based on camera azimuth
478
- 3. PBR-aware latent organization:
479
- - Maintains material/view separation throughout
480
- 4. Optimized VRAM management:
481
- - Selective tensor reshaping
482
-
483
- Processing Stages:
484
- 1. Setup & Validation: Configures pipeline components and validates inputs
485
- 2. Prompt Encoding: Processes text/material conditioning
486
- 3. Latent Initialization: Prepares noise for denoising process
487
- 4. Iterative Denoising:
488
- a) Scales and organizes latent variables
489
- b) Predicts noise at current timestep
490
- c) Applies view-dependent guidance
491
- d) Computes previous latent state
492
- 5. Output Decoding: Converts latents to final images
493
- 6. Cleanup: Releases resources and formats output
494
-
495
- """
496
-
497
- callback = kwargs.pop("callback", None)
498
- callback_steps = kwargs.pop("callback_steps", None)
499
-
500
- # open cache
501
- kwargs["cache"] = {}
502
-
503
- if callback is not None:
504
- deprecate(
505
- "callback",
506
- "1.0.0",
507
- "Passing `callback` as an input argument to `__call__` is deprecated,"
508
- "consider using `callback_on_step_end`",
509
- )
510
- if callback_steps is not None:
511
- deprecate(
512
- "callback_steps",
513
- "1.0.0",
514
- "Passing `callback` as an input argument to `__call__` is deprecated,"
515
- "consider using `callback_on_step_end`",
516
- )
517
-
518
- if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
519
- callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
520
-
521
- # 0. Default height and width to unet
522
- height = height or self.unet.config.sample_size * self.vae_scale_factor
523
- width = width or self.unet.config.sample_size * self.vae_scale_factor
524
- # to deal with lora scaling and other possible forward hooks
525
-
526
- # 1. Check inputs. Raise error if not correct
527
- self.check_inputs(
528
- prompt,
529
- height,
530
- width,
531
- callback_steps,
532
- negative_prompt,
533
- prompt_embeds,
534
- negative_prompt_embeds,
535
- ip_adapter_image,
536
- ip_adapter_image_embeds,
537
- callback_on_step_end_tensor_inputs,
538
- )
539
-
540
- self._guidance_scale = guidance_scale
541
- self._guidance_rescale = guidance_rescale
542
- self._clip_skip = clip_skip
543
- self._cross_attention_kwargs = cross_attention_kwargs
544
- self._interrupt = False
545
-
546
- # 2. Define call parameters
547
- if prompt is not None and isinstance(prompt, str):
548
- batch_size = 1
549
- elif prompt is not None and isinstance(prompt, list):
550
- batch_size = len(prompt)
551
- else:
552
- batch_size = prompt_embeds.shape[0]
553
-
554
- device = self._execution_device
555
-
556
- # 3. Encode input prompt
557
- lora_scale = self.cross_attention_kwargs.get("scale", None) if self.cross_attention_kwargs is not None else None
558
-
559
- """
560
- prompt_embeds, negative_prompt_embeds = self.encode_prompt(
561
- prompt,
562
- device,
563
- num_images_per_prompt,
564
- self.do_classifier_free_guidance,
565
- negative_prompt,
566
- prompt_embeds=prompt_embeds,
567
- negative_prompt_embeds=negative_prompt_embeds,
568
- lora_scale=lora_scale,
569
- clip_skip=self.clip_skip,
570
- )'
571
- """
572
-
573
- # For classifier free guidance, we need to do two forward passes.
574
- # Here we concatenate the unconditional and text embeddings into a single batch
575
- # to avoid doing two forward passes
576
- if self.do_classifier_free_guidance:
577
- # prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
578
- prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds, prompt_embeds])
579
-
580
- if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
581
- image_embeds = self.prepare_ip_adapter_image_embeds(
582
- ip_adapter_image,
583
- ip_adapter_image_embeds,
584
- device,
585
- batch_size * num_images_per_prompt,
586
- self.do_classifier_free_guidance,
587
- )
588
-
589
- # 4. Prepare timesteps
590
- timesteps, num_inference_steps = retrieve_timesteps(
591
- self.scheduler, num_inference_steps, device, timesteps, sigmas
592
- )
593
- assert num_images_per_prompt == 1
594
- # 5. Prepare latent variables
595
- n_pbr = len(self.unet.pbr_setting)
596
- num_channels_latents = self.unet.config.in_channels
597
- latents = self.prepare_latents(
598
- batch_size * kwargs["num_in_batch"] * n_pbr, # num_images_per_prompt,
599
- num_channels_latents,
600
- height,
601
- width,
602
- prompt_embeds.dtype,
603
- device,
604
- generator,
605
- latents,
606
- )
607
-
608
- # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
609
- extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
610
-
611
- # 6.1 Add image embeds for IP-Adapter
612
- added_cond_kwargs = (
613
- {"image_embeds": image_embeds}
614
- if (ip_adapter_image is not None or ip_adapter_image_embeds is not None)
615
- else None
616
- )
617
-
618
- # 6.2 Optionally get Guidance Scale Embedding
619
- timestep_cond = None
620
- if self.unet.config.time_cond_proj_dim is not None:
621
- guidance_scale_tensor = torch.tensor(self.guidance_scale - 1).repeat(batch_size * num_images_per_prompt)
622
- timestep_cond = self.get_guidance_scale_embedding(
623
- guidance_scale_tensor, embedding_dim=self.unet.config.time_cond_proj_dim
624
- ).to(device=device, dtype=latents.dtype)
625
-
626
- # 7. Denoising loop
627
- num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
628
- self._num_timesteps = len(timesteps)
629
- with self.progress_bar(total=num_inference_steps) as progress_bar:
630
- for i, t in enumerate(timesteps):
631
- if self.interrupt:
632
- continue
633
-
634
- # expand the latents if we are doing classifier free guidance
635
- latents = rearrange(
636
- latents, "(b n_pbr n) c h w -> b n_pbr n c h w", n=kwargs["num_in_batch"], n_pbr=n_pbr
637
- )
638
- # latent_model_input = torch.cat([latents] * 3) if self.do_classifier_free_guidance else latents
639
- latent_model_input = latents.repeat(3, 1, 1, 1, 1, 1) if self.do_classifier_free_guidance else latents
640
- latent_model_input = rearrange(latent_model_input, "b n_pbr n c h w -> (b n_pbr n) c h w")
641
- latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
642
- latent_model_input = rearrange(
643
- latent_model_input, "(b n_pbr n) c h w ->b n_pbr n c h w", n=kwargs["num_in_batch"], n_pbr=n_pbr
644
- )
645
-
646
- # predict the noise residual
647
-
648
- noise_pred = self.unet(
649
- latent_model_input,
650
- t,
651
- encoder_hidden_states=prompt_embeds,
652
- timestep_cond=timestep_cond,
653
- cross_attention_kwargs=self.cross_attention_kwargs,
654
- added_cond_kwargs=added_cond_kwargs,
655
- return_dict=False,
656
- **kwargs,
657
- )[0]
658
- latents = rearrange(latents, "b n_pbr n c h w -> (b n_pbr n) c h w")
659
- # perform guidance
660
- if self.do_classifier_free_guidance:
661
- # noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
662
- # noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
663
- noise_pred_uncond, noise_pred_ref, noise_pred_full = noise_pred.chunk(3)
664
-
665
- if "camera_azims" in kwargs.keys():
666
- camera_azims = kwargs["camera_azims"]
667
- else:
668
- camera_azims = [0] * kwargs["num_in_batch"]
669
-
670
- def cam_mapping(azim):
671
- if azim < 90 and azim >= 0:
672
- return float(azim) / 90.0 + 1
673
- elif azim >= 90 and azim < 330:
674
- return 2.0
675
- else:
676
- return -float(azim) / 90.0 + 5.0
677
-
678
- view_scale_tensor = (
679
- torch.from_numpy(np.asarray([cam_mapping(azim) for azim in camera_azims]))
680
- .unsqueeze(0)
681
- .repeat(n_pbr, 1)
682
- .view(-1)
683
- .to(noise_pred_uncond)[:, None, None, None]
684
- )
685
- noise_pred = noise_pred_uncond + self.guidance_scale * view_scale_tensor * (
686
- noise_pred_ref - noise_pred_uncond
687
- )
688
- noise_pred += self.guidance_scale * view_scale_tensor * (noise_pred_full - noise_pred_ref)
689
-
690
- if self.do_classifier_free_guidance and self.guidance_rescale > 0.0:
691
- # Based on 3.4. in https://arxiv.org/pdf/2305.08891.pdf
692
- noise_pred = rescale_noise_cfg(noise_pred, noise_pred_ref, guidance_rescale=self.guidance_rescale)
693
-
694
- # compute the previous noisy sample x_t -> x_t-1
695
- latents = self.scheduler.step(
696
- noise_pred, t, latents[:, :num_channels_latents, :, :], **extra_step_kwargs, return_dict=False
697
- )[0]
698
-
699
- if callback_on_step_end is not None:
700
- callback_kwargs = {}
701
- for k in callback_on_step_end_tensor_inputs:
702
- callback_kwargs[k] = locals()[k]
703
- callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
704
-
705
- latents = callback_outputs.pop("latents", latents)
706
- prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
707
- negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
708
-
709
- # call the callback, if provided
710
- if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
711
- progress_bar.update()
712
- if callback is not None and i % callback_steps == 0:
713
- step_idx = i // getattr(self.scheduler, "order", 1)
714
- callback(step_idx, t, latents)
715
-
716
- if not output_type == "latent":
717
- image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False, generator=generator)[0]
718
- image, has_nsfw_concept = self.run_safety_checker(image, device, prompt_embeds.dtype)
719
- else:
720
- image = latents
721
- has_nsfw_concept = None
722
-
723
- if has_nsfw_concept is None:
724
- do_denormalize = [True] * image.shape[0]
725
- else:
726
- do_denormalize = [not has_nsfw for has_nsfw in has_nsfw_concept]
727
-
728
- image = self.image_processor.postprocess(image, output_type=output_type, do_denormalize=do_denormalize)
729
-
730
- # Offload all models
731
- self.maybe_free_model_hooks()
732
-
733
- if not return_dict:
734
- return (image, has_nsfw_concept)
735
-
736
- return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=has_nsfw_concept)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/hunyuanpaintpbr/unet/attn_processor.py DELETED
@@ -1,839 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import torch
16
- import torch.nn as nn
17
- import torch.nn.functional as F
18
- from typing import Optional, Dict, Tuple, Union, Literal, List, Callable
19
- from einops import rearrange
20
- from diffusers.utils import deprecate
21
- from diffusers.models.attention_processor import Attention, AttnProcessor
22
-
23
-
24
- class AttnUtils:
25
- """
26
- Shared utility functions for attention processing.
27
-
28
- This class provides common operations used across different attention processors
29
- to eliminate code duplication and improve maintainability.
30
- """
31
-
32
- @staticmethod
33
- def check_pytorch_compatibility():
34
- """
35
- Check PyTorch compatibility for scaled_dot_product_attention.
36
-
37
- Raises:
38
- ImportError: If PyTorch version doesn't support scaled_dot_product_attention
39
- """
40
- if not hasattr(F, "scaled_dot_product_attention"):
41
- raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.")
42
-
43
- @staticmethod
44
- def handle_deprecation_warning(args, kwargs):
45
- """
46
- Handle deprecation warning for the 'scale' argument.
47
-
48
- Args:
49
- args: Positional arguments passed to attention processor
50
- kwargs: Keyword arguments passed to attention processor
51
- """
52
- if len(args) > 0 or kwargs.get("scale", None) is not None:
53
- deprecation_message = (
54
- "The `scale` argument is deprecated and will be ignored."
55
- "Please remove it, as passing it will raise an error in the future."
56
- "`scale` should directly be passed while calling the underlying pipeline component"
57
- "i.e., via `cross_attention_kwargs`."
58
- )
59
- deprecate("scale", "1.0.0", deprecation_message)
60
-
61
- @staticmethod
62
- def prepare_hidden_states(
63
- hidden_states, attn, temb, spatial_norm_attr="spatial_norm", group_norm_attr="group_norm"
64
- ):
65
- """
66
- Common preprocessing of hidden states for attention computation.
67
-
68
- Args:
69
- hidden_states: Input hidden states tensor
70
- attn: Attention module instance
71
- temb: Optional temporal embedding tensor
72
- spatial_norm_attr: Attribute name for spatial normalization
73
- group_norm_attr: Attribute name for group normalization
74
-
75
- Returns:
76
- Tuple of (processed_hidden_states, residual, input_ndim, shape_info)
77
- """
78
- residual = hidden_states
79
-
80
- spatial_norm = getattr(attn, spatial_norm_attr, None)
81
- if spatial_norm is not None:
82
- hidden_states = spatial_norm(hidden_states, temb)
83
-
84
- input_ndim = hidden_states.ndim
85
-
86
- if input_ndim == 4:
87
- batch_size, channel, height, width = hidden_states.shape
88
- hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2)
89
- else:
90
- batch_size, channel, height, width = None, None, None, None
91
-
92
- group_norm = getattr(attn, group_norm_attr, None)
93
- if group_norm is not None:
94
- hidden_states = group_norm(hidden_states.transpose(1, 2)).transpose(1, 2)
95
-
96
- return hidden_states, residual, input_ndim, (batch_size, channel, height, width)
97
-
98
- @staticmethod
99
- def prepare_attention_mask(attention_mask, attn, sequence_length, batch_size):
100
- """
101
- Prepare attention mask for scaled_dot_product_attention.
102
-
103
- Args:
104
- attention_mask: Input attention mask tensor or None
105
- attn: Attention module instance
106
- sequence_length: Length of the sequence
107
- batch_size: Batch size
108
-
109
- Returns:
110
- Prepared attention mask tensor reshaped for multi-head attention
111
- """
112
- if attention_mask is not None:
113
- attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size)
114
- attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1])
115
- return attention_mask
116
-
117
- @staticmethod
118
- def reshape_qkv_for_attention(tensor, batch_size, attn_heads, head_dim):
119
- """
120
- Reshape Q/K/V tensors for multi-head attention computation.
121
-
122
- Args:
123
- tensor: Input tensor to reshape
124
- batch_size: Batch size
125
- attn_heads: Number of attention heads
126
- head_dim: Dimension per attention head
127
-
128
- Returns:
129
- Reshaped tensor with shape [batch_size, attn_heads, seq_len, head_dim]
130
- """
131
- return tensor.view(batch_size, -1, attn_heads, head_dim).transpose(1, 2)
132
-
133
- @staticmethod
134
- def apply_norms(query, key, norm_q, norm_k):
135
- """
136
- Apply Q/K normalization layers if available.
137
-
138
- Args:
139
- query: Query tensor
140
- key: Key tensor
141
- norm_q: Query normalization layer (optional)
142
- norm_k: Key normalization layer (optional)
143
-
144
- Returns:
145
- Tuple of (normalized_query, normalized_key)
146
- """
147
- if norm_q is not None:
148
- query = norm_q(query)
149
- if norm_k is not None:
150
- key = norm_k(key)
151
- return query, key
152
-
153
- @staticmethod
154
- def finalize_output(hidden_states, input_ndim, shape_info, attn, residual, to_out):
155
- """
156
- Common output processing including projection, dropout, reshaping, and residual connection.
157
-
158
- Args:
159
- hidden_states: Processed hidden states from attention
160
- input_ndim: Original input tensor dimensions
161
- shape_info: Tuple containing original shape information
162
- attn: Attention module instance
163
- residual: Residual connection tensor
164
- to_out: Output projection layers [linear, dropout]
165
-
166
- Returns:
167
- Final output tensor after all processing steps
168
- """
169
- batch_size, channel, height, width = shape_info
170
-
171
- # Apply output projection and dropout
172
- hidden_states = to_out[0](hidden_states)
173
- hidden_states = to_out[1](hidden_states)
174
-
175
- # Reshape back if needed
176
- if input_ndim == 4:
177
- hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width)
178
-
179
- # Apply residual connection
180
- if attn.residual_connection:
181
- hidden_states = hidden_states + residual
182
-
183
- # Apply rescaling
184
- hidden_states = hidden_states / attn.rescale_output_factor
185
- return hidden_states
186
-
187
-
188
- # Base class for attention processors (eliminating initialization duplication)
189
- class BaseAttnProcessor(nn.Module):
190
- """
191
- Base class for attention processors with common initialization.
192
-
193
- This base class provides shared parameter initialization and module registration
194
- functionality to reduce code duplication across different attention processor types.
195
- """
196
-
197
- def __init__(
198
- self,
199
- query_dim: int,
200
- pbr_setting: List[str] = ["albedo", "mr"],
201
- cross_attention_dim: Optional[int] = None,
202
- heads: int = 8,
203
- kv_heads: Optional[int] = None,
204
- dim_head: int = 64,
205
- dropout: float = 0.0,
206
- bias: bool = False,
207
- upcast_attention: bool = False,
208
- upcast_softmax: bool = False,
209
- cross_attention_norm: Optional[str] = None,
210
- cross_attention_norm_num_groups: int = 32,
211
- qk_norm: Optional[str] = None,
212
- added_kv_proj_dim: Optional[int] = None,
213
- added_proj_bias: Optional[bool] = True,
214
- norm_num_groups: Optional[int] = None,
215
- spatial_norm_dim: Optional[int] = None,
216
- out_bias: bool = True,
217
- scale_qk: bool = True,
218
- only_cross_attention: bool = False,
219
- eps: float = 1e-5,
220
- rescale_output_factor: float = 1.0,
221
- residual_connection: bool = False,
222
- _from_deprecated_attn_block: bool = False,
223
- processor: Optional["AttnProcessor"] = None,
224
- out_dim: int = None,
225
- out_context_dim: int = None,
226
- context_pre_only=None,
227
- pre_only=False,
228
- elementwise_affine: bool = True,
229
- is_causal: bool = False,
230
- **kwargs,
231
- ):
232
- """
233
- Initialize base attention processor with common parameters.
234
-
235
- Args:
236
- query_dim: Dimension of query features
237
- pbr_setting: List of PBR material types to process (e.g., ["albedo", "mr"])
238
- cross_attention_dim: Dimension of cross-attention features (optional)
239
- heads: Number of attention heads
240
- kv_heads: Number of key-value heads for grouped query attention (optional)
241
- dim_head: Dimension per attention head
242
- dropout: Dropout rate
243
- bias: Whether to use bias in linear projections
244
- upcast_attention: Whether to upcast attention computation to float32
245
- upcast_softmax: Whether to upcast softmax computation to float32
246
- cross_attention_norm: Type of cross-attention normalization (optional)
247
- cross_attention_norm_num_groups: Number of groups for cross-attention norm
248
- qk_norm: Type of query-key normalization (optional)
249
- added_kv_proj_dim: Dimension for additional key-value projections (optional)
250
- added_proj_bias: Whether to use bias in additional projections
251
- norm_num_groups: Number of groups for normalization (optional)
252
- spatial_norm_dim: Dimension for spatial normalization (optional)
253
- out_bias: Whether to use bias in output projection
254
- scale_qk: Whether to scale query-key products
255
- only_cross_attention: Whether to only perform cross-attention
256
- eps: Small epsilon value for numerical stability
257
- rescale_output_factor: Factor to rescale output values
258
- residual_connection: Whether to use residual connections
259
- _from_deprecated_attn_block: Flag for deprecated attention blocks
260
- processor: Optional attention processor instance
261
- out_dim: Output dimension (optional)
262
- out_context_dim: Output context dimension (optional)
263
- context_pre_only: Whether to only process context in pre-processing
264
- pre_only: Whether to only perform pre-processing
265
- elementwise_affine: Whether to use element-wise affine transformations
266
- is_causal: Whether to use causal attention masking
267
- **kwargs: Additional keyword arguments
268
- """
269
- super().__init__()
270
- AttnUtils.check_pytorch_compatibility()
271
-
272
- # Store common attributes
273
- self.pbr_setting = pbr_setting
274
- self.n_pbr_tokens = len(self.pbr_setting)
275
- self.inner_dim = out_dim if out_dim is not None else dim_head * heads
276
- self.inner_kv_dim = self.inner_dim if kv_heads is None else dim_head * kv_heads
277
- self.query_dim = query_dim
278
- self.use_bias = bias
279
- self.is_cross_attention = cross_attention_dim is not None
280
- self.cross_attention_dim = cross_attention_dim if cross_attention_dim is not None else query_dim
281
- self.upcast_attention = upcast_attention
282
- self.upcast_softmax = upcast_softmax
283
- self.rescale_output_factor = rescale_output_factor
284
- self.residual_connection = residual_connection
285
- self.dropout = dropout
286
- self.fused_projections = False
287
- self.out_dim = out_dim if out_dim is not None else query_dim
288
- self.out_context_dim = out_context_dim if out_context_dim is not None else query_dim
289
- self.context_pre_only = context_pre_only
290
- self.pre_only = pre_only
291
- self.is_causal = is_causal
292
- self._from_deprecated_attn_block = _from_deprecated_attn_block
293
- self.scale_qk = scale_qk
294
- self.scale = dim_head**-0.5 if self.scale_qk else 1.0
295
- self.heads = out_dim // dim_head if out_dim is not None else heads
296
- self.sliceable_head_dim = heads
297
- self.added_kv_proj_dim = added_kv_proj_dim
298
- self.only_cross_attention = only_cross_attention
299
- self.added_proj_bias = added_proj_bias
300
-
301
- # Validation
302
- if self.added_kv_proj_dim is None and self.only_cross_attention:
303
- raise ValueError(
304
- "`only_cross_attention` can only be set to True if `added_kv_proj_dim` is not None."
305
- "Make sure to set either `only_cross_attention=False` or define `added_kv_proj_dim`."
306
- )
307
-
308
- def register_pbr_modules(self, module_types: List[str], **kwargs):
309
- """
310
- Generic PBR module registration to eliminate code repetition.
311
-
312
- Dynamically registers PyTorch modules for different PBR material types
313
- based on the specified module types and PBR settings.
314
-
315
- Args:
316
- module_types: List of module types to register ("qkv", "v_only", "out", "add_kv")
317
- **kwargs: Additional arguments for module configuration
318
- """
319
- for pbr_token in self.pbr_setting:
320
- if pbr_token == "albedo":
321
- continue
322
-
323
- for module_type in module_types:
324
- if module_type == "qkv":
325
- self.register_module(
326
- f"to_q_{pbr_token}", nn.Linear(self.query_dim, self.inner_dim, bias=self.use_bias)
327
- )
328
- self.register_module(
329
- f"to_k_{pbr_token}", nn.Linear(self.cross_attention_dim, self.inner_dim, bias=self.use_bias)
330
- )
331
- self.register_module(
332
- f"to_v_{pbr_token}", nn.Linear(self.cross_attention_dim, self.inner_dim, bias=self.use_bias)
333
- )
334
- elif module_type == "v_only":
335
- self.register_module(
336
- f"to_v_{pbr_token}", nn.Linear(self.cross_attention_dim, self.inner_dim, bias=self.use_bias)
337
- )
338
- elif module_type == "out":
339
- if not self.pre_only:
340
- self.register_module(
341
- f"to_out_{pbr_token}",
342
- nn.ModuleList(
343
- [
344
- nn.Linear(self.inner_dim, self.out_dim, bias=kwargs.get("out_bias", True)),
345
- nn.Dropout(self.dropout),
346
- ]
347
- ),
348
- )
349
- else:
350
- self.register_module(f"to_out_{pbr_token}", None)
351
- elif module_type == "add_kv":
352
- if self.added_kv_proj_dim is not None:
353
- self.register_module(
354
- f"add_k_proj_{pbr_token}",
355
- nn.Linear(self.added_kv_proj_dim, self.inner_kv_dim, bias=self.added_proj_bias),
356
- )
357
- self.register_module(
358
- f"add_v_proj_{pbr_token}",
359
- nn.Linear(self.added_kv_proj_dim, self.inner_kv_dim, bias=self.added_proj_bias),
360
- )
361
- else:
362
- self.register_module(f"add_k_proj_{pbr_token}", None)
363
- self.register_module(f"add_v_proj_{pbr_token}", None)
364
-
365
-
366
- # Rotary Position Embedding utilities (specialized for PoseRoPE)
367
- class RotaryEmbedding:
368
- """
369
- Rotary position embedding utilities for 3D spatial attention.
370
-
371
- Provides functions to compute and apply rotary position embeddings (RoPE)
372
- for 1D, 3D spatial coordinates used in 3D-aware attention mechanisms.
373
- """
374
-
375
- @staticmethod
376
- def get_1d_rotary_pos_embed(dim: int, pos: torch.Tensor, theta: float = 10000.0, linear_factor=1.0, ntk_factor=1.0):
377
- """
378
- Compute 1D rotary position embeddings.
379
-
380
- Args:
381
- dim: Embedding dimension (must be even)
382
- pos: Position tensor
383
- theta: Base frequency for rotary embeddings
384
- linear_factor: Linear scaling factor
385
- ntk_factor: NTK (Neural Tangent Kernel) scaling factor
386
-
387
- Returns:
388
- Tuple of (cos_embeddings, sin_embeddings)
389
- """
390
- assert dim % 2 == 0
391
- theta = theta * ntk_factor
392
- freqs = (
393
- 1.0
394
- / (theta ** (torch.arange(0, dim, 2, dtype=pos.dtype, device=pos.device)[: (dim // 2)] / dim))
395
- / linear_factor
396
- )
397
- freqs = torch.outer(pos, freqs)
398
- freqs_cos = freqs.cos().repeat_interleave(2, dim=1).float()
399
- freqs_sin = freqs.sin().repeat_interleave(2, dim=1).float()
400
- return freqs_cos, freqs_sin
401
-
402
- @staticmethod
403
- def get_3d_rotary_pos_embed(position, embed_dim, voxel_resolution, theta: int = 10000):
404
- """
405
- Compute 3D rotary position embeddings for spatial coordinates.
406
-
407
- Args:
408
- position: 3D position tensor with shape [..., 3]
409
- embed_dim: Embedding dimension
410
- voxel_resolution: Resolution of the voxel grid
411
- theta: Base frequency for rotary embeddings
412
-
413
- Returns:
414
- Tuple of (cos_embeddings, sin_embeddings) for 3D positions
415
- """
416
- assert position.shape[-1] == 3
417
- dim_xy = embed_dim // 8 * 3
418
- dim_z = embed_dim // 8 * 2
419
-
420
- grid = torch.arange(voxel_resolution, dtype=torch.float32, device=position.device)
421
- freqs_xy = RotaryEmbedding.get_1d_rotary_pos_embed(dim_xy, grid, theta=theta)
422
- freqs_z = RotaryEmbedding.get_1d_rotary_pos_embed(dim_z, grid, theta=theta)
423
-
424
- xy_cos, xy_sin = freqs_xy
425
- z_cos, z_sin = freqs_z
426
-
427
- embed_flattn = position.view(-1, position.shape[-1])
428
- x_cos = xy_cos[embed_flattn[:, 0], :]
429
- x_sin = xy_sin[embed_flattn[:, 0], :]
430
- y_cos = xy_cos[embed_flattn[:, 1], :]
431
- y_sin = xy_sin[embed_flattn[:, 1], :]
432
- z_cos = z_cos[embed_flattn[:, 2], :]
433
- z_sin = z_sin[embed_flattn[:, 2], :]
434
-
435
- cos = torch.cat((x_cos, y_cos, z_cos), dim=-1)
436
- sin = torch.cat((x_sin, y_sin, z_sin), dim=-1)
437
-
438
- cos = cos.view(*position.shape[:-1], embed_dim)
439
- sin = sin.view(*position.shape[:-1], embed_dim)
440
- return cos, sin
441
-
442
- @staticmethod
443
- def apply_rotary_emb(x: torch.Tensor, freqs_cis: Union[torch.Tensor, Tuple[torch.Tensor]]):
444
- """
445
- Apply rotary position embeddings to input tensor.
446
-
447
- Args:
448
- x: Input tensor to apply rotary embeddings to
449
- freqs_cis: Tuple of (cos_embeddings, sin_embeddings) or single tensor
450
-
451
- Returns:
452
- Tensor with rotary position embeddings applied
453
- """
454
- cos, sin = freqs_cis
455
- cos, sin = cos.to(x.device), sin.to(x.device)
456
- cos = cos.unsqueeze(1)
457
- sin = sin.unsqueeze(1)
458
-
459
- x_real, x_imag = x.reshape(*x.shape[:-1], -1, 2).unbind(-1)
460
- x_rotated = torch.stack([-x_imag, x_real], dim=-1).flatten(3)
461
-
462
- out = (x.float() * cos + x_rotated.float() * sin).to(x.dtype)
463
- return out
464
-
465
-
466
- # Core attention processing logic (eliminating major duplication)
467
- class AttnCore:
468
- """
469
- Core attention processing logic shared across processors.
470
-
471
- This class provides the fundamental attention computation pipeline
472
- that can be reused across different attention processor implementations.
473
- """
474
-
475
- @staticmethod
476
- def process_attention_base(
477
- attn: Attention,
478
- hidden_states: torch.Tensor,
479
- encoder_hidden_states: Optional[torch.Tensor] = None,
480
- attention_mask: Optional[torch.Tensor] = None,
481
- temb: Optional[torch.Tensor] = None,
482
- get_qkv_fn: Callable = None,
483
- apply_rope_fn: Optional[Callable] = None,
484
- **kwargs,
485
- ):
486
- """
487
- Generic attention processing core shared across different processors.
488
-
489
- This function implements the common attention computation pipeline including:
490
- 1. Hidden state preprocessing
491
- 2. Attention mask preparation
492
- 3. Q/K/V computation via provided function
493
- 4. Tensor reshaping for multi-head attention
494
- 5. Optional normalization and RoPE application
495
- 6. Scaled dot-product attention computation
496
-
497
- Args:
498
- attn: Attention module instance
499
- hidden_states: Input hidden states tensor
500
- encoder_hidden_states: Optional encoder hidden states for cross-attention
501
- attention_mask: Optional attention mask tensor
502
- temb: Optional temporal embedding tensor
503
- get_qkv_fn: Function to compute Q, K, V tensors
504
- apply_rope_fn: Optional function to apply rotary position embeddings
505
- **kwargs: Additional keyword arguments passed to subfunctions
506
-
507
- Returns:
508
- Tuple containing (attention_output, residual, input_ndim, shape_info,
509
- batch_size, num_heads, head_dim)
510
- """
511
- # Prepare hidden states
512
- hidden_states, residual, input_ndim, shape_info = AttnUtils.prepare_hidden_states(hidden_states, attn, temb)
513
-
514
- batch_size, sequence_length, _ = (
515
- hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape
516
- )
517
-
518
- # Prepare attention mask
519
- attention_mask = AttnUtils.prepare_attention_mask(attention_mask, attn, sequence_length, batch_size)
520
-
521
- # Get Q, K, V
522
- if encoder_hidden_states is None:
523
- encoder_hidden_states = hidden_states
524
- elif attn.norm_cross:
525
- encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states)
526
-
527
- query, key, value = get_qkv_fn(attn, hidden_states, encoder_hidden_states, **kwargs)
528
-
529
- # Reshape for attention
530
- inner_dim = key.shape[-1]
531
- head_dim = inner_dim // attn.heads
532
-
533
- query = AttnUtils.reshape_qkv_for_attention(query, batch_size, attn.heads, head_dim)
534
- key = AttnUtils.reshape_qkv_for_attention(key, batch_size, attn.heads, head_dim)
535
- value = AttnUtils.reshape_qkv_for_attention(value, batch_size, attn.heads, value.shape[-1] // attn.heads)
536
-
537
- # Apply normalization
538
- query, key = AttnUtils.apply_norms(query, key, getattr(attn, "norm_q", None), getattr(attn, "norm_k", None))
539
-
540
- # Apply RoPE if provided
541
- if apply_rope_fn is not None:
542
- query, key = apply_rope_fn(query, key, head_dim, **kwargs)
543
-
544
- # Compute attention
545
- hidden_states = F.scaled_dot_product_attention(
546
- query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False
547
- )
548
-
549
- return hidden_states, residual, input_ndim, shape_info, batch_size, attn.heads, head_dim
550
-
551
-
552
- # Specific processor implementations (minimal unique code)
553
- class PoseRoPEAttnProcessor2_0:
554
- """
555
- Attention processor with Rotary Position Encoding (RoPE) for 3D spatial awareness.
556
-
557
- This processor extends standard attention with 3D rotary position embeddings
558
- to provide spatial awareness for 3D scene understanding tasks.
559
- """
560
-
561
- def __init__(self):
562
- """Initialize the RoPE attention processor."""
563
- AttnUtils.check_pytorch_compatibility()
564
-
565
- def __call__(
566
- self,
567
- attn: Attention,
568
- hidden_states: torch.Tensor,
569
- encoder_hidden_states: Optional[torch.Tensor] = None,
570
- attention_mask: Optional[torch.Tensor] = None,
571
- position_indices: Dict = None,
572
- temb: Optional[torch.Tensor] = None,
573
- n_pbrs=1,
574
- *args,
575
- **kwargs,
576
- ) -> torch.Tensor:
577
- """
578
- Apply RoPE-enhanced attention computation.
579
-
580
- Args:
581
- attn: Attention module instance
582
- hidden_states: Input hidden states tensor
583
- encoder_hidden_states: Optional encoder hidden states for cross-attention
584
- attention_mask: Optional attention mask tensor
585
- position_indices: Dictionary containing 3D position information for RoPE
586
- temb: Optional temporal embedding tensor
587
- n_pbrs: Number of PBR material types
588
- *args: Additional positional arguments
589
- **kwargs: Additional keyword arguments
590
-
591
- Returns:
592
- Attention output tensor with applied rotary position encodings
593
- """
594
- AttnUtils.handle_deprecation_warning(args, kwargs)
595
-
596
- def get_qkv(attn, hidden_states, encoder_hidden_states, **kwargs):
597
- return attn.to_q(hidden_states), attn.to_k(encoder_hidden_states), attn.to_v(encoder_hidden_states)
598
-
599
- def apply_rope(query, key, head_dim, **kwargs):
600
- if position_indices is not None:
601
- if head_dim in position_indices:
602
- image_rotary_emb = position_indices[head_dim]
603
- else:
604
- image_rotary_emb = RotaryEmbedding.get_3d_rotary_pos_embed(
605
- rearrange(
606
- position_indices["voxel_indices"].unsqueeze(1).repeat(1, n_pbrs, 1, 1),
607
- "b n_pbrs l c -> (b n_pbrs) l c",
608
- ),
609
- head_dim,
610
- voxel_resolution=position_indices["voxel_resolution"],
611
- )
612
- position_indices[head_dim] = image_rotary_emb
613
-
614
- query = RotaryEmbedding.apply_rotary_emb(query, image_rotary_emb)
615
- key = RotaryEmbedding.apply_rotary_emb(key, image_rotary_emb)
616
- return query, key
617
-
618
- # Core attention processing
619
- hidden_states, residual, input_ndim, shape_info, batch_size, heads, head_dim = AttnCore.process_attention_base(
620
- attn,
621
- hidden_states,
622
- encoder_hidden_states,
623
- attention_mask,
624
- temb,
625
- get_qkv_fn=get_qkv,
626
- apply_rope_fn=apply_rope,
627
- position_indices=position_indices,
628
- n_pbrs=n_pbrs,
629
- )
630
-
631
- # Finalize output
632
- hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, heads * head_dim)
633
- hidden_states = hidden_states.to(hidden_states.dtype)
634
-
635
- return AttnUtils.finalize_output(hidden_states, input_ndim, shape_info, attn, residual, attn.to_out)
636
-
637
-
638
- class SelfAttnProcessor2_0(BaseAttnProcessor):
639
- """
640
- Self-attention processor with PBR (Physically Based Rendering) material support.
641
-
642
- This processor handles multiple PBR material types (e.g., albedo, metallic-roughness)
643
- with separate attention computation paths for each material type.
644
- """
645
-
646
- def __init__(self, **kwargs):
647
- """
648
- Initialize self-attention processor with PBR support.
649
-
650
- Args:
651
- **kwargs: Arguments passed to BaseAttnProcessor initialization
652
- """
653
- super().__init__(**kwargs)
654
- self.register_pbr_modules(["qkv", "out", "add_kv"], **kwargs)
655
-
656
- def process_single(
657
- self,
658
- attn: Attention,
659
- hidden_states: torch.Tensor,
660
- encoder_hidden_states: Optional[torch.Tensor] = None,
661
- attention_mask: Optional[torch.Tensor] = None,
662
- temb: Optional[torch.Tensor] = None,
663
- token: Literal["albedo", "mr"] = "albedo",
664
- multiple_devices=False,
665
- *args,
666
- **kwargs,
667
- ):
668
- """
669
- Process attention for a single PBR material type.
670
-
671
- Args:
672
- attn: Attention module instance
673
- hidden_states: Input hidden states tensor
674
- encoder_hidden_states: Optional encoder hidden states for cross-attention
675
- attention_mask: Optional attention mask tensor
676
- temb: Optional temporal embedding tensor
677
- token: PBR material type to process ("albedo", "mr", etc.)
678
- multiple_devices: Whether to use multiple GPU devices
679
- *args: Additional positional arguments
680
- **kwargs: Additional keyword arguments
681
-
682
- Returns:
683
- Processed attention output for the specified PBR material type
684
- """
685
- target = attn if token == "albedo" else attn.processor
686
- token_suffix = "" if token == "albedo" else "_" + token
687
-
688
- # Device management (if needed)
689
- if multiple_devices:
690
- device = torch.device("cuda:0") if token == "albedo" else torch.device("cuda:1")
691
- for attr in [f"to_q{token_suffix}", f"to_k{token_suffix}", f"to_v{token_suffix}", f"to_out{token_suffix}"]:
692
- getattr(target, attr).to(device)
693
-
694
- def get_qkv(attn, hidden_states, encoder_hidden_states, **kwargs):
695
- return (
696
- getattr(target, f"to_q{token_suffix}")(hidden_states),
697
- getattr(target, f"to_k{token_suffix}")(encoder_hidden_states),
698
- getattr(target, f"to_v{token_suffix}")(encoder_hidden_states),
699
- )
700
-
701
- # Core processing using shared logic
702
- hidden_states, residual, input_ndim, shape_info, batch_size, heads, head_dim = AttnCore.process_attention_base(
703
- attn, hidden_states, encoder_hidden_states, attention_mask, temb, get_qkv_fn=get_qkv
704
- )
705
-
706
- # Finalize
707
- hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, heads * head_dim)
708
- hidden_states = hidden_states.to(hidden_states.dtype)
709
-
710
- return AttnUtils.finalize_output(
711
- hidden_states, input_ndim, shape_info, attn, residual, getattr(target, f"to_out{token_suffix}")
712
- )
713
-
714
- def __call__(
715
- self,
716
- attn: Attention,
717
- hidden_states: torch.Tensor,
718
- encoder_hidden_states: Optional[torch.Tensor] = None,
719
- attention_mask: Optional[torch.Tensor] = None,
720
- temb: Optional[torch.Tensor] = None,
721
- *args,
722
- **kwargs,
723
- ) -> torch.Tensor:
724
- """
725
- Apply self-attention with PBR material processing.
726
-
727
- Processes multiple PBR material types sequentially, applying attention
728
- computation for each material type separately and combining results.
729
-
730
- Args:
731
- attn: Attention module instance
732
- hidden_states: Input hidden states tensor with PBR dimension
733
- encoder_hidden_states: Optional encoder hidden states for cross-attention
734
- attention_mask: Optional attention mask tensor
735
- temb: Optional temporal embedding tensor
736
- *args: Additional positional arguments
737
- **kwargs: Additional keyword arguments
738
-
739
- Returns:
740
- Combined attention output for all PBR material types
741
- """
742
- AttnUtils.handle_deprecation_warning(args, kwargs)
743
-
744
- B = hidden_states.size(0)
745
- pbr_hidden_states = torch.split(hidden_states, 1, dim=1)
746
-
747
- # Process each PBR setting
748
- results = []
749
- for token, pbr_hs in zip(self.pbr_setting, pbr_hidden_states):
750
- processed_hs = rearrange(pbr_hs, "b n_pbrs n l c -> (b n_pbrs n) l c").to("cuda:0")
751
- result = self.process_single(attn, processed_hs, None, attention_mask, temb, token, False)
752
- results.append(result)
753
-
754
- outputs = [rearrange(result, "(b n_pbrs n) l c -> b n_pbrs n l c", b=B, n_pbrs=1) for result in results]
755
- return torch.cat(outputs, dim=1)
756
-
757
-
758
- class RefAttnProcessor2_0(BaseAttnProcessor):
759
- """
760
- Reference attention processor with shared value computation across PBR materials.
761
-
762
- This processor computes query and key once, but uses separate value projections
763
- for different PBR material types, enabling efficient multi-material processing.
764
- """
765
-
766
- def __init__(self, **kwargs):
767
- """
768
- Initialize reference attention processor.
769
-
770
- Args:
771
- **kwargs: Arguments passed to BaseAttnProcessor initialization
772
- """
773
- super().__init__(**kwargs)
774
- self.pbr_settings = self.pbr_setting # Alias for compatibility
775
- self.register_pbr_modules(["v_only", "out"], **kwargs)
776
-
777
- def __call__(
778
- self,
779
- attn: Attention,
780
- hidden_states: torch.Tensor,
781
- encoder_hidden_states: Optional[torch.Tensor] = None,
782
- attention_mask: Optional[torch.Tensor] = None,
783
- temb: Optional[torch.Tensor] = None,
784
- *args,
785
- **kwargs,
786
- ) -> torch.Tensor:
787
- """
788
- Apply reference attention with shared Q/K and separate V projections.
789
-
790
- This method computes query and key tensors once and reuses them across
791
- all PBR material types, while using separate value projections for each
792
- material type to maintain material-specific information.
793
-
794
- Args:
795
- attn: Attention module instance
796
- hidden_states: Input hidden states tensor
797
- encoder_hidden_states: Optional encoder hidden states for cross-attention
798
- attention_mask: Optional attention mask tensor
799
- temb: Optional temporal embedding tensor
800
- *args: Additional positional arguments
801
- **kwargs: Additional keyword arguments
802
-
803
- Returns:
804
- Stacked attention output for all PBR material types
805
- """
806
- AttnUtils.handle_deprecation_warning(args, kwargs)
807
-
808
- def get_qkv(attn, hidden_states, encoder_hidden_states, **kwargs):
809
- query = attn.to_q(hidden_states)
810
- key = attn.to_k(encoder_hidden_states)
811
-
812
- # Concatenate values from all PBR settings
813
- value_list = [attn.to_v(encoder_hidden_states)]
814
- for token in ["_" + token for token in self.pbr_settings if token != "albedo"]:
815
- value_list.append(getattr(attn.processor, f"to_v{token}")(encoder_hidden_states))
816
- value = torch.cat(value_list, dim=-1)
817
-
818
- return query, key, value
819
-
820
- # Core processing
821
- hidden_states, residual, input_ndim, shape_info, batch_size, heads, head_dim = AttnCore.process_attention_base(
822
- attn, hidden_states, encoder_hidden_states, attention_mask, temb, get_qkv_fn=get_qkv
823
- )
824
-
825
- # Split and process each PBR setting output
826
- hidden_states_list = torch.split(hidden_states, head_dim, dim=-1)
827
- output_hidden_states_list = []
828
-
829
- for i, hs in enumerate(hidden_states_list):
830
- hs = hs.transpose(1, 2).reshape(batch_size, -1, heads * head_dim).to(hs.dtype)
831
- token_suffix = "_" + self.pbr_settings[i] if self.pbr_settings[i] != "albedo" else ""
832
- target = attn if self.pbr_settings[i] == "albedo" else attn.processor
833
-
834
- hs = AttnUtils.finalize_output(
835
- hs, input_ndim, shape_info, attn, residual, getattr(target, f"to_out{token_suffix}")
836
- )
837
- output_hidden_states_list.append(hs)
838
-
839
- return torch.stack(output_hidden_states_list, dim=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/hunyuanpaintpbr/unet/model.py DELETED
@@ -1,622 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import os
16
-
17
- # import ipdb
18
- import numpy as np
19
- import torch
20
- import torch.nn as nn
21
- import torch.nn.functional as F
22
- import pytorch_lightning as pl
23
- from tqdm import tqdm
24
- from torchvision.transforms import v2
25
- from torchvision.utils import make_grid, save_image
26
- from einops import rearrange
27
-
28
- from diffusers import (
29
- DiffusionPipeline,
30
- EulerAncestralDiscreteScheduler,
31
- DDPMScheduler,
32
- UNet2DConditionModel,
33
- ControlNetModel,
34
- )
35
-
36
- from .modules import Dino_v2, UNet2p5DConditionModel
37
- import math
38
-
39
-
40
- def extract_into_tensor(a, t, x_shape):
41
- b, *_ = t.shape
42
- out = a.gather(-1, t)
43
- return out.reshape(b, *((1,) * (len(x_shape) - 1)))
44
-
45
-
46
- class HunyuanPaint(pl.LightningModule):
47
- def __init__(
48
- self,
49
- stable_diffusion_config,
50
- control_net_config=None,
51
- num_view=6,
52
- view_size=320,
53
- drop_cond_prob=0.1,
54
- with_normal_map=None,
55
- with_position_map=None,
56
- pbr_settings=["albedo", "mr"],
57
- **kwargs,
58
- ):
59
- """Initializes the HunyuanPaint Lightning Module.
60
-
61
- Args:
62
- stable_diffusion_config: Configuration for loading the Stable Diffusion pipeline
63
- control_net_config: Configuration for ControlNet (optional)
64
- num_view: Number of views to process
65
- view_size: Size of input views (height/width)
66
- drop_cond_prob: Probability of dropping conditioning input during training
67
- with_normal_map: Flag indicating whether normal maps are used
68
- with_position_map: Flag indicating whether position maps are used
69
- pbr_settings: List of PBR materials to generate (e.g., albedo, metallic-roughness)
70
- **kwargs: Additional keyword arguments
71
- """
72
- super(HunyuanPaint, self).__init__()
73
-
74
- self.num_view = num_view
75
- self.view_size = view_size
76
- self.drop_cond_prob = drop_cond_prob
77
- self.pbr_settings = pbr_settings
78
-
79
- # init modules
80
- pipeline = DiffusionPipeline.from_pretrained(**stable_diffusion_config)
81
- pipeline.set_pbr_settings(self.pbr_settings)
82
- pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(
83
- pipeline.scheduler.config, timestep_spacing="trailing"
84
- )
85
-
86
- self.with_normal_map = with_normal_map
87
- self.with_position_map = with_position_map
88
-
89
- self.pipeline = pipeline
90
-
91
- self.pipeline.vae.use_slicing = True
92
-
93
- train_sched = DDPMScheduler.from_config(self.pipeline.scheduler.config)
94
-
95
- if isinstance(self.pipeline.unet, UNet2DConditionModel):
96
- self.pipeline.unet = UNet2p5DConditionModel(
97
- self.pipeline.unet, train_sched, self.pipeline.scheduler, self.pbr_settings
98
- )
99
- self.train_scheduler = train_sched # use ddpm scheduler during training
100
-
101
- self.register_schedule()
102
-
103
- pipeline.set_learned_parameters()
104
-
105
- if control_net_config is not None:
106
- pipeline.unet = pipeline.unet.bfloat16().requires_grad_(control_net_config.train_unet)
107
- self.pipeline.add_controlnet(
108
- ControlNetModel.from_pretrained(control_net_config.pretrained_model_name_or_path),
109
- conditioning_scale=0.75,
110
- )
111
-
112
- self.unet = pipeline.unet
113
-
114
- self.pipeline.set_progress_bar_config(disable=True)
115
- self.pipeline.vae = self.pipeline.vae.bfloat16()
116
- self.pipeline.text_encoder = self.pipeline.text_encoder.bfloat16()
117
-
118
- if self.unet.use_dino:
119
- self.dino_v2 = Dino_v2("facebook/dinov2-giant")
120
- self.dino_v2 = self.dino_v2.bfloat16()
121
-
122
- self.validation_step_outputs = []
123
-
124
- def register_schedule(self):
125
-
126
- self.num_timesteps = self.train_scheduler.config.num_train_timesteps
127
-
128
- betas = self.train_scheduler.betas.detach().cpu()
129
-
130
- alphas = 1.0 - betas
131
- alphas_cumprod = torch.cumprod(alphas, dim=0)
132
- alphas_cumprod_prev = torch.cat([torch.ones(1, dtype=torch.float64), alphas_cumprod[:-1]], 0)
133
-
134
- self.register_buffer("betas", betas.float())
135
- self.register_buffer("alphas_cumprod", alphas_cumprod.float())
136
- self.register_buffer("alphas_cumprod_prev", alphas_cumprod_prev.float())
137
-
138
- # calculations for diffusion q(x_t | x_{t-1}) and others
139
- self.register_buffer("sqrt_alphas_cumprod", torch.sqrt(alphas_cumprod).float())
140
- self.register_buffer("sqrt_one_minus_alphas_cumprod", torch.sqrt(1 - alphas_cumprod).float())
141
-
142
- self.register_buffer("sqrt_recip_alphas_cumprod", torch.sqrt(1.0 / alphas_cumprod).float())
143
- self.register_buffer("sqrt_recipm1_alphas_cumprod", torch.sqrt(1.0 / alphas_cumprod - 1).float())
144
-
145
- def on_fit_start(self):
146
- device = torch.device(f"cuda:{self.local_rank}")
147
- self.pipeline.to(device)
148
- if self.global_rank == 0:
149
- os.makedirs(os.path.join(self.logdir, "images_val"), exist_ok=True)
150
-
151
- def prepare_batch_data(self, batch):
152
- """Preprocesses a batch of input data for training/inference.
153
-
154
- Args:
155
- batch: Raw input batch dictionary
156
-
157
- Returns:
158
- tuple: Contains:
159
- - cond_imgs: Primary conditioning images (B, 1, C, H, W)
160
- - cond_imgs_another: Secondary conditioning images (B, 1, C, H, W)
161
- - target_imgs: Dictionary of target PBR images resized and clamped
162
- - images_normal: Preprocessed normal maps (if available)
163
- - images_position: Preprocessed position maps (if available)
164
- """
165
-
166
- images_cond = batch["images_cond"].to(self.device) # (B, M, C, H, W), where M is the number of reference images
167
- cond_imgs, cond_imgs_another = images_cond[:, 0:1, ...], images_cond[:, 1:2, ...]
168
-
169
- cond_size = self.view_size
170
- cond_imgs = v2.functional.resize(cond_imgs, cond_size, interpolation=3, antialias=True).clamp(0, 1)
171
- cond_imgs_another = v2.functional.resize(cond_imgs_another, cond_size, interpolation=3, antialias=True).clamp(
172
- 0, 1
173
- )
174
-
175
- target_imgs = {}
176
- for pbr_token in self.pbr_settings:
177
- target_imgs[pbr_token] = batch[f"images_{pbr_token}"].to(self.device)
178
- target_imgs[pbr_token] = v2.functional.resize(
179
- target_imgs[pbr_token], self.view_size, interpolation=3, antialias=True
180
- ).clamp(0, 1)
181
-
182
- images_normal = None
183
- if "images_normal" in batch:
184
- images_normal = batch["images_normal"] # (B, N, C, H, W)
185
- images_normal = v2.functional.resize(images_normal, self.view_size, interpolation=3, antialias=True).clamp(
186
- 0, 1
187
- )
188
- images_normal = [images_normal]
189
-
190
- images_position = None
191
- if "images_position" in batch:
192
- images_position = batch["images_position"] # (B, N, C, H, W)
193
- images_position = v2.functional.resize(
194
- images_position, self.view_size, interpolation=3, antialias=True
195
- ).clamp(0, 1)
196
- images_position = [images_position]
197
-
198
- return cond_imgs, cond_imgs_another, target_imgs, images_normal, images_position
199
-
200
- @torch.no_grad()
201
- def forward_text_encoder(self, prompts):
202
- device = next(self.pipeline.vae.parameters()).device
203
- text_embeds = self.pipeline.encode_prompt(prompts, device, 1, False)[0]
204
- return text_embeds
205
-
206
- @torch.no_grad()
207
- def encode_images(self, images):
208
- """Encodes input images into latent representations using the VAE.
209
-
210
- Handles both standard input (B, N, C, H, W) and PBR input (B, N_pbrs, N, C, H, W)
211
- Maintains original batch structure in output latents.
212
-
213
- Args:
214
- images: Input images tensor
215
-
216
- Returns:
217
- torch.Tensor: Latent representations with original batch dimensions preserved
218
- """
219
-
220
- B = images.shape[0]
221
- image_ndims = images.ndim
222
- if image_ndims != 5:
223
- N_pbrs, N = images.shape[1:3]
224
- images = (
225
- rearrange(images, "b n c h w -> (b n) c h w")
226
- if image_ndims == 5
227
- else rearrange(images, "b n_pbrs n c h w -> (b n_pbrs n) c h w")
228
- )
229
- dtype = next(self.pipeline.vae.parameters()).dtype
230
-
231
- images = (images - 0.5) * 2.0
232
- posterior = self.pipeline.vae.encode(images.to(dtype)).latent_dist
233
- latents = posterior.sample() * self.pipeline.vae.config.scaling_factor
234
-
235
- latents = (
236
- rearrange(latents, "(b n) c h w -> b n c h w", b=B)
237
- if image_ndims == 5
238
- else rearrange(latents, "(b n_pbrs n) c h w -> b n_pbrs n c h w", b=B, n_pbrs=N_pbrs)
239
- )
240
-
241
- return latents
242
-
243
- def forward_unet(self, latents, t, **cached_condition):
244
- """Runs the UNet model to predict noise/latent residuals.
245
-
246
- Args:
247
- latents: Noisy latent representations (B, C, H, W)
248
- t: Timestep tensor (B,)
249
- **cached_condition: Dictionary of conditioning inputs (text embeds, reference images, etc)
250
-
251
- Returns:
252
- torch.Tensor: UNet output (predicted noise or velocity)
253
- """
254
-
255
- dtype = next(self.unet.parameters()).dtype
256
- latents = latents.to(dtype)
257
- shading_embeds = cached_condition["shading_embeds"]
258
- pred_noise = self.pipeline.unet(latents, t, encoder_hidden_states=shading_embeds, **cached_condition)
259
- return pred_noise[0]
260
-
261
- def predict_start_from_z_and_v(self, x_t, t, v):
262
- """
263
- Predicts clean image (x0) from noisy latents (x_t) and
264
- velocity prediction (v) using the v-prediction formula.
265
-
266
- Args:
267
- x_t: Noisy latents at timestep t
268
- t: Current timestep
269
- v: Predicted velocity (v) from UNet
270
-
271
- Returns:
272
- torch.Tensor: Predicted clean image (x0)
273
- """
274
-
275
- return (
276
- extract_into_tensor(self.sqrt_alphas_cumprod, t, x_t.shape) * x_t
277
- - extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_t.shape) * v
278
- )
279
-
280
- def get_v(self, x, noise, t):
281
- """Computes the target velocity (v) for v-prediction training.
282
-
283
- Args:
284
- x: Clean latents (x0)
285
- noise: Added noise
286
- t: Current timestep
287
-
288
- Returns:
289
- torch.Tensor: Target velocity
290
- """
291
-
292
- return (
293
- extract_into_tensor(self.sqrt_alphas_cumprod, t, x.shape) * noise
294
- - extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x.shape) * x
295
- )
296
-
297
- def training_step(self, batch, batch_idx):
298
- """Performs a single training step with both conditioning paths.
299
-
300
- Implements:
301
- 1. Dual-conditioning path training (main ref + secondary ref)
302
- 2. Velocity-prediction with consistency loss
303
- 3. Conditional dropout for robust learning
304
- 4. PBR-specific losses (albedo/metallic-roughness)
305
-
306
- Args:
307
- batch: Input batch from dataloader
308
- batch_idx: Index of current batch
309
-
310
- Returns:
311
- torch.Tensor: Combined loss value
312
- """
313
-
314
- cond_imgs, cond_imgs_another, target_imgs, normal_imgs, position_imgs = self.prepare_batch_data(batch)
315
-
316
- B, N_ref = cond_imgs.shape[:2]
317
- _, N_gen, _, H, W = target_imgs["albedo"].shape
318
- N_pbrs = len(self.pbr_settings)
319
- t = torch.randint(0, self.num_timesteps, size=(B,)).long().to(self.device)
320
- t = t.unsqueeze(-1).repeat(1, N_pbrs, N_gen)
321
- t = rearrange(t, "b n_pbrs n -> (b n_pbrs n)")
322
-
323
- all_target_pbrs = []
324
- for pbr_token in self.pbr_settings:
325
- all_target_pbrs.append(target_imgs[pbr_token])
326
- all_target_pbrs = torch.stack(all_target_pbrs, dim=0).transpose(1, 0)
327
- gen_latents = self.encode_images(all_target_pbrs) #! B, N_pbrs N C H W
328
- ref_latents = self.encode_images(cond_imgs) #! B, M, C, H, W
329
- ref_latents_another = self.encode_images(cond_imgs_another) #! B, M, C, H, W
330
-
331
- all_shading_tokens = []
332
- for token in self.pbr_settings:
333
- if token in ["albedo", "mr"]:
334
- all_shading_tokens.append(
335
- getattr(self.unet, f"learned_text_clip_{token}").unsqueeze(dim=0).repeat(B, 1, 1)
336
- )
337
- shading_embeds = torch.stack(all_shading_tokens, dim=1)
338
-
339
- if self.unet.use_dino:
340
- dino_hidden_states = self.dino_v2(cond_imgs[:, :1, ...])
341
- dino_hidden_states_another = self.dino_v2(cond_imgs_another[:, :1, ...])
342
-
343
- gen_latents = rearrange(gen_latents, "b n_pbrs n c h w -> (b n_pbrs n) c h w")
344
- noise = torch.randn_like(gen_latents).to(self.device)
345
- latents_noisy = self.train_scheduler.add_noise(gen_latents, noise, t).to(self.device)
346
- latents_noisy = rearrange(latents_noisy, "(b n_pbrs n) c h w -> b n_pbrs n c h w", b=B, n_pbrs=N_pbrs)
347
-
348
- cached_condition = {}
349
-
350
- if normal_imgs is not None:
351
- normal_embeds = self.encode_images(normal_imgs[0])
352
- cached_condition["embeds_normal"] = normal_embeds #! B, N, C, H, W
353
-
354
- if position_imgs is not None:
355
- position_embeds = self.encode_images(position_imgs[0])
356
- cached_condition["embeds_position"] = position_embeds #! B, N, C, H, W
357
- cached_condition["position_maps"] = position_imgs[0] #! B, N, C, H, W
358
-
359
- for b in range(B):
360
- prob = np.random.rand()
361
- if prob < self.drop_cond_prob:
362
- if "normal_imgs" in cached_condition:
363
- cached_condition["embeds_normal"][b, ...] = torch.zeros_like(
364
- cached_condition["embeds_normal"][b, ...]
365
- )
366
- if "position_imgs" in cached_condition:
367
- cached_condition["embeds_position"][b, ...] = torch.zeros_like(
368
- cached_condition["embeds_position"][b, ...]
369
- )
370
-
371
- prob = np.random.rand()
372
- if prob < self.drop_cond_prob:
373
- if "position_maps" in cached_condition:
374
- cached_condition["position_maps"][b, ...] = torch.zeros_like(
375
- cached_condition["position_maps"][b, ...]
376
- )
377
-
378
- prob = np.random.rand()
379
- if prob < self.drop_cond_prob:
380
- dino_hidden_states[b, ...] = torch.zeros_like(dino_hidden_states[b, ...])
381
- prob = np.random.rand()
382
- if prob < self.drop_cond_prob:
383
- dino_hidden_states_another[b, ...] = torch.zeros_like(dino_hidden_states_another[b, ...])
384
-
385
- # MVA & Ref Attention
386
- prob = np.random.rand()
387
- cached_condition["mva_scale"] = 1.0
388
- cached_condition["ref_scale"] = 1.0
389
- if prob < self.drop_cond_prob:
390
- cached_condition["mva_scale"] = 0.0
391
- cached_condition["ref_scale"] = 0.0
392
- elif prob > 1.0 - self.drop_cond_prob:
393
- prob = np.random.rand()
394
- if prob < 0.5:
395
- cached_condition["mva_scale"] = 0.0
396
- else:
397
- cached_condition["ref_scale"] = 0.0
398
- else:
399
- pass
400
-
401
- if self.train_scheduler.config.prediction_type == "v_prediction":
402
-
403
- cached_condition["shading_embeds"] = shading_embeds
404
- cached_condition["ref_latents"] = ref_latents
405
- cached_condition["dino_hidden_states"] = dino_hidden_states
406
- v_pred = self.forward_unet(latents_noisy, t, **cached_condition)
407
- v_pred_albedo, v_pred_mr = torch.split(
408
- rearrange(
409
- v_pred, "(b n_pbr n) c h w -> b n_pbr n c h w", n_pbr=len(self.pbr_settings), n=self.num_view
410
- ),
411
- 1,
412
- dim=1,
413
- )
414
- v_target = self.get_v(gen_latents, noise, t)
415
- v_target_albedo, v_target_mr = torch.split(
416
- rearrange(
417
- v_target, "(b n_pbr n) c h w -> b n_pbr n c h w", n_pbr=len(self.pbr_settings), n=self.num_view
418
- ),
419
- 1,
420
- dim=1,
421
- )
422
-
423
- albedo_loss_1, _ = self.compute_loss(v_pred_albedo, v_target_albedo)
424
- mr_loss_1, _ = self.compute_loss(v_pred_mr, v_target_mr)
425
-
426
- cached_condition["ref_latents"] = ref_latents_another
427
- cached_condition["dino_hidden_states"] = dino_hidden_states_another
428
- v_pred_another = self.forward_unet(latents_noisy, t, **cached_condition)
429
- v_pred_another_albedo, v_pred_another_mr = torch.split(
430
- rearrange(
431
- v_pred_another,
432
- "(b n_pbr n) c h w -> b n_pbr n c h w",
433
- n_pbr=len(self.pbr_settings),
434
- n=self.num_view,
435
- ),
436
- 1,
437
- dim=1,
438
- )
439
-
440
- albedo_loss_2, _ = self.compute_loss(v_pred_another_albedo, v_target_albedo)
441
- mr_loss_2, _ = self.compute_loss(v_pred_another_mr, v_target_mr)
442
-
443
- consistency_loss, _ = self.compute_loss(v_pred_another, v_pred)
444
-
445
- albedo_loss = (albedo_loss_1 + albedo_loss_2) * 0.5
446
- mr_loss = (mr_loss_1 + mr_loss_2) * 0.5
447
-
448
- log_loss_dict = {}
449
- log_loss_dict.update({f"train/albedo_loss": albedo_loss})
450
- log_loss_dict.update({f"train/mr_loss": mr_loss})
451
- log_loss_dict.update({f"train/cons_loss": consistency_loss})
452
-
453
- loss_dict = log_loss_dict
454
-
455
- elif self.train_scheduler.config.prediction_type == "epsilon":
456
- e_pred = self.forward_unet(latents_noisy, t, **cached_condition)
457
- loss, loss_dict = self.compute_loss(e_pred, noise)
458
- else:
459
- raise f"No {self.train_scheduler.config.prediction_type}"
460
-
461
- # logging
462
- self.log_dict(loss_dict, prog_bar=True, logger=True, on_step=True, on_epoch=True)
463
- self.log("global_step", self.global_step, prog_bar=True, logger=True, on_step=True, on_epoch=False)
464
- lr = self.optimizers().param_groups[0]["lr"]
465
- self.log("lr_abs", lr, prog_bar=True, logger=True, on_step=True, on_epoch=False)
466
-
467
- return 0.85 * (albedo_loss + mr_loss) + 0.15 * consistency_loss
468
-
469
- def compute_loss(self, noise_pred, noise_gt):
470
- loss = F.mse_loss(noise_pred, noise_gt)
471
- prefix = "train"
472
- loss_dict = {}
473
- loss_dict.update({f"{prefix}/loss": loss})
474
- return loss, loss_dict
475
-
476
- @torch.no_grad()
477
- def validation_step(self, batch, batch_idx):
478
- """Performs validation on a single batch.
479
-
480
- Generates predicted images using:
481
- 1. Reference conditioning images
482
- 2. Optional normal/position maps
483
- 3. Frozen DINO features (if enabled)
484
- 4. Text prompt conditioning
485
-
486
- Compares predictions against ground truth targets and prepares visualization.
487
- Stores results for epoch-level aggregation.
488
-
489
- Args:
490
- batch: Input batch from validation dataloader
491
- batch_idx: Index of current batch
492
- """
493
- # [Validation image generation and comparison logic...]
494
- # Key steps:
495
- # 1. Preprocess conditioning images to PIL format
496
- # 2. Set up conditioning inputs (normal maps, position maps, DINO features)
497
- # 3. Run pipeline inference with fixed prompt ("high quality")
498
- # 4. Decode latent outputs to image space
499
- # 5. Arrange predictions and ground truths for visualization
500
-
501
- cond_imgs_tensor, _, target_imgs, normal_imgs, position_imgs = self.prepare_batch_data(batch)
502
- resolution = self.view_size
503
- image_pils = []
504
- for i in range(cond_imgs_tensor.shape[0]):
505
- image_pils.append([])
506
- for j in range(cond_imgs_tensor.shape[1]):
507
- image_pils[-1].append(v2.functional.to_pil_image(cond_imgs_tensor[i, j, ...]))
508
-
509
- outputs, gts = [], []
510
- for idx in range(len(image_pils)):
511
- cond_imgs = image_pils[idx]
512
-
513
- cached_condition = dict(num_in_batch=self.num_view, N_pbrs=len(self.pbr_settings))
514
- if normal_imgs is not None:
515
- cached_condition["images_normal"] = normal_imgs[0][idx, ...].unsqueeze(0)
516
- if position_imgs is not None:
517
- cached_condition["images_position"] = position_imgs[0][idx, ...].unsqueeze(0)
518
- if self.pipeline.unet.use_dino:
519
- dino_hidden_states = self.dino_v2([cond_imgs][0])
520
- cached_condition["dino_hidden_states"] = dino_hidden_states
521
-
522
- latent = self.pipeline(
523
- cond_imgs,
524
- prompt="high quality",
525
- num_inference_steps=30,
526
- output_type="latent",
527
- height=resolution,
528
- width=resolution,
529
- **cached_condition,
530
- ).images
531
-
532
- image = self.pipeline.vae.decode(latent / self.pipeline.vae.config.scaling_factor, return_dict=False)[
533
- 0
534
- ] # [-1, 1]
535
- image = (image * 0.5 + 0.5).clamp(0, 1)
536
-
537
- image = rearrange(
538
- image, "(b n_pbr n) c h w -> b n_pbr n c h w", n_pbr=len(self.pbr_settings), n=self.num_view
539
- )
540
- image = torch.cat((torch.ones_like(image[:, :, :1, ...]) * 0.5, image), dim=2)
541
- image = rearrange(image, "b n_pbr n c h w -> (b n_pbr n) c h w")
542
- image = rearrange(
543
- image,
544
- "(b n_pbr n) c h w -> b c (n_pbr h) (n w)",
545
- b=1,
546
- n_pbr=len(self.pbr_settings),
547
- n=self.num_view + 1,
548
- )
549
- outputs.append(image)
550
-
551
- all_target_pbrs = []
552
- for pbr_token in self.pbr_settings:
553
- all_target_pbrs.append(target_imgs[pbr_token])
554
- all_target_pbrs = torch.stack(all_target_pbrs, dim=0).transpose(1, 0)
555
- all_target_pbrs = torch.cat(
556
- (cond_imgs_tensor.unsqueeze(1).repeat(1, len(self.pbr_settings), 1, 1, 1, 1), all_target_pbrs), dim=2
557
- )
558
- all_target_pbrs = rearrange(all_target_pbrs, "b n_pbrs n c h w -> b c (n_pbrs h) (n w)")
559
- gts = all_target_pbrs
560
- outputs = torch.cat(outputs, dim=0).to(self.device)
561
- images = torch.cat([gts, outputs], dim=-2)
562
- self.validation_step_outputs.append(images)
563
-
564
- @torch.no_grad()
565
- def on_validation_epoch_end(self):
566
- """Aggregates validation results at epoch end.
567
-
568
- Gathers outputs from all GPUs (if distributed training),
569
- creates a unified visualization grid, and saves to disk.
570
- Only rank 0 process performs saving.
571
- """
572
- # [Result aggregation and visualization...]
573
- # Key steps:
574
- # 1. Gather validation outputs from all processes
575
- # 2. Create image grid combining ground truths and predictions
576
- # 3. Save visualization with step-numbered filename
577
- # 4. Clear memory for next validation cycle
578
-
579
- images = torch.cat(self.validation_step_outputs, dim=0)
580
- all_images = self.all_gather(images)
581
- all_images = rearrange(all_images, "r b c h w -> (r b) c h w")
582
-
583
- if self.global_rank == 0:
584
- grid = make_grid(all_images, nrow=8, normalize=True, value_range=(0, 1))
585
- save_image(grid, os.path.join(self.logdir, "images_val", f"val_{self.global_step:07d}.png"))
586
-
587
- self.validation_step_outputs.clear() # free memory
588
-
589
- def configure_optimizers(self):
590
- lr = self.learning_rate
591
- optimizer = torch.optim.AdamW(self.unet.parameters(), lr=lr)
592
-
593
- def lr_lambda(step):
594
- warm_up_step = 1000
595
- T_step = 9000
596
- gamma = 0.9
597
- min_lr = 0.1 if step >= warm_up_step else 0.0
598
- max_lr = 1.0
599
- normalized_step = step % (warm_up_step + T_step)
600
- current_max_lr = max_lr * gamma ** (step // (warm_up_step + T_step))
601
- if current_max_lr < min_lr:
602
- current_max_lr = min_lr
603
- if normalized_step < warm_up_step:
604
- lr_step = min_lr + (normalized_step / warm_up_step) * (current_max_lr - min_lr)
605
- else:
606
- step_wc_wp = normalized_step - warm_up_step
607
- ratio = step_wc_wp / T_step
608
- lr_step = min_lr + 0.5 * (current_max_lr - min_lr) * (1 + math.cos(math.pi * ratio))
609
- return lr_step
610
-
611
- lr_scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)
612
-
613
- lr_scheduler_config = {
614
- "scheduler": lr_scheduler,
615
- "interval": "step",
616
- "frequency": 1,
617
- "monitor": "val_loss",
618
- "strict": False,
619
- "name": None,
620
- }
621
-
622
- return {"optimizer": optimizer, "lr_scheduler": lr_scheduler_config}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/hunyuanpaintpbr/unet/modules.py DELETED
@@ -1,1102 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import os
16
- import json
17
- import copy
18
- import numpy as np
19
- import torch
20
- import torch.nn as nn
21
- from einops import rearrange
22
- from typing import Any, Callable, Dict, List, Optional, Union, Tuple, Literal
23
- import diffusers
24
- from diffusers.utils import deprecate
25
- from diffusers import (
26
- DDPMScheduler,
27
- EulerAncestralDiscreteScheduler,
28
- UNet2DConditionModel,
29
- )
30
- from diffusers.models import UNet2DConditionModel
31
- from diffusers.models.attention_processor import Attention, AttnProcessor
32
- from diffusers.models.transformers.transformer_2d import BasicTransformerBlock
33
- from .attn_processor import SelfAttnProcessor2_0, RefAttnProcessor2_0, PoseRoPEAttnProcessor2_0
34
-
35
- from transformers import AutoImageProcessor, AutoModel
36
-
37
-
38
- class Dino_v2(nn.Module):
39
-
40
- """Wrapper for DINOv2 vision transformer (frozen weights).
41
-
42
- Provides feature extraction for reference images.
43
-
44
- Args:
45
- dino_v2_path: Custom path to DINOv2 model weights (uses default if None)
46
- """
47
-
48
-
49
- def __init__(self, dino_v2_path):
50
- super(Dino_v2, self).__init__()
51
- self.dino_processor = AutoImageProcessor.from_pretrained(dino_v2_path)
52
- self.dino_v2 = AutoModel.from_pretrained(dino_v2_path)
53
-
54
- for param in self.parameters():
55
- param.requires_grad = False
56
-
57
- self.dino_v2.eval()
58
-
59
- def forward(self, images):
60
-
61
- """Processes input images through DINOv2 ViT.
62
-
63
- Handles both tensor input (B, N, C, H, W) and PIL image lists.
64
- Extracts patch embeddings and flattens spatial dimensions.
65
-
66
- Returns:
67
- torch.Tensor: Feature vectors [B, N*(num_patches), feature_dim]
68
- """
69
-
70
- if isinstance(images, torch.Tensor):
71
- batch_size = images.shape[0]
72
- dino_proceesed_images = self.dino_processor(
73
- images=rearrange(images, "b n c h w -> (b n) c h w"), return_tensors="pt", do_rescale=False
74
- ).pixel_values
75
- else:
76
- batch_size = 1
77
- dino_proceesed_images = self.dino_processor(images=images, return_tensors="pt").pixel_values
78
- dino_proceesed_images = torch.stack(
79
- [torch.from_numpy(np.array(image)) for image in dino_proceesed_images], dim=0
80
- )
81
- dino_param = next(self.dino_v2.parameters())
82
- dino_proceesed_images = dino_proceesed_images.to(dino_param)
83
- dino_hidden_states = self.dino_v2(dino_proceesed_images)[0]
84
- dino_hidden_states = rearrange(dino_hidden_states.to(dino_param), "(b n) l c -> b (n l) c", b=batch_size)
85
-
86
- return dino_hidden_states
87
-
88
-
89
- def _chunked_feed_forward(ff: nn.Module, hidden_states: torch.Tensor, chunk_dim: int, chunk_size: int):
90
- # "feed_forward_chunk_size" can be used to save memory
91
-
92
- """Memory-efficient feedforward execution via chunking.
93
-
94
- Divides input along specified dimension for sequential processing.
95
-
96
- Args:
97
- ff: Feedforward module to apply
98
- hidden_states: Input tensor
99
- chunk_dim: Dimension to split
100
- chunk_size: Size of each chunk
101
-
102
- Returns:
103
- torch.Tensor: Reassembled output tensor
104
- """
105
-
106
- if hidden_states.shape[chunk_dim] % chunk_size != 0:
107
- raise ValueError(
108
- f"`hidden_states` dimension to be chunked: {hidden_states.shape[chunk_dim]}"
109
- f"has to be divisible by chunk size: {chunk_size}."
110
- "Make sure to set an appropriate `chunk_size` when calling `unet.enable_forward_chunking`."
111
- )
112
-
113
- num_chunks = hidden_states.shape[chunk_dim] // chunk_size
114
- ff_output = torch.cat(
115
- [ff(hid_slice) for hid_slice in hidden_states.chunk(num_chunks, dim=chunk_dim)],
116
- dim=chunk_dim,
117
- )
118
- return ff_output
119
-
120
-
121
- @torch.no_grad()
122
- def compute_voxel_grid_mask(position, grid_resolution=8):
123
-
124
- """Generates view-to-view attention mask based on 3D position similarity.
125
-
126
- Uses voxel grid downsampling to determine spatially adjacent regions.
127
- Mask indicates where features should interact across different views.
128
-
129
- Args:
130
- position: Position maps [B, N, 3, H, W] (normalized 0-1)
131
- grid_resolution: Spatial reduction factor
132
-
133
- Returns:
134
- torch.Tensor: Attention mask [B, N*grid_res**2, N*grid_res**2]
135
- """
136
-
137
- position = position.half()
138
- B, N, _, H, W = position.shape
139
- assert H % grid_resolution == 0 and W % grid_resolution == 0
140
-
141
- valid_mask = (position != 1).all(dim=2, keepdim=True)
142
- valid_mask = valid_mask.expand_as(position)
143
- position[valid_mask == False] = 0
144
-
145
- position = rearrange(
146
- position,
147
- "b n c (num_h grid_h) (num_w grid_w) -> b n num_h num_w c grid_h grid_w",
148
- num_h=grid_resolution,
149
- num_w=grid_resolution,
150
- )
151
- valid_mask = rearrange(
152
- valid_mask,
153
- "b n c (num_h grid_h) (num_w grid_w) -> b n num_h num_w c grid_h grid_w",
154
- num_h=grid_resolution,
155
- num_w=grid_resolution,
156
- )
157
-
158
- grid_position = position.sum(dim=(-2, -1))
159
- count_masked = valid_mask.sum(dim=(-2, -1))
160
-
161
- grid_position = grid_position / count_masked.clamp(min=1)
162
- grid_position[count_masked < 5] = 0
163
-
164
- grid_position = grid_position.permute(0, 1, 4, 2, 3)
165
- grid_position = rearrange(grid_position, "b n c h w -> b n (h w) c")
166
-
167
- grid_position_expanded_1 = grid_position.unsqueeze(2).unsqueeze(4) # 形状变为 B, N, 1, L, 1, 3
168
- grid_position_expanded_2 = grid_position.unsqueeze(1).unsqueeze(3) # 形状变为 B, 1, N, 1, L, 3
169
-
170
- # 计算欧氏距离
171
- distances = torch.norm(grid_position_expanded_1 - grid_position_expanded_2, dim=-1) # 形状为 B, N, N, L, L
172
-
173
- weights = distances
174
- grid_distance = 1.73 / grid_resolution
175
- weights = weights < grid_distance
176
-
177
- return weights
178
-
179
-
180
- def compute_multi_resolution_mask(position_maps, grid_resolutions=[32, 16, 8]):
181
-
182
- """Generates attention masks at multiple spatial resolutions.
183
-
184
- Creates pyramid of position-based masks for hierarchical attention.
185
-
186
- Args:
187
- position_maps: Position maps [B, N, 3, H, W]
188
- grid_resolutions: List of downsampling factors
189
-
190
- Returns:
191
- dict: Resolution-specific masks keyed by flattened dimension size
192
- """
193
-
194
- position_attn_mask = {}
195
- with torch.no_grad():
196
- for grid_resolution in grid_resolutions:
197
- position_mask = compute_voxel_grid_mask(position_maps, grid_resolution)
198
- position_mask = rearrange(position_mask, "b ni nj li lj -> b (ni li) (nj lj)")
199
- position_attn_mask[position_mask.shape[1]] = position_mask
200
- return position_attn_mask
201
-
202
-
203
- @torch.no_grad()
204
- def compute_discrete_voxel_indice(position, grid_resolution=8, voxel_resolution=128):
205
-
206
- """Quantizes position maps to discrete voxel indices.
207
-
208
- Creates sparse 3D coordinate representations for efficient hashing.
209
-
210
- Args:
211
- position: Position maps [B, N, 3, H, W]
212
- grid_resolution: Spatial downsampling factor
213
- voxel_resolution: Quantization resolution
214
-
215
- Returns:
216
- torch.Tensor: Voxel indices [B, N, grid_res, grid_res, 3]
217
- """
218
-
219
- position = position.half()
220
- B, N, _, H, W = position.shape
221
- assert H % grid_resolution == 0 and W % grid_resolution == 0
222
-
223
- valid_mask = (position != 1).all(dim=2, keepdim=True)
224
- valid_mask = valid_mask.expand_as(position)
225
- position[valid_mask == False] = 0
226
-
227
- position = rearrange(
228
- position,
229
- "b n c (num_h grid_h) (num_w grid_w) -> b n num_h num_w c grid_h grid_w",
230
- num_h=grid_resolution,
231
- num_w=grid_resolution,
232
- )
233
- valid_mask = rearrange(
234
- valid_mask,
235
- "b n c (num_h grid_h) (num_w grid_w) -> b n num_h num_w c grid_h grid_w",
236
- num_h=grid_resolution,
237
- num_w=grid_resolution,
238
- )
239
-
240
- grid_position = position.sum(dim=(-2, -1))
241
- count_masked = valid_mask.sum(dim=(-2, -1))
242
-
243
- grid_position = grid_position / count_masked.clamp(min=1)
244
- voxel_mask_thres = (H // grid_resolution) * (W // grid_resolution) // (4 * 4)
245
- grid_position[count_masked < voxel_mask_thres] = 0
246
-
247
- grid_position = grid_position.permute(0, 1, 4, 2, 3).clamp(0, 1) # B N C H W
248
- voxel_indices = grid_position * (voxel_resolution - 1)
249
- voxel_indices = torch.round(voxel_indices).long()
250
- return voxel_indices
251
-
252
-
253
- def calc_multires_voxel_idxs(position_maps, grid_resolutions=[64, 32, 16, 8], voxel_resolutions=[512, 256, 128, 64]):
254
-
255
- """Generates multi-resolution voxel indices for position encoding.
256
-
257
- Creates pyramid of quantized position representations.
258
-
259
- Args:
260
- position_maps: Input position maps
261
- grid_resolutions: Spatial resolution levels
262
- voxel_resolutions: Quantization levels
263
-
264
- Returns:
265
- dict: Voxel indices keyed by flattened dimension size, with resolution metadata
266
- """
267
-
268
- voxel_indices = {}
269
- with torch.no_grad():
270
- for grid_resolution, voxel_resolution in zip(grid_resolutions, voxel_resolutions):
271
- voxel_indice = compute_discrete_voxel_indice(position_maps, grid_resolution, voxel_resolution)
272
- voxel_indice = rearrange(voxel_indice, "b n c h w -> b (n h w) c")
273
- voxel_indices[voxel_indice.shape[1]] = {"voxel_indices": voxel_indice, "voxel_resolution": voxel_resolution}
274
- return voxel_indices
275
-
276
-
277
- class Basic2p5DTransformerBlock(torch.nn.Module):
278
-
279
-
280
- """Enhanced transformer block for multiview 2.5D image generation.
281
-
282
- Extends standard transformer blocks with:
283
- - Material-specific attention (MDA)
284
- - Multiview attention (MA)
285
- - Reference attention (RA)
286
- - DINO feature integration
287
-
288
- Args:
289
- transformer: Base transformer block
290
- layer_name: Identifier for layer
291
- use_ma: Enable multiview attention
292
- use_ra: Enable reference attention
293
- use_mda: Enable material-aware attention
294
- use_dino: Enable DINO feature integration
295
- pbr_setting: List of PBR materials
296
- """
297
-
298
- def __init__(
299
- self,
300
- transformer: BasicTransformerBlock,
301
- layer_name,
302
- use_ma=True,
303
- use_ra=True,
304
- use_mda=True,
305
- use_dino=True,
306
- pbr_setting=None,
307
- ) -> None:
308
-
309
- """
310
- Initialization:
311
- 1. Material-Dimension Attention (MDA):
312
- - Processes each PBR material with separate projection weights
313
- - Uses custom SelfAttnProcessor2_0 with material awareness
314
-
315
- 2. Multiview Attention (MA):
316
- - Adds cross-view attention with PoseRoPE
317
- - Initialized as zero-initialized residual pathway
318
-
319
- 3. Reference Attention (RA):
320
- - Conditions on reference view features
321
- - Uses RefAttnProcessor2_0 for material-specific conditioning
322
-
323
- 4. DINO Attention:
324
- - Incorporates DINO-ViT features
325
- - Initialized as zero-initialized residual pathway
326
- """
327
-
328
- super().__init__()
329
- self.transformer = transformer
330
- self.layer_name = layer_name
331
- self.use_ma = use_ma
332
- self.use_ra = use_ra
333
- self.use_mda = use_mda
334
- self.use_dino = use_dino
335
- self.pbr_setting = pbr_setting
336
-
337
- if self.use_mda:
338
- self.attn1.set_processor(
339
- SelfAttnProcessor2_0(
340
- query_dim=self.dim,
341
- heads=self.num_attention_heads,
342
- dim_head=self.attention_head_dim,
343
- dropout=self.dropout,
344
- bias=self.attention_bias,
345
- cross_attention_dim=None,
346
- upcast_attention=self.attn1.upcast_attention,
347
- out_bias=True,
348
- pbr_setting=self.pbr_setting,
349
- )
350
- )
351
-
352
- # multiview attn
353
- if self.use_ma:
354
- self.attn_multiview = Attention(
355
- query_dim=self.dim,
356
- heads=self.num_attention_heads,
357
- dim_head=self.attention_head_dim,
358
- dropout=self.dropout,
359
- bias=self.attention_bias,
360
- cross_attention_dim=None,
361
- upcast_attention=self.attn1.upcast_attention,
362
- out_bias=True,
363
- processor=PoseRoPEAttnProcessor2_0(),
364
- )
365
-
366
- # ref attn
367
- if self.use_ra:
368
- self.attn_refview = Attention(
369
- query_dim=self.dim,
370
- heads=self.num_attention_heads,
371
- dim_head=self.attention_head_dim,
372
- dropout=self.dropout,
373
- bias=self.attention_bias,
374
- cross_attention_dim=None,
375
- upcast_attention=self.attn1.upcast_attention,
376
- out_bias=True,
377
- processor=RefAttnProcessor2_0(
378
- query_dim=self.dim,
379
- heads=self.num_attention_heads,
380
- dim_head=self.attention_head_dim,
381
- dropout=self.dropout,
382
- bias=self.attention_bias,
383
- cross_attention_dim=None,
384
- upcast_attention=self.attn1.upcast_attention,
385
- out_bias=True,
386
- pbr_setting=self.pbr_setting,
387
- ),
388
- )
389
-
390
- # dino attn
391
- if self.use_dino:
392
- self.attn_dino = Attention(
393
- query_dim=self.dim,
394
- heads=self.num_attention_heads,
395
- dim_head=self.attention_head_dim,
396
- dropout=self.dropout,
397
- bias=self.attention_bias,
398
- cross_attention_dim=self.cross_attention_dim,
399
- upcast_attention=self.attn2.upcast_attention,
400
- out_bias=True,
401
- )
402
-
403
- self._initialize_attn_weights()
404
-
405
- def _initialize_attn_weights(self):
406
-
407
- """Initializes specialized attention heads with base weights.
408
-
409
- Uses weight sharing strategy:
410
- - Copies base transformer weights to specialized heads
411
- - Initializes newly-added parameters to zero
412
- """
413
-
414
- if self.use_mda:
415
- for token in self.pbr_setting:
416
- if token == "albedo":
417
- continue
418
- getattr(self.attn1.processor, f"to_q_{token}").load_state_dict(self.attn1.to_q.state_dict())
419
- getattr(self.attn1.processor, f"to_k_{token}").load_state_dict(self.attn1.to_k.state_dict())
420
- getattr(self.attn1.processor, f"to_v_{token}").load_state_dict(self.attn1.to_v.state_dict())
421
- getattr(self.attn1.processor, f"to_out_{token}").load_state_dict(self.attn1.to_out.state_dict())
422
-
423
- if self.use_ma:
424
- self.attn_multiview.load_state_dict(self.attn1.state_dict(), strict=False)
425
- with torch.no_grad():
426
- for layer in self.attn_multiview.to_out:
427
- for param in layer.parameters():
428
- param.zero_()
429
-
430
- if self.use_ra:
431
- self.attn_refview.load_state_dict(self.attn1.state_dict(), strict=False)
432
- for token in self.pbr_setting:
433
- if token == "albedo":
434
- continue
435
- getattr(self.attn_refview.processor, f"to_v_{token}").load_state_dict(
436
- self.attn_refview.to_q.state_dict()
437
- )
438
- getattr(self.attn_refview.processor, f"to_out_{token}").load_state_dict(
439
- self.attn_refview.to_out.state_dict()
440
- )
441
- with torch.no_grad():
442
- for layer in self.attn_refview.to_out:
443
- for param in layer.parameters():
444
- param.zero_()
445
- for token in self.pbr_setting:
446
- if token == "albedo":
447
- continue
448
- for layer in getattr(self.attn_refview.processor, f"to_out_{token}"):
449
- for param in layer.parameters():
450
- param.zero_()
451
-
452
- if self.use_dino:
453
- self.attn_dino.load_state_dict(self.attn2.state_dict(), strict=False)
454
- with torch.no_grad():
455
- for layer in self.attn_dino.to_out:
456
- for param in layer.parameters():
457
- param.zero_()
458
-
459
- if self.use_dino:
460
- self.attn_dino.load_state_dict(self.attn2.state_dict(), strict=False)
461
- with torch.no_grad():
462
- for layer in self.attn_dino.to_out:
463
- for param in layer.parameters():
464
- param.zero_()
465
-
466
- def __getattr__(self, name: str):
467
- try:
468
- return super().__getattr__(name)
469
- except AttributeError:
470
- return getattr(self.transformer, name)
471
-
472
- def forward(
473
- self,
474
- hidden_states: torch.Tensor,
475
- attention_mask: Optional[torch.Tensor] = None,
476
- encoder_hidden_states: Optional[torch.Tensor] = None,
477
- encoder_attention_mask: Optional[torch.Tensor] = None,
478
- timestep: Optional[torch.LongTensor] = None,
479
- cross_attention_kwargs: Dict[str, Any] = None,
480
- class_labels: Optional[torch.LongTensor] = None,
481
- added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
482
- ) -> torch.Tensor:
483
-
484
- """Forward pass with multi-mechanism attention.
485
-
486
- Processing stages:
487
- 1. Material-aware self-attention (MDA)
488
- 2. Reference attention (RA)
489
- 3. Multiview attention (MA) with position-aware attention
490
- 4. Text conditioning (base attention)
491
- 5. DINO feature conditioning (optional)
492
- 6. Position-aware conditioning
493
- 7. Feed-forward network
494
-
495
- Args:
496
- hidden_states: Input features [B * N_materials * N_views, Seq_len, Feat_dim]
497
- See base transformer for other parameters
498
-
499
- Returns:
500
- torch.Tensor: Output features
501
- """
502
- # [Full multi-mechanism processing pipeline...]
503
- # Key processing stages:
504
- # 1. Material-aware self-attention (handles albedo/mr separation)
505
- # 2. Reference attention (conditioned on reference features)
506
- # 3. View-to-view attention with geometric constraints
507
- # 4. Text-to-image cross-attention
508
- # 5. DINO feature fusion (when enabled)
509
- # 6. Positional conditioning (RoPE-style)
510
- # 7. Feed-forward network with conditional normalization
511
-
512
- # Notice that normalization is always applied before the real computation in the following blocks.
513
- # 0. Self-Attention
514
- batch_size = hidden_states.shape[0]
515
-
516
- cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {}
517
- num_in_batch = cross_attention_kwargs.pop("num_in_batch", 1)
518
- mode = cross_attention_kwargs.pop("mode", None)
519
- mva_scale = cross_attention_kwargs.pop("mva_scale", 1.0)
520
- ref_scale = cross_attention_kwargs.pop("ref_scale", 1.0)
521
- condition_embed_dict = cross_attention_kwargs.pop("condition_embed_dict", None)
522
- dino_hidden_states = cross_attention_kwargs.pop("dino_hidden_states", None)
523
- position_voxel_indices = cross_attention_kwargs.pop("position_voxel_indices", None)
524
- N_pbr = len(self.pbr_setting) if self.pbr_setting is not None else 1
525
-
526
- if self.norm_type == "ada_norm":
527
- norm_hidden_states = self.norm1(hidden_states, timestep)
528
- elif self.norm_type == "ada_norm_zero":
529
- norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.norm1(
530
- hidden_states, timestep, class_labels, hidden_dtype=hidden_states.dtype
531
- )
532
- elif self.norm_type in ["layer_norm", "layer_norm_i2vgen"]:
533
- norm_hidden_states = self.norm1(hidden_states)
534
- elif self.norm_type == "ada_norm_continuous":
535
- norm_hidden_states = self.norm1(hidden_states, added_cond_kwargs["pooled_text_emb"])
536
- elif self.norm_type == "ada_norm_single":
537
- shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (
538
- self.scale_shift_table[None] + timestep.reshape(batch_size, 6, -1)
539
- ).chunk(6, dim=1)
540
- norm_hidden_states = self.norm1(hidden_states)
541
- norm_hidden_states = norm_hidden_states * (1 + scale_msa) + shift_msa
542
- else:
543
- raise ValueError("Incorrect norm used")
544
-
545
- if self.pos_embed is not None:
546
- norm_hidden_states = self.pos_embed(norm_hidden_states)
547
-
548
- # 1. Prepare GLIGEN inputs
549
- cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {}
550
- gligen_kwargs = cross_attention_kwargs.pop("gligen", None)
551
-
552
- if self.use_mda:
553
- mda_norm_hidden_states = rearrange(
554
- norm_hidden_states, "(b n_pbr n) l c -> b n_pbr n l c", n=num_in_batch, n_pbr=N_pbr
555
- )
556
- attn_output = self.attn1(
557
- mda_norm_hidden_states,
558
- encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None,
559
- attention_mask=attention_mask,
560
- **cross_attention_kwargs,
561
- )
562
- attn_output = rearrange(attn_output, "b n_pbr n l c -> (b n_pbr n) l c")
563
- else:
564
- attn_output = self.attn1(
565
- norm_hidden_states,
566
- encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None,
567
- attention_mask=attention_mask,
568
- **cross_attention_kwargs,
569
- )
570
-
571
- if self.norm_type == "ada_norm_zero":
572
- attn_output = gate_msa.unsqueeze(1) * attn_output
573
- elif self.norm_type == "ada_norm_single":
574
- attn_output = gate_msa * attn_output
575
-
576
- hidden_states = attn_output + hidden_states
577
- if hidden_states.ndim == 4:
578
- hidden_states = hidden_states.squeeze(1)
579
-
580
- # 1.2 Reference Attention
581
- if "w" in mode:
582
- condition_embed_dict[self.layer_name] = rearrange(
583
- norm_hidden_states, "(b n) l c -> b (n l) c", n=num_in_batch
584
- ) # B, (N L), C
585
-
586
- if "r" in mode and self.use_ra:
587
- condition_embed = condition_embed_dict[self.layer_name]
588
-
589
- #! Only using albedo features for reference attention
590
- ref_norm_hidden_states = rearrange(
591
- norm_hidden_states, "(b n_pbr n) l c -> b n_pbr (n l) c", n=num_in_batch, n_pbr=N_pbr
592
- )[:, 0, ...]
593
-
594
- attn_output = self.attn_refview(
595
- ref_norm_hidden_states,
596
- encoder_hidden_states=condition_embed,
597
- attention_mask=None,
598
- **cross_attention_kwargs,
599
- ) # b (n l) c
600
- attn_output = rearrange(attn_output, "b n_pbr (n l) c -> (b n_pbr n) l c", n=num_in_batch, n_pbr=N_pbr)
601
-
602
- ref_scale_timing = ref_scale
603
- if isinstance(ref_scale, torch.Tensor):
604
- ref_scale_timing = ref_scale.unsqueeze(1).repeat(1, num_in_batch * N_pbr).view(-1)
605
- for _ in range(attn_output.ndim - 1):
606
- ref_scale_timing = ref_scale_timing.unsqueeze(-1)
607
- hidden_states = ref_scale_timing * attn_output + hidden_states
608
- if hidden_states.ndim == 4:
609
- hidden_states = hidden_states.squeeze(1)
610
-
611
- # 1.3 Multiview Attention
612
- if num_in_batch > 1 and self.use_ma:
613
- multivew_hidden_states = rearrange(
614
- norm_hidden_states, "(b n_pbr n) l c -> (b n_pbr) (n l) c", n_pbr=N_pbr, n=num_in_batch
615
- )
616
- position_indices = None
617
- if position_voxel_indices is not None:
618
- if multivew_hidden_states.shape[1] in position_voxel_indices:
619
- position_indices = position_voxel_indices[multivew_hidden_states.shape[1]]
620
-
621
- attn_output = self.attn_multiview(
622
- multivew_hidden_states,
623
- encoder_hidden_states=multivew_hidden_states,
624
- position_indices=position_indices,
625
- n_pbrs=N_pbr,
626
- **cross_attention_kwargs,
627
- )
628
-
629
- attn_output = rearrange(attn_output, "(b n_pbr) (n l) c -> (b n_pbr n) l c", n_pbr=N_pbr, n=num_in_batch)
630
-
631
- hidden_states = mva_scale * attn_output + hidden_states
632
- if hidden_states.ndim == 4:
633
- hidden_states = hidden_states.squeeze(1)
634
-
635
- # 1.2 GLIGEN Control
636
- if gligen_kwargs is not None:
637
- hidden_states = self.fuser(hidden_states, gligen_kwargs["objs"])
638
-
639
- # 3. Cross-Attention
640
- if self.attn2 is not None:
641
- if self.norm_type == "ada_norm":
642
- norm_hidden_states = self.norm2(hidden_states, timestep)
643
- elif self.norm_type in ["ada_norm_zero", "layer_norm", "layer_norm_i2vgen"]:
644
- norm_hidden_states = self.norm2(hidden_states)
645
- elif self.norm_type == "ada_norm_single":
646
- # For PixArt norm2 isn't applied here:
647
- # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L70C1-L76C103
648
- norm_hidden_states = hidden_states
649
- elif self.norm_type == "ada_norm_continuous":
650
- norm_hidden_states = self.norm2(hidden_states, added_cond_kwargs["pooled_text_emb"])
651
- else:
652
- raise ValueError("Incorrect norm")
653
-
654
- if self.pos_embed is not None and self.norm_type != "ada_norm_single":
655
- norm_hidden_states = self.pos_embed(norm_hidden_states)
656
-
657
- attn_output = self.attn2(
658
- norm_hidden_states,
659
- encoder_hidden_states=encoder_hidden_states,
660
- attention_mask=encoder_attention_mask,
661
- **cross_attention_kwargs,
662
- )
663
- hidden_states = attn_output + hidden_states
664
-
665
- # dino attn
666
- if self.use_dino:
667
- dino_hidden_states = dino_hidden_states.unsqueeze(1).repeat(1, N_pbr * num_in_batch, 1, 1)
668
- dino_hidden_states = rearrange(dino_hidden_states, "b n l c -> (b n) l c")
669
- attn_output = self.attn_dino(
670
- norm_hidden_states,
671
- encoder_hidden_states=dino_hidden_states,
672
- attention_mask=None,
673
- **cross_attention_kwargs,
674
- )
675
-
676
- hidden_states = attn_output + hidden_states
677
-
678
- # 4. Feed-forward
679
- # i2vgen doesn't have this norm 🤷‍♂️
680
- if self.norm_type == "ada_norm_continuous":
681
- norm_hidden_states = self.norm3(hidden_states, added_cond_kwargs["pooled_text_emb"])
682
- elif not self.norm_type == "ada_norm_single":
683
- norm_hidden_states = self.norm3(hidden_states)
684
-
685
- if self.norm_type == "ada_norm_zero":
686
- norm_hidden_states = norm_hidden_states * (1 + scale_mlp[:, None]) + shift_mlp[:, None]
687
-
688
- if self.norm_type == "ada_norm_single":
689
- norm_hidden_states = self.norm2(hidden_states)
690
- norm_hidden_states = norm_hidden_states * (1 + scale_mlp) + shift_mlp
691
-
692
- if self._chunk_size is not None:
693
- # "feed_forward_chunk_size" can be used to save memory
694
- ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size)
695
- else:
696
- ff_output = self.ff(norm_hidden_states)
697
-
698
- if self.norm_type == "ada_norm_zero":
699
- ff_output = gate_mlp.unsqueeze(1) * ff_output
700
- elif self.norm_type == "ada_norm_single":
701
- ff_output = gate_mlp * ff_output
702
-
703
- hidden_states = ff_output + hidden_states
704
- if hidden_states.ndim == 4:
705
- hidden_states = hidden_states.squeeze(1)
706
-
707
- return hidden_states
708
-
709
-
710
- class ImageProjModel(torch.nn.Module):
711
-
712
- """Projects image embeddings into cross-attention space.
713
-
714
- Transforms CLIP embeddings into additional context tokens for conditioning.
715
-
716
- Args:
717
- cross_attention_dim: Dimension of attention space
718
- clip_embeddings_dim: Dimension of input CLIP embeddings
719
- clip_extra_context_tokens: Number of context tokens to generate
720
- """
721
-
722
- def __init__(self, cross_attention_dim=1024, clip_embeddings_dim=1024, clip_extra_context_tokens=4):
723
- super().__init__()
724
-
725
- self.generator = None
726
- self.cross_attention_dim = cross_attention_dim
727
- self.clip_extra_context_tokens = clip_extra_context_tokens
728
- self.proj = torch.nn.Linear(clip_embeddings_dim, self.clip_extra_context_tokens * cross_attention_dim)
729
- self.norm = torch.nn.LayerNorm(cross_attention_dim)
730
-
731
- def forward(self, image_embeds):
732
-
733
- """Projects image embeddings to cross-attention context tokens.
734
-
735
- Args:
736
- image_embeds: Input embeddings [B, N, C] or [B, C]
737
-
738
- Returns:
739
- torch.Tensor: Context tokens [B, N*clip_extra_context_tokens, cross_attention_dim]
740
- """
741
-
742
- embeds = image_embeds
743
- num_token = 1
744
- if embeds.dim() == 3:
745
- num_token = embeds.shape[1]
746
- embeds = rearrange(embeds, "b n c -> (b n) c")
747
-
748
- clip_extra_context_tokens = self.proj(embeds).reshape(
749
- -1, self.clip_extra_context_tokens, self.cross_attention_dim
750
- )
751
- clip_extra_context_tokens = self.norm(clip_extra_context_tokens)
752
-
753
- clip_extra_context_tokens = rearrange(clip_extra_context_tokens, "(b nt) n c -> b (nt n) c", nt=num_token)
754
-
755
- return clip_extra_context_tokens
756
-
757
-
758
- class UNet2p5DConditionModel(torch.nn.Module):
759
-
760
- """2.5D UNet extension for multiview PBR generation.
761
-
762
- Enhances standard 2D UNet with:
763
- - Multiview attention mechanisms
764
- - Material-aware processing
765
- - Position-aware conditioning
766
- - Dual-stream reference processing
767
-
768
- Args:
769
- unet: Base 2D UNet model
770
- train_sched: Training scheduler (DDPM)
771
- val_sched: Validation scheduler (EulerAncestral)
772
- """
773
-
774
- def __init__(
775
- self,
776
- unet: UNet2DConditionModel,
777
- train_sched: DDPMScheduler = None,
778
- val_sched: EulerAncestralDiscreteScheduler = None,
779
- ) -> None:
780
- super().__init__()
781
- self.unet = unet
782
- self.train_sched = train_sched
783
- self.val_sched = val_sched
784
-
785
- self.use_ma = True
786
- self.use_ra = True
787
- self.use_mda = True
788
- self.use_dino = True
789
- self.use_position_rope = True
790
- self.use_learned_text_clip = True
791
- self.use_dual_stream = True
792
- self.pbr_setting = ["albedo", "mr"]
793
- self.pbr_token_channels = 77
794
-
795
- if self.use_dual_stream and self.use_ra:
796
- self.unet_dual = copy.deepcopy(unet)
797
- self.init_attention(self.unet_dual)
798
-
799
- self.init_attention(
800
- self.unet,
801
- use_ma=self.use_ma,
802
- use_ra=self.use_ra,
803
- use_dino=self.use_dino,
804
- use_mda=self.use_mda,
805
- pbr_setting=self.pbr_setting,
806
- )
807
- self.init_condition(use_dino=self.use_dino)
808
-
809
- @staticmethod
810
- def from_pretrained(pretrained_model_name_or_path, **kwargs):
811
- torch_dtype = kwargs.pop("torch_dtype", torch.float32)
812
- config_path = os.path.join(pretrained_model_name_or_path, "config.json")
813
- unet_ckpt_path = os.path.join(pretrained_model_name_or_path, "diffusion_pytorch_model.bin")
814
- with open(config_path, "r", encoding="utf-8") as file:
815
- config = json.load(file)
816
- unet = UNet2DConditionModel(**config)
817
- unet_2p5d = UNet2p5DConditionModel(unet)
818
- unet_2p5d.unet.conv_in = torch.nn.Conv2d(
819
- 12,
820
- unet.conv_in.out_channels,
821
- kernel_size=unet.conv_in.kernel_size,
822
- stride=unet.conv_in.stride,
823
- padding=unet.conv_in.padding,
824
- dilation=unet.conv_in.dilation,
825
- groups=unet.conv_in.groups,
826
- bias=unet.conv_in.bias is not None,
827
- )
828
- unet_ckpt = torch.load(unet_ckpt_path, map_location="cpu", weights_only=True)
829
- unet_2p5d.load_state_dict(unet_ckpt, strict=True)
830
- unet_2p5d = unet_2p5d.to(torch_dtype)
831
- return unet_2p5d
832
-
833
- def init_condition(self, use_dino):
834
-
835
- """Initializes conditioning mechanisms for multiview PBR generation.
836
-
837
- Sets up:
838
- 1. Learned text embeddings: Material-specific tokens (albedo, mr) initialized to zeros
839
- 2. DINO projector: Model to process DINO-ViT features for cross-attention
840
-
841
- Args:
842
- use_dino: Flag to enable DINO feature integration
843
- """
844
-
845
- if self.use_learned_text_clip:
846
- for token in self.pbr_setting:
847
- self.unet.register_parameter(
848
- f"learned_text_clip_{token}", nn.Parameter(torch.zeros(self.pbr_token_channels, 1024))
849
- )
850
- self.unet.learned_text_clip_ref = nn.Parameter(torch.zeros(self.pbr_token_channels, 1024))
851
-
852
- if use_dino:
853
- self.unet.image_proj_model_dino = ImageProjModel(
854
- cross_attention_dim=self.unet.config.cross_attention_dim,
855
- clip_embeddings_dim=1536,
856
- clip_extra_context_tokens=4,
857
- )
858
-
859
- def init_attention(self, unet, use_ma=False, use_ra=False, use_mda=False, use_dino=False, pbr_setting=None):
860
-
861
- """Recursively replaces standard transformers with enhanced 2.5D blocks.
862
-
863
- Processes UNet architecture:
864
- 1. Downsampling blocks: Replaces transformers in attention layers
865
- 2. Middle block: Upgrades central transformers
866
- 3. Upsampling blocks: Modifies decoder transformers
867
-
868
- Args:
869
- unet: UNet model to enhance
870
- use_ma: Enable multiview attention
871
- use_ra: Enable reference attention
872
- use_mda: Enable material-specific attention
873
- use_dino: Enable DINO feature integration
874
- pbr_setting: List of PBR materials
875
- """
876
-
877
- for down_block_i, down_block in enumerate(unet.down_blocks):
878
- if hasattr(down_block, "has_cross_attention") and down_block.has_cross_attention:
879
- for attn_i, attn in enumerate(down_block.attentions):
880
- for transformer_i, transformer in enumerate(attn.transformer_blocks):
881
- if isinstance(transformer, BasicTransformerBlock):
882
- attn.transformer_blocks[transformer_i] = Basic2p5DTransformerBlock(
883
- transformer,
884
- f"down_{down_block_i}_{attn_i}_{transformer_i}",
885
- use_ma,
886
- use_ra,
887
- use_mda,
888
- use_dino,
889
- pbr_setting,
890
- )
891
-
892
- if hasattr(unet.mid_block, "has_cross_attention") and unet.mid_block.has_cross_attention:
893
- for attn_i, attn in enumerate(unet.mid_block.attentions):
894
- for transformer_i, transformer in enumerate(attn.transformer_blocks):
895
- if isinstance(transformer, BasicTransformerBlock):
896
- attn.transformer_blocks[transformer_i] = Basic2p5DTransformerBlock(
897
- transformer, f"mid_{attn_i}_{transformer_i}", use_ma, use_ra, use_mda, use_dino, pbr_setting
898
- )
899
-
900
- for up_block_i, up_block in enumerate(unet.up_blocks):
901
- if hasattr(up_block, "has_cross_attention") and up_block.has_cross_attention:
902
- for attn_i, attn in enumerate(up_block.attentions):
903
- for transformer_i, transformer in enumerate(attn.transformer_blocks):
904
- if isinstance(transformer, BasicTransformerBlock):
905
- attn.transformer_blocks[transformer_i] = Basic2p5DTransformerBlock(
906
- transformer,
907
- f"up_{up_block_i}_{attn_i}_{transformer_i}",
908
- use_ma,
909
- use_ra,
910
- use_mda,
911
- use_dino,
912
- pbr_setting,
913
- )
914
-
915
- def __getattr__(self, name: str):
916
- try:
917
- return super().__getattr__(name)
918
- except AttributeError:
919
- return getattr(self.unet, name)
920
-
921
- def forward(
922
- self,
923
- sample,
924
- timestep,
925
- encoder_hidden_states,
926
- *args,
927
- added_cond_kwargs=None,
928
- cross_attention_kwargs=None,
929
- down_intrablock_additional_residuals=None,
930
- down_block_res_samples=None,
931
- mid_block_res_sample=None,
932
- **cached_condition,
933
- ):
934
-
935
- """Forward pass with multiview/material conditioning.
936
-
937
- Key stages:
938
- 1. Input preparation (concat normal/position maps)
939
- 2. Reference feature extraction (dual-stream)
940
- 3. Position encoding (voxel indices)
941
- 4. DINO feature projection
942
- 5. Main UNet processing with attention conditioning
943
-
944
- Args:
945
- sample: Input latents [B, N_pbr, N_gen, C, H, W]
946
- cached_condition: Dictionary containing:
947
- - embeds_normal: Normal map embeddings
948
- - embeds_position: Position map embeddings
949
- - ref_latents: Reference image latents
950
- - dino_hidden_states: DINO features
951
- - position_maps: 3D position maps
952
- - mva_scale: Multiview attention scale
953
- - ref_scale: Reference attention scale
954
-
955
- Returns:
956
- torch.Tensor: Output features
957
- """
958
-
959
- B, N_pbr, N_gen, _, H, W = sample.shape
960
- assert H == W
961
-
962
- if "cache" not in cached_condition:
963
- cached_condition["cache"] = {}
964
-
965
- sample = [sample]
966
- if "embeds_normal" in cached_condition:
967
- sample.append(cached_condition["embeds_normal"].unsqueeze(1).repeat(1, N_pbr, 1, 1, 1, 1))
968
- if "embeds_position" in cached_condition:
969
- sample.append(cached_condition["embeds_position"].unsqueeze(1).repeat(1, N_pbr, 1, 1, 1, 1))
970
- sample = torch.cat(sample, dim=-3)
971
-
972
- sample = rearrange(sample, "b n_pbr n c h w -> (b n_pbr n) c h w")
973
-
974
- encoder_hidden_states_gen = encoder_hidden_states.unsqueeze(-3).repeat(1, 1, N_gen, 1, 1)
975
- encoder_hidden_states_gen = rearrange(encoder_hidden_states_gen, "b n_pbr n l c -> (b n_pbr n) l c")
976
-
977
- if added_cond_kwargs is not None:
978
- text_embeds_gen = added_cond_kwargs["text_embeds"].unsqueeze(1).repeat(1, N_gen, 1)
979
- text_embeds_gen = rearrange(text_embeds_gen, "b n c -> (b n) c")
980
- time_ids_gen = added_cond_kwargs["time_ids"].unsqueeze(1).repeat(1, N_gen, 1)
981
- time_ids_gen = rearrange(time_ids_gen, "b n c -> (b n) c")
982
- added_cond_kwargs_gen = {"text_embeds": text_embeds_gen, "time_ids": time_ids_gen}
983
- else:
984
- added_cond_kwargs_gen = None
985
-
986
- if self.use_position_rope:
987
- if "position_voxel_indices" in cached_condition["cache"]:
988
- position_voxel_indices = cached_condition["cache"]["position_voxel_indices"]
989
- else:
990
- if "position_maps" in cached_condition:
991
- position_voxel_indices = calc_multires_voxel_idxs(
992
- cached_condition["position_maps"],
993
- grid_resolutions=[H, H // 2, H // 4, H // 8],
994
- voxel_resolutions=[H * 8, H * 4, H * 2, H],
995
- )
996
- cached_condition["cache"]["position_voxel_indices"] = position_voxel_indices
997
- else:
998
- position_voxel_indices = None
999
-
1000
- if self.use_dino:
1001
- if "dino_hidden_states_proj" in cached_condition["cache"]:
1002
- dino_hidden_states = cached_condition["cache"]["dino_hidden_states_proj"]
1003
- else:
1004
- assert "dino_hidden_states" in cached_condition
1005
- dino_hidden_states = cached_condition["dino_hidden_states"]
1006
- dino_hidden_states = self.image_proj_model_dino(dino_hidden_states)
1007
- cached_condition["cache"]["dino_hidden_states_proj"] = dino_hidden_states
1008
- else:
1009
- dino_hidden_states = None
1010
-
1011
- if self.use_ra:
1012
- if "condition_embed_dict" in cached_condition["cache"]:
1013
- condition_embed_dict = cached_condition["cache"]["condition_embed_dict"]
1014
- else:
1015
- condition_embed_dict = {}
1016
- ref_latents = cached_condition["ref_latents"]
1017
- N_ref = ref_latents.shape[1]
1018
-
1019
- if not self.use_dual_stream:
1020
- ref_latents = [ref_latents]
1021
- if "embeds_normal" in cached_condition:
1022
- ref_latents.append(torch.zeros_like(ref_latents[0]))
1023
- if "embeds_position" in cached_condition:
1024
- ref_latents.append(torch.zeros_like(ref_latents[0]))
1025
- ref_latents = torch.cat(ref_latents, dim=2)
1026
-
1027
- ref_latents = rearrange(ref_latents, "b n c h w -> (b n) c h w")
1028
-
1029
- encoder_hidden_states_ref = self.unet.learned_text_clip_ref.repeat(B, N_ref, 1, 1)
1030
-
1031
- encoder_hidden_states_ref = rearrange(encoder_hidden_states_ref, "b n l c -> (b n) l c")
1032
-
1033
- if added_cond_kwargs is not None:
1034
- text_embeds_ref = added_cond_kwargs["text_embeds"].unsqueeze(1).repeat(1, N_ref, 1)
1035
- text_embeds_ref = rearrange(text_embeds_ref, "b n c -> (b n) c")
1036
- time_ids_ref = added_cond_kwargs["time_ids"].unsqueeze(1).repeat(1, N_ref, 1)
1037
- time_ids_ref = rearrange(time_ids_ref, "b n c -> (b n) c")
1038
- added_cond_kwargs_ref = {
1039
- "text_embeds": text_embeds_ref,
1040
- "time_ids": time_ids_ref,
1041
- }
1042
- else:
1043
- added_cond_kwargs_ref = None
1044
-
1045
- noisy_ref_latents = ref_latents
1046
- timestep_ref = 0
1047
- if self.use_dual_stream:
1048
- unet_ref = self.unet_dual
1049
- else:
1050
- unet_ref = self.unet
1051
- unet_ref(
1052
- noisy_ref_latents,
1053
- timestep_ref,
1054
- encoder_hidden_states=encoder_hidden_states_ref,
1055
- class_labels=None,
1056
- added_cond_kwargs=added_cond_kwargs_ref,
1057
- # **kwargs
1058
- return_dict=False,
1059
- cross_attention_kwargs={
1060
- "mode": "w",
1061
- "num_in_batch": N_ref,
1062
- "condition_embed_dict": condition_embed_dict,
1063
- },
1064
- )
1065
- cached_condition["cache"]["condition_embed_dict"] = condition_embed_dict
1066
- else:
1067
- condition_embed_dict = None
1068
-
1069
- mva_scale = cached_condition.get("mva_scale", 1.0)
1070
- ref_scale = cached_condition.get("ref_scale", 1.0)
1071
-
1072
- return self.unet(
1073
- sample,
1074
- timestep,
1075
- encoder_hidden_states_gen,
1076
- *args,
1077
- class_labels=None,
1078
- added_cond_kwargs=added_cond_kwargs_gen,
1079
- down_intrablock_additional_residuals=(
1080
- [sample.to(dtype=self.unet.dtype) for sample in down_intrablock_additional_residuals]
1081
- if down_intrablock_additional_residuals is not None
1082
- else None
1083
- ),
1084
- down_block_additional_residuals=(
1085
- [sample.to(dtype=self.unet.dtype) for sample in down_block_res_samples]
1086
- if down_block_res_samples is not None
1087
- else None
1088
- ),
1089
- mid_block_additional_residual=(
1090
- mid_block_res_sample.to(dtype=self.unet.dtype) if mid_block_res_sample is not None else None
1091
- ),
1092
- return_dict=False,
1093
- cross_attention_kwargs={
1094
- "mode": "r",
1095
- "num_in_batch": N_gen,
1096
- "dino_hidden_states": dino_hidden_states,
1097
- "condition_embed_dict": condition_embed_dict,
1098
- "mva_scale": mva_scale,
1099
- "ref_scale": ref_scale,
1100
- "position_voxel_indices": position_voxel_indices,
1101
- },
1102
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/packages/custom_rasterizer/custom_rasterizer/__init__.py DELETED
@@ -1,4 +0,0 @@
1
- """
2
- from .render import rasterize, interpolate
3
- """
4
- from .render import *
 
 
 
 
 
hy3dpaint/packages/custom_rasterizer/custom_rasterizer/render.py DELETED
@@ -1,32 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import custom_rasterizer_kernel
16
- import torch
17
-
18
-
19
- def rasterize(pos, tri, resolution, clamp_depth=torch.zeros(0), use_depth_prior=0):
20
- assert pos.device == tri.device
21
- findices, barycentric = custom_rasterizer_kernel.rasterize_image(
22
- pos[0], tri, clamp_depth, resolution[1], resolution[0], 1e-6, use_depth_prior
23
- )
24
- return findices, barycentric
25
-
26
-
27
- def interpolate(col, findices, barycentric, tri):
28
- f = findices - 1 + (findices == 0)
29
- vcol = col[0, tri.long()[f.long()]]
30
- result = barycentric.view(*barycentric.shape, 1) * vcol
31
- result = torch.sum(result, axis=-2)
32
- return result.view(1, *result.shape)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/packages/custom_rasterizer/setup.py DELETED
@@ -1,40 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- from setuptools import setup, find_packages
16
- import torch
17
- from torch.utils.cpp_extension import BuildExtension, CUDAExtension, CppExtension
18
-
19
- # build custom rasterizer
20
-
21
- custom_rasterizer_module = CUDAExtension(
22
- "custom_rasterizer_kernel",
23
- [
24
- "lib/custom_rasterizer_kernel/rasterizer.cpp",
25
- "lib/custom_rasterizer_kernel/grid_neighbor.cpp",
26
- "lib/custom_rasterizer_kernel/rasterizer_gpu.cu",
27
- ],
28
- )
29
-
30
- setup(
31
- packages=find_packages(),
32
- version="0.1",
33
- name="custom_rasterizer",
34
- include_package_data=True,
35
- package_dir={"": "."},
36
- ext_modules=[
37
- custom_rasterizer_module,
38
- ],
39
- cmdclass={"build_ext": BuildExtension},
40
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/src/__init__.py DELETED
@@ -1,13 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/src/utils/__init__.py DELETED
@@ -1,13 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/src/utils/train_util.py DELETED
@@ -1,40 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import importlib
16
-
17
-
18
- def count_params(model, verbose=False):
19
- total_params = sum(p.numel() for p in model.parameters())
20
- if verbose:
21
- print(f"{model.__class__.__name__} has {total_params*1.e-6:.2f} M params.")
22
- return total_params
23
-
24
-
25
- def instantiate_from_config(config):
26
- if not "target" in config:
27
- if config == "__is_first_stage__":
28
- return None
29
- elif config == "__is_unconditional__":
30
- return None
31
- raise KeyError("Expected key `target` to instantiate.")
32
- return get_obj_from_str(config["target"])(**config.get("params", dict()))
33
-
34
-
35
- def get_obj_from_str(string, reload=False):
36
- module, cls = string.rsplit(".", 1)
37
- if reload:
38
- module_imp = importlib.import_module(module)
39
- importlib.reload(module_imp)
40
- return getattr(importlib.import_module(module, package=None), cls)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/textureGenPipeline.py DELETED
@@ -1,192 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import os
16
- import torch
17
- import copy
18
- import trimesh
19
- import numpy as np
20
- from PIL import Image
21
- from typing import List
22
- from DifferentiableRenderer.MeshRender import MeshRender
23
- from utils.simplify_mesh_utils import remesh_mesh
24
- from utils.multiview_utils import multiviewDiffusionNet
25
- from utils.pipeline_utils import ViewProcessor
26
- from utils.image_super_utils import imageSuperNet
27
- from utils.uvwrap_utils import mesh_uv_wrap
28
- from DifferentiableRenderer.mesh_utils import convert_obj_to_glb
29
- import warnings
30
-
31
- warnings.filterwarnings("ignore")
32
- from diffusers.utils import logging as diffusers_logging
33
-
34
- diffusers_logging.set_verbosity(50)
35
-
36
-
37
- class Hunyuan3DPaintConfig:
38
- def __init__(self, max_num_view, resolution):
39
- self.device = "cuda"
40
-
41
- self.multiview_cfg_path = "cfgs/hunyuan-paint-pbr.yaml"
42
- self.custom_pipeline = "hunyuanpaintpbr"
43
- self.multiview_pretrained_path = "tencent/Hunyuan3D-2.1"
44
- self.dino_ckpt_path = "facebook/dinov2-giant"
45
- self.realesrgan_ckpt_path = "ckpt/RealESRGAN_x4plus.pth"
46
-
47
- self.raster_mode = "cr"
48
- self.bake_mode = "back_sample"
49
- self.render_size = 1024 * 2
50
- self.texture_size = 1024 * 4
51
- self.max_selected_view_num = max_num_view
52
- self.resolution = resolution
53
- self.bake_exp = 4
54
- self.merge_method = "fast"
55
-
56
- # view selection
57
- self.candidate_camera_azims = [0, 90, 180, 270, 0, 180]
58
- self.candidate_camera_elevs = [0, 0, 0, 0, 90, -90]
59
- self.candidate_view_weights = [1, 0.1, 0.5, 0.1, 0.05, 0.05]
60
-
61
- for azim in range(0, 360, 30):
62
- self.candidate_camera_azims.append(azim)
63
- self.candidate_camera_elevs.append(20)
64
- self.candidate_view_weights.append(0.01)
65
-
66
- self.candidate_camera_azims.append(azim)
67
- self.candidate_camera_elevs.append(-20)
68
- self.candidate_view_weights.append(0.01)
69
-
70
-
71
- class Hunyuan3DPaintPipeline:
72
-
73
- def __init__(self, config=None) -> None:
74
- self.config = config if config is not None else Hunyuan3DPaintConfig()
75
- self.models = {}
76
- self.stats_logs = {}
77
- self.render = MeshRender(
78
- default_resolution=self.config.render_size,
79
- texture_size=self.config.texture_size,
80
- bake_mode=self.config.bake_mode,
81
- raster_mode=self.config.raster_mode,
82
- )
83
- self.view_processor = ViewProcessor(self.config, self.render)
84
- self.load_models()
85
-
86
- def load_models(self):
87
- torch.cuda.empty_cache()
88
- self.models["super_model"] = imageSuperNet(self.config)
89
- self.models["multiview_model"] = multiviewDiffusionNet(self.config)
90
- print("Models Loaded.")
91
-
92
- @torch.no_grad()
93
- def __call__(self, mesh_path=None, image_path=None, output_mesh_path=None, use_remesh=True, save_glb=True):
94
- """Generate texture for 3D mesh using multiview diffusion"""
95
- # Ensure image_prompt is a list
96
- if isinstance(image_path, str):
97
- image_prompt = Image.open(image_path)
98
- elif isinstance(image_path, Image.Image):
99
- image_prompt = image_path
100
- if not isinstance(image_prompt, List):
101
- image_prompt = [image_prompt]
102
- else:
103
- image_prompt = image_path
104
-
105
- # Process mesh
106
- path = os.path.dirname(mesh_path)
107
- if use_remesh:
108
- processed_mesh_path = os.path.join(path, "white_mesh_remesh.obj")
109
- remesh_mesh(mesh_path, processed_mesh_path)
110
- else:
111
- processed_mesh_path = mesh_path
112
-
113
- # Output path
114
- if output_mesh_path is None:
115
- output_mesh_path = os.path.join(path, f"textured_mesh.obj")
116
-
117
- # Load mesh
118
- mesh = trimesh.load(processed_mesh_path)
119
- mesh = mesh_uv_wrap(mesh)
120
- self.render.load_mesh(mesh=mesh)
121
-
122
- ########### View Selection #########
123
- selected_camera_elevs, selected_camera_azims, selected_view_weights = self.view_processor.bake_view_selection(
124
- self.config.candidate_camera_elevs,
125
- self.config.candidate_camera_azims,
126
- self.config.candidate_view_weights,
127
- self.config.max_selected_view_num,
128
- )
129
-
130
- normal_maps = self.view_processor.render_normal_multiview(
131
- selected_camera_elevs, selected_camera_azims, use_abs_coor=True
132
- )
133
- position_maps = self.view_processor.render_position_multiview(selected_camera_elevs, selected_camera_azims)
134
-
135
- ########## Style ###########
136
- image_caption = "high quality"
137
- image_style = []
138
- for image in image_prompt:
139
- image = image.resize((512, 512))
140
- if image.mode == "RGBA":
141
- white_bg = Image.new("RGB", image.size, (255, 255, 255))
142
- white_bg.paste(image, mask=image.getchannel("A"))
143
- image = white_bg
144
- image_style.append(image)
145
- image_style = [image.convert("RGB") for image in image_style]
146
-
147
- ########### Multiview ##########
148
- multiviews_pbr = self.models["multiview_model"](
149
- image_style,
150
- normal_maps + position_maps,
151
- prompt=image_caption,
152
- custom_view_size=self.config.resolution,
153
- resize_input=True,
154
- )
155
- ########### Enhance ##########
156
- enhance_images = {}
157
- enhance_images["albedo"] = copy.deepcopy(multiviews_pbr["albedo"])
158
- enhance_images["mr"] = copy.deepcopy(multiviews_pbr["mr"])
159
-
160
- for i in range(len(enhance_images["albedo"])):
161
- enhance_images["albedo"][i] = self.models["super_model"](enhance_images["albedo"][i])
162
- enhance_images["mr"][i] = self.models["super_model"](enhance_images["mr"][i])
163
-
164
- ########### Bake ##########
165
- for i in range(len(enhance_images)):
166
- enhance_images["albedo"][i] = enhance_images["albedo"][i].resize(
167
- (self.config.render_size, self.config.render_size)
168
- )
169
- enhance_images["mr"][i] = enhance_images["mr"][i].resize((self.config.render_size, self.config.render_size))
170
- texture, mask = self.view_processor.bake_from_multiview(
171
- enhance_images["albedo"], selected_camera_elevs, selected_camera_azims, selected_view_weights
172
- )
173
- mask_np = (mask.squeeze(-1).cpu().numpy() * 255).astype(np.uint8)
174
- texture_mr, mask_mr = self.view_processor.bake_from_multiview(
175
- enhance_images["mr"], selected_camera_elevs, selected_camera_azims, selected_view_weights
176
- )
177
- mask_mr_np = (mask_mr.squeeze(-1).cpu().numpy() * 255).astype(np.uint8)
178
-
179
- ########## inpaint ###########
180
- texture = self.view_processor.texture_inpaint(texture, mask_np)
181
- self.render.set_texture(texture, force_set=True)
182
- if "mr" in enhance_images:
183
- texture_mr = self.view_processor.texture_inpaint(texture_mr, mask_mr_np)
184
- self.render.set_texture_mr(texture_mr)
185
-
186
- self.render.save_mesh(output_mesh_path, downsample=True)
187
-
188
- if save_glb:
189
- convert_obj_to_glb(output_mesh_path, output_mesh_path.replace(".obj", ".glb"))
190
- output_glb_path = output_mesh_path.replace(".obj", ".glb")
191
-
192
- return output_mesh_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/train.py DELETED
@@ -1,401 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import torch
16
- import os, sys
17
- import argparse
18
- import shutil
19
- import subprocess
20
- from omegaconf import OmegaConf
21
-
22
- from pytorch_lightning import seed_everything
23
- from pytorch_lightning.trainer import Trainer
24
- from pytorch_lightning.strategies import DDPStrategy
25
- from pytorch_lightning.callbacks import Callback
26
- from pytorch_lightning.utilities import rank_zero_only, rank_zero_warn
27
-
28
- from src.utils.train_util import instantiate_from_config
29
- import warnings
30
-
31
- warnings.filterwarnings("ignore")
32
- from diffusers.utils import logging as diffusers_logging
33
-
34
- diffusers_logging.set_verbosity(50)
35
-
36
-
37
- @rank_zero_only
38
- def rank_zero_print(*args):
39
- print(*args)
40
-
41
-
42
- def get_parser(**parser_kwargs):
43
- def str2bool(v):
44
- if isinstance(v, bool):
45
- return v
46
- if v.lower() in ("yes", "true", "t", "y", "1"):
47
- return True
48
- elif v.lower() in ("no", "false", "f", "n", "0"):
49
- return False
50
- else:
51
- raise argparse.ArgumentTypeError("Boolean value expected.")
52
-
53
- parser = argparse.ArgumentParser(**parser_kwargs)
54
- parser.add_argument(
55
- "-r",
56
- "--resume",
57
- type=str,
58
- default=None,
59
- help="resume from checkpoint",
60
- )
61
- parser.add_argument(
62
- "--resume_weights_only",
63
- action="store_true",
64
- help="only resume model weights",
65
- )
66
- parser.add_argument(
67
- "-b",
68
- "--base",
69
- type=str,
70
- default="base_config.yaml",
71
- help="path to base configs",
72
- )
73
- parser.add_argument(
74
- "-n",
75
- "--name",
76
- type=str,
77
- default="",
78
- help="experiment name",
79
- )
80
- parser.add_argument(
81
- "--num_nodes",
82
- type=int,
83
- default=1,
84
- help="number of nodes to use",
85
- )
86
- parser.add_argument(
87
- "--gpus",
88
- type=str,
89
- default="0,",
90
- help="gpu ids to use",
91
- )
92
- parser.add_argument(
93
- "-s",
94
- "--seed",
95
- type=int,
96
- default=42,
97
- help="seed for seed_everything",
98
- )
99
- parser.add_argument(
100
- "-l",
101
- "--logdir",
102
- type=str,
103
- default="logs",
104
- help="directory for logging data",
105
- )
106
- return parser
107
-
108
-
109
- class SetupCallback(Callback):
110
- def __init__(self, resume, logdir, ckptdir, cfgdir, config):
111
- super().__init__()
112
- self.resume = resume
113
- self.logdir = logdir
114
- self.ckptdir = ckptdir
115
- self.cfgdir = cfgdir
116
- self.config = config
117
-
118
- def on_fit_start(self, trainer, pl_module):
119
- if trainer.global_rank == 0:
120
- # Create logdirs and save configs
121
- os.makedirs(self.logdir, exist_ok=True)
122
- os.makedirs(self.ckptdir, exist_ok=True)
123
- os.makedirs(self.cfgdir, exist_ok=True)
124
-
125
- rank_zero_print("Project config")
126
- rank_zero_print(OmegaConf.to_yaml(self.config))
127
- OmegaConf.save(self.config, os.path.join(self.cfgdir, "project.yaml"))
128
-
129
-
130
- class CodeSnapshot(Callback):
131
- """
132
- Modified from https://github.com/threestudio-project/threestudio/blob/main/threestudio/utils/callbacks.py#L60
133
- """
134
-
135
- def __init__(self, savedir):
136
- self.savedir = savedir
137
-
138
- def get_file_list(self):
139
- return [
140
- b.decode()
141
- for b in set(subprocess.check_output('git ls-files -- ":!:configs/*"', shell=True).splitlines())
142
- | set( # hard code, TODO: use config to exclude folders or files
143
- subprocess.check_output("git ls-files --others --exclude-standard", shell=True).splitlines()
144
- )
145
- ]
146
-
147
- @rank_zero_only
148
- def save_code_snapshot(self):
149
- os.makedirs(self.savedir, exist_ok=True)
150
-
151
- # for f in self.get_file_list():
152
- # if not os.path.exists(f) or os.path.isdir(f):
153
- # continue
154
- # os.makedirs(os.path.join(self.savedir, os.path.dirname(f)), exist_ok=True)
155
- # shutil.copyfile(f, os.path.join(self.savedir, f))
156
-
157
- def on_fit_start(self, trainer, pl_module):
158
- try:
159
- self.save_code_snapshot()
160
- except:
161
- rank_zero_warn(
162
- "Code snapshot is not saved. Please make sure you have git installed and are in a git repository."
163
- )
164
-
165
-
166
- if __name__ == "__main__":
167
- # add cwd for convenience and to make classes in this file available when
168
- # running as `python main.py`
169
- sys.path.append(os.getcwd())
170
- torch.set_float32_matmul_precision("medium")
171
-
172
- parser = get_parser()
173
- opt, unknown = parser.parse_known_args()
174
-
175
- cfg_fname = os.path.split(opt.base)[-1]
176
- cfg_name = os.path.splitext(cfg_fname)[0]
177
- exp_name = "-" + opt.name if opt.name != "" else ""
178
- logdir = os.path.join(opt.logdir, cfg_name + exp_name)
179
-
180
- # assert not os.path.exists(logdir) or 'test' in logdir, logdir
181
- if os.path.exists(logdir) and opt.resume is None:
182
- auto_resume_path = os.path.join(logdir, "checkpoints", "last.ckpt")
183
- if os.path.exists(auto_resume_path):
184
- opt.resume = auto_resume_path
185
- print(f"Auto set resume ckpt {opt.resume}")
186
-
187
- ckptdir = os.path.join(logdir, "checkpoints")
188
- cfgdir = os.path.join(logdir, "configs")
189
- codedir = os.path.join(logdir, "code")
190
-
191
- node_rank = int(os.environ.get("NODE_RANK", 0)) # 当前节点的编号
192
- local_rank = int(os.environ.get("LOCAL_RANK", 0)) # 当前节点上的 GPU 编号
193
- num_gpus_per_node = torch.cuda.device_count() # 每个节点上的 GPU 数量
194
-
195
- global_rank = node_rank * num_gpus_per_node + local_rank
196
- seed_everything(opt.seed + global_rank)
197
-
198
- # init configs
199
- config = OmegaConf.load(opt.base)
200
- lightning_config = config.lightning
201
- trainer_config = lightning_config.trainer
202
-
203
- trainer_config["accelerator"] = "gpu"
204
- rank_zero_print(f"Running on GPUs {opt.gpus}")
205
- try:
206
- ngpu = int(opt.gpus)
207
- except:
208
- ngpu = len(opt.gpus.strip(",").split(","))
209
- trainer_config["devices"] = ngpu
210
-
211
- trainer_opt = argparse.Namespace(**trainer_config)
212
- lightning_config.trainer = trainer_config
213
-
214
- # model
215
- model = instantiate_from_config(config.model)
216
-
217
- model_unet = model.unet.unet
218
- model_unet_prefix = "unet.unet."
219
- if hasattr(model_unet, "unet"):
220
- model_unet = model_unet.unet
221
- model_unet_prefix += "unet."
222
-
223
- if getattr(config, "init_unet_from", None):
224
- unet_ckpt_path = config.init_unet_from
225
- sd = torch.load(unet_ckpt_path, map_location="cpu")
226
- model_unet.load_state_dict(sd, strict=True)
227
-
228
- if getattr(config, "init_vae_from", None):
229
- vae_ckpt_path = config.init_vae_from
230
- sd_vae = torch.load(vae_ckpt_path, map_location="cpu")
231
-
232
- def replace_key(key_str):
233
- replace_pairs = [("key", "to_k"), ("query", "to_q"), ("value", "to_v"), ("proj_attn", "to_out.0")]
234
- for replace_pair in replace_pairs:
235
- key_str = key_str.replace(replace_pair[0], replace_pair[1])
236
- return key_str
237
-
238
- sd_vae = {replace_key(k): v for k, v in sd_vae.items()}
239
- model.pipeline.vae.load_state_dict(sd_vae, strict=True)
240
-
241
- if hasattr(model.unet, "controlnet"):
242
- if getattr(config, "init_control_from", None):
243
- unet_ckpt_path = config.init_control_from
244
- sd_control = torch.load(unet_ckpt_path, map_location="cpu")
245
- model.unet.controlnet.load(sd_control, strict=True)
246
-
247
- noise_in_channels = config.model.params.get("noise_in_channels", None)
248
- if noise_in_channels is not None:
249
- with torch.no_grad():
250
- new_conv_in = torch.nn.Conv2d(
251
- noise_in_channels,
252
- model_unet.conv_in.out_channels,
253
- model_unet.conv_in.kernel_size,
254
- model_unet.conv_in.stride,
255
- model_unet.conv_in.padding,
256
- )
257
- new_conv_in.weight.zero_()
258
- new_conv_in.weight[:, : model_unet.conv_in.in_channels, :, :].copy_(model_unet.conv_in.weight)
259
-
260
- new_conv_in.bias.zero_()
261
- new_conv_in.bias[: model_unet.conv_in.bias.size(0)].copy_(model_unet.conv_in.bias)
262
-
263
- model_unet.conv_in = new_conv_in
264
-
265
- if hasattr(model.unet, "controlnet"):
266
- if config.model.params.get("control_in_channels", None):
267
- control_in_channels = config.model.params.control_in_channels
268
- model.unet.controlnet.config["conditioning_channels"] = control_in_channels
269
- condition_conv_in = model.unet.controlnet.controlnet_cond_embedding.conv_in
270
-
271
- new_condition_conv_in = torch.nn.Conv2d(
272
- control_in_channels,
273
- condition_conv_in.out_channels,
274
- kernel_size=condition_conv_in.kernel_size,
275
- stride=condition_conv_in.stride,
276
- padding=condition_conv_in.padding,
277
- )
278
-
279
- with torch.no_grad():
280
- new_condition_conv_in.weight[:, : condition_conv_in.in_channels, :, :] = condition_conv_in.weight
281
- if condition_conv_in.bias is not None:
282
- new_condition_conv_in.bias = condition_conv_in.bias
283
-
284
- model.unet.controlnet.controlnet_cond_embedding.conv_in = new_condition_conv_in
285
-
286
- rank_zero_print(f"Loaded Init ...")
287
-
288
- if getattr(config, "resume_from", None):
289
- cnet_ckpt_path = config.resume_from
290
- sds = torch.load(cnet_ckpt_path, map_location="cpu")["state_dict"]
291
- sd0 = {k[len(model_unet_prefix) :]: v for k, v in sds.items() if model_unet_prefix in k}
292
- # model.unet.unet.unet.load_state_dict(sd0, strict=True)
293
- model_unet.load_state_dict(sd0, strict=True)
294
- if hasattr(model.unet, "controlnet"):
295
- sd1 = {k[16:]: v for k, v in sds.items() if "unet.controlnet." in k}
296
- model.unet.controlnet.load_state_dict(sd1, strict=True)
297
- rank_zero_print(f"Loaded {cnet_ckpt_path} ...")
298
-
299
- if opt.resume and opt.resume_weights_only:
300
- model = model.__class__.load_from_checkpoint(opt.resume, **config.model.params)
301
-
302
- model.logdir = logdir
303
-
304
- # trainer and callbacks
305
- trainer_kwargs = dict()
306
-
307
- # logger
308
- default_logger_cfg = {
309
- "target": "pytorch_lightning.loggers.TensorBoardLogger",
310
- "params": {
311
- "name": "tensorboard",
312
- "save_dir": logdir,
313
- "version": "0",
314
- },
315
- }
316
- logger_cfg = OmegaConf.merge(default_logger_cfg)
317
- trainer_kwargs["logger"] = instantiate_from_config(logger_cfg)
318
-
319
- # model checkpoint
320
- default_modelckpt_cfg = {
321
- "target": "pytorch_lightning.callbacks.ModelCheckpoint",
322
- "params": {
323
- "dirpath": ckptdir,
324
- "filename": "{step:08}",
325
- "verbose": True,
326
- "save_last": True,
327
- "every_n_train_steps": 5000,
328
- "save_top_k": -1, # save all checkpoints
329
- },
330
- }
331
-
332
- if "modelcheckpoint" in lightning_config:
333
- modelckpt_cfg = lightning_config.modelcheckpoint
334
- else:
335
- modelckpt_cfg = OmegaConf.create()
336
- modelckpt_cfg = OmegaConf.merge(default_modelckpt_cfg, modelckpt_cfg)
337
-
338
- # callbacks
339
- default_callbacks_cfg = {
340
- "setup_callback": {
341
- "target": "train.SetupCallback",
342
- "params": {
343
- "resume": opt.resume,
344
- "logdir": logdir,
345
- "ckptdir": ckptdir,
346
- "cfgdir": cfgdir,
347
- "config": config,
348
- },
349
- },
350
- "learning_rate_logger": {
351
- "target": "pytorch_lightning.callbacks.LearningRateMonitor",
352
- "params": {
353
- "logging_interval": "step",
354
- },
355
- },
356
- "code_snapshot": {
357
- "target": "train.CodeSnapshot",
358
- "params": {
359
- "savedir": codedir,
360
- },
361
- },
362
- }
363
- default_callbacks_cfg["checkpoint_callback"] = modelckpt_cfg
364
-
365
- if "callbacks" in lightning_config:
366
- callbacks_cfg = lightning_config.callbacks
367
- else:
368
- callbacks_cfg = OmegaConf.create()
369
- callbacks_cfg = OmegaConf.merge(default_callbacks_cfg, callbacks_cfg)
370
-
371
- trainer_kwargs["callbacks"] = [instantiate_from_config(callbacks_cfg[k]) for k in callbacks_cfg]
372
-
373
- trainer_kwargs["precision"] = "bf16"
374
- trainer_kwargs["strategy"] = DDPStrategy(find_unused_parameters=False)
375
-
376
- # trainer
377
- trainer = Trainer(**trainer_config, **trainer_kwargs, num_nodes=opt.num_nodes, inference_mode=False)
378
- trainer.logdir = logdir
379
-
380
- # data
381
- data = instantiate_from_config(config.data)
382
- data.prepare_data()
383
- data.setup("fit")
384
-
385
- # configure learning rate
386
- base_lr = config.model.base_learning_rate
387
- if "accumulate_grad_batches" in lightning_config.trainer:
388
- accumulate_grad_batches = lightning_config.trainer.accumulate_grad_batches
389
- else:
390
- accumulate_grad_batches = 1
391
- rank_zero_print(f"accumulate_grad_batches = {accumulate_grad_batches}")
392
- lightning_config.trainer.accumulate_grad_batches = accumulate_grad_batches
393
- model.learning_rate = base_lr
394
- rank_zero_print("++++ NOT USING LR SCALING ++++")
395
- rank_zero_print(f"Setting learning rate to {model.learning_rate:.2e}")
396
-
397
- # run training loop
398
- if opt.resume and not opt.resume_weights_only:
399
- trainer.fit(model, data, ckpt_path=opt.resume)
400
- else:
401
- trainer.fit(model, data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/train_examples/001/render_cond/mesh.ply DELETED
Binary file (47.8 kB)
 
hy3dpaint/train_examples/001/render_cond/transforms.json DELETED
@@ -1,838 +0,0 @@
1
- {
2
- "aabb": [
3
- [
4
- -0.5,
5
- -0.5,
6
- -0.5
7
- ],
8
- [
9
- 0.5,
10
- 0.5,
11
- 0.5
12
- ]
13
- ],
14
- "scale": 0.5121457915021708,
15
- "offset": [
16
- 0.00032189488410949707,
17
- -0.03408946841955185,
18
- -0.2757369577884674
19
- ],
20
- "frames": [
21
- {
22
- "file_path": "000.png",
23
- "camera_angle_x": 0.47011032380678586,
24
- "proj_type": 0,
25
- "azimuth": 2.2539364673716054,
26
- "elevation": -1.3001257577149983,
27
- "cam_dis": 3.71849691615005,
28
- "transform_matrix": [
29
- [
30
- -0.7755944728851318,
31
- -0.6082496643066406,
32
- -0.1687772274017334,
33
- -0.6275975704193115
34
- ],
35
- [
36
- -0.6312316656112671,
37
- 0.7473564147949219,
38
- 0.2073766440153122,
39
- 0.7711292505264282
40
- ],
41
- [
42
- -2.3015424233108206e-08,
43
- 0.26737767457962036,
44
- -0.9635918736457825,
45
- -3.583113193511963
46
- ],
47
- [
48
- 0,
49
- 0,
50
- 0,
51
- 1
52
- ]
53
- ]
54
- },
55
- {
56
- "file_path": "001.png",
57
- "camera_angle_x": 0.5079176868006297,
58
- "proj_type": 0,
59
- "azimuth": 5.395529120961399,
60
- "elevation": -0.9221878379342444,
61
- "cam_dis": 3.4470348893999567,
62
- "transform_matrix": [
63
- [
64
- 0.7755942940711975,
65
- 0.5030443668365479,
66
- 0.3813132345676422,
67
- 1.3143998384475708
68
- ],
69
- [
70
- 0.6312316656112671,
71
- -0.6180905103683472,
72
- -0.4685195982456207,
73
- -1.615003228187561
74
- ],
75
- [
76
- -4.003486608894491e-08,
77
- 0.6040781140327454,
78
- -0.7969250082969666,
79
- -2.7470288276672363
80
- ],
81
- [
82
- 0,
83
- 0,
84
- 0,
85
- 1
86
- ]
87
- ]
88
- },
89
- {
90
- "file_path": "002.png",
91
- "camera_angle_x": 0.7090660480350758,
92
- "proj_type": 0,
93
- "azimuth": 3.824732794166502,
94
- "elevation": -0.6818860998422269,
95
- "cam_dis": 2.494654312477568,
96
- "transform_matrix": [
97
- [
98
- 0.6312316060066223,
99
- -0.4888249933719635,
100
- -0.6021600961685181,
101
- -1.5021814107894897
102
- ],
103
- [
104
- -0.7755944132804871,
105
- -0.39783918857574463,
106
- -0.49007895588874817,
107
- -1.222577691078186
108
- ],
109
- [
110
- -2.0854262317016037e-08,
111
- 0.7763853669166565,
112
- -0.6302586197853088,
113
- -1.5722770690917969
114
- ],
115
- [
116
- 0,
117
- 0,
118
- 0,
119
- 1
120
- ]
121
- ]
122
- },
123
- {
124
- "file_path": "003.png",
125
- "camera_angle_x": 0.7207915782104436,
126
- "proj_type": 0,
127
- "azimuth": 6.966325447756295,
128
- "elevation": -0.48204466653151123,
129
- "cam_dis": 2.4558020914544034,
130
- "transform_matrix": [
131
- [
132
- -0.6312316060066223,
133
- 0.35955917835235596,
134
- 0.6872146725654602,
135
- 1.687662959098816
136
- ],
137
- [
138
- 0.7755944728851318,
139
- 0.292633593082428,
140
- 0.5593021512031555,
141
- 1.37353515625
142
- ],
143
- [
144
- 7.981980587601356e-08,
145
- 0.8860490322113037,
146
- -0.46359172463417053,
147
- -1.1384897232055664
148
- ],
149
- [
150
- 0,
151
- 0,
152
- 0,
153
- 1
154
- ]
155
- ]
156
- },
157
- {
158
- "file_path": "004.png",
159
- "camera_angle_x": 1.1560563050396928,
160
- "proj_type": 0,
161
- "azimuth": 3.0393346307690536,
162
- "elevation": -0.30147096310802857,
163
- "cam_dis": 1.5850428518685533,
164
- "transform_matrix": [
165
- [
166
- -0.10207992047071457,
167
- -0.2953740656375885,
168
- -0.9499125480651855,
169
- -1.5056520700454712
170
- ],
171
- [
172
- -0.99477618932724,
173
- 0.03031015396118164,
174
- 0.09747619181871414,
175
- 0.15450391173362732
176
- ],
177
- [
178
- 4.873770365065866e-08,
179
- 0.9549007415771484,
180
- -0.2969251275062561,
181
- -0.47063907980918884
182
- ],
183
- [
184
- 0,
185
- 0,
186
- 0,
187
- 1
188
- ]
189
- ]
190
- },
191
- {
192
- "file_path": "005.png",
193
- "camera_angle_x": 1.1080063733527263,
194
- "proj_type": 0,
195
- "azimuth": 6.180927284358847,
196
- "elevation": -0.13062968094200955,
197
- "cam_dis": 1.6461361738590004,
198
- "transform_matrix": [
199
- [
200
- 0.10207971930503845,
201
- 0.1295779049396515,
202
- 0.9863009452819824,
203
- 1.6235853433609009
204
- ],
205
- [
206
- 0.9947763085365295,
207
- -0.013296757824718952,
208
- -0.10121013969182968,
209
- -0.16660575568675995
210
- ],
211
- [
212
- -1.0836848218787054e-07,
213
- 0.9914802312850952,
214
- -0.1302584558725357,
215
- -0.21442320942878723
216
- ],
217
- [
218
- 0,
219
- 0,
220
- 0,
221
- 1
222
- ]
223
- ]
224
- },
225
- {
226
- "file_path": "006.png",
227
- "camera_angle_x": 1.0631377508801954,
228
- "proj_type": 0,
229
- "azimuth": 4.610130957563951,
230
- "elevation": 0.012136358647873546,
231
- "cam_dis": 1.7085198579433762,
232
- "transform_matrix": [
233
- [
234
- 0.99477618932724,
235
- 0.0012388412142172456,
236
- -0.10207240283489227,
237
- -0.1743927001953125
238
- ],
239
- [
240
- -0.10207992047071457,
241
- 0.012072510085999966,
242
- -0.9947029948234558,
243
- -1.6994696855545044
244
- ],
245
- [
246
- -2.03229033601815e-09,
247
- 0.9999264478683472,
248
- 0.012135906144976616,
249
- 0.020734701305627823
250
- ],
251
- [
252
- 0,
253
- 0,
254
- 0,
255
- 1
256
- ]
257
- ]
258
- },
259
- {
260
- "file_path": "007.png",
261
- "camera_angle_x": 0.5894741433793819,
262
- "proj_type": 0,
263
- "azimuth": 7.751723611153743,
264
- "elevation": 0.06774341874087009,
265
- "cam_dis": 2.9812749570228485,
266
- "transform_matrix": [
267
- [
268
- -0.99477618932724,
269
- -0.006909956689924002,
270
- 0.10184576362371445,
271
- 0.30363020300865173
272
- ],
273
- [
274
- 0.10207990556955338,
275
- -0.06733796000480652,
276
- 0.9924944639205933,
277
- 2.9588990211486816
278
- ],
279
- [
280
- -8.816746444040291e-09,
281
- 0.9977062940597534,
282
- 0.06769154965877533,
283
- 0.20180732011795044
284
- ],
285
- [
286
- 0,
287
- 0,
288
- 0,
289
- 1
290
- ]
291
- ]
292
- },
293
- {
294
- "file_path": "008.png",
295
- "camera_angle_x": 1.1731446025768484,
296
- "proj_type": 0,
297
- "azimuth": 2.6466355490703295,
298
- "elevation": 0.12356134208787495,
299
- "cam_dis": 1.5646078921687379,
300
- "transform_matrix": [
301
- [
302
- -0.47499409317970276,
303
- 0.10845614224672318,
304
- -0.8732801675796509,
305
- -1.3663408756256104
306
- ],
307
- [
308
- -0.8799892067909241,
309
- -0.058541711419820786,
310
- 0.4713726341724396,
311
- 0.7375132441520691
312
- ],
313
- [
314
- 5.2883926571212214e-08,
315
- 0.9923761487007141,
316
- 0.12324699014425278,
317
- 0.19283349812030792
318
- ],
319
- [
320
- 0,
321
- 0,
322
- 0,
323
- 1
324
- ]
325
- ]
326
- },
327
- {
328
- "file_path": "009.png",
329
- "camera_angle_x": 0.9240898657044195,
330
- "proj_type": 0,
331
- "azimuth": 5.788228202660123,
332
- "elevation": 0.17976943361645015,
333
- "cam_dis": 1.9427212715365068,
334
- "transform_matrix": [
335
- [
336
- 0.4749939441680908,
337
- -0.15734440088272095,
338
- 0.8658080101013184,
339
- 1.6820236444473267
340
- ],
341
- [
342
- 0.8799890875816345,
343
- 0.08493020385503769,
344
- -0.4673393964767456,
345
- -0.9079101085662842
346
- ],
347
- [
348
- -2.0080790363863343e-08,
349
- 0.9838849902153015,
350
- 0.17880268394947052,
351
- 0.3473638594150543
352
- ],
353
- [
354
- 0,
355
- 0,
356
- 0,
357
- 1
358
- ]
359
- ]
360
- },
361
- {
362
- "file_path": "010.png",
363
- "camera_angle_x": 1.2064601116642644,
364
- "proj_type": 0,
365
- "azimuth": 4.217431875865226,
366
- "elevation": 0.23655841484996287,
367
- "cam_dis": 1.526559413456265,
368
- "transform_matrix": [
369
- [
370
- 0.8799890875816345,
371
- 0.11131872981786728,
372
- -0.46176549792289734,
373
- -0.7049124240875244
374
- ],
375
- [
376
- -0.47499391436576843,
377
- 0.20623266696929932,
378
- -0.855481743812561,
379
- -1.3059436082839966
380
- ],
381
- [
382
- 2.947104427164504e-08,
383
- 0.9721503853797913,
384
- 0.23435820639133453,
385
- 0.35776183009147644
386
- ],
387
- [
388
- 0,
389
- 0,
390
- 0,
391
- 1
392
- ]
393
- ]
394
- },
395
- {
396
- "file_path": "011.png",
397
- "camera_angle_x": 0.8106133703864192,
398
- "proj_type": 0,
399
- "azimuth": 7.359024529455018,
400
- "elevation": 0.2941368085661682,
401
- "cam_dis": 2.196358226678013,
402
- "transform_matrix": [
403
- [
404
- -0.8799890875816345,
405
- -0.13770732283592224,
406
- 0.454594224691391,
407
- 0.9984517693519592
408
- ],
409
- [
410
- 0.47499391436576843,
411
- -0.25512099266052246,
412
- 0.8421959280967712,
413
- 1.849764108657837
414
- ],
415
- [
416
- -1.5272490827555885e-08,
417
- 0.9570527672767639,
418
- 0.2899138629436493,
419
- 0.6367546319961548
420
- ],
421
- [
422
- 0,
423
- 0,
424
- 0,
425
- 1
426
- ]
427
- ]
428
- },
429
- {
430
- "file_path": "012.png",
431
- "camera_angle_x": 0.6777962932590744,
432
- "proj_type": 0,
433
- "azimuth": 3.4320337124677778,
434
- "elevation": 0.35273892986621935,
435
- "cam_dis": 2.604994196713999,
436
- "transform_matrix": [
437
- [
438
- 0.2863747477531433,
439
- 0.33100035786628723,
440
- -0.8991264700889587,
441
- -2.3422188758850098
442
- ],
443
- [
444
- -0.9581177234649658,
445
- 0.09893371164798737,
446
- -0.2687426805496216,
447
- -0.7000733017921448
448
- ],
449
- [
450
- 5.095670019272802e-08,
451
- 0.9384300708770752,
452
- 0.3454693555831909,
453
- 0.8999457955360413
454
- ],
455
- [
456
- 0,
457
- 0,
458
- 0,
459
- 1
460
- ]
461
- ]
462
- },
463
- {
464
- "file_path": "013.png",
465
- "camera_angle_x": 1.2201901414150222,
466
- "proj_type": 0,
467
- "azimuth": 6.57362636605757,
468
- "elevation": 0.4126354310485474,
469
- "cam_dis": 1.511532217466212,
470
- "transform_matrix": [
471
- [
472
- -0.2863748371601105,
473
- -0.38422903418540955,
474
- 0.8777002096176147,
475
- 1.3266719579696655
476
- ],
477
- [
478
- 0.9581177234649658,
479
- -0.11484345048666,
480
- 0.2623385488986969,
481
- 0.39653322100639343
482
- ],
483
- [
484
- -1.850063036101801e-08,
485
- 0.9160672426223755,
486
- 0.40102484822273254,
487
- 0.6061621308326721
488
- ],
489
- [
490
- 0,
491
- 0,
492
- 0,
493
- 1
494
- ]
495
- ]
496
- },
497
- {
498
- "file_path": "014.png",
499
- "camera_angle_x": 0.5705820549491503,
500
- "proj_type": 0,
501
- "azimuth": 5.002830039262674,
502
- "elevation": 0.4741478890444091,
503
- "cam_dis": 3.077158450915264,
504
- "transform_matrix": [
505
- [
506
- 0.958117663860321,
507
- -0.13075314462184906,
508
- 0.2547825574874878,
509
- 0.7840063571929932
510
- ],
511
- [
512
- 0.2863748073577881,
513
- 0.4374578595161438,
514
- -0.8524201512336731,
515
- -2.6230318546295166
516
- ],
517
- [
518
- -1.4379679669218604e-08,
519
- 0.8896821141242981,
520
- 0.4565805196762085,
521
- 1.4049705266952515
522
- ],
523
- [
524
- 0,
525
- 0,
526
- 0,
527
- 1
528
- ]
529
- ]
530
- },
531
- {
532
- "file_path": "015.png",
533
- "camera_angle_x": 1.1922232332059943,
534
- "proj_type": 0,
535
- "azimuth": 8.144422692852467,
536
- "elevation": 0.5376699116109869,
537
- "cam_dis": 1.5425377326560552,
538
- "transform_matrix": [
539
- [
540
- -0.9581177234649658,
541
- 0.1466628760099411,
542
- -0.24596858024597168,
543
- -0.3794158399105072
544
- ],
545
- [
546
- -0.2863748371601105,
547
- -0.4906865656375885,
548
- 0.8229314684867859,
549
- 1.2694027423858643
550
- ],
551
- [
552
- 1.9456553701502344e-08,
553
- 0.8589044213294983,
554
- 0.5121359825134277,
555
- 0.789989173412323
556
- ],
557
- [
558
- 0,
559
- 0,
560
- 0,
561
- 1
562
- ]
563
- ]
564
- },
565
- {
566
- "file_path": "016.png",
567
- "camera_angle_x": 0.4843348255851337,
568
- "proj_type": 0,
569
- "azimuth": 2.4502860082209676,
570
- "elevation": 0.6036991169616068,
571
- "cam_dis": 3.6113379310693747,
572
- "transform_matrix": [
573
- [
574
- -0.6375443935394287,
575
- 0.4373573660850525,
576
- -0.6342363953590393,
577
- -2.2904417514801025
578
- ],
579
- [
580
- -0.7704136967658997,
581
- -0.3619285523891449,
582
- 0.5248528718948364,
583
- 1.895421028137207
584
- ],
585
- [
586
- 4.0876805940115446e-08,
587
- 0.8232413530349731,
588
- 0.5676915049552917,
589
- 2.050126314163208
590
- ],
591
- [
592
- 0,
593
- 0,
594
- 0,
595
- 1
596
- ]
597
- ]
598
- },
599
- {
600
- "file_path": "017.png",
601
- "camera_angle_x": 1.0484863420149597,
602
- "proj_type": 0,
603
- "azimuth": 5.59187866181076,
604
- "elevation": 0.6728881287032298,
605
- "cam_dis": 1.7301201355596483,
606
- "transform_matrix": [
607
- [
608
- 0.6375443935394287,
609
- -0.4801580607891083,
610
- 0.6024826765060425,
611
- 1.0423673391342163
612
- ],
613
- [
614
- 0.7704136371612549,
615
- 0.39734768867492676,
616
- -0.4985755980014801,
617
- -0.8625956773757935
618
- ],
619
- [
620
- 3.110944390982695e-09,
621
- 0.7820249199867249,
622
- 0.6232471466064453,
623
- 1.0782924890518188
624
- ],
625
- [
626
- 0,
627
- 0,
628
- 0,
629
- 1
630
- ]
631
- ]
632
- },
633
- {
634
- "file_path": "018.png",
635
- "camera_angle_x": 0.8327046868500112,
636
- "proj_type": 0,
637
- "azimuth": 4.021082335015864,
638
- "elevation": 0.7461309541186378,
639
- "cam_dis": 2.1413633661978952,
640
- "transform_matrix": [
641
- [
642
- 0.7704136371612549,
643
- 0.432766854763031,
644
- -0.46816205978393555,
645
- -1.0025050640106201
646
- ],
647
- [
648
- -0.6375443935394287,
649
- 0.5229588747024536,
650
- -0.5657307505607605,
651
- -1.2114349603652954
652
- ],
653
- [
654
- 2.405637644642411e-08,
655
- 0.7343207597732544,
656
- 0.6788026690483093,
657
- 1.4535633325576782
658
- ],
659
- [
660
- 0,
661
- 0,
662
- 0,
663
- 1
664
- ]
665
- ]
666
- },
667
- {
668
- "file_path": "019.png",
669
- "camera_angle_x": 1.0378875875393918,
670
- "proj_type": 0,
671
- "azimuth": 7.162674988605656,
672
- "elevation": 0.8247207741120075,
673
- "cam_dis": 1.7461482839234206,
674
- "transform_matrix": [
675
- [
676
- -0.7704136371612549,
677
- -0.46818602085113525,
678
- 0.4327409267425537,
679
- 0.75562983751297
680
- ],
681
- [
682
- 0.6375443935394287,
683
- -0.5657596588134766,
684
- 0.5229275226593018,
685
- 0.9131090044975281
686
- ],
687
- [
688
- 5.7037539136217674e-09,
689
- 0.6787620186805725,
690
- 0.7343583106994629,
691
- 1.2822984457015991
692
- ],
693
- [
694
- 0,
695
- 0,
696
- 0,
697
- 1
698
- ]
699
- ]
700
- },
701
- {
702
- "file_path": "020.png",
703
- "camera_angle_x": 1.181698605034973,
704
- "proj_type": 0,
705
- "azimuth": 3.235684171618416,
706
- "elevation": 0.9106684775229277,
707
- "cam_dis": 1.5546175155141897,
708
- "transform_matrix": [
709
- [
710
- 0.09395270049571991,
711
- 0.7864198088645935,
712
- -0.6105054020881653,
713
- -0.9491023421287537
714
- ],
715
- [
716
- -0.9955766797065735,
717
- 0.07421454787254333,
718
- -0.057613469660282135,
719
- -0.08956695348024368
720
- ],
721
- [
722
- 1.1830872281848315e-08,
723
- 0.613217830657959,
724
- 0.7899138331413269,
725
- 1.2280138731002808
726
- ],
727
- [
728
- 0,
729
- 0,
730
- 0,
731
- 1
732
- ]
733
- ]
734
- },
735
- {
736
- "file_path": "021.png",
737
- "camera_angle_x": 1.197224930669693,
738
- "proj_type": 0,
739
- "azimuth": 6.377276825208208,
740
- "elevation": 1.0074435322145288,
741
- "cam_dis": 1.536877375373375,
742
- "transform_matrix": [
743
- [
744
- -0.09395267069339752,
745
- -0.8417297005653381,
746
- 0.5316617488861084,
747
- 0.81709885597229
748
- ],
749
- [
750
- 0.9955767393112183,
751
- -0.0794341042637825,
752
- 0.05017295107245445,
753
- 0.07710976153612137
754
- ],
755
- [
756
- -3.2885168366192374e-08,
757
- 0.5340239405632019,
758
- 0.8454694151878357,
759
- 1.2993828058242798
760
- ],
761
- [
762
- 0,
763
- 0,
764
- 0,
765
- 1
766
- ]
767
- ]
768
- },
769
- {
770
- "file_path": "022.png",
771
- "camera_angle_x": 0.7684080505969957,
772
- "proj_type": 0,
773
- "azimuth": 4.806480498413312,
774
- "elevation": 1.122126648780772,
775
- "cam_dis": 2.310502047946327,
776
- "transform_matrix": [
777
- [
778
- 0.9955766797065735,
779
- -0.08465376496315002,
780
- 0.040753625333309174,
781
- 0.09416133165359497
782
- ],
783
- [
784
- 0.0939527377486229,
785
- 0.8970394134521484,
786
- -0.43184858560562134,
787
- -0.9977869987487793
788
- ],
789
- [
790
- -3.0048674659610697e-09,
791
- 0.43376728892326355,
792
- 0.9010249376296997,
793
- 2.081820011138916
794
- ],
795
- [
796
- 0,
797
- 0,
798
- 0,
799
- 1
800
- ]
801
- ]
802
- },
803
- {
804
- "file_path": "023.png",
805
- "camera_angle_x": 1.2027037528892426,
806
- "proj_type": 0,
807
- "azimuth": 7.948073152003104,
808
- "elevation": 1.2750349593459749,
809
- "cam_dis": 1.5307354901339616,
810
- "transform_matrix": [
811
- [
812
- -0.9955767393112183,
813
- 0.08987335115671158,
814
- -0.027384238317608833,
815
- -0.04191803187131882
816
- ],
817
- [
818
- -0.0939527302980423,
819
- -0.9523493051528931,
820
- 0.29017895460128784,
821
- 0.4441872537136078
822
- ],
823
- [
824
- -4.757865657012417e-09,
825
- 0.29146820306777954,
826
- 0.9565805196762085,
827
- 1.4642717838287354
828
- ],
829
- [
830
- 0,
831
- 0,
832
- 0,
833
- 1
834
- ]
835
- ]
836
- }
837
- ]
838
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/train_examples/001/render_tex/transforms.json DELETED
@@ -1,226 +0,0 @@
1
- {
2
- "aabb": [
3
- [
4
- -0.5,
5
- -0.5,
6
- -0.5
7
- ],
8
- [
9
- 0.5,
10
- 0.5,
11
- 0.5
12
- ]
13
- ],
14
- "scale": 0.5121457915021708,
15
- "offset": [
16
- 0.00032189488410949707,
17
- -0.03408946841955185,
18
- -0.2757369577884674
19
- ],
20
- "frames": [
21
- {
22
- "file_path": "000.png",
23
- "camera_angle_x": 0.9232195630055808,
24
- "proj_type": 1,
25
- "azimuth": -1.5707963267948966,
26
- "elevation": 0,
27
- "cam_dis": 1.5,
28
- "transform_matrix": [
29
- [
30
- 1.0,
31
- 0.0,
32
- 6.123233601181349e-17,
33
- 9.184850732644269e-17
34
- ],
35
- [
36
- 6.123233601181349e-17,
37
- 3.422854177870249e-08,
38
- -0.9999999403953552,
39
- -1.5
40
- ],
41
- [
42
- 0.0,
43
- 0.9999999403953552,
44
- 3.422854177870249e-08,
45
- 0.0
46
- ],
47
- [
48
- 0,
49
- 0,
50
- 0,
51
- 1
52
- ]
53
- ]
54
- },
55
- {
56
- "file_path": "001.png",
57
- "camera_angle_x": 0.9232195630055808,
58
- "proj_type": 1,
59
- "azimuth": 0,
60
- "elevation": 0,
61
- "cam_dis": 1.5,
62
- "transform_matrix": [
63
- [
64
- -2.220446049250313e-16,
65
- 0.0,
66
- 1.0,
67
- 1.5
68
- ],
69
- [
70
- 1.0,
71
- -2.220446049250313e-16,
72
- 0.0,
73
- 0.0
74
- ],
75
- [
76
- 0.0,
77
- 1.0,
78
- -2.220446049250313e-16,
79
- 0.0
80
- ],
81
- [
82
- 0,
83
- 0,
84
- 0,
85
- 1
86
- ]
87
- ]
88
- },
89
- {
90
- "file_path": "002.png",
91
- "camera_angle_x": 0.9232195630055808,
92
- "proj_type": 1,
93
- "azimuth": 1.5707963267948966,
94
- "elevation": 0,
95
- "cam_dis": 1.5,
96
- "transform_matrix": [
97
- [
98
- -0.9999999403953552,
99
- 0.0,
100
- 6.123233601181349e-17,
101
- 9.184850732644269e-17
102
- ],
103
- [
104
- 6.123233601181349e-17,
105
- 3.422854177870249e-08,
106
- 0.9999999403953552,
107
- 1.5
108
- ],
109
- [
110
- 0.0,
111
- 0.9999999403953552,
112
- 3.422854177870249e-08,
113
- 0.0
114
- ],
115
- [
116
- 0,
117
- 0,
118
- 0,
119
- 1
120
- ]
121
- ]
122
- },
123
- {
124
- "file_path": "003.png",
125
- "camera_angle_x": 0.9232195630055808,
126
- "proj_type": 1,
127
- "azimuth": 3.141592653589793,
128
- "elevation": 0,
129
- "cam_dis": 1.5,
130
- "transform_matrix": [
131
- [
132
- -2.220446049250313e-16,
133
- 0.0,
134
- -1.0,
135
- -1.5
136
- ],
137
- [
138
- -1.0,
139
- -2.220446049250313e-16,
140
- 0.0,
141
- 1.8369701465288538e-16
142
- ],
143
- [
144
- 0.0,
145
- 1.0,
146
- -2.220446049250313e-16,
147
- 0.0
148
- ],
149
- [
150
- 0,
151
- 0,
152
- 0,
153
- 1
154
- ]
155
- ]
156
- },
157
- {
158
- "file_path": "004.png",
159
- "camera_angle_x": 0.9232195630055808,
160
- "proj_type": 1,
161
- "azimuth": -1.5707963267948966,
162
- "elevation": 1.5707963267948966,
163
- "cam_dis": 1.5,
164
- "transform_matrix": [
165
- [
166
- 1.0,
167
- -6.123233601181349e-17,
168
- 2.812049544789739e-33,
169
- 5.624099089579478e-33
170
- ],
171
- [
172
- 6.123233601181349e-17,
173
- 1.0,
174
- -3.0616171314629196e-17,
175
- -9.184850732644269e-17
176
- ],
177
- [
178
- -9.373498482632464e-34,
179
- 3.0616171314629196e-17,
180
- 1.0,
181
- 1.5
182
- ],
183
- [
184
- 0,
185
- 0,
186
- 0,
187
- 1
188
- ]
189
- ]
190
- },
191
- {
192
- "file_path": "005.png",
193
- "camera_angle_x": 0.9232195630055808,
194
- "proj_type": 1,
195
- "azimuth": -1.5707963267948966,
196
- "elevation": -1.5707963267948966,
197
- "cam_dis": 1.5,
198
- "transform_matrix": [
199
- [
200
- 1.0,
201
- 6.123233601181349e-17,
202
- 2.812049544789739e-33,
203
- 5.624099089579478e-33
204
- ],
205
- [
206
- 6.123233601181349e-17,
207
- -1.0,
208
- -3.0616171314629196e-17,
209
- -9.184850732644269e-17
210
- ],
211
- [
212
- 9.373498482632464e-34,
213
- 3.0616171314629196e-17,
214
- -1.0,
215
- -1.5
216
- ],
217
- [
218
- 0,
219
- 0,
220
- 0,
221
- 1
222
- ]
223
- ]
224
- }
225
- ]
226
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/train_examples/examples.json DELETED
@@ -1,3 +0,0 @@
1
- [
2
- "hy3dpaint/train_examples/001"
3
- ]
 
 
 
 
hy3dpaint/utils/__init__.py DELETED
@@ -1,13 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/utils/image_super_utils.py DELETED
@@ -1,41 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import numpy as np
16
- from PIL import Image
17
-
18
-
19
- class imageSuperNet:
20
- def __init__(self, config) -> None:
21
- from realesrgan import RealESRGANer
22
- from basicsr.archs.rrdbnet_arch import RRDBNet
23
-
24
- model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
25
- upsampler = RealESRGANer(
26
- scale=4,
27
- model_path=config.realesrgan_ckpt_path,
28
- dni_weight=None,
29
- model=model,
30
- tile=0,
31
- tile_pad=10,
32
- pre_pad=0,
33
- half=True,
34
- gpu_id=None,
35
- )
36
- self.upsampler = upsampler
37
-
38
- def __call__(self, image):
39
- output, _ = self.upsampler.enhance(np.array(image))
40
- output = Image.fromarray(output)
41
- return output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/utils/multiview_utils.py DELETED
@@ -1,128 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import os
16
- import torch
17
- import random
18
- import numpy as np
19
- from PIL import Image
20
- from typing import List
21
- import huggingface_hub
22
- from omegaconf import OmegaConf
23
- from diffusers import DiffusionPipeline
24
- from diffusers import EulerAncestralDiscreteScheduler, DDIMScheduler, UniPCMultistepScheduler
25
-
26
-
27
- class multiviewDiffusionNet:
28
- def __init__(self, config) -> None:
29
- self.device = config.device
30
-
31
- cfg_path = config.multiview_cfg_path
32
- custom_pipeline = config.custom_pipeline
33
- cfg = OmegaConf.load(cfg_path)
34
- self.cfg = cfg
35
- self.mode = self.cfg.model.params.stable_diffusion_config.custom_pipeline[2:]
36
-
37
- model_path = huggingface_hub.snapshot_download(
38
- repo_id=config.multiview_pretrained_path,
39
- allow_patterns=["hunyuan3d-paintpbr-v2-1/*"],
40
- )
41
-
42
- model_path = os.path.join(model_path, "hunyuan3d-paintpbr-v2-1")
43
- pipeline = DiffusionPipeline.from_pretrained(
44
- model_path,
45
- custom_pipeline=custom_pipeline,
46
- torch_dtype=torch.float16
47
- )
48
-
49
- pipeline.scheduler = UniPCMultistepScheduler.from_config(pipeline.scheduler.config, timestep_spacing="trailing")
50
- pipeline.set_progress_bar_config(disable=True)
51
- pipeline.eval()
52
- setattr(pipeline, "view_size", cfg.model.params.get("view_size", 320))
53
- self.pipeline = pipeline.to(self.device)
54
-
55
- if hasattr(self.pipeline.unet, "use_dino") and self.pipeline.unet.use_dino:
56
- from hunyuanpaintpbr.unet.modules import Dino_v2
57
- self.dino_v2 = Dino_v2(config.dino_ckpt_path).to(torch.float16)
58
- self.dino_v2 = self.dino_v2.to(self.device)
59
-
60
- def seed_everything(self, seed):
61
- random.seed(seed)
62
- np.random.seed(seed)
63
- torch.manual_seed(seed)
64
- os.environ["PL_GLOBAL_SEED"] = str(seed)
65
-
66
- @torch.no_grad()
67
- def __call__(self, images, conditions, prompt=None, custom_view_size=None, resize_input=False):
68
- pils = self.forward_one(
69
- images, conditions, prompt=prompt, custom_view_size=custom_view_size, resize_input=resize_input
70
- )
71
- return pils
72
-
73
- def forward_one(self, input_images, control_images, prompt=None, custom_view_size=None, resize_input=False):
74
- self.seed_everything(0)
75
- custom_view_size = custom_view_size if custom_view_size is not None else self.pipeline.view_size
76
- if not isinstance(input_images, List):
77
- input_images = [input_images]
78
- if not resize_input:
79
- input_images = [
80
- input_image.resize((self.pipeline.view_size, self.pipeline.view_size)) for input_image in input_images
81
- ]
82
- else:
83
- input_images = [input_image.resize((custom_view_size, custom_view_size)) for input_image in input_images]
84
- for i in range(len(control_images)):
85
- control_images[i] = control_images[i].resize((custom_view_size, custom_view_size))
86
- if control_images[i].mode == "L":
87
- control_images[i] = control_images[i].point(lambda x: 255 if x > 1 else 0, mode="1")
88
- kwargs = dict(generator=torch.Generator(device=self.pipeline.device).manual_seed(0))
89
-
90
- num_view = len(control_images) // 2
91
- normal_image = [[control_images[i] for i in range(num_view)]]
92
- position_image = [[control_images[i + num_view] for i in range(num_view)]]
93
-
94
- kwargs["width"] = custom_view_size
95
- kwargs["height"] = custom_view_size
96
- kwargs["num_in_batch"] = num_view
97
- kwargs["images_normal"] = normal_image
98
- kwargs["images_position"] = position_image
99
-
100
- if hasattr(self.pipeline.unet, "use_dino") and self.pipeline.unet.use_dino:
101
- dino_hidden_states = self.dino_v2(input_images[0])
102
- kwargs["dino_hidden_states"] = dino_hidden_states
103
-
104
- sync_condition = None
105
-
106
- infer_steps_dict = {
107
- "EulerAncestralDiscreteScheduler": 30,
108
- "UniPCMultistepScheduler": 15,
109
- "DDIMScheduler": 50,
110
- "ShiftSNRScheduler": 15,
111
- }
112
-
113
- mvd_image = self.pipeline(
114
- input_images[0:1],
115
- num_inference_steps=infer_steps_dict[self.pipeline.scheduler.__class__.__name__],
116
- prompt=prompt,
117
- sync_condition=sync_condition,
118
- guidance_scale=3.0,
119
- **kwargs,
120
- ).images
121
-
122
- if "pbr" in self.mode:
123
- mvd_image = {"albedo": mvd_image[:num_view], "mr": mvd_image[num_view:]}
124
- # mvd_image = {'albedo':mvd_image[:num_view]}
125
- else:
126
- mvd_image = {"hdr": mvd_image}
127
-
128
- return mvd_image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/utils/pipeline_utils.py DELETED
@@ -1,135 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import torch
16
- import numpy as np
17
-
18
-
19
- class ViewProcessor:
20
- def __init__(self, config, render):
21
- self.config = config
22
- self.render = render
23
-
24
- def render_normal_multiview(self, camera_elevs, camera_azims, use_abs_coor=True):
25
- normal_maps = []
26
- for elev, azim in zip(camera_elevs, camera_azims):
27
- normal_map = self.render.render_normal(elev, azim, use_abs_coor=use_abs_coor, return_type="pl")
28
- normal_maps.append(normal_map)
29
-
30
- return normal_maps
31
-
32
- def render_position_multiview(self, camera_elevs, camera_azims):
33
- position_maps = []
34
- for elev, azim in zip(camera_elevs, camera_azims):
35
- position_map = self.render.render_position(elev, azim, return_type="pl")
36
- position_maps.append(position_map)
37
-
38
- return position_maps
39
-
40
- def bake_view_selection(
41
- self, candidate_camera_elevs, candidate_camera_azims, candidate_view_weights, max_selected_view_num
42
- ):
43
-
44
- original_resolution = self.render.default_resolution
45
- self.render.set_default_render_resolution(1024)
46
-
47
- selected_camera_elevs = []
48
- selected_camera_azims = []
49
- selected_view_weights = []
50
- selected_alpha_maps = []
51
- viewed_tri_idxs = []
52
- viewed_masks = []
53
-
54
- # 计算每个三角片的面积
55
- face_areas = self.render.get_face_areas(from_one_index=True)
56
- total_area = face_areas.sum()
57
- face_area_ratios = face_areas / total_area
58
-
59
- candidate_view_num = len(candidate_camera_elevs)
60
- self.render.set_boundary_unreliable_scale(2)
61
-
62
- for elev, azim in zip(candidate_camera_elevs, candidate_camera_azims):
63
- viewed_tri_idx = self.render.render_alpha(elev, azim, return_type="np")
64
- viewed_tri_idxs.append(set(np.unique(viewed_tri_idx.flatten())))
65
- viewed_masks.append(viewed_tri_idx[0, :, :, 0] > 0)
66
-
67
- is_selected = [False for _ in range(candidate_view_num)]
68
- total_viewed_tri_idxs = set()
69
- total_viewed_area = 0.0
70
-
71
- for idx in range(6):
72
- selected_camera_elevs.append(candidate_camera_elevs[idx])
73
- selected_camera_azims.append(candidate_camera_azims[idx])
74
- selected_view_weights.append(candidate_view_weights[idx])
75
- selected_alpha_maps.append(viewed_masks[idx])
76
- is_selected[idx] = True
77
- total_viewed_tri_idxs.update(viewed_tri_idxs[idx])
78
-
79
- total_viewed_area = face_area_ratios[list(total_viewed_tri_idxs)].sum()
80
- for iter in range(max_selected_view_num - len(selected_view_weights)):
81
- max_inc = 0
82
- max_idx = -1
83
-
84
- for idx, (elev, azim, weight) in enumerate(
85
- zip(candidate_camera_elevs, candidate_camera_azims, candidate_view_weights)
86
- ):
87
- if is_selected[idx]:
88
- continue
89
- new_tri_idxs = viewed_tri_idxs[idx] - total_viewed_tri_idxs
90
- new_inc_area = face_area_ratios[list(new_tri_idxs)].sum()
91
-
92
- if new_inc_area > max_inc:
93
- max_inc = new_inc_area
94
- max_idx = idx
95
-
96
- if max_inc > 0.01:
97
- is_selected[max_idx] = True
98
- selected_camera_elevs.append(candidate_camera_elevs[max_idx])
99
- selected_camera_azims.append(candidate_camera_azims[max_idx])
100
- selected_view_weights.append(candidate_view_weights[max_idx])
101
- selected_alpha_maps.append(viewed_masks[max_idx])
102
- total_viewed_tri_idxs = total_viewed_tri_idxs.union(viewed_tri_idxs[max_idx])
103
- total_viewed_area += max_inc
104
- else:
105
- break
106
-
107
- self.render.set_default_render_resolution(original_resolution)
108
-
109
- return selected_camera_elevs, selected_camera_azims, selected_view_weights
110
-
111
- def bake_from_multiview(self, views, camera_elevs, camera_azims, view_weights):
112
- project_textures, project_weighted_cos_maps = [], []
113
- project_boundary_maps = []
114
-
115
- for view, camera_elev, camera_azim, weight in zip(views, camera_elevs, camera_azims, view_weights):
116
- project_texture, project_cos_map, project_boundary_map = self.render.back_project(
117
- view, camera_elev, camera_azim
118
- )
119
- project_cos_map = weight * (project_cos_map**self.config.bake_exp)
120
- project_textures.append(project_texture)
121
- project_weighted_cos_maps.append(project_cos_map)
122
- project_boundary_maps.append(project_boundary_map)
123
- texture, ori_trust_map = self.render.fast_bake_texture(project_textures, project_weighted_cos_maps)
124
- return texture, ori_trust_map > 1e-8
125
-
126
- def texture_inpaint(self, texture, mask, defualt=None):
127
- if defualt is not None:
128
- mask = mask.astype(bool)
129
- inpaint_value = torch.tensor(defualt, dtype=texture.dtype, device=texture.device)
130
- texture[~mask] = inpaint_value
131
- else:
132
- texture_np = self.render.uv_inpaint(texture, mask)
133
- texture = torch.tensor(texture_np / 255).float().to(texture.device)
134
-
135
- return texture
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/utils/simplify_mesh_utils.py DELETED
@@ -1,37 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import trimesh
16
- import pymeshlab
17
-
18
-
19
- def remesh_mesh(mesh_path, remesh_path):
20
- mesh = mesh_simplify_trimesh(mesh_path, remesh_path)
21
-
22
-
23
- def mesh_simplify_trimesh(inputpath, outputpath, target_count=40000):
24
- # 先去除离散面
25
- ms = pymeshlab.MeshSet()
26
- if inputpath.endswith(".glb"):
27
- ms.load_new_mesh(inputpath, load_in_a_single_layer=True)
28
- else:
29
- ms.load_new_mesh(inputpath)
30
- ms.save_current_mesh(outputpath.replace(".glb", ".obj"), save_textures=False)
31
- # 调用减面函数
32
- courent = trimesh.load(outputpath.replace(".glb", ".obj"), force="mesh")
33
- face_num = courent.faces.shape[0]
34
-
35
- if face_num > target_count:
36
- courent = courent.simplify_quadric_decimation(target_count)
37
- courent.export(outputpath)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/utils/torchvision_fix.py DELETED
@@ -1,111 +0,0 @@
1
- # Torchvision compatibility fix for functional_tensor module
2
- # This file helps resolve compatibility issues between different torchvision versions
3
-
4
- import sys
5
- import torch
6
- import torchvision
7
-
8
- def fix_torchvision_functional_tensor():
9
- """
10
- Fix torchvision.transforms.functional_tensor import issue
11
- """
12
- try:
13
- # Check if the module exists in the expected location
14
- import torchvision.transforms.functional_tensor
15
- print("torchvision.transforms.functional_tensor is available")
16
- return True
17
- except ImportError:
18
- print("torchvision.transforms.functional_tensor not found, applying compatibility fix...")
19
-
20
- try:
21
- # Create a mock functional_tensor module with the required functions
22
- import torchvision.transforms.functional as F
23
-
24
- class FunctionalTensorMock:
25
- """Mock module to replace functional_tensor"""
26
-
27
- @staticmethod
28
- def _get_grayscale_weights(img):
29
- """Helper to create grayscale weights based on image dimensions"""
30
- weights = torch.tensor([0.299, 0.587, 0.114], device=img.device, dtype=img.dtype)
31
- return weights.view(1, 3, 1, 1) if len(img.shape) == 4 else weights.view(3, 1, 1)
32
-
33
- @staticmethod
34
- def _try_import_fallback(module_names, attr_name):
35
- """Helper to try importing from multiple modules"""
36
- for module_name in module_names:
37
- try:
38
- module = __import__(module_name, fromlist=[attr_name])
39
- if hasattr(module, attr_name):
40
- return getattr(module, attr_name)
41
- except ImportError:
42
- continue
43
- return None
44
-
45
- @staticmethod
46
- def rgb_to_grayscale(img, num_output_channels=1):
47
- """Convert RGB image to grayscale"""
48
- if hasattr(F, 'rgb_to_grayscale'):
49
- return F.rgb_to_grayscale(img, num_output_channels)
50
-
51
- # Fallback implementation
52
- weights = FunctionalTensorMock._get_grayscale_weights(img)
53
- grayscale = torch.sum(img * weights, dim=-3, keepdim=True)
54
-
55
- if num_output_channels == 3:
56
- repeat_dims = (1, 3, 1, 1) if len(img.shape) == 4 else (3, 1, 1)
57
- grayscale = grayscale.repeat(*repeat_dims)
58
-
59
- return grayscale
60
-
61
- @staticmethod
62
- def resize(img, size, interpolation=2, antialias=None):
63
- """Resize function wrapper"""
64
- # Try v2.functional first, then regular functional, then torch.nn.functional
65
- resize_func = FunctionalTensorMock._try_import_fallback([
66
- 'torchvision.transforms.v2.functional',
67
- 'torchvision.transforms.functional'
68
- ], 'resize')
69
-
70
- if resize_func:
71
- try:
72
- return resize_func(img, size, interpolation=interpolation, antialias=antialias)
73
- except TypeError:
74
- # Fallback for older versions without antialias parameter
75
- return resize_func(img, size, interpolation=interpolation)
76
-
77
- # Final fallback using torch.nn.functional
78
- import torch.nn.functional as torch_F
79
- size = (size, size) if isinstance(size, int) else size
80
- img_input = img.unsqueeze(0) if len(img.shape) == 3 else img
81
- return torch_F.interpolate(img_input, size=size, mode='bilinear', align_corners=False)
82
-
83
- def __getattr__(self, name):
84
- """Fallback to regular functional module"""
85
- func = self._try_import_fallback([
86
- 'torchvision.transforms.functional',
87
- 'torchvision.transforms.v2.functional'
88
- ], name)
89
-
90
- if func:
91
- return func
92
-
93
- raise AttributeError(f"'{name}' not found in functional_tensor mock")
94
-
95
- # Create the mock module instance and monkey patch
96
- sys.modules['torchvision.transforms.functional_tensor'] = FunctionalTensorMock()
97
- print("Applied compatibility fix: created functional_tensor mock module")
98
- return True
99
-
100
- except Exception as e:
101
- print(f"Failed to create functional_tensor mock: {e}")
102
- return False
103
-
104
- def apply_fix():
105
- """Apply the torchvision compatibility fix"""
106
- print(f"Torchvision version: {torchvision.__version__}")
107
- return fix_torchvision_functional_tensor()
108
-
109
- if __name__ == "__main__":
110
- apply_fix()
111
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dpaint/utils/uvwrap_utils.py DELETED
@@ -1,32 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import trimesh
16
- import xatlas
17
-
18
-
19
- def mesh_uv_wrap(mesh):
20
- if isinstance(mesh, trimesh.Scene):
21
- mesh = mesh.dump(concatenate=True)
22
-
23
- if len(mesh.faces) > 500000000:
24
- raise ValueError("The mesh has more than 500,000,000 faces, which is not supported.")
25
-
26
- vmapping, indices, uvs = xatlas.parametrize(mesh.vertices, mesh.faces)
27
-
28
- mesh.vertices = mesh.vertices[vmapping]
29
- mesh.faces = indices
30
- mesh.visual.uv = uvs
31
-
32
- return mesh
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/.gitignore DELETED
@@ -1,169 +0,0 @@
1
- # Byte-compiled / optimized / DLL files
2
- __pycache__/
3
- *.py[cod]
4
- *$py.class
5
-
6
- # C extensions
7
- *.so
8
-
9
- # Distribution / packaging
10
- .Python
11
- build/
12
- develop-eggs/
13
- dist/
14
- downloads/
15
- eggs/
16
- .eggs/
17
- lib/
18
- !hy3dgen/texgen/custom_rasterizer/lib/
19
- lib64/
20
- parts/
21
- sdist/
22
- var/
23
- wheels/
24
- share/python-wheels/
25
- *.egg-info/
26
- .installed.cfg
27
- *.egg
28
- MANIFEST
29
-
30
- # PyInstaller
31
- # Usually these files are written by a python script from a template
32
- # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
- *.manifest
34
- *.spec
35
-
36
- # Installer logs
37
- pip-log.txt
38
- pip-delete-this-directory.txt
39
-
40
- # Unit test / coverage reports
41
- htmlcov/
42
- .tox/
43
- .nox/
44
- .coverage
45
- .coverage.*
46
- .cache
47
- nosetests.xml
48
- coverage.xml
49
- *.cover
50
- *.py,cover
51
- .hypothesis/
52
- .pytest_cache/
53
- cover/
54
-
55
- # Translations
56
- *.mo
57
- *.pot
58
-
59
- # Django stuff:
60
- *.log
61
- local_settings.py
62
- db.sqlite3
63
- db.sqlite3-journal
64
-
65
- # Flask stuff:
66
- instance/
67
- .webassets-cache
68
-
69
- # Scrapy stuff:
70
- .scrapy
71
-
72
- # Sphinx documentation
73
- docs/_build/
74
-
75
- # PyBuilder
76
- .pybuilder/
77
- target/
78
-
79
- # Jupyter Notebook
80
- .ipynb_checkpoints
81
-
82
- # IPython
83
- profile_default/
84
- ipython_config.py
85
-
86
- # pyenv
87
- # For a library or package, you might want to ignore these files since the code is
88
- # intended to run in multiple environments; otherwise, check them in:
89
- # .python-version
90
-
91
- # pipenv
92
- # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
- # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
- # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
- # install all needed dependencies.
96
- #Pipfile.lock
97
-
98
- # UV
99
- # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
- # This is especially recommended for binary packages to ensure reproducibility, and is more
101
- # commonly ignored for libraries.
102
- #uv.lock
103
-
104
- # poetry
105
- # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
- # This is especially recommended for binary packages to ensure reproducibility, and is more
107
- # commonly ignored for libraries.
108
- # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
- #poetry.lock
110
-
111
- # pdm
112
- # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
- #pdm.lock
114
- # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
115
- # in version control.
116
- # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
117
- .pdm.toml
118
- .pdm-python
119
- .pdm-build/
120
-
121
- # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
122
- __pypackages__/
123
-
124
- # Celery stuff
125
- celerybeat-schedule
126
- celerybeat.pid
127
-
128
- # SageMath parsed files
129
- *.sage.py
130
-
131
- # Environments
132
- .env
133
- .venv
134
- env/
135
- venv/
136
- ENV/
137
- env.bak/
138
- venv.bak/
139
-
140
- # Spyder project settings
141
- .spyderproject
142
- .spyproject
143
-
144
- # Rope project settings
145
- .ropeproject
146
-
147
- # mkdocs documentation
148
- /site
149
-
150
- # mypy
151
- .mypy_cache/
152
- .dmypy.json
153
- dmypy.json
154
-
155
- # Pyre type checker
156
- .pyre/
157
-
158
- # pytype static type analyzer
159
- .pytype/
160
- .DS_Store
161
- # Cython debug symbols
162
- cython_debug/
163
- gradio_cache/
164
- # PyCharm
165
- # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
166
- # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
167
- # and can be added to the global gitignore or merged into this file. For a more nuclear
168
- # option (not recommended) you can uncomment the following to ignore the entire idea folder.
169
- #.idea/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/LICENSE DELETED
@@ -1,81 +0,0 @@
1
- TENCENT HUNYUAN 3D 2.1 COMMUNITY LICENSE AGREEMENT
2
- Tencent Hunyuan 3D 2.1 Release Date: June 13, 2025
3
- THIS LICENSE AGREEMENT DOES NOT APPLY IN THE EUROPEAN UNION, UNITED KINGDOM AND SOUTH KOREA AND IS EXPRESSLY LIMITED TO THE TERRITORY, AS DEFINED BELOW.
4
- By clicking to agree or by using, reproducing, modifying, distributing, performing or displaying any portion or element of the Tencent Hunyuan 3D 2.1 Works, including via any Hosted Service, You will be deemed to have recognized and accepted the content of this Agreement, which is effective immediately.
5
- 1. DEFINITIONS.
6
- a. “Acceptable Use Policy” shall mean the policy made available by Tencent as set forth in the Exhibit A.
7
- b. “Agreement” shall mean the terms and conditions for use, reproduction, distribution, modification, performance and displaying of Tencent Hunyuan 3D 2.1 Works or any portion or element thereof set forth herein.
8
- c. “Documentation” shall mean the specifications, manuals and documentation for Tencent Hunyuan 3D 2.1 made publicly available by Tencent.
9
- d. “Hosted Service” shall mean a hosted service offered via an application programming interface (API), web access, or any other electronic or remote means.
10
- e. “Licensee,” “You” or “Your” shall mean a natural person or legal entity exercising the rights granted by this Agreement and/or using the Tencent Hunyuan 3D 2.1 Works for any purpose and in any field of use.
11
- f. “Materials” shall mean, collectively, Tencent’s proprietary Tencent Hunyuan 3D 2.1 and Documentation (and any portion thereof) as made available by Tencent under this Agreement.
12
- g. “Model Derivatives” shall mean all: (i) modifications to Tencent Hunyuan 3D 2.1 or any Model Derivative of Tencent Hunyuan 3D 2.1; (ii) works based on Tencent Hunyuan 3D 2.1 or any Model Derivative of Tencent Hunyuan 3D 2.1; or (iii) any other machine learning model which is created by transfer of patterns of the weights, parameters, operations, or Output of Tencent Hunyuan 3D 2.1 or any Model Derivative of Tencent Hunyuan 3D 2.1, to that model in order to cause that model to perform similarly to Tencent Hunyuan 3D 2.1 or a Model Derivative of Tencent Hunyuan 3D 2.1, including distillation methods, methods that use intermediate data representations, or methods based on the generation of synthetic data Outputs by Tencent Hunyuan 3D 2.1 or a Model Derivative of Tencent Hunyuan 3D 2.1 for training that model. For clarity, Outputs by themselves are not deemed Model Derivatives.
13
- h. “Output” shall mean the information and/or content output of Tencent Hunyuan 3D 2.1 or a Model Derivative that results from operating or otherwise using Tencent Hunyuan 3D 2.1 or a Model Derivative, including via a Hosted Service.
14
- i. “Tencent,” “We” or “Us” shall mean THL Q Limited.
15
- j. “Tencent Hunyuan 3D 2.1” shall mean the 3D generation models and their software and algorithms, including trained model weights, parameters (including optimizer states), machine-learning model code, inference-enabling code, training-enabling code, fine-tuning enabling code and other elements of the foregoing made publicly available by Us at [ https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1].
16
- k. “Tencent Hunyuan 3D 2.1 Works” shall mean: (i) the Materials; (ii) Model Derivatives; and (iii) all derivative works thereof.
17
- l. “Territory” shall mean the worldwide territory, excluding the territory of the European Union, United Kingdom and South Korea.
18
- m. “Third Party” or “Third Parties” shall mean individuals or legal entities that are not under common control with Us or You.
19
- n. “including” shall mean including but not limited to.
20
- 2. GRANT OF RIGHTS.
21
- We grant You, for the Territory only, a non-exclusive, non-transferable and royalty-free limited license under Tencent’s intellectual property or other rights owned by Us embodied in or utilized by the Materials to use, reproduce, distribute, create derivative works of (including Model Derivatives), and make modifications to the Materials, only in accordance with the terms of this Agreement and the Acceptable Use Policy, and You must not violate (or encourage or permit anyone else to violate) any term of this Agreement or the Acceptable Use Policy.
22
- 3. DISTRIBUTION.
23
- You may, subject to Your compliance with this Agreement, distribute or make available to Third Parties the Tencent Hunyuan 3D 2.1 Works, exclusively in the Territory, provided that You meet all of the following conditions:
24
- a. You must provide all such Third Party recipients of the Tencent Hunyuan 3D 2.1 Works or products or services using them a copy of this Agreement;
25
- b. You must cause any modified files to carry prominent notices stating that You changed the files;
26
- c. You are encouraged to: (i) publish at least one technology introduction blogpost or one public statement expressing Your experience of using the Tencent Hunyuan 3D 2.1 Works; and (ii) mark the products or services developed by using the Tencent Hunyuan 3D 2.1 Works to indicate that the product/service is “Powered by Tencent Hunyuan”; and
27
- d. All distributions to Third Parties (other than through a Hosted Service) must be accompanied by a “Notice” text file that contains the following notice: “Tencent Hunyuan 3D 2.1 is licensed under the Tencent Hunyuan 3D 2.1 Community License Agreement, Copyright © 2025 Tencent. All Rights Reserved. The trademark rights of “Tencent Hunyuan” are owned by Tencent or its affiliate.”
28
- You may add Your own copyright statement to Your modifications and, except as set forth in this Section and in Section 5, may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Model Derivatives as a whole, provided Your use, reproduction, modification, distribution, performance and display of the work otherwise complies with the terms and conditions of this Agreement (including as regards the Territory). If You receive Tencent Hunyuan 3D 2.1 Works from a Licensee as part of an integrated end user product, then this Section 3 of this Agreement will not apply to You.
29
- 4. ADDITIONAL COMMERCIAL TERMS.
30
- If, on the Tencent Hunyuan 3D 2.1 version release date, the monthly active users of all products or services made available by or for Licensee is greater than 1 million monthly active users in the preceding calendar month, You must request a license from Tencent, which Tencent may grant to You in its sole discretion, and You are not authorized to exercise any of the rights under this Agreement unless or until Tencent otherwise expressly grants You such rights.
31
- Subject to Tencent's written approval, you may request a license for the use of Tencent Hunyuan 3D 2.1 by submitting the following information to [email protected]:
32
- a. Your company’s name and associated business sector that plans to use Tencent Hunyuan 3D 2.1.
33
- b. Your intended use case and the purpose of using Tencent Hunyuan 3D 2.1.
34
- c. Your plans to modify Tencent Hunyuan 3D 2.1 or create Model Derivatives.
35
- 5. RULES OF USE.
36
- a. Your use of the Tencent Hunyuan 3D 2.1 Works must comply with applicable laws and regulations (including trade compliance laws and regulations) and adhere to the Acceptable Use Policy for the Tencent Hunyuan 3D 2.1 Works, which is hereby incorporated by reference into this Agreement. You must include the use restrictions referenced in these Sections 5(a) and 5(b) as an enforceable provision in any agreement (e.g., license agreement, terms of use, etc.) governing the use and/or distribution of Tencent Hunyuan 3D 2.1 Works and You must provide notice to subsequent users to whom You distribute that Tencent Hunyuan 3D 2.1 Works are subject to the use restrictions in these Sections 5(a) and 5(b).
37
- b. You must not use the Tencent Hunyuan 3D 2.1 Works or any Output or results of the Tencent Hunyuan 3D 2.1 Works to improve any other AI model (other than Tencent Hunyuan 3D 2.1 or Model Derivatives thereof).
38
- c. You must not use, reproduce, modify, distribute, or display the Tencent Hunyuan 3D 2.1 Works, Output or results of the Tencent Hunyuan 3D 2.1 Works outside the Territory. Any such use outside the Territory is unlicensed and unauthorized under this Agreement.
39
- 6. INTELLECTUAL PROPERTY.
40
- a. Subject to Tencent’s ownership of Tencent Hunyuan 3D 2.1 Works made by or for Tencent and intellectual property rights therein, conditioned upon Your compliance with the terms and conditions of this Agreement, as between You and Tencent, You will be the owner of any derivative works and modifications of the Materials and any Model Derivatives that are made by or for You.
41
- b. No trademark licenses are granted under this Agreement, and in connection with the Tencent Hunyuan 3D 2.1 Works, Licensee may not use any name or mark owned by or associated with Tencent or any of its affiliates, except as required for reasonable and customary use in describing and distributing the Tencent Hunyuan 3D 2.1 Works. Tencent hereby grants You a license to use “Tencent Hunyuan” (the “Mark”) in the Territory solely as required to comply with the provisions of Section 3(c), provided that You comply with any applicable laws related to trademark protection. All goodwill arising out of Your use of the Mark will inure to the benefit of Tencent.
42
- c. If You commence a lawsuit or other proceedings (including a cross-claim or counterclaim in a lawsuit) against Us or any person or entity alleging that the Materials or any Output, or any portion of any of the foregoing, infringe any intellectual property or other right owned or licensable by You, then all licenses granted to You under this Agreement shall terminate as of the date such lawsuit or other proceeding is filed. You will defend, indemnify and hold harmless Us from and against any claim by any Third Party arising out of or related to Your or the Third Party’s use or distribution of the Tencent Hunyuan 3D 2.1 Works.
43
- d. Tencent claims no rights in Outputs You generate. You and Your users are solely responsible for Outputs and their subsequent uses.
44
- 7. DISCLAIMERS OF WARRANTY AND LIMITATIONS OF LIABILITY.
45
- a. We are not obligated to support, update, provide training for, or develop any further version of the Tencent Hunyuan 3D 2.1 Works or to grant any license thereto.
46
- b. UNLESS AND ONLY TO THE EXTENT REQUIRED BY APPLICABLE LAW, THE TENCENT HUNYUAN 3D 2.1 WORKS AND ANY OUTPUT AND RESULTS THEREFROM ARE PROVIDED “AS IS” WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND INCLUDING ANY WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, COURSE OF DEALING, USAGE OF TRADE, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, REPRODUCING, MODIFYING, PERFORMING, DISPLAYING OR DISTRIBUTING ANY OF THE TENCENT HUNYUAN 3D 2.1 WORKS OR OUTPUTS AND ASSUME ANY AND ALL RISKS ASSOCIATED WITH YOUR OR A THIRD PARTY’S USE OR DISTRIBUTION OF ANY OF THE TENCENT HUNYUAN 3D 2.1 WORKS OR OUTPUTS AND YOUR EXERCISE OF RIGHTS AND PERMISSIONS UNDER THIS AGREEMENT.
47
- c. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL TENCENT OR ITS AFFILIATES BE LIABLE UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, NEGLIGENCE, PRODUCTS LIABILITY, OR OTHERWISE, FOR ANY DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR LOST PROFITS OF ANY KIND ARISING FROM THIS AGREEMENT OR RELATED TO ANY OF THE TENCENT HUNYUAN 3D 2.1 WORKS OR OUTPUTS, EVEN IF TENCENT OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF ANY OF THE FOREGOING.
48
- 8. SURVIVAL AND TERMINATION.
49
- a. The term of this Agreement shall commence upon Your acceptance of this Agreement or access to the Materials and will continue in full force and effect until terminated in accordance with the terms and conditions herein.
50
- b. We may terminate this Agreement if You breach any of the terms or conditions of this Agreement. Upon termination of this Agreement, You must promptly delete and cease use of the Tencent Hunyuan 3D 2.1 Works. Sections 6(a), 6(c), 7 and 9 shall survive the termination of this Agreement.
51
- 9. GOVERNING LAW AND JURISDICTION.
52
- a. This Agreement and any dispute arising out of or relating to it will be governed by the laws of the Hong Kong Special Administrative Region of the People’s Republic of China, without regard to conflict of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement.
53
- b. Exclusive jurisdiction and venue for any dispute arising out of or relating to this Agreement will be a court of competent jurisdiction in the Hong Kong Special Administrative Region of the People’s Republic of China, and Tencent and Licensee consent to the exclusive jurisdiction of such court with respect to any such dispute.
54
-
55
- EXHIBIT A
56
- ACCEPTABLE USE POLICY
57
-
58
- Tencent reserves the right to update this Acceptable Use Policy from time to time.
59
- Last modified: November 5, 2024
60
-
61
- Tencent endeavors to promote safe and fair use of its tools and features, including Tencent Hunyuan 3D 2.1. You agree not to use Tencent Hunyuan 3D 2.1 or Model Derivatives:
62
- 1. Outside the Territory;
63
- 2. In any way that violates any applicable national, federal, state, local, international or any other law or regulation;
64
- 3. To harm Yourself or others;
65
- 4. To repurpose or distribute output from Tencent Hunyuan 3D 2.1 or any Model Derivatives to harm Yourself or others;
66
- 5. To override or circumvent the safety guardrails and safeguards We have put in place;
67
- 6. For the purpose of exploiting, harming or attempting to exploit or harm minors in any way;
68
- 7. To generate or disseminate verifiably false information and/or content with the purpose of harming others or influencing elections;
69
- 8. To generate or facilitate false online engagement, including fake reviews and other means of fake online engagement;
70
- 9. To intentionally defame, disparage or otherwise harass others;
71
- 10. To generate and/or disseminate malware (including ransomware) or any other content to be used for the purpose of harming electronic systems;
72
- 11. To generate or disseminate personal identifiable information with the purpose of harming others;
73
- 12. To generate or disseminate information (including images, code, posts, articles), and place the information in any public context (including –through the use of bot generated tweets), without expressly and conspicuously identifying that the information and/or content is machine generated;
74
- 13. To impersonate another individual without consent, authorization, or legal right;
75
- 14. To make high-stakes automated decisions in domains that affect an individual’s safety, rights or wellbeing (e.g., law enforcement, migration, medicine/health, management of critical infrastructure, safety components of products, essential services, credit, employment, housing, education, social scoring, or insurance);
76
- 15. In a manner that violates or disrespects the social ethics and moral standards of other countries or regions;
77
- 16. To perform, facilitate, threaten, incite, plan, promote or encourage violent extremism or terrorism;
78
- 17. For any use intended to discriminate against or harm individuals or groups based on protected characteristics or categories, online or offline social behavior or known or predicted personal or personality characteristics;
79
- 18. To intentionally exploit any of the vulnerabilities of a specific group of persons based on their age, social, physical or mental characteristics, in order to materially distort the behavior of a person pertaining to that group in a manner that causes or is likely to cause that person or another person physical or psychological harm;
80
- 19. For military purposes;
81
- 20. To engage in the unauthorized or unlicensed practice of any profession including, but not limited to, financial, legal, medical/health, or other professional practices.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/NOTICE DELETED
@@ -1,214 +0,0 @@
1
- Usage and Legal Notices:
2
-
3
- Tencent is pleased to support the open source community by making Hunyuan 3D 2.0 available.
4
-
5
- Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. The below software and/or models in this distribution may have been modified by THL A29 Limited ("Tencent Modifications"). All Tencent Modifications are Copyright (C) THL A29 Limited.
6
-
7
- Hunyuan 3D 2.0 is licensed under the TENCENT HUNYUAN 3D 2.0 COMMUNITY LICENSE AGREEMENT except for the third-party components listed below, which is licensed under different terms. Hunyuan 3D 2.0 does not impose any additional limitations beyond what is outlined in the respective licenses of these third-party components. Users must comply with all terms and conditions of original licenses of these third-party components and must ensure that the usage of the third party components adheres to all relevant laws and regulations.
8
-
9
- For avoidance of doubts, Hunyuan 3D 2.0 means inference-enabling code, parameters, and weights of this Model only, which are made publicly available by Tencent in accordance with TENCENT HUNYUAN 3D 2.0 COMMUNITY LICENSE AGREEMENT.
10
-
11
-
12
- Other dependencies and licenses:
13
-
14
-
15
- Open Source Model Licensed under the MIT and CreativeML Open RAIL++-M License:
16
- --------------------------------------------------------------------
17
- 1. Stable Diffusion
18
- Copyright (c) 2022 Stability AI
19
-
20
-
21
- Terms of the MIT and CreativeML Open RAIL++-M License:
22
- --------------------------------------------------------------------
23
- Permission is hereby granted, free of charge, to any person obtaining a copy
24
- of this software and associated documentation files (the "Software"), to deal
25
- in the Software without restriction, including without limitation the rights
26
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27
- copies of the Software, and to permit persons to whom the Software is
28
- furnished to do so, subject to the following conditions:
29
-
30
- The above copyright notice and this permission notice shall be included in all
31
- copies or substantial portions of the Software.
32
-
33
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39
- SOFTWARE.
40
-
41
-
42
- CreativeML Open RAIL++-M License
43
- dated November 24, 2022
44
-
45
- Section I: PREAMBLE
46
-
47
- Multimodal generative models are being widely adopted and used, and have the potential to transform the way artists, among other individuals, conceive and benefit from AI or ML technologies as a tool for content creation.
48
-
49
- Notwithstanding the current and potential benefits that these artifacts can bring to society at large, there are also concerns about potential misuses of them, either due to their technical limitations or ethical considerations.
50
-
51
- In short, this license strives for both the open and responsible downstream use of the accompanying model. When it comes to the open character, we took inspiration from open source permissive licenses regarding the grant of IP rights. Referring to the downstream responsible use, we added use-based restrictions not permitting the use of the Model in very specific scenarios, in order for the licensor to be able to enforce the license in case potential misuses of the Model may occur. At the same time, we strive to promote open and responsible research on generative models for art and content generation.
52
-
53
- Even though downstream derivative versions of the model could be released under different licensing terms, the latter will always have to include - at minimum - the same use-based restrictions as the ones in the original license (this license). We believe in the intersection between open and responsible AI development; thus, this License aims to strike a balance between both in order to enable responsible open-science in the field of AI.
54
-
55
- This License governs the use of the model (and its derivatives) and is informed by the model card associated with the model.
56
-
57
- NOW THEREFORE, You and Licensor agree as follows:
58
-
59
- 1. Definitions
60
-
61
- - "License" means the terms and conditions for use, reproduction, and Distribution as defined in this document.
62
- - "Data" means a collection of information and/or content extracted from the dataset used with the Model, including to train, pretrain, or otherwise evaluate the Model. The Data is not licensed under this License.
63
- - "Output" means the results of operating a Model as embodied in informational content resulting therefrom.
64
- - "Model" means any accompanying machine-learning based assemblies (including checkpoints), consisting of learnt weights, parameters (including optimizer states), corresponding to the model architecture as embodied in the Complementary Material, that have been trained or tuned, in whole or in part on the Data, using the Complementary Material.
65
- - "Derivatives of the Model" means all modifications to the Model, works based on the Model, or any other model which is created or initialized by transfer of patterns of the weights, parameters, activations or output of the Model, to the other model, in order to cause the other model to perform similarly to the Model, including - but not limited to - distillation methods entailing the use of intermediate data representations or methods based on the generation of synthetic data by the Model for training the other model.
66
- - "Complementary Material" means the accompanying source code and scripts used to define, run, load, benchmark or evaluate the Model, and used to prepare data for training or evaluation, if any. This includes any accompanying documentation, tutorials, examples, etc, if any.
67
- - "Distribution" means any transmission, reproduction, publication or other sharing of the Model or Derivatives of the Model to a third party, including providing the Model as a hosted service made available by electronic or other remote means - e.g. API-based or web access.
68
- - "Licensor" means the copyright owner or entity authorized by the copyright owner that is granting the License, including the persons or entities that may have rights in the Model and/or distributing the Model.
69
- - "You" (or "Your") means an individual or Legal Entity exercising permissions granted by this License and/or making use of the Model for whichever purpose and in any field of use, including usage of the Model in an end-use application - e.g. chatbot, translator, image generator.
70
- - "Third Parties" means individuals or legal entities that are not under common control with Licensor or You.
71
- - "Contribution" means any work of authorship, including the original version of the Model and any modifications or additions to that Model or Derivatives of the Model thereof, that is intentionally submitted to Licensor for inclusion in the Model by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Model, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
72
- - "Contributor" means Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Model.
73
-
74
- Section II: INTELLECTUAL PROPERTY RIGHTS
75
-
76
- Both copyright and patent grants apply to the Model, Derivatives of the Model and Complementary Material. The Model and Derivatives of the Model are subject to additional terms as described in Section III.
77
-
78
- 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare, publicly display, publicly perform, sublicense, and distribute the Complementary Material, the Model, and Derivatives of the Model.
79
- 3. Grant of Patent License. Subject to the terms and conditions of this License and where and as applicable, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this paragraph) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Model and the Complementary Material, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Model to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Model and/or Complementary Material or a Contribution incorporated within the Model and/or Complementary Material constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for the Model and/or Work shall terminate as of the date such litigation is asserted or filed.
80
-
81
- Section III: CONDITIONS OF USAGE, DISTRIBUTION AND REDISTRIBUTION
82
-
83
- 4. Distribution and Redistribution. You may host for Third Party remote access purposes (e.g. software-as-a-service), reproduce and distribute copies of the Model or Derivatives of the Model thereof in any medium, with or without modifications, provided that You meet the following conditions:
84
- Use-based restrictions as referenced in paragraph 5 MUST be included as an enforceable provision by You in any type of legal agreement (e.g. a license) governing the use and/or distribution of the Model or Derivatives of the Model, and You shall give notice to subsequent users You Distribute to, that the Model or Derivatives of the Model are subject to paragraph 5. This provision does not apply to the use of Complementary Material.
85
- You must give any Third Party recipients of the Model or Derivatives of the Model a copy of this License;
86
- You must cause any modified files to carry prominent notices stating that You changed the files;
87
- You must retain all copyright, patent, trademark, and attribution notices excluding those notices that do not pertain to any part of the Model, Derivatives of the Model.
88
- You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions - respecting paragraph 4.a. - for use, reproduction, or Distribution of Your modifications, or for any such Derivatives of the Model as a whole, provided Your use, reproduction, and Distribution of the Model otherwise complies with the conditions stated in this License.
89
- 5. Use-based restrictions. The restrictions set forth in Attachment A are considered Use-based restrictions. Therefore You cannot use the Model and the Derivatives of the Model for the specified restricted uses. You may use the Model subject to this License, including only for lawful purposes and in accordance with the License. Use may include creating any content with, finetuning, updating, running, training, evaluating and/or reparametrizing the Model. You shall require all of Your users who use the Model or a Derivative of the Model to comply with the terms of this paragraph (paragraph 5).
90
- 6. The Output You Generate. Except as set forth herein, Licensor claims no rights in the Output You generate using the Model. You are accountable for the Output you generate and its subsequent uses. No use of the output can contravene any provision as stated in the License.
91
-
92
- Section IV: OTHER PROVISIONS
93
-
94
- 7. Updates and Runtime Restrictions. To the maximum extent permitted by law, Licensor reserves the right to restrict (remotely or otherwise) usage of the Model in violation of this License.
95
- 8. Trademarks and related. Nothing in this License permits You to make use of Licensors’ trademarks, trade names, logos or to otherwise suggest endorsement or misrepresent the relationship between the parties; and any rights not expressly granted herein are reserved by the Licensors.
96
- 9. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Model and the Complementary Material (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Model, Derivatives of the Model, and the Complementary Material and assume any risks associated with Your exercise of permissions under this License.
97
- 10. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Model and the Complementary Material (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
98
- 11. Accepting Warranty or Additional Liability. While redistributing the Model, Derivatives of the Model and the Complementary Material thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
99
- 12. If any provision of this License is held to be invalid, illegal or unenforceable, the remaining provisions shall be unaffected thereby and remain valid as if such provision had not been set forth herein.
100
-
101
- END OF TERMS AND CONDITIONS
102
-
103
-
104
-
105
-
106
- Attachment A
107
-
108
- Use Restrictions
109
-
110
- You agree not to use the Model or Derivatives of the Model:
111
-
112
- - In any way that violates any applicable national, federal, state, local or international law or regulation;
113
- - For the purpose of exploiting, harming or attempting to exploit or harm minors in any way;
114
- - To generate or disseminate verifiably false information and/or content with the purpose of harming others;
115
- - To generate or disseminate personal identifiable information that can be used to harm an individual;
116
- - To defame, disparage or otherwise harass others;
117
- - For fully automated decision making that adversely impacts an individual’s legal rights or otherwise creates or modifies a binding, enforceable obligation;
118
- - For any use intended to or which has the effect of discriminating against or harming individuals or groups based on online or offline social behavior or known or predicted personal or personality characteristics;
119
- - To exploit any of the vulnerabilities of a specific group of persons based on their age, social, physical or mental characteristics, in order to materially distort the behavior of a person pertaining to that group in a manner that causes or is likely to cause that person or another person physical or psychological harm;
120
- - For any use intended to or which has the effect of discriminating against individuals or groups based on legally protected characteristics or categories;
121
- - To provide medical advice and medical results interpretation;
122
- - To generate or disseminate information for the purpose to be used for administration of justice, law enforcement, immigration or asylum processes, such as predicting an individual will commit fraud/crime commitment (e.g. by text profiling, drawing causal relationships between assertions made in documents, indiscriminate and arbitrarily-targeted use).
123
-
124
-
125
-
126
- Open Source Model Licensed under the TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT and Other Licenses of the Third-Party Components therein:
127
- --------------------------------------------------------------------
128
- 1. HunyuanDiT
129
- Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
130
-
131
-
132
- Terms of the TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT:
133
- --------------------------------------------------------------------
134
- TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT
135
- Tencent Hunyuan Release Date: 2024/5/14
136
- By clicking to agree or by using, reproducing, modifying, distributing, performing or displaying any portion or element of the Tencent Hunyuan Works, including via any Hosted Service, You will be deemed to have recognized and accepted the content of this Agreement, which is effective immediately.
137
- 1. DEFINITIONS.
138
- a. “Acceptable Use Policy” shall mean the policy made available by Tencent as set forth in the Exhibit A.
139
- b. “Agreement” shall mean the terms and conditions for use, reproduction, distribution, modification, performance and displaying of the Hunyuan Works or any portion or element thereof set forth herein.
140
- c. “Documentation” shall mean the specifications, manuals and documentation for Tencent Hunyuan made publicly available by Tencent.
141
- d. “Hosted Service” shall mean a hosted service offered via an application programming interface (API), web access, or any other electronic or remote means.
142
- e. “Licensee,” “You” or “Your” shall mean a natural person or legal entity exercising the rights granted by this Agreement and/or using the Tencent Hunyuan Works for any purpose and in any field of use.
143
- f. “Materials” shall mean, collectively, Tencent’s proprietary Tencent Hunyuan and Documentation (and any portion thereof) as made available by Tencent under this Agreement.
144
- g. “Model Derivatives” shall mean all: (i) modifications to Tencent Hunyuan or any Model Derivative of Tencent Hunyuan; (ii) works based on Tencent Hunyuan or any Model Derivative of Tencent Hunyuan; or (iii) any other machine learning model which is created by transfer of patterns of the weights, parameters, operations, or Output of Tencent Hunyuan or any Model Derivative of Tencent Hunyuan, to that model in order to cause that model to perform similarly to Tencent Hunyuan or a Model Derivative of Tencent Hunyuan, including distillation methods, methods that use intermediate data representations, or methods based on the generation of synthetic data Outputs by Tencent Hunyuan or a Model Derivative of Tencent Hunyuan for training that model. For clarity, Outputs by themselves are not deemed Model Derivatives.
145
- h. “Output” shall mean the information and/or content output of Tencent Hunyuan or a Model Derivative that results from operating or otherwise using Tencent Hunyuan or a Model Derivative, including via a Hosted Service.
146
- i. “Tencent,” “We” or “Us” shall mean THL A29 Limited.
147
- j. “Tencent Hunyuan” shall mean the large language models, image/video/audio/3D generation models, and multimodal large language models and their software and algorithms, including trained model weights, parameters (including optimizer states), machine-learning model code, inference-enabling code, training-enabling code, fine-tuning enabling code and other elements of the foregoing made publicly available by Us at https://huggingface.co/Tencent-Hunyuan/HunyuanDiT and https://github.com/Tencent/HunyuanDiT .
148
- k. “Tencent Hunyuan Works” shall mean: (i) the Materials; (ii) Model Derivatives; and (iii) all derivative works thereof.
149
- l. “Third Party” or “Third Parties” shall mean individuals or legal entities that are not under common control with Us or You.
150
- m. “including” shall mean including but not limited to.
151
- 2. GRANT OF RIGHTS.
152
- We grant You a non-exclusive, worldwide, non-transferable and royalty-free limited license under Tencent’s intellectual property or other rights owned by Us embodied in or utilized by the Materials to use, reproduce, distribute, create derivative works of (including Model Derivatives), and make modifications to the Materials, only in accordance with the terms of this Agreement and the Acceptable Use Policy, and You must not violate (or encourage or permit anyone else to violate) any term of this Agreement or the Acceptable Use Policy.
153
- 3. DISTRIBUTION.
154
- You may, subject to Your compliance with this Agreement, distribute or make available to Third Parties the Tencent Hunyuan Works, provided that You meet all of the following conditions:
155
- a. You must provide all such Third Party recipients of the Tencent Hunyuan Works or products or services using them a copy of this Agreement;
156
- b. You must cause any modified files to carry prominent notices stating that You changed the files;
157
- c. You are encouraged to: (i) publish at least one technology introduction blogpost or one public statement expressing Your experience of using the Tencent Hunyuan Works; and (ii) mark the products or services developed by using the Tencent Hunyuan Works to indicate that the product/service is “Powered by Tencent Hunyuan”; and
158
- d. All distributions to Third Parties (other than through a Hosted Service) must be accompanied by a “Notice” text file that contains the following notice: “Tencent Hunyuan is licensed under the Tencent Hunyuan Community License Agreement, Copyright © 2024 Tencent. All Rights Reserved. The trademark rights of “Tencent Hunyuan” are owned by Tencent or its affiliate.”
159
- You may add Your own copyright statement to Your modifications and, except as set forth in this Section and in Section 5, may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Model Derivatives as a whole, provided Your use, reproduction, modification, distribution, performance and display of the work otherwise complies with the terms and conditions of this Agreement. If You receive Tencent Hunyuan Works from a Licensee as part of an integrated end user product, then this Section 3 of this Agreement will not apply to You.
160
- 4. ADDITIONAL COMMERCIAL TERMS.
161
- If, on the Tencent Hunyuan version release date, the monthly active users of all products or services made available by or for Licensee is greater than 100 million monthly active users in the preceding calendar month, You must request a license from Tencent, which Tencent may grant to You in its sole discretion, and You are not authorized to exercise any of the rights under this Agreement unless or until Tencent otherwise expressly grants You such rights.
162
- 5. RULES OF USE.
163
- a. Your use of the Tencent Hunyuan Works must comply with applicable laws and regulations (including trade compliance laws and regulations) and adhere to the Acceptable Use Policy for the Tencent Hunyuan Works, which is hereby incorporated by reference into this Agreement. You must include the use restrictions referenced in these Sections 5(a) and 5(b) as an enforceable provision in any agreement (e.g., license agreement, terms of use, etc.) governing the use and/or distribution of Tencent Hunyuan Works and You must provide notice to subsequent users to whom You distribute that Tencent Hunyuan Works are subject to the use restrictions in these Sections 5(a) and 5(b).
164
- b. You must not use the Tencent Hunyuan Works or any Output or results of the Tencent Hunyuan Works to improve any other large language model (other than Tencent Hunyuan or Model Derivatives thereof).
165
- 6. INTELLECTUAL PROPERTY.
166
- a. Subject to Tencent’s ownership of Tencent Hunyuan Works made by or for Tencent and intellectual property rights therein, conditioned upon Your compliance with the terms and conditions of this Agreement, as between You and Tencent, You will be the owner of any derivative works and modifications of the Materials and any Model Derivatives that are made by or for You.
167
- b. No trademark licenses are granted under this Agreement, and in connection with the Tencent Hunyuan Works, Licensee may not use any name or mark owned by or associated with Tencent or any of its affiliates, except as required for reasonable and customary use in describing and distributing the Tencent Hunyuan Works. Tencent hereby grants You a license to use “Tencent Hunyuan” (the “Mark”) solely as required to comply with the provisions of Section 3(c), provided that You comply with any applicable laws related to trademark protection. All goodwill arising out of Your use of the Mark will inure to the benefit of Tencent.
168
- c. If You commence a lawsuit or other proceedings (including a cross-claim or counterclaim in a lawsuit) against Us or any person or entity alleging that the Materials or any Output, or any portion of any of the foregoing, infringe any intellectual property or other right owned or licensable by You, then all licenses granted to You under this Agreement shall terminate as of the date such lawsuit or other proceeding is filed. You will defend, indemnify and hold harmless Us from and against any claim by any Third Party arising out of or related to Your or the Third Party’s use or distribution of the Tencent Hunyuan Works.
169
- d. Tencent claims no rights in Outputs You generate. You and Your users are solely responsible for Outputs and their subsequent uses.
170
- 7. DISCLAIMERS OF WARRANTY AND LIMITATIONS OF LIABILITY.
171
- a. We are not obligated to support, update, provide training for, or develop any further version of the Tencent Hunyuan Works or to grant any license thereto.
172
- b. UNLESS AND ONLY TO THE EXTENT REQUIRED BY APPLICABLE LAW, THE TENCENT HUNYUAN WORKS AND ANY OUTPUT AND RESULTS THEREFROM ARE PROVIDED “AS IS” WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND INCLUDING ANY WARRANTIES OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, COURSE OF DEALING, USAGE OF TRADE, OR FITNESS FOR A PARTICULAR PURPOSE. YOU ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USING, REPRODUCING, MODIFYING, PERFORMING, DISPLAYING OR DISTRIBUTING ANY OF THE TENCENT HUNYUAN WORKS OR OUTPUTS AND ASSUME ANY AND ALL RISKS ASSOCIATED WITH YOUR OR A THIRD PARTY’S USE OR DISTRIBUTION OF ANY OF THE TENCENT HUNYUAN WORKS OR OUTPUTS AND YOUR EXERCISE OF RIGHTS AND PERMISSIONS UNDER THIS AGREEMENT.
173
- c. TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL TENCENT OR ITS AFFILIATES BE LIABLE UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, NEGLIGENCE, PRODUCTS LIABILITY, OR OTHERWISE, FOR ANY DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR LOST PROFITS OF ANY KIND ARISING FROM THIS AGREEMENT OR RELATED TO ANY OF THE TENCENT HUNYUAN WORKS OR OUTPUTS, EVEN IF TENCENT OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF ANY OF THE FOREGOING.
174
- 8. SURVIVAL AND TERMINATION.
175
- a. The term of this Agreement shall commence upon Your acceptance of this Agreement or access to the Materials and will continue in full force and effect until terminated in accordance with the terms and conditions herein.
176
- b. We may terminate this Agreement if You breach any of the terms or conditions of this Agreement. Upon termination of this Agreement, You must promptly delete and cease use of the Tencent Hunyuan Works. Sections 6(a), 6(c), 7 and 9 shall survive the termination of this Agreement.
177
- 9. GOVERNING LAW AND JURISDICTION.
178
- a. This Agreement and any dispute arising out of or relating to it will be governed by the laws of the Hong Kong Special Administrative Region of the People’s Republic of China, without regard to conflict of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement.
179
- b. Exclusive jurisdiction and venue for any dispute arising out of or relating to this Agreement will be a court of competent jurisdiction in the Hong Kong Special Administrative Region of the People’s Republic of China, and Tencent and Licensee consent to the exclusive jurisdiction of such court with respect to any such dispute.
180
-
181
-
182
- EXHIBIT A
183
- ACCEPTABLE USE POLICY
184
-
185
- Tencent reserves the right to update this Acceptable Use Policy from time to time.
186
- Last modified: 2024/5/14
187
-
188
- Tencent endeavors to promote safe and fair use of its tools and features, including Tencent Hunyuan. You agree not to use Tencent Hunyuan or Model Derivatives:
189
- 1. In any way that violates any applicable national, federal, state, local, international or any other law or regulation;
190
- 2. To harm Yourself or others;
191
- 3. To repurpose or distribute output from Tencent Hunyuan or any Model Derivatives to harm Yourself or others;
192
- 4. To override or circumvent the safety guardrails and safeguards We have put in place;
193
- 5. For the purpose of exploiting, harming or attempting to exploit or harm minors in any way;
194
- 6. To generate or disseminate verifiably false information and/or content with the purpose of harming others or influencing elections;
195
- 7. To generate or facilitate false online engagement, including fake reviews and other means of fake online engagement;
196
- 8. To intentionally defame, disparage or otherwise harass others;
197
- 9. To generate and/or disseminate malware (including ransomware) or any other content to be used for the purpose of harming electronic systems;
198
- 10. To generate or disseminate personal identifiable information with the purpose of harming others;
199
- 11. To generate or disseminate information (including images, code, posts, articles), and place the information in any public context (including –through the use of bot generated tweets), without expressly and conspicuously identifying that the information and/or content is machine generated;
200
- 12. To impersonate another individual without consent, authorization, or legal right;
201
- 13. To make high-stakes automated decisions in domains that affect an individual’s safety, rights or wellbeing (e.g., law enforcement, migration, medicine/health, management of critical infrastructure, safety components of products, essential services, credit, employment, housing, education, social scoring, or insurance);
202
- 14. In a manner that violates or disrespects the social ethics and moral standards of other countries or regions;
203
- 15. To perform, facilitate, threaten, incite, plan, promote or encourage violent extremism or terrorism;
204
- 16. For any use intended to discriminate against or harm individuals or groups based on protected characteristics or categories, online or offline social behavior or known or predicted personal or personality characteristics;
205
- 17. To intentionally exploit any of the vulnerabilities of a specific group of persons based on their age, social, physical or mental characteristics, in order to materially distort the behavior of a person pertaining to that group in a manner that causes or is likely to cause that person or another person physical or psychological harm;
206
- 18. For military purposes;
207
- 19. To engage in the unauthorized or unlicensed practice of any profession including, but not limited to, financial, legal, medical/health, or other professional practices.
208
-
209
- For the license of other third party components, please refer to the following URL:
210
- https://huggingface.co/Tencent-Hunyuan/HunyuanDiT/blob/main/Notice
211
-
212
- --------------------------------------------------------------------
213
-
214
- This Model also incorporates insights from Flux's neural network architechtures (https://github.com/black-forest-labs/flux?tab=readme-ov-file). Credits are given to the orginal authors.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/README-zh.md DELETED
@@ -1,47 +0,0 @@
1
- # Hunyuan3D-2.1-Shape
2
-
3
-
4
- # 训练
5
-
6
- 我们会展示小数据集上DiT的训练全流程
7
-
8
- ## 数据预处理
9
-
10
- 渲染和水密化参考[链接](tools/README.md),最终得到如下结构
11
-
12
- ``` yaml
13
- dataset/preprocessed/{uid}
14
- ├── geo_data
15
- │ ├── {uid}_sdf.npz
16
- │ ├── {uid}_surface.npz
17
- │ └── {uid}_watertight.obj
18
- └── render_cond
19
- ├── 000.png
20
- ├── ...
21
- ├── 023.png
22
- ├── mesh.ply
23
- └── transforms.json
24
- ```
25
-
26
- 我们提供了一个8个case(均来自Objaverse-XL)预处理后的结果在 tools/mini_trainset,可以直接用于过拟合训练
27
-
28
-
29
-
30
- ## 启动训练
31
-
32
- 我们提供了可供参考的训练配置文件和启动脚本(默认单机8卡deepspeed训练),用户根据需要自行修改。
33
-
34
- 配置文件
35
- ```
36
- configs/dit-from-scratch-overfitting-flowmatching-dinog518-bf16-lr1e4-1024.yaml
37
- ```
38
- 启动脚本
39
-
40
- ```
41
- export node_num=1
42
- export node_rank=0
43
- export master_ip=0.0.0.0 # set your master_ip
44
- export config='configs/dit-from-scratch-overfitting-flowmatching-dinog518-bf16-lr1e4-1024.yaml'
45
- export output_dir='output_folder/dit/overfitting'
46
- bash scripts/train_deepspeed.sh $node_num $node_rank $master_ip $config $output_dir
47
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/README.md DELETED
@@ -1,54 +0,0 @@
1
- # Hunyuan3D-2.1-Shape
2
-
3
- ## Quick Inference
4
-
5
- Given a reference image `image.png`, you can run inference using the following code. The result will be saved as `demo.glb`.
6
-
7
- ```bash
8
- python3 minimal_demo.py
9
- ```
10
-
11
- **Memory Recommendation:** For we recommend using a GPU with at least **10GB VRAM**.
12
-
13
- # Training
14
-
15
- Here we demonstrate the complete training workflow of DiT on a small dataset.
16
-
17
- ## Data Preprocessing
18
-
19
- The rendering and watertight mesh generation process is described in detail in [this document](tools/README.md). After preprocessing, the dataset directory structure should look like the following:
20
-
21
- ```yaml
22
- dataset/preprocessed/{uid}
23
- ├── geo_data
24
- │ ├── {uid}_sdf.npz
25
- │ ├── {uid}_surface.npz
26
- │ └── {uid}_watertight.obj
27
- └── render_cond
28
- ├── 000.png
29
- ├── ...
30
- ├── 023.png
31
- ├── mesh.ply
32
- └── transforms.json
33
- ```
34
-
35
- We provide a preprocessed mini_dataset containing 8 cases (all sourced from Objaverse-XL) as `tools/mini_trainset`, which can be used directly for DiT overfitting training experiments.
36
-
37
- ## Launching Training
38
-
39
- We provide example configuration files and launch scripts for reference. By default, the training runs on a single node with 8 GPUs using DeepSpeed. Users can modify the configurations and scripts as needed to suit their environment.
40
-
41
- Configuration File
42
- ```
43
- configs/hunyuandit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-512.yaml
44
- ```
45
- Launch Script
46
-
47
- ```
48
- export node_num=1
49
- export node_rank=0
50
- export master_ip=0.0.0.0 # set your master_ip
51
- export config=configs/hunyuandit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-512.yaml
52
- export output_dir=output_folder/dit/overfitting
53
- bash scripts/train_deepspeed.sh $node_num $node_rank $master_ip $config $output_dir
54
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/configs/hunyuan3ddit-full-params-finetuning-flowmatching-dinog518-bf16-lr1e5-512.yaml DELETED
@@ -1,174 +0,0 @@
1
- name: "DiT: Flux large flowmatching; VAE: 1024 token length; ImageEncoder: DINO Giant; ImageSize: 518"
2
-
3
- training:
4
- steps: 10_0000_0000
5
- use_amp: true
6
- amp_type: "bf16"
7
- base_lr: 1.e-5
8
- gradient_clip_val: 1.0
9
- gradient_clip_algorithm: "norm"
10
- every_n_train_steps: 2000 # 5000
11
- val_check_interval: 50 # 4096
12
- limit_val_batches: 16
13
-
14
- dataset:
15
- target: hy3dshape.data.dit_asl.AlignedShapeLatentModule
16
- params:
17
- #! Base setting
18
- batch_size: 4
19
- num_workers: 8
20
- val_num_workers: 4
21
-
22
- # Data
23
- train_data_list: tools/mini_trainset/preprocessed
24
- val_data_list: tools/mini_trainset/preprocessed
25
-
26
- #! Image loading
27
- cond_stage_key: "image" # image / text / image_text
28
- image_size: 518
29
- mean: &mean [0.5, 0.5, 0.5]
30
- std: &std [0.5, 0.5, 0.5]
31
-
32
- #! Point cloud sampling
33
- pc_size: &pc_size 30720
34
- pc_sharpedge_size: &pc_sharpedge_size 30720
35
- sharpedge_label: &sharpedge_label true
36
- return_normal: true
37
-
38
- #! Augmentation
39
- padding: true
40
-
41
- model:
42
- target: hy3dshape.models.diffusion.flow_matching_sit.Diffuser
43
- params:
44
- first_stage_key: "surface"
45
- cond_stage_key: "image"
46
- scale_by_std: false
47
- z_scale_factor: &z_scale_factor 0.9990943042622529 # 1 / 1.0009065167661184
48
- torch_compile: false
49
-
50
- # ema_config:
51
- # ema_model: LitEma
52
- # ema_decay: 0.999
53
- # ema_inference: false
54
-
55
- first_stage_config:
56
- target: hy3dshape.models.autoencoders.ShapeVAE
57
- from_pretrained: tencent/Hunyuan3D-2.1
58
- params:
59
- num_latents: &num_latents 512
60
- embed_dim: 64
61
- num_freqs: 8
62
- include_pi: false
63
- heads: 16
64
- width: 1024
65
- point_feats: 4
66
- num_decoder_layers: 16
67
- pc_size: *pc_size
68
- pc_sharpedge_size: *pc_sharpedge_size
69
- qkv_bias: false
70
- qk_norm: true
71
- scale_factor: *z_scale_factor
72
- geo_decoder_mlp_expand_ratio: 4
73
- geo_decoder_downsample_ratio: 1
74
- geo_decoder_ln_post: true
75
-
76
- cond_stage_config:
77
- target: hy3dshape.models.conditioner.SingleImageEncoder
78
- params:
79
- main_image_encoder:
80
- type: DinoImageEncoder # dino giant
81
- kwargs:
82
- config:
83
- attention_probs_dropout_prob: 0.0
84
- drop_path_rate: 0.0
85
- hidden_act: gelu
86
- hidden_dropout_prob: 0.0
87
- hidden_size: 1536
88
- image_size: 518
89
- initializer_range: 0.02
90
- layer_norm_eps: 1.e-6
91
- layerscale_value: 1.0
92
- mlp_ratio: 4
93
- model_type: dinov2
94
- num_attention_heads: 24
95
- num_channels: 3
96
- num_hidden_layers: 40
97
- patch_size: 14
98
- qkv_bias: true
99
- torch_dtype: float32
100
- use_swiglu_ffn: true
101
- image_size: 518
102
-
103
- denoiser_cfg:
104
- target: hy3dshape.models.denoisers.hunyuan3ddit.Hunyuan3DDiT
105
- params:
106
- ckpt_path: ~/.cache/hy3dgen/tencent/Hunyuan3D-2-1-Shape/dit/model.fp16.ckpt
107
- input_size: *num_latents
108
- context_in_dim: 1536
109
- hidden_size: 1024
110
- mlp_ratio: 4.0
111
- num_heads: 16
112
- depth: 16
113
- depth_single_blocks: 32
114
- axes_dim: [64]
115
- theta: 10000
116
- qkv_bias: true
117
- use_pe: false
118
- force_norm_fp32: true
119
-
120
- scheduler_cfg:
121
- transport:
122
- target: hy3dshape.models.diffusion.transport.create_transport
123
- params:
124
- path_type: Linear
125
- prediction: velocity
126
- sampler:
127
- target: hy3dshape.models.diffusion.transport.Sampler
128
- params: {}
129
- ode_params:
130
- sampling_method: euler # dopri5 ...
131
- num_steps: &num_steps 50
132
-
133
- optimizer_cfg:
134
- optimizer:
135
- target: torch.optim.AdamW
136
- params:
137
- betas: [0.9, 0.99]
138
- eps: 1.e-6
139
- weight_decay: 1.e-2
140
-
141
- scheduler:
142
- target: hy3dshape.utils.trainings.lr_scheduler.LambdaWarmUpCosineFactorScheduler
143
- params:
144
- warm_up_steps: 50 # 5000
145
- f_start: 1.e-6
146
- f_min: 1.e-3
147
- f_max: 1.0
148
-
149
- pipeline_cfg:
150
- target: hy3dshape.pipelines.Hunyuan3DDiTFlowMatchingPipeline
151
-
152
- image_processor_cfg:
153
- target: hy3dshape.preprocessors.ImageProcessorV2
154
- params: {}
155
-
156
- callbacks:
157
- logger:
158
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalASLDiffuserLogger
159
- params:
160
- step_frequency: 100 # 10000
161
- num_samples: 1
162
- sample_times: 1
163
- mean: *mean
164
- std: *std
165
- bounds: [-1.01, -1.01, -1.01, 1.01, 1.01, 1.01]
166
- octree_depth: 8
167
- num_chunks: 50000
168
- mc_level: 0.0
169
-
170
- file_loggers:
171
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalFixASLDiffuserLogger
172
- params:
173
- step_frequency: 50 # 5000
174
- test_data_path: "tools/mini_testset/images.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/configs/hunyuan3ddit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-512.yaml DELETED
@@ -1,173 +0,0 @@
1
- name: "DiT: Flux large flowmatching; VAE: 1024 token length; ImageEncoder: DINO Giant; ImageSize: 518"
2
-
3
- training:
4
- steps: 10_0000_0000
5
- use_amp: true
6
- amp_type: "bf16"
7
- base_lr: 1e-4
8
- gradient_clip_val: 1.0
9
- gradient_clip_algorithm: "norm"
10
- every_n_train_steps: 2000 # 5000
11
- val_check_interval: 50 # 4096
12
- limit_val_batches: 16
13
-
14
- dataset:
15
- target: hy3dshape.data.dit_asl.AlignedShapeLatentModule
16
- params:
17
- #! Base setting
18
- batch_size: 2
19
- num_workers: 8
20
- val_num_workers: 4
21
-
22
- # Data
23
- train_data_list: tools/mini_trainset/preprocessed
24
- val_data_list: tools/mini_trainset/preprocessed
25
-
26
- #! Image loading
27
- cond_stage_key: "image" # image / text / image_text
28
- image_size: 518
29
- mean: &mean [0.5, 0.5, 0.5]
30
- std: &std [0.5, 0.5, 0.5]
31
-
32
- #! Point cloud sampling
33
- pc_size: &pc_size 10240
34
- pc_sharpedge_size: &pc_sharpedge_size 10240
35
- sharpedge_label: &sharpedge_label true
36
- return_normal: true
37
-
38
- #! Augmentation
39
- padding: true
40
-
41
- model:
42
- target: hy3dshape.models.diffusion.flow_matching_sit.Diffuser
43
- params:
44
- first_stage_key: "surface"
45
- cond_stage_key: "image"
46
- scale_by_std: false
47
- z_scale_factor: &z_scale_factor 0.9990943042622529 # 1 / 1.0009065167661184
48
- torch_compile: false
49
-
50
- # ema_config:
51
- # ema_model: LitEma
52
- # ema_decay: 0.999
53
- # ema_inference: false
54
-
55
- first_stage_config:
56
- target: hy3dshape.models.autoencoders.ShapeVAE
57
- from_pretrained: tencent/Hunyuan3D-2.1
58
- params:
59
- num_latents: &num_latents 512
60
- embed_dim: 64
61
- num_freqs: 8
62
- include_pi: false
63
- heads: 16
64
- width: 1024
65
- point_feats: 4
66
- num_decoder_layers: 16
67
- pc_size: *pc_size
68
- pc_sharpedge_size: *pc_sharpedge_size
69
- qkv_bias: false
70
- qk_norm: true
71
- scale_factor: *z_scale_factor
72
- geo_decoder_mlp_expand_ratio: 4
73
- geo_decoder_downsample_ratio: 1
74
- geo_decoder_ln_post: true
75
-
76
- cond_stage_config:
77
- target: hy3dshape.models.conditioner.SingleImageEncoder
78
- params:
79
- main_image_encoder:
80
- type: DinoImageEncoder # dino giant
81
- kwargs:
82
- config:
83
- attention_probs_dropout_prob: 0.0
84
- drop_path_rate: 0.0
85
- hidden_act: gelu
86
- hidden_dropout_prob: 0.0
87
- hidden_size: 1536
88
- image_size: 518
89
- initializer_range: 0.02
90
- layer_norm_eps: 1.e-6
91
- layerscale_value: 1.0
92
- mlp_ratio: 4
93
- model_type: dinov2
94
- num_attention_heads: 24
95
- num_channels: 3
96
- num_hidden_layers: 40
97
- patch_size: 14
98
- qkv_bias: true
99
- torch_dtype: float32
100
- use_swiglu_ffn: true
101
- image_size: 518
102
-
103
- denoiser_cfg:
104
- target: hy3dshape.models.denoisers.hunyuan3ddit.Hunyuan3DDiT
105
- params:
106
- input_size: *num_latents
107
- context_in_dim: 1536
108
- hidden_size: 1024
109
- mlp_ratio: 4.0
110
- num_heads: 16
111
- depth: 8
112
- depth_single_blocks: 16
113
- axes_dim: [64]
114
- theta: 10000
115
- qkv_bias: true
116
- use_pe: false
117
- force_norm_fp32: true
118
-
119
- scheduler_cfg:
120
- transport:
121
- target: hy3dshape.models.diffusion.transport.create_transport
122
- params:
123
- path_type: Linear
124
- prediction: velocity
125
- sampler:
126
- target: hy3dshape.models.diffusion.transport.Sampler
127
- params: {}
128
- ode_params:
129
- sampling_method: euler # dopri5 ...
130
- num_steps: &num_steps 50
131
-
132
- optimizer_cfg:
133
- optimizer:
134
- target: torch.optim.AdamW
135
- params:
136
- betas: [0.9, 0.99]
137
- eps: 1.e-6
138
- weight_decay: 1.e-2
139
-
140
- scheduler:
141
- target: hy3dshape.utils.trainings.lr_scheduler.LambdaWarmUpCosineFactorScheduler
142
- params:
143
- warm_up_steps: 50 # 5000
144
- f_start: 1.e-6
145
- f_min: 1.e-3
146
- f_max: 1.0
147
-
148
- pipeline_cfg:
149
- target: hy3dshape.pipelines.Hunyuan3DDiTFlowMatchingPipeline
150
-
151
- image_processor_cfg:
152
- target: hy3dshape.preprocessors.ImageProcessorV2
153
- params: {}
154
-
155
- callbacks:
156
- logger:
157
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalASLDiffuserLogger
158
- params:
159
- step_frequency: 100 # 10000
160
- num_samples: 1
161
- sample_times: 1
162
- mean: *mean
163
- std: *std
164
- bounds: [-1.01, -1.01, -1.01, 1.01, 1.01, 1.01]
165
- octree_depth: 8
166
- num_chunks: 50000
167
- mc_level: 0.0
168
-
169
- file_loggers:
170
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalFixASLDiffuserLogger
171
- params:
172
- step_frequency: 50 # 5000
173
- test_data_path: "tools/mini_testset/images.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/configs/hunyuandit-finetuning-flowmatching-dinog518-bf16-lr1e5-4096.yaml DELETED
@@ -1,180 +0,0 @@
1
- name: "DiT: Flux large flowmatching; VAE: 1024 token length; ImageEncoder: DINO Giant; ImageSize: 518"
2
-
3
- training:
4
- steps: 10_0000_0000
5
- use_amp: true
6
- amp_type: "bf16"
7
- base_lr: 1e-5
8
- gradient_clip_val: 1.0
9
- gradient_clip_algorithm: "norm"
10
- every_n_train_steps: 2000 # 5000
11
- val_check_interval: 50 # 4096
12
- limit_val_batches: 16
13
-
14
- dataset:
15
- target: hy3dshape.data.dit_asl.AlignedShapeLatentModule
16
- params:
17
- #! Base setting
18
- batch_size: 4
19
- num_workers: 8
20
- val_num_workers: 4
21
-
22
- # Data
23
- train_data_list: tools/mini_trainset/preprocessed
24
- val_data_list: tools/mini_trainset/preprocessed
25
-
26
- #! Image loading
27
- cond_stage_key: "image" # image / text / image_text
28
- image_size: 518
29
- mean: &mean [0.5, 0.5, 0.5]
30
- std: &std [0.5, 0.5, 0.5]
31
-
32
- #! Point cloud sampling
33
- pc_size: &pc_size 81920
34
- pc_sharpedge_size: &pc_sharpedge_size 0
35
- sharpedge_label: &sharpedge_label true
36
- return_normal: true
37
-
38
- #! Augmentation
39
- padding: true
40
-
41
- model:
42
- target: hy3dshape.models.diffusion.flow_matching_sit.Diffuser
43
- params:
44
- first_stage_key: "surface"
45
- cond_stage_key: "image"
46
- scale_by_std: false
47
- z_scale_factor: &z_scale_factor 1.0039506158752403
48
- torch_compile: false
49
-
50
- # ema_config:
51
- # ema_model: LitEma
52
- # ema_decay: 0.999
53
- # ema_inference: false
54
-
55
- first_stage_config:
56
- target: hy3dshape.models.autoencoders.ShapeVAE
57
- from_pretrained: tencent/Hunyuan3D-2.1
58
- params:
59
- num_latents: &num_latents 4096
60
- embed_dim: 64
61
- num_freqs: 8
62
- include_pi: false
63
- heads: 16
64
- width: 1024
65
- num_encoder_layers: 8
66
- num_decoder_layers: 16
67
- qkv_bias: false
68
- qk_norm: true
69
- scale_factor: *z_scale_factor
70
- geo_decoder_mlp_expand_ratio: 4
71
- geo_decoder_downsample_ratio: 1
72
- geo_decoder_ln_post: true
73
- point_feats: 4
74
- pc_size: *pc_size
75
- pc_sharpedge_size: *pc_sharpedge_size
76
-
77
- cond_stage_config:
78
- target: hy3dshape.models.conditioner.SingleImageEncoder
79
- params:
80
- main_image_encoder:
81
- type: DinoImageEncoder # dino large
82
- kwargs:
83
- config:
84
- attention_probs_dropout_prob: 0.0
85
- drop_path_rate: 0.0
86
- hidden_act: gelu
87
- hidden_dropout_prob: 0.0
88
- hidden_size: 1024
89
- image_size: 518
90
- initializer_range: 0.02
91
- layer_norm_eps: 1.e-6
92
- layerscale_value: 1.0
93
- mlp_ratio: 4
94
- model_type: dinov2
95
- num_attention_heads: 16
96
- num_channels: 3
97
- num_hidden_layers: 24
98
- patch_size: 14
99
- qkv_bias: true
100
- torch_dtype: float32
101
- use_swiglu_ffn: false
102
- image_size: 518
103
- use_cls_token: true
104
-
105
-
106
- denoiser_cfg:
107
- target: hy3dshape.models.denoisers.hunyuandit.HunYuanDiTPlain
108
- params:
109
- input_size: *num_latents
110
- in_channels: 64
111
- hidden_size: 2048
112
- context_dim: 1024
113
- depth: 21
114
- num_heads: 16
115
- qk_norm: true
116
- text_len: 1370
117
- with_decoupled_ca: false
118
- use_attention_pooling: false
119
- qk_norm_type: 'rms'
120
- qkv_bias: false
121
- use_pos_emb: false
122
- num_moe_layers: 6
123
- num_experts: 8
124
- moe_top_k: 2
125
-
126
- scheduler_cfg:
127
- transport:
128
- target: hy3dshape.models.diffusion.transport.create_transport
129
- params:
130
- path_type: Linear
131
- prediction: velocity
132
- sampler:
133
- target: hy3dshape.models.diffusion.transport.Sampler
134
- params: {}
135
- ode_params:
136
- sampling_method: euler # dopri5 ...
137
- num_steps: &num_steps 50
138
-
139
- optimizer_cfg:
140
- optimizer:
141
- target: torch.optim.AdamW
142
- params:
143
- betas: [0.9, 0.99]
144
- eps: 1.e-6
145
- weight_decay: 1.e-2
146
-
147
- scheduler:
148
- target: hy3dshape.utils.trainings.lr_scheduler.LambdaWarmUpCosineFactorScheduler
149
- params:
150
- warm_up_steps: 50 # 5000
151
- f_start: 1.e-6
152
- f_min: 1.e-3
153
- f_max: 1.0
154
-
155
- pipeline_cfg:
156
- target: hy3dshape.pipelines.Hunyuan3DDiTFlowMatchingPipeline
157
-
158
- image_processor_cfg:
159
- target: hy3dshape.preprocessors.ImageProcessorV2
160
- params: {}
161
-
162
- callbacks:
163
- logger:
164
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalASLDiffuserLogger
165
- params:
166
- step_frequency: 100 # 10000
167
- num_samples: 1
168
- sample_times: 1
169
- mean: *mean
170
- std: *std
171
- bounds: [-1.01, -1.01, -1.01, 1.01, 1.01, 1.01]
172
- octree_depth: 8
173
- num_chunks: 50000
174
- mc_level: 0.0
175
-
176
- file_loggers:
177
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalFixASLDiffuserLogger
178
- params:
179
- step_frequency: 50 # 5000
180
- test_data_path: "tools/mini_testset/images.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/configs/hunyuandit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-4096.yaml DELETED
@@ -1,180 +0,0 @@
1
- name: "DiT: Flux large flowmatching; VAE: 1024 token length; ImageEncoder: DINO Giant; ImageSize: 518"
2
-
3
- training:
4
- steps: 10_0000_0000
5
- use_amp: true
6
- amp_type: "bf16"
7
- base_lr: 1e-4
8
- gradient_clip_val: 1.0
9
- gradient_clip_algorithm: "norm"
10
- every_n_train_steps: 2000 # 5000
11
- val_check_interval: 50 # 4096
12
- limit_val_batches: 16
13
-
14
- dataset:
15
- target: hy3dshape.data.dit_asl.AlignedShapeLatentModule
16
- params:
17
- #! Base setting
18
- batch_size: 2
19
- num_workers: 8
20
- val_num_workers: 4
21
-
22
- # Data
23
- train_data_list: tools/mini_trainset/preprocessed
24
- val_data_list: tools/mini_trainset/preprocessed
25
-
26
- #! Image loading
27
- cond_stage_key: "image" # image / text / image_text
28
- image_size: 518
29
- mean: &mean [0.5, 0.5, 0.5]
30
- std: &std [0.5, 0.5, 0.5]
31
-
32
- #! Point cloud sampling
33
- pc_size: &pc_size 81920
34
- pc_sharpedge_size: &pc_sharpedge_size 0
35
- sharpedge_label: &sharpedge_label true
36
- return_normal: true
37
-
38
- #! Augmentation
39
- padding: true
40
-
41
- model:
42
- target: hy3dshape.models.diffusion.flow_matching_sit.Diffuser
43
- params:
44
- first_stage_key: "surface"
45
- cond_stage_key: "image"
46
- scale_by_std: false
47
- z_scale_factor: &z_scale_factor 1.0039506158752403
48
- torch_compile: false
49
-
50
- # ema_config:
51
- # ema_model: LitEma
52
- # ema_decay: 0.999
53
- # ema_inference: false
54
-
55
- first_stage_config:
56
- target: hy3dshape.models.autoencoders.ShapeVAE
57
- from_pretrained: tencent/Hunyuan3D-2.1
58
- params:
59
- num_latents: &num_latents 4096
60
- embed_dim: 64
61
- num_freqs: 8
62
- include_pi: false
63
- heads: 16
64
- width: 1024
65
- num_encoder_layers: 8
66
- num_decoder_layers: 16
67
- qkv_bias: false
68
- qk_norm: true
69
- scale_factor: *z_scale_factor
70
- geo_decoder_mlp_expand_ratio: 4
71
- geo_decoder_downsample_ratio: 1
72
- geo_decoder_ln_post: true
73
- point_feats: 4
74
- pc_size: *pc_size
75
- pc_sharpedge_size: *pc_sharpedge_size
76
-
77
- cond_stage_config:
78
- target: hy3dshape.models.conditioner.SingleImageEncoder
79
- params:
80
- main_image_encoder:
81
- type: DinoImageEncoder # dino large
82
- kwargs:
83
- config:
84
- attention_probs_dropout_prob: 0.0
85
- drop_path_rate: 0.0
86
- hidden_act: gelu
87
- hidden_dropout_prob: 0.0
88
- hidden_size: 1024
89
- image_size: 518
90
- initializer_range: 0.02
91
- layer_norm_eps: 1.e-6
92
- layerscale_value: 1.0
93
- mlp_ratio: 4
94
- model_type: dinov2
95
- num_attention_heads: 16
96
- num_channels: 3
97
- num_hidden_layers: 24
98
- patch_size: 14
99
- qkv_bias: true
100
- torch_dtype: float32
101
- use_swiglu_ffn: false
102
- image_size: 518
103
- use_cls_token: true
104
-
105
-
106
- denoiser_cfg:
107
- target: hy3dshape.models.denoisers.hunyuandit.HunYuanDiTPlain
108
- params:
109
- input_size: *num_latents
110
- in_channels: 64
111
- hidden_size: 2048
112
- context_dim: 1024
113
- depth: 11
114
- num_heads: 16
115
- qk_norm: true
116
- text_len: 1370
117
- with_decoupled_ca: false
118
- use_attention_pooling: false
119
- qk_norm_type: 'rms'
120
- qkv_bias: false
121
- use_pos_emb: false
122
- num_moe_layers: 6
123
- num_experts: 8
124
- moe_top_k: 2
125
-
126
- scheduler_cfg:
127
- transport:
128
- target: hy3dshape.models.diffusion.transport.create_transport
129
- params:
130
- path_type: Linear
131
- prediction: velocity
132
- sampler:
133
- target: hy3dshape.models.diffusion.transport.Sampler
134
- params: {}
135
- ode_params:
136
- sampling_method: euler # dopri5 ...
137
- num_steps: &num_steps 50
138
-
139
- optimizer_cfg:
140
- optimizer:
141
- target: torch.optim.AdamW
142
- params:
143
- betas: [0.9, 0.99]
144
- eps: 1.e-6
145
- weight_decay: 1.e-2
146
-
147
- scheduler:
148
- target: hy3dshape.utils.trainings.lr_scheduler.LambdaWarmUpCosineFactorScheduler
149
- params:
150
- warm_up_steps: 50 # 5000
151
- f_start: 1.e-6
152
- f_min: 1.e-3
153
- f_max: 1.0
154
-
155
- pipeline_cfg:
156
- target: hy3dshape.pipelines.Hunyuan3DDiTFlowMatchingPipeline
157
-
158
- image_processor_cfg:
159
- target: hy3dshape.preprocessors.ImageProcessorV2
160
- params: {}
161
-
162
- callbacks:
163
- logger:
164
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalASLDiffuserLogger
165
- params:
166
- step_frequency: 100 # 10000
167
- num_samples: 1
168
- sample_times: 1
169
- mean: *mean
170
- std: *std
171
- bounds: [-1.01, -1.01, -1.01, 1.01, 1.01, 1.01]
172
- octree_depth: 8
173
- num_chunks: 50000
174
- mc_level: 0.0
175
-
176
- file_loggers:
177
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalFixASLDiffuserLogger
178
- params:
179
- step_frequency: 50 # 5000
180
- test_data_path: "tools/mini_testset/images.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/configs/hunyuandit-mini-overfitting-flowmatching-dinog518-bf16-lr1e4-512.yaml DELETED
@@ -1,180 +0,0 @@
1
- name: "DiT: Flux large flowmatching; VAE: 1024 token length; ImageEncoder: DINO Giant; ImageSize: 518"
2
-
3
- training:
4
- steps: 10_0000_0000
5
- use_amp: true
6
- amp_type: "bf16"
7
- base_lr: 1e-4
8
- gradient_clip_val: 1.0
9
- gradient_clip_algorithm: "norm"
10
- every_n_train_steps: 2000 # 5000
11
- val_check_interval: 50 # 4096
12
- limit_val_batches: 16
13
-
14
- dataset:
15
- target: hy3dshape.data.dit_asl.AlignedShapeLatentModule
16
- params:
17
- #! Base setting
18
- batch_size: 2
19
- num_workers: 8
20
- val_num_workers: 4
21
-
22
- # Data
23
- train_data_list: tools/mini_trainset/preprocessed
24
- val_data_list: tools/mini_trainset/preprocessed
25
-
26
- #! Image loading
27
- cond_stage_key: "image" # image / text / image_text
28
- image_size: 518
29
- mean: &mean [0.5, 0.5, 0.5]
30
- std: &std [0.5, 0.5, 0.5]
31
-
32
- #! Point cloud sampling
33
- pc_size: &pc_size 81920
34
- pc_sharpedge_size: &pc_sharpedge_size 0
35
- sharpedge_label: &sharpedge_label true
36
- return_normal: true
37
-
38
- #! Augmentation
39
- padding: true
40
-
41
- model:
42
- target: hy3dshape.models.diffusion.flow_matching_sit.Diffuser
43
- params:
44
- first_stage_key: "surface"
45
- cond_stage_key: "image"
46
- scale_by_std: false
47
- z_scale_factor: &z_scale_factor 1.0039506158752403
48
- torch_compile: false
49
-
50
- # ema_config:
51
- # ema_model: LitEma
52
- # ema_decay: 0.999
53
- # ema_inference: false
54
-
55
- first_stage_config:
56
- target: hy3dshape.models.autoencoders.ShapeVAE
57
- from_pretrained: tencent/Hunyuan3D-2.1
58
- params:
59
- num_latents: &num_latents 512
60
- embed_dim: 64
61
- num_freqs: 8
62
- include_pi: false
63
- heads: 16
64
- width: 1024
65
- num_encoder_layers: 8
66
- num_decoder_layers: 16
67
- qkv_bias: false
68
- qk_norm: true
69
- scale_factor: *z_scale_factor
70
- geo_decoder_mlp_expand_ratio: 4
71
- geo_decoder_downsample_ratio: 1
72
- geo_decoder_ln_post: true
73
- point_feats: 4
74
- pc_size: *pc_size
75
- pc_sharpedge_size: *pc_sharpedge_size
76
-
77
- cond_stage_config:
78
- target: hy3dshape.models.conditioner.SingleImageEncoder
79
- params:
80
- main_image_encoder:
81
- type: DinoImageEncoder # dino large
82
- kwargs:
83
- config:
84
- attention_probs_dropout_prob: 0.0
85
- drop_path_rate: 0.0
86
- hidden_act: gelu
87
- hidden_dropout_prob: 0.0
88
- hidden_size: 1024
89
- image_size: 518
90
- initializer_range: 0.02
91
- layer_norm_eps: 1.e-6
92
- layerscale_value: 1.0
93
- mlp_ratio: 4
94
- model_type: dinov2
95
- num_attention_heads: 16
96
- num_channels: 3
97
- num_hidden_layers: 24
98
- patch_size: 14
99
- qkv_bias: true
100
- torch_dtype: float32
101
- use_swiglu_ffn: false
102
- image_size: 518
103
- use_cls_token: true
104
-
105
-
106
- denoiser_cfg:
107
- target: hy3dshape.models.denoisers.hunyuandit.HunYuanDiTPlain
108
- params:
109
- input_size: *num_latents
110
- in_channels: 64
111
- hidden_size: 768
112
- context_dim: 1024
113
- depth: 6
114
- num_heads: 12
115
- qk_norm: true
116
- text_len: 1370
117
- with_decoupled_ca: false
118
- use_attention_pooling: false
119
- qk_norm_type: 'rms'
120
- qkv_bias: false
121
- use_pos_emb: false
122
- num_moe_layers: 3
123
- num_experts: 4
124
- moe_top_k: 2
125
-
126
- scheduler_cfg:
127
- transport:
128
- target: hy3dshape.models.diffusion.transport.create_transport
129
- params:
130
- path_type: Linear
131
- prediction: velocity
132
- sampler:
133
- target: hy3dshape.models.diffusion.transport.Sampler
134
- params: {}
135
- ode_params:
136
- sampling_method: euler # dopri5 ...
137
- num_steps: &num_steps 50
138
-
139
- optimizer_cfg:
140
- optimizer:
141
- target: torch.optim.AdamW
142
- params:
143
- betas: [0.9, 0.99]
144
- eps: 1.e-6
145
- weight_decay: 1.e-2
146
-
147
- scheduler:
148
- target: hy3dshape.utils.trainings.lr_scheduler.LambdaWarmUpCosineFactorScheduler
149
- params:
150
- warm_up_steps: 50 # 5000
151
- f_start: 1.e-6
152
- f_min: 1.e-3
153
- f_max: 1.0
154
-
155
- pipeline_cfg:
156
- target: hy3dshape.pipelines.Hunyuan3DDiTFlowMatchingPipeline
157
-
158
- image_processor_cfg:
159
- target: hy3dshape.preprocessors.ImageProcessorV2
160
- params: {}
161
-
162
- callbacks:
163
- logger:
164
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalASLDiffuserLogger
165
- params:
166
- step_frequency: 100 # 10000
167
- num_samples: 1
168
- sample_times: 1
169
- mean: *mean
170
- std: *std
171
- bounds: [-1.01, -1.01, -1.01, 1.01, 1.01, 1.01]
172
- octree_depth: 8
173
- num_chunks: 50000
174
- mc_level: 0.0
175
-
176
- file_loggers:
177
- target: hy3dshape.utils.trainings.mesh_log_callback.ImageConditionalFixASLDiffuserLogger
178
- params:
179
- step_frequency: 50 # 5000
180
- test_data_path: "tools/mini_testset/images.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/hy3dshape/__init__.py DELETED
@@ -1,17 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- from .pipelines import Hunyuan3DDiTPipeline, Hunyuan3DDiTFlowMatchingPipeline
16
- from .postprocessors import FaceReducer, FloaterRemover, DegenerateFaceRemover, MeshSimplifier
17
- from .preprocessors import ImageProcessorV2, IMAGE_PROCESSORS, DEFAULT_IMAGEPROCESSOR
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/hy3dshape/models/__init__.py DELETED
@@ -1,28 +0,0 @@
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
- from .autoencoders import ShapeVAE
27
- from .conditioner import DualImageEncoder, SingleImageEncoder, DinoImageEncoder, CLIPImageEncoder
28
- from .denoisers import Hunyuan3DDiT
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/hy3dshape/models/autoencoders/__init__.py DELETED
@@ -1,20 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- from .attention_blocks import CrossAttentionDecoder
16
- from .attention_processors import FlashVDMCrossAttentionProcessor, CrossAttentionProcessor, \
17
- FlashVDMTopMCrossAttentionProcessor
18
- from .model import ShapeVAE, VectsetVAE
19
- from .surface_extractors import SurfaceExtractors, MCSurfaceExtractor, DMCSurfaceExtractor, Latent2MeshOutput
20
- from .volume_decoders import HierarchicalVolumeDecoding, FlashVDMVolumeDecoding, VanillaVolumeDecoder
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/hy3dshape/models/autoencoders/attention_blocks.py DELETED
@@ -1,716 +0,0 @@
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 os
27
- from typing import Optional, Union, List
28
-
29
- import torch
30
- import torch.nn as nn
31
- from einops import rearrange
32
- from torch import Tensor
33
-
34
- from .attention_processors import CrossAttentionProcessor
35
- from ...utils import logger
36
-
37
- scaled_dot_product_attention = nn.functional.scaled_dot_product_attention
38
-
39
- if os.environ.get('USE_SAGEATTN', '0') == '1':
40
- try:
41
- from sageattention import sageattn
42
- except ImportError:
43
- raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
44
- scaled_dot_product_attention = sageattn
45
-
46
-
47
- class FourierEmbedder(nn.Module):
48
- """The sin/cosine positional embedding. Given an input tensor `x` of shape [n_batch, ..., c_dim], it converts
49
- each feature dimension of `x[..., i]` into:
50
- [
51
- sin(x[..., i]),
52
- sin(f_1*x[..., i]),
53
- sin(f_2*x[..., i]),
54
- ...
55
- sin(f_N * x[..., i]),
56
- cos(x[..., i]),
57
- cos(f_1*x[..., i]),
58
- cos(f_2*x[..., i]),
59
- ...
60
- cos(f_N * x[..., i]),
61
- x[..., i] # only present if include_input is True.
62
- ], here f_i is the frequency.
63
-
64
- Denote the space is [0 / num_freqs, 1 / num_freqs, 2 / num_freqs, 3 / num_freqs, ..., (num_freqs - 1) / num_freqs].
65
- If logspace is True, then the frequency f_i is [2^(0 / num_freqs), ..., 2^(i / num_freqs), ...];
66
- Otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)].
67
-
68
- Args:
69
- num_freqs (int): the number of frequencies, default is 6;
70
- logspace (bool): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
71
- otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)];
72
- input_dim (int): the input dimension, default is 3;
73
- include_input (bool): include the input tensor or not, default is True.
74
-
75
- Attributes:
76
- frequencies (torch.Tensor): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
77
- otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1);
78
-
79
- out_dim (int): the embedding size, if include_input is True, it is input_dim * (num_freqs * 2 + 1),
80
- otherwise, it is input_dim * num_freqs * 2.
81
-
82
- """
83
-
84
- def __init__(self,
85
- num_freqs: int = 6,
86
- logspace: bool = True,
87
- input_dim: int = 3,
88
- include_input: bool = True,
89
- include_pi: bool = True) -> None:
90
-
91
- """The initialization"""
92
-
93
- super().__init__()
94
-
95
- if logspace:
96
- frequencies = 2.0 ** torch.arange(
97
- num_freqs,
98
- dtype=torch.float32
99
- )
100
- else:
101
- frequencies = torch.linspace(
102
- 1.0,
103
- 2.0 ** (num_freqs - 1),
104
- num_freqs,
105
- dtype=torch.float32
106
- )
107
-
108
- if include_pi:
109
- frequencies *= torch.pi
110
-
111
- self.register_buffer("frequencies", frequencies, persistent=False)
112
- self.include_input = include_input
113
- self.num_freqs = num_freqs
114
-
115
- self.out_dim = self.get_dims(input_dim)
116
-
117
- def get_dims(self, input_dim):
118
- temp = 1 if self.include_input or self.num_freqs == 0 else 0
119
- out_dim = input_dim * (self.num_freqs * 2 + temp)
120
-
121
- return out_dim
122
-
123
- def forward(self, x: torch.Tensor) -> torch.Tensor:
124
- """ Forward process.
125
-
126
- Args:
127
- x: tensor of shape [..., dim]
128
-
129
- Returns:
130
- embedding: an embedding of `x` of shape [..., dim * (num_freqs * 2 + temp)]
131
- where temp is 1 if include_input is True and 0 otherwise.
132
- """
133
-
134
- if self.num_freqs > 0:
135
- embed = (x[..., None].contiguous() * self.frequencies).view(*x.shape[:-1], -1)
136
- if self.include_input:
137
- return torch.cat((x, embed.sin(), embed.cos()), dim=-1)
138
- else:
139
- return torch.cat((embed.sin(), embed.cos()), dim=-1)
140
- else:
141
- return x
142
-
143
-
144
- class DropPath(nn.Module):
145
- """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
146
- """
147
-
148
- def __init__(self, drop_prob: float = 0., scale_by_keep: bool = True):
149
- super(DropPath, self).__init__()
150
- self.drop_prob = drop_prob
151
- self.scale_by_keep = scale_by_keep
152
-
153
- def forward(self, x):
154
- """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
155
-
156
- This is the same as the DropConnect impl I created for EfficientNet, etc networks, however,
157
- the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
158
- See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for
159
- changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use
160
- 'survival rate' as the argument.
161
-
162
- """
163
- if self.drop_prob == 0. or not self.training:
164
- return x
165
- keep_prob = 1 - self.drop_prob
166
- shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
167
- random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
168
- if keep_prob > 0.0 and self.scale_by_keep:
169
- random_tensor.div_(keep_prob)
170
- return x * random_tensor
171
-
172
- def extra_repr(self):
173
- return f'drop_prob={round(self.drop_prob, 3):0.3f}'
174
-
175
-
176
- class MLP(nn.Module):
177
- def __init__(
178
- self, *,
179
- width: int,
180
- expand_ratio: int = 4,
181
- output_width: int = None,
182
- drop_path_rate: float = 0.0
183
- ):
184
- super().__init__()
185
- self.width = width
186
- self.c_fc = nn.Linear(width, width * expand_ratio)
187
- self.c_proj = nn.Linear(width * expand_ratio, output_width if output_width is not None else width)
188
- self.gelu = nn.GELU()
189
- self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
190
-
191
- def forward(self, x):
192
- return self.drop_path(self.c_proj(self.gelu(self.c_fc(x))))
193
-
194
-
195
- class QKVMultiheadCrossAttention(nn.Module):
196
- def __init__(
197
- self,
198
- *,
199
- heads: int,
200
- n_data: Optional[int] = None,
201
- width=None,
202
- qk_norm=False,
203
- norm_layer=nn.LayerNorm
204
- ):
205
- super().__init__()
206
- self.heads = heads
207
- self.n_data = n_data
208
- self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
209
- self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
210
-
211
- self.attn_processor = CrossAttentionProcessor()
212
-
213
- def forward(self, q, kv):
214
- _, n_ctx, _ = q.shape
215
- bs, n_data, width = kv.shape
216
- attn_ch = width // self.heads // 2
217
- q = q.view(bs, n_ctx, self.heads, -1)
218
- kv = kv.view(bs, n_data, self.heads, -1)
219
- k, v = torch.split(kv, attn_ch, dim=-1)
220
-
221
- q = self.q_norm(q)
222
- k = self.k_norm(k)
223
- q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.heads), (q, k, v))
224
- out = self.attn_processor(self, q, k, v)
225
- out = out.transpose(1, 2).reshape(bs, n_ctx, -1)
226
- return out
227
-
228
-
229
- class MultiheadCrossAttention(nn.Module):
230
- def __init__(
231
- self,
232
- *,
233
- width: int,
234
- heads: int,
235
- qkv_bias: bool = True,
236
- n_data: Optional[int] = None,
237
- data_width: Optional[int] = None,
238
- norm_layer=nn.LayerNorm,
239
- qk_norm: bool = False,
240
- kv_cache: bool = False,
241
- ):
242
- super().__init__()
243
- self.n_data = n_data
244
- self.width = width
245
- self.heads = heads
246
- self.data_width = width if data_width is None else data_width
247
- self.c_q = nn.Linear(width, width, bias=qkv_bias)
248
- self.c_kv = nn.Linear(self.data_width, width * 2, bias=qkv_bias)
249
- self.c_proj = nn.Linear(width, width)
250
- self.attention = QKVMultiheadCrossAttention(
251
- heads=heads,
252
- n_data=n_data,
253
- width=width,
254
- norm_layer=norm_layer,
255
- qk_norm=qk_norm
256
- )
257
- self.kv_cache = kv_cache
258
- self.data = None
259
-
260
- def forward(self, x, data):
261
- x = self.c_q(x)
262
- if self.kv_cache:
263
- if self.data is None:
264
- self.data = self.c_kv(data)
265
- logger.info('Save kv cache,this should be called only once for one mesh')
266
- data = self.data
267
- else:
268
- data = self.c_kv(data)
269
- x = self.attention(x, data)
270
- x = self.c_proj(x)
271
- return x
272
-
273
-
274
- class ResidualCrossAttentionBlock(nn.Module):
275
- def __init__(
276
- self,
277
- *,
278
- n_data: Optional[int] = None,
279
- width: int,
280
- heads: int,
281
- mlp_expand_ratio: int = 4,
282
- data_width: Optional[int] = None,
283
- qkv_bias: bool = True,
284
- norm_layer=nn.LayerNorm,
285
- qk_norm: bool = False
286
- ):
287
- super().__init__()
288
-
289
- if data_width is None:
290
- data_width = width
291
-
292
- self.attn = MultiheadCrossAttention(
293
- n_data=n_data,
294
- width=width,
295
- heads=heads,
296
- data_width=data_width,
297
- qkv_bias=qkv_bias,
298
- norm_layer=norm_layer,
299
- qk_norm=qk_norm
300
- )
301
- self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
302
- self.ln_2 = norm_layer(data_width, elementwise_affine=True, eps=1e-6)
303
- self.ln_3 = norm_layer(width, elementwise_affine=True, eps=1e-6)
304
- self.mlp = MLP(width=width, expand_ratio=mlp_expand_ratio)
305
-
306
- def forward(self, x: torch.Tensor, data: torch.Tensor):
307
- x = x + self.attn(self.ln_1(x), self.ln_2(data))
308
- x = x + self.mlp(self.ln_3(x))
309
- return x
310
-
311
-
312
- class QKVMultiheadAttention(nn.Module):
313
- def __init__(
314
- self,
315
- *,
316
- heads: int,
317
- n_ctx: int,
318
- width=None,
319
- qk_norm=False,
320
- norm_layer=nn.LayerNorm
321
- ):
322
- super().__init__()
323
- self.heads = heads
324
- self.n_ctx = n_ctx
325
- self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
326
- self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
327
-
328
- def forward(self, qkv):
329
- bs, n_ctx, width = qkv.shape
330
- attn_ch = width // self.heads // 3
331
- qkv = qkv.view(bs, n_ctx, self.heads, -1)
332
- q, k, v = torch.split(qkv, attn_ch, dim=-1)
333
-
334
- q = self.q_norm(q)
335
- k = self.k_norm(k)
336
-
337
- q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.heads), (q, k, v))
338
- out = scaled_dot_product_attention(q, k, v).transpose(1, 2).reshape(bs, n_ctx, -1)
339
- return out
340
-
341
-
342
- class MultiheadAttention(nn.Module):
343
- def __init__(
344
- self,
345
- *,
346
- n_ctx: int,
347
- width: int,
348
- heads: int,
349
- qkv_bias: bool,
350
- norm_layer=nn.LayerNorm,
351
- qk_norm: bool = False,
352
- drop_path_rate: float = 0.0
353
- ):
354
- super().__init__()
355
- self.n_ctx = n_ctx
356
- self.width = width
357
- self.heads = heads
358
- self.c_qkv = nn.Linear(width, width * 3, bias=qkv_bias)
359
- self.c_proj = nn.Linear(width, width)
360
- self.attention = QKVMultiheadAttention(
361
- heads=heads,
362
- n_ctx=n_ctx,
363
- width=width,
364
- norm_layer=norm_layer,
365
- qk_norm=qk_norm
366
- )
367
- self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
368
-
369
- def forward(self, x):
370
- x = self.c_qkv(x)
371
- x = self.attention(x)
372
- x = self.drop_path(self.c_proj(x))
373
- return x
374
-
375
-
376
- class ResidualAttentionBlock(nn.Module):
377
- def __init__(
378
- self,
379
- *,
380
- n_ctx: int,
381
- width: int,
382
- heads: int,
383
- qkv_bias: bool = True,
384
- norm_layer=nn.LayerNorm,
385
- qk_norm: bool = False,
386
- drop_path_rate: float = 0.0,
387
- ):
388
- super().__init__()
389
- self.attn = MultiheadAttention(
390
- n_ctx=n_ctx,
391
- width=width,
392
- heads=heads,
393
- qkv_bias=qkv_bias,
394
- norm_layer=norm_layer,
395
- qk_norm=qk_norm,
396
- drop_path_rate=drop_path_rate
397
- )
398
- self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
399
- self.mlp = MLP(width=width, drop_path_rate=drop_path_rate)
400
- self.ln_2 = norm_layer(width, elementwise_affine=True, eps=1e-6)
401
-
402
- def forward(self, x: torch.Tensor):
403
- x = x + self.attn(self.ln_1(x))
404
- x = x + self.mlp(self.ln_2(x))
405
- return x
406
-
407
-
408
- class Transformer(nn.Module):
409
- def __init__(
410
- self,
411
- *,
412
- n_ctx: int,
413
- width: int,
414
- layers: int,
415
- heads: int,
416
- qkv_bias: bool = True,
417
- norm_layer=nn.LayerNorm,
418
- qk_norm: bool = False,
419
- drop_path_rate: float = 0.0
420
- ):
421
- super().__init__()
422
- self.n_ctx = n_ctx
423
- self.width = width
424
- self.layers = layers
425
- self.resblocks = nn.ModuleList(
426
- [
427
- ResidualAttentionBlock(
428
- n_ctx=n_ctx,
429
- width=width,
430
- heads=heads,
431
- qkv_bias=qkv_bias,
432
- norm_layer=norm_layer,
433
- qk_norm=qk_norm,
434
- drop_path_rate=drop_path_rate
435
- )
436
- for _ in range(layers)
437
- ]
438
- )
439
-
440
- def forward(self, x: torch.Tensor):
441
- for block in self.resblocks:
442
- x = block(x)
443
- return x
444
-
445
-
446
- class CrossAttentionDecoder(nn.Module):
447
-
448
- def __init__(
449
- self,
450
- *,
451
- num_latents: int,
452
- out_channels: int,
453
- fourier_embedder: FourierEmbedder,
454
- width: int,
455
- heads: int,
456
- mlp_expand_ratio: int = 4,
457
- downsample_ratio: int = 1,
458
- enable_ln_post: bool = True,
459
- qkv_bias: bool = True,
460
- qk_norm: bool = False,
461
- label_type: str = "binary"
462
- ):
463
- super().__init__()
464
-
465
- self.enable_ln_post = enable_ln_post
466
- self.fourier_embedder = fourier_embedder
467
- self.downsample_ratio = downsample_ratio
468
- self.query_proj = nn.Linear(self.fourier_embedder.out_dim, width)
469
- if self.downsample_ratio != 1:
470
- self.latents_proj = nn.Linear(width * downsample_ratio, width)
471
- if self.enable_ln_post == False:
472
- qk_norm = False
473
- self.cross_attn_decoder = ResidualCrossAttentionBlock(
474
- n_data=num_latents,
475
- width=width,
476
- mlp_expand_ratio=mlp_expand_ratio,
477
- heads=heads,
478
- qkv_bias=qkv_bias,
479
- qk_norm=qk_norm
480
- )
481
-
482
- if self.enable_ln_post:
483
- self.ln_post = nn.LayerNorm(width)
484
- self.output_proj = nn.Linear(width, out_channels)
485
- self.label_type = label_type
486
- self.count = 0
487
-
488
- def set_cross_attention_processor(self, processor):
489
- self.cross_attn_decoder.attn.attention.attn_processor = processor
490
-
491
- def set_default_cross_attention_processor(self):
492
- self.cross_attn_decoder.attn.attention.attn_processor = CrossAttentionProcessor
493
-
494
- def forward(self, queries=None, query_embeddings=None, latents=None):
495
- if query_embeddings is None:
496
- query_embeddings = self.query_proj(self.fourier_embedder(queries).to(latents.dtype))
497
- self.count += query_embeddings.shape[1]
498
- if self.downsample_ratio != 1:
499
- latents = self.latents_proj(latents)
500
- x = self.cross_attn_decoder(query_embeddings, latents)
501
- if self.enable_ln_post:
502
- x = self.ln_post(x)
503
- occ = self.output_proj(x)
504
- return occ
505
-
506
-
507
- def fps(
508
- src: torch.Tensor,
509
- batch: Optional[Tensor] = None,
510
- ratio: Optional[Union[Tensor, float]] = None,
511
- random_start: bool = True,
512
- batch_size: Optional[int] = None,
513
- ptr: Optional[Union[Tensor, List[int]]] = None,
514
- ):
515
- src = src.float()
516
- from torch_cluster import fps as fps_fn
517
- output = fps_fn(src, batch, ratio, random_start, batch_size, ptr)
518
- return output
519
-
520
-
521
- class PointCrossAttentionEncoder(nn.Module):
522
-
523
- def __init__(
524
- self, *,
525
- num_latents: int,
526
- downsample_ratio: float,
527
- pc_size: int,
528
- pc_sharpedge_size: int,
529
- fourier_embedder: FourierEmbedder,
530
- point_feats: int,
531
- width: int,
532
- heads: int,
533
- layers: int,
534
- normal_pe: bool = False,
535
- qkv_bias: bool = True,
536
- use_ln_post: bool = False,
537
- use_checkpoint: bool = False,
538
- qk_norm: bool = False
539
- ):
540
-
541
- super().__init__()
542
-
543
- self.use_checkpoint = use_checkpoint
544
- self.num_latents = num_latents
545
- self.downsample_ratio = downsample_ratio
546
- self.point_feats = point_feats
547
- self.normal_pe = normal_pe
548
-
549
- if pc_sharpedge_size == 0:
550
- print(
551
- f'PointCrossAttentionEncoder INFO: pc_sharpedge_size is not given, using pc_size as pc_sharpedge_size')
552
- else:
553
- print(
554
- f'PointCrossAttentionEncoder INFO: pc_sharpedge_size is given, using pc_size={pc_size}, pc_sharpedge_size={pc_sharpedge_size}')
555
-
556
- self.pc_size = pc_size
557
- self.pc_sharpedge_size = pc_sharpedge_size
558
-
559
- self.fourier_embedder = fourier_embedder
560
-
561
- self.input_proj = nn.Linear(self.fourier_embedder.out_dim + point_feats, width)
562
- self.cross_attn = ResidualCrossAttentionBlock(
563
- width=width,
564
- heads=heads,
565
- qkv_bias=qkv_bias,
566
- qk_norm=qk_norm
567
- )
568
-
569
- self.self_attn = None
570
- if layers > 0:
571
- self.self_attn = Transformer(
572
- n_ctx=num_latents,
573
- width=width,
574
- layers=layers,
575
- heads=heads,
576
- qkv_bias=qkv_bias,
577
- qk_norm=qk_norm
578
- )
579
-
580
- if use_ln_post:
581
- self.ln_post = nn.LayerNorm(width)
582
- else:
583
- self.ln_post = None
584
-
585
- def sample_points_and_latents(self, pc: torch.FloatTensor, feats: Optional[torch.FloatTensor] = None):
586
- B, N, D = pc.shape
587
- num_pts = self.num_latents * self.downsample_ratio
588
-
589
- # Compute number of latents
590
- num_latents = int(num_pts / self.downsample_ratio)
591
-
592
- # Compute the number of random and sharpedge latents
593
- num_random_query = self.pc_size / (self.pc_size + self.pc_sharpedge_size) * num_latents
594
- num_sharpedge_query = num_latents - num_random_query
595
-
596
- # Split random and sharpedge surface points
597
- random_pc, sharpedge_pc = torch.split(pc, [self.pc_size, self.pc_sharpedge_size], dim=1)
598
- assert random_pc.shape[1] <= self.pc_size, "Random surface points size must be less than or equal to pc_size"
599
- assert sharpedge_pc.shape[
600
- 1] <= self.pc_sharpedge_size, "Sharpedge surface points size must be less than or equal to pc_sharpedge_size"
601
-
602
- # Randomly select random surface points and random query points
603
- input_random_pc_size = int(num_random_query * self.downsample_ratio)
604
- random_query_ratio = num_random_query / input_random_pc_size
605
- idx_random_pc = torch.randperm(random_pc.shape[1], device=random_pc.device)[:input_random_pc_size]
606
- input_random_pc = random_pc[:, idx_random_pc, :]
607
- flatten_input_random_pc = input_random_pc.view(B * input_random_pc_size, D)
608
- N_down = int(flatten_input_random_pc.shape[0] / B)
609
- batch_down = torch.arange(B).to(pc.device)
610
- batch_down = torch.repeat_interleave(batch_down, N_down)
611
- idx_query_random = fps(flatten_input_random_pc, batch_down, ratio=random_query_ratio)
612
- query_random_pc = flatten_input_random_pc[idx_query_random].view(B, -1, D)
613
-
614
- # Randomly select sharpedge surface points and sharpedge query points
615
- input_sharpedge_pc_size = int(num_sharpedge_query * self.downsample_ratio)
616
- if input_sharpedge_pc_size == 0:
617
- input_sharpedge_pc = torch.zeros(B, 0, D, dtype=input_random_pc.dtype).to(pc.device)
618
- query_sharpedge_pc = torch.zeros(B, 0, D, dtype=query_random_pc.dtype).to(pc.device)
619
- else:
620
- sharpedge_query_ratio = num_sharpedge_query / input_sharpedge_pc_size
621
- idx_sharpedge_pc = torch.randperm(sharpedge_pc.shape[1], device=sharpedge_pc.device)[
622
- :input_sharpedge_pc_size]
623
- input_sharpedge_pc = sharpedge_pc[:, idx_sharpedge_pc, :]
624
- flatten_input_sharpedge_surface_points = input_sharpedge_pc.view(B * input_sharpedge_pc_size, D)
625
- N_down = int(flatten_input_sharpedge_surface_points.shape[0] / B)
626
- batch_down = torch.arange(B).to(pc.device)
627
- batch_down = torch.repeat_interleave(batch_down, N_down)
628
- idx_query_sharpedge = fps(flatten_input_sharpedge_surface_points, batch_down, ratio=sharpedge_query_ratio)
629
- query_sharpedge_pc = flatten_input_sharpedge_surface_points[idx_query_sharpedge].view(B, -1, D)
630
-
631
- # Concatenate random and sharpedge surface points and query points
632
- query_pc = torch.cat([query_random_pc, query_sharpedge_pc], dim=1)
633
- input_pc = torch.cat([input_random_pc, input_sharpedge_pc], dim=1)
634
-
635
- # PE
636
- query = self.fourier_embedder(query_pc)
637
- data = self.fourier_embedder(input_pc)
638
-
639
- # Concat normal if given
640
- if self.point_feats != 0:
641
-
642
- random_surface_feats, sharpedge_surface_feats = torch.split(feats, [self.pc_size, self.pc_sharpedge_size],
643
- dim=1)
644
- input_random_surface_feats = random_surface_feats[:, idx_random_pc, :]
645
- flatten_input_random_surface_feats = input_random_surface_feats.view(B * input_random_pc_size, -1)
646
- query_random_feats = flatten_input_random_surface_feats[idx_query_random].view(B, -1,
647
- flatten_input_random_surface_feats.shape[
648
- -1])
649
-
650
- if input_sharpedge_pc_size == 0:
651
- input_sharpedge_surface_feats = torch.zeros(B, 0, self.point_feats,
652
- dtype=input_random_surface_feats.dtype).to(pc.device)
653
- query_sharpedge_feats = torch.zeros(B, 0, self.point_feats, dtype=query_random_feats.dtype).to(
654
- pc.device)
655
- else:
656
- input_sharpedge_surface_feats = sharpedge_surface_feats[:, idx_sharpedge_pc, :]
657
- flatten_input_sharpedge_surface_feats = input_sharpedge_surface_feats.view(B * input_sharpedge_pc_size,
658
- -1)
659
- query_sharpedge_feats = flatten_input_sharpedge_surface_feats[idx_query_sharpedge].view(B, -1,
660
- flatten_input_sharpedge_surface_feats.shape[
661
- -1])
662
-
663
- query_feats = torch.cat([query_random_feats, query_sharpedge_feats], dim=1)
664
- input_feats = torch.cat([input_random_surface_feats, input_sharpedge_surface_feats], dim=1)
665
-
666
- if self.normal_pe:
667
- query_normal_pe = self.fourier_embedder(query_feats[..., :3])
668
- input_normal_pe = self.fourier_embedder(input_feats[..., :3])
669
- query_feats = torch.cat([query_normal_pe, query_feats[..., 3:]], dim=-1)
670
- input_feats = torch.cat([input_normal_pe, input_feats[..., 3:]], dim=-1)
671
-
672
- query = torch.cat([query, query_feats], dim=-1)
673
- data = torch.cat([data, input_feats], dim=-1)
674
-
675
- if input_sharpedge_pc_size == 0:
676
- query_sharpedge_pc = torch.zeros(B, 1, D).to(pc.device)
677
- input_sharpedge_pc = torch.zeros(B, 1, D).to(pc.device)
678
-
679
- # print(f'query_pc: {query_pc.shape}')
680
- # print(f'input_pc: {input_pc.shape}')
681
- # print(f'query_random_pc: {query_random_pc.shape}')
682
- # print(f'input_random_pc: {input_random_pc.shape}')
683
- # print(f'query_sharpedge_pc: {query_sharpedge_pc.shape}')
684
- # print(f'input_sharpedge_pc: {input_sharpedge_pc.shape}')
685
-
686
- return query.view(B, -1, query.shape[-1]), data.view(B, -1, data.shape[-1]), [query_pc, input_pc,
687
- query_random_pc, input_random_pc,
688
- query_sharpedge_pc,
689
- input_sharpedge_pc]
690
-
691
- def forward(self, pc, feats):
692
- """
693
-
694
- Args:
695
- pc (torch.FloatTensor): [B, N, 3]
696
- feats (torch.FloatTensor or None): [B, N, C]
697
-
698
- Returns:
699
-
700
- """
701
-
702
- query, data, pc_infos = self.sample_points_and_latents(pc, feats)
703
-
704
- query = self.input_proj(query)
705
- query = query
706
- data = self.input_proj(data)
707
- data = data
708
-
709
- latents = self.cross_attn(query, data)
710
- if self.self_attn is not None:
711
- latents = self.self_attn(latents)
712
-
713
- if self.ln_post is not None:
714
- latents = self.ln_post(latents)
715
-
716
- return latents, pc_infos
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hy3dshape/hy3dshape/models/autoencoders/attention_processors.py DELETED
@@ -1,96 +0,0 @@
1
- # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
- # except for the third-party components listed below.
3
- # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
- # in the repsective licenses of these third-party components.
5
- # Users must comply with all terms and conditions of original licenses of these third-party
6
- # components and must ensure that the usage of the third party components adheres to
7
- # all relevant laws and regulations.
8
-
9
- # For avoidance of doubts, Hunyuan 3D means the large language models and
10
- # their software and algorithms, including trained model weights, parameters (including
11
- # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
- # fine-tuning enabling code and other elements of the foregoing made publicly available
13
- # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
-
15
- import os
16
-
17
- import torch
18
- import torch.nn.functional as F
19
-
20
- scaled_dot_product_attention = F.scaled_dot_product_attention
21
- if os.environ.get('CA_USE_SAGEATTN', '0') == '1':
22
- try:
23
- from sageattention import sageattn
24
- except ImportError:
25
- raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
26
- scaled_dot_product_attention = sageattn
27
-
28
-
29
- class CrossAttentionProcessor:
30
- def __call__(self, attn, q, k, v):
31
- out = scaled_dot_product_attention(q, k, v)
32
- return out
33
-
34
-
35
- class FlashVDMCrossAttentionProcessor:
36
- def __init__(self, topk=None):
37
- self.topk = topk
38
-
39
- def __call__(self, attn, q, k, v):
40
- if k.shape[-2] == 3072:
41
- topk = 1024
42
- elif k.shape[-2] == 512:
43
- topk = 256
44
- else:
45
- topk = k.shape[-2] // 3
46
-
47
- if self.topk is True:
48
- q1 = q[:, :, ::100, :]
49
- sim = q1 @ k.transpose(-1, -2)
50
- sim = torch.mean(sim, -2)
51
- topk_ind = torch.topk(sim, dim=-1, k=topk).indices.squeeze(-2).unsqueeze(-1)
52
- topk_ind = topk_ind.expand(-1, -1, -1, v.shape[-1])
53
- v0 = torch.gather(v, dim=-2, index=topk_ind)
54
- k0 = torch.gather(k, dim=-2, index=topk_ind)
55
- out = scaled_dot_product_attention(q, k0, v0)
56
- elif self.topk is False:
57
- out = scaled_dot_product_attention(q, k, v)
58
- else:
59
- idx, counts = self.topk
60
- start = 0
61
- outs = []
62
- for grid_coord, count in zip(idx, counts):
63
- end = start + count
64
- q_chunk = q[:, :, start:end, :]
65
- k0, v0 = self.select_topkv(q_chunk, k, v, topk)
66
- out = scaled_dot_product_attention(q_chunk, k0, v0)
67
- outs.append(out)
68
- start += count
69
- out = torch.cat(outs, dim=-2)
70
- self.topk = False
71
- return out
72
-
73
- def select_topkv(self, q_chunk, k, v, topk):
74
- q1 = q_chunk[:, :, ::50, :]
75
- sim = q1 @ k.transpose(-1, -2)
76
- sim = torch.mean(sim, -2)
77
- topk_ind = torch.topk(sim, dim=-1, k=topk).indices.squeeze(-2).unsqueeze(-1)
78
- topk_ind = topk_ind.expand(-1, -1, -1, v.shape[-1])
79
- v0 = torch.gather(v, dim=-2, index=topk_ind)
80
- k0 = torch.gather(k, dim=-2, index=topk_ind)
81
- return k0, v0
82
-
83
-
84
- class FlashVDMTopMCrossAttentionProcessor(FlashVDMCrossAttentionProcessor):
85
- def select_topkv(self, q_chunk, k, v, topk):
86
- q1 = q_chunk[:, :, ::30, :]
87
- sim = q1 @ k.transpose(-1, -2)
88
- # sim = sim.to(torch.float32)
89
- sim = sim.softmax(-1)
90
- sim = torch.mean(sim, 1)
91
- activated_token = torch.where(sim > 1e-6)[2]
92
- index = torch.unique(activated_token, return_counts=True)[0].unsqueeze(0).unsqueeze(0).unsqueeze(-1)
93
- index = index.expand(-1, v.shape[1], -1, v.shape[-1])
94
- v0 = torch.gather(v, dim=-2, index=index)
95
- k0 = torch.gather(k, dim=-2, index=index)
96
- return k0, v0