Files

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,)