Merge model loading nodes, change model paths, add automatic downloads, and add display memory.
This commit is contained in:
Binary file not shown.
Binary file not shown.
+230
-177
@@ -3,144 +3,228 @@ import torch
|
||||
import torch.nn.functional as F
|
||||
import numpy as np
|
||||
from torchvision import transforms
|
||||
|
||||
import folder_paths
|
||||
import comfy
|
||||
from comfy.utils import load_torch_file
|
||||
|
||||
# 延迟导入核心模型,避免在依赖未安装时阻断节点注册
|
||||
def download_with_progress(url, filepath, description="File"):
|
||||
try:
|
||||
import requests
|
||||
from tqdm import tqdm
|
||||
|
||||
print(f"[SDMatte] Starting download: {description}")
|
||||
print(f"[SDMatte] URL: {url}")
|
||||
print(f"[SDMatte] Save path: {filepath}")
|
||||
|
||||
response = requests.get(url, stream=True, timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
total_size = int(response.headers.get('content-length', 0))
|
||||
|
||||
with open(filepath, 'wb') as f:
|
||||
with tqdm(
|
||||
total=total_size,
|
||||
unit='iB',
|
||||
unit_scale=True,
|
||||
unit_divisor=1024,
|
||||
desc=f"Downloading {description}"
|
||||
) as pbar:
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
size = f.write(chunk)
|
||||
pbar.update(size)
|
||||
|
||||
print(f"[SDMatte] Download completed: {filepath}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"[SDMatte] Download failed: {e}")
|
||||
return False
|
||||
def verify_file(filepath):
|
||||
if not os.path.exists(filepath):
|
||||
return False
|
||||
|
||||
actual_size = os.path.getsize(filepath)
|
||||
if actual_size > 0:
|
||||
print(f"[SDMatte] File verification successful, size: {actual_size} bytes")
|
||||
return True
|
||||
|
||||
return False
|
||||
SDMatteCore = None
|
||||
|
||||
|
||||
class SDMatteModelLoader:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"ckpt_name": (folder_paths.get_filename_list("checkpoints"), ),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SDMATTE_MODEL",)
|
||||
FUNCTION = "load_model"
|
||||
CATEGORY = "Matting/SDMatte"
|
||||
|
||||
def load_model(self, ckpt_name):
|
||||
device = comfy.model_management.get_torch_device()
|
||||
|
||||
global SDMatteCore
|
||||
if SDMatteCore is None:
|
||||
from .src.modeling.SDMatte.meta_arch import SDMatte as SDMatteCore # type: ignore
|
||||
|
||||
# 使用本地固定路径作为 diffusers 基底
|
||||
base_dir = os.path.dirname(__file__)
|
||||
pretrained_repo = os.path.join(base_dir, "src", "SDMatte")
|
||||
required_subdirs = ["text_encoder", "vae", "unet", "scheduler", "tokenizer"]
|
||||
missing = [d for d in required_subdirs if not os.path.isdir(os.path.join(pretrained_repo, d))]
|
||||
if missing:
|
||||
raise FileNotFoundError(
|
||||
f"本地基底缺少目录: {missing}. 期望路径: {pretrained_repo},需包含 {required_subdirs} 子目录。"
|
||||
)
|
||||
|
||||
print(f"[SDMatte] Using base repo: {pretrained_repo}")
|
||||
# 实例化模型(使用默认优化配置)
|
||||
model = SDMatteCore(
|
||||
pretrained_model_name_or_path=pretrained_repo,
|
||||
load_weight=False,
|
||||
use_aux_input=True,
|
||||
aux_input="trimap",
|
||||
use_encoder_hidden_states=True,
|
||||
use_attention_mask=True,
|
||||
add_noise=False,
|
||||
SDMATTE_MODEL_LIST = [
|
||||
"SDMatte.pth",
|
||||
"SDMatte_plus.pth"
|
||||
]
|
||||
SDMATTE_MODEL_MAPPING = {
|
||||
"SDMatte.pth": {
|
||||
"url": "https://huggingface.co/LongfeiHuang/SDMatte/resolve/main/SDMatte.pth",
|
||||
"filename": "SDMatte.pth",
|
||||
"description": "SDMatte Standard Version (12.2 GB)"
|
||||
},
|
||||
"SDMatte_plus.pth": {
|
||||
"url": "https://huggingface.co/LongfeiHuang/SDMatte/resolve/main/SDMatte_plus.pth",
|
||||
"filename": "SDMatte_plus.pth",
|
||||
"description": "SDMatte Plus Version (12.1 GB)"
|
||||
}
|
||||
}
|
||||
_sdmatte_model_cache = {
|
||||
'model': None,
|
||||
'ckpt_name': None,
|
||||
}
|
||||
def clear_memory():
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.empty_cache()
|
||||
torch.cuda.ipc_collect()
|
||||
def get_model_path(ckpt_name):
|
||||
sdmatte_dir = os.path.join(folder_paths.models_dir, "SDMatte")
|
||||
os.makedirs(sdmatte_dir, exist_ok=True)
|
||||
|
||||
local_model_path = os.path.join(sdmatte_dir, ckpt_name)
|
||||
|
||||
if os.path.exists(local_model_path):
|
||||
print(f"[SDMatte] Using local model from models/SDMatte/: {local_model_path}")
|
||||
return local_model_path
|
||||
|
||||
if ckpt_name not in SDMATTE_MODEL_MAPPING:
|
||||
print(f"[SDMatte] User custom model path: {local_model_path}")
|
||||
return local_model_path
|
||||
|
||||
model_info = SDMATTE_MODEL_MAPPING[ckpt_name]
|
||||
print(f"[SDMatte] Local model {ckpt_name} not found, preparing to download from HuggingFace...")
|
||||
print(f"[SDMatte] Model description: {model_info['description']}")
|
||||
|
||||
try:
|
||||
success = download_with_progress(
|
||||
url=model_info["url"],
|
||||
filepath=local_model_path,
|
||||
description=model_info["description"]
|
||||
)
|
||||
|
||||
# 加载合并后的权重
|
||||
ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
|
||||
print(f"[SDMatte] Loading merged weights: {ckpt_path}")
|
||||
# 更健壮的加载与 state_dict 提取
|
||||
state_root = None
|
||||
|
||||
if success and verify_file(local_model_path):
|
||||
print(f"[SDMatte] Model download and verification successful: {local_model_path}")
|
||||
return local_model_path
|
||||
else:
|
||||
raise FileNotFoundError(f"Model download or verification failed: {local_model_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[SDMatte] Auto download failed: {e}")
|
||||
if os.path.exists(local_model_path):
|
||||
try:
|
||||
os.remove(local_model_path)
|
||||
print(f"[SDMatte] Cleaned up incomplete download file: {local_model_path}")
|
||||
except:
|
||||
pass
|
||||
raise FileNotFoundError(f"Unable to get model {ckpt_name}: {e}")
|
||||
def load_sdmatte_model(ckpt_name, force_cpu=False):
|
||||
global _sdmatte_model_cache, SDMatteCore
|
||||
|
||||
if (_sdmatte_model_cache['model'] is not None and
|
||||
_sdmatte_model_cache['ckpt_name'] == ckpt_name):
|
||||
print(f"[SDMatte] Reusing cached model: {ckpt_name}")
|
||||
return _sdmatte_model_cache['model']
|
||||
|
||||
if _sdmatte_model_cache['model'] is not None:
|
||||
print("[SDMatte] Cleaning up old model cache")
|
||||
del _sdmatte_model_cache['model']
|
||||
clear_memory()
|
||||
|
||||
device = comfy.model_management.get_torch_device()
|
||||
if force_cpu:
|
||||
device = torch.device('cpu')
|
||||
if SDMatteCore is None:
|
||||
from .src.modeling.SDMatte.meta_arch import SDMatte as SDMatteCore # type: ignore
|
||||
base_dir = os.path.dirname(__file__)
|
||||
pretrained_repo = os.path.join(base_dir, "src", "SDMatte")
|
||||
required_subdirs = ["text_encoder", "vae", "unet", "scheduler", "tokenizer"]
|
||||
missing = [d for d in required_subdirs if not os.path.isdir(os.path.join(pretrained_repo, d))]
|
||||
if missing:
|
||||
raise FileNotFoundError(
|
||||
f"Local base missing directories: {missing}. Expected path: {pretrained_repo}, must contain {required_subdirs} subdirectories."
|
||||
)
|
||||
print(f"[SDMatte] Using base repo: {pretrained_repo}")
|
||||
model = SDMatteCore(
|
||||
pretrained_model_name_or_path=pretrained_repo,
|
||||
load_weight=False,
|
||||
use_aux_input=True,
|
||||
aux_input="trimap",
|
||||
use_encoder_hidden_states=True,
|
||||
use_attention_mask=True,
|
||||
add_noise=False,
|
||||
)
|
||||
ckpt_path = get_model_path(ckpt_name)
|
||||
print(f"[SDMatte] Loading merged weights: {ckpt_path}")
|
||||
state_root = None
|
||||
try:
|
||||
from torch.serialization import add_safe_globals
|
||||
try:
|
||||
from torch.serialization import add_safe_globals
|
||||
try:
|
||||
from omegaconf.listconfig import ListConfig
|
||||
add_safe_globals([ListConfig])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
from omegaconf.base import ContainerMetadata
|
||||
add_safe_globals([ContainerMetadata])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
state_root = torch.load(ckpt_path, map_location='cpu', weights_only=True)
|
||||
print("[SDMatte] torch.load(weights_only=True) ok")
|
||||
except Exception as e:
|
||||
print(f"[SDMatte] weights_only=True failed: {e}; retry weights_only=False")
|
||||
try:
|
||||
state_root = torch.load(ckpt_path, map_location='cpu', weights_only=False)
|
||||
print("[SDMatte] torch.load(weights_only=False) ok")
|
||||
except TypeError:
|
||||
state_root = torch.load(ckpt_path, map_location='cpu')
|
||||
from omegaconf.listconfig import ListConfig
|
||||
add_safe_globals([ListConfig])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
from omegaconf.base import ContainerMetadata
|
||||
add_safe_globals([ContainerMetadata])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
state_root = torch.load(ckpt_path, map_location='cpu', weights_only=True)
|
||||
print("[SDMatte] torch.load(weights_only=True) ok")
|
||||
except Exception as e:
|
||||
print(f"[SDMatte] torch.load failed: {e}; fallback comfy.load_torch_file")
|
||||
state_root = load_torch_file(ckpt_path)
|
||||
|
||||
# 尝试从常见键中提取真正权重
|
||||
candidate_keys = [
|
||||
'state_dict','model_state_dict','params','weights',
|
||||
'ema','model_ema','ema_state_dict','net','module','model','unet'
|
||||
]
|
||||
state_dict = None
|
||||
if isinstance(state_root, dict):
|
||||
for k in candidate_keys:
|
||||
inner = state_root.get(k)
|
||||
if isinstance(inner, dict) and len(inner) > 50:
|
||||
state_dict = inner
|
||||
print(f"[SDMatte] extracted inner dict via key: {k}")
|
||||
break
|
||||
if state_dict is None and isinstance(state_root, dict):
|
||||
# 如果顶层就是大量权重键
|
||||
if len(state_root) > 50 and any('.weight' in x or '.bias' in x for x in state_root.keys()):
|
||||
state_dict = state_root
|
||||
if state_dict is None:
|
||||
top_keys = list(state_root.keys()) if isinstance(state_root, dict) else []
|
||||
raise RuntimeError(
|
||||
f"无法从权重中提取 state_dict。顶层键: {top_keys[:10]} (共{len(top_keys)})。请使用官方脚本导出仅模型权重的 sdmatte.pth。"
|
||||
)
|
||||
|
||||
print(f"[SDMatte] Weights loaded, keys: {len(list(state_dict.keys()))}")
|
||||
missing, unexpected = model.load_state_dict(state_dict, strict=False)
|
||||
print(f"[SDMatte] load_state_dict done. missing={len(missing)}, unexpected={len(unexpected)}")
|
||||
|
||||
model.eval()
|
||||
model.to(device)
|
||||
|
||||
return (model,)
|
||||
|
||||
|
||||
print(f"[SDMatte] weights_only=True failed: {e}; retry weights_only=False")
|
||||
try:
|
||||
state_root = torch.load(ckpt_path, map_location='cpu', weights_only=False)
|
||||
print("[SDMatte] torch.load(weights_only=False) ok")
|
||||
except TypeError:
|
||||
state_root = torch.load(ckpt_path, map_location='cpu')
|
||||
except Exception as e:
|
||||
print(f"[SDMatte] torch.load failed: {e}; fallback comfy.load_torch_file")
|
||||
state_root = load_torch_file(ckpt_path)
|
||||
candidate_keys = [
|
||||
'state_dict','model_state_dict','params','weights',
|
||||
'ema','model_ema','ema_state_dict','net','module','model','unet'
|
||||
]
|
||||
state_dict = None
|
||||
if isinstance(state_root, dict):
|
||||
for k in candidate_keys:
|
||||
inner = state_root.get(k)
|
||||
if isinstance(inner, dict) and len(inner) > 50:
|
||||
state_dict = inner
|
||||
print(f"[SDMatte] extracted inner dict via key: {k}")
|
||||
break
|
||||
if state_dict is None and isinstance(state_root, dict):
|
||||
if len(state_root) > 50 and any('.weight' in x or '.bias' in x for x in state_root.keys()):
|
||||
state_dict = state_root
|
||||
if state_dict is None:
|
||||
top_keys = list(state_root.keys()) if isinstance(state_root, dict) else []
|
||||
raise RuntimeError(
|
||||
f"Unable to extract state_dict from weights. Top-level keys: {top_keys[:10]} (total {len(top_keys)}). Please use official script to export model-only weights to sdmatte.pth."
|
||||
)
|
||||
print(f"[SDMatte] Weights loaded, keys: {len(list(state_dict.keys()))}")
|
||||
missing, unexpected = model.load_state_dict(state_dict, strict=False)
|
||||
print(f"[SDMatte] load_state_dict done. missing={len(missing)}, unexpected={len(unexpected)}")
|
||||
model.eval()
|
||||
model.to(device)
|
||||
|
||||
_sdmatte_model_cache['model'] = model
|
||||
_sdmatte_model_cache['ckpt_name'] = ckpt_name
|
||||
|
||||
return model
|
||||
def _resize_norm_image_bchw(image_bchw: torch.Tensor, size_hw=(1024, 1024)) -> torch.Tensor:
|
||||
# image_bchw in [0,1], float32
|
||||
resize = transforms.Resize(size_hw, antialias=True)
|
||||
norm = transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
|
||||
x = resize(image_bchw)
|
||||
x = norm(x)
|
||||
return x
|
||||
|
||||
|
||||
def _resize_mask_b1hw(mask_b1hw: torch.Tensor, size_hw=(1024, 1024)) -> torch.Tensor:
|
||||
resize = transforms.Resize(size_hw)
|
||||
return resize(mask_b1hw)
|
||||
|
||||
|
||||
class SDMatteApply:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"sdmatte_model": ("SDMATTE_MODEL", {"tooltip": "已加载的SDMatte模型,来自SDMatte Model Loader节点"}),
|
||||
"ckpt_name": (SDMATTE_MODEL_LIST, {
|
||||
"default": "SDMatte.pth",
|
||||
"tooltip": "选择SDMatte模型:SDMatte.pth(标准版) 或 SDMatte_plus.pth(增强版)。模型将存储在models/SDMatte/目录,如果本地不存在将自动下载"
|
||||
}),
|
||||
"image": ("IMAGE", {"tooltip": "需要进行抠图的输入图像"}),
|
||||
"trimap": ("MASK", {"tooltip": "三值图掩码:白色=前景,黑色=背景,灰色=未知区域"}),
|
||||
"inference_size": ([512, 640, 768, 896, 1024], {
|
||||
@@ -152,7 +236,7 @@ class SDMatteApply:
|
||||
"tooltip": "输入图像是否包含透明通道。如果原图有透明背景请启用"
|
||||
}),
|
||||
"output_mode": (["alpha_only", "matted_rgba", "matted_rgb"], {
|
||||
"default": "alpha_only",
|
||||
"default": "matted_rgba",
|
||||
"tooltip": "输出模式:alpha_only=只输出遮罩;matted_rgba=透明背景抠图;matted_rgb=黑色背景抠图(推荐,避免干扰)"
|
||||
}),
|
||||
"mask_refine": ("BOOLEAN", {
|
||||
@@ -163,6 +247,10 @@ class SDMatteApply:
|
||||
"default": 0.8, "min": 0.1, "max": 1.0, "step": 0.1,
|
||||
"tooltip": "trimap约束强度(0.1-1.0)。越高约束越严格,0.8=平衡,0.9=严格过滤,0.6=宽松保留"
|
||||
}),
|
||||
"unload_model_after": ("BOOLEAN", {
|
||||
"default": False,
|
||||
"tooltip": "推理完成后卸载模型释放显存。适用于显存紧张的情况,但会影响后续推理速度"
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"force_cpu": ("BOOLEAN", {
|
||||
@@ -171,141 +259,106 @@ class SDMatteApply:
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK", "IMAGE")
|
||||
RETURN_NAMES = ("alpha_mask", "matted_image")
|
||||
FUNCTION = "apply_matte"
|
||||
CATEGORY = "Matting/SDMatte"
|
||||
|
||||
def apply_matte(self, sdmatte_model, image, trimap, inference_size, is_transparent, output_mode, mask_refine, trimap_constraint, force_cpu=False):
|
||||
def apply_matte(self, ckpt_name, image, trimap, inference_size, is_transparent, output_mode, mask_refine, trimap_constraint, unload_model_after=False, force_cpu=False):
|
||||
sdmatte_model = load_sdmatte_model(ckpt_name, force_cpu)
|
||||
|
||||
device = comfy.model_management.get_torch_device()
|
||||
if force_cpu:
|
||||
device = torch.device('cpu')
|
||||
sdmatte_model.to(device)
|
||||
|
||||
# 自动显存优化(内置默认设置)
|
||||
if device.type == 'cuda':
|
||||
try:
|
||||
torch.cuda.empty_cache() # 预清理显存
|
||||
torch.cuda.empty_cache()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 启用注意力切片以节省显存
|
||||
try:
|
||||
unet = getattr(sdmatte_model, 'unet', None)
|
||||
if unet is not None and hasattr(unet, 'set_attn_processor'):
|
||||
from diffusers.models.attention_processor import SlicedAttnProcessor
|
||||
# 使用适中的切片大小平衡性能和显存
|
||||
unet.set_attn_processor(SlicedAttnProcessor(slice_size=1))
|
||||
except Exception as e:
|
||||
print(f'[SDMatte] 注意力优化跳过: {e}')
|
||||
|
||||
print(f'[SDMatte] Attention optimization skipped: {e}')
|
||||
B, H, W, C = image.shape
|
||||
orig_h, orig_w = H, W
|
||||
|
||||
# IMAGE: (B,H,W,C)->(B,C,H,W) in [0,1]
|
||||
img_bchw = image.permute(0, 3, 1, 2).contiguous().to(device)
|
||||
img_in = _resize_norm_image_bchw(img_bchw, (int(inference_size), int(inference_size)))
|
||||
|
||||
# 构造必要的数据字典字段
|
||||
is_trans = torch.tensor([1 if is_transparent else 0] * B, device=device)
|
||||
|
||||
data = {"image": img_in, "is_trans": is_trans, "caption": [""] * B}
|
||||
|
||||
def to_b1hw(x):
|
||||
return _resize_mask_b1hw(x.unsqueeze(1).contiguous().to(device), (int(inference_size), int(inference_size)))
|
||||
|
||||
# 处理 trimap 输入
|
||||
tri = to_b1hw(trimap) * 2 - 1
|
||||
data["trimap"] = tri
|
||||
data["trimap_coords"] = torch.tensor([[0,0,1,1]]*B, dtype=tri.dtype, device=device)
|
||||
|
||||
with torch.no_grad():
|
||||
if device.type == 'cuda':
|
||||
with torch.autocast(device_type='cuda', dtype=torch.float16):
|
||||
pred_alpha = sdmatte_model(data)
|
||||
else:
|
||||
pred_alpha = sdmatte_model(data)
|
||||
|
||||
# 还原到原尺寸
|
||||
out = transforms.Resize((orig_h, orig_w))(pred_alpha)
|
||||
out = out.squeeze(1).clamp(0, 1).detach().cpu()
|
||||
|
||||
# 遮罩优化:使用trimap约束来过滤不需要的区域
|
||||
if mask_refine:
|
||||
trimap_cpu = trimap.cpu() # (B, H, W)
|
||||
trimap_cpu = trimap.cpu()
|
||||
|
||||
# 创建严格的前景约束
|
||||
# 只在trimap明确标记为前景(白色区域)的地方保留高alpha值
|
||||
# 在未知区域(灰色)允许模型输出,但在背景区域(黑色)强制为0
|
||||
foreground_regions = trimap_cpu > trimap_constraint # 前景区域
|
||||
background_regions = trimap_cpu < (1.0 - trimap_constraint) # 背景区域
|
||||
unknown_regions = ~(foreground_regions | background_regions) # 未知区域
|
||||
foreground_regions = trimap_cpu > trimap_constraint
|
||||
background_regions = trimap_cpu < (1.0 - trimap_constraint)
|
||||
unknown_regions = ~(foreground_regions | background_regions)
|
||||
|
||||
# 优化alpha遮罩
|
||||
refined_alpha = out.clone()
|
||||
|
||||
# 背景区域强制为0
|
||||
refined_alpha[background_regions] = 0.0
|
||||
|
||||
# 前景区域保持原值或增强
|
||||
refined_alpha[foreground_regions] = torch.clamp(refined_alpha[foreground_regions] * 1.2, 0, 1)
|
||||
|
||||
# 未知区域保持模型输出,但应用阈值过滤
|
||||
alpha_threshold = 0.3 # 过滤掉低置信度的区域
|
||||
alpha_threshold = 0.3
|
||||
low_confidence = (refined_alpha < alpha_threshold) & unknown_regions
|
||||
refined_alpha[low_confidence] = 0.0
|
||||
|
||||
out = refined_alpha
|
||||
|
||||
# 根据输出模式创建不同的抠图结果
|
||||
# out: (B, H, W), image: (B, H, W, C)
|
||||
alpha_expanded = out.unsqueeze(-1) # (B, H, W, 1)
|
||||
alpha_expanded = out.unsqueeze(-1)
|
||||
|
||||
if output_mode == "alpha_only":
|
||||
# 只输出alpha遮罩,图像输出为黑色
|
||||
matted_image = torch.zeros_like(image.cpu())
|
||||
elif output_mode == "matted_rgba":
|
||||
# 创建RGBA图像:RGB通道保持原图,A通道为alpha遮罩
|
||||
# 背景变为透明,只保留前景对象
|
||||
matted_image = torch.cat([
|
||||
image.cpu() * alpha_expanded, # RGB通道应用遮罩
|
||||
alpha_expanded.expand(-1, -1, -1, 1) # A通道为遮罩本身
|
||||
image.cpu() * alpha_expanded,
|
||||
alpha_expanded.expand(-1, -1, -1, 1)
|
||||
], dim=-1)
|
||||
# 确保输出仍为3通道(ComfyUI IMAGE格式)
|
||||
matted_image = matted_image[..., :3]
|
||||
elif output_mode == "matted_rgb":
|
||||
# 只保留前景对象,背景变为黑色
|
||||
# 使用trimap的前景信息来更精确地提取对象
|
||||
trimap_cpu = trimap.cpu() # (B, H, W)
|
||||
trimap_expanded = trimap_cpu.unsqueeze(-1) # (B, H, W, 1)
|
||||
trimap_cpu = trimap.cpu()
|
||||
trimap_expanded = trimap_cpu.unsqueeze(-1)
|
||||
|
||||
# 只在trimap标记为前景(>0.8)或未知区域(0.2-0.8)的地方保留原图
|
||||
# 结合alpha遮罩进一步细化
|
||||
foreground_mask = (trimap_expanded > 0.2) & (alpha_expanded > 0.1)
|
||||
matted_image = image.cpu() * foreground_mask.float()
|
||||
else:
|
||||
# 默认:直接应用alpha遮罩
|
||||
matted_image = image.cpu() * alpha_expanded
|
||||
|
||||
# 推理后清理显存
|
||||
if device.type == 'cuda':
|
||||
try:
|
||||
torch.cuda.empty_cache()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
if unload_model_after:
|
||||
global _sdmatte_model_cache
|
||||
print("[SDMatte] Unloading model to free GPU memory")
|
||||
if _sdmatte_model_cache['model'] is not None:
|
||||
del _sdmatte_model_cache['model']
|
||||
_sdmatte_model_cache['model'] = None
|
||||
_sdmatte_model_cache['ckpt_name'] = None
|
||||
clear_memory()
|
||||
return (out, matted_image)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SDMatteModelLoader": SDMatteModelLoader,
|
||||
"SDMatteApply": SDMatteApply,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SDMatteModelLoader": "Load SDMatte Model",
|
||||
"SDMatteApply": "Apply SDMatte",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user