Refactor Cutout and Image Editing Logic

- Renamed "Base Plane Depth" to "Reference Depth" across validate_cutout.py for consistency.
- Introduced a new specification for Cutout mesh sampling to ensure source pixel spacing is preserved and limits are applied correctly.
- Unified naming conventions for image editing results to maintain consistency across Remove Background, Upscale, and Crop operations.
- Updated the handling of Refine Selection to ensure it retains the original bounds and does not apply unnecessary cropping.
- Enhanced material generation logic to connect image color directly to IOR Level, improving the rendering of dark areas.
- Established clear requirements for geometry depth controls and image result materials to ensure consistent behavior across different components.
- Removed outdated logic and ensured all changes are documented and tested for regression.
This commit is contained in:
2026-09-03 01:02:10 +08:00
parent a6041cbe03
commit bdbde1a10d
47 changed files with 1120 additions and 257 deletions

View File

@@ -38,7 +38,7 @@ flowchart TD
Menu --> Runtime["注册 JobRuntime"]
```
`__init__.py` 按依赖顺序注册,并在注销时逆序清理。扩展级设置统一由 `preferences.addon_preferences()` 读取;`runtime.py` 只消费 `configured_storage_root` 回调,不持有 Blender 偏好设置的定位逻辑。交互中的临时状态和 Debug 参数保存在 `Scene.anyimage_settings`。`max_ai_input_size` 与 `max_cutout_mesh_resolution` 都在 `preferences.py` 定义与读取:前者由 Operator 冻结到 AI Job 请求,后者在 Blender 侧统一限制所有 Cutout Shape 的网格采样密度。Upscale 还用 AI 输入上限判断当前 Image 是否允许执行。
`__init__.py` 按依赖顺序注册,并在注销时逆序清理。扩展级设置统一由 `preferences.addon_preferences()` 读取;`runtime.py` 只消费 `configured_storage_root` 回调,不持有 Blender 偏好设置的定位逻辑。交互中的临时状态和 Debug 参数保存在 `Scene.anyimage_settings`。`max_ai_input_size` 与 `max_cutout_mesh_resolution` 都在 `preferences.py` 定义与读取:前者由 Operator 冻结到 AI Job 请求,后者在 Blender 侧统一限制所有 Cutout Shape 实际几何范围的最长采样边。Upscale 还用 AI 输入上限判断当前 Image 是否允许执行。
## 同步与异步路径

View File

@@ -22,14 +22,17 @@
| `image.image_base_name()` | 去除图片或视频扩展名和 Blender 数字后缀,得到数据块基础名。 |
| `image.depth_data_name()` | 生成 Depth 结果数据块文件名。 |
| `image.color_data_name()` | 生成 Color 结果数据块文件名。 |
| `image.load_color_result_image()` | 加载、命名、Pack 并配置 Job 的 Color 或 Sequence 结果;不修改 Empty。 |
| `image.load_image_edit_result()` | 加载、Pack 并配置直接编辑源 Image 的 Job 结果,以源 Image 完整名称请求新数据块名称。 |
| `image.load_color_result_image()` | 加载、命名、Pack 并配置材质 Color 或 Sequence 结果;不修改 Empty。 |
| `image.load_normal_result_image()` | 加载、命名、设为 Non-Color 并 Pack Normal Map。 |
| `image.load_packed_rgba_image()` | 加载已有 RGBA 文件,继承源图颜色空间和 Alpha mode,命名并 Pack。 |
| `image.create_pixel_result_image()` | 从本地编辑后的 RGBA 像素创建 Packed Image;不修改 Empty。 |
| `image.create_image_edit_result()` | 从本地编辑后的 RGBA 像素创建 Packed Image,以源 Image 完整名称请求新数据块名称;不修改 Empty。 |
| `image.replace_empty_image()` | 执行 Image Empty 结果替换、共享引用分流、旧数据清理、帧设置继承和可选画布对齐。 |
| `image.place_empty_image_result()` | 按 Keep Original 决定替换源 Empty 或复制结果,并选择、激活结果 Empty。 |
| `image.image_empty_bounds()` | 计算 Image Empty 在对象局部坐标中的实际显示边界。 |
Remove Background、Upscale 与四种 Crop 都属于直接 Image 编辑,结果保留源 Image 数据块的完整名称,包括名称中的 `.png` 等格式文字。源数据仍被其他对象引用或启用 Keep Original 时,重名结果由 Blender 自动追加数字后缀;源数据可清理时,替换入口让结果恢复准确原名。`color_data_name()`、`depth_data_name()`、`normal_data_name()` 仅用于 Plane 与 Cutout 等材质派生贴图。
## Image Selection 与视图交互
这组公共能力使用固定术语:
@@ -67,6 +70,9 @@
| --- | --- |
| `selection.selection_path_bounds()` | 校验 Selection Path 并返回整数像素包围框。 |
| `selection.rasterize_selection_path()` | 以局部 non-zero scanline 把 `SelectionPath` 栅格化为纯几何 `SelectionMask`。 |
| `selection.save_selection_mask()` | 将一次栅格化的局部 Mask 保存为 Blender 异步响应使用的 sidecar。 |
| `selection.load_selection_mask()` | 从本地 sidecar 恢复带原 bounds 的 `SelectionMask`。 |
| `selection.cleanup_selection_mask()` | 删除本地 Selection Mask sidecar。 |
| `selection.crop_pixels_to_alpha()` | 按 Alpha 非零区域紧裁切 RGBA 像素。 |
## Depth 产物
@@ -87,7 +93,7 @@
| 函数 | 职责 |
| --- | --- |
| `node.load_node_group()` | 从随扩展分发的 Blend 资产加载指定 Node Group。 |
| `material.create_image_material()` | 从 Image Empty 创建包含颜色、位移、Normal Map、Alpha 和动画设置的完整材质。 |
| `material.create_image_material()` | 从 Image Empty 创建包含颜色、位移、Normal Map、Alpha 和动画设置的完整材质;非 Shadeless 材质由 Color 直接驱动 Principled IOR Level。 |
| `object.modifier_input_identifier()` | 按接口名称解析 Geometry Nodes Modifier 输入 Identifier。 |
| `object.set_modifier_input()` | 跨受支持 Blender RNA 形式设置 Modifier 输入。 |
| `object.finalize_object_result()` | 激活结果对象,并按需替换或保留源 Empty。 |

View File

@@ -41,6 +41,8 @@ uv run build_node --skip-tests
`O Image Cutout` 在四个 Shape 分支与平滑之后统一处理 **Inward Axis**。Shape 子节点保持内部 `+X` 朝向;默认 `-Z` 分支执行一次轴转换,`+X` 分支保留内部坐标。运行时对象矩阵与该接口配对,保证两种模式落在相同世界空间位置。
`O Image Depth Plane`、`O Image Cutout`、`O Image Depth Balloon` 与 `O Image Depth Surface` 统一使用 **Reference Depth** 作为采样深度的运算基准。
## 修改节点组
1. 修改 `tools/node_assets/build_*.py`;

View File

@@ -39,13 +39,13 @@ EXR 的 RGB 重复保存完整相机 Z,Alpha 保存模型 Validity;JSON 保
### 创建 Depth Plane
Job 返回后,`object.create_depth_plane_from_result()` 加载并 Pack `z-depth.exr` 与 `tangent-normal.png`。Depth 数据块仍由 `depth_data_name()` 命名;Normal 图片设为 Non-Color,并以 Tangent Space 接入 `O Image Layer`。结果材质与 Cutout 一样使用 Principled IOR `1.2`。`depth.json` 的参考深度乘以统一尺度后初始化 **Base Plane Depth**,使它和采样深度处于同一局部距离单位。
Job 返回后,`object.create_depth_plane_from_result()` 加载并 Pack `z-depth.exr` 与 `tangent-normal.png`。Depth 数据块仍由 `depth_data_name()` 命名;Normal 图片设为 Non-Color,并以 Tangent Space 接入 `O Image Layer`。结果材质与 Cutout 一样使用 Principled IOR `1.2`,并由 `O Image Layer` Color 直接驱动 IOR Level。`depth.json` 的参考深度乘以统一尺度后初始化 **Reference Depth**,使它和采样深度处于同一局部距离单位。
`O Image Depth Plane` 把 `O Mesh Plane` 的固定底面放在原图片平面,`Thickness` 只沿正法线移动可变面与中间环,再按各层位置应用深度位移:
```mermaid
flowchart TD
Depth["Z Depth × Uniform Scale"] --> Offset["(Base Plane Depth - Depth) × Depth Scale"]
Depth["Z Depth × Uniform Scale"] --> Offset["(Reference Depth - Depth) × Depth Scale"]
Offset --> Clamp["负向位移限制在固定底面之前"]
Block["O Mesh Plane 厚度体块"] --> Weight["固定底面 0 → 中间环 → 可变面 1"]
Clamp --> Apply["位移 × 层权重"]

