Add model catalog with specifications for DA3 and MoGe-2 models

- Introduced ModelSpec dataclass to define model attributes.
- Added multiple models including DA3 Small, Base, Giant 1.1, Nested Giant Large 1.1, and MoGe-2 variants.
- Implemented functions to retrieve model details and metadata.
- Included BEN2 model specifications for background removal.
This commit is contained in:
2026-08-17 14:53:25 +08:00
parent c5b77c5c85
commit 1c98b21235
44 changed files with 637 additions and 2709 deletions

View File

@@ -1,255 +0,0 @@
# BlendJob 开发者指南
## 目标与边界
`blendjob` 连接 Blender Operator 与独立 Python Job Server。开发者新增任务时只需要一个 Blender Operator 和一个 Server Handler,不需要编写提交线程、HTTP 轮询、取消、进度条、Server 进程或 Environment 安装 Operator。
- `BlenderJobRuntime`:Blender 侧唯一运行时,持有 Storage Root、Environment、Server 进程和当前 Job
- `JobOperatorBase`:绑定 Runtime 后供业务 Operator 直接继承的 Modal Mixin
- `JobServer`:独立 Python 进程中的 Job 注册、状态、取消、工作目录和 Resource
- `JobContext`:Handler 的工作目录、Storage Root、进度、取消与 Resource 接口
- `JobResult`:Server 成功结果及其 Job 文件
- `ServerController`:Runtime 内部的专属 Server 进程控制器
## 项目配置
Server 实例直接定义在项目的 `server` package:
```python
# snapform/server/__init__.py
from blendjob import JobServer
server = JobServer("SnapForm Job Server")
```
Blender 侧把这个实例交给 Runtime。实例不会跨进程传输;Runtime 从导出该实例的 package 自动找到独立 Python 入口。
```python
# snapform/job_runtime.py
from .server import server
environment = {
"python": "3.12",
"packages": [
"numpy==2.4.2",
"pillow==12.1.1",
"av==17.0.0",
"trimesh==4.11.2",
"huggingface-hub==1.4.1",
],
"platform_packages": {
"windows": ["onnxruntime-directml==1.24.4"],
"default": ["onnxruntime==1.24.4"],
},
}
def post_install(runtime):
runtime.request(
"download-model",
{"model": "MOGE2_VITS_NORMAL"},
)
runtime = BlenderJobRuntime(
server,
storage_root=storage_root,
environment=environment,
post_install=post_install,
)
JobOperatorBase = runtime.JobOperatorBase
```
Runtime 按约定管理 `.venv`、Manifest、安装状态与日志路径。BlendJob 根据 `environment` 字典安装固定 Python、通用的 FastAPI/Uvicorn Server 依赖、项目 packages 以及当前平台 packages。配置会生成稳定 Hash;Python 或依赖声明变化后 Environment 自动判定为需要重装,不再需要项目维护 `install_env.py` 或手工 Revision。
`platform_packages` 优先选择 `windows`、`macos` 或 `linux`,没有对应项时使用 `default`。基础 packages 与平台 packages 会合并安装。
`post_install` 是可选的普通 callable,在 Environment 可用且 Server 能启动后执行。它不限定为 Job 列表,可以执行任意 Python;SnapForm 用它发起默认模型下载。连续 request 会分别重置进度,Status Bar 不为未知数量的 post-install 操作显示 `2/2` 等阶段总数。post-install 失败不会撤销已经安装成功的 Environment。
`runtime.register()` 自动注册 Environment 安装、Job 取消、Server 启动、停止、重启和日志 Operator,并挂载内建 Status Bar。Status Bar 使用可由 Blender 4.5 标记所有权的稳定函数回调,Runtime 卸载时移除同一个回调对象。业务 `CLASSES` 只包含业务类型。
```python
CLASSES = (
GenerateMoGe2Depth,
)
def register():
for class_type in CLASSES:
bpy.utils.register_class(class_type)
runtime.register()
def unregister():
runtime.unregister()
for class_type in reversed(CLASSES):
bpy.utils.unregister_class(class_type)
```
启用 Runtime 不会立即启动 Server。第一次提交 Job 或显式调用 Start Server 时才启动当前 Blender 专属进程。
## Blender Operator
最小 Operator 只声明 `job_type` 与自己的 Blender Property:
```python
class GenerateMoGe2Depth(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.generate_moge2_depth"
bl_label = "Generate MoGe-2 Depth"
job_type = "generate-moge2-depth"
input_path: bpy.props.StringProperty()
resolution_level: bpy.props.IntProperty(
default=5,
min=0,
max=9,
)
```
执行 `bpy.ops.snapform.generate_moge2_depth()` 调用的就是这个 Operator。`JobOperatorBase` 提供它的 `execute()` 和 `modal()`,不会再创建或调用隐藏的 Job Operator。
没有覆盖 `request()` 时,Operator 声明的全部 Property 会作为参数提交:
```json
{
"input_path": "D:/image.png",
"resolution_level": 5
}
```
后端可直接忽略不使用的字段。BlendJob 不提供 Property 排除表。
### request
需要读取 Blender Context 或生成临时输入时覆盖:
```python
def request(self, context):
input_path, temporary = prepare_image_input(
context.object.data,
context.scene,
)
self._temporary_input = input_path if temporary else None
return {
"input": str(input_path),
"resolution_level": self.resolution_level,
}
```
返回值是完整参数,纯替换默认 Property 参数,不做合并。它必须是可由 JSON 表示的 `dict`。
### response
Server 成功后,Runtime 在 Blender 主线程调用:
```python
def response(self, context, result):
build_moge2_depth_plane(
context,
source_object=bpy.data.objects[self._source_object_name],
npz_path=result.file("depth"),
)
```
`response()` 是可选的。未覆盖时 Job 成功即返回 `FINISHED`。开发者可以直接调用 Python 函数,也可以调用其它 Blender Operator;BlendJob 不限制后处理方式。
`cleanup()` 同样可选,在成功、失败和取消后都会执行,适合删除临时输入。
业务代码直接返回 `dict`。BlendJob 没有公开 `JobRequest` 包装类型;HTTP Payload 由 Runtime 内部构造。
## 直接 Request
不需要 Blender Operator 时,可以直接向 Runtime 发起一个同步 request:
```python
def downloaded(result):
print(result.directory)
result = runtime.request(
"download-model",
{"model": "MOGE2_VITS_NORMAL"},
response=downloaded,
)
```
`runtime.request(job_type, parameters, response=None)` 负责启动 Server、提交、轮询、失败处理与构造 `JobResult`,返回值就是 Server response。可选的 `response(result)` 在发起 request 的线程执行。它是同步 API,适合 `post_install`、后台线程或脚本;Blender 主线程中的交互功能仍应使用 `JobOperatorBase`,避免阻塞界面。
## Server Handler
Handler 直接注册为 `handler(context, parameters)`:
```python
@server.job("generate-moge2-depth")
def generate_moge2_depth(context, parameters):
model = context.resource("model_manager").get(
parameters["model"],
parameters["device"],
)
context.progress(0.1, "Loading MoGe-2")
context.check_cancelled()
depth = model.infer(
parameters["input"],
resolution_level=parameters["resolution_level"],
)
output = context.directory / "results.npz"
save_depth(output, depth)
return {"depth": output.name}
```
返回值保存在终态的 `result` 字段。Blender 端的 `JobResult.file("depth")` 会把相对路径限制在当前 Job Directory 内并验证文件存在。
`JobContext` 提供:
- `storage_root`:Runtime 传入的唯一共享根目录
- `directory`:`storage_root/jobs/<job-id>`
- `progress(value, message, **details)`:发布运行进度
- `check_cancelled()`:在安全检查点响应取消
- `resource(name)`:访问 Server 生命周期 Resource
Handler 不需要也不应管理 `queued`、`succeeded`、`failed` 等状态;这些状态由 `JobServer` 内部更新,因此公开 API 不再同时提供语义重复的 `update()`。Environment 安装发生在 Server 启动前,仍通过 Runtime 的 Bootstrap 状态文件回传进度,不属于 `JobContext`。
## Storage Root
Runtime 是 Storage Root 的唯一配置者。`JobServer` 不需要在项目代码中声明 `jobs_directory`,启动后固定使用:
```text
<storage_root>/
├── .venv/
├── environment.json
├── jobs/
├── models/
├── tools/
├── environment.log
├── environment-status.json
├── server.log
└── manifest.json
```
需要 Storage Root 的 Resource 使用延迟工厂:
```python
@server.resource("model_manager")
def create_model_manager(job_server):
return ModelManager(job_server.storage_root / "models")
```
Resource 在独立进程绑定 Storage Root 后创建,关闭 Server 时按注册逆序关闭。
模型等领域数据应由 Server Resource 管理。Blender 可以通过 `runtime.resource("model_manager")` 查询 Resource Snapshot,不应自行拼接模型目录或检查模型文件。
## Server 生命周期
每个 Blender 实例为当前插件持有一个专属 Server。Runtime 自动处理随机端口、Instance ID、父 Blender PID、启动日志、连接验证、重启和停止。
Server 只监听 `127.0.0.1`。当前本地专属进程协议不使用一次性 Token,不发现或复用其它 Blender 启动的 Server。
`JobServer` 内建一个标准库单 Worker FIFO Queue。新 Job 会立即得到 ID 和目录,空闲时执行,忙碌时保持 `queued`;同一时刻只运行一个 Handler。排队任务可以取消,调用方不需要自己等待 Server Idle,`runtime.request()` 也不使用额外的 Idle 轮询补丁。
模型和 Environment 可以共享同一 Storage Root;共享磁盘目录不表示共享 Server 进程。多个进程可能同时下载同一模型时,业务层仍需要文件锁。

