update: 合并README文件,删除冗余文档

This commit is contained in:
wTechArtist
2025-06-18 09:57:28 +08:00
parent a9304a8c0d
commit 93632600a7
6 changed files with 400 additions and 399 deletions
+54 -139
View File
@@ -1,178 +1,93 @@
# VGGT ComfyUI 集成
# ComfyUI VVL VideoCamera Advanced - 环境安装指南
在 ComfyUI 中集成 Facebook Research 的 VGGT(Visual Geometry Grounded Transformer)模型,实现原生的多视图3D重建、相机姿态估计、深度预测和轨迹跟踪功能。
## 功能特性
- ✅ **原生VGGT算法**: 直接使用官方VGGT推理流程
- ✅ **多视图3D重建**: 支持1-200帧图像序列输入
- ✅ **相机姿态估计**: 自动估计相机内外参数
- ✅ **深度图预测**: 生成高质量密集深度图
- ✅ **点云重建**: 基于深度和姿态信息重建3D点云
- ✅ **原生结果输出**: 保留所有原生Tensor数据
- ✅ **自动模型管理**: 支持本地缓存和HuggingFace自动下载
这是一个集成了VGGT(Visual Geometry Grounded Transformer)和GLB点云处理功能的ComfyUI自定义节点包。
## 安装
### 1. 自动安装(推荐)
### 方法1: 自动安装(推荐)
```bash
cd custom_nodes/ComfyUI_VVL_VideoCamera_Advanced
python install.py
```
### 2. 手动安装
### 方法2: 手动安装
```bash
# 安装VGGT专用依赖
# 1. 安装VGGT专用依赖
pip install -r requirements_vggt.txt
# 创建模型目录
# 2. 创建模型目录
mkdir -p models/vggt
# 3. 安装GLB处理依赖
pip install trimesh scikit-learn
```
## 使用方法
### 基本工作流
1. **加载模型**: 使用 `VGGT 模型加载器` 节点
- 选择设备(auto/cuda/cpu)
- 选择模型版本(VGGT-1B)
2. **输入图像**: 连接图像序列到 `VGGT 多输入重建` 节点
- 支持1-200帧图像
- 自动预处理到336x336分辨率
3. **配置参数**:
- `confidence_threshold`: 置信度阈值(0-100%)
- `show_cameras`: 是否显示相机位置
- `mask_*`: 各种遮罩选项
4. **获取结果**:
- `raw_result`: 原生VGGT推理结果(包含所有Tensor)
- `intrinsics_json`: 相机内参JSON
- `trajectory_preview`: 轨迹预览图像
- `poses_json`: 相机姿态JSON
- `model_3d_path`: 3D模型文件路径
### 节点说明
#### VGGT 模型加载器
- **输入**: 设备选择、模型版本
- **输出**: VGGT模型实例
- **功能**: 自动下载和缓存模型权重
#### VGGT 多输入重建
- **输入**: VGGT模型、图像序列、配置参数
- **输出**: 原生结果、JSON数据、预览图像、3D模型
- **功能**: 执行完整的VGGT推理流程
## 模型管理
### 支持的模型
| 模型名称 | 参数量 | 文件大小 | HuggingFace Hub |
|---------|--------|----------|-----------------|
| VGGT-1B | 1B | ~4.7GB | facebook/VGGT-1B |
### 模型存储
模型权重存储在 `models/vggt/` 目录:
- 自动下载:首次使用时从HuggingFace Hub下载
- 本地缓存:下载后自动缓存,避免重复下载
- 手动放置:可手动将 `vggt_1b.pt` 放入该目录
## 技术架构
### 核心组件
## 依赖要求
### VGGT核心依赖
```
ComfyUI Framework
├── VVL_VideoCamera_Advanced/
│ ├── vggt/ # VGGT原生代码
│ ├── vggt_native_interface.py # 原生接口封装
│ ├── vggt_model_loader.py # 模型加载管理
│ ├── comfyui_vggt_nodes.py # ComfyUI节点
│ └── visual_util.py # 可视化工具
torch>=2.0.0
torchvision
transformers
accelerate
huggingface_hub
numpy
pillow
```
### 推理流程
### GLB处理依赖
```
trimesh # GLB文件读写(必需)
scikit-learn # 离群点检测(可选)
numpy # 数值计算
```
1. **图像预处理**: 使用VGGT官方预处理函数
2. **特征聚合**: `model.aggregator(images)`
3. **多分支预测**:
- 相机姿态: `model.camera_head()`
- 深度图: `model.depth_head()`
- 点云: `model.point_head()`
- 轨迹跟踪: `model.track_head()` (可选)
4. **结果后处理**: 保留原生Tensor + 生成可视化
## 验证安装
## 性能优化
运行测试脚本验证安装是否成功:
### 内存使用
```bash
python test_vggt_integration.py
```
| 输入帧数 | GPU内存 | 推理时间 |
|---------|---------|----------|
| 1 | 1.88GB | 0.04s |
| 10 | 3.63GB | 0.14s |
| 50 | 11.41GB | 1.04s |
| 100 | 21.15GB | 3.12s |
| 200 | 40.63GB | 8.75s |
### 优化建议
- 使用Flash Attention 3(如果可用)
- 启用混合精度推理(FP16)
- 根据GPU内存调整输入帧数
- 使用CUDA设备获得最佳性能
如果看到 "🎉 所有测试通过!" 消息,说明安装成功。
## 故障排除
### 常见问题
1. **模型下载失败**
- 检查网络连接
- 手动下载模型文件到 `models/vggt/`
1. **"trimesh库不可用"错误**
```bash
pip install trimesh
```
2. **内存不足**
2. **"No module named 'vggt'"错误**
- 确保所有 `__init__.py` 文件存在
- 重新运行 `python install.py`
3. **模型下载失败**
- 检查网络连接
- 模型会自动从HuggingFace下载到 `models/vggt/` 目录
4. **内存不足**
- 减少输入图像数量
- 使用CPU推理
- 关闭其他GPU程序
3. **依赖缺失**
- 运行 `python install.py` 重新安装
- 检查Python环境和包版本
### 模型存储
### 日志调试
模型权重自动存储在 `ComfyUI/models/vggt/` 目录:
- 首次使用时自动从HuggingFace Hub下载
- 本地缓存避免重复下载
- 可手动放置模型文件到该目录
启用详细日志:
```python
import logging
logging.getLogger('vvl_vggt_nodes').setLevel(logging.DEBUG)
```
## 系统要求
## 开发说明
- Python 3.8+
- PyTorch 2.0+
- CUDA(推荐,CPU也支持)
- 至少8GB内存(GPU推理需要更多显存)
### 扩展节点
参考 `vggt.md` 技术规范文档,可以基于原生接口开发新的专用节点:
- 深度估计专用节点
- 相机姿态估计专用节点
- 点跟踪专用节点
### 贡献指南
1. 遵循技术规范文档
2. 保持原生VGGT API兼容性
3. 添加适当的错误处理和日志
4. 更新文档和测试
## 许可证
本项目遵循原始VGGT项目的许可证条款。
## 致谢
- [Facebook Research VGGT](https://github.com/facebookresearch/vggt)
- [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
- VGGT论文作者和贡献者
安装完成后即可在ComfyUI中使用VGGT和GLB处理节点。
-116
View File
@@ -1,116 +0,0 @@
# VGGT ComfyUI集成修复总结
## 问题诊断
❌ **初始错误**: `RuntimeError: VGGT not available: No module named 'vggt'`
这个错误表明虽然VGGT代码存在于 `vggt/` 目录中,但Python无法将其识别为一个可导入的模块包。
## 根本原因
🔍 **缺失的 `__init__.py` 文件**
Python需要 `__init__.py` 文件来识别目录为模块包。检查发现:
- ✅ `vggt/heads/track_modules/__init__.py` - 存在
- ✅ `vggt/dependency/__init__.py` - 存在
- ✅ `vggt/layers/__init__.py` - 存在
- ❌ `vggt/__init__.py` - **缺失** (根目录)
- ❌ `vggt/models/__init__.py` - **缺失**
- ❌ `vggt/utils/__init__.py` - **缺失**
- ❌ `vggt/heads/__init__.py` - **缺失**
## 修复措施
✅ **创建缺失的 `__init__.py` 文件**
```bash
# 创建的文件:
custom_nodes/ComfyUI_VVL_VideoCamera_Advanced/vggt/__init__.py
custom_nodes/ComfyUI_VVL_VideoCamera_Advanced/vggt/models/__init__.py
custom_nodes/ComfyUI_VVL_VideoCamera_Advanced/vggt/utils/__init__.py
custom_nodes/ComfyUI_VVL_VideoCamera_Advanced/vggt/heads/__init__.py
```
每个文件都包含适当的模块文档字符串,使Python能够正确识别这些目录为模块包。
## 验证结果
✅ **所有导入测试通过**
```python
# 测试通过的导入:
from vggt.models.vggt import VGGT # ✓ 成功
from vggt.utils.load_fn import load_and_preprocess_images # ✓ 成功
from vggt_model_loader import VVLVGGTLoader # ✓ 成功
from vggt_native_interface import VGGTNativeInterface # ✓ 成功
from comfyui_vggt_nodes import VGGTMultiInputNode # ✓ 成功
```
✅ **节点创建测试通过**
```
VGGTLoader输入类型: ['device', 'vggt_model']
VGGTMultiInputNode输入类型: ['vggt_model', 'images']
VGGTMultiInputNode输出类型: ('RAW_VGGT_RESULT', 'STRING', 'IMAGE', 'STRING', 'STRING')
```
✅ **模型信息获取正常**
```
VGGT可用性: True
模型目录: /home/game-netease/.cache/comfyui/models/vggt
本地模型: {'VGGT-1B': False}
```
## 解决方案效果
🎉 **完全修复**: ComfyUI现在可以正确识别和使用VGGT节点
### 修复前:
```
RuntimeError: VGGT not available: No module named 'vggt'
```
### 修复后:
```
🎉 所有测试通过!VGGT节点可以正常工作了!
```
## 技术细节
### Python模块系统要求
- Python需要 `__init__.py` 文件来将目录识别为包
- 缺少根目录的 `__init__.py` 会导致整个包无法导入
- 子目录的 `__init__.py` 也是必需的
### 文件结构 (修复后)
```
vggt/
├── __init__.py # ✅ 新增
├── models/
│ ├── __init__.py # ✅ 新增
│ └── vggt.py
├── utils/
│ ├── __init__.py # ✅ 新增
│ └── load_fn.py
├── heads/
│ ├── __init__.py # ✅ 新增
│ └── track_modules/
└── ...
```
## 后续使用
现在用户可以在ComfyUI中正常使用VGGT节点:
1. **加载模型**: 使用 `VGGT 模型加载器` 节点
2. **处理图像**: 连接图像序列到 `VGGT 多输入重建` 节点
3. **获得结果**: 原生VGGT结果 + 格式化输出
## 总结
✅ **问题完全解决**: 通过添加缺失的 `__init__.py` 文件,VGGT模块现在可以正确导入
✅ **功能完整**: 所有原生VGGT功能都可以在ComfyUI中使用
✅ **测试验证**: 所有组件都经过测试验证正常工作
这是一个简单但关键的修复,确保了Python模块系统能够正确识别和导入VGGT包结构。
-140
View File
@@ -1,140 +0,0 @@
# VGGT ComfyUI集成重构完成总结
## 重构概况
✅ **重构成功完成**!已根据最新的 vggt.md 技术规范和官方 Facebook Research VGGT 仓库,成功重构代码以实现原生的 VGGT 算法集成。
## 重构重点
### 1. 原生VGGT算法实现
- ✅ 创建了 `vggt_native_interface.py`,实现完整的原生VGGT推理流程
- ✅ 严格按照官方API:`aggregator → camera_head → depth_head → point_head → track_head`
- ✅ 使用官方预处理函数和工具函数
- ✅ 支持混合精度推理(FP16)
### 2. 输入输出规范化
- ✅ 输入改为仅接受图片序列(移除内部视频预处理)
- ✅ 输出同时包含原生结果和格式化结果
- ✅ 原生Tensor数据完整保留
- ✅ 支持1-200帧图像序列
### 3. 模型管理优化
- ✅ 模型文件存储在 `ComfyUI/models/vggt/` 目录
- ✅ 本地存在则直接加载,否则自动从HuggingFace下载
- ✅ 支持模型缓存和设备管理
- ✅ 断点续传和错误恢复
### 4. 依赖管理
- ✅ 创建了 `requirements_vggt.txt` 专用依赖文件
- ✅ 包含所有核心依赖,注释掉可能有编译问题的可选依赖
- ✅ 版本锁定以避免兼容性问题
## 文件结构
```
ComfyUI_VVL_VideoCamera_Advanced/
├── vggt/ # VGGT原生代码(已存在)
├── vggt_native_interface.py # ✅ 新增:原生接口封装
├── vggt_model_loader.py # ✅ 更新:优化模型加载
├── comfyui_vggt_nodes.py # ✅ 重构:使用原生接口
├── requirements_vggt.txt # ✅ 新增:专用依赖
├── install.py # ✅ 新增:自动安装脚本
├── test_vggt_integration.py # ✅ 新增:集成测试
├── README.md # ✅ 新增:使用文档
├── VGGT_INTEGRATION_SUMMARY.md # ✅ 新增:本总结文档
├── vggt.md # ✅ 更新:技术规范
└── __init__.py # ✅ 更新:节点注册
```
## 核心组件
### VGGTNativeInterface
- 完整的VGGT推理管道
- 选择性推理支持
- 官方工具函数集成
### VGGTImageProcessor
- 官方预处理函数优先
- 备用预处理方案
- 遮罩应用支持
### VGGTResultProcessor
- 原生结果保留
- 格式化输出生成
- JSON序列化支持
### VGGTMultiInputNode
- 图片序列输入(必需)
- 原生结果输出(首位)
- 配置参数支持
### VVLVGGTLoader
- 自动模型下载
- 本地缓存管理
- 设备自动选择
## 测试验证
✅ **所有集成测试通过**:
- 依赖检查: ✓ 通过
- 模型目录: ✓ 通过
- VGGT模型加载: ✓ 通过
- 原生接口: ✓ 通过
- ComfyUI节点: ✓ 通过
## 安装使用
### 自动安装
```bash
cd custom_nodes/ComfyUI_VVL_VideoCamera_Advanced
python install.py
```
### 验证安装
```bash
python test_vggt_integration.py
```
### 在ComfyUI中使用
1. 使用 `VGGT 模型加载器` 加载模型
2. 连接图像序列到 `VGGT 多输入重建` 节点
3. 获得原生VGGT结果和格式化输出
## 技术特性
### 性能优化
- 混合精度推理(FP16)
- 模型缓存机制
- 内存使用监控
- 批处理支持
### 错误处理
- 完整的异常捕获
- 优雅的错误恢复
- 详细的日志记录
- 用户友好的错误信息
### 兼容性
- PyTorch 2.5.1+ 支持
- CUDA 和 CPU 推理
- ComfyUI 工作流集成
- 官方VGGT API兼容
## 后续扩展
根据 vggt.md 技术规范,可以进一步开发:
- 深度估计专用节点
- 相机姿态估计专用节点
- 点跟踪专用节点
- 可选依赖集成(flash-attn、pytorch3d等)
## 总结
🎉 **重构成功完成**!现在 ComfyUI 中已集成了完全原生的 VGGT 算法,用户可以:
1. **使用原生VGGT算法**进行多视图3D重建
2. **获得原生Tensor结果**用于后续处理
3. **享受自动化模型管理**无需手动下载
4. **体验优化的性能**和稳定性
该集成严格遵循 Facebook Research VGGT 的官方实现,确保了算法的原始性和准确性。
+6 -3
View File
@@ -5,17 +5,20 @@ ComfyUI VVL Video Camera Advanced
from .comfyui_vggt_nodes import VGGTMultiInputNode
from .vggt_model_loader import VVLVGGTLoader
from .glb_point_cloud_processor import GLBPointCloudProcessor
# 节点类映射
NODE_CLASS_MAPPINGS = {
"VVLVGGTLoader": VVLVGGTLoader,
"VGGTMultiInputNode": VGGTMultiInputNode,
"VGGTMultiInputNode": VGGTMultiInputNode,
"GLBPointCloudProcessor": GLBPointCloudProcessor,
}
# 节点显示名称映射
NODE_DISPLAY_NAME_MAPPINGS = {
"VVLVGGTLoader": "VGGT 模型加载器",
"VGGTMultiInputNode": "VGGT 多输入重建",
"VVLVGGTLoader": "VVL VGGT 模型加载器",
"VGGTMultiInputNode": "VVL VGGT 多输入重建",
"GLBPointCloudProcessor": "VVL GLB 点云黑色点清理",
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+4 -1
View File
@@ -2097,4 +2097,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
# 如果模型加载器可用,添加到映射中
if MODEL_LOADER_AVAILABLE:
NODE_CLASS_MAPPINGS["VVLVGGTLoader"] = VVLVGGTLoader
NODE_DISPLAY_NAME_MAPPINGS["VVLVGGTLoader"] = "VVL VGGT Model Loader"
NODE_DISPLAY_NAME_MAPPINGS["VVLVGGTLoader"] = "VVL VGGT Model Loader"
# 注意:GLBPointCloudProcessor现在在独立文件中自行注册
# 无需在此文件中导入,ComfyUI会自动发现glb_point_cloud_processor.py
+336
View File
@@ -0,0 +1,336 @@
import os
import json
import tempfile
import logging
import struct
from typing import List, Any, Dict, Tuple
import numpy as np
import torch
# 导入ComfyUI的路径管理
try:
import folder_paths
FOLDER_PATHS_AVAILABLE = True
except ImportError:
folder_paths = None
FOLDER_PATHS_AVAILABLE = False
# 导入trimesh用于GLB文件处理
try:
import trimesh
TRIMESH_AVAILABLE = True
except ImportError:
TRIMESH_AVAILABLE = False
trimesh = None
# 配置日志
logger = logging.getLogger('glb_point_cloud_processor')
class GLBPointCloudProcessor:
"""GLB点云文件处理器 - 专门用于删除黑色点和暗色点优化"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"glb_file_path": ("STRING", {
"default": "",
"tooltip": "GLB点云文件路径:输入需要处理的GLB格式点云文件路径。支持绝对路径(如C:/path/to/file.glb)或相对于ComfyUI output目录的相对路径(如pointcloud.glb)。GLB文件应包含点云数据,支持带颜色信息的点云处理"
}),
},
"optional": {
"black_threshold": ("INT", {
"default": 30, "min": 0, "max": 255, "step": 1,
"tooltip": "黑色阈值控制:RGB三色总和小于此值的点将被删除。0=仅删除纯黑点(0,0,0);30=删除深黑色点;60=删除较暗点;100=删除中等暗度点;255=删除所有点。调大=删除更多暗色点,点云更亮净;调小=保留更多暗色细节,但可能有噪点"
}),
"output_filename": ("STRING", {
"default": "clean_pointcloud.glb",
"tooltip": "输出GLB文件名:处理后点云的保存文件名,自动添加.glb扩展名。文件将保存到ComfyUI的output目录中。建议使用描述性名称如'clean_pointcloud.glb'、'filtered_scan.glb'等便于识别"
}),
}
}
RETURN_TYPES = (
"STRING", # 处理后的GLB文件路径
"STRING", # 处理统计信息(JSON格式)
"STRING", # 处理日志信息
)
RETURN_NAMES = (
"processed_glb_path",
"processing_stats",
"processing_log",
)
OUTPUT_TOOLTIPS = [
"处理后的GLB文件完整路径",
"处理统计信息(JSON格式)- 包含删除的点数、剩余点数等",
"详细的处理日志信息"
]
OUTPUT_NODE = True
FUNCTION = "process_point_cloud"
CATEGORY = "💃VVL/Point Cloud Cleaning"
def process_point_cloud(self,
glb_file_path: str,
black_threshold: int = 30,
output_filename: str = "clean_pointcloud.glb"):
"""
处理GLB点云文件,删除黑色点和暗色点
"""
processing_log = []
processing_log.append("开始GLB点云黑色点清理...")
# 检查依赖
if not TRIMESH_AVAILABLE:
error_msg = "trimesh库不可用,无法处理GLB文件"
logger.error(error_msg)
processing_log.append(f"错误: {error_msg}")
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
# 验证输入文件路径
if not glb_file_path or not glb_file_path.strip():
error_msg = "GLB文件路径为空"
logger.error(error_msg)
processing_log.append(f"错误: {error_msg}")
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
# 处理文件路径
input_path = self._resolve_file_path(glb_file_path.strip())
processing_log.append(f"输入文件路径: {input_path}")
if not os.path.exists(input_path):
error_msg = f"GLB文件不存在: {input_path}"
logger.error(error_msg)
processing_log.append(f"错误: {error_msg}")
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
try:
# 加载GLB文件
processing_log.append("正在加载GLB文件...")
scene = trimesh.load(input_path)
# 提取点云数据
point_clouds = []
other_geometries = []
if isinstance(scene, trimesh.Scene):
for name, geometry in scene.geometry.items():
if isinstance(geometry, trimesh.PointCloud):
point_clouds.append((name, geometry))
processing_log.append(f"发现点云: {name}, 点数: {len(geometry.vertices)}")
else:
other_geometries.append((name, geometry))
processing_log.append(f"发现其他几何体: {name}, 类型: {type(geometry).__name__}")
elif isinstance(scene, trimesh.PointCloud):
point_clouds.append(("main_pointcloud", scene))
processing_log.append(f"发现点云: main_pointcloud, 点数: {len(scene.vertices)}")
else:
# 尝试转换为点云
if hasattr(scene, 'vertices'):
pc = trimesh.PointCloud(vertices=scene.vertices, colors=getattr(scene.visual, 'vertex_colors', None))
point_clouds.append(("converted_pointcloud", pc))
processing_log.append(f"转换为点云: converted_pointcloud, 点数: {len(pc.vertices)}")
else:
error_msg = "GLB文件中未找到点云数据"
processing_log.append(f"错误: {error_msg}")
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
if not point_clouds:
error_msg = "GLB文件中没有点云数据"
processing_log.append(f"错误: {error_msg}")
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
# 处理每个点云
processed_point_clouds = []
total_original_points = 0
total_removed_points = 0
for name, point_cloud in point_clouds:
processing_log.append(f"\n处理点云: {name}")
original_count = len(point_cloud.vertices)
total_original_points += original_count
# 获取顶点和颜色
vertices = point_cloud.vertices.copy()
colors = None
if hasattr(point_cloud.visual, 'vertex_colors') and point_cloud.visual.vertex_colors is not None:
colors = point_cloud.visual.vertex_colors.copy()
elif hasattr(point_cloud, 'colors') and point_cloud.colors is not None:
colors = point_cloud.colors.copy()
# 初始化掩码(所有点都保留)
keep_mask = np.ones(len(vertices), dtype=bool)
# 删除暗色点(默认启用)
if colors is not None:
removed_count = self._remove_dark_points(vertices, colors, keep_mask, black_threshold, processing_log)
processing_log.append(f"删除黑色点: {removed_count} 个")
else:
processing_log.append("跳过黑色点过滤: 点云无颜色信息")
# 应用掩码
filtered_vertices = vertices[keep_mask]
filtered_colors = colors[keep_mask] if colors is not None else None
remaining_count = len(filtered_vertices)
removed_count = original_count - remaining_count
total_removed_points += removed_count
processing_log.append(f"点云 {name}: 原始 {original_count} -> 剩余 {remaining_count} (删除 {removed_count})")
# 创建处理后的点云
if remaining_count > 0:
processed_pc = trimesh.PointCloud(vertices=filtered_vertices, colors=filtered_colors)
processed_point_clouds.append((name, processed_pc))
else:
processing_log.append(f"警告: 点云 {name} 处理后没有剩余点")
# 创建新的场景
if processed_point_clouds:
new_scene = trimesh.Scene()
# 添加处理后的点云
for name, pc in processed_point_clouds:
new_scene.add_geometry(pc, node_name=name)
# 添加其他几何体(如相机模型)
for name, geometry in other_geometries:
new_scene.add_geometry(geometry, node_name=name)
processing_log.append(f"保留几何体: {name}")
# 生成输出路径
output_path = self._generate_output_path(output_filename)
processing_log.append(f"输出文件路径: {output_path}")
# 保存处理后的GLB文件
processing_log.append("正在保存处理后的GLB文件...")
new_scene.export(output_path)
# 验证文件是否保存成功
if os.path.exists(output_path):
file_size = os.path.getsize(output_path)
processing_log.append(f"GLB文件保存成功,文件大小: {file_size} bytes")
# 生成统计信息
stats = {
"original_points": total_original_points,
"remaining_points": total_original_points - total_removed_points,
"removed_points": total_removed_points,
"removal_percentage": (total_removed_points / total_original_points * 100) if total_original_points > 0 else 0,
"point_clouds_processed": len(point_clouds),
"other_geometries_preserved": len(other_geometries),
"output_file_size": file_size,
"settings": {
"black_threshold": black_threshold,
"remove_dark_points": True
}
}
processing_log.append("GLB点云黑色点清理完成!")
return output_path, json.dumps(stats, indent=2), "\n".join(processing_log)
else:
error_msg = "GLB文件保存失败"
processing_log.append(f"错误: {error_msg}")
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
else:
error_msg = "处理后没有剩余的点云数据"
processing_log.append(f"错误: {error_msg}")
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
except Exception as e:
error_msg = f"处理GLB文件时发生错误: {str(e)}"
logger.error(error_msg)
processing_log.append(f"错误: {error_msg}")
import traceback
traceback.print_exc()
return "", json.dumps({"error": error_msg}), "\n".join(processing_log)
def _resolve_file_path(self, file_path: str) -> str:
"""解析文件路径,支持绝对路径和相对路径"""
if os.path.isabs(file_path):
return file_path
# 尝试相对于ComfyUI输出目录
if FOLDER_PATHS_AVAILABLE:
output_dir = folder_paths.get_output_directory()
candidate_path = os.path.join(output_dir, file_path)
if os.path.exists(candidate_path):
return candidate_path
# 尝试相对于当前工作目录
if os.path.exists(file_path):
return os.path.abspath(file_path)
# 返回原始路径(让后续检查处理错误)
return file_path
def _generate_output_path(self, filename: str) -> str:
"""生成输出文件路径"""
if FOLDER_PATHS_AVAILABLE:
output_dir = folder_paths.get_output_directory()
else:
output_dir = "output"
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
# 处理文件名,确保是.glb扩展名
if not filename.lower().endswith('.glb'):
filename = f"{filename}.glb"
return os.path.join(output_dir, filename)
def _remove_dark_points(self, vertices: np.ndarray, colors: np.ndarray, keep_mask: np.ndarray,
black_threshold: int, processing_log: List[str]) -> int:
"""删除暗色点"""
removed_count = 0
# 确保颜色数据格式正确
if colors.shape[1] == 4: # RGBA
rgb_colors = colors[:, :3]
else: # RGB
rgb_colors = colors
# 如果颜色值在0-1范围内,转换为0-255
if rgb_colors.max() <= 1.0:
rgb_colors = (rgb_colors * 255).astype(np.uint8)
else:
rgb_colors = rgb_colors.astype(np.uint8)
# 计算RGB总和
rgb_sum = rgb_colors.sum(axis=1)
# 应用黑色阈值过滤
if black_threshold > 0:
dark_mask = rgb_sum >= black_threshold
before_count = keep_mask.sum()
keep_mask &= dark_mask
after_count = keep_mask.sum()
removed_by_black = before_count - after_count
removed_count += removed_by_black
processing_log.append(f" 按RGB总和阈值({black_threshold})删除: {removed_by_black} 个点")
return removed_count
# -----------------------------------------------------------------------------
# 节点注册 - 独立注册,ComfyUI会自动发现此文件
# -----------------------------------------------------------------------------
NODE_CLASS_MAPPINGS = {
"GLBPointCloudProcessor": GLBPointCloudProcessor,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"GLBPointCloudProcessor": "VVL GLB Point Cloud Processor",
}
# # 添加节点信息,帮助ComfyUI更好地识别
# WEB_DIRECTORY = "./web"
# __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]