View File

@@ -18,10 +18,11 @@ flowchart TD
Path --> Mask["栅格化 SelectionMask"]
Mask --> Refine{"Box / Lasso Refine Selection?"}
Refine -- "否" --> Local["源 Alpha × Selection"]
Refine -- "是" --> Job["refine-image-selection"]
Refine -- "是" --> Job["refine-image-selection<br/>保持 Selection bounds"]
Job --> Refined["BEN2 Alpha × Selection"]
Kind -- "Perspective" --> Quad["四点 Quad 与 Homography"]
Local --> Result["创建结果 Image"]
Job --> Result
Refined --> Result
Quad --> Result
Result --> Keep{"当前子工具 Keep Original?"}
Keep -- "否" --> Replace["替换源 Empty"]
@@ -32,10 +33,12 @@ flowchart TD
`box.py`、`lasso.py` 和 `polyline.py` 定义 WorkspaceTool,交互与落地集中在 `selection.py`。公共 `CropSelectionGesture` 从 `common/viewport.py` 读取当前子工具的独立设置,并把内存中的 `SelectionPath` 交给 `CropImageSelection`。
本地路径在几何 bounds 内把 Selection 值乘入源 Alpha,再按可见内容紧裁切。Box 或 Lasso 开启 Refine Selection 时,Operator 写出保留源 Alpha 的 bounds 矩形并提交 `server/jobs/refine_image_selection.py`;响应直接加载 BEN2 RGBA。Job request 不包含 Keep Original,保留策略只在 Blender 主线程落地结果时执行。
本地路径在几何 bounds 内把 Selection 值乘入源 Alpha,再按可见内容紧裁切。Box 或 Lasso 开启 Refine Selection 时,Operator 写出保留源 Alpha 的 bounds 矩形,并把已栅格化的 Selection Mask 保存为同一临时容器内的 Blender 本地 sidecar。`server/jobs/refine_image_selection.py` 返回与输入同尺寸的 `Source Alpha × BEN2 Alpha` RGBA;Blender 读取 sidecar,把 Selection 值乘入结果 Alpha,并以提交前的 bounds 创建结果 Image。Job request 只包含图片输入、模型和设备;Keep Original 在 Blender 主线程落地结果时执行。
## Crop Perspective
`perspective.py` 接受任意顺序的四个角点并规范为凸 Quad,创建 512 × 512 预览,随后按目标宽高比执行分块 Homography。输出最长边不超过 8192 px,总像素不超过 16,777,216;插值使用预乘 Alpha,结果按可见 Alpha 紧裁切。
四种 Crop 最终都调用 `common/image.py` 的 `place_empty_image_result()`。该入口负责替换或复制 Image Empty、画布对齐、Collection 归属,以及 Keep Original 下选择并激活结果对象。
本地像素路径与 Refine Job 路径都通过公共 Image 编辑结果入口创建数据块。结果以源 Image 的完整名称请求命名,不移除 `.png` 等名称内容,也不添加材质贴图后缀;源 Image 被保留时产生的 `.001` 等数字后缀完全由 Blender 处理。

