feat(node_assets): add image plane geometry nodes and validation scripts
- Implemented `image_plane.py` to build the Image Plane Geometry Nodes group with customizable parameters for geometry, subdivide, and thickness. - Created `image_plane_validation.py` to validate the functionality of the image plane, ensuring correct geometry generation and material assignment. - Developed `material_layers.py` to construct shader node groups for image materials, including depth and normal handling. - Authored `plane_layouts.py` to define node arrangements for image geometry assets. - Introduced `shadeless.py` to create a shadeless shader node group that displays color without scene lighting while preserving shadows and reflections.
This commit is contained in:
@@ -1,68 +1,43 @@
|
||||
# 程序架构
|
||||
|
||||
AnyImage 由 Blender 交互层、BlendJob Runtime 和独立 Job Server 三部分组成。AI 依赖运行在 Server Environment 中,Blender 数据在主线程创建和修改。
|
||||
|
||||
## 对应文件
|
||||
|
||||
```text
|
||||
src/anyimage/
|
||||
├── __init__.py # 类型、工具、菜单与 Runtime 注册
|
||||
├── menu.py # Image Empty 上下文入口
|
||||
├── panel.py # Server 与 Debug 面板
|
||||
├── preferences.py # Storage Root、Device 与功能默认值
|
||||
├── properties.py # Scene 状态、模型状态与动态枚举
|
||||
├── runtime.py # Environment 和 JobRuntime
|
||||
├── common/ # Blender 侧共享原子能力
|
||||
├── operators/ # Blender 侧业务入口
|
||||
└── server/ # 独立计算端
|
||||
```
|
||||
|
||||
## 职责
|
||||
|
||||
| 层次 | 职责 |
|
||||
| 模块 | 职责 |
|
||||
| --- | --- |
|
||||
| Blender 交互层 | UI、手势、RNA 状态、输入准备、结果导入和 Undo |
|
||||
| BlendJob Runtime | Environment、Server 生命周期、队列、进度、取消和 Job 目录 |
|
||||
| Job Server | 媒体解码、ONNX 推理、数组派生和文件输出 |
|
||||
| `__init__.py` | 注册类型、工具、菜单、快捷键与 Runtime |
|
||||
| `menu.py`、`panel.py` | Image Empty 上下文菜单与 Job Server 面板 |
|
||||
| `preferences.py` | 扩展设置及读取入口 |
|
||||
| `properties.py` | Scene 手势状态、视频帧数、模型状态与枚举 |
|
||||
| `common/` | Blender 侧公共能力 |
|
||||
| `operators/` | 交互、任务参数、结果导入和 Undo |
|
||||
| `server/` | 独立环境内的媒体处理、推理和产物写出 |
|
||||
|
||||
Operator 向 Server 发送普通字典。Server 返回文件名和 Job 目录;Operator 的 `response()` 再读取这些文件,并在 Blender 主线程中创建 Image、Mesh、Material 和 Geometry Nodes Modifier。
|
||||
|
||||
## 注册与状态
|
||||
## 注册与配置
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Register["anyimage.register()"] --> Types["注册 Preferences / PropertyGroup / Operator / Panel"]
|
||||
Types --> Tools["注册 Image Edit 与 Cutout WorkspaceTool"]
|
||||
Tools --> RNA["挂载 Scene 与 Image Property"]
|
||||
RNA --> Menu["挂载上下文菜单和剪贴板快捷键"]
|
||||
Menu --> Runtime["注册 JobRuntime"]
|
||||
A[register] --> B[按 CLASSES 顺序注册类型]
|
||||
B --> C[注册 Frame、Mask、Rectify、Cutout 工具]
|
||||
C --> D[挂载 Scene.anyimage_settings]
|
||||
D --> E[挂载 View3D 与 Outliner 菜单及剪贴板快捷键]
|
||||
E --> F[注册 JobRuntime]
|
||||
```
|
||||
|
||||
`__init__.py` 按依赖顺序注册,并在注销时逆序清理。扩展级设置统一由 `preferences.addon_preferences()` 读取;`runtime.py` 只消费 `configured_storage_root` 回调,不持有 Blender 偏好设置的定位逻辑。交互中的临时状态和 Debug 参数保存在 `Scene.anyimage_settings`。`max_ai_input_size`、`max_frame_resolution` 与 `max_cutout_mesh_resolution` 都在 `preferences.py` 定义与读取:它们分别限制 AI 分析图、Frame 输出和 Cutout 几何采样的最长边。Upscale 还用 AI 输入上限判断当前 Image 是否允许执行。
|
||||
注销先关闭 Runtime 和交互入口,再移除 Scene 属性,按 `CLASSES` 逆序注销类型。设备、存储目录、模型、材质显示适配和功能上限统一通过 `preferences.py` 读取。
|
||||
|
||||
## 同步与异步路径
|
||||
**Maximum AI Input Size** 限制模型分析图和 Upscale 可提交源图;**Maximum Frame Resolution** 限制 Frame 输出;**Maximum Cutout Mesh Resolution** 限制轮廓网格采样。三者各自作用于对应数据阶段。
|
||||
|
||||
无需 AI 或媒体处理的功能直接在 Blender 中完成,例如 Plane、Frame、Mask、Rectify,以及不生成 Normal Map 的 Surface 和 Balloon。需要模型或独立文件处理时,功能 Operator 派生自 `JobOperatorBase`:
|
||||
## 执行边界
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant Op as Blender Operator
|
||||
participant RT as JobRuntime
|
||||
participant Job as Server Job
|
||||
User->>Op: 执行功能
|
||||
Op->>Op: 准备文件与普通字典
|
||||
Op->>RT: 提交 Job
|
||||
RT->>Job: 排队执行
|
||||
Job-->>RT: 写入产物并返回索引
|
||||
RT-->>Op: 主线程 response
|
||||
Op->>Op: 导入数据并提交 Undo
|
||||
flowchart TD
|
||||
A[Operator 在主线程读取 Blender 数据] --> B[准备输入文件]
|
||||
B --> C[request 返回普通字典]
|
||||
C --> D[BlendJob 队列执行 Server Job]
|
||||
D --> E[模型与媒体处理写入 Job 目录]
|
||||
E --> F[主线程 response 加载结果]
|
||||
F --> G[提交 Image 或 Object 修改及 Undo]
|
||||
G --> H[清理临时输入]
|
||||
```
|
||||
|
||||
`server/` 保持纯 Python 实现;Operator 在 Blender 主线程读取产物并创建 RNA 数据。
|
||||
Plane、Frame、Mask、Rectify 和无需 AI 的 Cutout 在 Blender 内执行。Server 的 `jobs/` 编排任务,`models/` 负责模型适配,`geometry/` 处理深度与法线产物。Server 模块通过内部相对导入共享能力,Blender 侧通过 `common/` 复用。
|
||||
|
||||
几何模型的原始输出先由 `server/models/` 适配为单帧 `GeometryFrame`。它统一保存 Depth、Validity、Intrinsics,以及可选的 Normal 和 Points;Depth、Normal 与 Metadata Writer 只消费该结构。MoGe-2 的 Mask 转为 `0/1` Validity,DA3 的 Confidence 作为连续 Validity。
|
||||
|
||||
## 共享能力
|
||||
|
||||
`common/` 按 AI、Image、Selection、Viewport、Depth、Material、Object 和 Node 八个业务模块提供 Blender 侧复用能力。公开函数见[Common 公共函数](common.md)。
|
||||
MoGe-2 输出统一为 `GeometryFrame`:包含 Depth、Validity、Intrinsics,以及按需提供的 Normal 和 Points。产物写入器消费同一个 Frame,保持深度、法线和相机数据一致。参见 [Runtime](runtime.md) 与 [产物协议](outputs.md)。
|
||||
|
||||
@@ -1,126 +1,54 @@
|
||||
# Common 公共函数
|
||||
# Blender 公共能力
|
||||
|
||||
`src/anyimage/common/` 按 AI、Image、Selection、Viewport、Depth、Material、Object 和 Node 八个业务模块提供 Blender 侧复用能力。调用方直接从对应模块导入函数。
|
||||
`src/anyimage/common/` 按业务职责提供公共函数,调用方从对应模块直接导入。
|
||||
|
||||
## Image 数据块
|
||||
|
||||
`image.py` 处理 Blender `Image` 数据块及其在 Image Empty 上的完整生命周期,包括像素读取、动画帧、Job 输入、结果 Image 创建和 Empty 引用替换。
|
||||
|
||||
| 函数 | 职责 |
|
||||
| 模块 | 职责与主要入口 |
|
||||
| --- | --- |
|
||||
| `image.is_image_empty()` | 判断对象是否为带有效 Image 数据的 Empty。 |
|
||||
| `image.require_image_empty()` | 按名称取得 Image Empty;对象已失效时抛错。 |
|
||||
| `image.image_pixels()` | 以 Blender 原始顺序读取一维 float32 像素。 |
|
||||
| `image.image_rgba()` | 读取为 Top-down RGBA float32 数组。 |
|
||||
| `image.homography_from_points()` | 求解四点之间的 Homography。 |
|
||||
| `image.projective_output_size()` | 按源像素密度、目标宽高比和资源上限计算投影输出尺寸。 |
|
||||
| `image.premultiplied_rgba()` | 将 Blender 顺序像素转换为 Top-down 预乘 Alpha RGBA。 |
|
||||
| `image.sample_premultiplied_rgba()` | 在图片边界坐标中双线性采样预乘 Alpha RGBA,范围外返回透明。 |
|
||||
| `image.straight_rgba()` | 转换为 straight RGBA,并把相邻可见 RGB 扩展到一圈透明边缘。 |
|
||||
| `image.warp_projective_pixels()` | 将源 Quad 重采样为矩形 Blender Image 像素。 |
|
||||
| `image.video_total_frames()` | 返回 Movie 或 Sequence 总帧数,静态图返回 1。 |
|
||||
| `image.image_user_settings()` | 读取 Image Empty 的帧起点、偏移、时长和循环设置。 |
|
||||
| `image.default_extract_frames()` | 取得当前 Empty 默认应处理的帧数。 |
|
||||
| `image.source_start_frame()` | 从源 Image Empty 取得 Job 应处理的起始帧。 |
|
||||
| `image.prepare_image_input()` | 将 Packed、Dirty、Sequence 等输入准备为 Job 可读路径,并返回临时标记。 |
|
||||
| `image.prepare_image_bounds_input()` | 复用调用方已读取的 RGBA,按几何 bounds 写出保留原始 Alpha 的矩形 Job 输入。 |
|
||||
| `image.cleanup_image_input()` | 清理由图片输入 helper 创建的临时文件或目录。 |
|
||||
| `image.image_base_name()` | 去除图片或视频扩展名和 Blender 数字后缀,得到数据块基础名。 |
|
||||
| `image.depth_data_name()` | 生成 Depth 结果数据块文件名。 |
|
||||
| `image.color_data_name()` | 生成 Color 结果数据块文件名。 |
|
||||
| `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_image_edit_result()` | 从本地编辑后的 RGBA 像素创建 Packed Image,以源 Image 完整名称请求新数据块名称;不修改 Empty。 |
|
||||
| `image.replace_empty_image()` | 执行 Image Empty 结果替换、共享引用分流、旧数据清理、帧设置继承和可选画布对齐。 |
|
||||
| `image.image_empty_bounds()` | 计算 Image Empty 在对象局部坐标中的实际显示边界。 |
|
||||
| `ai.py` | 环境和模型就绪检查、输入路径校验、`production_device()`、`moge2_parameters()` 与安装 UI |
|
||||
| `image.py` | `image_rgba()`、动画帧设置、Job 输入准备、像素采样、结果 Image 和 `replace_empty_image()` |
|
||||
| `selection.py` | `SelectionPath`、`SelectionMask`、non-zero winding 栅格化与 Alpha 修整 |
|
||||
| `viewport.py` | 原生点选、屏幕投影、`ImageGesture`、Lasso / Brush / Polyline 与 Overlay |
|
||||
| `depth.py` | 深度图片和元数据加载、相机坐标与显示范围的尺度换算 |
|
||||
| `material.py` | Image Layer、Normal、Alpha、Shadeless 和动画材质 |
|
||||
| `object.py` | Modifier 输入设置与结果对象激活、源对象替换 |
|
||||
| `node.py` | `node_asset_path()` 与 `load_node_group()` |
|
||||
|
||||
Remove Background、Upscale、Frame、Mask 与 Rectify 都属于直接 Image 编辑,结果保留 active 或源 Image 数据块的完整名称,包括名称中的 `.png` 等格式文字。源数据仍被其他对象引用时,重名结果由 Blender 自动追加数字后缀;源数据可清理时,替换入口让结果恢复准确原名。`color_data_name()`、`depth_data_name()`、`normal_data_name()` 仅用于 Plane 与 Cutout 等材质派生贴图。
|
||||
## 图像生命周期
|
||||
|
||||
## Image Selection 与视图交互
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Image 与 Image Empty] --> B[image_rgba 读取业务像素]
|
||||
B --> C{处理位置}
|
||||
C -- 本地 --> D[像素算法]
|
||||
C -- Server --> E[prepare_image_input 或 prepare_image_bounds_input]
|
||||
E --> F[Job 输出文件]
|
||||
F --> G[加载并 Pack]
|
||||
D --> G
|
||||
G --> H[replace_empty_image]
|
||||
H --> I[保留身份、共享引用、帧设置和画框]
|
||||
```
|
||||
|
||||
这组公共能力使用固定术语:
|
||||
业务像素采用 Top-down RGBA,`image_pixels()` 使用 Blender 像素顺序。预乘采样用于投影插值;文件或 Packed 图像的业务读取保留隐藏 RGB。直接编辑结果通过统一入口处理共享 Image、失败恢复和源数据清理。
|
||||
|
||||
- Selection 是用户在图片空间圈定的业务范围;`SelectionPath` 只保存路径点,并提供 JSON 编解码。
|
||||
- `SelectionMask` 是 Selection 的纯几何栅格化结果,只包含局部像素权重 `values` 和源图几何 `bounds`。
|
||||
- Mask 表示二维像素权重或 Alpha 运算。
|
||||
`prepare_image_input()` 返回路径及临时标记;`cleanup_image_input()` 与创建方配对。Cutout 的 `prepare_image_bounds_input()` 复用已读取的源 RGBA,输出指定几何 bounds 的矩形图片。
|
||||
|
||||
`viewport.py` 处理 View3D 中的点击、坐标投影、`ImageGesture` 和 Overlay 绘制;它为 Lasso、Brush 与 Polyline 提供对应状态机,Brush 拖动阶段只维护简化后的屏幕路径及 Overlay、提交共用的圆形印记和连接条。`selection.py` 负责图片空间 Selection 的值、栅格化和 Alpha 修整;`SelectionPath → SelectionMask` 只在图片编辑或 Shape 生成阶段执行。Frame 在自身业务模块中组合冻结的 Viewport 投影矩阵与对象世界矩阵,把屏幕像素映射到 Image Empty 局部平面;正交视图接受任意有限深度,透视视图只采样观察方向前方的有效交点。
|
||||
## 手势与 Selection
|
||||
|
||||
Viewport 对象命中继续由 Blender 原生点选决定。Mask、Rectify 与 Cutout 点到另一对象时只保留原生选择结果;没有命中对象时保留当前 active Image Empty,并把透明区域或 Viewport 空白作为图片操作起点。Frame 的普通空白点击继续清空选择。手势是否与图片范围或可见内容有效相交,由各业务在提交阶段判断。
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[原生对象点选] --> B[ImageGesture 冻结源图与交互状态]
|
||||
B --> C[屏幕路径与 Overlay]
|
||||
C --> D[提交时投影到图片空间]
|
||||
D --> E[SelectionPath]
|
||||
E --> F[局部 non-zero winding 栅格化]
|
||||
F --> G[SelectionMask values 与 bounds]
|
||||
G --> H[Mask Alpha 运算或 Cutout 内容轮廓]
|
||||
```
|
||||
|
||||
所有 Path 使用 non-zero winding 填充,同方向重复绕行的重叠区域保持选中。栅格器按路径边与像素中心扫描行的交点累计 winding,只建立 Path Bounds 的局部画布,抗锯齿时增加 2 px 边缘。栅格阶段不接收图片 Alpha 或颜色,Mask 与 Cutout 在各自业务层组合几何 Mask 和图片 Alpha。
|
||||
Brush 的圆形印记和连接条由 `brush_footprint_polygons()` 生成,供预览与提交共用。拖动时维护屏幕路径与局部 Overlay,提交时生成覆盖值。Mask、Rectify 与 Cutout 的空命中保留 active Image Empty;点中另一对象时完成原生选择。
|
||||
|
||||
### Viewport 交互
|
||||
## 材质、节点与对象
|
||||
|
||||
| 函数 | 职责 |
|
||||
| --- | --- |
|
||||
| `viewport.report_image_edit_exception()` | 将可预期的 Selection 警告和异常映射为 Operator Report。 |
|
||||
| `viewport.image_edit_poll()` | 判断当前上下文是否允许启动图片编辑工具。 |
|
||||
| `viewport.resolve_image_edit_click()` | 执行原生点选;点到另一对象时只保留选择结果,空命中时保留 active Image Empty 并开始点选型编辑。 |
|
||||
| `viewport.active_image_edit_source()` | 要求并取得当前 Selection 内的 active Image Empty。 |
|
||||
| `viewport.image_edit_selection_keymap()` | 创建普通点击和 Shift 点击的对象选择 keymap,并配置普通点击的空命中策略。 |
|
||||
| `viewport.image_edit_drag_keymap()` | 创建拖动图片手势及对象点选 keymap。 |
|
||||
| `viewport.image_edit_point_keymap()` | 创建点选型图片手势及 Shift 追加选择 keymap。 |
|
||||
| `viewport.active_view3d_tool_id()` | 取得当前 View3D Workspace Tool ID。 |
|
||||
| `viewport.draw_dashed_line()` | 绘制手势或透视框虚线。 |
|
||||
| `viewport.draw_polygon_fill()` | 绘制手势或透视框填充预览。 |
|
||||
| `viewport.ImageGesture` | 复用 Lasso、Brush 与 Polyline 的 Modal 生命周期、源图解析和 Overlay 绘制。 |
|
||||
| `viewport.brush_footprint_polygons()` | 生成 Brush Overlay 与释放提交共用的圆形印记和连接条。 |
|
||||
| `viewport.preview_union_geometry()` | 在 screen-space scanline bands 中融合 Brush Primitive,并返回一次填充与外轮廓虚线。 |
|
||||
| `viewport.preview_fill_geometry()` | 三角化 Lasso、Polyline 的 non-zero winding 填充与外轮廓。 |
|
||||
| `viewport.rectangle_screen_path()` | 将两个屏幕点规范为 Frame 使用的矩形四角。 |
|
||||
| `viewport.screen_path_to_image_pixels()` | 将屏幕路径投影为 Image Empty 像素坐标。 |
|
||||
| `viewport.serialize_matrix()` | 将 4×4 Matrix 序列化为 Operator 可保存的字符串。 |
|
||||
| `viewport.deserialize_matrix()` | 恢复已序列化的 4×4 Matrix。 |
|
||||
| `viewport.drawing_in_region()` | 判断 Modal 绘制是否仍位于启动时的 Area 和 Region。 |
|
||||
`create_image_material()` 连接颜色、法线、Alpha 和动画帧设置;材质显示适配读取当前 Preferences。`set_modifier_input()` 按实际 RNA 输入形式设置值,`finalize_object_result()` 完成结果对象激活和源对象处理。
|
||||
|
||||
### Selection 栅格化
|
||||
|
||||
| 函数 | 职责 |
|
||||
| --- | --- |
|
||||
| `selection.selection_path_bounds()` | 校验 Selection Path 并返回整数像素包围框。 |
|
||||
| `selection.rasterize_selection_path()` | 以局部 non-zero scanline 把 `SelectionPath` 栅格化为纯几何 `SelectionMask`。 |
|
||||
| `selection.trim_pixels_to_alpha()` | 按 Alpha 非零区域修整 RGBA 像素。 |
|
||||
|
||||
## Depth 产物
|
||||
|
||||
`depth.py` 负责 Blender 侧共享的 Depth 文件加载和相机尺度换算,直接加载 Server 写出的 EXR。
|
||||
|
||||
| 函数 | 职责 |
|
||||
| --- | --- |
|
||||
| `depth.load_depth_metadata()` | 读取并校验 `depth.json` 中的图片尺寸、相机内参与参考深度。 |
|
||||
| `depth.load_depth_result_image()` | 直接加载、命名、设为 Non-Color 并 Pack `vector-depth.exr` 或 `z-depth.exr`。 |
|
||||
| `depth.camera_depth_values()` | 从已加载 Depth Image 读取用于本地标定的 Z 与有效区。 |
|
||||
| `depth.depth_uniform_scale()` | 根据相机内参、参考深度和 Image Empty 范围计算统一尺度。 |
|
||||
|
||||
## Object 构建
|
||||
|
||||
`material.py` 构建 Image 材质;`object.py` 处理 Object 与 Geometry Nodes Modifier;`node.py` 加载节点资产。
|
||||
|
||||
| 函数 | 职责 |
|
||||
| --- | --- |
|
||||
| `node.load_node_group()` | 从随扩展分发的 Blend 资产加载指定 Node Group。 |
|
||||
| `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。 |
|
||||
|
||||
|
||||
## AI 准备
|
||||
|
||||
`ai.py` 统一处理 AI 功能的 Environment、模型、Device、输入校验、Job 参数和公共 UI。
|
||||
|
||||
| 函数 | 职责 |
|
||||
| --- | --- |
|
||||
| `ai.draw_ai_setup()` | 绘制 AI 未就绪时的安装入口,并返回当前状态。 |
|
||||
| `ai.draw_ai_property()` | 按 AI 就绪状态绘制并启用业务属性。 |
|
||||
| `ai.require_input_path()` | 解析 Blender 路径并要求其为现存文件或目录。 |
|
||||
| `ai.require_background_input_path()` | 在通用路径校验之外限制 BEN2 支持的媒体类型。 |
|
||||
| `ai.production_device()` | 读取正式任务使用的计算设备。 |
|
||||
| `ai.require_environment()` | 要求 AnyImage AI Environment 已就绪。 |
|
||||
| `ai.invoke_ai_setup_if_needed()` | Environment 未就绪时启动安装交互。 |
|
||||
| `ai.require_model()` | 校验模型标识和下载状态,并返回模型记录。 |
|
||||
| `ai.da3_parameters()` | 组装 `debug-da3` 参数。 |
|
||||
| `ai.moge2_parameters()` | 组装 MoGe-2 正式任务与 Debug 共用参数。 |
|
||||
Geometry Nodes 从 `src/anyimage/assets/O_AnyImage.blend` 按公开名称加载。构建与布局职责见[节点资产](node-assets.md),深度字段见[Job 产物](outputs.md)。
|
||||
|
||||
@@ -1,92 +1,48 @@
|
||||
# 开发与发布
|
||||
|
||||
AnyImage 的仓库环境用于测试 Blender 侧代码、Server 纯 Python 模块、Manifest 和打包逻辑。AI Runtime 使用独立的 Python 3.12 Environment。
|
||||
|
||||
## 对应文件
|
||||
|
||||
```text
|
||||
.
|
||||
├── pyproject.toml
|
||||
├── uv.lock
|
||||
├── .python-version
|
||||
├── tools/
|
||||
│ ├── pack.py
|
||||
│ ├── model_exports.py
|
||||
│ ├── sync_models.py
|
||||
│ ├── build_node.py
|
||||
│ └── node_assets/
|
||||
├── src/anyimage/blender_manifest.toml
|
||||
├── tests/
|
||||
└── .github/workflows/
|
||||
├── test.yml
|
||||
├── build.yml
|
||||
└── docs.yml
|
||||
```
|
||||
|
||||
## 开发环境
|
||||
|
||||
仓库固定使用 CPython 3.11,因为开发依赖 `bpy==4.5.3` 对应这一解释器版本。项目根目录执行:
|
||||
仓库使用 CPython 3.11 与 `bpy==4.5.3` 测试 Blender 代码;AI Server 使用 `runtime.py` 声明的独立 Python 3.12 Environment。
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
快速检查注册、Manifest 和打包时使用:
|
||||
## 测试组织
|
||||
|
||||
```bash
|
||||
uv run pytest tests/test_addon.py tests/test_manifest.py tests/test_pack.py -v
|
||||
```
|
||||
| 测试范围 | 文件 |
|
||||
| --- | --- |
|
||||
| 扩展注册、设置、安装和 Job 交互 | `test_addon_registration.py`、`test_preferences.py`、`test_ai_setup.py`、`test_job_operator.py` |
|
||||
| Frame、Mask、Rectify、Plane、Cutout | 对应 `test_*_tool.py`、`test_*_operators.py` 与对象、投影、网格测试 |
|
||||
| 图像身份、Alpha、材质与 Undo | 图像编辑集成、共享替换、材质适配与 Undo 测试 |
|
||||
| Server | `test_job_server.py`、`test_server_jobs.py`、`test_media_input.py`、`test_geometry_artifacts.py` |
|
||||
| 模型 | `test_model_manager.py`、目录、下载、推理与各 ONNX 模型测试 |
|
||||
| 节点资产 | 布局检查、Depth Plane 模式、深度分离与 Cutout 体积求值 |
|
||||
| 工具与包协议 | 扩展 Manifest、打包、模型同步、节点启动器和文档命令测试 |
|
||||
|
||||
测试按边界组织:`test_addon.py` 以 Blender Stub 验证注册和 Operator 流程;`test_mesh.py`、`test_image.py` 等验证可脱离 Blender 的算法;`test_server.py`、`test_models.py` 和各 ONNX 测试覆盖计算端;`test_pack.py` 与 `test_manifest.py` 固定发布包结构。
|
||||
Blender Stub 共用 `blender_test_support.py`;Server 和实际 Blender 几何 fixture 按使用范围存放在对应 support 模块。测试命名描述功能、触发条件与可观察结果。
|
||||
|
||||
## 扩展打包
|
||||
## 发布命令
|
||||
|
||||
```bash
|
||||
uv run pack
|
||||
```
|
||||
| 命令 | 职责 |
|
||||
| --- | --- |
|
||||
| `uv run pack` | 生成 Windows、macOS arm64、Linux 扩展包 |
|
||||
| `uv run pack --prepare` | 为本机同步扩展 wheel 与 Manifest |
|
||||
| `uv run build_node` | 构建、验证节点资产并运行测试 |
|
||||
| `uv run sync_models` | 准备、校验并上传模型声明的文件 |
|
||||
| `uv run docs -dev` | 本地预览文档 |
|
||||
| `uv run docs` | 通过 Mike 发布项目版本并更新 latest |
|
||||
|
||||
`tools/pack.py` 为 `windows-x64`、`macos-arm64` 和 `linux-x64` 分别解析依赖 wheel,生成平台专属 Manifest,再把 `src/anyimage/` 与 wheel 写入 `dist/`:
|
||||
`tools/pack.py` 从 `pyproject.toml` 读取依赖,按平台与 Blender Python 版本解析 wheel,使用 Blender 提供的 NumPy。产物命名为 `AnyImage.v<version>.<platform>.zip`,写入 `dist/`。本地 BlendJob wheel 内容变化后运行 `uv lock --refresh`,并核对锁文件、Manifest 与打包测试。
|
||||
|
||||
```text
|
||||
dist/
|
||||
├── AnyImage.v<version>.windows-x64.zip
|
||||
├── AnyImage.v<version>.macos-arm64.zip
|
||||
└── AnyImage.v<version>.linux-x64.zip
|
||||
```
|
||||
`tools/sync_models.py` 根据模型目录选择上游 ONNX 或本地导出流程;`model_exports.py` 处理对应 PyTorch 权重导出。文件校验依据声明大小和 SHA-256,上传使用逐文件复制。`--prepare-only` 用于本地准备。
|
||||
|
||||
本地 `src/anyimage/wheels/` 是 wheel 解析的优先来源,用于携带尚未发布的 BlendJob 构建。依赖、wheel 或 Manifest 变化后,需要同时检查 `pyproject.toml`、`uv.lock`、`blender_manifest.toml` 和打包测试;本地 wheel 内容变化后运行 `uv lock --refresh`。
|
||||
|
||||
## 模型发布
|
||||
|
||||
```bash
|
||||
uv run sync_models
|
||||
```
|
||||
|
||||
`tools/sync_models.py` 从公开原址下载已有 ONNX;Real-ESRGAN General WDN x4v3 与 SAFMN Real x4 则从官方 PyTorch 权重调用 `tools/model_exports.py` 构建。所有制品通过固定大小、SHA-256、ONNX Checker 和 PyTorch/ONNX Runtime 数值对比后,逐文件上传到 R2。添加 `--prepare-only` 可仅准备和验证制品。
|
||||
|
||||
## 文档
|
||||
|
||||
```bash
|
||||
uv run mkdocs serve
|
||||
uv run mkdocs build --strict
|
||||
```
|
||||
|
||||
站点导航由 `mkdocs.yml` 显式维护,因此移动页面时必须同时更新导航和相对链接。本地版本化发布使用 Mike;CI 构建严格模式站点并部署到 Cloudflare Worker。
|
||||
|
||||
## CI
|
||||
## CI 与文档导航
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Main["push main / Pull Request"] --> Test["uv run --frozen pytest"]
|
||||
Tag["push v* tag"] --> Release["测试 → tools/pack.py → Release ZIP"]
|
||||
Docs["main 上文档变更"] --> Site["mkdocs --strict → Cloudflare"]
|
||||
A[main push、Pull Request 或手动触发] --> B[test.yml 运行测试]
|
||||
C[v 前缀 Tag] --> D[build.yml 测试、打包并发布 ZIP]
|
||||
E[main 文档路径变化或手动触发] --> F[docs.yml 构建站点并部署 Cloudflare]
|
||||
```
|
||||
|
||||
`test.yml` 在 main push、Pull Request 与手动触发时运行完整测试。`build.yml` 只响应 `v*` tag,测试通过后构建并上传三个平台包。`docs.yml` 只在文档相关路径变化或手动触发时部署站点。
|
||||
|
||||
## 许可证
|
||||
|
||||
AnyImage Extension 代码采用 GPL-3.0-or-later,完整文本见仓库 `LICENSE`。模型权重保持各自上游许可证;模型目录声明下载来源、文件校验和许可信息。
|
||||
|
||||
Geometry Nodes 资产必须由 Blender 构建和验证,见[节点资产](node-assets.md)。
|
||||
站点导航由 `mkdocs.yml` 显式维护,页面移动时同步导航和相对链接。节点资产的脚本职责及检查入口见[节点资产](node-assets.md)。
|
||||
|
||||
@@ -1,44 +1,27 @@
|
||||
# 内部实现总览
|
||||
|
||||
Blender 进程负责交互与数据创建,独立 Job Server 负责模型推理和文件处理。两侧通过 BlendJob 传递可序列化参数与 Job 目录中的产物。
|
||||
AnyImage 在 Blender 主线程处理交互、像素编辑和数据创建,通过 BlendJob 把模型推理交给独立 Job Server。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Input["Image Empty / 手势"] --> Operator["Blender Operator"]
|
||||
Operator --> Choice{"需要独立计算?"}
|
||||
Choice -- "否" --> Build["Blender 主线程构建"]
|
||||
Choice -- "是" --> Runtime["BlendJob Runtime"]
|
||||
Runtime --> Server["Job Server"]
|
||||
Server --> Files["Job 产物"]
|
||||
Files --> Build
|
||||
Build --> Result["Image / Mesh / Material / Modifier"]
|
||||
A[Image Empty 或剪贴板图片] --> B[Operator 与手势工具]
|
||||
B --> C{需要 AI?}
|
||||
C -- 是 --> D[准备文件与普通字典参数]
|
||||
D --> E[JobRuntime → Job Server]
|
||||
E --> F[图片与相机元数据]
|
||||
F --> G[Blender 主线程导入]
|
||||
C -- 否 --> G
|
||||
G --> H[Image 编辑或 Mesh 与材质创建]
|
||||
```
|
||||
|
||||
## 项目主干
|
||||
| 主题 | 对应职责 |
|
||||
| --- | --- |
|
||||
| [程序架构](architecture.md) | 进程、注册、配置与主线程边界 |
|
||||
| [Common](common.md) | Blender 侧图像、手势、几何与 AI 公共能力 |
|
||||
| [Runtime 与 Server](runtime.md) | Environment、Job、模型下载与 Session 生命周期 |
|
||||
| [Job 产物](outputs.md) | 文件协议、相机坐标和 Blender 消费方式 |
|
||||
| [Operator 索引](operators/index.md) | 按实际用户功能查找实现 |
|
||||
| [节点资产](node-assets.md) | 资产、源码职责、构建和验证 |
|
||||
| [开发与发布](development.md) | 测试组织、工具命令与发布流程 |
|
||||
|
||||
```text
|
||||
.
|
||||
├── src/anyimage/
|
||||
│ ├── __init__.py # Blender 注册入口
|
||||
│ ├── menu.py # Image Empty 右键菜单
|
||||
│ ├── preferences.py # 扩展级配置
|
||||
│ ├── properties.py # Scene 状态与模型枚举
|
||||
│ ├── runtime.py # BlendJob Runtime 配置
|
||||
│ ├── common/ # Blender 侧共享原子能力
|
||||
│ ├── operators/ # 交互、任务提交与结果导入
|
||||
│ ├── server/ # 无 Blender 依赖的计算端
|
||||
│ ├── assets/ # 随扩展分发的节点资产
|
||||
│ └── wheels/ # 随扩展分发的 BlendJob wheel
|
||||
├── tools/ # 打包、模型同步与 Blender 节点资产
|
||||
└── tests/ # Python 自动化测试
|
||||
```
|
||||
|
||||
## 文档分层
|
||||
|
||||
- [程序架构](architecture.md):注册、配置、线程和模块边界。
|
||||
- [Common 公共函数](common.md):仅列生产功能从 `common/` 外部调用的函数。
|
||||
- [Job Runtime 与 Server](runtime.md):任务生命周期、媒体输入和模型 Session。
|
||||
- [Operator 实现索引](operators/index.md):按 `src/anyimage/operators/` 中的完整功能拆分实现页。
|
||||
- [Job 产物](outputs.md):文件用途和 Blender 导入关系。
|
||||
- [开发与发布](development.md):本地环境、测试、打包和 CI。
|
||||
- [节点资产](node-assets.md):Geometry Nodes 的构建、加载与验证。
|
||||
源码位于 `src/anyimage/`,开发工具位于 `tools/`,自动化测试位于 `tests/`。扩展级设置由 `preferences.py` 定义,运行时资源由 `runtime.py` 接入 BlendJob。
|
||||
|
||||
@@ -29,15 +29,36 @@ uv run build_node --skip-tests
|
||||
|
||||
| 节点组 | 构建模块 |
|
||||
| --- | --- |
|
||||
| `O Mesh Plane` | `build_mesh_plane.py` |
|
||||
| `O Image Depth Plane` | `build_depth_plane.py` |
|
||||
| `O Image Layer`、`O Image Depth Layer` | `build_material_layers.py` |
|
||||
| `O Shadeless` | `build_shadeless.py` |
|
||||
| `O Image Cutout`、`O Image Depth Cutout`、`O Image Depth Balloon` | `build_cutout_modifiers.py` |
|
||||
| `O Image Plane` | `image_plane.py` |
|
||||
| `O Image Depth Plane` | `depth_plane.py` |
|
||||
| `O Image Layer`、`O Image Depth Layer` | `material_layers.py` |
|
||||
| `O Shadeless` | `shadeless.py` |
|
||||
| `O Image Cutout`、`O Image Depth Cutout`、`O Image Depth Balloon` | `cutout.py` |
|
||||
|
||||
构建从空 Factory 文件生成全部节点组,关闭 `.blend1` 备份,并覆盖唯一发布资产。
|
||||
|
||||
Depth Cutout 的布局由 `cutout_depth_surface_layout.py` 编排:Split 属性采样位于主链上方,原始几何旁路沿下方的 Reroute 通道汇入 Switch;长距离投影引用共用分段干线,并在 Uniform 厚度与零厚度输出附近分叉。几何统计支线就近放置在其使用区域。
|
||||
## 源码职责
|
||||
|
||||
`tools/node_assets/` 的模块按资产、几何计算、布局和验证组织:
|
||||
|
||||
| 模块 | 职责 |
|
||||
| --- | --- |
|
||||
| `asset_library.py` | 构建全部节点组并保存唯一资产 |
|
||||
| `image_plane.py`、`depth_plane.py` | 普通平面与 Relief / Camera 深度平面 |
|
||||
| `material_layers.py`、`shadeless.py` | 图像材质层与无光照着色 |
|
||||
| `cutout.py`、`cutout_shapes.py` | Cutout 修改器接口与各预设几何 |
|
||||
| `depth_plane_camera.py`、`depth_surface.py` | Camera 平面、共享投影、深度分离与厚度 |
|
||||
| `cutout_volume.py`、`cutout_rounding.py` | 平滑中心曲面与轮廓圆润 |
|
||||
| `cutout_inflation.py`、`cutout_shell.py` | 正面膨胀、法线和正背面位置 |
|
||||
| `plane_layouts.py`、`depth_plane_layout.py` | 平面资产与 Camera 分支布局 |
|
||||
| `cutout_layout.py`、`cutout_shape_layouts.py`、`depth_cutout_layout.py` | Cutout 阶段布局、坐标与跨区域几何引用 |
|
||||
| `node_graph.py`、`node_layout.py` | 共用字段节点、平滑构建、布局与边界检查 |
|
||||
| `asset_validation.py` | 资产清单、接口、结构与布局检查 |
|
||||
| `image_plane_validation.py`、`cutout_validation.py` | 平面和 Cutout 几何求值验证 |
|
||||
|
||||
`tools/build_node.py` 依次启动资产构建与三个验证脚本。运行时加载入口位于 `common/node.py`。
|
||||
|
||||
Depth Cutout 的布局由 `depth_cutout_layout.py` 编排:Split 属性采样位于主链上方,原始几何旁路沿下方的 Reroute 通道汇入 Switch;长距离投影引用共用分段干线,并在 Uniform 厚度与零厚度输出附近分叉。几何统计支线就近放置在其使用区域。
|
||||
|
||||
三个 Cutout 节点组直接作为修改器使用,统一接收局部 XY 平面,以 `-Z` 表示向图片内部的方向。每组在塑形与平滑后处理 **Inward Axis**:默认 `-Z` 直接输出原生几何,`+X` 分支执行一次 Z 到 X 的轴转换;运行时对象矩阵使用逆变换补偿,保证两种模式落在相同世界空间位置。
|
||||
|
||||
@@ -89,19 +110,35 @@ flowchart TD
|
||||
|
||||
未关联选中切边的顶点使用原始 XYZ。切口顶点以 `原 XYZ × 所属侧 Z / 原 Z` 保持原相机射线,随后执行既有 Uniform Scale、Reference Depth 和 Depth Scale 投影。
|
||||
|
||||
厚度前固定顶点数据,使正背面和侧壁使用相同位置。连接时以原顶点编号和正背面身份构造临时整数坐标,合并同一身份的副本后恢复场景坐标,保持近距离分离区域各自独立。实现位于 `cutout_depth_surface.py`,由 `build_cutout_modifiers.py` 构建进共享节点资产。
|
||||
厚度前固定顶点数据,使正背面和侧壁使用相同位置。连接时以原顶点编号和正背面身份构造临时整数坐标,合并同一身份的副本后恢复场景坐标,保持近距离分离区域各自独立。实现位于 `depth_surface.py`,由 `cutout.py` 构建进共享节点资产。
|
||||
|
||||
暂存值通过 Store Named Attribute 写入保留前缀 `o_depth_`:面中心与相机采样使用 FACE,所属侧相机采样与切口标记使用 CORNER,最终相机坐标、表面位置与源顶点编号使用 POINT。计算区域就近使用 Named Attribute,末尾用一个 Wildcard 模式的 Remove Named Attribute 匹配 `o_depth_*`,输出保留 UVMap 与用户属性。
|
||||
|
||||
### 整图 Depth Plane
|
||||
|
||||
`O Image Depth Plane` 提供默认 **Relief** 与 **Camera** 模式,共用完整矩形网格和 `depth.exr`。RGB 保存相机 XYZ,Alpha 保存有效性。Relief 与 Depth Balloon 读取 B/Z,Camera 与 Depth Cutout 读取 XYZ。用户控制为 **Mode**、**Subdivide**、**Thickness**、**Depth Scale** 和 Options 中的 **Reference Depth**、**Normal Smooth**;**Depth Image** 与 **Uniform Scale** 为隐藏数据输入。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[O Image Plane 零厚度矩形细分] --> B[按 Depth Scale 插值相机 XY 与相对 Z]
|
||||
B --> C{Thickness}
|
||||
C -- 0 --> D[单层曲面]
|
||||
C -- 大于 0 --> E[沿平滑法线向内生成背面并连接边界]
|
||||
D --> F[清理临时属性并输出]
|
||||
E --> F
|
||||
```
|
||||
|
||||
两个独立输入均显示 Thickness,默认均为 0 m:第一个控制 Relief 的固定底面基础厚度,第二个控制 Camera 的向内壳厚度。上图描述 Camera 分支:Depth Scale 为 0 时恢复平面,1 时使用完整相机投影;正面 POINT 法线按 Normal Smooth 平滑并归一化,再按 Thickness 向内生成背面。Normal Smooth 范围 0–50、默认 50,仅影响 Camera。模式切换保留各自厚度值并复用图片。Camera 分支位于 `depth_plane_camera.py`,投影与厚度共用 `depth_surface.py`,布局由 `depth_plane_layout.py` 编排。
|
||||
|
||||
### 几何节点布局
|
||||
|
||||
全部五个几何节点组使用连续横排的几何主轴。主轴可适当宽松,支线内部紧凑,同一目的的计算聚在同一侧,内部间距明显小于与其他部分的距离;独立用途可在两侧错落展开。长算式横排,末端就近汇入主轴。Join Geometry、Menu Switch、Combine XYZ 的同级独立分支末端排成一列并对齐输出端,菜单分支按输入顺序上下排列,共享上游留在公共位置。必要的向左引用直接连接。Group Input 与 Named Attribute 就近放置,全接线的简单运算可折叠,常数和域设置保持可见。
|
||||
|
||||
`geometry_layouts.py` 组织平面资产布局,`cutout_shape_layouts.py` 和 `cutout_depth_surface_layout.py` 组织 Cutout 的局部计算阶段;`cutout_layout.py` 将阶段布局放入三个修改器,`node_layout.py` 验证节点边界与同级输入对齐。具体规则见 [AGENTS.md](../../AGENTS.md)。布局验收包含实际节点编辑器预览及几何求值。
|
||||
`plane_layouts.py` 组织平面资产布局,`cutout_shape_layouts.py` 和 `depth_cutout_layout.py` 组织 Cutout 的局部计算阶段;`cutout_layout.py` 将阶段布局放入三个修改器,`node_layout.py` 验证节点边界与同级输入对齐。布局验收包含实际节点编辑器预览及几何求值。
|
||||
|
||||
## 修改节点组
|
||||
|
||||
1. 修改 `tools/node_assets/build_*.py`;
|
||||
1. 修改 `tools/node_assets/` 中对应资产、计算或布局模块;
|
||||
2. 更新对应验证;
|
||||
3. 运行一键构建脚本;
|
||||
4. 搜索并清理旧名称、接口和路径;
|
||||
|
||||
@@ -38,4 +38,4 @@ flowchart TD
|
||||
|
||||
落点仍在 `actions.py` 中完成:3D View 创建 Reference 或 Plane,Node Editor 创建与当前节点树匹配的 Image 节点,绘制模式把 Image 配置为当前画笔的 Texture。鼠标位置和视图位置也在这里换算。
|
||||
|
||||
Plane 路径调用 `convert_to_plane/mesh_plane.py` 创建中心原点基础网格,并从节点资产加载 `O Mesh Plane`。`actions.py` 创建图像材质并放入 Mesh 第 `0` 个材质槽,节点组只生成几何。
|
||||
Plane 路径调用 `convert_to_plane/image_plane.py` 创建中心原点基础网格,并从节点资产加载 `O Image Plane`。`actions.py` 创建图像材质并放入 Mesh 第 `0` 个材质槽,节点组只生成几何。
|
||||
|
||||
@@ -1,56 +1,23 @@
|
||||
# Plane 转换
|
||||
|
||||
Plane 转换从当前 Image Empty 创建普通 Plane 或 Depth Plane:普通 Plane 同步完成,Depth Plane 先取得 MoGe-2 Z Depth 与 Tangent Normal 文件,再回到 Blender 创建对象。
|
||||
|
||||
## 执行过程
|
||||
`operators/convert_to_plane/conversion.py` 提供 `ConvertToPlane`、`ConvertToDepthPlane` 与 `GenerateDepthPlane`。对象和材质导入位于 `object.py`,基础矩形网格位于 `image_plane.py`。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Empty["Image Empty"] --> Kind{"转换类型"}
|
||||
Kind -- "Plane" --> Color["复制 Color Image"]
|
||||
Color --> Plane["创建 Plane Object"]
|
||||
Kind -- "Depth Plane" --> Job["generate-depth-plane-geometry"]
|
||||
Job --> Result["Color Image + z-depth.exr + depth.json + tangent-normal.png"]
|
||||
Result --> Import["加载并 Pack Depth / Normal Image"]
|
||||
Import --> Depth["创建 Depth Plane Object"]
|
||||
A[Image Empty] --> B{转换类型}
|
||||
B -- Plane --> C[复制 Color 与动画设置]
|
||||
C --> D[中心原点四边形与 O Image Plane]
|
||||
B -- Depth Plane --> E[检查静态图与 AI 就绪状态]
|
||||
E --> F[generate-depth-plane-geometry]
|
||||
F --> G[Color、depth.exr、depth.json、Tangent Normal]
|
||||
G --> H[加载并 Pack 数据]
|
||||
H --> I[O Image Depth Plane 与材质]
|
||||
D --> J[替换源 Empty 并保留世界位置]
|
||||
I --> J
|
||||
```
|
||||
|
||||
两条路径共用 Image Empty 的显示范围、颜色材质和中心原点 Plane。Depth Plane 通过一次 MoGe-2 Server Job 同时生成 Z Depth 与 Tangent Normal;节点根据相机元数据生成单面地形体块,Normal Map 则进入材质着色。
|
||||
普通 Plane 支持图片、视频和序列,材质保留 Image User 帧设置。基础四边形以局部中心为原点,对象矩阵补偿原 Image Empty 显示范围;节点组生成细分与厚度,材质位于 Mesh 第 0 个槽。
|
||||
|
||||
## 节点说明
|
||||
Depth Plane 从 Preferences 取得 MoGe-2 模型、分辨率级别与 AI 输入上限,一次推理生成相机 XYZ 深度和切线法线。`create_depth_plane_from_result()` 创建颜色材质与深度修改器,并在失败时清理未使用的数据块。
|
||||
|
||||
### 选择 Plane 或 Depth Plane
|
||||
|
||||
`operators/convert_to_plane/__init__.py` 导出注册类型,实际入口在 `conversion.py`。`ConvertToPlane` 进入本地路径,`ConvertToDepthPlane` 检查静态图片和 AI 环境后启动 `GenerateDepthPlane`。
|
||||
|
||||
### 创建普通 Plane
|
||||
|
||||
`conversion.py` 先调用 `object.create_color_image()` 复制当前 Image,再由 `object.create_plane_object()` 创建对象。`mesh_plane.py` 把基础四边形顶点放在局部原点两侧,并把 Image Empty 显示范围中心补入对象矩阵,因此世界空间位置不变而对象原点位于面中心。随后从节点资产加载 `O Mesh Plane` 修改器。
|
||||
|
||||
材质存入 Mesh 第 `0` 个材质槽。`O Mesh Plane` 将带匿名标记的输入几何与生成几何临时合并,统一设置材质索引 `0` 后再删除输入几何,从而让纯生成面保留原 Mesh 的材质集合。Movie 或 Sequence 的 Image User 设置会保留。转换完成后,新 Plane 替换源 Empty,并沿用原对象名称。
|
||||
|
||||
### 生成 Depth 与 Normal
|
||||
|
||||
Depth 路径中的 `GenerateDepthPlane` 位于 `conversion.py`。它从 Preferences 读取 **Depth & Normal Model**、**MoGe Resolution Level** 和 **Maximum AI Input Size**,提交 `generate-depth-plane-geometry` Job。
|
||||
|
||||
`server/jobs/depth_plane.py` 编排完整图片任务,`server/geometry/prediction_artifacts.py` 把图片合成到黑底后调用一次 MoGe-2,并从同一个 `GeometryFrame` 写出 `z-depth.exr`、`depth.json` 与 `tangent-normal.png`。该 Artifact 生成层也由 Cutout Job 复用;两种顶层 Job 继续各自负责完整图片与 Selection 的输入、结果协议。
|
||||
|
||||
EXR 的 RGB 重复保存完整相机 Z,Alpha 保存模型 Validity;JSON 保存图片尺寸、相机内参与有效区域的参考深度。Tangent Normal 使用 PNG 保存,只参与材质着色。AI 输入上限只影响模型分析图,颜色副本仍保留原始分辨率。
|
||||
|
||||
### 创建 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`,并由 `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["(Reference Depth - Depth) × Depth Scale"]
|
||||
Offset --> Clamp["负向位移限制在固定底面之前"]
|
||||
Block["O Mesh Plane 厚度体块"] --> Weight["固定底面 0 → 中间环 → 可变面 1"]
|
||||
Clamp --> Apply["位移 × 层权重"]
|
||||
Weight --> Apply
|
||||
Apply --> Terrain["单面地形体块"]
|
||||
```
|
||||
|
||||
固定底面位于原图片平面,未置换可变面位于正法线方向的 **Thickness** 距离处。**Depth Scale** 为 `0` 时成品厚度等于 **Thickness**;正向位移可以超出基础厚度,负向位移不会穿过固定底面。`conversion.py` 最后用一个 Undo 步骤替换源 Empty。
|
||||
`O Image Depth Plane` 提供默认 Relief 和 Camera:Relief 读取深度 Z 形成固定底面的地形;Camera 使用相机 XYZ 投影并沿平滑法线形成壳。两个模式各自保存 Thickness。完整接口见[节点资产](../node-assets.md),文件协议见[Job 产物](../outputs.md)。
|
||||
|
||||
@@ -1,108 +1,39 @@
|
||||
# Cutout Tool
|
||||
# Cutout
|
||||
|
||||
Cutout 从图片上的一个 Selection 开始,可按需生成 Depth 或 Normal Map,最后在 Blender 中创建带材质与 Modifier 的 Mesh Object。
|
||||
|
||||
## 执行过程
|
||||
Cutout 在静态 Image Empty 上圈定 Selection,按轮廓创建 Flat、Solid、Depth Solid 或 Depth Balloon。入口位于 `operators/cutout_tool/main.py`,菜单与采样级别映射位于 `interaction.py`,标识和资产对应关系位于 `shape.py`。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Select["圈定 SelectionPath"]
|
||||
Menu["打开 Shape Pie Menu"]
|
||||
Shape["选择 Shape"]
|
||||
Raster["SelectionPath → 几何 SelectionMask"]
|
||||
Route{"需要后台生成数据?"}
|
||||
Local["读取局部 RGBA<br/>源 Alpha × Selection 值"]
|
||||
Bounds["写出几何 bounds 矩形 RGBA"]
|
||||
Job["生成 Depth / Normal 产物"]
|
||||
Encoded["加载并 Pack 输入 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
|
||||
A[Lasso 拖动并提交 SelectionPath] --> B[选择 Shape]
|
||||
B --> C[栅格化 SelectionMask 并读取源 RGBA]
|
||||
C --> D{需要 Depth 或 Normal?}
|
||||
D -- 是 --> E[准备 bounds 矩形 RGBA]
|
||||
E --> F[generate-cutout-artifacts]
|
||||
F --> G[加载 Depth、Metadata 与 Normal]
|
||||
G --> H[源 Alpha 与 SelectionMask 组合]
|
||||
D -- 否 --> H
|
||||
H --> I[轮廓、采样、约束三角化与 o_balloon]
|
||||
I --> J[BaseShape]
|
||||
J --> K[创建材质、对象与对应节点修改器]
|
||||
```
|
||||
|
||||
Surface 与 Balloon 在本地组合源 Alpha 与几何 Selection 后构建 `BaseShape`;Depth Shape 和 Normal Map 先通过同一个矩形图片协议生成所需产物。
|
||||
|
||||
## 节点说明
|
||||
|
||||
### 圈定 Selection,选择 Shape
|
||||
|
||||
Cutout 工具由 `__init__.py` 注册,交互从 `main.py` 中的 `SelectCutoutSelection` 开始。它接收 Lasso 或双击整图手势,只构建内存中的 `SelectionPath`,然后调用 `interaction.py` 打开 Shape 饼菜单。此时不读取源图像素、不栅格化 Mask,也不扫描可见 Alpha。
|
||||
|
||||
对象命中由 Blender 原生点选决定。普通点击另一对象时只切换 active;空白单击保留当前 active Image Empty。Lasso 可从透明区域或 Viewport 空白开始拖动,双击也可从空命中位置打开整图 Shape 菜单。起始点不判断图片范围或 Alpha,完整 Selection 的几何与内容有效性在后续栅格化和 Shape 构建阶段判断。
|
||||
|
||||
`interaction.py` 读取工具选项,并在构建菜单按钮时把只含 Points 的完整 `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 在 Path Bounds 与 2 px 抗锯齿边缘内栅格化为一次纯几何 `SelectionMask`。`values` 保存局部几何权重,`bounds` 保存它在源图中的位置;栅格阶段不读取、接收或合并源 Alpha。
|
||||
|
||||
实际 Cutout 业务随后读取一次源 RGBA。无需 AI 时,它在几何 bounds 内显式计算 `source_alpha × selection_values`,由该内容 Mask 判断空内容并约束轮廓;原始 `SelectionMask` 不被修改。此时按 `Selection bounds 长边 ÷ Mesh Detail 像素间距` 计算实际采样长边;只有它超过 **Maximum Cutout Mesh Resolution** 时才等比增大有效间距。bounds 的原始像素长边本身不会触发降密。
|
||||
|
||||
### 需要后台生成数据?
|
||||
|
||||
选择 Depth Balloon、Depth Surface 或开启 **Normal Map** 时需要提交 Job。
|
||||
|
||||
Surface 和 Balloon 的本地路径直接使用本地内容 Mask。
|
||||
|
||||
### 生成 Depth / Normal 产物
|
||||
|
||||
`main.py` 先通过 `common/image.py` 把 Selection 几何 bounds 内的原始 RGBA 写成矩形输入。该文件保留源 Alpha,不预乘 Lasso 值;文件、Packed 或内存图片都使用同一个入口。随后把请求提交给 `server/jobs/cutout.py`,生成本次 Shape 需要的产物:
|
||||
|
||||
- 需要 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 原生 XY / Z 对象空间,正面中性法线为局部 `+Z`;其余 Shape 使用 Tangent Space。`main.normal_mode_for_shape()` 根据 Shape 确定 Normal Space。
|
||||
|
||||
**Maximum AI Input Size** 约束 MoGe-2 的分析图长边;网格密度使用 **Maximum Cutout Mesh Resolution**,最终颜色图使用 Selection bounds 分辨率。
|
||||
|
||||
Color 使用 Selection bounds 内的源 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 输入作为颜色图。它从同一 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 的矩形输入。
|
||||
|
||||
| Shape | Modifier 如何形成结果 | 必需后台产物 |
|
||||
| 预设 | 节点组 | AI 产物 |
|
||||
| --- | --- | --- |
|
||||
| Surface | 保持基础平面轮廓,可增加真实厚度 | 无 |
|
||||
| Balloon | 使用 `o_balloon` 从边缘向内部鼓起 | 无 |
|
||||
| Depth Balloon | 在 Balloon 上叠加相对基准面的深度 | `z-depth.exr`、`depth.json` |
|
||||
| Depth Surface | 使用相机点 XYZ 重建透视表面 | `vector-depth.exr`、`depth.json` |
|
||||
| Flat | `O Image Cutout`,Thickness 0 | 可选 Tangent Normal |
|
||||
| Solid | `O Image Cutout`,Thickness 1 | 可选 Tangent Normal |
|
||||
| Depth Solid | `O Image Depth Cutout` | Depth 与 Object Normal |
|
||||
| Depth Balloon | `O Image Depth Balloon` | Depth 与 Tangent Normal |
|
||||
|
||||
Depth Shape 的 Reference Depth,以及 Depth Balloon 的 Direction,都由 `object.py` 调用 `geometry.py` 的标定函数,根据最终网格内容 Mask 计算。透明像素达到 `5%` 时使用有效内容区域自适应标定,否则使用稳定的固定基准。
|
||||
## 轮廓与网格
|
||||
|
||||
Normal Map 关闭时材质使用几何法线;开启时,`object.py` 把生成的图片和已经确定的 Normal Space 交给材质节点。最后 `main.py` 提交 Undo,整条 Cutout 流程结束。
|
||||
`geometry.py` 从内容 Alpha 提取连通轮廓和孔洞,按 Mesh Detail 采样并生成 `BaseShape`。Low、Medium、High、Ultra 分别以 32、16、8、4 个源像素作为基础间距,并受 Maximum Cutout Mesh Resolution 限制。
|
||||
|
||||
### Inward Axis
|
||||
`fine_mesh.py` 负责 Fine Outline 的细节保留与约束网格,`polygon.py` 提供轮廓运算。Balloon Profile 按 Preferences 选择 Poisson 或 Teddy,生成所有预设共用的 POINT 属性 `o_balloon`。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Base["BaseShape 与四种 Shape<br/>原生 XY / Z"] --> Switch{"Inward Axis"}
|
||||
Switch -- "-Z(默认)" --> Native["节点几何直接输出<br/>对象沿用源 Empty local transform"]
|
||||
Switch -- "+X" --> Orient["O Image Cutout 执行 Z → X<br/>对象矩阵执行 X → Z"]
|
||||
Native --> World["相同世界空间结果"]
|
||||
Orient --> World
|
||||
Normal["Depth Surface<br/>Object Space Normal"] --> Mode{"Inward Axis"}
|
||||
Mode -- "-Z" --> Keep["保持原生 Z 轴通道"]
|
||||
Mode -- "+X" --> Swizzle["按 Z → X 转换一次"]
|
||||
```
|
||||
## AI 与对象创建
|
||||
|
||||
BaseShape 与 Shape 子节点始终在局部 XY 平面建模,并沿 `-Z` 向图片内部生成厚度和深度。统一的 `O Image Cutout` Modifier 只在选择 `+X` 时转换最终几何;`object.py` 同步使用逆矩阵,因此默认 `-Z` 与源 Image Empty 的 local transform 一致,`+X` 模式仍保持相同世界空间形状。Depth Surface 的 Object Space Normal 默认直接使用原生 Z 轴 Artifact,仅在 `+X` 模式转换通道;Tangent Space Normal 不变。
|
||||
Server 接收 Selection bounds 内保留源 Alpha 的 RGBA,一次 MoGe-2 推理按 `generate_depth` 与 `normal_mode` 返回产物。深度边缘延展使用所选 Alpha Threshold,Normal 使用原预测;AI 输入上限只影响分析分辨率。
|
||||
|
||||
Balloon 与 Depth Balloon 的材质会创建 Geometry Attribute 节点,把 `o_balloon` 的 Float 输出直接连接到 `O Image Layer` 已有的 **Normal Scale**;Surface 与 Depth Surface 不建立这条连接。
|
||||
`object.py` 的 `create_shape_object()` 消费 BaseShape、Color、Depth 和 Normal,加载对应节点组。基础网格位于局部 XY,向图片内部为 -Z;选择 +X 时节点与对象矩阵配对变换,保持世界几何一致。Object Normal 同步按方向转换。
|
||||
|
||||
材质使用 IOR 1.2,颜色进入 Image Layer,`o_normal_scale` 控制法线影响。Depth Solid 使用正面 FACE 标记,其他适用预设使用轮廓权重。节点计算见[节点资产](../node-assets.md)。
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# Debug Operator
|
||||
|
||||
Debug Operator 从面板中的模型参数发起一次原始推理,把结果文件写入 Job 目录并打开目录供开发者检查。
|
||||
|
||||
## 执行过程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Panel["Debug 面板"] --> Operator["选择模型 Debug Operator"]
|
||||
Operator --> Request["读取 Scene 参数"]
|
||||
Request --> Job["提交对应 Debug Job"]
|
||||
Job --> Output["写出原始预测产物"]
|
||||
Output --> Directory["打开 Job 目录"]
|
||||
```
|
||||
|
||||
DA3、MoGe-2、BEN2 和 Upscale 都走这条调试路径,用于验证模型、设备、请求参数和输出格式。
|
||||
|
||||
## 节点说明
|
||||
|
||||
### 从 Debug 面板进入
|
||||
|
||||
入口显示在 `src/anyimage/panel.py`,四个 Operator 定义在 `operators/debug.py`:`DebugDa3`、`DebugMoge2`、`DebugBen2` 和 `DebugUpscale`。它们读取 `Scene.anyimage_settings` 中的输入路径、设备、模型和分辨率参数,并检查 Environment 与模型是否可用。
|
||||
|
||||
### 提交原始推理
|
||||
|
||||
`debug.py` 分别提交 `debug-da3`、`debug-moge2`、`debug-ben2` 和 `debug-upscale` Job。Server 侧由 `server/jobs/debug_predictions.py` 接收请求,再调用相应模型适配器。
|
||||
|
||||
DA3 和 MoGe-2 的可视化与数组输出由 `server/outputs/debug_da3.py`、`server/outputs/debug_moge2.py` 写出;BEN2 与 Upscale 保存模型返回的图片结果。
|
||||
|
||||
### 查看结果
|
||||
|
||||
任务完成后,`debug.py` 的 `_open_job_directory()` 调用 Blender 的路径打开操作,直接显示该 Job 的目录。
|
||||
21
docs/internals/operators/frame.md
Normal file
21
docs/internals/operators/frame.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Frame 多图合成
|
||||
|
||||
`operators/image_edit_tool/frame.py` 定义 `FrameImages`、`FrameTool` 和工具激活入口。Frame 将选中的静态 Image Empty 按当前视图合成为一张图片。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[拖出屏幕矩形] --> B[冻结视图矩阵并收集选中静态图片]
|
||||
B --> C[检查矩形与 active 图片的有效投射交集]
|
||||
C --> D[按 Region 与 Maximum Frame Resolution 计算输出尺寸]
|
||||
D --> E[观察射线投影到各图片平面]
|
||||
E --> F[按样本深度从远到近合成,等深度 active 优先]
|
||||
F --> G[边界覆盖采样与独立 Alpha]
|
||||
G --> H[创建 Packed Image 并复用 active Object]
|
||||
H --> I[删除其他参与图片对象并提交 Undo]
|
||||
```
|
||||
|
||||
正交视图接受有限深度,透视视图采样观察方向前方的交点。交叠检查采用连续投影范围;输出尺寸受 Region、最长边上限和总像素上限约束。
|
||||
|
||||
RGB 与 Alpha 分别累计,保留被 Mask 隐藏的有效颜色。边界使用覆盖采样,内部复用直接采样,避免黑边和不必要的模糊。结果平面朝向观察者,位置由 active 图片的有效深度确定。
|
||||
|
||||
Frame 保留 active 对象身份、名称和 Collection;图像替换复用 [Common](../common.md) 的共享数据与失败恢复逻辑。
|
||||
@@ -1,57 +0,0 @@
|
||||
# Image Edit Tool
|
||||
|
||||
工具栏包含 **Frame**、**Mask**、**Rectify** 三合一 Image Edit 组,Cutout 作为紧接其后的独立工具。
|
||||
|
||||
## 工具设置
|
||||
|
||||
Frame 和 Rectify 没有业务选项。Frame 让 active Image Empty 承载合成结果,并删除其他参与合并的选中 Image Empty;Rectify 直接替换 active Image Empty 的图片。
|
||||
|
||||
Mask 保存 **Gesture**、**Mode** 和 **Radius**。Gesture 包含 Lasso、Brush 与 Polyline;Mode 包含 Set、Add 与 Subtract,默认使用 Subtract;Radius 默认 25 px,仅在 Brush 时显示和生效。
|
||||
|
||||
## 执行过程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Group["Frame / Mask / Rectify"]
|
||||
Group --> Kind{"子工具"}
|
||||
Kind -- "Frame" --> Sources["选中 still Image Empty<br/>active 为对象和深度基准"]
|
||||
Sources --> Project["Frame 像素生成观察射线<br/>与图片平面求交"]
|
||||
Project --> Composite["逐像素深度与 Alpha 合成"]
|
||||
Composite --> Canvas["完整 Frame Canvas<br/>范围外透明"]
|
||||
Canvas --> Facing["active 朝向当前视角<br/>删除其他参与对象"]
|
||||
Kind -- "Mask" --> Gesture{"Lasso / Brush / Polyline"}
|
||||
Gesture --> Mask["栅格化 SelectionMask"]
|
||||
Mask --> Mode{"Set / Add / Subtract"}
|
||||
Mode --> Alpha["只合成当前 Alpha<br/>保持完整 Canvas"]
|
||||
Kind -- "Rectify" --> Quad["四点 Quad 与 Homography"]
|
||||
Alpha --> Replace["替换 active Empty 的 Image"]
|
||||
Quad --> Replace
|
||||
```
|
||||
|
||||
## Frame
|
||||
|
||||
`frame.py` 读取所有选中的 still Image Empty,并要求 active object 是 Selection 内的 still Image Empty。仅有 active 状态但未选中的图片不构成输入;active 是非图片对象时,即使 Selection 中存在图片也以 warning 取消。Frame 可以从 Viewport 任意位置开始,但结果矩形必须与 active 图片投射相交。选中的非 Image Empty 被忽略;任一选中 Image Empty 是视频或序列时整体取消。
|
||||
|
||||
每个参与图片使用冻结的 Viewport 投影矩阵与对象世界矩阵建立屏幕到图片平面的 Projective Transform。Frame 输出像素中心沿当前观察射线映射到图片局部平面,再换算为源像素坐标;有限且位于 Image Empty 显示边界内的交点参与采样。正交视图接受任意有限视图深度;透视视图只接受观察方向前方的交点。图片部分跨越透视观察原点时,前方区域继续采样,后方、平行射线和图片范围外透明。
|
||||
|
||||
输出像素按观察射线上的几何深度从远到近排序,再以预乘 Alpha 合成;同深度时 active 位于最上层。Frame 与 active 的交叠通过连续有效投射区域和 Frame 矩形的正面积交集判断,不依赖源 Alpha 或离散输出像素中心。输出分辨率使用 Frame 在当前 Region 中的屏幕像素宽高,不超过 Region 尺寸,并由 Preferences 的 **Maximum Frame Resolution** 等比限制最长边;总像素不超过 16,777,216。透明边缘保留在完整 Canvas 中;紧邻内容的一圈透明像素保留相邻 RGB,避免线性插值产生黑边。
|
||||
|
||||
Frame 与 Cutout Lasso 只在 `CLICK_DRAG` 后进入手势。Mask 与 Rectify 使用 `PRESS` 启动。三个绘制型工具都由 Blender 原生点选决定命中对象:点到另一对象时只切换 active,不开始编辑;没有命中对象时保留当前 active Image Empty,因此透明区域和 Viewport 空白都可作为起点。Mask 使用 `PAINT_CROSS` cursor;Brush 单击生成一个圆形印记。Polyline 通过逐点单击建立路径,至少三个点后在起点周围显示随鼠标接近而放大的闭合圈,并以点击 15 px UI Scale 命中范围、双击或 Enter 完成;Backspace 删除最后一点。手势与图片范围、可见内容的有效性在提交阶段判断。Frame 的普通空白点击继续清空选择,Shift 点击由显式 Viewport 对象选择 keymap 处理。
|
||||
|
||||
结果平面本地 XY 平行视平面且正面朝向观察者。正交视图与 active 原点位于透视观察方向前方时,结果沿用 active 对象原点的视图深度;透视 active 原点位于视后方但图片仍有有效前方交叠时,结果使用有效交叠区域屏幕中心与 active 图片平面的交点深度。active Blender Object 保留名称、Collection 和身份并承载 packed 合成 Image,其他参与 Image Empty 随后删除;整个操作属于一个 Undo 步骤。
|
||||
|
||||
## Mask
|
||||
|
||||
`mask.py` 定义一个 WorkspaceTool 和一个 `EditImageAlpha` Operator。Operator 启动时冻结 Scene 中保存的 Gesture、Mode 和 Radius;Lasso 把自由手绘闭合路径转换为图片像素,Polyline 把逐点闭合路径送入同一个 SelectionMask 与 Alpha 合成入口。
|
||||
|
||||
Brush 拖动阶段只维护经过距离与近共线简化的屏幕轨迹,并为每个采样点生成圆、为每对相邻点生成等宽连接条;不读取源 RGBA,不创建临时 Image,也不投影、栅格化或更新 Pixel Buffer。Overlay 在局部 screen-space scanline bands 中合并这些 Primitive,生成无内部接缝、尖角或三角孔的一次填充与闭合外轮廓;Subtract 为红色,Set/Add 为灰色。释放后把同一组 Primitive 投影到图片,以逐像素最大值合入单个覆盖画布,再通过公共 Alpha 合成与 Image 替换入口提交 Packed 结果。取消或切换工具只结束 Overlay,源 Image 与共享引用始终不变。
|
||||
|
||||
设当前 Alpha 为 `A`、手势权重为 `M`:Set 计算 `A × M`,Add 计算 `max(A, M)`,Subtract 计算 `A × (1 - M)`。RGB 原样保留。结果始终使用源图片宽高和原有画框,因此 Image Empty 的 identity、matrix、display size 和 offset 保持不变;全透明结果同样有效。
|
||||
|
||||
## Rectify
|
||||
|
||||
`rectify.py` 接受任意顺序的四个角点并规范为凸 Quad,创建 512 × 512 预览,随后按目标宽高比执行分块 Homography。输出最长边不超过 8192 px,总像素不超过 16,777,216;插值使用预乘 Alpha,结果按可见 Alpha trim 后映射回源图片平面。
|
||||
|
||||
Mask 与 Rectify 最终都以 packed Image 调用 `common/image.py` 的 `replace_empty_image()`。Mask 不提供 placement bounds;Rectify 使用校正区域的 placement bounds 调整图片画框。Frame 复用同模块的 Image 创建、Homography、预乘采样和单对象替换函数,并在自身业务边界完成多图合成与对象删除。
|
||||
|
||||
三个工具的结果都以源 Image 的完整名称请求命名,不移除 `.png` 等名称内容,也不添加材质贴图后缀。源数据仍被其他对象引用时,Blender 为结果追加数字后缀;源数据可清理时,替换入口让结果恢复准确原名。
|
||||
@@ -1,17 +1,15 @@
|
||||
# Operator 实现索引
|
||||
|
||||
`src/anyimage/operators/` 负责 Blender 侧交互、Job 请求组装和结果导入。
|
||||
| 源码(相对 `src/anyimage/`) | 功能 |
|
||||
| --- | --- |
|
||||
| `operators/ai_setup.py` | [AI 环境管理](ai-setup.md) |
|
||||
| `operators/clipboard_image/` | [剪贴板图片](clipboard-image.md) |
|
||||
| `operators/convert_to_plane/` | [Plane 与 Depth Plane](convert-to-plane.md) |
|
||||
| `operators/cutout_tool/` | [Cutout](cutout-tool.md) |
|
||||
| `operators/image_edit_tool/frame.py` | [Frame 多图合成](frame.md) |
|
||||
| `operators/image_edit_tool/mask.py` | [Mask Alpha 编辑](mask.md) |
|
||||
| `operators/image_edit_tool/rectify.py` | [Rectify 透视校正](rectify.md) |
|
||||
| `operators/remove_background.py` | [Remove Background](remove-background.md) |
|
||||
| `operators/upscale.py` | [Upscale](upscale.md) |
|
||||
|
||||
| Operator 路径 | 实现页 | 主要职责 |
|
||||
| --- | --- | --- |
|
||||
| `operators/__init__.py` | 本页 | 汇总 Blender 类型注册顺序 |
|
||||
| `operators/ai_setup.py` | [AI 环境管理](ai-setup.md) | 环境安装、模型下载与缓存释放 |
|
||||
| `operators/clipboard_image/` | [Clipboard Image](clipboard-image.md) | 系统剪贴板读取、粘贴与快捷键 |
|
||||
| `operators/convert_to_plane/` | [Convert to Plane](convert-to-plane.md) | Mesh Plane 与 Depth Plane 创建 |
|
||||
| `operators/cutout_tool/` | [Cutout Tool](cutout-tool.md) | Selection、基础 Shape 构建与 Blender 对象创建 |
|
||||
| `operators/image_edit_tool/` | [Image Edit Tool](image-edit-tool.md) | Frame 合成、Mask Alpha 编辑与 Rectify 透视校正 |
|
||||
| `operators/debug.py` | [Debug Operator](debug.md) | 模型接口与原始产物验证 |
|
||||
| `operators/remove_background.py` | [Remove Background](remove-background.md) | BEN2 去背与结果替换 |
|
||||
| `operators/upscale.py` | [Upscale](upscale.md) | 当前 Image 的 4× 高清与输入长边检查 |
|
||||
|
||||
`operators/__init__.py` 聚合注册类,扩展入口按顺序注册并逆序注销。
|
||||
类型在 `operators/__init__.py` 和扩展 `__init__.py` 汇总注册。AI Operator 使用普通字典提交任务,并在主线程响应中读取文件和修改 Blender 数据。
|
||||
|
||||
23
docs/internals/operators/mask.md
Normal file
23
docs/internals/operators/mask.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Mask Alpha 编辑
|
||||
|
||||
`operators/image_edit_tool/mask.py` 定义 `EditImageAlpha` 和 `MaskTool`,使用 `common/viewport.py` 的 `ImageGesture` 管理 Lasso、Brush 与 Polyline。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[冻结 Scene 的 Gesture、Mode、Radius] --> B[屏幕手势与 Overlay]
|
||||
B --> C[完成后投影并生成覆盖 M]
|
||||
C --> D[读取源 RGBA]
|
||||
D --> E[按模式计算 Alpha]
|
||||
E --> F[保留 RGB 和源画布尺寸]
|
||||
F --> G[替换 Packed Image 并提交一个 Undo]
|
||||
```
|
||||
|
||||
| 模式 | 源 Alpha A 与覆盖 M 的结果 |
|
||||
| --- | --- |
|
||||
| Set | `A × M` |
|
||||
| Add | `max(A, M)` |
|
||||
| Subtract | `A × (1 − M)` |
|
||||
|
||||
Brush 拖动时维护简化屏幕路径、圆形印记与连接条的并集预览;释放时一次生成图片覆盖。Polyline 使用逐点路径,至少三个点后通过闭合圈、双击或 Enter 完成,Backspace 删除末点。
|
||||
|
||||
结果保留 Empty 的身份、矩阵、显示尺寸和偏移。全透明结果有效,取消或切换工具清理未提交的 Overlay;共享 Image 通过统一替换入口分流。
|
||||
17
docs/internals/operators/rectify.md
Normal file
17
docs/internals/operators/rectify.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Rectify 透视校正
|
||||
|
||||
`operators/image_edit_tool/rectify.py` 定义 `RectifyImagePerspective` 与 `RectifyTool`,将图片内的四角区域校正为矩形。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[点选四角] --> B[规范为凸 Quad 并检查图片交叠]
|
||||
B --> C[512×512 预览与宽高比调整]
|
||||
C --> D[按像素密度和输出上限确定尺寸]
|
||||
D --> E[分块 Homography 与预乘 Alpha 插值]
|
||||
E --> F[按可见 Alpha 裁剪]
|
||||
F --> G[映射 placement bounds 并替换 Packed Image]
|
||||
```
|
||||
|
||||
四角允许任意点击顺序,退化或非凸输入在提交前校验。预览根据源颜色空间配置显示,像素重采样复用 `common/image.py`。输出最长边上限 8192,总像素上限 16,777,216。
|
||||
|
||||
`replace_empty_image()` 根据校正区域的 placement bounds 调整画框,保留源 Image 名称、对象身份和共享引用语义。
|
||||
@@ -1,44 +1,21 @@
|
||||
# Upscale
|
||||
|
||||
Upscale 从当前 Image Empty 读取单图或多帧媒体,在输入长边允许时生成 4× RGBA 结果并替换原图。
|
||||
|
||||
## 执行过程
|
||||
`operators/upscale.py` 从当前 Image Empty 生成 2× 结果并替换原图,支持单图、视频与序列帧。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Empty["Image Empty"] --> Gate{"长边小于输入上限?"}
|
||||
Gate -- "否" --> Disabled["禁用或报告错误"]
|
||||
Gate -- "是" --> Prepare["准备媒体输入"]
|
||||
Prepare --> Job["upscale-image Job"]
|
||||
Job --> RGB["分块执行 4× RGB 推理"]
|
||||
Job --> Alpha["独立缩放 Alpha"]
|
||||
RGB --> Merge["合并 RGBA"]
|
||||
Alpha --> Merge
|
||||
Merge --> Replace["导入并替换当前 Image"]
|
||||
A[检查源图长边小于 AI 输入上限] --> B[冻结 Preferences 模型与尺寸设置]
|
||||
B --> C[准备媒体输入]
|
||||
C --> D[upscale-image 逐帧处理]
|
||||
D --> E{所选模型}
|
||||
E -- Real-ESRGAN 或 SAFMN --> F[256×256 tile 与边缘上下文执行 4× RGB 推理]
|
||||
E -- PiSA-SR --> G[分阶段生成推理,细节强度 0.7]
|
||||
F --> H[统一缩放到源图 2×]
|
||||
G --> H
|
||||
H --> I[原 Alpha 独立 Lanczos 缩放并回填]
|
||||
I --> J[写出 upscale 序列并替换 Image]
|
||||
```
|
||||
|
||||
模型只处理 RGB,Alpha 单独放大后再合并。Blender 侧在提交前检查 **Maximum AI Input Size**,Server 模型层也应用同一个输入上限。
|
||||
`server/models/upscale.py` 统一处理输入上限、模型分派和最终 RGB/Alpha 尺寸。`onnx_upscale.py` 处理常规 4× 模型,`onnx_pisa_sr.py` 处理 PiSA-SR。多帧与多 tile 只在最后一次运行请求内存收缩。
|
||||
|
||||
## 节点说明
|
||||
|
||||
### 检查尺寸并准备输入
|
||||
|
||||
入口位于 `operators/upscale.py`。`UpscaleImage.poll()` 读取 `preferences.py` 中的 **Maximum AI Input Size**;当前 Image 长边必须严格小于该值。菜单显示的预计尺寸来自同一文件的 `upscale_output_size()`。
|
||||
|
||||
执行时,`upscale.py` 冻结当前模型和输入上限,并通过 `common/image.py` 准备单图、视频或序列输入。Packed Image 与内存修改结果会导出临时文件,多帧输入保留帧起点和抽帧设置。
|
||||
|
||||
### 分块放大 RGB
|
||||
|
||||
`UpscaleImage` 提交 `upscale-image` Job。`server/jobs/upscale.py` 展开媒体帧并逐帧调用模型;`server/models/upscale.py` 负责输入约束、RGB/Alpha 分离和结果合并,`server/models/onnx_upscale.py` 以 256 × 256 分块执行 4× ONNX 推理并带入相邻上下文。
|
||||
|
||||
`server/media/resolution.py` 统一解释输入长边上限。Blender 侧要求源图长边小于该值,结果长边小于该值的四倍。
|
||||
|
||||
### 合并 Alpha
|
||||
|
||||
`server/models/upscale.py` 使用 Lanczos 将 Alpha 放大到目标尺寸,再与 4× RGB 合并为 RGBA。Provider 资源不足时,Server 释放当前 Upscale Session,并返回降低输入上限或改用 CPU 的提示。
|
||||
|
||||
### 导入并替换结果
|
||||
|
||||
响应阶段回到 `operators/upscale.py`,通过 `common/image.py` 读取 `upscale.*` 结果序列并替换当前 Empty。
|
||||
|
||||
后续 Upscale 和 Depth Plane 都以当前结果 Image 为输入。
|
||||
Provider 资源错误会释放当前 Upscale Session,并返回降低输入上限或切换 CPU 的提示。响应通过 `common/image.py` 加载、Pack、保留图像共享与帧设置,并提交 Undo。
|
||||
|
||||
@@ -1,51 +1,43 @@
|
||||
# Job 产物
|
||||
|
||||
Server 在 Job 目录写入图片、JSON 或 Debug NPZ;Blender 根据 Job 返回值读取文件并创建 Image、Mesh、Material 与 Object。
|
||||
Server 把结果写入 Job 目录,返回以相对文件名为值的字典。Blender 主线程通过 JobResult 读取文件,再创建或更新 Image、Mesh、Material 和 Object。
|
||||
|
||||
## 正式 Job
|
||||
| Job | 文件 | 消费方 |
|
||||
| --- | --- | --- |
|
||||
| `remove-background` | `image.0001.png` 起 | Image Empty 替换 |
|
||||
| `upscale-image` | `upscale.0001.png` 起 | 2× Image Empty 替换 |
|
||||
| `generate-depth-plane-geometry` | `image.0001.*`、`depth.exr`、`depth.json`、`tangent-normal.png` | Plane 对象与材质 |
|
||||
| `generate-cutout-artifacts` | 按请求提供深度、元数据和法线 | Cutout 对象与材质 |
|
||||
|
||||
| Job | 主要文件 | Blender 消费方式 |
|
||||
| | | |
|
||||
| `remove-background` | `image.0001.png` 起 | 替换 Image Empty 的颜色数据 |
|
||||
| `upscale-image` | `upscale.0001.png` 起 | 替换或新建高清颜色数据 |
|
||||
| `generate-depth-plane-geometry` | `image.0001.*`、`z-depth.exr`、`depth.json` | 创建 Depth Plane |
|
||||
| `generate-cutout-artifacts` | 可选 Depth 与 Normal 文件 | 创建 Cutout Shape 与材质 |
|
||||
## 深度与法线协议
|
||||
|
||||
Depth Job 保存可由 Blender 直接加载的模型空间 EXR;Blender 主线程负责轮廓提取、三角化、Depth 标定和数据创建。
|
||||
| 返回字段 | 文件 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| `depth` | `depth.exr` | float32 RGB 保存相机空间 XYZ,Alpha 保存 Validity |
|
||||
| `depth_metadata` | `depth.json` | `image_size`、3×3 `intrinsics`、`reference_depth` |
|
||||
| `tangent_normal` | `tangent-normal.png` | 切线空间法线 |
|
||||
| `object_normal` | `object-normal.png` | Cutout 原生 XY / Z 坐标的对象空间法线 |
|
||||
|
||||
## Depth 与 Normal 文件
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[矩形源 RGBA] --> B[限尺寸并合成黑底模型输入]
|
||||
B --> C[MoGe-2 GeometryFrame]
|
||||
C --> D[原预测生成 Normal]
|
||||
C --> E{Cutout 深度?}
|
||||
E -- 是 --> F[按源 Alpha 延展轮廓深度]
|
||||
E -- 否 --> G[完整相机 Points]
|
||||
F --> G
|
||||
G --> H[depth.exr 与 depth.json]
|
||||
D --> I[Blender 加载并 Pack]
|
||||
H --> I
|
||||
```
|
||||
|
||||
MoGe-2 的原始结果由模型适配器转换为单帧 `GeometryFrame`。它包含 Depth、Validity、Intrinsics,以及可选的 Normal 和 Points;`geometry/prediction_artifacts.py` 把同一 Frame 交给各 Writer,并按请求写出以下文件:
|
||||
`depth_texture.py` 保存完整有限 Points;模型 Mask 转为 `0/1` Validity。参考深度取有效、有限、正深度且满足源 Alpha 条件的样本中位数:整图使用 Alpha ≥ 0.1,Cutout 使用所选 Alpha Threshold 并要求 Alpha > 0。非法字段和空参考区域抛出错误。
|
||||
|
||||
| 文件 | 内容 |
|
||||
| --- | --- |
|
||||
| `vector-depth.exr` | RGB 保存完整相机空间 XYZ,Alpha 保存 Validity |
|
||||
| `z-depth.exr` | RGB 重复保存完整相机 Z,Alpha 保存 Validity |
|
||||
| `depth.json` | 保存 `image_size`、3×3 `intrinsics` 与有效区域的 `reference_depth` |
|
||||
| `tangent-normal.png` | Tangent Space Normal Map |
|
||||
| `object-normal.png` | Cutout 原生 XY / Z 空间的 Object Space Normal Map |
|
||||
`common/depth.py` 加载深度并配置 Non-Color、CHANNEL_PACKED 与 Pack。Relief 和 Depth Balloon 读取 B/Z,Camera 与 Depth Solid 读取 XYZ,共用同一 EXR。
|
||||
|
||||
MoGe-2 的布尔 Mask 以 `0/1` 写入 Validity。Mask 外的有限 Depth/XYZ 仍保留在 RGB,使 Geometry Nodes 的线性采样维持连续;输入 Alpha、正深度和有限性只参与 `depth.json` 的参考样本筛选。任一待写 Field 包含 NaN/Inf 时不生成 EXR。
|
||||
## Selection 与颜色
|
||||
|
||||
Blender 通过 `common/depth.py` 加载 EXR,设为 Non-Color 并 Pack。Depth 与 Normal 数据块分别由 `depth_data_name()` 和 `normal_data_name()` 命名。Geometry Nodes 采样已加载的 EXR。
|
||||
Cutout 提交几何 bounds 内的原始 RGBA。Blender 在创建网格时组合源 Alpha 和 `SelectionMask`;颜色图保留矩形源 RGBA,法线可见区域取源 Alpha。AI 分析尺寸与颜色分辨率分别处理。
|
||||
|
||||
## Cutout Selection 产物
|
||||
|
||||
Blender 先把 `SelectionPath` 栅格化为 `SelectionMask`,再把局部 RGBA 作为 Server 输入。MoGe-2 输入统一按 `RGB × Alpha` 合成到黑底;完全透明像素下的隐藏 RGB 不影响预测。Server 不接收 Path 或 Mask 对象协议:
|
||||
|
||||
- 需要 Vector Depth 时请求 `vector-depth.exr` 与 `depth.json`;
|
||||
- 需要 Z Depth 时请求 `z-depth.exr` 与 `depth.json`;
|
||||
- Normal Map 按空间请求 `tangent-normal.png` 或 `object-normal.png`。
|
||||
|
||||
Cutout 的 Color、Normal 与 Depth 使用同一图片 Alpha 内容范围;Blender 再将该 Alpha 与 `SelectionMask` 相乘构建 Shape。Surface 与 Balloon 的本地路径直接组合源 Alpha 和 `SelectionMask`。
|
||||
|
||||
## Debug 产物
|
||||
|
||||
| Job | 文件 |
|
||||
| | |
|
||||
| `debug-da3` | `prediction.npz` |
|
||||
| `debug-moge2` | `prediction*.npz` |
|
||||
| `debug-ben2` | `foreground*.png` |
|
||||
| `debug-upscale` | `prediction*.png` |
|
||||
|
||||
Debug Job 保存模型适配层的直接输出。
|
||||
Remove Background、Upscale、Frame、Mask、Rectify 通过 `common/image.py` 的编辑结果入口保留源 Image 名称和共享引用语义。Plane 与 Cutout 的材质贴图使用 Color、Depth、Normal 命名函数。
|
||||
|
||||
@@ -1,93 +1,53 @@
|
||||
# Job Runtime 与 Server
|
||||
|
||||
AnyImage 使用 BlendJob 管理独立 Python Environment、Server 进程与单 Worker 队列。Blender 侧准备输入、提交普通字典参数并导入结果。
|
||||
|
||||
## 目录职责
|
||||
|
||||
```text
|
||||
src/anyimage/
|
||||
├── runtime.py # Environment 与 JobRuntime 配置
|
||||
└── server/
|
||||
├── app.py # 公开 Job 注册
|
||||
├── model_catalog.py # 模型声明
|
||||
├── model_download.py # 下载与校验
|
||||
├── model_manager.py # 模型目录与 Session 生命周期
|
||||
├── jobs/ # 公开协议与流程编排
|
||||
├── media/ # 图片、视频、序列帧和尺寸处理
|
||||
├── geometry/ # Depth / Normal 文件产物与相机元数据
|
||||
├── models/ # 模型适配与 ONNX 推理
|
||||
└── outputs/ # Debug Prediction 序列化
|
||||
```
|
||||
|
||||
`jobs/` 编排任务,领域目录提供媒体、Geometry、模型和输出能力。
|
||||
|
||||
## 生命周期
|
||||
`runtime.py` 配置 BlendJob `JobRuntime` 和独立 Python 3.12 Environment。`server/app.py` 注册 Job,`model_manager.py` 作为 `model_manager` Resource 管理下载文件与缓存 Session。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Request["Operator request()"] --> Runtime["JobRuntime"]
|
||||
Runtime --> Queue["单 Worker 队列"]
|
||||
Queue --> Job["app.py 注册的 Job"]
|
||||
Job --> Resource["ModelManager / Media"]
|
||||
Resource --> Directory["jobs/<job-id>/"]
|
||||
Directory --> Response["Operator response()"]
|
||||
A[Operator request] --> B[JobRuntime]
|
||||
B --> C[Server 单 Worker 队列]
|
||||
C --> D[Job 调用媒体与模型模块]
|
||||
D --> E[写入 jobs/job-id 目录]
|
||||
E --> F[主线程 response]
|
||||
```
|
||||
|
||||
Job 在阶段边界调用 `context.check_cancelled()`,并把稳定产物写入当前 Job 目录。Blender 响应完成后,再由输入所有者清理临时 Packed Image 文件。
|
||||
Job 在阶段和帧边界检查取消,进度通过 `context.progress()` 返回。视频临时帧由 `media/input.py` 的上下文管理器清理,Blender 导出的临时输入由对应 Operator 清理。
|
||||
|
||||
## 公开 Job
|
||||
## Job 与数据模块
|
||||
|
||||
| Job | 主要用途 |
|
||||
| Job | 实现与用途 |
|
||||
| --- | --- |
|
||||
| `download-model`、`download-required-models` | 准备模型文件 |
|
||||
| `remove-background` | 生成 BEN2 透明前景 |
|
||||
| `upscale-image` | 4× 图片或帧序列 |
|
||||
| `generate-depth-plane-geometry` | 为 Depth Plane 复制颜色并生成 Z Depth 与相机元数据 |
|
||||
| `generate-cutout-artifacts` | 为 Cutout 生成 Depth 或 Normal 文件产物 |
|
||||
| `debug-*` | 保存模型适配层的直接输出 |
|
||||
| `download-model`、`download-required-models` | `app.py` 调用 ModelManager 下载单个或缺失模型 |
|
||||
| `remove-background` | `jobs/remove_background.py`,生成 BEN2 前景 |
|
||||
| `upscale-image` | `jobs/upscale.py`,生成 2× 图像或序列 |
|
||||
| `generate-depth-plane-geometry` | `jobs/depth_plane.py`,复制整图颜色并生成深度、元数据与切线法线 |
|
||||
| `generate-cutout-artifacts` | `jobs/cutout.py`,按请求生成 Cutout 深度和法线 |
|
||||
|
||||
`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 Mask 由 Blender 在最终图片或网格阶段组合。
|
||||
`media/` 处理单图、序列、视频与输入尺寸。Depth Plane 和 Cutout 按单张静态图片工作;Remove Background 与 Upscale 逐帧处理动画输入。
|
||||
|
||||
## 尺寸约束
|
||||
`geometry/prediction_artifacts.py` 将限尺寸后的 RGBA 合成到黑底,运行一次 MoGe-2,再调用深度、法线和元数据写入器。Cutout 的深度分支通过 `edge_depth.py` 延展轮廓附近的深度;法线使用原预测。文件字段见 [Job 产物](outputs.md)。
|
||||
|
||||
`server/media/resolution.py` 提供 **Maximum AI Input Size**,默认 `2048 px`。它限制 Upscale 的可提交源图长边,以及 Depth、Normal Map 的模型分析图长边;材质颜色图使用原始分辨率。
|
||||
## 模型与缓存
|
||||
|
||||
Blender 侧的 **Maximum Cutout Mesh Resolution** 统一限制所有 Cutout Shape 实际几何范围的最长网格采样边,不限制 Selection bounds 的原始像素尺寸。
|
||||
| 用途 | 模型 | Session 分类 |
|
||||
| --- | --- | --- |
|
||||
| Depth 与 Normal | MoGe-2 ViT-S / ViT-B / ViT-L Normal | geometry |
|
||||
| Remove Background | BEN2 | ben2 |
|
||||
| Upscale | Real-ESRGAN WDN、SAFMN、Real-ESRGAN x4plus、PiSA-SR | upscale |
|
||||
|
||||
`generate-depth-plane-geometry` 与 `generate-cutout-artifacts` 只接受一帧,因此 Depth Plane 与 Cutout 是静态图工作流。Remove Background 与 Upscale 继续通过媒体入口处理单图、视频和序列帧。
|
||||
|
||||
## 模型
|
||||
|
||||
Depth Plane、Cutout Depth 与 Normal Map 使用 MoGe-2,Remove Background 使用 BEN2,Upscale 使用所选高清模型。MoGe-2 生产推理直接返回 `GeometryFrame`;DA3 调试推理保留模型原始 Prediction,并可按帧适配到相同结构。
|
||||
|
||||
`ModelManager` 按模型族管理下载目录与 ONNX Session,同一族只保留最近使用的模型和 Device,避免同类模型重复占用资源。
|
||||
|
||||
BEN2、DA3、MoGe-2 与 Upscale 共用 ONNX Runtime 推理入口。CUDA Session 使用可收缩的 memory arena;单次图片、完整帧集合或完整 Upscale tile 集合只在最后一次模型运行结束时收缩空闲 arena。Session 持有的模型权重与执行计划继续由 `ModelManager` 缓存,后续相同模型与 Device 直接复用。CPU、DirectML 与 CoreML 按各自 Provider 的普通运行方式执行。macOS arm64 默认使用 CPU;用户可选择 `coreml`,由 ONNX Runtime CoreML Execution Provider 在 Apple CPU、GPU 与 Neural Engine 之间调度,不使用 PyTorch MPS backend。MoGe-2 的动态 ONNX 图只把静态输入 shape 分区交给 CoreML,其余节点由 CPU Provider 执行;其他模型不使用该限制。
|
||||
`model_catalog.py` 声明文件来源、许可、大小和 SHA-256。`model_download.py` 下载文件并校验;ModelManager 按模型路径和设备缓存各类最近使用的 Session,切换模型或设备时释放对应类别。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Request["模型请求"] --> Cache["ModelManager 获取缓存 Session"]
|
||||
Cache --> Runs["执行单次或批量模型运行"]
|
||||
Runs --> Final["最后一次模型运行"]
|
||||
Final --> Shrink["CUDA 收缩空闲 memory arena"]
|
||||
Shrink --> Reuse["Session 留在 ModelManager 中复用"]
|
||||
A[取得缓存 Session] --> B[运行单图、多帧或 tile]
|
||||
B --> C[最后一次推理]
|
||||
C --> D[CUDA 收缩空闲 arena]
|
||||
D --> E[保留 Session 供后续复用]
|
||||
E --> F[Clear Models 或关闭 Server 时释放]
|
||||
```
|
||||
|
||||
## Environment 与 Storage Root
|
||||
Windows 默认 DirectML,macOS arm64 默认 CPU 并支持选择 CoreML。MoGe-2 为 CoreML 配置静态 shape 分区,其余计算由 CPU 执行。PiSA-SR 在 CoreML 上返回明确的支持限制;其分阶段 Session 在切换阶段、取消和清理时关闭。
|
||||
|
||||
`runtime.py` 声明独立 Python 3.12 环境和固定依赖。首次安装会准备默认 MoGe-2、BEN2 与 Real-ESRGAN WDN 模型。Storage Root 默认是 `~/.anyimage`:
|
||||
## Environment 与存储
|
||||
|
||||
```text
|
||||
~/.anyimage/
|
||||
├── .venv/
|
||||
├── models/
|
||||
├── jobs/
|
||||
├── environment.json
|
||||
├── manifest.json
|
||||
├── install.log
|
||||
└── server.log
|
||||
```
|
||||
|
||||
切换 Storage Root 等同于切换整套 Environment、模型与任务数据,不自动迁移旧目录。
|
||||
|
||||
各 Job 的稳定产物见 [Job 产物](outputs.md)。
|
||||
首次安装通过 `post_install()` 下载默认 MoGe-2、BEN2 与 Real-ESRGAN WDN。Storage Root 默认 `~/.anyimage`,其中保存 `.venv/`、`models/`、`jobs/` 及环境状态和日志。设置入口与安装流程见 [AI 环境管理](operators/ai-setup.md)。
|
||||
|
||||
Reference in New Issue
Block a user