Add image selection refinement functionality with BEN2 integration
- Implemented viewport.py for handling 3D view projections, gestures, and overlay drawing. - Created refine_image_selection.py to manage the refinement of gesture masks and output image bounds. - Added selection module with refine_selection.py to process and crop images based on alpha channel visibility. - Introduced __init__.py for selection module to expose refine_selection function.
This commit is contained in:
@@ -33,7 +33,7 @@ Movie 或 Sequence 的 Image User 设置会保留。转换完成后,新 Plane
|
||||
|
||||
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,写出 `z-depth.exr` 与 `depth.json`。EXR 的 RGB 重复保存相机 Z,Alpha 保存有效区;JSON 保存图片尺寸、相机内参与参考深度。AI 输入上限只影响模型分析图,颜色副本仍保留原始分辨率。
|
||||
`server/jobs/depth_plane.py` 编排任务,`server/geometry/prediction_artifacts.py` 把图片合成到黑底后调用 MoGe-2,并从 `GeometryFrame` 写出 `z-depth.exr` 与 `depth.json`。EXR 的 RGB 重复保存完整相机 Z,Alpha 保存模型 Validity;JSON 保存图片尺寸、相机内参与有效区域的参考深度。AI 输入上限只影响模型分析图,颜色副本仍保留原始分辨率。
|
||||
|
||||
### 创建 Depth Plane
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Cutout Tool
|
||||
|
||||
Cutout 从图片上的一个 Region 开始,可按需执行 Fit to Foreground、生成 Depth 或 Normal Map,最后在 Blender 中创建带材质与 Modifier 的 Mesh Object。
|
||||
Cutout 从图片上的一个 Selection 开始,可按需执行 Refine Selection、生成 Depth 或 Normal Map,最后在 Blender 中创建带材质与 Modifier 的 Mesh Object。
|
||||
|
||||
## 执行过程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Select["圈定 Region,选择 Shape"]
|
||||
Raster["栅格化 Region"]
|
||||
Select["圈定 Selection,选择 Shape"]
|
||||
Raster["栅格化 Selection"]
|
||||
Route{"需要后台生成数据?"}
|
||||
Local["沿用本地 RegionMask"]
|
||||
Job["执行 Fit to Foreground,生成 Depth / Normal 产物"]
|
||||
Local["沿用本地 SelectionMask"]
|
||||
Job["执行 Refine Selection,生成 Depth / Normal 产物"]
|
||||
Ready["汇总 Shape 输入"]
|
||||
Build["构建 BaseShape"]
|
||||
Create["创建 Blender 对象"]
|
||||
@@ -21,19 +21,19 @@ flowchart TD
|
||||
Ready --> Build --> Create
|
||||
```
|
||||
|
||||
Surface 与 Balloon 直接使用 `RegionMask` 构建 `BaseShape`;Fit to Foreground、Depth Shape 和 Normal Map 先通过 Server Job 生成所需产物。
|
||||
Surface 与 Balloon 直接使用 `SelectionMask` 构建 `BaseShape`;Refine Selection、Depth Shape 和 Normal Map 先通过 Server Job 生成所需产物。
|
||||
|
||||
## 节点说明
|
||||
|
||||
### 圈定 Region,选择 Shape
|
||||
### 圈定 Selection,选择 Shape
|
||||
|
||||
Cutout 工具由 `__init__.py` 注册,交互从 `main.py` 中的 `SelectCutoutRegion` 开始。它接收 Lasso 或双击整图手势,把图片空间路径保存为 `RegionPath`,然后调用 `interaction.py` 打开 Shape 饼菜单。
|
||||
Cutout 工具由 `__init__.py` 注册,交互从 `main.py` 中的 `SelectCutoutSelection` 开始。它接收 Lasso 或双击整图手势,把图片空间路径保存为 `SelectionPath`,然后调用 `interaction.py` 打开 Shape 饼菜单。
|
||||
|
||||
`interaction.py` 完成 Region 有效性检查、读取三个工具选项,并根据 **Mesh Sampling Spacing** 换算期望网格分辨率。菜单中的 Surface、Balloon、Depth Balloon 和 Depth Surface,以及它们对应的节点分支索引,统一来自 `shape.py`。
|
||||
`interaction.py` 完成 Selection 有效性检查并读取三个工具选项。**Mesh Detail** 的 Low、Medium、High、Ultra 分别映射到 32、16、8、4 px 的采样间距,再换算期望网格分辨率;档位越高,采样越密。菜单中的 Surface、Balloon、Depth Balloon 和 Depth Surface,以及它们对应的节点分支索引,统一来自 `shape.py`。
|
||||
|
||||
### 栅格化 Region
|
||||
### 栅格化 Selection
|
||||
|
||||
用户选定 Shape 后,`main.py` 中的 `CutoutRegionToShape` 接过主线。它使用 `common/image_region.py` 把 `RegionPath` 栅格化为 `RegionMask`:`values` 保存局部像素权重,`bounds` 保存它在源图中的位置。套索外和源图透明区域不会进入最终 Region。
|
||||
用户选定 Shape 后,`main.py` 中的 `CutoutSelectionToShape` 接过主线。它使用 `common/selection.py` 把 `SelectionPath` 栅格化为 `SelectionMask`:`values` 保存局部像素权重,`bounds` 保存它在源图中的位置。套索外和源图透明区域不会进入最终 Selection。
|
||||
|
||||
此时也会应用 **Maximum Cutout Mesh Resolution**,统一限制四种 Shape 裁切区域的最长采样边。
|
||||
|
||||
@@ -41,30 +41,30 @@ Cutout 工具由 `__init__.py` 注册,交互从 `main.py` 中的 `SelectCutout
|
||||
|
||||
只有以下情况需要提交 Job:
|
||||
|
||||
- 开启 **Fit to Foreground**;
|
||||
- 开启 **Refine Selection**;
|
||||
- 选择 Depth Balloon 或 Depth Surface;
|
||||
- 开启 **Generate Normal Map**。
|
||||
- 开启 **Normal Map**。
|
||||
|
||||
Surface 和 Balloon 的本地路径直接沿用 `RegionMask`。
|
||||
Surface 和 Balloon 的本地路径直接沿用 `SelectionMask`。
|
||||
|
||||
### 执行 Fit to Foreground,生成 Depth / Normal 产物
|
||||
### 执行 Refine Selection,生成 Depth / Normal 产物
|
||||
|
||||
`main.py` 把请求提交给 `server/jobs/cutout.py`,生成本次 Shape 需要的产物:
|
||||
|
||||
- **Fit to Foreground** 调用 `server/region/refine_region.py`,写出收紧后的 `region.png` 和 `region_bounds.json`;
|
||||
- **Refine Selection** 调用 `server/selection/refine_selection.py`,写出收紧后的 `selection.png` 和 `selection_bounds.json`;
|
||||
- 需要 Vector Depth 时写出相机空间 XYZ 组成的 `vector-depth.exr`,需要 Z Depth 时写出 `z-depth.exr`,两者都配套 `depth.json`;
|
||||
- **Generate Normal Map** 根据既定空间写出 `tangent-normal.png` 或 `object-normal.png`。
|
||||
- **Normal Map** 根据既定空间写出 `tangent-normal.png` 或 `object-normal.png`。
|
||||
|
||||
Depth 与 Normal Map 共享同一次 MoGe-2 Prediction。Depth Surface 的 Normal Map 编码为 Cutout 对象空间,其正面是对象局部 `-X`;其余 Shape 使用 Tangent Space。`main.normal_mode_for_shape()` 根据 Shape 确定 Normal Space。
|
||||
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**,最终颜色图使用源图裁切分辨率。
|
||||
|
||||
### 构建 BaseShape
|
||||
|
||||
Job 完成后,`main.py` 在 Blender 主线程读取可选的 RegionMask,加载并 Pack Depth 与 Normal 图片。`main.create_cutout_shape()` 调用 `geometry.py` 中的 `build_base_shape()`:
|
||||
Job 完成后,`main.py` 在 Blender 主线程读取可选的 SelectionMask,加载并 Pack Depth 与 Normal 图片。`main.create_cutout_shape()` 调用 `geometry.py` 中的 `build_base_shape()`:
|
||||
|
||||
1. 从最终 `RegionMask` 提取外轮廓和孔洞;
|
||||
2. 按 Mesh Sampling Spacing 生成轮廓与内部采样点;
|
||||
1. 从最终 `SelectionMask` 提取外轮廓和孔洞;
|
||||
2. 按 Mesh Detail 对应的采样间距生成轮廓与内部采样点;
|
||||
3. 进行约束三角化;
|
||||
4. 计算四种 Shape 共用的 `o_balloon`;
|
||||
5. 汇总顶点、三角面、UV 和 Balloon 权重,得到内存中的 `BaseShape`。
|
||||
@@ -73,7 +73,7 @@ Job 完成后,`main.py` 在 Blender 主线程读取可选的 RegionMask,加
|
||||
|
||||
### 创建 Blender 对象
|
||||
|
||||
`main.create_cutout_shape()` 把 `BaseShape` 和可选产物交给 `object.py` 的 `create_shape_object()`。这里创建裁切后的 Color Image、Material、Mesh Object,并挂上统一的 `O Image Cutout` Modifier。材质通过 `common/object.py` 创建,Cutout 使用 `1.2` 的 IOR。Color Image 按最终 Region 包围矩形裁切源图并保留 RGBA。
|
||||
`main.create_cutout_shape()` 把 `BaseShape` 和可选产物交给 `object.py` 的 `create_shape_object()`。这里创建裁切后的 Color Image、Material、Mesh Object,并挂上统一的 `O Image Cutout` Modifier。材质通过 `common/material.py` 创建,Cutout 使用 `1.2` 的 IOR。Color Image 按最终 Selection 包围矩形裁切源图并保留 RGBA。
|
||||
|
||||
| Shape | Modifier 如何形成结果 | 必需后台产物 |
|
||||
| --- | --- | --- |
|
||||
@@ -82,7 +82,7 @@ Job 完成后,`main.py` 在 Blender 主线程读取可选的 RegionMask,加
|
||||
| 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` 的标定函数,根据最终 Region Alpha 计算。透明像素达到 `5%` 时使用有效 Region 自适应标定,否则使用稳定的固定基准。
|
||||
Depth Shape 的 Base Plane Depth,以及 Depth Balloon 的 Direction,都由 `object.py` 调用 `geometry.py` 的标定函数,根据最终 Selection Alpha 计算。透明像素达到 `5%` 时使用有效 Selection 自适应标定,否则使用稳定的固定基准。
|
||||
|
||||
Normal Map 关闭时材质使用几何法线;开启时,`object.py` 把生成的图片和已经确定的 Normal Space 交给材质节点。最后 `main.py` 提交 Undo,整条 Cutout 流程结束。
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# Image Tool
|
||||
|
||||
Image Tool 从 Box、Lasso、Line 或 Polyline 手势得到一个图片 Region,再用它修改当前 Image Empty 的 Alpha。
|
||||
Image Tool 从 Box、Lasso、Line 或 Polyline 手势得到一个图片 Selection,再用它修改当前 Image Empty 的 Alpha。
|
||||
|
||||
## 执行过程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Tool["Box / Lasso / Line / Polyline"] --> Gesture["采集图片空间手势"]
|
||||
Gesture --> Region["RegionPath → RegionMask"]
|
||||
Region --> Refine{"Fit to Foreground?"}
|
||||
Refine -- "否" --> Mask["使用本地 RegionMask"]
|
||||
Refine -- "是" --> Job["refine-image-region"]
|
||||
Gesture --> Selection["SelectionPath → SelectionMask"]
|
||||
Selection --> Refine{"Refine Selection?"}
|
||||
Refine -- "否" --> Mask["使用本地 SelectionMask"]
|
||||
Refine -- "是" --> Job["refine-image-selection"]
|
||||
Job --> Mask
|
||||
Mask --> Alpha["源 Alpha × RegionMask"]
|
||||
Mask --> Alpha["源 Alpha × SelectionMask"]
|
||||
Alpha --> Crop["紧裁切并写回 Empty"]
|
||||
```
|
||||
|
||||
四个工具共用 `EditImageMask`。Box 和 Lasso 可以执行 Fit to Foreground;Line 与 Polyline 始终保留几何边界,直接使用本地 `RegionMask`。
|
||||
四个工具共用 `EditImageMask`。Box 和 Lasso 可以执行 Refine Selection;Line 与 Polyline 始终保留几何边界,直接使用本地 `SelectionMask`。
|
||||
|
||||
## 节点说明
|
||||
|
||||
@@ -24,22 +24,22 @@ flowchart TD
|
||||
|
||||
`operators/image_tool/__init__.py` 汇总注册类型;`box.py`、`lasso.py`、`line.py` 和 `polyline.py` 分别定义四个 WorkspaceTool。它们都把操作交给 `edit_mask.py` 中的 `EditImageMask`。
|
||||
|
||||
屏幕坐标投射、点击选择、轨迹预览和状态切换来自 `common/image_interaction.py`。Lasso 按移动距离采点并合并近似共线点;Line 扩展为有向半平面;Polyline 在确认时闭合路径。
|
||||
屏幕坐标投射、点击选择、轨迹预览和状态切换来自 `common/viewport.py`。Lasso 按移动距离采点并合并近似共线点;Line 扩展为有向半平面;Polyline 在确认时闭合路径。
|
||||
|
||||
### RegionPath → RegionMask
|
||||
### SelectionPath → SelectionMask
|
||||
|
||||
`EditImageMask` 通过 `common/image_region.py` 把手势整理为 `RegionPath`,再栅格化为 `RegionMask`。凹多边形使用三角剖分,自交路径使用 `EVEN_ODD` 填充。预览缓存只在轨迹变化时更新。
|
||||
`EditImageMask` 通过 `common/selection.py` 把手势整理为 `SelectionPath`,再栅格化为 `SelectionMask`。凹多边形使用三角剖分,自交路径使用 `EVEN_ODD` 填充。预览缓存只在轨迹变化时更新。
|
||||
|
||||
Box 和 Lasso 分别读取自己的 **Fit to Foreground** 设置。Line 与 Polyline 直接使用几何边界。
|
||||
Box 和 Lasso 分别读取自己的 **Refine Selection** 设置。Line 与 Polyline 直接使用几何边界。
|
||||
|
||||
### Fit to Foreground
|
||||
### Refine Selection
|
||||
|
||||
未开启 Fit to Foreground 时,`edit_mask.py` 直接使用本地 `RegionMask`。开启后,`RefineImageRegion` 把带 Region Alpha 的局部 RGBA 提交给 `server/jobs/refine_image_region.py`;Job 调用 `server/region/refine_region.py` 运行 BEN2,并返回收紧后的图片和 `bounds`。
|
||||
未开启 Refine Selection 时,`edit_mask.py` 直接使用本地 `SelectionMask`。开启后,`RefineImageSelection` 把带 Selection Alpha 的局部 RGBA 提交给 `server/jobs/refine_image_selection.py`;Job 调用 `server/selection/refine_selection.py` 运行 BEN2,并返回收紧后的图片和 `bounds`。
|
||||
|
||||
响应阶段仍由 `edit_mask.py` 把局部结果映射回完整画布。
|
||||
|
||||
### 源 Alpha × RegionMask
|
||||
### 源 Alpha × SelectionMask
|
||||
|
||||
`edit_mask.py` 保留源 RGB,只把最终 `RegionMask` 乘入源 Alpha,再按非零 Alpha 紧裁切。像素读取、结果 Image 创建和共享引用处理使用 `common/image_data.py`。
|
||||
`edit_mask.py` 保留源 RGB,只把最终 `SelectionMask` 乘入源 Alpha,再按非零 Alpha 紧裁切。像素读取、结果 Image 创建和共享引用处理使用 `common/image.py`。
|
||||
|
||||
公共 Image Empty 替换入口同步更新 `empty_display_size` 和 `empty_image_offset`,使裁切后的内容对齐原图片平面并保持对象 `matrix_world`。
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
| `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) | Region 选择、基础 Shape 构建与 Blender 对象创建 |
|
||||
| `operators/cutout_tool/` | [Cutout Tool](cutout-tool.md) | Selection、基础 Shape 构建与 Blender 对象创建 |
|
||||
| `operators/debug.py` | [Debug Operator](debug.md) | 模型接口与原始产物验证 |
|
||||
| `operators/image_tool/` | [Image Tool](image-tool.md) | Box、Lasso、Line 与 Polyline 编辑 |
|
||||
| `operators/perspective_tool/` | [Perspective Tool](perspective-tool.md) | 四点透视框与透视校正 |
|
||||
|
||||
@@ -23,7 +23,7 @@ flowchart TD
|
||||
|
||||
`operators/perspective_tool/__init__.py` 导出注册类型,交互和计算集中在 `edit_perspective.py`。`ImagePerspectiveTool` 启动 `EditImagePerspective`,后者接受任意顺序的四个图片角点,再根据源图 X/Y 方向整理成不交错的凸 Quad。
|
||||
|
||||
图片坐标投射和首次点击选择复用 `common/image_interaction.py`。四个角点在该流程中统一表示为 Quad。
|
||||
图片坐标投射和首次点击选择复用 `common/viewport.py`。四个角点在该流程中统一表示为 Quad。
|
||||
|
||||
### 预览并调整比例
|
||||
|
||||
@@ -37,4 +37,4 @@ flowchart TD
|
||||
|
||||
### 裁切并写回
|
||||
|
||||
Warp 结果按可见 Alpha 紧裁切,再通过 `common/image_data.py` 创建 Packed Image 并替换当前 Empty。写回时保持中心和覆盖尺度近似一致。
|
||||
Warp 结果按可见 Alpha 紧裁切,再通过 `common/image.py` 创建 Packed Image 并替换当前 Empty。写回时保持中心和覆盖尺度近似一致。
|
||||
|
||||
@@ -20,7 +20,7 @@ Packed 图片和内存中修改过的单图会先导出临时文件;文件图
|
||||
|
||||
### 准备媒体输入
|
||||
|
||||
入口是 `operators/remove_background.py` 中的 `RemoveImageBackground`。它检查当前对象、AI Environment 和帧数,再调用 `common/image_data.py` 的 `prepare_image_input()`。多帧输入会先写入 Scene 中的抽帧设置。
|
||||
入口是 `operators/remove_background.py` 中的 `RemoveImageBackground`。它检查当前对象、AI Environment 和帧数,再调用 `common/image.py` 的 `prepare_image_input()`。多帧输入会先写入 Scene 中的抽帧设置。
|
||||
|
||||
准备完成后,内部 Operator `RunBackgroundRemoval` 保存输入路径、源对象名和临时文件清理标记。
|
||||
|
||||
@@ -32,6 +32,6 @@ BEN2 推理前先用源 Alpha 屏蔽透明区域,模型输出的前景 Alpha
|
||||
|
||||
### 导入并替换结果
|
||||
|
||||
Job 返回后,`remove_background.py` 的 `apply_background_response()` 调用 `common/image_data.py` 创建单图或序列 Image,并恢复源 Image User 的帧起点、偏移和循环设置。
|
||||
Job 返回后,`remove_background.py` 的 `apply_background_response()` 调用 `common/image.py` 创建单图或序列 Image,并恢复源 Image User 的帧起点、偏移和循环设置。
|
||||
|
||||
`replace_empty_image()` 随后替换 `Empty.data`,提交一个 Remove Background Undo 步骤,并清理本次创建的临时输入。
|
||||
|
||||
@@ -25,7 +25,7 @@ flowchart TD
|
||||
|
||||
入口位于 `operators/upscale.py`。`UpscaleImage.poll()` 读取 `preferences.py` 中的 **Maximum AI Input Size**;当前 Image 长边必须严格小于该值。菜单显示的预计尺寸来自同一文件的 `upscale_output_size()`。
|
||||
|
||||
执行时,`upscale.py` 冻结当前模型和输入上限,并通过 `common/image_data.py` 准备单图、视频或序列输入。Packed Image 与内存修改结果会导出临时文件,多帧输入保留帧起点和抽帧设置。
|
||||
执行时,`upscale.py` 冻结当前模型和输入上限,并通过 `common/image.py` 准备单图、视频或序列输入。Packed Image 与内存修改结果会导出临时文件,多帧输入保留帧起点和抽帧设置。
|
||||
|
||||
### 分块放大 RGB
|
||||
|
||||
@@ -39,6 +39,6 @@ flowchart TD
|
||||
|
||||
### 导入并替换结果
|
||||
|
||||
响应阶段回到 `operators/upscale.py`,通过 `common/image_data.py` 读取 `upscale.*` 结果序列并替换当前 Empty。
|
||||
响应阶段回到 `operators/upscale.py`,通过 `common/image.py` 读取 `upscale.*` 结果序列并替换当前 Empty。
|
||||
|
||||
后续 Upscale 和 Depth Plane 都以当前结果 Image 为输入。
|
||||
|
||||
Reference in New Issue
Block a user