View File

@@ -15,7 +15,7 @@ flowchart TD
Bounds["写出几何 bounds 矩形 RGBA"]
Job["执行 Refine Selection,生成 Depth / Normal 产物"]
Encoded["加载并 Pack 输入或 Refine RGBA"]
Ready["汇总内容 Mask 与 Shape 输入"]
Ready["贴图 Alpha × Selection Mask"]
Build["构建 BaseShape"]
Create["创建 Blender 对象"]
@@ -33,13 +33,13 @@ Surface 与 Balloon 在本地组合源 Alpha 与几何 Selection 后构建 `Base
Cutout 工具由 `__init__.py` 注册,交互从 `main.py` 中的 `SelectCutoutSelection` 开始。它接收 Lasso 或双击整图手势,只构建内存中的 `SelectionPath`,然后调用 `interaction.py` 打开 Shape 饼菜单。此时不读取源图像素、不栅格化 Mask,也不扫描可见 Alpha。
`interaction.py` 读取工具选项,并在构建菜单按钮时把只含 Points 与 Invert 的完整 `SelectionPath` 编码一次,同时把当前 **Inward Axis** 固定到各 `CutoutSelectionToShape`。这次编码只用于跨 Blender Operator 传值,不属于 Selection Mask 构建。**Mesh Detail** 的 Low、Medium、High、Ultra 分别映射到 32、16、8、4 px 的采样间距,再换算期望网格分辨率;档位越高,采样越密。菜单中的 Surface、Balloon、Depth Balloon 和 Depth Surface,以及它们对应的节点分支索引,统一来自 `shape.py`。
`interaction.py` 读取工具选项,并在构建菜单按钮时把只含 Points 与 Invert 的完整 `SelectionPath` 编码一次,同时把当前 **Inward Axis** 固定到各 `CutoutSelectionToShape`。这次编码只用于跨 Blender Operator 传值,不属于 Selection Mask 构建。**Mesh Detail** 的 Low、Medium、High、Ultra 分别携带 32、16、8、4 px 的源图采样间距;档位越高,采样越密。菜单中的 Surface、Balloon、Depth Balloon 和 Depth Surface,以及它们对应的节点分支索引,统一来自 `shape.py`。
### 栅格化 Selection
用户选定 Shape 后,`main.py` 中的 `CutoutSelectionToShape` 接过主线。它从自身 Property 恢复 `SelectionPath`,再使用 `common/selection.py` 以 non-zero winding 栅格化为一次纯几何 `SelectionMask`:非反选只处理 Path Bounds 与 2 px 抗锯齿边缘,反选覆盖完整图片范围。`values` 保存局部几何权重,`bounds` 保存它在源图中的位置;栅格阶段不读取、接收或合并源 Alpha。
实际 Cutout 业务随后读取一次源 RGBA。无需 AI 时,它在几何 bounds 内显式计算 `source_alpha × selection_values`,由该内容 Mask 判断空内容并约束轮廓;原始 `SelectionMask` 不被修改。此时也会应用 **Maximum Cutout Mesh Resolution**,统一限制四种 Shape 几何范围的最长采样边。
实际 Cutout 业务随后读取一次源 RGBA。无需 AI 时,它在几何 bounds 内显式计算 `source_alpha × selection_values`,由该内容 Mask 判断空内容并约束轮廓;原始 `SelectionMask` 不被修改。此时按 `Selection bounds 长边 ÷ Mesh Detail 像素间距` 计算实际采样长边;只有它超过 **Maximum Cutout Mesh Resolution** 时才等比增大有效间距。bounds 的原始像素长边本身不会触发降密。
### 需要后台生成数据?
@@ -55,7 +55,7 @@ Surface 和 Balloon 的本地路径直接使用本地内容 Mask。
`main.py` 先通过 `common/image.py` 把 Selection 几何 bounds 内的原始 RGBA 写成矩形输入。该文件保留源 Alpha,不预乘 Lasso 值;文件、Packed 或内存图片都使用同一个入口。随后把请求提交给 `server/jobs/cutout.py`,生成本次 Shape 需要的产物:
- **Refine Selection** 调用 `server/selection/refine_selection.py`,写出收紧后的 `selection.png` 和 `selection_bounds.json`;
- **Refine Selection** 调用 `server/selection/refine_selection.py`,写出与输入 bounds 同尺寸的 `selection.png`;
- 需要 Vector Depth 时写出相机空间 XYZ 组成的 `vector-depth.exr`,需要 Z Depth 时写出 `z-depth.exr`,两者都配套 `depth.json`;
- **Normal Map** 根据既定空间写出 `tangent-normal.png` 或 `object-normal.png`。
@@ -63,11 +63,11 @@ Depth 与 Normal Map 共享同一个 MoGe-2 `GeometryFrame`。模型输入先按
**Maximum AI Input Size** 约束 BEN2 和 MoGe-2 的分析图长边;网格密度使用 **Maximum Cutout Mesh Resolution**,最终颜色图使用源图裁切分辨率。
Refine 把 Lasso 视为矩形上下文,因此 BEN2 可以保留 bounds 内、Lasso 外的内容。启用 Refine 时,最终内容 Alpha 完全来自 BEN2;只生成 Depth 或 Normal 时,最终几何仍以本地几何 Selection 值乘矩形输入 Alpha 约束。
Refine 的贴图内容 Alpha 统一为 `Source Alpha × BEN2 Alpha`。Color 直接使用该 RGBA;Normal 的可见区域、MoGe-2 黑底输入和 Depth Metadata 的参考区域使用同一 Alpha。Depth EXR 的 RGB 仍保存连续 Field,Alpha 保存模型 Validity。Selection Mask 只在 Blender 构建网格时与贴图 Alpha 相乘。
### 构建 BaseShape
Job 完成后,`main.py` 在 Blender 主线程加载并 Pack 可选的 Depth 与 Normal 图片,并选取一份已有编码 RGBA:启用 Refine 时使用 BEN2 输出;只生成 Depth 或 Normal 时使用提交前的矩形输入。它从同一局部 Image 读取 Alpha 构建内容 Mask,并把这个 Image 直接交给颜色材质。`main.create_cutout_shape()` 调用 `geometry.py` 中的 `build_base_shape()`:
Job 完成后,`main.py` 在 Blender 主线程加载并 Pack 可选的 Depth 与 Normal 图片,并选取一份已有编码 RGBA:启用 Refine 时使用完整 bounds 的 BEN2 输出;只生成 Depth 或 Normal 时使用提交前的矩形输入。它从同一 Image 读取 Alpha,与已栅格化的 Selection Mask 相乘后构建网格内容 Mask;颜色及 AI 贴图保持原 Alpha。`main.create_cutout_shape()` 调用 `geometry.py` 中的 `build_base_shape()`:
1. 从内容 Alpha 值提取外轮廓和孔洞;
2. 按 Mesh Detail 对应的采样间距生成轮廓与内部采样点;
@@ -79,7 +79,7 @@ Job 完成后,`main.py` 在 Blender 主线程加载并 Pack 可选的 Depth
### 创建 Blender 对象
`main.create_cutout_shape()` 把 `BaseShape`、颜色 Image 和可选产物交给 `object.py` 的 `create_shape_object()`。这里创建 Material、Mesh Object,并挂上统一的 `O Image Cutout` Modifier。材质通过 `common/material.py` 创建,Cutout 使用 `1.2` 的 IOR。本地 Cutout 创建一次几何 bounds 颜色图;AI Cutout 直接复用已编码并 Pack 的矩形输入或 BEN2 输出,不再重读完整源图并重新编码同一颜色内容。
`main.create_cutout_shape()` 把 `BaseShape`、颜色 Image 和可选产物交给 `object.py` 的 `create_shape_object()`。这里创建 Material、Mesh Object,并挂上统一的 `O Image Cutout` Modifier。材质通过 `common/material.py` 创建,Cutout 使用 `1.2` 的 IOR,并由 `O Image Layer` Color 直接驱动 Principled BSDF 的 IOR Level。本地 Cutout 创建一次几何 bounds 颜色图;AI Cutout 直接复用已编码并 Pack 的矩形输入或 BEN2 输出。
| Shape | Modifier 如何形成结果 | 必需后台产物 |
| --- | --- | --- |
@@ -88,7 +88,7 @@ Job 完成后,`main.py` 在 Blender 主线程加载并 Pack 可选的 Depth
| Depth Balloon | 在 Balloon 上叠加相对基准面的深度 | `z-depth.exr`、`depth.json` |
| Depth Surface | 使用相机点 XYZ 重建透视表面 | `vector-depth.exr`、`depth.json` |
Depth Shape 的 Base Plane Depth,以及 Depth Balloon 的 Direction,都由 `object.py` 调用 `geometry.py` 的标定函数,根据最终内容 Alpha 计算。透明像素达到 `5%` 时使用有效内容区域自适应标定,否则使用稳定的固定基准。
Depth Shape 的 Reference Depth,以及 Depth Balloon 的 Direction,都由 `object.py` 调用 `geometry.py` 的标定函数,根据最终网格内容 Mask 计算。透明像素达到 `5%` 时使用有效内容区域自适应标定,否则使用稳定的固定基准。
Normal Map 关闭时材质使用几何法线;开启时,`object.py` 把生成的图片和已经确定的 Normal Space 交给材质节点。最后 `main.py` 提交 Undo,整条 Cutout 流程结束。

View File

@@ -8,7 +8,7 @@ Server 在 Job 目录写入图片、JSON 或 Debug NPZ;Blender 根据 Job 返
| | | |
| `remove-background` | `image.0001.png` 起 | 替换 Image Empty 的颜色数据 |
| `upscale-image` | `upscale.0001.png` 起 | 替换或新建高清颜色数据 |
| `refine-image-selection` | `image.png`、`bounds.json` | Image Box / Lasso 读取 Refine Selection 结果 |
| `refine-image-selection` | `image.png` | Image Box / Lasso 读取完整 bounds 的 Refine Selection 结果 |
| `generate-depth-plane-geometry` | `image.0001.*`、`z-depth.exr`、`depth.json` | 创建 Depth Plane |
| `generate-cutout-artifacts` | 可选 Selection、Depth 与 Normal 文件 | 创建 Cutout Shape 与材质 |
@@ -34,12 +34,12 @@ Blender 通过 `common/depth.py` 加载 EXR,设为 Non-Color 并 Pack。Depth
Blender 先把 `SelectionPath` 栅格化为 `SelectionMask`,再把局部 RGBA 作为 Server 输入。MoGe-2 输入统一按 `RGB × Alpha` 合成到黑底;完全透明像素下的隐藏 RGB 不影响预测。Server 不接收 Path 或 Mask 对象协议:
- 开启 Refine Selection 时,`selection.png` 保存细化后的局部 RGBA,`selection_bounds.json` 保存它相对输入裁切图的范围;
- 开启 Refine Selection 时,`selection.png` 以输入 bounds 的完整尺寸保存 `Source Alpha × BEN2 Alpha` RGBA;
- 需要 Vector Depth 时请求 `vector-depth.exr` 与 `depth.json`;
- 需要 Z Depth 时请求 `z-depth.exr` 与 `depth.json`;
- Normal Map 按空间请求 `tangent-normal.png` 或 `object-normal.png`。
Surface 与 Balloon 的本地路径直接使用 `SelectionMask` 构建 Shape。最终颜色图由 Blender 按 Selection 范围裁切源 Image,并保留源 RGBA。
Cutout 的 Color、Normal 与 Depth 使用同一图片 Alpha 内容范围;Blender 再将该 Alpha 与 `SelectionMask` 相乘构建 Shape。Surface 与 Balloon 的本地路径直接组合源 Alpha 和 `SelectionMask`。
## Debug 产物

View File

@@ -48,13 +48,13 @@ Job 在阶段边界调用 `context.check_cancelled()`,并把稳定产物写入
| `generate-cutout-artifacts` | 按需执行 Refine Selection,并生成 Depth 或 Normal 文件产物 |
| `debug-*` | 保存模型适配层的直接输出 |
`generate-depth-plane-geometry` 对应 `jobs/depth_plane.py`;`generate-cutout-artifacts` 对应 `jobs/cutout.py`。共享的 MoGe-2 推理编排位于 `geometry/prediction_artifacts.py`,它把最终 Selection RGBA 合成到黑底后执行推理,并把单帧 `GeometryFrame` 交给 Depth、Normal 与 Metadata Writer。Depth EXR 与元数据写入位于 `geometry/depth_texture.py`,Normal 编码位于 `geometry/normal_texture.py`,Refine Selection 的通用实现位于 `selection/refine_selection.py`。
`generate-depth-plane-geometry` 对应 `jobs/depth_plane.py`;`generate-cutout-artifacts` 对应 `jobs/cutout.py`。共享的 MoGe-2 推理编排位于 `geometry/prediction_artifacts.py`,它把最终 Selection RGBA 合成到黑底后执行推理,并把单帧 `GeometryFrame` 交给 Depth、Normal 与 Metadata Writer。Depth EXR 与元数据写入位于 `geometry/depth_texture.py`,Normal 编码位于 `geometry/normal_texture.py`。`selection/refine_selection.py` 使用 BEN2 生成与输入同尺寸的 `Source Alpha × BEN2 Alpha` RGBA;Selection Mask 由 Blender 在最终图片或网格阶段组合。
## 尺寸约束
`server/media/resolution.py` 提供 **Maximum AI Input Size**,默认 `2048 px`。它限制 Upscale 的可提交源图长边,以及 Depth、Normal Map、Refine Selection 的模型分析图长边;材质颜色图使用原始分辨率。
Blender 侧的 **Maximum Cutout Mesh Resolution** 统一限制所有 Cutout Shape 的网格采样密度。
Blender 侧的 **Maximum Cutout Mesh Resolution** 统一限制所有 Cutout Shape 实际几何范围的最长网格采样边,不限制 Selection bounds 的原始像素尺寸。
`generate-depth-plane-geometry` 与 `generate-cutout-artifacts` 只接受一帧,因此 Depth Plane 与 Cutout 是静态图工作流。Remove Background 与 Upscale 继续通过媒体入口处理单图、视频和序列帧。