Remove obsolete implementation and simplify project structure

This commit is contained in:
2026-08-21 09:46:50 +08:00
parent b5a2f9690c
commit af5603045c
100 changed files with 365 additions and 365 deletions

View File

@@ -28,8 +28,8 @@ jobs:
- name: 整理发布目录
run: |
mkdir -p public/snapform
cp -a site/. public/snapform/
mkdir -p public/anyimage
cp -a site/. public/anyimage/
- name: 配置 Node.js
uses: actions/setup-node@v6
@@ -50,5 +50,5 @@ jobs:
--name "$CLOUDFLARE_WORKER_NAME" \
--compatibility-date 2026-08-19 \
--keep-vars \
--message "SnapForm docs $GITHUB_SHA" \
--message "AnyImage docs $GITHUB_SHA" \
--autoconfig=false

View File

@@ -1,4 +1,4 @@
# SnapForm 开发约定
# AnyImage 开发约定
## 总则
@@ -15,7 +15,7 @@
├── docs/ # 按功能拆分的设计与开发文档
├── pack.py # 构建各平台 Blender Extension ZIP
├── sync_models.py # 准备并同步 ONNX 模型
├── src/snapform/
├── src/anyimage/
│ ├── __init__.py # Blender 注册入口
│ ├── operators.py # Job Operator 与结果导入
│ ├── panel.py # 3D View 面板
@@ -24,7 +24,7 @@
│ ├── job_runtime.py # BlendJob Runtime 配置
│ ├── blender/ # Depth Plane、Surface 的 Blender 侧实现
│ ├── server/ # Job、模型、ONNX 推理与输出实现
│ ├── assets/O_SnapForm.blend # Geometry Nodes 与材质资产
│ ├── assets/O_AnyImage.blend # Geometry Nodes 与材质资产
│ └── wheels/ # 随扩展分发的 BlendJob wheel
├── tools/ # Blender 资产构建与验证脚本
└── tests/ # Python 自动化测试
@@ -58,19 +58,19 @@ uv lock --refresh
## 命名规范
- 变量、函数、Docstring 使用英文;同一概念在代码和文档中使用统一表达
- Python 类使用大驼峰,不添加项目名前缀。Operator 使用动宾结构,如 `ConvertToDepthPlane`、`RemoveImageBackground`;Menu、Panel、AddonPreferences、PropertyGroup 分别使用实际类型后缀,如 `SnapFormImageMenu`、`ServerPanel`、`SnapFormPreferences`、`SnapFormSettings`
- `bl_idname` 与挂载到公共 Blender RNA 类型的 Property 使用 `snapform` 项目前缀:Operator 如 `snapform.convert_to_depth_plane`,Menu 如 `SNAPFORM_MT_image_context`,Panel 如 `SNAPFORM_PT_server`,公共 Property 如 `Scene.snapform_settings`
- Python 类使用大驼峰,不添加项目名前缀。Operator 使用动宾结构,如 `ConvertToDepthPlane`、`RemoveImageBackground`;Menu、Panel、AddonPreferences、PropertyGroup 分别使用实际类型后缀,如 `AnyImageImageMenu`、`ServerPanel`、`AnyImagePreferences`、`AnyImageSettings`
- `bl_idname` 与挂载到公共 Blender RNA 类型的 Property 使用 `anyimage` 项目前缀:Operator 如 `anyimage.convert_to_depth_plane`,Menu 如 `ANYIMAGE_MT_image_context`,Panel 如 `ANYIMAGE_PT_server`,公共 Property 如 `Scene.anyimage_settings`
- Operator 自身 Property 不加项目前缀,使用业务名称,如 `input_path`、`process_res`、`resolution_level`
- 扩展包名使用 Manifest 中的真实名称、版本和平台,例如 `SnapForm.v0.11.2.windows-x64.zip`;节点组名称沿用资产中的 `O DA3 Depth Plane`、`O MoGe2 Surface` 等既有名称
- 扩展包名使用 Manifest 中的真实名称、版本和平台,例如 `AnyImage.v0.11.2.windows-x64.zip`;节点组名称沿用资产中的 `O DA3 Depth Plane`、`O MoGe2 Surface` 等既有名称
## 代码规范
- 避免不必要的嵌套,优先使用提前返回和小而明确的函数
- 只为特殊、非常规或存在兼容性原因的实现写注释,不复述代码本身
- 删除功能时同时删除无效参数、分支、测试和文档,不保留没有实际消费者的兼容层
- Blender 侧不载入 AI 依赖:UI、Operator 和结果导入放在 `src/snapform/` 或 `src/snapform/blender/`,模型下载、ONNX 推理和输出放在 `src/snapform/server/`
- Blender 侧不载入 AI 依赖:UI、Operator 和结果导入放在 `src/anyimage/` 或 `src/anyimage/blender/`,模型下载、ONNX 推理和输出放在 `src/anyimage/server/`
- Job 参数使用可序列化的普通字典;Blender 数据只能在主线程的 Operator 响应阶段读写
- 新增或修改 Blender 类型时同步检查 `src/snapform/__init__.py` 的注册与逆序注销,并补充对应测试
- 新增或修改 Blender 类型时同步检查 `src/anyimage/__init__.py` 的注册与逆序注销,并补充对应测试
- 依赖版本、wheel 或 Manifest 改变时同步检查 `pyproject.toml`、`uv.lock`、`blender_manifest.toml` 和打包测试
## 文档与 Markdown 规范

View File

@@ -1,6 +1,6 @@
# SnapForm
# AnyImage
SnapForm 是一个 Blender 扩展,可以把一张照片、一段视频或一组序列帧快速变成有真实深度的立体面片。
AnyImage 是一个 Blender 扩展,可以把一张照片、一段视频或一组序列帧快速变成有真实深度的立体面片。
完整使用说明请从[文档首页](docs/index.md)开始;首次使用请依次阅读
[安装与首次配置](docs/installation.md)和[五分钟上手](docs/quick-start.md)。
@@ -22,14 +22,14 @@ SnapForm 是一个 Blender 扩展,可以把一张照片、一段视频或一
3. 点击 `Install from Disk`,选择下载的压缩包
4. 在扩展偏好设置中确认 Storage Root 与 Device(保持默认即可);Device 默认取本机
最快的 DirectML provider;Apple M4 或更新芯片可选 MPS/CoreML,其他 Mac 只提供 CPU
5. 打开 3D 视图右侧的 `SnapForm` 面板:使用官方源点击 `Install Environment`,使用国内镜像点击 `Install Environment (Mirror)`
5. 打开 3D 视图右侧的 `AnyImage` 面板:使用官方源点击 `Install Environment`,使用国内镜像点击 `Install Environment (Mirror)`
扩展不内置 uv 或 AI 运行库。首次使用会先下载并校验固定版本的 uv,随后自动安装轻量的 ONNX Runtime 环境,再下载 `MoGe-2 ViT-S Normal` 和
`BEN2 Base`;不安装 PyTorch,也不下载各模型的 Python 源码包。其他 AI 模型可在
`Models` panel 中按需下载,请保持网络畅通,首次安装需要一些时间。
Environment Bootstrap 使用连续的 0–100%:uv 为 1–20%、Python 为 20–50%、packages 为 50–98%,最终验证为 98–100%。三个阶段显示真实的 `1/3`、`2/3`、`3/3`,下载占各阶段的 84–87%;开始与最终验证不显示编号,单阶段状态不显示 `1/1`。官方按钮固定使用 GitHub、uv 默认 Python 下载源和 PyPI;镜像按钮固定使用 CNB uv Release、CNB Python 解释器镜像和清华 PyPI,两种方式都不竞速或自动切换。MoGe-2 与 BEN2 属于 post-install,各自使用独立进度,不计入 Environment 的这轮 0–100%,其模型下载源逻辑不受按钮选择影响。
任一必需模型在两个来源都下载失败时,`Install Environment` 会明确报错;Environment 本身仍会保留。
选中 Image Empty 后,右键 `SnapForm` 子菜单会在缺少 `MoGe-2 ViT-S Normal` 或 `BEN2 Base`
选中 Image Empty 后,右键 `AnyImage` 子菜单会在缺少 `MoGe-2 ViT-S Normal` 或 `BEN2 Base`
时显示 `Download Required Models`,一次补齐缺失项,不会重复下载已经就绪的模型。
Environment 未安装时,同一位置显示两个安装按钮。Environment 或必需模型未就绪时,
菜单仍显示 `Convert to Depth Plane`、`Convert to Surface` 和 `Remove Background`,但三个入口都会保持禁用。
@@ -41,7 +41,7 @@ Environment 未安装时,同一位置显示两个安装按钮。Environment
### 转换为立体面片
1. 在 3D 视图中选中一个 Image Empty(空物体,显示方式为 Image)
2. 右键打开 `SnapForm` 子菜单,选择 `Convert to Depth Plane`
2. 右键打开 `AnyImage` 子菜单,选择 `Convert to Depth Plane`
3. 在对话框中选择:
- `Depth from`:`Original` 直接用原图,`DA3` 使用现有 DA3 流程,`MoGe-2` 生成 metric depth、normal 与有效区域 mask
- `Resolution Scale` 使用 `1–100%`,按原图长边计算 Process Res;结果会限制在 `252–1008` 并自动对齐到 14 的倍数
@@ -52,7 +52,7 @@ Environment 未安装时,同一位置显示两个安装按钮。Environment
### 套索生成 Balloon
选中单张图片的 Image Empty,右键打开 `SnapForm` 子菜单并选择 `Balloon Lasso Tool`。Blender 会激活 3D Viewport 左侧工具栏底部、紧邻 Add Cube、且不增加分隔空隙的独立 Balloon Lasso,Poly Build 的绿色图标、Annotate 同款铅笔光标、Tool Header 与状态栏共同提示当前工具状态,不再用黄色外框或强制修改 Shading、Overlay。Blender 的公开 Workspace Tool API 不支持按 Active Object 动态隐藏工具,因此图标在 Object Mode 中保持可见,但只有 Active Object 是 Image Empty 时才能启动 Stroke。
选中单张图片的 Image Empty,右键打开 `AnyImage` 子菜单并选择 `Balloon Lasso Tool`。Blender 会激活 3D Viewport 左侧工具栏底部、紧邻 Add Cube、且不增加分隔空隙的独立 Balloon Lasso,Poly Build 的绿色图标、Annotate 同款铅笔光标、Tool Header 与状态栏共同提示当前工具状态,不再用黄色外框或强制修改 Shading、Overlay。Blender 的公开 Workspace Tool API 不支持按 Active Object 动态隐藏工具,因此图标在 Object Mode 中保持可见,但只有 Active Object 是 Image Empty 时才能启动 Stroke。
按住左键沿目标轮廓绘制,松开后生成一个带原图 UV 的封闭网格。每次拖动都是一个独立完成的顶层 `Extract Balloon` Operator,工具本身继续保持激活,下一次拖动会创建另一个 Balloon;因此 `Ctrl+Z` 只撤销最近一次生成,`Ctrl+Shift+Z` 重做。每次生成后源 Image Empty 都保持选中和 Active。未绘制时没有持续 Modal 拦截事件,视图导航和其他原生快捷键由 Blender 正常处理;绘制中可用右键或 `Esc` 取消当前轨迹。选择左侧工具栏中的其他工具即可离开 Balloon Lasso。
@@ -68,13 +68,13 @@ MoGe normal 会转换成适配 Balloon UV 的 tangent-space `normal.png`,由
### 去除背景
选中 Image Empty 后,右键打开 `SnapForm` 子菜单并选择 `Remove Background`,完成后 Empty 会自动
选中 Image Empty 后,右键打开 `AnyImage` 子菜单并选择 `Remove Background`,完成后 Empty 会自动
换成透明前景图。该操作在子菜单中使用独立分割区域。
### 转换为透视网格
选中单张图片的 Image Empty,右键打开 `SnapForm` 子菜单并选择 `Convert to Surface`。可选择 MoGe-2
ViT-S、ViT-B、ViT-L 或 DA3 Small、Base,其中 MoGe-2 ViT-S 为默认模型。SnapForm 会按模型输出的
选中单张图片的 Image Empty,右键打开 `AnyImage` 子菜单并选择 `Convert to Surface`。可选择 MoGe-2
ViT-S、ViT-B、ViT-L 或 DA3 Small、Base,其中 MoGe-2 ViT-S 为默认模型。AnyImage 会按模型输出的
相机内参与三维点建立透视网格,并匹配原 Empty 的可见尺寸、Offset 与 Transform。Server 通过统一的
Surface 输出流程构建 DA3 与 MoGe-2 源网格:MoGe-2 使用深度断层 mask,DA3 保留 confidence 与边拉伸
数据。两者再由独立的 Geometry Nodes
@@ -102,19 +102,19 @@ Layer Color 同时驱动 Principled BSDF 的 Base Color、Subsurface Radius 和
### 视频与序列帧
SnapForm 支持视频(mp4、mkv、mov 等)和序列帧,会逐帧处理并生成逐帧对应的立体面片。`.color` 与 `.depth` 按源 Empty Image 的类型载入:普通单图为 `IMAGE`,Movie/Sequence 即使只处理一帧也仍为 `SEQUENCE`。处理范围默认
AnyImage 支持视频(mp4、mkv、mov 等)和序列帧,会逐帧处理并生成逐帧对应的立体面片。`.color` 与 `.depth` 按源 Empty Image 的类型载入:普通单图为 `IMAGE`,Movie/Sequence 即使只处理一帧也仍为 `SEQUENCE`。处理范围默认
跟随 Empty 上设置的 `Image Frames` 与 `Offset`,不需要额外配置。DA3 的转换任务复用同一个 ONNX
Session 逐帧推理,显存峰值不随视频帧数平方增长。Debug 只保存模型 API 的直接返回:DA3 多帧输入
保留联合推理,MoGe-2 与 BEN2 按各自的单图 API 逐帧保存。
## 任务结果保存在哪里
任务结果保存在 Runtime Storage Root 的 `jobs/` 下,默认是 `~/.snapform/jobs/`。Job Server 接受任务时自动生成 opaque Job ID,并创建同名文件夹;Blender 不预先申请 ID 或目录。Packed Image 和序列帧的临时输入由 Blender 在任务结束后清理,Server 产物直接写入该 Job 目录。视频或序列帧生成的面片会引用其中的序列帧,请保留对应文件夹,不要随意删除。
任务结果保存在 Runtime Storage Root 的 `jobs/` 下,默认是 `~/.anyimage/jobs/`。Job Server 接受任务时自动生成 opaque Job ID,并创建同名文件夹;Blender 不预先申请 ID 或目录。Packed Image 和序列帧的临时输入由 Blender 在任务结束后清理,Server 产物直接写入该 Job 目录。视频或序列帧生成的面片会引用其中的序列帧,请保留对应文件夹,不要随意删除。
## 常见问题
- 从 0.12.0–0.12.5 升级到 0.12.6 或更高版本后,请重新执行一次 `Install Environment`,重建不再单独安装 BlendJob 的 Server Environment
- 首次使用需要下载模型,如果下载慢或失败,可在 Image Empty 的 `SnapForm` 右键菜单点击
- 首次使用需要下载模型,如果下载慢或失败,可在 Image Empty 的 `AnyImage` 右键菜单点击
`Download Required Models`,也可在 `Models` panel 里逐项重试
- 安装 Environment 时下载状态会显示平均速度;网络适合海外源时选择 `Install Environment`,国内网络选择 `Install Environment (Mirror)`。每个按钮固定使用对应来源,失败时不会自动切换
- 默认模型下载会在 Hugging Face 与 OmooLab 镜像之间自动回退;某一来源暂时断线不会阻止后续模型尝试另一来源