View File

@@ -23,7 +23,7 @@
Image Empty 引用视频或序列帧时,DA3 与 MoGe-2 会按 Empty 的 Image Frames 数值逐帧估计
深度(未设置时为完整帧数,显式设为 1 时只处理 1 帧),起始帧跟随 Empty 的 Offset,与背景蒙版范围一致;
DA3 会复用同一个 ONNX Session 对每帧独立推理,再合并为原有序列输出,避免多帧 Attention 的平方级显存占用。
该行为只用于 Depth Plane 与 Surface 转换;Debug 多帧输出仍联合推理。
该行为只用于 Depth Plane 转换;Surface 仅支持单张图片,Debug 多帧输出仍联合推理。
Offset 超出视频长度时同样把窗口收进视频内。材质节点与置换贴图的 Start 保持与原 Empty
一致;Original 模式的视频或序列帧使用 `frame.0001.jpg` 等原图序列作为颜色与深度,
`foreground.0001.png` 只提供前景 Alpha。

View File

@@ -1,57 +1,115 @@
# 开发与许可证
# 程序设计、开发与许可证
## 开发
## 程序设计
```bash
uv run pytest tests -v
uv run python pack.py
SnapForm 分成 Blender 主进程与独立 Job Server 两侧。Blender 侧只负责界面、任务参数和 `.blend` 数据修改;模型下载、媒体准备、ONNX 推理和文件输出都在 Python 3.12 Environment 中运行。两侧由随扩展分发的 BlendJob wheel 连接,避免把 FastAPI、ONNX Runtime 等计算依赖载入 Blender Python。
```mermaid
flowchart LR
User["Image Empty / Debug Panel"] --> Operator["Blender Operator"]
Operator --> Runtime["JobRuntime"]
Runtime --> Server["JobServer FIFO Queue"]
Server --> Handler["SnapForm Job Handler"]
Handler --> Model["ModelManager / ONNX"]
Handler --> JobFiles["Job Directory"]
JobFiles --> Response["Operator response"]
Response --> Scene["Blender Scene"]
```
项目结构:
核心边界如下:
- `src/snapform/__init__.py` 是注册入口,集中注册 Blender 类型、`Scene.snapform_settings`、右键菜单与 Runtime
- `panel.py`、`preferences.py`、`properties.py` 只描述 UI 和 Blender 属性,不直接导入 Server 模型实现
- `operators.py` 将 Blender 状态转换为普通字典参数;Job 完成后在主线程导入图片、网格、材质和相机
- `blender/depth_plane.py` 与 `blender/surface.py` 实现 Image Empty 输入检查、几何构建和资产挂载
- `job_runtime.py` 以入口字符串声明独立 Server,并配置 Environment、Storage Root、BlendJob wheel 与首次安装后的推荐模型下载
- `server/__init__.py` 创建 `JobServer`、注册公开 Job,并把 `ModelManager` 注册为 Resource
- `server/media.py`、`background.py`、`da3.py`、`moge2.py` 负责业务流程;`onnx_*.py` 只处理模型输入、推理和输出恢复
- 根目录 `model_catalog.py` 是 Blender UI 与独立 Server 共用的纯数据声明;`server/model_download.py`、`server/model_manager.py` 分别负责下载和 Session 生命周期
- `server/geometry.py` 生成 NPZ、预览、点云和纹理网格;所有任务产物写入 Runtime 分配的 opaque-ID Job 目录
功能细节继续按主题维护:[Depth Plane](depth-plane.md)、[Surface](surface.md)、[输出格式](outputs.md) 和 [Job Server](server.md)。
## 项目结构
```text
.
├── AGENTS.md
├── README.md
├── pack.py
├── sync_models.py
├── docs/
├── src/
│ └── snapform/
│ ├── __init__.py
│ ├── blender_manifest.toml
│ ├── job_runtime.py
│ ├── operators.py
│ ├── panel.py
│ ├── preferences.py
│ ├── properties.py
│ ├── job_runtime.py
│ ├── blendjob/
│ │ ├── client.py
│ │ ├── controller.py
│ │ ├── environment.py
│ │ ├── installer/environment.py
│ │ ├── operator.py
│ │ ├── runner.py
│ │ ├── runtime.py
│ │ └── server.py
│ ├── assets/
│ │ └── O_SnapForm.blend
│ ├── blender/
│ │ ├── depth_plane.py
│ │ └── surface.py
│ └── server/
│ ├── __init__.py
│ ├── model_catalog.py
│ ├── model_download.py
│ ├── model_manager.py
│ ├── media.py
│ ├── geometry.py
│ ├── background.py
│ ├── moge2.py
│ ├── da3.py
│ ├── onnx_ben2.py
│ ├── onnx_da3.py
│ ├── onnx_moge2.py
│ └── onnx_runtime.py
└── tests/
│ ├── model_catalog.py
│ ├── server/
│ │ ├── __init__.py
│ │ ├── background.py
│ │ ├── da3.py
│ │ ├── geometry.py
│ │ ├── media.py
│ │ ├── model_download.py
│ │ ├── model_manager.py
│ │ ├── moge2.py
│ │ ├── onnx_ben2.py
│ │ ├── onnx_da3.py
│ │ ├── onnx_moge2.py
│ │ └── onnx_runtime.py
│ └── wheels/
│ └── blendjob-0.1.5-py3-none-any.whl
├── tests/
└── tools/
```
## 常见命令
在项目根目录使用 Git Bash:
```bash
uv sync
uv run pytest
uv run pytest tests/test_addon.py tests/test_manifest.py tests/test_pack.py -v
uv run python pack.py
```
`pack.py` 根据 `blender_manifest.toml` 分别生成 Windows、macOS 和 Linux 扩展包,例如 `dist/SnapForm.v0.12.3.windows-x64.zip`。更改 `src/snapform/wheels/` 中的本地 wheel 后,先刷新锁文件再测试:
```bash
uv lock --refresh
uv run pytest
```
## 资产构建与验证
`tools/` 脚本依赖 `bpy`,必须由 Blender 执行。设置本机 Blender 可执行文件后,可以重建或验证 `O_SnapForm.blend`:
```bash
BLENDER_BIN="/c/Program Files/Blender Foundation/Blender 4.5/blender.exe"
"$BLENDER_BIN" --background --python tools/build_geometry_nodes_asset.py
"$BLENDER_BIN" --background --python tools/validate_depth_plane_asset.py
"$BLENDER_BIN" --background --python tools/validate_geometry_nodes_asset.py
"$BLENDER_BIN" --background --python tools/validate_moge_depth_plane.py
"$BLENDER_BIN" --background --python tools/validate_moge_surface.py
```
只改 Python、文档或打包逻辑时不需要重建 `.blend` 资产;修改节点组、材质组或其接口时,应运行相应资产验证脚本,并再次运行完整测试。
## 上游与许可证
- Depth Anything 3 代码采用 Apache 2.0。
- BEN2 ONNX 模型采用其仓库声明的 Apache 2.0 License。
- 本 Extension 代码采用 GPL-3.0-or-later。
- 模型权重许可证由所选 Hugging Face 模型仓库决定,Extension 下载前会在界面显示。
- Depth Anything 3 代码采用 Apache 2.0
- BEN2 ONNX 模型采用其仓库声明的 Apache 2.0 License
- SnapForm Extension 代码采用 GPL-3.0-or-later
- 模型权重许可证由所选 Hugging Face 模型仓库决定,Extension 下载前会在界面显示

View File

@@ -36,6 +36,10 @@
组成三角面并写入 `mesh.glb`;每个输入帧生成一张独立纹理网格,不做多视图融合
- Image Empty 右键菜单可直接运行 DA3,根据 `mini_npz` 深度生成面片
- Image Empty 右键菜单可直接运行 BEN2,保留原图并换用新建的透明背景图片
- 缺少必需的 `MoGe-2 ViT-S Normal` 或 `BEN2 Base` 时,Image Empty 的 `SnapForm`
右键子菜单显示 `Download Required Models`;单次任务只下载缺失项,并在补齐前禁用
`Convert to Depth Plane`
- 菜单、模型面板和转换对话框直接检查 Storage Root 的 `models/` 必需文件,显示模型状态时不会启动 Job Server
- Image Empty 支持视频与序列帧输入:`Convert to Depth Plane` 逐帧估计深度,
`Remove Background` 逐帧抠图,入口保持不变。DA3 转换时每帧独立执行 ONNX 推理并复用模型 Session;
Debug 多帧任务继续联合推理,以保留跨视图相机与深度关系
@@ -51,14 +55,14 @@
## 使用
1. 运行 `uv run python pack.py`,分别构建 `windows-x64`、`macos-arm64`、
`linux-x64` 三个平台的 Extension ZIP(如 `SnapForm.v0.11.2.macos-arm64.zip`)。
`linux-x64` 三个平台的 Extension ZIP(如 `SnapForm.v0.12.3.macos-arm64.zip`)。
安装包不携带 uv 或 AI 项目源码;首次安装 Environment 时下载固定版本 uv,官方源建连超过 3 秒后回退 OmooLab 镜像,并校验固定 SHA-256。
2. 在 Blender 的 `Preferences > Extensions` 中选择 `Install from Disk`。
3. 在 Extension Preferences 中确认 Storage Root 与 Device。Device 是正式功能
(Image Empty 右键菜单)使用的推理设备,默认取本机最快的 DirectML/CoreML provider,否则 CPU,且只列出
本机支持的设备。
4. 在 `Job Server` 点击 `Install Environment`。首次安装会下载 Python、ONNX Runtime、FastAPI、Uvicorn;
uv 由 BlendJob Environment 安装器下载并校验,DA3、BEN2 与 MoGe 不安装源码包,
uv 由 Job Runtime 的 Environment 安装器下载并校验,DA3、BEN2 与 MoGe 不安装源码包,
并在 Environment 安装后自动下载 `MoGe-2 ViT-S Normal` 和 FP16 `BEN2 Base` 推荐模型。首次安装
依次显示 `Environment`、`MoGe-2 ViT-S Normal` 与 `BEN2 Base`;每项都独立从 0 走到 100%,
进入下一项时进度条归零,模型 post-install 不显示 `2/2` 等阶段编号。uv、Python Environment 与 packages 合并在第一轮,
@@ -68,10 +72,12 @@
`Downloading DA3 Small 14.2 MB/s`;镜像来源显示为
`Downloading DA3 Small (mirror) 14.2 MB/s`。下载按字节连续推进,重试或切换镜像时进度不会倒退。
Environment 安装阶段与 `Models` 面板单独下载使用相同的模型名称格式,不会改写为通用的 `Downloading packages`。
Environment 安装后即使模型下载中断也不影响 Server 使用,可稍后在 `Models` 面板单独补下。
安装不做启动网络预检;每个模型都先尝试 Hugging Face,失败后尝试 OmooLab R2,两者都失败时当前 Job 失败。
下载源不写入持久状态;需要手动指定依赖镜像时可通过
`UV_INDEX_URL` 环境变量覆盖。
Environment 安装后即使模型下载中断也仍会保留,但 `Install Environment` 会以失败结束并显示模型下载错误。
可稍后在 Image Empty 的 `SnapForm` 右键菜单用 `Download Required Models` 一次补齐缺失的
MoGe-2 ViT-S 与 BEN2,也可在 `Models` 面板单独补下。
安装不做启动网络预检;每个模型都先尝试 Hugging Face,失败后尝试 OmooLab R2,两者都失败时当前 Job 失败。
下载源不写入持久状态;需要手动指定依赖镜像时可通过
`UV_INDEX_URL` 环境变量覆盖。
5. 在 3D View 按 `N`,打开 `SnapForm` 页签。
6. 如需其他模型,展开 `Models` panel,找到模型并点击该行的 `Download`。
7. 展开默认收起的 `Debug`,选择 `Output Type` 和本次执行使用的模型。

View File

@@ -7,5 +7,4 @@
- [Surface](surface.md):透视网格、Geometry Nodes 后处理与材质法线
- [输出格式](outputs.md):Debug 输出类型、参数和任务产物
- [Job Server](server.md):Environment、模型缓存、任务目录与运行机制
- [BlendJob 开发者指南](blendjob.md):可复用 Job Server、Blender Client 与装饰器注册 API
- [开发与许可证](development.md):开发验证、打包、上游版本与许可证
- [程序设计、开发与许可证](development.md):模块边界、调用流程、项目结构、常用命令与许可证

View File

@@ -2,7 +2,7 @@
## 运行架构
SnapForm 在独立的 Python 3.12 Environment 中运行 HTTP Job Server,Blender 只通过标准库 HTTP Client 提交和轮询任务,不把计算依赖载入 Blender Python。Server 只监听 `127.0.0.1`,由 BlendJob `ServerController` 保存随机端口与 Instance ID;专属本地进程协议不使用额外 Token 鉴权。
SnapForm 在独立的 Python 3.12 Environment 中运行 HTTP Job Server,Blender 只通过标准库 HTTP Client 提交和轮询任务,不把计算依赖载入 Blender Python。Server 只监听 `127.0.0.1`,由外置 BlendJob wheel 提供的 `ServerController` 保存随机端口与 Instance ID;专属本地进程协议不使用额外 Token 鉴权。
每个 Blender 实例启动并持有一个 SnapForm 专属 Server,不发现或复用其它 Blender、其它插件启动的本地 Server。多个 Blender 可以对应多个进程,但模型、Environment 与任务产物仍可共享同一个 Storage Root。
@@ -15,27 +15,28 @@ Environment、模型和任务结果默认保存在 `~/.snapform`:
├── models/
├── jobs/
├── manifest.json
├── environment.log
├── environment-status.json
├── install.log
├── install-status.json # 仅在安装期间存在
└── server.log
```
不同 Blender 版本可共享此目录。也可以在 Extension Preferences 的 `Storage Root` 中修改位置。连接端口与 Instance ID 只保存在启动它们的 Blender 进程内存中。
通用 HTTP 层位于 `blendjob` module。`JobServer` 包装 FastAPI 并提供 Job、FIFO Queue 与 Resource API,`JobClient` 是无第三方依赖的 Blender 侧访问器。SnapForm 的 `server/__init__.py` 直接导出 `server` 实例、集中注册全部公开 Job,并把 `ModelManager` 注册为延迟创建的 `model_manager` Resource。完整接入方式见 [BlendJob 开发者指南](blendjob.md)。
通用 HTTP 层由 `src/snapform/wheels/` 中的 BlendJob package 提供。`JobServer` 包装 FastAPI 并提供 Job、FIFO Queue 与 Resource API,`JobClient` 是无第三方依赖的 Blender 侧访问器。Blender 侧只把 `server/__init__.py:server` 入口字符串交给 Runtime,不导入 `server` package;独立进程启动后才取得其中的 `server` 实例、注册全部公开 Job,并创建 `model_manager` Resource。
SnapForm 根 package 不再有 `client.py`、`environment.py`、`storage.py` 或 `models.py`。这些文件会制造并不存在的业务边界:
- Storage Root、`.venv`、Manifest、安装状态与日志路径由 `BlenderJobRuntime` 唯一管理;
- Storage Root、`.venv`、Manifest、安装状态与日志路径由 `JobRuntime` 唯一管理;
- `InstallEnvironment` 由 `runtime.operator_classes()` 提供,Python 与 packages 直接声明在传给 Runtime 的 `environment` 字典中;
- 模型 catalog、磁盘目录、下载和 ONNX Session 都属于 Server,分别位于 `model_catalog.py`、`model_download.py` 与 `model_manager.py`;
- Blender UI 只读取 `model_manager` Resource Snapshot,不导入模型 catalog,也不检查模型文件。
- 模型 catalog 由 `model_catalog.py` 统一声明;下载和 ONNX Session 属于 Server,分别位于 `model_download.py` 与 `model_manager.py`;
- Blender UI 复用同一 catalog,直接检查 Storage Root 下的模型必需文件,不为显示下载状态启动 Server;已加载 Session 状态仍读取现有 Server Snapshot。
`blendjob/client.py` 仍然存在,但它只是 BlendJob 内部的无依赖 HTTP transport,不是 SnapForm 业务抽象,也不向 Operator 暴露 `JobRequest`。
BlendJob 的 `JobClient` 只是无依赖 HTTP transport,不是 SnapForm 业务抽象,也不向 Operator 暴露 `JobRequest`。
SnapForm 的业务 Operator 直接提交参数字典,Job Type 一一对应:
- `download_model` → `download-model`
- `download_required_models` → `download-required-models`
- `remove_background` → `remove-background`
- `extract_frames` → `extract-frames`
- `generate_da3_depth` → `generate-da3-depth`
@@ -62,7 +63,7 @@ Server 端没有 `execute_*`、`*_result` 或 CLI worker 中转。全部 `@serve
Windows 当前发行环境提供 DirectML 与 CPU;`Auto` 优先使用 `DmlExecutionProvider`。显式指定不可用设备时会安全回退到 CPU。其他平台安装标准 ONNX Runtime,并按可用 provider 选择 CoreML、OpenVINO 或 CPU。
`environment.json` 保存规范化后的声明,`manifest.json` 保存该声明与 BlendJob 基础 Server packages 的 Hash。Python、packages、平台 packages 或 BlendJob Environment Schema 改变后需要重新执行 `Install Environment`,不再维护手工 Manifest Revision。
`environment.json` 保存规范化后的声明,`manifest.json` 保存该声明、BlendJob wheel 与基础 Server packages 的 Hash。Python、packages、平台 packages 或 Environment Schema 改变后需要重新执行 `Install Environment`,不再维护手工 Manifest Revision。
## 模型
@@ -72,11 +73,16 @@ Windows 当前发行环境提供 DirectML 与 CPU;`Auto` 优先使用 `DmlExec
- DA3 Small、DA3 Base
- BEN2 Base
首次安装在 `post_install(runtime)` 中自动下载 `MoGe-2 ViT-S Normal` 与 FP16 `BEN2 Base`。它们和 `Models` panel 的后续下载一样,都是 `download-model` Job,与推理共用队列、HTTP 进度和取消协议。模型按声明的目录名保存在 `~/.snapform/models/`,并保留仓库内部结构。声明了文件清单与校验值的模型先走 Hugging Face `resolve` 直链流式下载,失败后尝试 `https://models.omoolab.xyz/<repository-name>/<relative-path>`;两者都失败时当前 Job 直接失败,不记录或猜测后续下载源。建连成功后不限制模型文件读取时长。R2 文件会校验大小与 SHA-256。未声明清单的仓库仍使用 `huggingface_hub` 快照下载。可用 `HF_ENDPOINT` 指定 Hugging Face 镜像。
首次安装在 `post_install(runtime)` 中自动下载 `MoGe-2 ViT-S Normal` 与 FP16 `BEN2 Base`。它们和 `Models` panel 的后续下载一样,都是 `download-model` Job,与推理共用队列、HTTP 进度和取消协议。模型按声明的目录名保存在 `~/.snapform/models/`,并保留仓库内部结构。声明了文件清单与校验值的模型先走 Hugging Face `resolve` 直链流式下载,失败后尝试 `https://models.omoolab.xyz/<repository-name>/<relative-path>`;两者都失败时当前 Job 直接失败,不记录或猜测后续下载源。模型直链的 TCP、代理 CONNECT 与 TLS 建连最长等待 10 秒;连接后连续 60 秒没有收到响应数据则视为当前来源失败并切换下一来源。该空闲超时不限制模型总下载时长。R2 文件会校验大小与 SHA-256。未声明清单的仓库仍使用 `huggingface_hub` 快照下载。可用 `HF_ENDPOINT` 指定 Hugging Face 镜像。
`model_manager` Resource 对外统一表示模型系统,持有模型 catalog、磁盘就绪状态和内存 Session。Blender 不拼接模型目录,也不直接检查权重文件。`ModelSessionCache` 保留 BEN2,并在 DA3 与 MoGe-2 之间只保留最近使用的一种 Depth Session。`GET /resources/model_manager` 返回 catalog、下载状态和当前加载状态;`POST /resources/model_manager/clear` 只允许在没有 Active 或 Queued Job 时执行。
Image Empty 的右键菜单会读取 `model_manager` 状态;缺少 MoGe-2 ViT-S 或 BEN2 时显示
`Download Required Models`。对应的 `download-required-models` Job 在 Server 端再次检查就绪状态,
只下载缺失项,并把多个下载的进度合并为单次 0–100%。必需模型补齐前,右键菜单中的
`Convert to Depth Plane` 保持禁用。
DA3 ONNX 使用模型的 camera decoder 恢复外参与内参;旧版 `Use Ray Pose` 参数只为任务协议兼容保留,不参与 ONNX 推理。MoGe-2 的 metric depth、normal、mask、normalized intrinsics,以及 BEN2 的 Alpha,均在 NumPy 后处理中恢复。
`model_manager` Resource 在 Server 内持有模型 catalog、磁盘就绪状态和内存 Session。Blender UI 使用 catalog 声明的目录名与 `ready_patterns` 直接检查 `<storage-root>/models/`,因此展开模型面板、打开转换对话框或 Image Empty 右键菜单都不会仅为状态检测启动 Server。真正下载或推理时,Server 仍通过 `ModelManager.ready()` 重新验证文件。`ModelSessionCache` 保留 BEN2,并在 DA3 与 MoGe-2 之间只保留最近使用的一种 Depth Session。`GET /resources/model_manager` 继续返回 catalog、下载状态和当前加载状态;`POST /resources/model_manager/clear` 只允许在没有 Active 或 Queued Job 时执行。
DA3 ONNX 使用模型的 camera decoder 恢复外参与内参,不提供 ray-pose 参数。MoGe-2 的 metric depth、normal、mask、normalized intrinsics,以及 BEN2 的 Alpha,均在 NumPy 后处理中恢复。
## 安装网络与镜像
@@ -85,7 +91,9 @@ DA3 ONNX 使用模型的 camera decoder 恢复外参与内参;旧版 `Use Ray
uv 默认从 Astral 的 GitHub Release 下载,失败后回退到 `https://mirror.omoolab.xyz/uv/<version>/`。两个来源使用相同的连续字节进度并共用固定 SHA-256 校验。下载压缩包只作临时文件,提取完成后删除。模型下载不经过 GitHub。
首次安装依次显示 Environment、MoGe-2 下载与 BEN2 下载,每项分别使用 0–100%。Environment 由 BlendJob 依次完成 uv 下载、Python Environment 创建与 packages 安装,uv 不再占用独立一轮进度。两个模型下载由 SnapForm 的 `post_install(runtime)` 通过 `runtime.request()` 顺序发起;切换模型时进度归零,但不显示 `2/2` 等阶段编号。
首次安装依次显示 Environment、MoGe-2 下载与 BEN2 下载,每项分别使用 0–100%。Environment 由 Job Runtime 依次完成 uv 下载、Python Environment 创建与 packages 安装,uv 不再占用独立一轮进度。两个模型下载由 SnapForm 的 `post_install(runtime)` 通过 `runtime.request()` 顺序发起;切换模型时进度归零,但不显示 `2/2` 等阶段编号。
任一 post-install 模型失败时,Environment 文件仍然保留,但安装 Operator 返回失败并显示聚合后的模型错误,
不再把带 `post_install_error` 的终态报告为成功。
## 生命周期
@@ -93,19 +101,19 @@ uv 默认从 Astral 的 GitHub Release 下载,失败后回退到 `https://mirr
AI、帧提取和 Models panel 下载的 Job 进度保存在 Server 内存中。Handler 只使用 `context.progress(value, message)` 发布进度,终态由 JobServer 内部管理;SnapForm 不再维护独立的 `server/progress.py` 或 Job 状态 JSON。Blender 每 0.25 秒轮询 `GET /jobs/{job_id}`,任务目录不写 `status.json`。Operator 直接提交参数字典;Job 被接受时由 `JobServer` 自动生成 ID 并创建 `jobs/<opaque-job-id>/`,提交响应同时返回 `job_id` 与 `directory`。路径通过 `JobContext.directory` 传给 Handler,SnapForm Server 强制将输出写入该目录。
`job_runtime.py` 创建唯一的 `runtime = BlenderJobRuntime(...)`,直接接收 `server` 实例并导出 `runtime.JobOperatorBase`。Runtime 独占 Storage Root、Environment 路径、安装进程、Server 控制和 Status Bar。Operator 未覆盖 `request(context)` 时会提交全部 Property;覆盖后返回值纯替换默认参数。可选的 `response(context, result)` 在 Blender 主线程直接完成结果导入。SnapForm 不再提供 Client 请求层、Job Mixin、Status Bar 或 Build 中转 Operator。
`job_runtime.py` 创建唯一的 `runtime = JobRuntime(...)`,以自身目录为 `entrypoint_root`,传入 `server:server` 相对入口字符串与显式 `snapform` namespace,并导出 `runtime.JobOperatorBase`。Runtime 独占 Storage Root、Environment 路径、安装进程、Server 控制和 Status Bar。Operator 未覆盖 `request(context)` 时会提交全部 Property;覆盖后返回值纯替换默认参数。可选的 `response(context, result)` 在 Blender 主线程直接完成结果导入。SnapForm 不再提供 Client 请求层、Job Mixin、Status Bar 或 Build 中转 Operator。
Environment 安装是 Server 启动前唯一的 Bootstrap 流程,使用 Storage Root 下共享的 `environment-status.json` 和 `environment.log` 回传进度,不创建 Job 目录。Environment 成功后执行普通的 `post_install(runtime)`;默认模型下载此时已经是正式 Server Job,会创建自己的 Job 目录,并使用与其它任务相同的进度和取消协议。
Environment 安装是 Server 启动前唯一的 Bootstrap 流程:`environment.json` 记录期望安装的环境声明,`install-status.json` 在安装期间回传进度并在任务收尾后删除,`install.log` 持久记录安装输出。Bootstrap 不创建 Job 目录。Environment 成功后执行普通的 `post_install(runtime)`;默认模型下载此时已经是正式 Server Job,会创建自己的 Job 目录,并使用与其它任务相同的进度和取消协议。
Server 使用标准库单 Worker FIFO Queue,全局同时只执行一个 Job,包括模型下载。Server 忙碌时新 Job 进入 `queued`,不再返回 409;排队任务可以在执行前取消。取消运行中任务不会重启 Server;流式模型下载会在分块检查点结束,ONNX Runtime 当前调用则在返回后的最近检查点结束。JobServer 最多保留 100 个已完成 Job 的内存状态,后续提交时清理更早记录。
启动与崩溃信息写入 `~/.snapform/server.log`。
Server 由 Environment 通过 `python -m blendjob.runner` 启动,避免 BlendJob 内部模块名遮蔽 Python 标准库。启动与崩溃信息写入 `~/.snapform/server.log`。
## 已知限制
DA3 ONNX 的注意力是朴素实现(`MatMul → Softmax → MatMul`),峰值显存与 `(帧数 × 每帧 patch 数)²` 成正比。迁移到 ONNX 前的 PyTorch 版使用 `scaled_dot_product_attention`(`XFORMERS_DISABLED=1`),按块计算注意力分数、不落地完整注意力矩阵,显存近似线性,因此视频能处理几十上百帧;ONNX 导出把 SDPA 回退成朴素注意力,多帧在中高分辨率下会显存爆炸(约 12 帧 @ 1008 即需 ~90 GB)。
Depth Plane 与 Surface 的视频、序列帧转换会复用同一个 ONNX Session 逐帧独立推理,再把结果合并到现有序列输出,因此峰值显存只取决于单帧分辨率。逐帧结果不保留 DA3 的跨视图尺度与相机位姿一致性,适用于当前逐帧动画结构。Debug 多帧输出仍使用联合推理,以保留多视图关系。
Depth Plane 的视频、序列帧转换会复用同一个 ONNX Session 逐帧独立推理,再把结果合并到现有序列输出,因此峰值显存只取决于单帧分辨率。逐帧结果不保留 DA3 的跨视图尺度与相机位姿一致性,适用于当前逐帧动画结构。Surface 只支持单张图片;Debug 多帧输出仍使用联合推理,以保留多视图关系。
联合推理时,`onnx_da3.infer` 会在执行前按 `views × patches` 预估注意力张量,超过 4 GiB 直接报错并提示减少帧数或 Resolution Scale,避免 onnxruntime 报出难以定位的分配失败或 `UnicodeDecodeError`(DirectML 在中文 Windows 下报错信息为 GBK 编码,被 UTF-8 解码掩盖)。

View File

@@ -166,9 +166,9 @@ Image Empty 使用 Packed Image 时,SnapForm 会通过 Blender 写出临时 PN
`DA3 Nested Giant Large 1.1`。Small 和 Base 没有 Gaussian head,无法由
DA3 生成 `gs_ply`。
每个 Server Job 被接受时,BlendJob 在 Runtime Storage Root 的 `jobs/` 下创建一个独立文件夹。文件夹名是 32 位 opaque Job ID,不包含任务类型或对象名称。执行逻辑从 `JobContext.directory` 获取路径,输出产物直接写入该目录。
每个 Server Job 被接受时,Job Runtime 在 Runtime Storage Root 的 `jobs/` 下创建一个独立文件夹。文件夹名是 32 位 opaque Job ID,不包含任务类型或对象名称。执行逻辑从 `JobContext.directory` 获取路径,输出产物直接写入该目录。
Blender 为 Packed Image 或序列帧生成的输入副本先保存在系统临时目录,Server 读取完成后由 Blender 清理,不归档到 Job 目录。普通磁盘文件和视频仍直接读取原文件。Server 日志统一写入 `server.log`,进度只保存在 Server 内存中。Environment Bootstrap 使用 Storage Root 下的 `environment.log` 与 `environment-status.json`,不创建 Job 目录。
Blender 为 Packed Image 或序列帧生成的输入副本先保存在系统临时目录,Server 读取完成后由 Blender 清理,不归档到 Job 目录。普通磁盘文件和视频仍直接读取原文件。Server 日志统一写入 `server.log`,进度只保存在 Server 内存中。Environment Bootstrap 使用 Storage Root 下的 `install.log` 与临时的 `install-status.json`,不创建 Job 目录。
与几何输出相关的 Job Type 为 `generate-da3-depth`、`generate-moge2-depth`、`generate-da3-surface`、`generate-moge2-surface`、`debug-da3` 与 `debug-moge2`。每种 Job 只接收对应模型与输出所需的参数。