215 lines
7.1 KiB
Python
215 lines
7.1 KiB
Python
"""
|
|
VGGT Model Loader
|
|
独立的VGGT模型加载和管理模块
|
|
"""
|
|
|
|
import os
|
|
import logging
|
|
from typing import Dict, Optional
|
|
import torch
|
|
|
|
# ComfyUI相关导入
|
|
try:
|
|
import folder_paths
|
|
import comfy.model_management
|
|
COMFYUI_AVAILABLE = True
|
|
except ImportError:
|
|
folder_paths = None
|
|
comfy = None
|
|
COMFYUI_AVAILABLE = False
|
|
|
|
# VGGT相关导入
|
|
try:
|
|
from vggt.models.vggt import VGGT
|
|
VGGT_AVAILABLE = True
|
|
except Exception as e:
|
|
VGGT = None
|
|
VGGT_AVAILABLE = False
|
|
_VGGT_IMPORT_ERROR = e
|
|
|
|
# 配置日志
|
|
logger = logging.getLogger('vvl_vggt_loader')
|
|
|
|
# VGGT模型配置
|
|
VGGT_MODEL_DIR_NAME = "vggt"
|
|
VGGT_MODEL_CONFIG = {
|
|
"VGGT-1B": {
|
|
"model_name": "facebook/VGGT-1B",
|
|
"description": "VGGT 1B parameter model for camera pose estimation",
|
|
"file_name": "vggt_1b.pt",
|
|
"size_mb": 4700 # 约4.7GB
|
|
},
|
|
# 可以添加更多模型配置
|
|
# "VGGT-Large": {
|
|
# "model_name": "facebook/VGGT-Large",
|
|
# "description": "VGGT Large model with better accuracy",
|
|
# "file_name": "vggt_large.pt"
|
|
# }
|
|
}
|
|
|
|
# 全局模型缓存
|
|
_VGGT_MODEL_CACHE = {}
|
|
|
|
def get_vggt_model_dir() -> str:
|
|
"""获取VGGT模型目录路径"""
|
|
if COMFYUI_AVAILABLE and folder_paths:
|
|
# 使用ComfyUI的models目录
|
|
model_dir = os.path.join(folder_paths.models_dir, VGGT_MODEL_DIR_NAME)
|
|
else:
|
|
# 备用路径
|
|
model_dir = os.path.join(os.path.expanduser("~"), ".cache", "comfyui", "models", VGGT_MODEL_DIR_NAME)
|
|
|
|
# 确保目录存在
|
|
os.makedirs(model_dir, exist_ok=True)
|
|
return model_dir
|
|
|
|
def get_vggt_model_path(model_config: dict) -> Optional[str]:
|
|
"""获取VGGT模型的本地路径"""
|
|
model_dir = get_vggt_model_dir()
|
|
model_path = os.path.join(model_dir, model_config["file_name"])
|
|
|
|
# 检查模型文件是否存在
|
|
if os.path.exists(model_path):
|
|
logger.info(f"Found local VGGT model: {model_path}")
|
|
return model_path
|
|
|
|
logger.warning(f"Local VGGT model not found: {model_path}")
|
|
logger.info(f"Expected model directory: {model_dir}")
|
|
return None
|
|
|
|
def list_available_models() -> Dict[str, bool]:
|
|
"""列出可用的模型及其本地可用性"""
|
|
available_models = {}
|
|
for model_name, config in VGGT_MODEL_CONFIG.items():
|
|
local_path = get_vggt_model_path(config)
|
|
available_models[model_name] = local_path is not None
|
|
return available_models
|
|
|
|
def load_vggt_model(model_name: str, device: torch.device) -> Optional[torch.nn.Module]:
|
|
"""加载VGGT模型"""
|
|
if not VGGT_AVAILABLE:
|
|
logger.error(f"VGGT not available: {_VGGT_IMPORT_ERROR}")
|
|
return None
|
|
|
|
try:
|
|
# 检查缓存
|
|
cache_key = f"{model_name}_{device}"
|
|
if cache_key in _VGGT_MODEL_CACHE:
|
|
logger.info(f"Using cached VGGT model: {model_name}")
|
|
return _VGGT_MODEL_CACHE[cache_key]
|
|
|
|
if model_name not in VGGT_MODEL_CONFIG:
|
|
raise ValueError(f"Unknown VGGT model: {model_name}. Available: {list(VGGT_MODEL_CONFIG.keys())}")
|
|
|
|
config = VGGT_MODEL_CONFIG[model_name]
|
|
local_path = get_vggt_model_path(config)
|
|
|
|
# 加载模型
|
|
if local_path and os.path.exists(local_path):
|
|
logger.info(f"Loading VGGT model from local path: {local_path}")
|
|
# 注意:这里可能需要根据实际的本地加载API调整
|
|
# 目前先使用HuggingFace的方式,后续可以优化为直接加载本地权重
|
|
model = VGGT.from_pretrained(config["model_name"])
|
|
else:
|
|
# 从HuggingFace加载(会自动下载)
|
|
logger.info(f"Loading VGGT model from HuggingFace: {config['model_name']}")
|
|
logger.warning(f"Model will be downloaded (~{config.get('size_mb', 'Unknown')}MB)")
|
|
model = VGGT.from_pretrained(config["model_name"])
|
|
|
|
# 移动到指定设备并设置为评估模式
|
|
model = model.to(device)
|
|
model.eval()
|
|
|
|
# 缓存模型
|
|
_VGGT_MODEL_CACHE[cache_key] = model
|
|
|
|
logger.info(f"VGGT model {model_name} loaded successfully on {device}")
|
|
return model
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to load VGGT model {model_name}: {e}")
|
|
return None
|
|
|
|
def clear_model_cache():
|
|
"""清除模型缓存"""
|
|
global _VGGT_MODEL_CACHE
|
|
for key in list(_VGGT_MODEL_CACHE.keys()):
|
|
del _VGGT_MODEL_CACHE[key]
|
|
logger.info("VGGT model cache cleared")
|
|
|
|
def get_model_info() -> Dict:
|
|
"""获取模型信息"""
|
|
return {
|
|
"available": VGGT_AVAILABLE,
|
|
"error": _VGGT_IMPORT_ERROR if not VGGT_AVAILABLE else None,
|
|
"model_dir": get_vggt_model_dir(),
|
|
"cached_models": list(_VGGT_MODEL_CACHE.keys()),
|
|
"config": VGGT_MODEL_CONFIG,
|
|
"local_models": list_available_models()
|
|
}
|
|
|
|
class VVLVGGTLoader:
|
|
"""VGGT模型加载器节点"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
vggt_models = list(VGGT_MODEL_CONFIG.keys())
|
|
device_list = ["auto", "cuda", "cpu"]
|
|
|
|
return {
|
|
"required": {
|
|
"device": (device_list, {
|
|
"default": "auto",
|
|
"tooltip": "选择运行设备。auto会自动选择CUDA(如果可用)或CPU"
|
|
}),
|
|
"vggt_model": (vggt_models, {
|
|
"default": vggt_models[0] if vggt_models else "VGGT-1B",
|
|
"tooltip": "选择VGGT模型版本。VGGT-1B是标准的10亿参数模型,约4.7GB"
|
|
}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("VVL_VGGT_MODEL",)
|
|
RETURN_NAMES = ("vggt_model",)
|
|
FUNCTION = "load_vggt_model"
|
|
CATEGORY = "💃VVL/VGGT"
|
|
|
|
def load_vggt_model(self, device: str, vggt_model: str):
|
|
"""加载VGGT模型并返回模型实例"""
|
|
|
|
# 确定设备
|
|
if device == "auto":
|
|
torch_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
else:
|
|
torch_device = torch.device(device)
|
|
|
|
logger.info(f"VVLVGGTLoader: Loading VGGT model: {vggt_model} on device: {torch_device}")
|
|
|
|
# 检查VGGT是否可用
|
|
if not VGGT_AVAILABLE:
|
|
error_msg = f"VGGT not available: {_VGGT_IMPORT_ERROR}"
|
|
logger.error(error_msg)
|
|
raise RuntimeError(error_msg)
|
|
|
|
# 显示模型信息
|
|
model_info = get_model_info()
|
|
logger.info(f"Model directory: {model_info['model_dir']}")
|
|
logger.info(f"Local models: {model_info['local_models']}")
|
|
|
|
# 加载模型
|
|
model_instance = load_vggt_model(vggt_model, torch_device)
|
|
|
|
if model_instance is None:
|
|
raise RuntimeError(f"Failed to load VGGT model: {vggt_model}")
|
|
|
|
# 创建包含模型和相关信息的字典
|
|
model_data = {
|
|
'model': model_instance,
|
|
'device': torch_device,
|
|
'model_name': vggt_model,
|
|
'config': VGGT_MODEL_CONFIG[vggt_model],
|
|
'loader_info': model_info
|
|
}
|
|
|
|
logger.info("VVLVGGTLoader: VGGT model loaded successfully")
|
|
return (model_data,) |