Files
AnyImage/openspec/changes/separate-selection-from-image-content/design.md
nan 7f6e47432e feat(selection): separate selection geometry from image content processing
- Introduced a new design for selection rasterization that decouples the selection geometry from the image content, ensuring that the SelectionMask is independent of the source image's alpha and color.
- Updated the `rasterize_selection_path` function to only accept selection paths and image dimensions, removing parameters related to source alpha and alpha thresholds.
- Implemented a new approach for handling selection paths using non-zero winding fill, ensuring overlapping areas are preserved.
- Enhanced performance by utilizing a scanline algorithm for rasterization, significantly reducing processing time for high-resolution images.
- Updated internal documentation and added comprehensive tests to validate the new behavior and performance improvements.
- Ensured backward compatibility by maintaining existing interfaces where necessary while introducing breaking changes in the selection processing logic.
2026-09-01 22:36:20 +08:00

83 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
`rasterize_selection_path()` 当前同时完成路径填充、抗锯齿、源 Alpha 相交、空内容判断和内容 bounds 收紧。调用方因此必须先通过 Blender `Image.pixels` 取得 RGBA,导致 `SelectionMask` 随图片透明度变化,而不是稳定表达用户圈选。
Image Tool 与 Cutout 的实际业务确实需要图片内容,但目的不同:本地编辑要把 Selection 乘入源 Alpha,Cutout 几何要用内容 Mask,AI 操作要把局部图片写成 Job 输入。需要 AI 时,现有代码先把 Lasso Mask 乘入临时 RGBA 并编码 PNG;响应后又读取源图并新建颜色 Image,造成同一操作中的第二次大图编码。实测 6288×5920 RGBA PNG 的 Selection 栅格化约 0.17–0.55 秒,提交前 PNG 保存约 1.79 秒,新建颜色 Image 的 Pack 约 1.74 秒。图片编码成本不属于 Selection,并且本设计接受生成新图片所需的单次编码成本。
## Goals / Non-Goals
**Goals:**
- 让 SelectionPath 与 SelectionMask 只表达用户圈选几何,不受源图片 Alpha 或颜色影响。
- 让 Image Tool、Cutout 与 AI 输入准备在各自业务边界显式消费源 Alpha 或 RGBA。
- 所有 AI 操作统一使用 Selection 几何 bounds 内、未乘 Lasso Mask 的矩形局部 RGBA 文件。
- 复用提交前输入或 Refine 输出的已有编码图片,避免 AI 响应阶段重新构建并编码相同颜色图。
- 保持 non-zero winding、Invert、抗锯齿和局部画布语义。
**Non-Goals:**
- 不优化生成新图片时不可避免的 PNG 编码、Pack 或磁盘写入耗时。
- 不按 FILE、Packed、Generated 或 Dirty 来源建立不同的 Operator 主链。
- 不引入 Pillow、GPU readback、后台 Blender 数据访问或新的运行时依赖。
- 不改变 SelectionPath JSON、AI 模型、Cutout Shape 算法或 Job 进程边界。
## Decisions
### 1. Selection 栅格化只产生几何 Mask
`rasterize_selection_path(image_size, selection_path, *, antialias=False)` 只根据路径、Invert、图片尺寸和抗锯齿生成 `SelectionMask(values, bounds)`。删除 `source_alpha` 与 `alpha_threshold` 参数,栅格器不导入 Blender Image helper,也不执行内容可见性检查。
普通 Selection 的 bounds 来自路径与抗锯齿 padding 裁切到图片范围;Invert 仍使用整张图片。相同 Path、尺寸和抗锯齿设置必须在透明、半透明与不透明图片上得到完全相同的 SelectionMask。
相比保留可选 Alpha 参数,彻底删除内容入口可以固定职责并阻止调用方继续把内容 bounds 误当成 Selection bounds;不保留兼容调用方式。
### 2. 图片业务直接合并局部 Alpha 与 Selection 值
Image Tool 在 `SelectionMask.bounds` 内裁切源 RGBA,并直接执行 `local_alpha *= selection_values`。最终内容 bounds、空内容和结果图片创建都属于 Image 编辑业务。实现应在已有局部数组上计算行列可见范围,不为每个可见像素生成坐标数组,也不把内容相交结果写回 SelectionMask。
本地 Cutout 在构建 BaseShape 时显式组合局部源 Alpha 与 Selection 值;颜色、几何和空内容判断使用该业务结果。SelectionMask 本身在调用链中保持不变。相比新增第二种与 SelectionMask 同形的公共值类型,业务直接消费两个一通道局部数组更简单,也避免把图片内容重新包装成“Selection”。
### 3. AI 输入是 Selection bounds 的原始矩形 RGBA
将当前“Selection 输入”改为“Bounds 图片输入”:公共图片 helper 接收源 RGBA 与几何 bounds,只裁切并写出矩形 RGBA,不把 SelectionMask 乘入 Alpha。Image Tool、Cutout Refine、Normal 与 Depth 都调用同一个入口;来源已经统一为 Blender Image,不在 Operator 中按文件、Packed 或生成图片分流。
Refine 把 Lasso 当作上下文范围。BEN2 可以在该矩形内保留 Lasso 外的内容,其输出 Alpha 是 Refine 业务结果,不再与原始 Lasso Mask 相交。Normal 或 Depth 也消费同一矩形输入,最终 Shape 仍由本地 Selection 几何约束。
Invert Selection 的几何 bounds 是全图,因此其 AI 输入自然退化为全图矩形,不增加特殊协议。
### 4. 已编码的 AI 图片直接成为颜色资产
AI 响应选择一个已有文件作为最终局部颜色图:启用 Refine 时使用 BEN2 输出;仅生成 Normal 或 Depth 时使用提交前的 Bounds 输入。Blender 直接加载该文件,继承源颜色空间和 Alpha 约定,Pack 已编码文件后交给 Image Empty 或 Cutout 材质。
Image Tool Refine 直接按响应 bounds 替换源 Image Empty,不再提取输出 Alpha、重新读取源 RGBA、创建 Pixel Result Image。Cutout 从同一个局部文件读取需要的 Alpha 构建几何,并把该已加载 Image 用作颜色纹理,不再调用 `_cropped_color_image()` 重建同一颜色内容。
输入临时文件必须保持到响应完成且 Blender 已加载并 Pack;随后由现有 Job 清理生命周期删除。相比继续从原图重建,这一方案保持颜色来源一致,并把已有文件 Pack 从大图重新编码变为文件收纳。
### 5. 内容错误由消费业务报告
纯 Selection 只在路径与图片范围没有几何交集时报告空 Selection。源 Alpha 全透明、Image 编辑结果为空、Refine 输出为空或 Cutout 内容 Mask 为空,分别由执行该内容操作的业务报告。这样错误发生位置与实际失败职责一致。
## Risks / Trade-offs
- [Refine 结果扩展到 Lasso 外] → 这是明确接受的产品语义;测试固定 Lasso 只提供矩形上下文,不作为 Refine 硬 Mask。
- [Selection bounds 不再等于内容 bounds] → 在类型和文档中只把 SelectionMask bounds 称为几何 bounds,内容业务独立计算并传递 placement bounds。
- [AI 输入矩形包含额外背景] → 最终本地 Shape 仍受 Selection 几何约束;Refine 则有意允许模型利用并保留矩形上下文。
- [复用 PNG 改变颜色空间或 Alpha 配置] → 加载后显式继承源 Image 的颜色空间与 Alpha mode,并用结果等价测试覆盖。
- [临时文件被过早清理] → 保持现有 Job cleanup 边界,只有成功加载并 Pack 或操作取消后才删除输入。
- [全图或 Invert 仍需处理大图] → 接受单次新图片编码成本,本次只删除 Selection 的内容依赖和 AI 响应阶段的重复编码。
## Migration Plan
1. 修改 Selection 栅格 API 与测试,使其只产生几何 Mask,并迁移所有调用方。
2. 将 Image Tool 与本地 Cutout 的源 Alpha 合并、内容 bounds 和空内容检查放入各自业务阶段。
3. 将 `prepare_selection_input` 替换为统一的 Bounds 矩形 RGBA 输入,并更新 AI Job 输入语义和测试。
4. 调整 Image Tool 与 Cutout AI 响应,直接加载并 Pack 输入或 Refine 输出,删除源图重读和颜色 Image 重建。
5. 更新内部文档,运行相关测试、完整测试和真实高分辨率阶段基准。
回退时整体恢复旧栅格参数、Masked AI 输入和响应重建链,不保留双入口或兼容层。
## Open Questions
无。