View File

@@ -1,6 +1,6 @@
# 视频与序列帧
SnapForm 可以逐帧处理视频和序列帧,并生成随场景时间播放的 Depth Plane 或透明 Image Empty。
AnyImage 可以逐帧处理视频和序列帧,并生成随场景时间播放的 Depth Plane 或透明 Image Empty。
## 支持的工作流
@@ -17,7 +17,7 @@ SnapForm 可以逐帧处理视频和序列帧,并生成随场景时间播放
- **Start** 决定结果从场景的哪一帧开始播放。
- **Cyclic** 会保留到生成的颜色与深度序列中。
显式把 Image Frames 设为 `1` 时,SnapForm 只处理一帧。范围超过视频长度时,SnapForm 会把窗口收回有效范围内。
显式把 Image Frames 设为 `1` 时,AnyImage 只处理一帧。范围超过视频长度时,AnyImage 会把窗口收回有效范围内。
## 性能建议
@@ -28,7 +28,7 @@ SnapForm 可以逐帧处理视频和序列帧,并生成随场景时间播放
## 保存和移动工程
Blender 无法把整组动画图片序列完整打包进 `.blend`。SnapForm 因此让动画结果直接引用 Storage Root 的 Job 文件夹。
Blender 无法把整组动画图片序列完整打包进 `.blend`。AnyImage 因此让动画结果直接引用 Storage Root 的 Job 文件夹。
!!! danger
删除、移动或清理仍被工程引用的 Job 文件夹,会导致动画颜色或深度序列丢失。

View File

@@ -2,7 +2,7 @@
## 程序设计
SnapForm 分为 Blender 交互层、BlendJob Runtime 和 Job Server 计算层。
AnyImage 分为 Blender 交互层、BlendJob Runtime 和 Job Server 计算层。
```mermaid
flowchart LR
@@ -30,7 +30,7 @@ Blender 交互层管理 UI、RNA 状态、任务参数和结果导入。Runtime
| `server/geometry/` | 纯数据几何处理 |
| `server/outputs/` | 文件序列化 |
| `server/media/` | 媒体输入 |
| `assets/O_SnapForm.blend` | Geometry Nodes 与材质资产 |
| `assets/O_AnyImage.blend` | Geometry Nodes 与材质资产 |
Depth Plane、Surface、Background 和 Balloon 按功能组织。一个功能的入口、请求和 Blender 回写放在一起;跨功能的图片、材质和参数处理放在 `operators/common.py`、`operators/image.py` 与 `operators/material.py`。
@@ -44,7 +44,7 @@ Server 的模型模块返回 Prediction,几何模块处理数组与网格,
├── docs/
├── pack.py
├── sync_models.py
├── src/snapform/
├── src/anyimage/
│ ├── blender_manifest.toml
│ ├── runtime.py
│ ├── menu.py
@@ -94,7 +94,7 @@ uv run --only-group docs mike deploy <version> -p
uv run --only-group docs mike set-default <version> -p
```
自动部署将 MkDocs 站点发布到专用 Cloudflare Worker。仓库需要配置 `CLOUDFLARE_ACCOUNT_ID`、`CLOUDFLARE_API_TOKEN` 和 `CLOUDFLARE_WORKER_NAME`。Worker 路由对应 `docs.omoolab.xyz/snapform/*`。
自动部署将 MkDocs 站点发布到专用 Cloudflare Worker。仓库需要配置 `CLOUDFLARE_ACCOUNT_ID`、`CLOUDFLARE_API_TOKEN` 和 `CLOUDFLARE_WORKER_NAME`。Worker 路由对应 `docs.omoolab.xyz/anyimage/*`。
## 资产构建与验证
@@ -117,5 +117,5 @@ BLENDER_BIN="/c/Program Files/Blender Foundation/Blender 4.5/blender.exe"
- Depth Anything 3 代码采用 Apache 2.0
- BEN2 ONNX 模型采用其仓库声明的 Apache 2.0 License
- SnapForm Extension 代码采用 GPL-3.0-or-later
- AnyImage Extension 代码采用 GPL-3.0-or-later
- 模型权重许可证由所选 Hugging Face 模型仓库决定

View File

@@ -1,8 +1,8 @@
# 常见问题
## 找不到 SnapForm 右键菜单
## 找不到 AnyImage 右键菜单
SnapForm 菜单只会在选中 Image Empty 时出现。确认对象类型是 Empty,并且显示方式为 Image。普通 Mesh、图片编辑器中的图片或未选中的 Image Empty 都不会显示入口。
AnyImage 菜单只会在选中 Image Empty 时出现。确认对象类型是 Empty,并且显示方式为 Image。普通 Mesh、图片编辑器中的图片或未选中的 Image Empty 都不会显示入口。
## 转换入口是灰色的
@@ -10,7 +10,7 @@ SnapForm 菜单只会在选中 Image Empty 时出现。确认对象类型是 Emp
- Environment 尚未安装。
- 默认必需模型尚未就绪。
- 另一个 SnapForm 任务正在运行。
- 另一个 AnyImage 任务正在运行。
- Surface 的输入不是单张图片。
如果子菜单显示 **Install Environment** 或 **Download Required Models**,先完成对应操作。
@@ -50,7 +50,7 @@ SnapForm 菜单只会在选中 Image Empty 时出现。确认对象类型是 Emp
## 如何完全撤销一次转换
转换完成后直接按 `Ctrl+Z`。SnapForm 会把网格、材质、图片和修改器的创建合并为一次 Blender Undo 操作。
转换完成后直接按 `Ctrl+Z`。AnyImage 会把网格、材质、图片和修改器的创建合并为一次 Blender Undo 操作。
## 从旧版本升级后功能不可用

View File

@@ -1,6 +1,6 @@
# 功能参考
本页概览 SnapForm 的能力。安装步骤见[安装与首次配置](installation.md),实际操作见[工作流选择](workflows.md)与各使用指南。
本页概览 AnyImage 的能力。安装步骤见[安装与首次配置](installation.md),实际操作见[工作流选择](workflows.md)与各使用指南。
## 工作流
@@ -16,9 +16,9 @@ Depth Plane 与 Surface 可使用 DA3 或 MoGe-2。DA3 提供相对深度和相
## Blender 交互
主要功能位于 Image Empty 的 `SnapForm` 右键菜单。菜单会显示 Environment 和必需模型的就绪状态,并在缺少内容时提供安装或下载入口。
主要功能位于 Image Empty 的 `AnyImage` 右键菜单。菜单会显示 Environment 和必需模型的就绪状态,并在缺少内容时提供安装或下载入口。
转换任务完成后,SnapForm 在一个 Undo 步骤中创建网格、图片、材质和修改器。源 Image Empty 是否保留由转换选项决定。
转换任务完成后,AnyImage 在一个 Undo 步骤中创建网格、图片、材质和修改器。源 Image Empty 是否保留由转换选项决定。
Balloon 默认在 Blender 内部生成,无需 Environment 或模型。Tool Settings 提供模式、厚度、采样距离、材质复用、`Contour`、`Depth` 与 MoGe-2 `Depth Model`。Depth 会把物理距离相对化并稳健拟合初始基面,保留未截断的正负起伏。Base Plane 子 Empty 的旋转定义基面朝向,沿自身局部 Z 移动定义 middle level。
@@ -36,7 +36,7 @@ Blender 无法整体 Pack 图片序列,因此动画结果会继续引用 Job
AI 任务在独立 Python Environment 中通过 ONNX Runtime 执行。Blender 负责界面、任务提交和结果导入,Job Server 负责模型与文件处理。
默认 Storage Root 为 `~/.snapform`,其中保存 Environment、模型、日志和 Job 结果。连续任务会复用已加载的模型 Session;DA3 与 MoGe-2 共享一个深度模型缓存位置,BEN2 可同时保留。
默认 Storage Root 为 `~/.anyimage`,其中保存 Environment、模型、日志和 Job 结果。连续任务会复用已加载的模型 Session;DA3 与 MoGe-2 共享一个深度模型缓存位置,BEN2 可同时保留。
安装、下载和推理都在后台运行,并通过状态栏提供进度与取消操作。详细设计见[Job Server](server.md)。

View File

@@ -5,7 +5,7 @@ Depth Plane 将图片转换为带位移的立体面片。它适合快速场景
## 创建 Depth Plane
1. 选中 Image Empty。
2. 在 3D View 中右键,选择 **SnapForm > Convert to Depth Plane**。
2. 在 3D View 中右键,选择 **AnyImage > Convert to Depth Plane**。
3. 选择深度来源和需要的选项。
4. 确认后等待任务完成。

View File

@@ -8,7 +8,7 @@ Surface 从单张图片构建带透视关系的纹理网格。它适合建筑、
## 创建 Surface
1. 选中只包含一帧的 Image Empty。
2. 在 3D View 中右键,选择 **SnapForm > Convert to Surface**。
2. 在 3D View 中右键,选择 **AnyImage > Convert to Surface**。
3. 选择模型和转换选项。
4. 确认后等待网格、材质和 Geometry Nodes 修改器创建完成。
@@ -16,7 +16,7 @@ Surface 从单张图片构建带透视关系的纹理网格。它适合建筑、
默认的 **MoGe-2 ViT-S Normal** 适合多数图片。MoGe-2 使用 **Resolution Level**,DA3 使用 **Resolution Scale**。提高分辨率会增加耗时和内存占用,建议先用默认值判断整体结构。
如果某个模型名称后显示 **(Missing)**,请先在 3D View 的 **SnapForm > Models** 面板下载它。
如果某个模型名称后显示 **(Missing)**,请先在 3D View 的 **AnyImage > Models** 面板下载它。
## 转换选项

View File

@@ -1,8 +1,8 @@
# SnapForm
# AnyImage
把图片里的空间感带进 Blender。
SnapForm 是一款 Blender 扩展,可以把照片、视频或序列帧转换为带真实深度变化的立体面片。你只需导入图片、选中 Image Empty,然后从右键菜单选择需要的结果。
AnyImage 是一款 Blender 扩展,可以把照片、视频或序列帧转换为带真实深度变化的立体面片。你只需导入图片、选中 Image Empty,然后从右键菜单选择需要的结果。
- 用 **Depth Plane** 快速得到可调节的浮雕式面片
- 用 **Surface** 从单张图片生成透视纹理网格
@@ -11,13 +11,13 @@ SnapForm 是一款 Blender 扩展,可以把照片、视频或序列帧转换
- 使用 Geometry Nodes 继续调整深度、细分、重拓扑与平滑效果
!!! info "运行环境与 Blender 分离"
SnapForm 的 AI 计算运行在独立环境中,不会把 ONNX Runtime 等计算依赖装进 Blender Python。首次配置完成后,日常操作都可以从 Image Empty 的右键菜单完成。
AnyImage 的 AI 计算运行在独立环境中,不会把 ONNX Runtime 等计算依赖装进 Blender Python。首次配置完成后,日常操作都可以从 Image Empty 的右键菜单完成。
## 从这里开始
<div class="grid cards" markdown>
- **安装 SnapForm**
- **安装 AnyImage**
下载与你的系统匹配的扩展包,完成环境和默认模型配置。
@@ -51,4 +51,4 @@ SnapForm 是一款 Blender 扩展,可以把照片、视频或序列帧转换
- 正式使用前需要安装独立 Environment,以及默认的 MoGe-2 和 BEN2 模型
!!! tip
不确定从哪里开始时,先使用默认设置将一张人物或物体照片转换为 Depth Plane。它速度快、参数直观,也最适合熟悉 SnapForm 的基本工作方式。
不确定从哪里开始时,先使用默认设置将一张人物或物体照片转换为 Depth Plane。它速度快、参数直观,也最适合熟悉 AnyImage 的基本工作方式。

