mirror of
https://git.datalinker.icu/kijai/ComfyUI-Hunyuan3DWrapper.git
synced 2025-12-09 04:44:26 +08:00
update
This commit is contained in:
parent
f19cad5079
commit
d194d4ee33
@ -32,6 +32,7 @@ from einops import rearrange, repeat
|
|||||||
from skimage import measure
|
from skimage import measure
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from comfy.utils import ProgressBar
|
||||||
|
|
||||||
class FourierEmbedder(nn.Module):
|
class FourierEmbedder(nn.Module):
|
||||||
"""The sin/cosine positional embedding. Given an input tensor `x` of shape [n_batch, ..., c_dim], it converts
|
"""The sin/cosine positional embedding. Given an input tensor `x` of shape [n_batch, ..., c_dim], it converts
|
||||||
@ -579,6 +580,7 @@ class ShapeVAE(nn.Module):
|
|||||||
# 2. latents to 3d volume
|
# 2. latents to 3d volume
|
||||||
batch_logits = []
|
batch_logits = []
|
||||||
batch_size = latents.shape[0]
|
batch_size = latents.shape[0]
|
||||||
|
comfy_pbar = ProgressBar(num_chunks * xyz_samples.shape[0])
|
||||||
for start in tqdm(range(0, xyz_samples.shape[0], num_chunks),
|
for start in tqdm(range(0, xyz_samples.shape[0], num_chunks),
|
||||||
desc=f"MC Level {mc_level} Implicit Function:"):
|
desc=f"MC Level {mc_level} Implicit Function:"):
|
||||||
queries = xyz_samples[start: start + num_chunks, :].to(device)
|
queries = xyz_samples[start: start + num_chunks, :].to(device)
|
||||||
@ -591,6 +593,7 @@ class ShapeVAE(nn.Module):
|
|||||||
logits = torch.sigmoid(logits) * 2 - 1
|
logits = torch.sigmoid(logits) * 2 - 1
|
||||||
print(f'Training with soft labels, inference with sigmoid and marching cubes level 0.')
|
print(f'Training with soft labels, inference with sigmoid and marching cubes level 0.')
|
||||||
batch_logits.append(logits)
|
batch_logits.append(logits)
|
||||||
|
comfy_pbar.update(1)
|
||||||
grid_logits = torch.cat(batch_logits, dim=1)
|
grid_logits = torch.cat(batch_logits, dim=1)
|
||||||
grid_logits = grid_logits.view((batch_size, grid_size[0], grid_size[1], grid_size[2])).float()
|
grid_logits = grid_logits.view((batch_size, grid_size[0], grid_size[1], grid_size[2])).float()
|
||||||
|
|
||||||
|
|||||||
37
nodes.py
37
nodes.py
@ -1,15 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import torch
|
import torch
|
||||||
import gc
|
from PIL import Image
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from .hy3dgen.shapegen import Hunyuan3DDiTFlowMatchingPipeline, FaceReducer, FloaterRemover, DegenerateFaceRemover
|
from .hy3dgen.shapegen import Hunyuan3DDiTFlowMatchingPipeline, FaceReducer, FloaterRemover, DegenerateFaceRemover
|
||||||
from .hy3dgen.texgen import Hunyuan3DPaintPipeline
|
|
||||||
from .hy3dgen.texgen.utils.dehighlight_utils import Light_Shadow_Remover
|
|
||||||
|
|
||||||
from accelerate import init_empty_weights
|
|
||||||
from accelerate.utils import set_module_tensor_to_device
|
|
||||||
|
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
|
||||||
@ -46,7 +41,6 @@ class Hy3DModelLoader:
|
|||||||
config_path = os.path.join(script_directory, "configs", "dit_config.yaml")
|
config_path = os.path.join(script_directory, "configs", "dit_config.yaml")
|
||||||
model_path = folder_paths.get_full_path("diffusion_models", model)
|
model_path = folder_paths.get_full_path("diffusion_models", model)
|
||||||
pipe = Hunyuan3DDiTFlowMatchingPipeline.from_single_file(ckpt_path=model_path, config_path=config_path, use_safetensors=True, device=device)
|
pipe = Hunyuan3DDiTFlowMatchingPipeline.from_single_file(ckpt_path=model_path, config_path=config_path, use_safetensors=True, device=device)
|
||||||
|
|
||||||
return (pipe,)
|
return (pipe,)
|
||||||
|
|
||||||
class DownloadAndLoadHy3DDelightModel:
|
class DownloadAndLoadHy3DDelightModel:
|
||||||
@ -89,6 +83,7 @@ class DownloadAndLoadHy3DDelightModel:
|
|||||||
)
|
)
|
||||||
delight_pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(delight_pipe.scheduler.config)
|
delight_pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(delight_pipe.scheduler.config)
|
||||||
delight_pipe = delight_pipe.to(device, torch.float16)
|
delight_pipe = delight_pipe.to(device, torch.float16)
|
||||||
|
delight_pipe.enable_model_cpu_offload()
|
||||||
|
|
||||||
return (delight_pipe,)
|
return (delight_pipe,)
|
||||||
|
|
||||||
@ -120,7 +115,7 @@ class Hy3DDelightImage:
|
|||||||
|
|
||||||
image = image.permute(0, 3, 1, 2).to(device)
|
image = image.permute(0, 3, 1, 2).to(device)
|
||||||
|
|
||||||
delight_pipe = delight_pipe.to(device)
|
#delight_pipe = delight_pipe.to(device)
|
||||||
|
|
||||||
image = delight_pipe(
|
image = delight_pipe(
|
||||||
prompt="",
|
prompt="",
|
||||||
@ -134,7 +129,7 @@ class Hy3DDelightImage:
|
|||||||
output_type="pt"
|
output_type="pt"
|
||||||
).images[0]
|
).images[0]
|
||||||
|
|
||||||
delight_pipe = delight_pipe.to(offload_device)
|
#delight_pipe = delight_pipe.to(offload_device)
|
||||||
|
|
||||||
out_tensor = image.unsqueeze(0).permute(0, 2, 3, 1).cpu().float()
|
out_tensor = image.unsqueeze(0).permute(0, 2, 3, 1).cpu().float()
|
||||||
|
|
||||||
@ -180,7 +175,7 @@ class DownloadAndLoadHy3DPaintModel:
|
|||||||
torch_dtype=torch.float16)
|
torch_dtype=torch.float16)
|
||||||
|
|
||||||
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(pipeline.scheduler.config, timestep_spacing='trailing')
|
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(pipeline.scheduler.config, timestep_spacing='trailing')
|
||||||
|
pipeline.enable_model_cpu_offload()
|
||||||
return (pipeline,)
|
return (pipeline,)
|
||||||
|
|
||||||
class Hy3DRenderMultiView:
|
class Hy3DRenderMultiView:
|
||||||
@ -251,7 +246,7 @@ class Hy3DRenderMultiView:
|
|||||||
normal_image = [[control_images[i] for i in range(num_view)]]
|
normal_image = [[control_images[i] for i in range(num_view)]]
|
||||||
position_image = [[control_images[i + num_view] for i in range(num_view)]]
|
position_image = [[control_images[i + num_view] for i in range(num_view)]]
|
||||||
|
|
||||||
pipeline = pipeline.to(device)
|
#pipeline = pipeline.to(device)
|
||||||
|
|
||||||
multiview_images = pipeline(
|
multiview_images = pipeline(
|
||||||
input_image,
|
input_image,
|
||||||
@ -267,7 +262,7 @@ class Hy3DRenderMultiView:
|
|||||||
output_type="pt",
|
output_type="pt",
|
||||||
).images
|
).images
|
||||||
|
|
||||||
pipeline = pipeline.to(offload_device)
|
#pipeline = pipeline.to(offload_device)
|
||||||
|
|
||||||
out_tensors = multiview_images.permute(0, 2, 3, 1).cpu().float()
|
out_tensors = multiview_images.permute(0, 2, 3, 1).cpu().float()
|
||||||
|
|
||||||
@ -301,8 +296,8 @@ class Hy3DBakeFromMultiview:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("HY3DMESH",)
|
RETURN_TYPES = ("HY3DMESH", "IMAGE", )
|
||||||
RETURN_NAMES = ("mesh",)
|
RETURN_NAMES = ("mesh", "texture",)
|
||||||
FUNCTION = "process"
|
FUNCTION = "process"
|
||||||
CATEGORY = "Hunyuan3DWrapper"
|
CATEGORY = "Hunyuan3DWrapper"
|
||||||
|
|
||||||
@ -310,16 +305,17 @@ class Hy3DBakeFromMultiview:
|
|||||||
device = mm.get_torch_device()
|
device = mm.get_torch_device()
|
||||||
self.render = renderer
|
self.render = renderer
|
||||||
|
|
||||||
multiviews = images.permute(0, 3, 1, 2).to(device)
|
multiviews = images.permute(0, 3, 1, 2)
|
||||||
|
multiviews = multiviews.cpu().numpy()
|
||||||
device = mm.get_torch_device()
|
multiviews_pil = [Image.fromarray((image.transpose(1, 2, 0) * 255).astype(np.uint8)) for image in multiviews]
|
||||||
|
|
||||||
selected_camera_azims = [0, 90, 180, 270, 0, 180]
|
selected_camera_azims = [0, 90, 180, 270, 0, 180]
|
||||||
selected_camera_elevs = [0, 0, 0, 0, 90, -90]
|
selected_camera_elevs = [0, 0, 0, 0, 90, -90]
|
||||||
selected_view_weights = [1, 0.1, 0.5, 0.1, 0.05, 0.05]
|
selected_view_weights = [1, 0.1, 0.5, 0.1, 0.05, 0.05]
|
||||||
merge_method = 'fast'
|
merge_method = 'fast'
|
||||||
|
self.bake_exp = 4
|
||||||
|
|
||||||
texture, mask = self.bake_from_multiview(multiviews,
|
texture, mask = self.bake_from_multiview(multiviews_pil,
|
||||||
selected_camera_elevs, selected_camera_azims, selected_view_weights,
|
selected_camera_elevs, selected_camera_azims, selected_view_weights,
|
||||||
method=merge_method)
|
method=merge_method)
|
||||||
|
|
||||||
@ -327,11 +323,12 @@ class Hy3DBakeFromMultiview:
|
|||||||
|
|
||||||
texture_np = self.render.uv_inpaint(texture, mask_np)
|
texture_np = self.render.uv_inpaint(texture, mask_np)
|
||||||
texture = torch.tensor(texture_np / 255).float().to(texture.device)
|
texture = torch.tensor(texture_np / 255).float().to(texture.device)
|
||||||
|
print(texture.shape)
|
||||||
|
|
||||||
self.render.set_texture(texture)
|
self.render.set_texture(texture)
|
||||||
textured_mesh = self.render.save_mesh()
|
textured_mesh = self.render.save_mesh()
|
||||||
|
|
||||||
return (textured_mesh,)
|
return (textured_mesh, texture.unsqueeze(0).cpu().float(),)
|
||||||
|
|
||||||
def bake_from_multiview(self, views, camera_elevs,
|
def bake_from_multiview(self, views, camera_elevs,
|
||||||
camera_azims, view_weights, method='graphcut'):
|
camera_azims, view_weights, method='graphcut'):
|
||||||
@ -341,7 +338,7 @@ class Hy3DBakeFromMultiview:
|
|||||||
views, camera_elevs, camera_azims, view_weights):
|
views, camera_elevs, camera_azims, view_weights):
|
||||||
project_texture, project_cos_map, project_boundary_map = self.render.back_project(
|
project_texture, project_cos_map, project_boundary_map = self.render.back_project(
|
||||||
view, camera_elev, camera_azim)
|
view, camera_elev, camera_azim)
|
||||||
project_cos_map = weight * (project_cos_map ** self.config.bake_exp)
|
project_cos_map = weight * (project_cos_map ** self.bake_exp)
|
||||||
project_textures.append(project_texture)
|
project_textures.append(project_texture)
|
||||||
project_weighted_cos_maps.append(project_cos_map)
|
project_weighted_cos_maps.append(project_cos_map)
|
||||||
project_boundary_maps.append(project_boundary_map)
|
project_boundary_maps.append(project_boundary_map)
|
||||||
|
|||||||
24
readme.md
Normal file
24
readme.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ComfyUI wrapper for [Hunyuan3D-2](https://github.com/Tencent/Hunyuan3D-2)
|
||||||
|
|
||||||
|
# WORKINPROGRESS
|
||||||
|
|
||||||
|
Main model, original: https://huggingface.co/tencent/Hunyuan3D-2/blob/main/hunyuan3d-dit-v2-0/model.ckpt
|
||||||
|
|
||||||
|
Converted to .safetensors: https://huggingface.co/Kijai/Hunyuan3D-2_safetensors
|
||||||
|
|
||||||
|
to `ComfyUI/diffusion_models/`
|
||||||
|
|
||||||
|
Rest of the models are diffusers models, so they are wrapped and autodownloaded for now.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
# for texture (Linux only for now)
|
||||||
|
cd hy3dgen/texgen/custom_rasterizer
|
||||||
|
python3 setup.py install
|
||||||
|
cd hy3dgen/texgen/differentiable_renderer
|
||||||
|
bash compile_mesh_painter.sh
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
12
requirements.txt
Normal file
12
requirements.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
trimesh
|
||||||
|
diffusers
|
||||||
|
accelerate
|
||||||
|
huggingface_hub
|
||||||
|
einops
|
||||||
|
opencv-python
|
||||||
|
transformers
|
||||||
|
xatlas
|
||||||
|
pymeshlab
|
||||||
|
pygltflib
|
||||||
|
scikit-learn
|
||||||
|
scikit-image
|
||||||
Loading…
x
Reference in New Issue
Block a user