Files
AnyImage/README.md
nan a503786c62 Refactor and enhance SnapForm functionality
- Introduced MODEL_NUMBERS for better model indexing.
- Improved enum_items function to cache results and handle download status.
- Updated ActiveJob class to manage temporary input paths and added displacement_via property.
- Changed IMAGE_DEPTH_PROCESS_RES_METHOD from "lower_bound_resize" to "upper_bound_resize".
- Enhanced RunJob operator to include new properties and improved error messages.
- Modified panel layout for better user experience and added runtime installation checks.
- Updated SnapFormSettings to reflect new model properties and defaults.
- Added temporary input path management in runtime.
- Refined worker functions to improve error handling and messaging.
- Expanded tests to cover new functionality and ensure backward compatibility.
- Fixed packaging script to align with versioning conventions.
2026-08-03 13:47:15 +08:00

241 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SnapForm
`SnapForm` 是一套从图片快速构建空间结构的 Blender Extension。目前以
[Depth Anything 3](https://github.com/ByteDance-Seed/Depth-Anything-3)
作为首个 AI 能力,在独立 Runtime 中运行 DA3,可将官方导出结果保存在独立 job 目录,
也可把 Image Empty 转换成带几何与材质置换的立体面片。
## 当前功能
- 使用 Blender Extension manifest,可通过 `Install from Disk` 安装
- 在独立 Python Runtime 中运行 Torch 与 DA3,避免污染 Blender Python 进程
- Runtime、模型和任务结果默认保存在 `~/.snapform`,可由多个 Blender 版本共享
- 支持 DA3 Small、Base、Large 1.1、Giant 1.1 和 Nested Giant Large 1.1
- 支持单张 JPG、JPEG、PNG、WEBP 图片或常见视频文件输入
- 视频均匀抽帧,可设置参与重建的最大帧数
- `Setup` 集中管理 Runtime,并逐项列出和下载所有可用模型
- Runtime 已就绪时不额外占用面板空间;模型列表每个模型只占一行
- `Debug` 是独立且默认折叠的 DA3 测试面板
- 使用一个导出类型下拉菜单和一个 `Export` 按钮
- 只显示当前格式的专属参数;GLB 与 PLY 设置不会同时出现
- Debug 的模型选项按格式过滤;PLY 只提供两个带 Gaussian head 的 Giant 模型
- Debug 与 Convert to Depth Plane 的模型选项直接显示下载状态;未下载模型带
`(Missing)`,已下载模型保持原名
- 分别调用 DA3 的 `glb`、`gs_ply`、`depth_vis` 和 `mini_npz` exporter
- Image Empty 右键菜单可直接运行 DA3,根据 `mini_npz` 深度生成面片
- 使用浮点深度贴图、Simple Subdivision、Displace Modifier 和原图 Bump
构建立体面片
- 转换对话框可选择推理模式、处理分辨率和面片网格细分
- Debug 导出完成后只打开对应 job 文件夹,不向当前 Blender 文件添加任何数据
- 完整保留每次任务生成的所有官方文件
- 安装、模型下载和推理均在后台子进程运行,状态栏实时显示阶段、百分比进度条与取消按钮
- 后台任务运行时,Install、Download 和 Export 按钮保持原位置并变灰,任务完成后恢复
## 使用
1. 使用 `uv run python pack.py` 构建 Extension ZIP。
2. 在 Blender 的 `Preferences > Extensions` 中选择 `Install from Disk`。
3. 在 Extension Preferences 中确认 `uv` 路径和 Torch Backend。
4. 点击 `Install Runtime`。首次安装会下载 Python、PyTorch 和 DA3 依赖。
5. 在 3D View 按 `N`,打开 `SnapForm` 页签。
6. 在 `Setup` 中找到需要的模型并点击该行的 `Download`。
7. 展开默认收起的 `Debug`,选择 `Export Format` 和本次执行使用的模型。
8. 选择一张图片或一个视频文件。视频可设置均匀抽取的帧数,然后点击 `Export`。
## Image Empty 转深度面片
1. 在 3D View 选中 `Object Type = Empty`、`Display As = Image` 的对象。
2. 右键选择 `Convert to Depth Plane`,通过 `Depth from` 选择深度来源:
- `AI Generated`:显示 Model、Use Ray Pose 和 Resolution Scale。实际 Process Res 为图片长边像素数
乘以 Resolution Scale;范围为 `0.1–1.0`,默认 `0.5`。计算后的 Process Res 会直接显示在
`Resolution Scale (… px)` 标签中。
转换导出 `mini_npz`,使用前需要在
`Setup` 下载所选模型,并固定使用 `process_res_method="upper_bound_resize"`;设备和参考视图策略
继续使用场景设置。
- `Original`:直接把原图作为深度贴图,不显示估算参数,也不启动 Runtime 或要求源图片存在于磁盘。
`Original` 是默认深度来源。选择 `AI Generated` 时,其 Model、Use Ray Pose 和 Resolution Scale
参数会集中显示在独立边框中。两种模式始终显示 Displace via、Subdivision 与 Delete Original。
Displace via 可选 `Modifier`(默认)或 `Material`;
Subdivision 默认为 `5`,并默认删除原对象。
3. AI Generated 完成后会自动创建面片。面片几何先由 `mini_npz` 的相机内参与深度确定,再通过对象
Transform 匹配原 Image Empty 的可见尺寸、位置、旋转和 Offset。Original 模式直接使用源 Empty
的可见尺寸、Offset 与 Transform。开启 `Delete Original` 时,成功生成后
删除源 Empty。生成面片 Object 使用源 Empty 的原名;关闭 `Delete Original` 时不处理
同名冲突,由 Blender 自动为生成面片追加编号。
4. 短边分段数为 `2^Subdivision`,长边分段数根据图像宽高比取最接近的整数。例如高:宽为
`2:3`、Subdivision 为 `1` 时,直接生成 `2×3` 个面;Subdivision 为 `3` 时生成
`8×12` 个面。随后的 Simple Subdivision 修改器使用原生名称,Viewport Levels 固定为
`1`,Render Levels 固定为 `2`,不再随 Resolution Scale 或 Subdivision 动态变化。
所有任务使用统一的状态栏进度显示。变化中的阶段说明显示在右侧的紧凑进度条内部,取消按钮
位于进度条右侧。Image Depth 的阶段包括 Runtime 启动、输入准备、模型加载、深度估算、缓存
写出和面片构建。状态更新仅标记相关区域重绘,不强制执行窗口 Draw/Swap。
导入过程精确保留 NPZ 内深度值之间的差值。例如深度 `2.0` 和 `5.5` 在
`Displacement Strength = -1` 时,沿局部 Z 轴相差 `3.5 Blender Unit`。它不会把数值压缩到
`0..1`,也不再提供视觉归一化模式。
面片尺寸以有效深度的中位数 `Zref` 作为参考截面。先用 `mini_npz` 中的相机内参计算原始
投影面积,再恢复源图纵横比:
```text
RawWidth = depth_width × Zref × abs(Displacement Strength) / fx
RawHeight = depth_height × Zref × abs(Displacement Strength) / fy
Area = RawWidth × RawHeight
Aspect = source_image_width / source_image_height
Width = sqrt(Area × Aspect)
Height = sqrt(Area / Aspect)
```
这样即使 DA3 估计出的 `fx/fy` 不一致,也不会把正方形原图拉成长方形。主点 `cx/cy` 仍用于
确定原始相机面片,随后对象 XY Transform 将它匹配到源 Image Empty。生成对象会记录
`snapform_reference_depth`、`snapform_camera_plane_width` 和
`snapform_camera_plane_height`,便于检查实际采用的
参考深度和尺寸。
选择 `Modifier` 时,AI Generated 的 Displace Modifier 深度贴图使用 `Extension = Extend`,
`Strength` 固定为 `-1`、`Midlevel` 固定为 `1`。Original 把原图指定给 Displace Texture,并设置
`Texture Coordinates = UV`、`Midlevel = 0`;其余修改器设置保留 Blender 默认值。
选择 `Material` 时,不创建 Displace Modifier;深度 Image Texture 的 Color 连接到 Displacement
节点的 Height,再连接 Material Output 的 Displacement,并把材质 Surface Displacement 设置为
`Displacement Only`。AI Generated 使用 `Midlevel = 1`、`Scale = -1`;Original 使用
`Midlevel = 0`、`Scale = 1`,与对应修改器值一致。无论选择哪种呈现方式,原始彩色贴图都会连接
Principled BSDF 的 `Base Color`,并通过
`Bump.Height → Bump.Normal → Principled.Normal` 提供表面细节。Bump 的基础 Distance 为
`0.001`,会乘以面片 Transform 的统一缩放倍率。原图 Color 还会经过 `Float Curve` 连接到
Principled BSDF 的 `Roughness`,曲线两端输出分别为 `0.5` 和 `0.1`;材质 IOR 保持
Principled BSDF 默认值 `1.5`。
这里的“原始距离”是 DA3 模型输出的尺度。普通 Small、Base、Large 和 Giant 模型的深度不应
被解释为现实世界的米;需要现实尺度时应选择带 metric depth 的
`DA3 Nested Giant Large 1.1`。此外,当前结果仍是固定 XY 平面上的深度位移面,不是使用
相机内参逐像素反投影得到的完整 camera-space 网格;中位深度截面为现有 Displace Modifier
工作流提供正确的整体高宽尺度。
生成完成后会直接创建面片,不提供缓存重建入口或单独的导入参数。重新选择
`Convert to Depth Plane` 会按当前 Depth from 再次转换。
面片、深度 Image、Texture 与 Material 会在同一个 Blender Undo 步骤中创建;转换完成后执行
`Ctrl+Z` 会完整撤销本次转换,不会越过转换去回退更早的 Image Empty 操作。
Image Empty 使用 Packed Image 时,SnapForm 会通过 Blender 写出临时 PNG 交给 DA3,
不会 Unpack 或修改源 Image;任务结束后自动删除临时文件。普通图片仍直接读取源文件。DA3 的原始
`results.npz` 保留在对应 job 的 `exports/mini_npz/` 中,生成的浮点深度 Image 会 Pack 到
当前 `.blend`。Depth Image 与 Displace Texture 根据源 Image datablock 命名。例如 Empty Object
名为 `obj_img`、引用的 Image 名为 `img.png` 时,Depth Image 与 Texture 都命名为 `img.depth`;
生成的 Object、Mesh 与 Material 仍命名为 `obj_img`。Pack 使用 OpenEXR,因而不会把大于 `1.0`
的原始深度截断。已有同名 datablock 时由 Blender 自动追加 `.001` 等编号。
选择 PLY 后,模型菜单只会提供 `DA3 Giant 1.1` 和
`DA3 Nested Giant Large 1.1`。Small、Base 和 Large 没有 Gaussian head,无法由
DA3 生成 `gs_ply`。
每次任务会在 `~/.snapform/jobs/<任务目录>/` 中保留 DA3 官方产物:
```text
<GLB 任务目录>/
└── scene.glb
<PLY 任务目录>/
└── gs_ply/*.ply
<Depth 任务目录>/
└── depth_vis/*.jpg
<Mini NPZ 任务目录>/
└── exports/mini_npz/results.npz
```
建议从 `DA3 Base` 开始。它使用 Apache 2.0 模型权重;`DA3 Large 1.1` 权重采用
CC BY-NC 4.0,仅限非商业用途。
## 输出格式说明
`Export Format` 下拉菜单分别把以下原始 `export_format` 传给 DA3:
- GLB:`glb`
- PLY:`gs_ply`,同时传入 `infer_gs=True`
- Depth Images:`depth_vis`
- Mini NPZ:`mini_npz`
界面使用 DA3 API 的参数名、选项和默认值。GLB 专属设置仅在选择 `GLB` 时显示,
`gs_views_interval` 仅在选择 `PLY (3DGS)` 时显示:
- `process_res=1024`
- `process_res_method="upper_bound_resize"`
- `use_ray_pose=False`
- `ref_view_strategy="saddle_balanced"`
- `conf_thresh_percentile=40.0`
- `num_max_points=1_000_000`
- `show_cameras=True`
- `export_kwargs["gs_ply"]["gs_views_interval"]=1`
DA3 官方 `glb` 是彩色点云与可选相机线框;`gs_ply` 是带 Gaussian 属性的 PLY;
`depth_vis` 是原图与着色深度图的并排预览;`mini_npz` 保存深度、置信度和可用的相机参数。
GLB 任务会关闭其默认附带的深度预览,使四种 job 产物保持分离。
官方格式与限制可参阅
[DA3 Python API 文档](https://github.com/ByteDance-Seed/Depth-Anything-3/blob/main/docs/API.md)。
## Runtime
Extension 本身不包含 Torch。`Install Runtime` 使用 `uv` 创建独立 Python 3.12
环境,并安装:
- PyTorch 2.9.1
- TorchVision 0.24.1
- xFormers 0.0.33.post2(与 PyTorch 2.9.1 对应)
- 固定到已验证 commit 的 Depth Anything 3
- 固定到 DA3 官方推荐 commit 的 `gsplat`;仅安装 PLY 导出所需的纯 Python 部分,
不编译本机 CUDA rasterizer
- `addict` 2.4.0(补充 DA3 上游包元数据暂未声明的运行依赖)
默认 CUDA Backend 为 CUDA 12.8,也可以选择 CPU。CPU 可以用于验证,但实际推理
通常很慢。
Runtime、模型和任务结果默认保存在用户主目录的 `~/.snapform`:
```text
~/.snapform/
├── runtime/
├── models/
└── jobs/
```
不同 Blender 版本安装相同 Extension 后会共用这个目录。也可以在 Extension
Preferences 的 `Storage Root` 中修改共享位置。不要把模型写入 Extension 安装目录,
因为该目录可能只读,并且升级时会被替换。不要同时从多个 Blender 实例执行
`Install Runtime`;推理任务和模型读取可以共享。
## 开发
```bash
uv run python -m unittest discover -s tests -v
uv run python pack.py
```
项目结构:
```text
.
├── pack.py
├── src/
│ └── snapform/
│ ├── blender_manifest.toml
│ ├── bootstrap_runtime.py
│ ├── models.py
│ ├── image_depth.py
│ ├── operators.py
│ ├── panel.py
│ ├── preferences.py
│ ├── properties.py
│ ├── runtime.py
│ └── worker.py
└── tests/
```
## 上游与许可证
- Depth Anything 3 代码采用 Apache 2.0。
- 本 Extension 代码采用 GPL-3.0-or-later。
- 模型权重许可证由所选 Hugging Face 模型仓库决定,Extension 下载前会在界面显示。