View File

@@ -1,13 +1,13 @@
# 安装与首次配置
SnapForm 的安装分为两步:先安装 Blender Extension,再安装独立运行环境和默认模型。第二步只需在首次使用或环境版本发生变化时执行。
AnyImage 的安装分为两步:先安装 Blender Extension,再安装独立运行环境和默认模型。第二步只需在首次使用或环境版本发生变化时执行。
## 安装 Blender Extension
1. 下载与你的系统匹配的 ZIP 文件:
- `SnapForm.v版本号.windows-x64.zip`
- `SnapForm.v版本号.macos-arm64.zip`
- `SnapForm.v版本号.linux-x64.zip`
- `AnyImage.v版本号.windows-x64.zip`
- `AnyImage.v版本号.macos-arm64.zip`
- `AnyImage.v版本号.linux-x64.zip`
2. 打开 Blender,进入 **Edit > Preferences > Extensions**。
3. 点击右上角菜单,选择 **Install from Disk**。
4. 选择下载的 ZIP 文件并完成安装。
@@ -17,21 +17,21 @@ SnapForm 的安装分为两步:先安装 Blender Extension,再安装独立
## 检查扩展偏好设置
在 SnapForm 的扩展偏好设置中可以看到两个选项:
在 AnyImage 的扩展偏好设置中可以看到两个选项:
- **Storage Root**:保存运行环境、模型和任务文件的位置。默认是用户目录下的 `.snapform`,一般无需修改。
- **Device**:正式转换任务使用的计算设备。SnapForm 默认选择本机可用的最快设备;如果 GPU Provider 不可用,会使用 CPU。
- **Storage Root**:保存运行环境、模型和任务文件的位置。默认是用户目录下的 `.anyimage`,一般无需修改。
- **Device**:正式转换任务使用的计算设备。AnyImage 默认选择本机可用的最快设备;如果 GPU Provider 不可用,会使用 CPU。
如果准备把 Storage Root 放到其他磁盘,建议在首次安装 Environment 之前修改。更换目录后,旧目录中的环境和模型不会自动移动。
## 安装 Environment
打开 3D View,按 `N` 显示侧栏并切换到 **SnapForm** 页签。在 **Job Server** 面板中选择一个安装入口:
打开 3D View,按 `N` 显示侧栏并切换到 **AnyImage** 页签。在 **Job Server** 面板中选择一个安装入口:
- **Install Environment**:使用官方源,适合能稳定访问 GitHub、Python 下载源和 PyPI 的网络。
- **Install Environment (Mirror)**:使用国内镜像下载 uv、Python 和 Python 包。
安装程序会依次准备 uv、独立 Python 和运行包。Environment 完成后,SnapForm 还会下载日常功能需要的 **MoGe-2 ViT-S Normal** 与 **BEN2 Base**。
安装程序会依次准备 uv、独立 Python 和运行包。Environment 完成后,AnyImage 还会下载日常功能需要的 **MoGe-2 ViT-S Normal** 与 **BEN2 Base**。
!!! note
两个安装按钮只影响 Environment 的下载源。模型会按自己的下载源与备用镜像规则获取。
@@ -46,7 +46,7 @@ SnapForm 的安装分为两步:先安装 Blender Extension,再安装独立
- 展开 **Models** 面板,MoGe-2 ViT-S Normal 和 BEN2 Base 显示 **Ready**。
- 选中 Image Empty 后,右键菜单中的 **Convert to Depth Plane**、**Convert to Surface** 和 **Remove Background** 可以点击。
如果 Environment 已安装但默认模型缺失,Image Empty 的 **SnapForm** 右键子菜单会显示 **Download Required Models**。点击一次即可补齐缺失项。
如果 Environment 已安装但默认模型缺失,Image Empty 的 **AnyImage** 右键子菜单会显示 **Download Required Models**。点击一次即可补齐缺失项。
## 下一步

View File

@@ -1,10 +1,10 @@
# 模型、设备与存储
SnapForm 把运行环境、模型和任务结果放在 Blender 之外,多个 Blender 版本可以共享同一套数据。
AnyImage 把运行环境、模型和任务结果放在 Blender 之外,多个 Blender 版本可以共享同一套数据。
## Models 面板
打开 3D View 侧栏中的 **SnapForm > Models**,可以查看和下载模型。
打开 3D View 侧栏中的 **AnyImage > Models**,可以查看和下载模型。
- **Ready** 表示模型文件已经就绪。
- **Download** 用于按需下载模型。
@@ -25,10 +25,10 @@ Debug 面板中的 **Debug Device** 只影响 Debug 任务,不会覆盖正式
## Storage Root
默认 Storage Root 是 `~/.snapform`,主要包含:
默认 Storage Root 是 `~/.anyimage`,主要包含:
```text
~/.snapform/
~/.anyimage/
├── .venv/ # 独立 Python Environment
├── models/ # 模型文件
├── jobs/ # 每次任务的结果
@@ -36,7 +36,7 @@ Debug 面板中的 **Debug Device** 只影响 Debug 任务,不会覆盖正式
└── server.log # Job Server 日志
```
更改 Storage Root 相当于切换到另一套环境、模型和任务数据。SnapForm 不会自动移动旧目录。
更改 Storage Root 相当于切换到另一套环境、模型和任务数据。AnyImage 不会自动移动旧目录。
## Job 文件夹

View File

@@ -8,9 +8,9 @@
建议第一次尝试使用主体清晰、前后景关系明确的图片。人物、产品、建筑立面和插画通常都比较适合。
## 2. 打开 SnapForm 菜单
## 2. 打开 AnyImage 菜单
保持 Image Empty 处于选中状态,在 3D View 中点击右键,选择 **SnapForm > Convert to Depth Plane**。
保持 Image Empty 处于选中状态,在 3D View 中点击右键,选择 **AnyImage > Convert to Depth Plane**。
如果菜单不存在,请确认当前对象是 Image Empty;如果转换入口是灰色,请先完成[安装与首次配置](installation.md)。

View File

@@ -5,10 +5,10 @@ Remove Background 使用 BEN2 为 Image Empty 生成透明前景,不创建网
## 使用方法
1. 选中 Image Empty。
2. 在 3D View 中右键,选择 **SnapForm > Remove Background**。
2. 在 3D View 中右键,选择 **AnyImage > Remove Background**。
3. 等待状态栏提示任务完成。
SnapForm 会创建新的透明颜色图,并把当前 Empty 切换到新图。对象的 Transform、显示尺寸、Offset 和其他显示设置保持不变。
AnyImage 会创建新的透明颜色图,并把当前 Empty 切换到新图。对象的 Transform、显示尺寸、Offset 和其他显示设置保持不变。
## 在转换时去除背景

View File

