Files
AnyImage/docs/internals/operators/cutout-tool.md
nan bdbde1a10d 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.
2026-09-03 01:02:10 +08:00

8.1 KiB
Raw Blame History

Cutout Tool

Cutout 从图片上的一个 Selection 开始,可按需执行 Refine Selection、生成 Depth 或 Normal Map,最后在 Blender 中创建带材质与 Modifier 的 Mesh Object。

执行过程

flowchart TD
    Select["圈定 SelectionPath"]
    Menu["打开 Shape Pie Menu"]
    Shape["选择 Shape"]
    Raster["SelectionPath → 几何 SelectionMask"]
    Route{"需要后台生成数据?"}
    Local["读取局部 RGBA<br/>源 Alpha × Selection 值"]
    Bounds["写出几何 bounds 矩形 RGBA"]
    Job["执行 Refine Selection,生成 Depth / Normal 产物"]
    Encoded["加载并 Pack 输入或 Refine RGBA"]
    Ready["贴图 Alpha × Selection Mask"]
    Build["构建 BaseShape"]
    Create["创建 Blender 对象"]

    Select --> Menu --> Shape --> Raster --> Route
    Route -- "否" --> Local --> Ready
    Route -- "是" --> Bounds --> Job --> Encoded --> Ready
    Ready --> Build --> Create

Surface 与 Balloon 在本地组合源 Alpha 与几何 Selection 后构建 BaseShape;Refine Selection、Depth Shape 和 Normal Map 先通过同一个矩形图片协议生成所需产物。

节点说明

圈定 Selection,选择 Shape

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。

栅格化 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 不被修改。此时按 Selection bounds 长边 ÷ Mesh Detail 像素间距 计算实际采样长边;只有它超过 Maximum Cutout Mesh Resolution 时才等比增大有效间距。bounds 的原始像素长边本身不会触发降密。

需要后台生成数据?

只有以下情况需要提交 Job:

  • 开启 Refine Selection;
  • 选择 Depth Balloon 或 Depth Surface;
  • 开启 Normal Map。

Surface 和 Balloon 的本地路径直接使用本地内容 Mask。

执行 Refine Selection,生成 Depth / Normal 产物

main.py 先通过 common/image.py 把 Selection 几何 bounds 内的原始 RGBA 写成矩形输入。该文件保留源 Alpha,不预乘 Lasso 值;文件、Packed 或内存图片都使用同一个入口。随后把请求提交给 server/jobs/cutout.py,生成本次 Shape 需要的产物:

  • 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。

Depth 与 Normal Map 共享同一个 MoGe-2 GeometryFrame。模型输入先按 Alpha 合成到黑底;Vector/Z Depth 的 RGB 保留完整连续 Field,Alpha 保存模型 Validity。Depth Surface 的 Normal Map 编码为 Cutout 对象空间,其正面是对象局部 -X;其余 Shape 使用 Tangent Space。main.normal_mode_for_shape() 根据 Shape 确定 Normal Space。

Maximum AI Input Size 约束 BEN2 和 MoGe-2 的分析图长边;网格密度使用 Maximum Cutout Mesh Resolution,最终颜色图使用源图裁切分辨率。

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 时使用完整 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 对应的采样间距生成轮廓与内部采样点;
  3. 进行约束三角化;
  4. 计算四种 Shape 共用的 o_balloon;
  5. 汇总顶点、三角面、UV 和 Balloon 权重,得到内存中的 BaseShape。

geometry.py 还提供 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,并由 O Image Layer Color 直接驱动 Principled BSDF 的 IOR Level。本地 Cutout 创建一次几何 bounds 颜色图;AI Cutout 直接复用已编码并 Pack 的矩形输入或 BEN2 输出。

Shape Modifier 如何形成结果 必需后台产物
Surface 保持基础平面轮廓,可增加真实厚度 无
Balloon 使用 o_balloon 从边缘向内部鼓起 无
Depth Balloon 在 Balloon 上叠加相对基准面的深度 z-depth.exr、depth.json
Depth Surface 使用相机点 XYZ 重建透视表面 vector-depth.exr、depth.json

Depth Shape 的 Reference Depth,以及 Depth Balloon 的 Direction,都由 object.py 调用 geometry.py 的标定函数,根据最终网格内容 Mask 计算。透明像素达到 5% 时使用有效内容区域自适应标定,否则使用稳定的固定基准。

Normal Map 关闭时材质使用几何法线;开启时,object.py 把生成的图片和已经确定的 Normal Space 交给材质节点。最后 main.py 提交 Undo,整条 Cutout 流程结束。

Inward Axis

flowchart TD
    Base["四种 Shape<br/>内部几何 +X"] --> Switch{"Inward Axis"}
    Switch -- "+X" --> Legacy["节点几何不转换<br/>对象矩阵追加轴变换"]
    Switch -- "-Z(默认)" --> Orient["O Image Cutout 最终几何轴转换<br/>对象沿用源 Empty local transform"]
    Legacy --> World["相同世界空间结果"]
    Orient --> World
    Normal["Depth Surface<br/>Object Space Normal"] --> Mode{"Inward Axis"}
    Mode -- "+X" --> Keep["保持原通道"]
    Mode -- "-Z" --> Swizzle["(-y, z, -x)"]

Shape 子节点始终按内部 +X 建模。统一的 O Image Cutout Modifier 在最终输出处按选项切换轴转换;object.py 同步选择对象矩阵,因此默认 -Z 与源 Image Empty 的 local transform 一致,旧 +X 模式仍保持相同世界空间形状。Depth Surface 的 Object Space Normal 仅在 -Z 模式执行通道转换;Tangent Space Normal 不变。

Balloon 与 Depth Balloon 的材质会创建 Geometry Attribute 节点,把 o_balloon 的 Float 输出直接连接到 O Image Layer 已有的 Normal Scale;Surface 与 Depth Surface 不建立这条连接。