# 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`。 Box、Lasso、Line 与 Polyline 的屏幕预览统一绘制白色虚线轮廓和淡灰半透明填充。简单选区使用 Blender 的凹多边形三角剖分,不使用从首点展开的 Triangle Fan;通过屏幕网格快速检测到自交后改用 2 px 扫描填充,并读取 Operator 的最终填充规则。Image Mask 使用偶奇规则,Cutout 使用与最终几何一致的非零环绕规则。Lasso 只在游标相对末点移动至少 2 px 时采点,停留产生的重复 `MOUSEMOVE` 不增加轨迹;新点与前一线段的偏差不超过 0.75 px 时直接替换末点,在线消除近似共线点。填充三角形和闭合虚线段都按当前屏幕 polygon 缓存,额外重绘不重复计算。松开鼠标后使用这条已简化的闭合 polygon 生成几何 mask。 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 参数保持为可序列化字典。