- Added upscale operator to handle image upscaling by 4x using AI models. - Introduced new jobs for upscale processing in the server, including debug and standard upscale jobs. - Created a new ONNX model handler for upscale operations, supporting tiled inference and resource management. - Updated tests to cover new upscale functionality, including input validation and output verification. - Added documentation for the upscale feature, detailing usage and model options.
89 lines
5.3 KiB
Markdown
89 lines
5.3 KiB
Markdown
# Image 工具技术实现
|
||
|
||
Image 工具位于 `operators/image_edit/`。Box、Lasso、Line 与 Polyline 共享直接编辑管线;Perspective 在工具栏中独立显示并处理 Homography,但复用源图选择、绘制和 Empty 写回。
|
||
|
||
## 模块职责
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Tools["box / lasso / line / polyline\nWorkspaceTool"] --> Mask["mask.py\n手势、Refine、Invert"]
|
||
Perspective["perspective.py\n四点、比例、Homography"]
|
||
Mask --> Common["common.py\n选择、绘制、裁切、Empty 写回"]
|
||
Perspective --> Common
|
||
Mask --> Job["refine-image-mask\nBEN2 内容 mask"]
|
||
```
|
||
|
||
- `box.py`、`lasso.py`、`line.py`、`polyline.py` 只声明 WorkspaceTool。
|
||
- `mask.py` 用 `EditImageMask` 统一四种手势;`RefineImageMask` 负责异步 BEN2 响应。
|
||
- `perspective.py` 维护四点输入、比例预览和全分辨率重采样。
|
||
- `common.py` 只放跨两条管线实际共用的逻辑。
|
||
|
||
## 首次点击
|
||
|
||
`resolve_image_edit_click()` 临时调用 Viewport Select 判断点击目标:
|
||
|
||
| 点击目标 | 行为 |
|
||
| --- | --- |
|
||
| 另一张 Image Empty | 选择并设为 Active,本次不编辑 |
|
||
| 当前 Active 图片、空白或非图片对象 | 恢复原选择并开始编辑当前图片 |
|
||
|
||
这避免工具在无有效源图时退化为对象移动,也让用户无需切回 Select Tool。
|
||
|
||
## Mask 管线
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Gesture["屏幕手势"] --> Polygon["源图片像素 polygon / mask"]
|
||
Polygon --> Refine{"Refine Selection?"}
|
||
Refine -- 否 --> Content["几何 mask"]
|
||
Refine -- 是 --> Server["选区外 RGBA 清零 → BEN2"]
|
||
Server --> Content2["BEN2 Alpha × 几何 mask"]
|
||
Content --> Invert{"Invert?"}
|
||
Content2 --> Invert
|
||
Invert -- 否 --> Final["final mask"]
|
||
Invert -- 是 --> Reverse["1 - final mask"]
|
||
Final --> Apply["源 Alpha × mask"]
|
||
Reverse --> Apply
|
||
Apply --> Crop["按非零 Alpha 紧裁切\n保留源 RGB"]
|
||
Crop --> Write["替换 Image 并更新 Empty 显示"]
|
||
```
|
||
|
||
Box / Lasso 的顺序固定为 **Refine → Invert**。BEN2 只提供内容 Alpha;最终 RGB 始终来自源图。Blender 侧只修改 Alpha,不清理透明区 RGB,以避免 Viewport 纹理过滤产生黑边。Line / Polyline 强制关闭 Refine,只执行几何 mask 与 Invert。
|
||
|
||
未形成有效区域的单击直接返回 `CANCELLED`,不报告消息;最终 Alpha 全空时,Operator 报告 `WARNING` 并保持源图不变。其他数据、状态或运行时异常仍报告 `ERROR`。
|
||
|
||
Line 的屏幕坐标 Y 轴与图片坐标 Y 轴方向相反,因此像素阶段会反转线段端点,使半透明预览侧与实际保留侧一致;`Ctrl` 只影响屏幕端点的 5° 吸附。零长度点击在投影前直接取消,投影结果不足两个点时也会安全终止。Polyline 接近起点时绘制闭合圆环,并支持单击起点或 `Enter` 完成;完成时显式闭合,不把当前游标位置作为额外顶点。
|
||
|
||
## Perspective 管线
|
||
|
||
四点先按源图片 X/Y 方向规范为凸四边形,输入顺序不参与输出方向。第四点后只生成一次 512 × 512 临时纹理;移动鼠标改变 UI 比例,确认时才进行全分辨率 Homography。
|
||
|
||
预览引用在删除前先从 Operator 清空;若 Undo 或 Blender 已移除临时 Image,清理会忽略失效的 RNA 引用,因此切换工具和重复结束都安全。
|
||
|
||
透视重采样使用预乘 Alpha 插值,随后恢复 RGB、清理透明像素并紧裁切。输出最长边限制为 8192 px,总像素限制约 16 MP。
|
||
|
||
Perspective 不调用 BEN2,也不支持 Invert。它是 deform 工具,不属于 mask 清理语义。
|
||
|
||
## Empty 写回
|
||
|
||
`replace_empty_image_pixels()` 先创建并验证完整结果,再根据引用源 Image 的 Image Empty 数量选择提交方式:只有一个引用时,把原 Image 的用户重映射到结果、删除旧 datablock 并恢复原名,最终不留下额外图片;两个或更多引用时,只替换当前 Empty 的 `data`,其他 Empty 继续使用原图。共享图片的新结果沿用源 Image 基础名,不附加 `.mask`、`.perspective` 或其他工具后缀;同名冲突交给 Blender 自动添加 `.001`。两种方式都不复制对象,也不修改 `matrix_world`。
|
||
|
||
`empty_display_alignment()` 在写入像素前,根据源画布、旧 `plane_bounds`、结果尺寸和放置 bounds 计算显示参数;写回函数随后更新:
|
||
|
||
- `empty_display_size`;
|
||
- `empty_image_offset`。
|
||
|
||
Box、Lasso、Line 与 Polyline 的放置 bounds 等于最终裁切范围,因此像素与原图片平面严格对齐。Perspective 的结果比例可能不同于四边形轴对齐范围,因此只保持中心和覆盖尺度近似一致。
|
||
|
||
## Server 边界
|
||
|
||
普通 Mask 和 Perspective 完全在 Blender 侧执行。只有 Box / Lasso 的 **Refine Selection** 提交 `refine-image-mask`:
|
||
|
||
1. 根据 polygon 创建局部透明画布;
|
||
2. 清空选区外 RGBA;BEN2 公共入口再以输入 Alpha 遮住透明 RGB;
|
||
3. 使用共享的 `remove_image_background()` 运行 BEN2;
|
||
4. 再乘几何 selection mask;
|
||
5. 返回紧裁切的 `image.png` 与源图坐标 `bounds.json`。
|
||
|
||
Blender 只读取返回图片的 Alpha,并把它应用到原图 RGB。相同的 BEN2 输入清理规则也用于 Remove Background、Depth Plane、Surface 和 Balloon;正式输出使用源 RGB 与 BEN2 Alpha。Blender 数据只在主线程 Operator 中读写,Job 参数保持为可序列化字典。
|