@@ -1,6 +1,6 @@
# Job Server
SnapForm 把 AI 与媒体处理放在独立 Job Server 中运行,使 Blender Python 保持轻量。安装和使用问题见[安装与首次配置](installation.md)与[常见问题](faq.md)。
AnyImage 把 AI 与媒体处理放在独立 Job Server 中运行,使 Blender Python 保持轻量。安装和使用问题见[安装与首次配置](installation.md)与[常见问题](faq.md)。
## 架构
@@ -8,7 +8,7 @@ SnapForm 把 AI 与媒体处理放在独立 Job Server 中运行,使 Blender P
flowchart LR
Blender["Blender Operator"] --> Runtime["JobRuntime"]
Runtime --> Queue["FIFO Queue"]
Queue --> Job["SnapForm Job"]
Queue --> Job["AnyImage Job"]
Job --> Media["Media"]
Job --> Models["ModelManager"]
Job --> Geometry["Geometry"]
@@ -19,14 +19,14 @@ flowchart LR
每个 Blender 实例拥有自己的 Server 进程,连接绑定到本机随机端口和 Instance ID。多个实例可以共享 Storage Root,但各自维护进程、队列和内存中的模型 Session。
BlendJob 提供 Runtime、HTTP transport、队列、状态和取消协议。SnapForm 的 Operator 负责业务参数,Server Job 负责工作流编排,结果由 Operator 在 Blender 主线程中导入。
BlendJob 提供 Runtime、HTTP transport、队列、状态和取消协议。AnyImage 的 Operator 负责业务参数,Server Job 负责工作流编排,结果由 Operator 在 Blender 主线程中导入。
## Storage Root
默认位置为 `~/.snapform`:
默认位置为 `~/.anyimage`:
```text
~/.snapform/
~/.anyimage/
├── .venv/ Python Environment
├── models/ ONNX 模型
├── jobs/ 任务产物

View File

@@ -53,7 +53,7 @@ DA3 使用 `O DA3 Surface`,MoGe-2 使用 `O MoGe2 Surface`。两者采用相
网格在 camera space 中生成,再通过源 Image Empty 的 Transform 放入场景。导入过程会匹配图片可见尺寸、宽高比、Offset 与主点位置。
启用 Create Matching Camera 时,SnapForm 根据模型 intrinsics 创建相机并使用相同坐标变换,使网格与相机视图保持一致。模型估计的焦距和主点决定透视关系;`reference_depth` 决定整体尺度。
启用 Create Matching Camera 时,AnyImage 根据模型 intrinsics 创建相机并使用相同坐标变换,使网格与相机视图保持一致。模型估计的焦距和主点决定透视关系;`reference_depth` 决定整体尺度。
## 材质与文件

View File

@@ -1,6 +1,6 @@
# 选择转换方式
SnapForm 提供三种日常工作流。选择结果时,先考虑你需要的是可调节的“深度面片”、带透视结构的“空间网格”,还是只需要透明前景。
AnyImage 提供三种日常工作流。选择结果时,先考虑你需要的是可调节的“深度面片”、带透视结构的“空间网格”,还是只需要透明前景。
| 工作流 | 适合场景 | 输入 | 结果 | 主要优点 |
| --- | --- | --- | --- | --- |

View File

@@ -1,13 +1,13 @@
site_name: SnapForm 文档
site_url: https://docs.omoolab.xyz/snapform/
site_name: AnyImage 文档
site_url: https://docs.omoolab.xyz/anyimage/
site_author: OmooLab
site_description: SnapForm 是一款将图片、视频和序列帧转换为立体面片的 Blender 扩展。
site_description: AnyImage 是一款将图片、视频和序列帧转换为立体面片的 Blender 扩展。
repo_name: OmooLab/SnapForm
repo_url: https://git.omoolab.xyz/icrdr/SnapForm
repo_name: OmooLab/AnyImage
repo_url: https://git.omoolab.xyz/icrdr/AnyImage
nav:
- SnapForm:
- AnyImage:
- index.md
- 开始使用:
- 安装与首次配置: installation.md

View File

@@ -8,7 +8,7 @@ from zipfile import ZIP_DEFLATED, ZipFile
PROJECT_ROOT = Path(__file__).resolve().parent
SOURCE_DIR = PROJECT_ROOT / "src" / "snapform"
SOURCE_DIR = PROJECT_ROOT / "src" / "anyimage"
DIST_DIR = PROJECT_ROOT / "dist"
PYTHON_VERSION = "3.11"

View File

@@ -1,5 +1,5 @@
[project]
name = "snapform"
name = "anyimage"
version = "0.12.15"
description = "Fast image-guided structure building tools for Blender"
readme = "README.md"

View File

@@ -3,9 +3,9 @@ import bpy
from .runtime import runtime
from .operators import CLASSES as OPERATOR_CLASSES
from .operators.balloon import BalloonTool
from .preferences import SnapFormPreferences
from .properties import SnapFormSettings
from .menu import SnapFormImageMenu, draw_image_context_menu
from .preferences import AnyImagePreferences
from .properties import AnyImageSettings
from .menu import AnyImageImageMenu, draw_image_context_menu
from .panel import (
Ben2RemovalDebugPanel,
Da3OutputDebugPanel,
@@ -17,10 +17,10 @@ from .panel import (
CLASSES = (
SnapFormPreferences,
SnapFormSettings,
AnyImagePreferences,
AnyImageSettings,
*OPERATOR_CLASSES,
SnapFormImageMenu,
AnyImageImageMenu,
ServerPanel,
ModelsPanel,
DebugPanel,
@@ -37,8 +37,8 @@ def register():
BalloonTool,
separator=False,
)
bpy.types.Scene.snapform_settings = bpy.props.PointerProperty(
type=SnapFormSettings,
bpy.types.Scene.anyimage_settings = bpy.props.PointerProperty(
type=AnyImageSettings,
)
bpy.types.VIEW3D_MT_object_context_menu.append(draw_image_context_menu)
bpy.types.OUTLINER_MT_object.append(draw_image_context_menu)
@@ -50,7 +50,7 @@ def unregister():
bpy.utils.unregister_tool(BalloonTool)
bpy.types.VIEW3D_MT_object_context_menu.remove(draw_image_context_menu)
bpy.types.OUTLINER_MT_object.remove(draw_image_context_menu)
if hasattr(bpy.types.Scene, "snapform_settings"):
del bpy.types.Scene.snapform_settings
if hasattr(bpy.types.Scene, "anyimage_settings"):
del bpy.types.Scene.anyimage_settings
for class_type in reversed(CLASSES):
bpy.utils.unregister_class(class_type)

View File

@@ -1,8 +1,8 @@
schema_version = "1.0.0"
id = "snapform"
id = "anyimage"
version = "0.12.15"
name = "SnapForm"
name = "AnyImage"
tagline = "Build spatial structures from images, fast"
maintainer = "OmooLab"
type = "add-on"

View File

@@ -3,9 +3,9 @@ import bpy
from .operators.image import is_image_empty
class SnapFormImageMenu(bpy.types.Menu):
bl_idname = "SNAPFORM_MT_image_context"
bl_label = "SnapForm"
class AnyImageImageMenu(bpy.types.Menu):
bl_idname = "ANYIMAGE_MT_image_context"
bl_label = "AnyImage"
def draw(self, _context):
from .runtime import runtime
@@ -14,7 +14,7 @@ class SnapFormImageMenu(bpy.types.Menu):
layout = self.layout
layout.operator_context = "INVOKE_DEFAULT"
layout.operator(
"snapform.activate_balloon_tool",
"anyimage.activate_balloon_tool",
text="Balloon Lasso Tool",
icon="MOD_SKIN",
)
@@ -22,19 +22,19 @@ class SnapFormImageMenu(bpy.types.Menu):
environment_ready = runtime.environment_ready()
if not environment_ready:
layout.operator(
"snapform.install_environment",
"anyimage.install_environment",
text="Install Environment",
icon="IMPORT",
)
layout.operator(
"snapform.install_environment_mirror",
"anyimage.install_environment_mirror",
text="Install Environment (Mirror)",
icon="IMPORT",
)
models_ready = required_models_ready()
if environment_ready and not models_ready:
layout.operator(
"snapform.download_required_models",
"anyimage.download_required_models",
text="Download Required Models",
icon="IMPORT",
)
@@ -44,14 +44,14 @@ class SnapFormImageMenu(bpy.types.Menu):
depth_plane_row = layout.row()
depth_plane_row.enabled = features_ready
depth_plane_row.operator(
"snapform.convert_to_depth_plane",
"anyimage.convert_to_depth_plane",
text="Convert to Depth Plane",
icon="MOD_DISPLACE",
)
surface_row = layout.row()
surface_row.enabled = features_ready
surface_row.operator(
"snapform.convert_to_surface",
"anyimage.convert_to_surface",
text="Convert to Surface",
icon="MESH_GRID",
)
@@ -59,7 +59,7 @@ class SnapFormImageMenu(bpy.types.Menu):
background_row = layout.row()
background_row.enabled = features_ready
background_row.operator(
"snapform.remove_image_background",
"anyimage.remove_image_background",
text="Remove Background",
icon="IMAGE_ALPHA",
)
@@ -70,7 +70,7 @@ def draw_image_context_menu(self, context):
return
self.layout.separator()
self.layout.menu(
SnapFormImageMenu.bl_idname,
text="SnapForm",
AnyImageImageMenu.bl_idname,
text="AnyImage",
icon="PLUGIN",
)

View File

@@ -23,7 +23,7 @@ from .image import (
class RemoveImageBackground(bpy.types.Operator):
bl_idname = "snapform.remove_image_background"
bl_idname = "anyimage.remove_image_background"
bl_label = "Remove Background"
bl_description = "Remove the background from this Image Empty with BEN2"
bl_options = {"REGISTER"}
@@ -44,7 +44,7 @@ class RemoveImageBackground(bpy.types.Operator):
source_object = context.object
total_frames = video_total_frames(source_object.data)
if total_frames > 1:
context.scene.snapform_settings.video_frames = (
context.scene.anyimage_settings.video_frames = (
default_extract_frames(source_object)
)
try:
@@ -56,7 +56,7 @@ class RemoveImageBackground(bpy.types.Operator):
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
result = bpy.ops.snapform.process_image_background(
result = bpy.ops.anyimage.process_image_background(
input_path=str(source_path),
source_object_name=source_object.name,
delete_input=delete_input,
@@ -83,7 +83,7 @@ def apply_background_response(result, source_object_name):
class ProcessImageBackground(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.process_image_background"
bl_idname = "anyimage.process_image_background"
bl_label = "Process Image Background"
job_type = "remove-background"
starting_message = "Starting Image Empty background removal"
@@ -95,7 +95,7 @@ class ProcessImageBackground(JobOperatorBase, bpy.types.Operator):
def request(self, context):
require_environment()
require_model(BEN2_MODEL_KEY)
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
return {
"input": str(background_input_path(self.input_path)),
"model": BEN2_MODEL_KEY,

View File

@@ -8,7 +8,7 @@ import bpy
BALLOON_DEPTH_ATTRIBUTE = "o_balloon_depth"
BALLOON_SHAPE_NODE_GROUP_NAME = "O Balloon Shape"
BALLOON_SHAPE_NODE_GROUP_VERSION = 16
BALLOON_SHAPE_VERSION_PROPERTY = "snapform_balloon_shape_version"
BALLOON_SHAPE_VERSION_PROPERTY = "anyimage_balloon_shape_version"
def load_balloon_depth_data(path):
@@ -216,8 +216,8 @@ def create_balloon_base_plane(context, balloon, vertices, local_plane):
controller.empty_display_size = max(float(extent.max()) * 0.15, 0.01)
controller.show_in_front = True
controller.hide_render = True
controller["snapform_source_type"] = "BALLOON_BASE_PLANE"
controller["snapform_base_plane"] = (slope_y, slope_z, offset)
controller["anyimage_source_type"] = "BALLOON_BASE_PLANE"
controller["anyimage_base_plane"] = (slope_y, slope_z, offset)
return controller

View File

@@ -49,9 +49,9 @@ from ..surface import assign_vertex_uv
MIN_PATH_DISTANCE = 2.0
BALLOON_MATERIAL_PROPERTY = "snapform_balloon_material"
BALLOON_SOURCE_IMAGE_PROPERTY = "snapform_source_image"
BALLOON_NORMAL_IMAGE_PROPERTY = "snapform_balloon_normal_image"
BALLOON_MATERIAL_PROPERTY = "anyimage_balloon_material"
BALLOON_SOURCE_IMAGE_PROPERTY = "anyimage_source_image"
BALLOON_NORMAL_IMAGE_PROPERTY = "anyimage_balloon_normal_image"
def simplify_screen_path(path):
@@ -395,11 +395,11 @@ def extract_balloon_object(
balloon = bpy.data.objects.new(balloon_name, mesh)
context.collection.objects.link(balloon)
balloon.matrix_world = orient_balloon_world_matrix(matrix_world)
balloon["snapform_source_type"] = "BALLOON"
balloon["snapform_balloon_algorithm"] = algorithm
balloon["snapform_source_object"] = source_name
balloon["snapform_inflation"] = inflation
balloon["snapform_resolution"] = point_spacing
balloon["anyimage_source_type"] = "BALLOON"
balloon["anyimage_balloon_algorithm"] = algorithm
balloon["anyimage_source_object"] = source_name
balloon["anyimage_inflation"] = inflation
balloon["anyimage_resolution"] = point_spacing
balloon.data.materials.append(material)
if depth_data is not None:
base_plane = create_balloon_base_plane(
@@ -410,8 +410,8 @@ def extract_balloon_object(
)
add_balloon_shape_modifier(balloon, thickness, base_plane)
if depth_data is not None:
balloon["snapform_balloon_depth_analysis"] = True
balloon["snapform_reference_depth"] = depth_data["reference_depth"]
balloon["anyimage_balloon_depth_analysis"] = True
balloon["anyimage_reference_depth"] = depth_data["reference_depth"]
except Exception:
if base_plane is not None:
bpy.data.objects.remove(base_plane, do_unlink=True)
@@ -473,7 +473,7 @@ def drawing_in_region(area_pointer, region_pointer):
class ExtractBalloon(bpy.types.Operator):
bl_idname = "snapform.extract_balloon"
bl_idname = "anyimage.extract_balloon"
bl_label = "Extract Balloon"
bl_description = "Build one closed Balloon mesh from a saved lasso region"
bl_options = {"REGISTER", "UNDO"}
@@ -536,7 +536,7 @@ class ExtractBalloon(bpy.types.Operator):
def invoke(self, context, event):
source_object = context.active_object
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
self.mode = settings.balloon_mode
self.thickness = settings.balloon_thickness
self.resolution = settings.balloon_resolution
@@ -622,7 +622,7 @@ class ExtractBalloon(bpy.types.Operator):
source_object.data,
context.scene,
)
result = bpy.ops.snapform.analyze_balloon(
result = bpy.ops.anyimage.analyze_balloon(
input_path=str(source_path),
delete_input=delete_input,
source_object_name=self.source_object_name,
@@ -657,7 +657,7 @@ class ExtractBalloon(bpy.types.Operator):
select_result=False,
reuse_material=self.reuse_material,
)
balloon["snapform_resolution_m"] = self.resolution
balloon["anyimage_resolution_m"] = self.resolution
except (json.JSONDecodeError, RuntimeError, TypeError, ValueError) as error:
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
@@ -665,7 +665,7 @@ class ExtractBalloon(bpy.types.Operator):
class AnalyzeBalloon(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.analyze_balloon"
bl_idname = "anyimage.analyze_balloon"
bl_label = "Analyze Balloon"
job_type = "analyze-balloon"
starting_message = "Starting Balloon AI analysis"
@@ -744,11 +744,11 @@ class AnalyzeBalloon(JobOperatorBase, bpy.types.Operator):
depth_data=depth_data,
normal_path=(result.file("normal") if self.depth_analysis else None),
)
balloon["snapform_resolution_m"] = self.resolution
balloon["anyimage_resolution_m"] = self.resolution
if self.contour_analysis:
balloon["snapform_balloon_contour_analysis"] = True
balloon["anyimage_balloon_contour_analysis"] = True
if self.depth_analysis:
balloon["snapform_balloon_depth_model"] = self.depth_model
balloon["anyimage_balloon_depth_model"] = self.depth_model
push_undo("Extract Balloon")
return "AI-analyzed Balloon is ready"
@@ -757,7 +757,7 @@ class AnalyzeBalloon(JobOperatorBase, bpy.types.Operator):
class ActivateBalloonTool(bpy.types.Operator):
bl_idname = "snapform.activate_balloon_tool"
bl_idname = "anyimage.activate_balloon_tool"
bl_label = "Balloon Lasso Tool"
bl_description = "Activate the Balloon Lasso tool in the 3D Viewport"
bl_options = {"INTERNAL"}
@@ -780,7 +780,7 @@ class ActivateBalloonTool(bpy.types.Operator):
class BalloonTool(bpy.types.WorkSpaceTool):
bl_space_type = "VIEW_3D"
bl_context_mode = "OBJECT"
bl_idname = "snapform.balloon_lasso"
bl_idname = "anyimage.balloon_lasso"
bl_label = "Balloon Lasso"
bl_description = (
"Draw an Image Empty region and extract one independently undoable "
@@ -802,11 +802,11 @@ class BalloonTool(bpy.types.WorkSpaceTool):
"properties": [
(
"data_path_primary",
"scene.snapform_settings.balloon_thickness",
"scene.anyimage_settings.balloon_thickness",
),
(
"zoom_path",
"scene.snapform_settings.balloon_thickness_radial_zoom",
"scene.anyimage_settings.balloon_thickness_radial_zoom",
),
]
},
@@ -818,11 +818,11 @@ class BalloonTool(bpy.types.WorkSpaceTool):
"properties": [
(
"data_path_primary",
"scene.snapform_settings.balloon_resolution",
"scene.anyimage_settings.balloon_resolution",
),
(
"zoom_path",
"scene.snapform_settings.balloon_resolution_radial_zoom",
"scene.anyimage_settings.balloon_resolution_radial_zoom",
),
]
},
@@ -830,7 +830,7 @@ class BalloonTool(bpy.types.WorkSpaceTool):
)
def draw_settings(context, layout, tool):
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
layout.prop(settings, "balloon_mode", text="")
layout.prop(settings, "balloon_thickness")
layout.prop(settings, "balloon_resolution", text="Resolution (m)")

View File

@@ -46,7 +46,7 @@ def production_device():
def require_environment():
if not runtime.environment_ready():
raise RuntimeError("Install the SnapForm Environment first")
raise RuntimeError("Install the AnyImage Environment first")
def require_model(model_key):

View File

@@ -14,14 +14,14 @@ from .common import (
class DebugDa3(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.debug_da3"
bl_idname = "anyimage.debug_da3"
bl_label = "Debug DA3"
job_type = "debug-da3"
starting_message = "Starting DA3 API inference"
def request(self, context):
require_environment()
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
model = settings.model
require_model(model)
return {
@@ -43,14 +43,14 @@ class DebugDa3(JobOperatorBase, bpy.types.Operator):
class DebugMoge2(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.debug_moge2"
bl_idname = "anyimage.debug_moge2"
bl_label = "Debug MoGe-2"
job_type = "debug-moge2"
starting_message = "Starting MoGe-2 inference"
def request(self, context):
require_environment()
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
require_model(settings.moge_model)
return {
**moge2_parameters(
@@ -69,14 +69,14 @@ class DebugMoge2(JobOperatorBase, bpy.types.Operator):
class DebugBen2(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.debug_ben2"
bl_idname = "anyimage.debug_ben2"
bl_label = "Debug BEN2"
job_type = "debug-ben2"
starting_message = "Starting BEN2 background removal"
def request(self, context):
require_environment()
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
require_model(BEN2_MODEL_KEY)
return {
"input": str(background_input_path(settings.background_input_path)),

View File

@@ -326,7 +326,7 @@ def build_image_depth_plane(
use_cyclic=source_use_cyclic,
animate_frame=is_animated_image(source_object.data),
)
plane_object["snapform_frame_count"] = frame_count
plane_object["anyimage_frame_count"] = frame_count
return finish_depth_plane(
context,
source_object,
@@ -431,11 +431,11 @@ def build_depth_plane(
use_cyclic=source_use_cyclic,
animate_frame=is_animated_image(source_object.data),
)
plane_object["snapform_reference_depth"] = reference_depth
plane_object["snapform_camera_plane_width"] = bounds[1] - bounds[0]
plane_object["snapform_camera_plane_height"] = bounds[3] - bounds[2]
plane_object["snapform_depth_scale"] = depth_scale
plane_object["snapform_frame_count"] = frame_count
plane_object["anyimage_reference_depth"] = reference_depth
plane_object["anyimage_camera_plane_width"] = bounds[1] - bounds[0]
plane_object["anyimage_camera_plane_height"] = bounds[3] - bounds[2]
plane_object["anyimage_depth_scale"] = depth_scale
plane_object["anyimage_frame_count"] = frame_count
return finish_depth_plane(
context,
@@ -536,9 +536,9 @@ def build_moge2_depth_plane(
use_cyclic=source_use_cyclic,
animate_frame=is_animated_image(source_object.data),
)
plane_object["snapform_reference_depth"] = reference_depth
plane_object["snapform_depth_scale"] = depth_scale
plane_object["snapform_frame_count"] = len(depth)
plane_object["anyimage_reference_depth"] = reference_depth
plane_object["anyimage_depth_scale"] = depth_scale
plane_object["anyimage_frame_count"] = len(depth)
return finish_depth_plane(
context,
source_object,

View File

@@ -36,7 +36,7 @@ def image_depth_model_items(owner, context):
class ConvertToDepthPlane(bpy.types.Operator):
bl_idname = "snapform.convert_to_depth_plane"
bl_idname = "anyimage.convert_to_depth_plane"
bl_label = "Convert to Depth Plane"
bl_description = "Convert an image to a displaced depth plane"
bl_options = {"REGISTER"}
@@ -101,7 +101,7 @@ class ConvertToDepthPlane(bpy.types.Operator):
def execute(self, context):
source_object = context.object
if video_total_frames(source_object.data) > 1:
context.scene.snapform_settings.video_frames = (
context.scene.anyimage_settings.video_frames = (
default_extract_frames(source_object)
)
if self.model == "ORIGINAL":
@@ -114,9 +114,9 @@ class ConvertToDepthPlane(bpy.types.Operator):
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
run_original = (
bpy.ops.snapform.generate_original_background
bpy.ops.anyimage.generate_original_background
if self.remove_background
else bpy.ops.snapform.extract_original_frames
else bpy.ops.anyimage.extract_original_frames
)
result = run_original(
input_path=str(source_path),
@@ -140,7 +140,7 @@ class ConvertToDepthPlane(bpy.types.Operator):
except RuntimeError as error:
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
result = bpy.ops.snapform.generate_moge2_depth(
result = bpy.ops.anyimage.generate_moge2_depth(
input_path=str(source_path),
source_object_name=source_object.name,
resolution_level=self.resolution_level,
@@ -174,7 +174,7 @@ class ConvertToDepthPlane(bpy.types.Operator):
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
result = bpy.ops.snapform.generate_da3_depth(
result = bpy.ops.anyimage.generate_da3_depth(
input_path=str(source_path),
source_object_name=source_object.name,
depth_data_name=depth_data_name(source_object),
@@ -306,7 +306,7 @@ def build_original_depth_response(
push_undo("Convert to Depth Plane")
class GenerateDa3Depth(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.generate_da3_depth"
bl_idname = "anyimage.generate_da3_depth"
bl_label = "Generate DA3 Depth"
job_type = "generate-depth"
starting_message = "Starting depth estimation"
@@ -326,7 +326,7 @@ class GenerateDa3Depth(JobOperatorBase, bpy.types.Operator):
require_model(self.model)
if self.remove_background:
require_model(BEN2_MODEL_KEY)
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
return {
**da3_parameters(
settings,
@@ -358,7 +358,7 @@ class GenerateDa3Depth(JobOperatorBase, bpy.types.Operator):
class GenerateMoge2Depth(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.generate_moge2_depth"
bl_idname = "anyimage.generate_moge2_depth"
bl_label = "Generate MoGe-2 Depth"
job_type = "generate-depth"
starting_message = "Starting MoGe-2 inference"
@@ -377,7 +377,7 @@ class GenerateMoge2Depth(JobOperatorBase, bpy.types.Operator):
require_model(self.model)
if self.remove_background:
require_model(BEN2_MODEL_KEY)
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
return {
**moge2_parameters(
settings,
@@ -407,7 +407,7 @@ class GenerateMoge2Depth(JobOperatorBase, bpy.types.Operator):
class GenerateOriginalBackground(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.generate_original_background"
bl_idname = "anyimage.generate_original_background"
bl_label = "Generate Original Background"
job_type = "remove-background"
starting_message = "Starting background removal"
@@ -421,7 +421,7 @@ class GenerateOriginalBackground(JobOperatorBase, bpy.types.Operator):
def request(self, context):
require_environment()
require_model(BEN2_MODEL_KEY)
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
return {
"input": str(background_input_path(self.input_path)),
"model": BEN2_MODEL_KEY,
@@ -446,7 +446,7 @@ class GenerateOriginalBackground(JobOperatorBase, bpy.types.Operator):
class ExtractOriginalFrames(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.extract_original_frames"
bl_idname = "anyimage.extract_original_frames"
bl_label = "Extract Original Frames"
job_type = "extract-frames"
starting_message = "Extracting source frames"
@@ -459,7 +459,7 @@ class ExtractOriginalFrames(JobOperatorBase, bpy.types.Operator):
def request(self, context):
require_environment()
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
return {
"input": str(input_path(self.input_path)),
"video_frames": settings.video_frames,

View File

@@ -205,7 +205,7 @@ def draw_process_resolution(layout, owner, image):
def save_packed_image(image, scene):
output_path = Path(tempfile.mkdtemp(prefix="snapform-input-")) / "input.png"
output_path = Path(tempfile.mkdtemp(prefix="anyimage-input-")) / "input.png"
image_settings = scene.render.image_settings
original_file_format = image_settings.file_format
try:
@@ -261,7 +261,7 @@ def prepare_image_input(image, scene):
return source_path, False
sequence_frames = collect_sequence_frames(image)
if sequence_frames:
temporary_directory = Path(tempfile.mkdtemp(prefix="snapform-input-"))
temporary_directory = Path(tempfile.mkdtemp(prefix="anyimage-input-"))
try:
for index, frame_path in enumerate(sequence_frames):
suffix = frame_path.suffix.lower() or ".jpg"

View File

@@ -10,7 +10,7 @@ ORIGINAL_DISPLACEMENT_MIDLEVEL = 0.0
BUMP_BASE_DISTANCE = 0.001
DEPTH_PLANE_NODE_GROUP_NAME = "O DA3 Depth Plane"
MOGE_DEPTH_PLANE_NODE_GROUP_NAME = "O MoGe2 Depth Plane"
NODE_ASSET_NAME = "O_SnapForm.blend"
NODE_ASSET_NAME = "O_AnyImage.blend"
MATERIAL_DISPLACEMENT_ATTRIBUTE = "o_material_displacement"
BACKGROUND_DEPTH_ATTRIBUTE = "o_background_depth"
DEPTH_SCALE_ATTRIBUTE = "o_depth_scale"

View File

@@ -6,7 +6,7 @@ from .common import HIDDEN, require_environment
class DownloadModel(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.download_model"
bl_idname = "anyimage.download_model"
bl_label = "Download Model"
job_type = "download-model"
@@ -23,7 +23,7 @@ class DownloadModel(JobOperatorBase, bpy.types.Operator):
class DownloadRequiredModels(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.download_required_models"
bl_idname = "anyimage.download_required_models"
bl_label = "Download Required Models"
job_type = "download-required-models"
starting_message = "Starting required model downloads"
@@ -36,7 +36,7 @@ class DownloadRequiredModels(JobOperatorBase, bpy.types.Operator):
class ClearModels(bpy.types.Operator):
bl_idname = "snapform.clear_models"
bl_idname = "anyimage.clear_models"
bl_label = "Clear Models"
def execute(self, _context):

View File

@@ -50,7 +50,7 @@ OPTIONAL_FACE_ATTRIBUTES = {
}
NODE_GROUP_NAME = "O DA3 Surface"
MOGE_NODE_GROUP_NAME = "O MoGe2 Surface"
NODE_ASSET_NAME = "O_SnapForm.blend"
NODE_ASSET_NAME = "O_AnyImage.blend"
def image_surface_model_items(owner, context):
@@ -212,7 +212,7 @@ def assign_mesh_attribute(mesh, name, data_type, domain, values):
attribute.data.foreach_set("value", array.reshape(-1))
def assign_snapform_attributes(mesh, attributes, moge2=False):
def assign_anyimage_attributes(mesh, attributes, moge2=False):
for name, (data_type, key) in POINT_ATTRIBUTES.items():
assign_mesh_attribute(
mesh,
@@ -446,7 +446,7 @@ def build_image_mesh(
mesh = bpy.data.meshes.new(source_name)
mesh.from_pydata(vertices.tolist(), [], faces.tolist())
assign_vertex_uv(mesh, uv)
assign_snapform_attributes(mesh, attributes, moge2=moge2)
assign_anyimage_attributes(mesh, attributes, moge2=moge2)
mesh.update()
for polygon in mesh.polygons:
polygon.use_smooth = True
@@ -476,8 +476,8 @@ def build_image_mesh(
scene=context.scene,
)
mesh.materials.append(material)
mesh_object["snapform_reference_depth"] = reference_depth
mesh_object["snapform_source_type"] = "IMAGE_MESH"
mesh_object["anyimage_reference_depth"] = reference_depth
mesh_object["anyimage_source_type"] = "IMAGE_MESH"
add_geometry_nodes_processing(
mesh_object,
vertices,
@@ -505,7 +505,7 @@ def build_image_mesh(
class ConvertToSurface(bpy.types.Operator):
bl_idname = "snapform.convert_to_surface"
bl_idname = "anyimage.convert_to_surface"
bl_label = "Convert to Surface"
bl_description = "Convert an Image Empty into a perspective textured surface"
bl_options = {"REGISTER"}
@@ -604,12 +604,12 @@ class ConvertToSurface(bpy.types.Operator):
"remove_background": self.remove_background,
}
if self.model.startswith("MOGE2_"):
result = bpy.ops.snapform.generate_moge2_surface(
result = bpy.ops.anyimage.generate_moge2_surface(
**common,
resolution_level=self.resolution_level,
)
else:
result = bpy.ops.snapform.generate_da3_surface(
result = bpy.ops.anyimage.generate_da3_surface(
**common,
process_res=process_res,
)
@@ -662,7 +662,7 @@ def build_surface_response(
class GenerateDa3Surface(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.generate_da3_surface"
bl_idname = "anyimage.generate_da3_surface"
bl_label = "Generate DA3 Surface"
job_type = "generate-surface"
starting_message = "Starting perspective surface generation"
@@ -682,7 +682,7 @@ class GenerateDa3Surface(JobOperatorBase, bpy.types.Operator):
require_model(self.model)
if self.remove_background:
require_model(BEN2_MODEL_KEY)
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
return {
**da3_parameters(
settings,
@@ -713,7 +713,7 @@ class GenerateDa3Surface(JobOperatorBase, bpy.types.Operator):
class GenerateMoge2Surface(JobOperatorBase, bpy.types.Operator):
bl_idname = "snapform.generate_moge2_surface"
bl_idname = "anyimage.generate_moge2_surface"
bl_label = "Generate MoGe-2 Surface"
job_type = "generate-surface"
starting_message = "Starting MoGe-2 inference"
@@ -733,7 +733,7 @@ class GenerateMoge2Surface(JobOperatorBase, bpy.types.Operator):
require_model(self.model)
if self.remove_background:
require_model(BEN2_MODEL_KEY)
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
return {
**moge2_parameters(
settings,

View File

@@ -5,10 +5,10 @@ from .properties import BEN2_MODEL_KEY, model_catalog, model_ready
class ServerPanel(bpy.types.Panel):
bl_label = "Job Server"
bl_idname = "SNAPFORM_PT_server"
bl_idname = "ANYIMAGE_PT_server"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "SnapForm"
bl_category = "AnyImage"
bl_order = 0
def draw(self, _context):
@@ -22,14 +22,14 @@ class ServerPanel(bpy.types.Panel):
install_row = layout.row()
install_row.enabled = not running
install_row.operator(
"snapform.install_environment",
"anyimage.install_environment",
text="Install Environment",
icon="IMPORT",
)
mirror_row = layout.row()
mirror_row.enabled = not running
mirror_row.operator(
"snapform.install_environment_mirror",
"anyimage.install_environment_mirror",
text="Install Environment (Mirror)",
icon="IMPORT",
)
@@ -58,18 +58,18 @@ class ServerPanel(bpy.types.Panel):
if state == "READY":
controls = row.row(align=True)
controls.operator(
"snapform.restart_server",
"anyimage.restart_server",
text="",
icon="FILE_REFRESH",
)
controls.operator(
"snapform.stop_server",
"anyimage.stop_server",
text="",
icon="PAUSE",
)
elif state == "STOPPED":
row.operator(
"snapform.start_server",
"anyimage.start_server",
text="Start",
icon="PLAY",
)
@@ -80,7 +80,7 @@ class ServerPanel(bpy.types.Panel):
)
if state == "ERROR":
row.operator(
"snapform.start_server",
"anyimage.start_server",
text="Start",
icon="PLAY",
)
@@ -103,7 +103,7 @@ class ServerPanel(bpy.types.Panel):
clear = cached_row.row(align=True)
clear.enabled = state == "READY"
clear.operator(
"snapform.clear_models",
"anyimage.clear_models",
text="",
icon="TRASH",
)
@@ -111,10 +111,10 @@ class ServerPanel(bpy.types.Panel):
class ModelsPanel(bpy.types.Panel):
bl_label = "Models"
bl_idname = "SNAPFORM_PT_models"
bl_idname = "ANYIMAGE_PT_models"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "SnapForm"
bl_category = "AnyImage"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 1
@@ -150,7 +150,7 @@ class ModelsPanel(bpy.types.Panel):
download_row = row.row(align=True)
download_row.enabled = runtime.environment_ready() and not running
operator = download_row.operator(
"snapform.download_model",
"anyimage.download_model",
text="Download",
icon="IMPORT",
)
@@ -159,16 +159,16 @@ class ModelsPanel(bpy.types.Panel):
class DebugPanel(bpy.types.Panel):
bl_label = "Debug"
bl_idname = "SNAPFORM_PT_debug"
bl_idname = "ANYIMAGE_PT_debug"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "SnapForm"
bl_category = "AnyImage"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 2
def draw(self, context):
layout = self.layout
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
layout.prop(settings, "device")
@@ -198,17 +198,17 @@ def draw_da3_input_settings(layout, settings, model_property):
class Da3OutputDebugPanel(bpy.types.Panel):
bl_label = "DA3"
bl_idname = "SNAPFORM_PT_debug_da3_output"
bl_idname = "ANYIMAGE_PT_debug_da3_output"
bl_parent_id = DebugPanel.bl_idname
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "SnapForm"
bl_category = "AnyImage"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 1
def draw(self, context):
layout = self.layout
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
draw_da3_input_settings(layout, settings, "model")
export_row = layout.row()
@@ -217,7 +217,7 @@ class Da3OutputDebugPanel(bpy.types.Panel):
and model_ready(settings.model)
)
export_row.operator(
"snapform.debug_da3",
"anyimage.debug_da3",
text="Run DA3 API",
icon="EXPORT",
)
@@ -225,17 +225,17 @@ class Da3OutputDebugPanel(bpy.types.Panel):
class Moge2ApiDebugPanel(bpy.types.Panel):
bl_label = "MoGe-2"
bl_idname = "SNAPFORM_PT_debug_moge2_api"
bl_idname = "ANYIMAGE_PT_debug_moge2_api"
bl_parent_id = DebugPanel.bl_idname
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "SnapForm"
bl_category = "AnyImage"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 0
def draw(self, context):
layout = self.layout
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
layout.prop(settings, "input_path")
layout.prop(settings, "moge_model")
layout.prop(settings, "moge_resolution_level")
@@ -245,7 +245,7 @@ class Moge2ApiDebugPanel(bpy.types.Panel):
and model_ready(settings.moge_model)
)
run_row.operator(
"snapform.debug_moge2",
"anyimage.debug_moge2",
text="Run MoGe-2 API",
icon="EXPORT",
)
@@ -253,17 +253,17 @@ class Moge2ApiDebugPanel(bpy.types.Panel):
class Ben2RemovalDebugPanel(bpy.types.Panel):
bl_label = "BEN2"
bl_idname = "SNAPFORM_PT_debug_ben2_removal"
bl_idname = "ANYIMAGE_PT_debug_ben2_removal"
bl_parent_id = DebugPanel.bl_idname
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "SnapForm"
bl_category = "AnyImage"
bl_options = {"DEFAULT_CLOSED"}
bl_order = 2
def draw(self, context):
layout = self.layout
settings = context.scene.snapform_settings
settings = context.scene.anyimage_settings
layout.prop(settings, "background_input_path")
background_row = layout.row()
@@ -272,7 +272,7 @@ class Ben2RemovalDebugPanel(bpy.types.Panel):
and model_ready(BEN2_MODEL_KEY)
)
background_row.operator(
"snapform.debug_ben2",
"anyimage.debug_ben2",
text="Remove Background",
icon="IMAGE_ALPHA",
)

View File

@@ -4,7 +4,7 @@ from .runtime import DEFAULT_STORAGE_ROOT, runtime
from .properties import device_enum_items
class SnapFormPreferences(bpy.types.AddonPreferences):
class AnyImagePreferences(bpy.types.AddonPreferences):
bl_idname = __package__
storage_root: bpy.props.StringProperty(
@@ -38,20 +38,20 @@ class SnapFormPreferences(bpy.types.AddonPreferences):
install_row = layout.row()
install_row.enabled = runtime.active_job is None
install_row.operator(
"snapform.install_environment",
"anyimage.install_environment",
text="Install Environment",
icon="IMPORT",
)
mirror_row = layout.row()
mirror_row.enabled = runtime.active_job is None
mirror_row.operator(
"snapform.install_environment_mirror",
"anyimage.install_environment_mirror",
text="Install Environment (Mirror)",
icon="IMPORT",
)
layout.separator()
layout.operator(
"snapform.open_server_log",
"anyimage.open_server_log",
text="Open Server Log",
icon="TEXT",
)

View File

@@ -269,7 +269,7 @@ def balloon_radial_zoom_property(scale):
)
class SnapFormSettings(bpy.types.PropertyGroup):
class AnyImageSettings(bpy.types.PropertyGroup):
balloon_mode: balloon_mode_property()
balloon_thickness: balloon_thickness_property()
balloon_resolution: balloon_resolution_property()

View File

@@ -5,7 +5,7 @@ import bpy
from blendjob import JobRuntime
DEFAULT_STORAGE_ROOT = Path.home() / ".snapform"
DEFAULT_STORAGE_ROOT = Path.home() / ".anyimage"
def configured_storage_root():
@@ -53,7 +53,7 @@ runtime = JobRuntime(
storage_root=configured_storage_root,
environment=ENVIRONMENT,
post_install=post_install,
namespace="snapform",
namespace="anyimage",
)
JobOperatorBase = runtime.JobOperatorBase

View File

@@ -1,4 +1,4 @@
"""SnapForm Job Server without Blender dependencies."""
"""AnyImage Job Server without Blender dependencies."""
from blendjob import JobServer
@@ -13,7 +13,7 @@ from .jobs.frames import run as _extract_frames
from .model_manager import ModelManager, ModelSessionCache
server = JobServer("SnapForm Job Server")
server = JobServer("AnyImage Job Server")
@server.resource("model_manager")

View File

@@ -101,7 +101,7 @@ def prepare_input(
import av
temporary_directory = Path(
tempfile.mkdtemp(prefix="snapform-video-", dir=temporary_root)
tempfile.mkdtemp(prefix="anyimage-video-", dir=temporary_root)
)
try:
images = extract_video_frames(

View File

@@ -116,7 +116,7 @@ def create_download_progress_class(
description = str(kwargs.get("desc", ""))
# huggingface_hub 1.x 的字节进度条描述是 "Downloading (incomplete total...)",
# 用前缀匹配而非精确匹配,否则真实下载进度会被忽略。
self.snapform_reports_progress = (
self.anyimage_reports_progress = (
description.startswith("Downloading")
or description.startswith("Fetching ")
)
@@ -124,13 +124,13 @@ def create_download_progress_class(
def update(self, amount=1):
result = super().update(amount)
if self.snapform_reports_progress:
if self.anyimage_reports_progress:
reporter.report(self.n, self.total, self._download_rate())
return result
def refresh(self, *args, **kwargs):
result = super().refresh(*args, **kwargs)
if self.snapform_reports_progress:
if self.anyimage_reports_progress:
reporter.report(self.n, self.total, self._download_rate())
return result
@@ -295,7 +295,7 @@ def _download_file_from_url(
cancel_check=None,
):
"""Download and verify one model file atomically from a direct URL."""
request = Request(url, headers={"User-Agent": "SnapForm"})
request = Request(url, headers={"User-Agent": "AnyImage"})
opener = _download_opener.open if request.type == "https" else urlopen
last_error = None
for _attempt in range(retries):

View File

@@ -53,7 +53,7 @@ def infer(
if provider_label(model) == "DirectML":
raise RuntimeError(
"DA3 inference failed on DirectML. Switch the device to "
"CPU in SnapForm Preferences and retry. "
"CPU in AnyImage Preferences and retry. "
f"Original error: {error}"
) from error
raise

View File

@@ -1,4 +1,4 @@
"""Prepare SnapForm ONNX models and sync them to OmooLab R2."""
"""Prepare AnyImage ONNX models and sync them to OmooLab R2."""
import argparse
import hashlib
@@ -114,7 +114,7 @@ def download_once(model, url):
destination.parent.mkdir(parents=True, exist_ok=True)
temporary = destination.with_suffix(".onnx.part")
temporary.unlink(missing_ok=True)
request = Request(url, headers={"User-Agent": "SnapForm model sync"})
request = Request(url, headers={"User-Agent": "AnyImage model sync"})
print(f"Download: {url}")
downloaded = 0
digest = hashlib.sha256()

View File

@@ -19,7 +19,7 @@ class AddonTest(unittest.TestCase):
cls.remove_modules()
def append_status_bar(draw):
draw._owner = "snapform"
draw._owner = "anyimage"
cls.status_bar_draws.append(draw)
fake_bpy = ModuleType("bpy")
@@ -79,28 +79,28 @@ class AddonTest(unittest.TestCase):
sys.modules["bpy"] = fake_bpy
sys.path.insert(0, str(SOURCE_ROOT))
cls.paths_before_import = tuple(sys.path)
cls.addon = importlib.import_module("snapform")
cls.job_runtime = importlib.import_module("snapform.runtime")
cls.model_operators = importlib.import_module("snapform.operators.models")
cls.image_depth = importlib.import_module("snapform.operators.material")
cls.image_data = importlib.import_module("snapform.operators.image")
cls.addon = importlib.import_module("anyimage")
cls.job_runtime = importlib.import_module("anyimage.runtime")
cls.model_operators = importlib.import_module("anyimage.operators.models")
cls.image_depth = importlib.import_module("anyimage.operators.material")
cls.image_data = importlib.import_module("anyimage.operators.image")
cls.depth_operator = importlib.import_module(
"snapform.operators.depth_plane.operator"
"anyimage.operators.depth_plane.operator"
)
cls.depth_build = importlib.import_module(
"snapform.operators.depth_plane.build"
"anyimage.operators.depth_plane.build"
)
cls.image_background = importlib.import_module("snapform.operators.background")
cls.image_mesh = importlib.import_module("snapform.operators.surface")
cls.balloon = importlib.import_module("snapform.operators.balloon.operator")
cls.image_background = importlib.import_module("anyimage.operators.background")
cls.image_mesh = importlib.import_module("anyimage.operators.surface")
cls.balloon = importlib.import_module("anyimage.operators.balloon.operator")
cls.balloon_geometry = importlib.import_module(
"snapform.operators.balloon.geometry"
"anyimage.operators.balloon.geometry"
)
cls.balloon_depth = importlib.import_module("snapform.operators.balloon.depth")
cls.balloon_depth = importlib.import_module("anyimage.operators.balloon.depth")
cls.balloon_poisson = importlib.import_module(
"snapform.operators.balloon.poisson"
"anyimage.operators.balloon.poisson"
)
cls.balloon_teddy = importlib.import_module("snapform.operators.balloon.teddy")
cls.balloon_teddy = importlib.import_module("anyimage.operators.balloon.teddy")
cls.paths_after_import = tuple(sys.path)
cls.fake_bpy = fake_bpy
@@ -113,7 +113,7 @@ class AddonTest(unittest.TestCase):
@staticmethod
def remove_modules():
for module_name in tuple(sys.modules):
if module_name == "snapform" or module_name.startswith("snapform."):
if module_name == "anyimage" or module_name.startswith("anyimage."):
sys.modules.pop(module_name)
def setUp(self):
@@ -122,7 +122,7 @@ class AddonTest(unittest.TestCase):
device="CUDA",
)
self.fake_bpy.context.preferences.addons = {
"snapform": SimpleNamespace(preferences=preferences)
"anyimage": SimpleNamespace(preferences=preferences)
}
def tearDown(self):
@@ -135,8 +135,8 @@ class AddonTest(unittest.TestCase):
self.fake_bpy.data = SimpleNamespace(
objects=SimpleNamespace(get=lambda _name: None),
)
if hasattr(self.fake_bpy.types.Scene, "snapform_settings"):
del self.fake_bpy.types.Scene.snapform_settings
if hasattr(self.fake_bpy.types.Scene, "anyimage_settings"):
del self.fake_bpy.types.Scene.anyimage_settings
def test_runtime_and_operator_support_modules_have_explicit_boundaries(self):
for name in (
@@ -146,13 +146,13 @@ class AddonTest(unittest.TestCase):
"models.py",
"status_bar.py",
):
self.assertFalse((SOURCE_ROOT / "snapform" / name).exists(), name)
self.assertTrue((SOURCE_ROOT / "snapform" / "runtime.py").is_file())
self.assertFalse((SOURCE_ROOT / "snapform" / "job_runtime.py").exists())
self.assertFalse((SOURCE_ROOT / "snapform" / "client").exists())
self.assertFalse((SOURCE_ROOT / "snapform" / "ui").exists())
self.assertFalse((SOURCE_ROOT / "snapform" / "operators.py").exists())
self.assertFalse((SOURCE_ROOT / "snapform" / "blender").exists())
self.assertFalse((SOURCE_ROOT / "anyimage" / name).exists(), name)
self.assertTrue((SOURCE_ROOT / "anyimage" / "runtime.py").is_file())
self.assertFalse((SOURCE_ROOT / "anyimage" / "job_runtime.py").exists())
self.assertFalse((SOURCE_ROOT / "anyimage" / "client").exists())
self.assertFalse((SOURCE_ROOT / "anyimage" / "ui").exists())
self.assertFalse((SOURCE_ROOT / "anyimage" / "operators.py").exists())
self.assertFalse((SOURCE_ROOT / "anyimage" / "blender").exists())
def test_mps_requires_m4_or_newer(self):
properties = self.addon.properties
@@ -205,7 +205,7 @@ class AddonTest(unittest.TestCase):
self.assertEqual(self.paths_after_import, self.paths_before_import)
self.assertFalse(
any(
name == "snapform.server" or name.startswith("snapform.server.")
name == "anyimage.server" or name.startswith("anyimage.server.")
for name in sys.modules
)
)
@@ -217,7 +217,7 @@ class AddonTest(unittest.TestCase):
def test_operator_modules_use_plain_properties(self):
operators = self.addon.operators
for name in (
"SnapFormJobMixin",
"AnyImageJobMixin",
"ImageInputJobOperator",
"GeometryImageJobOperator",
"Da3ImageJobOperator",
@@ -227,11 +227,11 @@ class AddonTest(unittest.TestCase):
self.assertFalse(hasattr(operators, name), name)
for operator in operators.CLASSES:
for name in getattr(operator, "__annotations__", {}):
self.assertFalse(name.startswith("snapform_"), (operator, name))
self.assertFalse(name.startswith("anyimage_"), (operator, name))
def test_concrete_job_operator_is_the_blender_operator(self):
operator = self.addon.operators.GenerateMoge2Depth
self.assertEqual(operator.bl_idname, "snapform.generate_moge2_depth")
self.assertEqual(operator.bl_idname, "anyimage.generate_moge2_depth")
self.assertIn(self.job_runtime.JobOperatorBase, operator.__mro__)
self.assertIn(self.fake_bpy.types.Operator, operator.__mro__)
@@ -364,7 +364,7 @@ class AddonTest(unittest.TestCase):
)
self.assertTrue(callable(runtime.post_install))
def test_snapform_post_install_is_ordinary_code_using_runtime_request(self):
def test_anyimage_post_install_is_ordinary_code_using_runtime_request(self):
calls = []
fake_runtime = SimpleNamespace(
request=lambda job_type, parameters: calls.append((job_type, parameters))
@@ -378,7 +378,7 @@ class AddonTest(unittest.TestCase):
],
)
def test_snapform_post_install_names_each_failed_model(self):
def test_anyimage_post_install_names_each_failed_model(self):
calls = []
def request(_job_type, parameters):
@@ -476,7 +476,7 @@ class AddonTest(unittest.TestCase):
self.assertNotIn("stages", controller.post_status)
def test_conversion_model_lists_default_to_moge2_vits(self):
properties = importlib.import_module("snapform.properties")
properties = importlib.import_module("anyimage.properties")
catalog = (
{
"key": "SMALL",
@@ -534,7 +534,7 @@ class AddonTest(unittest.TestCase):
def separator(self):
calls.append(("separator", self.enabled))
menu = self.addon.SnapFormImageMenu()
menu = self.addon.AnyImageImageMenu()
menu.layout = Layout()
with (
patch.object(
@@ -543,7 +543,7 @@ class AddonTest(unittest.TestCase):
return_value=True,
),
patch(
"snapform.properties.required_models_ready",
"anyimage.properties.required_models_ready",
return_value=False,
),
):
@@ -551,15 +551,15 @@ class AddonTest(unittest.TestCase):
self.assertEqual(
calls[0],
("snapform.activate_balloon_tool", True),
("anyimage.activate_balloon_tool", True),
)
self.assertIn(
("snapform.download_required_models", True),
("anyimage.download_required_models", True),
calls,
)
self.assertIn(("snapform.convert_to_depth_plane", False), calls)
self.assertIn(("snapform.convert_to_surface", False), calls)
self.assertIn(("snapform.remove_image_background", False), calls)
self.assertIn(("anyimage.convert_to_depth_plane", False), calls)
self.assertIn(("anyimage.convert_to_surface", False), calls)
self.assertIn(("anyimage.remove_image_background", False), calls)
def test_image_menu_keeps_disabled_features_when_environment_is_missing(self):
calls = []
@@ -581,7 +581,7 @@ class AddonTest(unittest.TestCase):
def separator(self):
calls.append(("separator", self.enabled))
menu = self.addon.SnapFormImageMenu()
menu = self.addon.AnyImageImageMenu()
menu.layout = Layout()
with (
patch.object(
@@ -590,7 +590,7 @@ class AddonTest(unittest.TestCase):
return_value=False,
),
patch(
"snapform.properties.required_models_ready",
"anyimage.properties.required_models_ready",
return_value=False,
),
):
@@ -598,14 +598,14 @@ class AddonTest(unittest.TestCase):
self.assertEqual(
calls[0],
("snapform.activate_balloon_tool", True),
("anyimage.activate_balloon_tool", True),
)
self.assertIn(("snapform.install_environment", True), calls)
self.assertIn(("snapform.install_environment_mirror", True), calls)
self.assertNotIn(("snapform.download_required_models", True), calls)
self.assertIn(("snapform.convert_to_depth_plane", False), calls)
self.assertIn(("snapform.convert_to_surface", False), calls)
self.assertIn(("snapform.remove_image_background", False), calls)
self.assertIn(("anyimage.install_environment", True), calls)
self.assertIn(("anyimage.install_environment_mirror", True), calls)
self.assertNotIn(("anyimage.download_required_models", True), calls)
self.assertIn(("anyimage.convert_to_depth_plane", False), calls)
self.assertIn(("anyimage.convert_to_surface", False), calls)
self.assertIn(("anyimage.remove_image_background", False), calls)
def test_balloon_contour_is_smooth_and_evenly_resampled(self):
import numpy as np
@@ -892,7 +892,7 @@ class AddonTest(unittest.TestCase):
self.assertIn("contour_analysis", operator.__annotations__)
self.assertIn("depth_analysis", operator.__annotations__)
self.assertIn("depth_model", operator.__annotations__)
settings = self.addon.properties.SnapFormSettings.__annotations__
settings = self.addon.properties.AnyImageSettings.__annotations__
for name in (
"balloon_contour_analysis",
"balloon_depth_analysis",
@@ -908,7 +908,7 @@ class AddonTest(unittest.TestCase):
request_source = inspect.getsource(self.balloon.AnalyzeBalloon.request)
response_source = inspect.getsource(self.balloon.AnalyzeBalloon.response)
tool_source = inspect.getsource(self.balloon.BalloonTool.draw_settings)
self.assertIn("bpy.ops.snapform.analyze_balloon", execute_source)
self.assertIn("bpy.ops.anyimage.analyze_balloon", execute_source)
self.assertIn("prepare_image_input(", execute_source)
self.assertIn(
"settings.balloon_contour_analysis",
@@ -1103,7 +1103,7 @@ class AddonTest(unittest.TestCase):
self.assertIn("resolution", operator.__annotations__)
self.assertIn("reuse_material", operator.__annotations__)
self.assertIn("source_matrix_data", operator.__annotations__)
settings = self.addon.properties.SnapFormSettings.__annotations__
settings = self.addon.properties.AnyImageSettings.__annotations__
self.assertIn("balloon_mode", settings)
self.assertIn("balloon_thickness", settings)
self.assertIn("balloon_resolution", settings)
@@ -1150,7 +1150,7 @@ class AddonTest(unittest.TestCase):
self.assertIn("return self.execute(context)", modal_source)
self.assertIn('return {"RUNNING_MODAL"}', modal_source)
self.assertIn('return {"PASS_THROUGH"}', modal_source)
self.assertNotIn("bpy.ops.snapform.extract_balloon", modal_source)
self.assertNotIn("bpy.ops.anyimage.extract_balloon", modal_source)
tool = self.balloon.BalloonTool
self.assertEqual(tool.bl_space_type, "VIEW_3D")
@@ -1169,11 +1169,11 @@ class AddonTest(unittest.TestCase):
[
(
"data_path_primary",
"scene.snapform_settings.balloon_thickness",
"scene.anyimage_settings.balloon_thickness",
),
(
"zoom_path",
"scene.snapform_settings.balloon_thickness_radial_zoom",
"scene.anyimage_settings.balloon_thickness_radial_zoom",
),
],
)
@@ -1182,11 +1182,11 @@ class AddonTest(unittest.TestCase):
[
(
"data_path_primary",
"scene.snapform_settings.balloon_resolution",
"scene.anyimage_settings.balloon_resolution",
),
(
"zoom_path",
"scene.snapform_settings.balloon_resolution_radial_zoom",
"scene.anyimage_settings.balloon_resolution_radial_zoom",
),
],
)
@@ -1281,20 +1281,20 @@ class AddonTest(unittest.TestCase):
)
marked = MarkedMaterial(
"unrelated-name",
snapform_balloon_material=True,
snapform_source_image=color_image,
anyimage_balloon_material=True,
anyimage_source_image=color_image,
)
marked_other = MarkedMaterial(
"cow",
snapform_balloon_material=True,
snapform_source_image=other_image,
anyimage_balloon_material=True,
anyimage_source_image=other_image,
)
normal_image = SimpleNamespace(name="cow.normal")
marked_normal = MarkedMaterial(
"depth-material",
snapform_balloon_material=True,
snapform_source_image=color_image,
snapform_balloon_normal_image=normal_image,
anyimage_balloon_material=True,
anyimage_source_image=color_image,
anyimage_balloon_normal_image=normal_image,
)
wrong_group = SimpleNamespace(
name="cow",
@@ -1393,7 +1393,7 @@ class AddonTest(unittest.TestCase):
return_value=False,
),
patch(
"snapform.properties.required_models_ready",
"anyimage.properties.required_models_ready",
return_value=models_ready,
),
):
@@ -1414,7 +1414,7 @@ class AddonTest(unittest.TestCase):
self.assertEqual(operator.poll(context), expected)
def test_ui_reads_required_model_files_without_starting_server(self):
properties = importlib.import_module("snapform.properties")
properties = importlib.import_module("anyimage.properties")
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
moge = root / "models" / "moge-2-vits-normal-onnx"
@@ -1516,7 +1516,7 @@ class AddonTest(unittest.TestCase):
],
)
self.assertEqual(len(self.status_bar_draws), 1)
self.assertEqual(self.status_bar_draws[0]._owner, "snapform")
self.assertEqual(self.status_bar_draws[0]._owner, "anyimage")
self.addon.unregister()
self.assertEqual(self.registered, [])
@@ -1545,7 +1545,7 @@ class AddonTest(unittest.TestCase):
self.assertEqual(calls[0][1]["factor"], 0.4)
self.assertEqual(calls[0][1]["text"], "Working")
self.assertEqual(calls[1], ("operator", "snapform.cancel_job"))
self.assertEqual(calls[1], ("operator", "anyimage.cancel_job"))
def test_moge_depth_request_is_written_on_the_operator(self):
operator = self.addon.operators.GenerateMoge2Depth()
@@ -1555,7 +1555,7 @@ class AddonTest(unittest.TestCase):
operator.resolution_level = 6
operator.remove_background = False
settings = SimpleNamespace(video_frames=12)
context = SimpleNamespace(scene=SimpleNamespace(snapform_settings=settings))
context = SimpleNamespace(scene=SimpleNamespace(anyimage_settings=settings))
with (
patch.object(self.depth_operator, "require_environment"),
@@ -1603,11 +1603,11 @@ class AddonTest(unittest.TestCase):
settings = SimpleNamespace(video_frames=1)
context = SimpleNamespace(
object=source_object,
scene=SimpleNamespace(snapform_settings=settings),
scene=SimpleNamespace(anyimage_settings=settings),
)
calls = []
self.fake_bpy.ops = SimpleNamespace(
snapform=SimpleNamespace(
anyimage=SimpleNamespace(
generate_moge2_depth=lambda **options: (
calls.append(options) or {"RUNNING_MODAL"}
)
@@ -1623,7 +1623,7 @@ class AddonTest(unittest.TestCase):
self.assertEqual(calls[0]["resolution_level"], 7)
self.assertEqual(calls[0]["source_object_name"], "Reference")
self.assertFalse(any(key.startswith("snapform_") for key in calls[0]))
self.assertFalse(any(key.startswith("anyimage_") for key in calls[0]))
if __name__ == "__main__":

View File

@@ -10,7 +10,7 @@ from PIL import Image
SOURCE_DIR = (
Path(__file__).resolve().parents[1]
/ "src"
/ "snapform"
/ "anyimage"
/ "server"
/ "models"
)

View File

@@ -11,7 +11,7 @@ from PIL import Image
SOURCE_DIR = (
Path(__file__).resolve().parents[1]
/ "src"
/ "snapform"
/ "anyimage"
/ "server"
/ "models"
)

View File

@@ -10,7 +10,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
def load_download_module():
addon_directory = PROJECT_ROOT / "src" / "snapform"
addon_directory = PROJECT_ROOT / "src" / "anyimage"
sys.path.insert(0, str(addon_directory))
try:
return __import__(

View File

@@ -27,28 +27,28 @@ def load_image_depth():
module_paths = (
(
"snapform.operators.image",
PROJECT_ROOT / "src" / "snapform" / "operators" / "image.py",
"anyimage.operators.image",
PROJECT_ROOT / "src" / "anyimage" / "operators" / "image.py",
),
(
"snapform.operators.material",
PROJECT_ROOT / "src" / "snapform" / "operators" / "material.py",
"anyimage.operators.material",
PROJECT_ROOT / "src" / "anyimage" / "operators" / "material.py",
),
(
"snapform.operators.depth_plane.data",
PROJECT_ROOT / "src" / "snapform" / "operators" / "depth_plane" / "data.py",
"anyimage.operators.depth_plane.data",
PROJECT_ROOT / "src" / "anyimage" / "operators" / "depth_plane" / "data.py",
),
(
"snapform.operators.depth_plane.build",
PROJECT_ROOT / "src" / "snapform" / "operators" / "depth_plane" / "build.py",
"anyimage.operators.depth_plane.build",
PROJECT_ROOT / "src" / "anyimage" / "operators" / "depth_plane" / "build.py",
),
)
packages = {
name: ModuleType(name)
for name in (
"snapform",
"snapform.operators",
"snapform.operators.depth_plane",
"anyimage",
"anyimage.operators",
"anyimage.operators.depth_plane",
)
}
for package in packages.values():
@@ -81,7 +81,7 @@ class ImageDepthTest(unittest.TestCase):
def test_material_asset_path_survives_package_split(self):
self.assertEqual(
self.image_depth.node_asset_path(),
PROJECT_ROOT / "src" / "snapform" / "assets" / "O_SnapForm.blend",
PROJECT_ROOT / "src" / "anyimage" / "assets" / "O_AnyImage.blend",
)
def test_material_color_space_matches_scene_view_transform(self):
mappings = {

View File

@@ -4,7 +4,7 @@ from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
MANIFEST_PATH = PROJECT_ROOT / "src" / "snapform" / "blender_manifest.toml"
MANIFEST_PATH = PROJECT_ROOT / "src" / "anyimage" / "blender_manifest.toml"
class ManifestTest(unittest.TestCase):
@@ -14,7 +14,7 @@ class ManifestTest(unittest.TestCase):
def test_declares_modern_extension_metadata(self):
self.assertEqual(self.manifest["schema_version"], "1.0.0")
self.assertEqual(self.manifest["id"], "snapform")
self.assertEqual(self.manifest["id"], "anyimage")
self.assertEqual(self.manifest["type"], "add-on")
self.assertNotIn("bl_info", self.manifest)

View File

@@ -20,12 +20,12 @@ class ModelTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.models = load_module(
"snapform_models",
"src/snapform/model_catalog.py",
"anyimage_models",
"src/anyimage/model_catalog.py",
)
cls.server_models = load_module(
"snapform_server_models",
"src/snapform/server/model_catalog.py",
"anyimage_server_models",
"src/anyimage/server/model_catalog.py",
)
def test_client_and_server_catalogs_share_storage_contract(self):

View File

@@ -12,19 +12,19 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
def load_module():
snapform = ModuleType("snapform")
snapform.__path__ = [str(PROJECT_ROOT / "src" / "snapform")]
server = ModuleType("snapform.server")
server.__path__ = [str(PROJECT_ROOT / "src" / "snapform" / "server")]
models = ModuleType("snapform.server.models")
anyimage = ModuleType("anyimage")
anyimage.__path__ = [str(PROJECT_ROOT / "src" / "anyimage")]
server = ModuleType("anyimage.server")
server.__path__ = [str(PROJECT_ROOT / "src" / "anyimage" / "server")]
models = ModuleType("anyimage.server.models")
models.__path__ = [
str(PROJECT_ROOT / "src" / "snapform" / "server" / "models")
str(PROJECT_ROOT / "src" / "anyimage" / "server" / "models")
]
specification = importlib.util.spec_from_file_location(
"snapform.server.models.onnx_moge2",
"anyimage.server.models.onnx_moge2",
PROJECT_ROOT
/ "src"
/ "snapform"
/ "anyimage"
/ "server"
/ "models"
/ "onnx_moge2.py",
@@ -33,9 +33,9 @@ def load_module():
with patch.dict(
sys.modules,
{
"snapform": snapform,
"snapform.server": server,
"snapform.server.models": models,
"anyimage": anyimage,
"anyimage.server": server,
"anyimage.server.models": models,
},
):
specification.loader.exec_module(module)

View File

@@ -12,7 +12,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
def load_pack():
specification = importlib.util.spec_from_file_location(
"snapform_pack",
"anyimage_pack",
PROJECT_ROOT / "pack.py",
)
module = importlib.util.module_from_spec(specification)
@@ -47,7 +47,7 @@ class PackTest(unittest.TestCase):
self.assertEqual(
{path.name for path in output_paths},
{
f"SnapForm.v{version}.{platform}.zip"
f"AnyImage.v{version}.{platform}.zip"
for platform in pack.PLATFORMS
},
)
@@ -66,7 +66,7 @@ class PackTest(unittest.TestCase):
self.assertIn("operators/balloon/operator.py", names)
self.assertIn("operators/surface.py", names)
self.assertIn(
"assets/O_SnapForm.blend",
"assets/O_AnyImage.blend",
names,
)
self.assertIn("server/media/input.py", names)
@@ -100,7 +100,7 @@ class PackTest(unittest.TestCase):
any(name.startswith("blendjob/") for name in names)
)
self.assertNotIn("install_env.py", names)
self.assertNotIn("snapform/__init__.py", names)
self.assertNotIn("anyimage/__init__.py", names)
self.assertNotIn("importer.py", names)
self.assertFalse(
any(name.startswith("sources/") for name in names)

View File

@@ -13,7 +13,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
def load_server_module():
server_directory = PROJECT_ROOT / "src" / "snapform" / "server"
server_directory = PROJECT_ROOT / "src" / "anyimage" / "server"
specification = importlib.util.spec_from_file_location(
"server",
server_directory / "__init__.py",
@@ -73,7 +73,7 @@ class ServerTest(unittest.TestCase):
self.assertEqual(result, {"downloaded": ["MOGE2_VITS_NORMAL"]})
def test_models_are_owned_by_the_server_resource(self):
self.assertFalse((PROJECT_ROOT / "src" / "snapform" / "models.py").exists())
self.assertFalse((PROJECT_ROOT / "src" / "anyimage" / "models.py").exists())
with tempfile.TemporaryDirectory() as directory:
manager = self.server_module.ModelManager(Path(directory))
snapshot = manager.snapshot()
@@ -99,7 +99,7 @@ class ServerTest(unittest.TestCase):
def create_server(self):
source = self.server_module.server
server = self.server_module.JobServer(
"SnapForm Job Server", storage_root=self.storage_root
"AnyImage Job Server", storage_root=self.storage_root
)
server.handlers.update(source.handlers)
server.resource_factories.update(source.resource_factories)

View File

@@ -15,7 +15,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
def load_server_module(name):
addon_directory = PROJECT_ROOT / "src" / "snapform"
addon_directory = PROJECT_ROOT / "src" / "anyimage"
sys.path.insert(0, str(addon_directory))
try:
return __import__(f"server.{name}", fromlist=[name])

View File

@@ -14,9 +14,9 @@ DEPTH_SCALE_ATTRIBUTE = "o_depth_scale"
OUTPUT_PATH = (
Path(__file__).parents[1]
/ "src"
/ "snapform"
/ "anyimage"
/ "assets"
/ "O_SnapForm.blend"
/ "O_AnyImage.blend"
)

View File

@@ -8,20 +8,20 @@ PROJECT_ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(
0,
str(next((PROJECT_ROOT / "src" / "snapform" / "wheels").glob("blendjob-*.whl"))),
str(next((PROJECT_ROOT / "src" / "anyimage" / "wheels").glob("blendjob-*.whl"))),
)
from snapform.operators.balloon.depth import (
from anyimage.operators.balloon.depth import (
BALLOON_DEPTH_ATTRIBUTE,
add_balloon_shape_modifier,
balloon_shape_node_group,
)
from snapform.operators.balloon.operator import (
from anyimage.operators.balloon.operator import (
mark_balloon_material,
merge_balloon_normal_image,
reusable_balloon_material,
)
from snapform.operators.material import (
from anyimage.operators.material import (
BALLOON_MATERIAL_NODE_GROUP_NAME,
material_node_group,
set_modifier_input,

View File

@@ -8,10 +8,10 @@ PROJECT_ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(
0,
str(next((PROJECT_ROOT / "src" / "snapform" / "wheels").glob("blendjob-*.whl"))),
str(next((PROJECT_ROOT / "src" / "anyimage" / "wheels").glob("blendjob-*.whl"))),
)
from snapform.operators.material import (
from anyimage.operators.material import (
DEPTH_PLANE_MATERIAL_NODE_GROUP_NAME,
SHADELESS_NODE_GROUP_NAME,
SURFACE_MATERIAL_NODE_GROUP_NAME,
@@ -23,8 +23,8 @@ from snapform.operators.material import (
modifier_input_identifier,
set_modifier_input,
)
from snapform.operators.depth_plane.build import add_depth_plane_processing
from snapform.operators.surface import load_geometry_node_group
from anyimage.operators.depth_plane.build import add_depth_plane_processing
from anyimage.operators.surface import load_geometry_node_group
def main():

View File

@@ -9,11 +9,11 @@ PROJECT_ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(
0,
str(next((PROJECT_ROOT / "src" / "snapform" / "wheels").glob("blendjob-*.whl"))),
str(next((PROJECT_ROOT / "src" / "anyimage" / "wheels").glob("blendjob-*.whl"))),
)
from snapform.operators.material import get_modifier_input, set_modifier_input
from snapform.operators.surface import create_matching_camera, load_geometry_node_group
from anyimage.operators.material import get_modifier_input, set_modifier_input
from anyimage.operators.surface import create_matching_camera, load_geometry_node_group
def set_input(modifier, group, name, value):

View File

@@ -12,17 +12,17 @@ PROJECT_ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(
0,
str(next((PROJECT_ROOT / "src" / "snapform" / "wheels").glob("blendjob-*.whl"))),
str(next((PROJECT_ROOT / "src" / "anyimage" / "wheels").glob("blendjob-*.whl"))),
)
from snapform.operators.image import create_color_image
from snapform.operators.material import (
from anyimage.operators.image import create_color_image
from anyimage.operators.material import (
MOGE_DEPTH_PLANE_NODE_GROUP_NAME,
MOGE_MATERIAL_NODE_GROUP_NAME,
get_modifier_input,
modifier_input_identifier,
)
from snapform.operators.depth_plane.build import build_moge2_depth_plane
from anyimage.operators.depth_plane.build import build_moge2_depth_plane
def validate_single_frame_source_types():

View File

@@ -10,16 +10,16 @@ PROJECT_ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(
0,
str(next((PROJECT_ROOT / "src" / "snapform" / "wheels").glob("blendjob-*.whl"))),
str(next((PROJECT_ROOT / "src" / "anyimage" / "wheels").glob("blendjob-*.whl"))),
)
from snapform.operators.material import (
from anyimage.operators.material import (
MOGE_SURFACE_MATERIAL_NODE_GROUP_NAME,
material_color_space_name,
)
from snapform.operators.surface import MOGE_NODE_GROUP_NAME, build_image_mesh
from snapform.server.geometry import GeometryFrame, build_surface
from snapform.server.outputs.surface import write_surface_archive
from anyimage.operators.surface import MOGE_NODE_GROUP_NAME, build_image_mesh
from anyimage.server.geometry import GeometryFrame, build_surface
from anyimage.server.outputs.surface import write_surface_archive
def write_test_normal_texture(normal, output_path):

2
uv.lock generated
View File

@@ -1061,7 +1061,7 @@ wheels = [
]
[[package]]
name = "snapform"
name = "anyimage"
version = "0.12.15"
source = { virtual = "." }
dependencies = [