fix texturing multi view
|
Before Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
@@ -1,5 +0,0 @@
|
||||
一片绿色的树叶在白色背景上居中展现,清晰的纹理
|
||||
一只棕白相间的仓鼠,站在白色背景前。照片采用居中构图方式,卡通风格
|
||||
一盆绿色植物生长在红色花盆中,居中,写实
|
||||
a pot of green plants grows in a red flower pot.
|
||||
a lovely rabbit eating carrots
|
||||
@@ -1,81 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<!-- Import the component -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@google/model-viewer@3.1.1/dist/model-viewer.min.js" type="module"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const modelViewers = document.querySelectorAll('model-viewer');
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
modelViewers.forEach(modelViewer => {
|
||||
modelViewer.setAttribute(
|
||||
"environment-image",
|
||||
"/static/env_maps/gradient.jpg"
|
||||
);
|
||||
// if (!isSafari) {
|
||||
// modelViewer.setAttribute(
|
||||
// "environment-image",
|
||||
// "/static/env_maps/gradient.jpg"
|
||||
// );
|
||||
// } else {
|
||||
// modelViewer.addEventListener('load', (event) => {
|
||||
// const [material] = modelViewer.model.materials;
|
||||
// let color = [43, 44, 46, 255];
|
||||
// color = color.map(x => x / 255);
|
||||
// material.pbrMetallicRoughness.setMetallicFactor(0.1); // 完全金属
|
||||
// material.pbrMetallicRoughness.setRoughnessFactor(0.7); // 低粗糙度
|
||||
// material.pbrMetallicRoughness.setBaseColorFactor(color); // CornflowerBlue in RGB
|
||||
// });
|
||||
// }
|
||||
// modelViewer.addEventListener('load', (event) => {
|
||||
// const [material] = modelViewer.model.materials;
|
||||
// let color = [43, 44, 46, 255];
|
||||
// color = color.map(x => x / 255);
|
||||
// material.pbrMetallicRoughness.setMetallicFactor(0.1); // 完全金属
|
||||
// material.pbrMetallicRoughness.setRoughnessFactor(0.7); // 低粗糙度
|
||||
// material.pbrMetallicRoughness.setBaseColorFactor(color); // CornflowerBlue in RGB
|
||||
// });
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.centered-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 8px;
|
||||
border-color: #e5e7eb;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="centered-container">
|
||||
<div class="column is-mobile is-centered">
|
||||
<model-viewer id="modelviewer" style="height: #height#px; width: #width#px;"
|
||||
rotation-per-second="10deg"
|
||||
src="#src#" disable-tap
|
||||
environment-image="neutral"
|
||||
camera-target="0m 0m 0m"
|
||||
camera-orbit="0deg 90deg 8m"
|
||||
orientation="0deg 0deg 0deg"
|
||||
shadow-intensity=".9"
|
||||
ar auto-rotate
|
||||
camera-controls>
|
||||
</model-viewer>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,136 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<!-- Import the component -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@google/model-viewer@3.1.1/dist/model-viewer.min.js"
|
||||
type="module"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.centered-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modelviewer-panel-button {
|
||||
height: 30px;
|
||||
margin: 4px 4px;
|
||||
padding: 0px 14px;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.modelviewer-panel-button.checked {
|
||||
background: #6567C9;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modelviewer-panel-button:hover {
|
||||
background-color: #e2e6ea;
|
||||
}
|
||||
|
||||
.modelviewer-panel-button-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.centered-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="centered-container">
|
||||
<div class="centered-container">
|
||||
<div class="column is-mobile is-centered">
|
||||
<model-viewer id="modelviewer" style="height: #height#px; width: #width#px;"
|
||||
rotation-per-second="10deg"
|
||||
src="#src#" disable-tap
|
||||
environment-image="neutral"
|
||||
camera-target="0m 0m 0m"
|
||||
camera-orbit="0deg 90deg 12m"
|
||||
orientation="0deg 0deg 0deg"
|
||||
shadow-intensity=".9"
|
||||
ar auto-rotate
|
||||
camera-controls>
|
||||
</model-viewer>
|
||||
</div>
|
||||
|
||||
<div class="modelviewer-panel-button-container">
|
||||
<div id="appearance-button" class="modelviewer-panel-button small checked" onclick="showTexture()">
|
||||
Appearance
|
||||
</div>
|
||||
<div id="geometry-button" class="modelviewer-panel-button small" onclick="hideTexture()">Geometry</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const modelViewers = document.querySelectorAll('model-viewer');
|
||||
|
||||
modelViewers.forEach(modelViewer => {
|
||||
modelViewer.addEventListener('load', (event) => {
|
||||
const [material] = modelViewer.model.materials;
|
||||
material.pbrMetallicRoughness.setMetallicFactor(0.1);
|
||||
material.pbrMetallicRoughness.setRoughnessFactor(0.5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var window_state = {};
|
||||
|
||||
function hideTexture() {
|
||||
let appearanceButton = document.getElementById('appearance-button');
|
||||
let geometryButton = document.getElementById('geometry-button');
|
||||
appearanceButton.classList.remove('checked');
|
||||
geometryButton.classList.add('checked');
|
||||
let modelViewer = document.getElementById('modelviewer');
|
||||
if (modelViewer.model.materials[0].pbrMetallicRoughness.baseColorTexture.texture === null) return;
|
||||
window_state.textures = [];
|
||||
for (let i = 0; i < modelViewer.model.materials.length; i++) {
|
||||
window_state.textures.push(modelViewer.model.materials[i].pbrMetallicRoughness.baseColorTexture.texture);
|
||||
}
|
||||
window_state.exposure = modelViewer.exposure;
|
||||
modelViewer.environmentImage = '/static/env_maps/gradient.jpg';
|
||||
for (let i = 0; i < modelViewer.model.materials.length; i++) {
|
||||
modelViewer.model.materials[i].pbrMetallicRoughness.baseColorTexture.setTexture(null);
|
||||
}
|
||||
modelViewer.exposure = 4;
|
||||
}
|
||||
|
||||
function showTexture() {
|
||||
let appearanceButton = document.getElementById('appearance-button');
|
||||
let geometryButton = document.getElementById('geometry-button');
|
||||
appearanceButton.classList.add('checked');
|
||||
geometryButton.classList.remove('checked');
|
||||
let modelViewer = document.getElementById('modelviewer');
|
||||
if (modelViewer.model.materials[0].pbrMetallicRoughness.baseColorTexture.texture !== null) return;
|
||||
modelViewer.environmentImage = '/static/env_maps/white.jpg';
|
||||
for (let i = 0; i < modelViewer.model.materials.length; i++) {
|
||||
modelViewer.model.materials[i].pbrMetallicRoughness.baseColorTexture.setTexture(window_state.textures[i]);
|
||||
}
|
||||
modelViewer.exposure = window_state.exposure;
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 83 KiB |
@@ -1,12 +1,3 @@
|
||||
# Open Source Model Licensed under the Apache License Version 2.0
|
||||
# and Other Licenses of the Third-Party Components therein:
|
||||
# The below Model in this distribution may have been modified by THL A29 Limited
|
||||
# ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
|
||||
# Copyright (C) 2024 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.
|
||||
|
||||
# Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
|
||||
# except for the third-party components listed below.
|
||||
# Hunyuan 3D does not impose any additional limitations beyond what is outlined
|
||||
@@ -87,7 +78,7 @@ class ImageProcessorV2:
|
||||
interpolation=cv2.INTER_AREA)
|
||||
|
||||
bg = np.ones((result.shape[0], result.shape[1], 3), dtype=np.uint8) * 255
|
||||
# bg = np.zeros((result.shape[0], result.shape[1], 3), dtype=np.uint8) * 255
|
||||
|
||||
mask = result[..., 3:].astype(np.float32) / 255
|
||||
result = result[..., :3] * mask + bg * (1 - mask)
|
||||
|
||||
@@ -96,15 +87,13 @@ class ImageProcessorV2:
|
||||
mask = mask.clip(0, 255).astype(np.uint8)
|
||||
return result, mask
|
||||
|
||||
def __call__(self, image, border_ratio=0.15, to_tensor=True, return_mask=False, **kwargs):
|
||||
if self.border_ratio is not None:
|
||||
border_ratio = self.border_ratio
|
||||
print(f"Using border_ratio from init: {border_ratio}")
|
||||
def load_image(self, image, border_ratio=0.15, to_tensor=True):
|
||||
if isinstance(image, str):
|
||||
image = cv2.imread(image, cv2.IMREAD_UNCHANGED)
|
||||
image, mask = self.recenter(image, border_ratio=border_ratio)
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||
elif isinstance(image, Image.Image):
|
||||
image = image.convert("RGBA")
|
||||
image = np.asarray(image)
|
||||
image, mask = self.recenter(image, border_ratio=border_ratio)
|
||||
|
||||
@@ -115,13 +104,64 @@ class ImageProcessorV2:
|
||||
if to_tensor:
|
||||
image = array_to_tensor(image)
|
||||
mask = array_to_tensor(mask)
|
||||
if return_mask:
|
||||
return image, mask
|
||||
return image
|
||||
return image, mask
|
||||
|
||||
def __call__(self, image, border_ratio=0.15, to_tensor=True, **kwargs):
|
||||
if self.border_ratio is not None:
|
||||
border_ratio = self.border_ratio
|
||||
image, mask = self.load_image(image, border_ratio=border_ratio, to_tensor=to_tensor)
|
||||
outputs = {
|
||||
'image': image,
|
||||
'mask': mask
|
||||
}
|
||||
return outputs
|
||||
|
||||
|
||||
class MVImageProcessorV2(ImageProcessorV2):
|
||||
"""
|
||||
view order: front, front clockwise 90, back, front clockwise 270
|
||||
"""
|
||||
return_view_idx = True
|
||||
|
||||
def __init__(self, size=512, border_ratio=None):
|
||||
super().__init__(size, border_ratio)
|
||||
self.view2idx = {
|
||||
'front': 0,
|
||||
'left': 1,
|
||||
'back': 2,
|
||||
'right': 3
|
||||
}
|
||||
|
||||
def __call__(self, image_dict, border_ratio=0.15, to_tensor=True, **kwargs):
|
||||
if self.border_ratio is not None:
|
||||
border_ratio = self.border_ratio
|
||||
|
||||
images = []
|
||||
masks = []
|
||||
view_idxs = []
|
||||
for idx, (view_tag, image) in enumerate(image_dict.items()):
|
||||
view_idxs.append(self.view2idx[view_tag])
|
||||
image, mask = self.load_image(image, border_ratio=border_ratio, to_tensor=to_tensor)
|
||||
images.append(image)
|
||||
masks.append(mask)
|
||||
|
||||
zipped_lists = zip(view_idxs, images, masks)
|
||||
sorted_zipped_lists = sorted(zipped_lists)
|
||||
view_idxs, images, masks = zip(*sorted_zipped_lists)
|
||||
|
||||
image = torch.cat(images, 0).unsqueeze(0)
|
||||
mask = torch.cat(masks, 0).unsqueeze(0)
|
||||
outputs = {
|
||||
'image': image,
|
||||
'mask': mask,
|
||||
'view_idxs': view_idxs
|
||||
}
|
||||
return outputs
|
||||
|
||||
|
||||
IMAGE_PROCESSORS = {
|
||||
"v2": ImageProcessorV2,
|
||||
'mv_v2': MVImageProcessorV2,
|
||||
}
|
||||
|
||||
DEFAULT_IMAGEPROCESSOR = 'v2'
|
||||
|
||||
@@ -53,38 +53,19 @@ class Hunyuan3DTexGenConfig:
|
||||
class Hunyuan3DPaintPipeline:
|
||||
@classmethod
|
||||
def from_pretrained(cls, model_path, subfolder='hunyuan3d-paint-v2-0-turbo'):
|
||||
original_model_path = model_path
|
||||
if not os.path.exists(model_path):
|
||||
# try local path
|
||||
base_dir = os.environ.get('HY3DGEN_MODELS', '~/.cache/hy3dgen')
|
||||
model_path = os.path.expanduser(os.path.join(base_dir, model_path))
|
||||
delight_model_path = os.path.join(model_path, 'hunyuan3d-delight-v2-0')
|
||||
multiview_model_path = os.path.join(model_path, subfolder)
|
||||
|
||||
delight_model_path = os.path.join(model_path, 'hunyuan3d-delight-v2-0')
|
||||
multiview_model_path = os.path.join(model_path, subfolder)
|
||||
|
||||
if not os.path.exists(delight_model_path) or not os.path.exists(multiview_model_path):
|
||||
try:
|
||||
import huggingface_hub
|
||||
# download from huggingface
|
||||
model_path = huggingface_hub.snapshot_download(
|
||||
repo_id=original_model_path, allow_patterns=["hunyuan3d-delight-v2-0/*"]
|
||||
)
|
||||
model_path = huggingface_hub.snapshot_download(
|
||||
repo_id=original_model_path, allow_patterns=[f'{subfolder}/*']
|
||||
)
|
||||
delight_model_path = os.path.join(model_path, 'hunyuan3d-delight-v2-0')
|
||||
multiview_model_path = os.path.join(model_path, subfolder)
|
||||
return cls(Hunyuan3DTexGenConfig(delight_model_path, multiview_model_path, subfolder))
|
||||
except ImportError:
|
||||
logger.warning(
|
||||
"You need to install HuggingFace Hub to load models from the hub."
|
||||
)
|
||||
raise RuntimeError(f"Model path {model_path} not found")
|
||||
else:
|
||||
return cls(Hunyuan3DTexGenConfig(delight_model_path, multiview_model_path, subfolder))
|
||||
|
||||
raise FileNotFoundError(f"Model path {original_model_path} not found and we could not find it at huggingface")
|
||||
if os.path.exists(delight_model_path) and os.path.exists(multiview_model_path):
|
||||
return cls(Hunyuan3DTexGenConfig(
|
||||
delight_model_path,
|
||||
multiview_model_path,
|
||||
subfolder
|
||||
))
|
||||
|
||||
raise FileNotFoundError(
|
||||
f"Required models not found in local paths: {delight_model_path} or {multiview_model_path}"
|
||||
)
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.models = {}
|
||||
|
||||
@@ -4330,10 +4330,10 @@ class Load_Hunyuan3D_V2_ShapeGen_Pipeline:
|
||||
repo, subfolder, def_steps = self._MODES[generation_mode]
|
||||
use_safe = (weights_format == "safetensors")
|
||||
pipe = self._build_pipe(repo, subfolder, use_safe, flash_vdm)
|
||||
pipe.default_steps = def_steps
|
||||
pipe.num_inference_steps = def_steps
|
||||
return (pipe,)
|
||||
|
||||
class Load_Hunyuan3D_V2_TexGen_Pipeline:
|
||||
class Load_Hunyuan3D_V2_TexGen_Pipeline:
|
||||
CATEGORY = "Comfy3D/Algorithm"
|
||||
RETURN_TYPES = ("DIFFUSERS_PIPE",)
|
||||
RETURN_NAMES = ("texgen_pipe",)
|
||||
@@ -4348,32 +4348,35 @@ class Load_Hunyuan3D_V2_TexGen_Pipeline:
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {
|
||||
"generation_mode": (list(cls.MODEL2REPO.keys()),),
|
||||
"weights_format": (["safetensors","ckpt"],),
|
||||
}}
|
||||
|
||||
@staticmethod
|
||||
def _ensure_weights(repo_id: str, subfolder: str, use_safetensors: bool):
|
||||
base_dir = os.path.join(CKPT_DIFFUSERS_PATH, repo_id)
|
||||
os.makedirs(base_dir, exist_ok=True)
|
||||
target_dir = os.path.join(base_dir, subfolder)
|
||||
if not os.path.exists(target_dir):
|
||||
def _download_required_weights(self, repo_id, subfolder):
|
||||
ckpt_download_dir = os.path.join(CKPT_DIFFUSERS_PATH, repo_id)
|
||||
os.makedirs(ckpt_download_dir, exist_ok=True)
|
||||
|
||||
# Only download the "delight" and target submodel directories
|
||||
for folder in ["hunyuan3d-delight-v2-0", subfolder]:
|
||||
snapshot_download(
|
||||
repo_id=repo_id,
|
||||
local_dir=ckpt_download_dir,
|
||||
repo_type="model",
|
||||
local_dir=base_dir,
|
||||
resume_download=True,
|
||||
ignore_patterns = HF_DOWNLOAD_IGNORE
|
||||
force_download=False,
|
||||
ignore_patterns=HF_DOWNLOAD_IGNORE
|
||||
)
|
||||
|
||||
def load(self, generation_mode, weights_format):
|
||||
repo_id,subfolder=self.MODEL2REPO[generation_mode]
|
||||
Load_Hunyuan3D_V2_TexGen_Pipeline._ensure_weights(repo_id,subfolder,
|
||||
weights_format=="safetensors")
|
||||
pipe=Hunyuan3DPaintPipeline.from_pretrained(
|
||||
model_path=repo_id,
|
||||
subfolder=subfolder,
|
||||
def load(self, generation_mode):
|
||||
repo_id, subfolder = self.MODEL2REPO[generation_mode]
|
||||
|
||||
self._download_required_weights(repo_id, subfolder)
|
||||
|
||||
local_repo_dir = os.path.join(CKPT_DIFFUSERS_PATH, repo_id)
|
||||
|
||||
pipe = Hunyuan3DPaintPipeline.from_pretrained(
|
||||
model_path=local_repo_dir,
|
||||
subfolder=subfolder
|
||||
)
|
||||
return (pipe.to("cuda",torch.float16),)
|
||||
|
||||
return (pipe.to("cuda", torch.float16),)
|
||||
|
||||
class Hunyuan3D_V2_Paint_Model_Turbo_MV:
|
||||
"""
|
||||
@@ -4443,7 +4446,8 @@ class Multi_Background_Remover:
|
||||
self,
|
||||
image_front,
|
||||
image_back=None,
|
||||
image_left=None
|
||||
image_left=None,
|
||||
image_right=None
|
||||
):
|
||||
rmbg = BackgroundRemover()
|
||||
|
||||
@@ -4451,7 +4455,8 @@ class Multi_Background_Remover:
|
||||
k: v for k, v in {
|
||||
"front": image_front,
|
||||
"back": image_back,
|
||||
"left": image_left
|
||||
"left": image_left,
|
||||
"right": image_right
|
||||
}.items() if v is not None
|
||||
}
|
||||
|
||||
@@ -4504,7 +4509,7 @@ class Hunyuan3D_V2_ShapeGen_MV:
|
||||
if len(images) == 1:
|
||||
image = images[0]
|
||||
else:
|
||||
directions = ["front", "back", "left"]
|
||||
directions = ["front", "back", "left", "right"]
|
||||
image = {k: v for k, v in zip(directions, images)}
|
||||
|
||||
steps = shapegen_pipe.default_steps if num_inference_steps == 0 else num_inference_steps
|
||||
|
||||