Files
AnyImage/docs/server.md
nan c5b77c5c85 Add ONNX model inference implementations for MoGe-2, BEN2, and DA3
- Introduced `onnx_moge2.py` for MoGe-2 ONNX inference with NumPy post-processing.
- Added `onnx_ben2.py` for BEN2 ONNX inference with Pillow and NumPy preprocessing.
- Created `onnx_da3.py` for Depth Anything 3 inference with ONNX Runtime and NumPy.
- Implemented shared ONNX Runtime session helpers in `onnx_runtime.py`.
- Developed unit tests for server functionalities in `test_server.py`, including model caching and job management.
- Enhanced job server to handle frame extraction without requiring models.
2026-08-17 10:37:55 +08:00

11 KiB
Raw Blame History

Job Server

运行架构

SnapForm 在独立的 Python 3.12 Environment 中运行 HTTP Job Server,Blender 只通过标准库 HTTP Client 提交和轮询任务,不把计算依赖载入 Blender Python。Server 只监听 127.0.0.1,由 BlendJob ServerController 保存随机端口与 Instance ID;专属本地进程协议不使用额外 Token 鉴权。

每个 Blender 实例启动并持有一个 SnapForm 专属 Server,不发现或复用其它 Blender、其它插件启动的本地 Server。多个 Blender 可以对应多个进程,但模型、Environment 与任务产物仍可共享同一个 Storage Root。

Environment、模型和任务结果默认保存在 ~/.snapform:

~/.snapform/
├── .venv/
├── environment.json
├── models/
├── jobs/
├── manifest.json
├── environment.log
├── environment-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 开发者指南。

SnapForm 根 package 不再有 client.py、environment.py、storage.py 或 models.py。这些文件会制造并不存在的业务边界:

  • Storage Root、.venv、Manifest、安装状态与日志路径由 BlenderJobRuntime 唯一管理;
  • 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,也不检查模型文件。

blendjob/client.py 仍然存在,但它只是 BlendJob 内部的无依赖 HTTP transport,不是 SnapForm 业务抽象,也不向 Operator 暴露 JobRequest。

SnapForm 的业务 Operator 直接提交参数字典,Job Type 一一对应:

  • download_model → download-model
  • remove_background → remove-background
  • extract_frames → extract-frames
  • generate_da3_depth → generate-da3-depth
  • generate_moge2_depth → generate-moge2-depth
  • generate_da3_surface → generate-da3-surface
  • generate_moge2_surface → generate-moge2-surface
  • debug_da3 → debug-da3
  • debug_moge2 → debug-moge2
  • debug_ben2 → debug-ben2

DA3 与 MoGe-2 的生产任务不再合并成带 Backend 参数的通用请求。每个 Operator 的 request(context) 直接构造自己的有效参数,Debug 任务也与生产任务完全分开。

Server 端没有 execute_*、*_result 或 CLI worker 中转。全部 @server.job(...) 都集中在 server/__init__.py:简单任务直接实现,复杂任务调用按领域拆分的 media.py、background.py、moge2.py 或 da3.py;共享的纯几何计算位于 geometry.py。模型下载由 ModelManager 单向调用 model_download.py,不存在循环 import。

ONNX-only Environment

扩展不内置 uv 可执行文件或 AI 运行库。Install Environment 首先下载固定版本的 uv,校验 SHA-256 后解压到共享 Storage Root,再用它创建环境并安装:

  • ONNX Runtime 1.24.4;Windows 使用 onnxruntime-directml
  • NumPy 2.4.2、Pillow 12.1.1、PyAV 17.0.0、trimesh 4.11.2
  • FastAPI 0.139.2、Uvicorn 0.51.0、huggingface-hub 1.4.1

运行环境不安装 PyTorch、TorchVision、xFormers,也不安装 DA3、MoGe、BEN2 或 utils3d 的 Python 源码包。模型推理和 SnapForm 所需的相机、点云、网格后处理均由 ONNX Runtime、NumPy 与内部实现完成。

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。

模型

所有正式功能都使用 ONNX:

  • MoGe-2 ViT-S、ViT-B、ViT-L Normal
  • 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 镜像。

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 时执行。

DA3 ONNX 使用模型的 camera decoder 恢复外参与内参;旧版 Use Ray Pose 参数只为任务协议兼容保留,不参与 ONNX 推理。MoGe-2 的 metric depth、normal、mask、normalized intrinsics,以及 BEN2 的 Alpha,均在 NumPy 后处理中恢复。

安装网络与镜像

安装器直接开始工作,不做阻塞式网络预检。packages 由 uv 安装,可用 UV_INDEX_URL 显式指定镜像。Windows 系统代理会注入子进程;同时支持 HTTP_PROXY、HTTPS_PROXY 与 ALL_PROXY。

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 等阶段编号。

生命周期

扩展启用后不启动 Server;第一次执行 Job 或点击 Start 时才为当前 Blender 启动专属实例。正常关闭或禁用扩展时直接停止自己启动的进程;Server 也监视 Blender 父进程 PID,父进程异常退出后会自行停止。

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。

Environment 安装是 Server 启动前唯一的 Bootstrap 流程,使用 Storage Root 下共享的 environment-status.json 和 environment.log 回传进度,不创建 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。

已知限制

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 多帧输出仍使用联合推理,以保留多视图关系。

联合推理时,onnx_da3.infer 会在执行前按 views × patches 预估注意力张量,超过 4 GiB 直接报错并提示减少帧数或 Resolution Scale,避免 onnxruntime 报出难以定位的分配失败或 UnicodeDecodeError(DirectML 在中文 Windows 下报错信息为 GBK 编码,被 UTF-8 解码掩盖)。

待解决方向:

  • 重新导出 DA3 ONNX,使用 ORT 的 Attention / MultiHeadAttention 贡献算子,或注入内存高效的注意力算子;
  • 若未来需要统一坐标系的视频重建,可按小窗口分批推理,再对齐相机位姿与深度尺度。