Add comprehensive tests for image processing and mask functionalities

- Introduced tests for color image handling, ensuring source preservation and premultiplied alpha usage.
- Implemented tests for brush stroke operations, validating segment boundaries and brush anti-aliasing.
- Added tests for frame anti-aliasing, confirming correct rendering behavior with various alpha values and depth settings.
- Created tests for frame projection, ensuring accurate overlap calculations and half-plane clipping.
- Developed tests for image premultiplied retention, verifying behavior during loading and packing processes.
- Added undo functionality tests for mask operations, ensuring single transaction behavior during edits.
This commit is contained in:
2026-09-05 21:33:11 +08:00
parent bbaa4bff46
commit 1994d38ccd
111 changed files with 7691 additions and 2341 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-03

View File

@@ -0,0 +1,104 @@
## Context
当前 `operators/crop_tool/` 同时承载 Frame、Lasso、Polyline 与 Perspective。Lasso 和 Polyline 把一次几何 Selection 乘入源 Alpha,随后按可见内容紧裁切并调整 Image Empty 画框;Lasso 还可以提交 BEN2 Refine Job。三个 Crop 子工具分别保存 Keep Original,公共 `CropSelectionGesture` 因而混合了工具设置、Lasso/Polyline 交互和 Cutout 复用职责。
新交互把 Alpha 视为可反复编辑的当前状态:Mask 用 Lasso 或 Brush 生成一次手势 Mask,再以 Set、Add 或 Subtract 合成 Alpha。Frame、Mask、Rectify 是同一个简短的 Image Edit 工具组。当前工作区的 `standardize-cutout-z-axis` 同时修改 Cutout 主链,实施本 change 时应以该 change 完成后的 Cutout 结构为基线。
## Goals / Non-Goals
**Goals:**
- 用 **Frame / Mask / Rectify** 表达三个工具的真实产物,并删除全部旧 Crop 工具标识。
- 让一个 Mask WorkSpaceTool 和一个业务 Operator 承载 Lasso、Brush 及统一 Alpha 合成。
- 让 Mask 的图片尺寸、Image Empty 身份和矩形画框在每次编辑后保持不变。
- 删除 Crop/Cutout Refine Selection 与 Crop Keep Original 的完整参数和执行路径。
- 保留 Rectify 已有的四点透视校正、宽高比、输出限制和 Alpha trim 行为。
**Non-Goals:**
- 不为 Mask 维护独立 Blender Mask、Selection 历史或隐藏的原始 Alpha 副本。
- 不让 Cutout 使用 Brush、Set/Add/Subtract,Cutout 继续以单次 Lasso 定义 Shape 范围。
- 不改变 Frame 合成、Rectify Homography、Cutout Shape、Depth、Normal Map 或 Remove Background。
- 不为已删除的类、Operator ID、Tool ID、属性或 Job 保留兼容层。
## Decisions
### 1. 用 Image Edit 模块承载 Frame、Mask、Rectify
将 `operators/crop_tool/` 改为与业务边界一致的 `operators/image_edit_tool/`,内部文件使用 `frame.py`、`mask.py` 和 `rectify.py`。注册顺序固定为 Frame、Mask、Rectify,Frame 继续作为工具组首项;Cutout 保持为相邻的独立工具。
公开名称和 ID 只保留当前术语:`MaskTool`、`EditImageAlpha`、`RectifyTool`、`RectifyImagePerspective`,以及 `anyimage.mask`、`anyimage.edit_image_alpha`、`anyimage.rectify`、`anyimage.rectify_image_perspective`。激活入口改为 Image Edit 语义并默认选择 Frame。
不选择继续使用 Crop 模块并只修改标签,因为源码边界、状态属性和错误信息仍会表达已经不存在的裁切模型。
### 2. 一个 Mask Tool 读取 Gesture、Mode 与 Radius
Scene 设置保存 `mask_gesture`、`mask_mode` 和 `mask_radius`:Gesture 为 `LASSO` 或 `BRUSH`,Mode 为 `SET`、`ADD` 或 `SUBTRACT`,Radius 是 Brush 使用的正整数屏幕像素,默认 25 px。Tool Settings 以展开的三个图标显示 Mode;Radius 仅在 Brush 时显示。
Mask 只有一个 WorkSpaceTool 和一个 Operator。Operator 在 invoke 时冻结三个设置;Lasso 收集并闭合自由手绘路径,Brush 收集连续笔画并显示当前 Radius 圆环。单击 Brush 生成一个圆形印记,拖动时相邻采样之间生成连续覆盖,不因鼠标事件间距留下空洞。两种手势最终都只产生一个 `SelectionMask` 并进入同一个 Alpha 合成入口。
不选择两个 WorkSpaceTool,因为它们只有手势采集不同,分开后会重复模式、结果和设置逻辑,也不符合用户在当前 Tool 内切换 Gesture 的预期。
### 3. 直接以当前 Alpha 和本次手势 Mask 合成
设当前像素 Alpha 为 `A`,本次带边缘权重的手势 Mask 为 `M`:
```text
Set A' = A × M
Add A' = max(A, M)
Subtract A' = A × (1 - M)
```
所有运算逐像素限制在 `0–1`,RGB 原样复制。Set 保留本次手势内的当前可见内容;Add 可以重新提高已擦除区域的 Alpha;Subtract 降低本次手势覆盖区域的 Alpha。因为不保存初始 Alpha,Add 以本次 Mask 权重为恢复值,可能高于图片导入时的 Alpha;这是直接编辑当前 Alpha 的明确语义。
不选择保存隐藏的原始 Alpha,因为它会引入跨图片替换的额外状态、生命周期和同步规则,也会让 Add 不再是直接的 Alpha 涂抹。
### 4. Mask 始终替换同尺寸图片而不改变画框
Mask 读取完整当前 RGBA,应用 Alpha 合成后创建相同宽高的 packed Image,并用公共图片替换入口赋给 active Image Empty。它不传 `placement_bounds`,因此对象 identity、matrix、display size 和 image offset 不变;共享旧 Image 的其他对象继续引用旧数据。即使结果 Alpha 全部为零也允许完成,仍作为一个 Undo 步骤。
删除 Mask 的紧裁切和空 Alpha 拒绝逻辑。Rectify 仍可按自身业务生成不同尺寸并通过 placement bounds 对齐,因此“稳定画框”只属于 Mask。
### 5. Brush Radius 使用 Viewport 屏幕像素
Radius 与 Blender Circle Select 一样以 Region 屏幕像素表达,使圆形预览与用户看到的范围一致,不受图片分辨率影响。Brush 将屏幕笔画分解为相邻线段的圆角胶囊和端点圆形,在图片平面投影后合并局部 Mask;图片外覆盖被裁掉。实现复用 SelectionMask 的 bounds/value 表示和公共抗锯齿规则,不引入 Pillow、OpenCV 或几何依赖。
不选择图片像素 Radius,因为同一设置会随图片分辨率和 Viewport 缩放产生明显不同的屏幕笔刷尺寸。
### 6. Rectify 只更换业务名称并删除 Keep Original
Perspective 的四点采集、凸 Quad 规范化、512 px 预览、目标宽高比、分块 Homography、8192 px 长边限制、总像素限制、可见 Alpha trim 和结果 placement 保持不变。类、ID、状态文本、warning、Undo 名称、文档和测试统一改为 Rectify;完成后直接替换 active Image Empty,不再复制源对象。
不选择 `Perspective`,因为它只描述概念;`Rectify` 用一个单词表达把透视四边形校正为矩形的动作,并与 Frame、Mask 的长度一致。
### 7. 完整移除 Refine Selection 和 Keep Original 路径
Mask 删除 BEN2 属性、AI readiness、临时 bounds 输入、SelectionMask sidecar、异步响应 Operator 和 `refine-image-selection` Job。Cutout 删除对应工具设置、Pie 参数、Operator 参数和 `refine_model` 请求;需要 Depth 或 Normal 时继续提交原始 Selection bounds 矩形,颜色图直接复用该输入。
Server Cutout Job 不再导入或执行 refine,删除专用 Job 注册、Job 文件和只为该能力存在的 `server/selection/` 封装。BEN2 模型、缓存、Remove Background 与 Debug 保留。
删除三个旧 Crop Keep Original 属性和 Operator 参数。`place_empty_image_result()` 若不再有调用方则删除,由 Mask 和 Rectify 直接复用 `replace_empty_image()`;不保留复制结果对象的公共胶水代码。
## Risks / Trade-offs
- [Add 会让原本透明的像素变得可见] → 文档和公式明确它直接涂抹当前 Alpha;RGB 始终保留,Undo 提供操作级恢复。
- [屏幕圆形投影到倾斜图片后不再是图片像素圆形] → 以 Viewport 视觉范围为准,并用投影后的胶囊覆盖测试倾斜与缩放图片。
- [快速拖动产生断裂笔画] → 以相邻事件线段生成连续胶囊,而不是只绘制离散圆点。
- [模块和 ID 全量改名遗漏注册或注销] → 参数化检查类导出、工具顺序、Operator ID、状态文本、打包内容和逆序注销。
- [删除 Refine 时误删 BEN2 公共能力] → 测试固定 Remove Background、BEN2 Debug、模型目录和打包内容仍存在。
- [与 Cutout Z 轴 change 修改同一文件] → 先完成 `standardize-cutout-z-axis`,再基于其最终 `main.py`、测试和文档移除 Refine 分支。
## Migration Plan
1. 先完成并验证 `standardize-cutout-z-axis`,冻结本 change 的 Cutout 基线。
2. 将 Crop 模块与公共命名迁移为 Image Edit,注册 Frame / Mask / Rectify 并删除 Polyline 和旧 ID。
3. 实现 Mask 设置、Lasso/Brush 手势、统一 Alpha 合成和稳定画布结果,删除 Keep Original。
4. 将 Perspective 完整改名为 Rectify,保留 Homography 行为并删除 Keep Original。
5. 删除 Blender 与 Server 两侧 Refine Selection 路径,验证 Cutout 的本地、Depth 和 Normal 分支。
6. 更新测试与当前文档并运行完整测试;不构建节点资产、文档站点或发布包。
回退必须整体恢复旧 Crop 模块、工具注册、属性、Refine Job 和文档,避免 Blender 请求已删除的 Server Job 或注册不存在的 Tool ID。
## Open Questions
无。

View File

@@ -0,0 +1,30 @@
## Why
现有 Crop Lasso 实际需要反复编辑图片 Alpha,却会紧裁切画布并把 Lasso、Polyline 分成独立工具;Crop Perspective 的名称也没有直接表达透视校正目的。将图片编辑入口收敛为简短的 **Frame / Mask / Rectify**,可以让工具名称、画布行为和用户操作保持一致。
## What Changes
- **BREAKING**:将 Crop Tool 组改为 **Frame / Mask / Rectify**,删除 Crop 命名和旧工具 ID,不保留兼容入口。
- **BREAKING**:删除 Crop Polyline;Mask 在一个 Tool 中提供 Lasso 与 Brush 两种手势。
- Mask 提供 Set、Add、Subtract 三种 Alpha 编辑模式;Brush 额外提供可持久保存的 Radius。
- Mask 始终保留当前图片的完整矩形画布、分辨率和 Image Empty 变换,只修改 Alpha,不再按可见内容紧裁切。
- 将 Crop Perspective 重命名为 **Rectify**,继续通过四点 Homography 生成透视校正结果。
- **BREAKING**:删除 Frame / Mask / Rectify 工具组中的 **Keep Original**,结果直接替换 active Image Empty 承载的图片。
- **BREAKING**:删除 Crop 与 Cutout 的 **Refine Selection** 选项、请求参数、Job 和结果处理;BEN2 继续用于 Remove Background 与调试。
## Capabilities
### New Capabilities
- `image-edit-tools`: 规定 Frame、Mask、Rectify 的工具组成、命名、Mask 手势与 Alpha 模式、稳定画布,以及结果替换行为。
### Modified Capabilities
无。
## Impact
- 影响 Crop 工具注册、激活入口、WorkspaceTool、公共 Viewport 手势、Scene 设置、图片结果落地和相关测试。
- 影响 Cutout 的工具设置、交互参数、Job 编排,以及 Server 的 Refine Selection Job 注册与文件。
- 影响 README、用户索引、内部架构、Operator、Runtime、Output 和公共模块文档。
- 不新增依赖,不改变 Frame 合成、Rectify Homography、Cutout Shape、Depth、Normal Map、Remove Background 或 BEN2 模型实现。

View File

@@ -0,0 +1,124 @@
## ADDED Requirements
### Requirement: Image Edit group uses Frame Mask and Rectify
系统 SHALL 将图片编辑工具组依次注册为 **Frame**、**Mask** 和 **Rectify**,并 SHALL 将 Cutout 作为其后的独立工具。系统 MUST NOT 注册旧 Crop Lasso、Crop Polyline 或 Crop Perspective Tool ID,也 MUST NOT 提供旧 ID 的兼容入口。
#### Scenario: Image Edit tools are registered
- **WHEN** AnyImage 扩展完成注册
- **THEN** 3D Viewport 图片编辑工具组按 Frame、Mask、Rectify 顺序显示,Cutout 紧随其后
#### Scenario: Legacy Crop tools are absent
- **WHEN** 系统枚举已注册的 AnyImage WorkspaceTool 和 Operator
- **THEN** 不存在 Crop Lasso、Crop Polyline、Crop Perspective 或其旧 ID
### Requirement: Mask combines Lasso and Brush gestures
Mask SHALL 是一个 WorkSpaceTool 和一个图片 Alpha 编辑 Operator,并 SHALL 提供 `LASSO` 与 `BRUSH` Gesture。两种 Gesture MUST 生成相同的 SelectionMask 产物并进入相同的 Alpha 合成入口。
#### Scenario: Lasso edits Alpha
- **WHEN** 用户在 Mask 中选择 Lasso 并完成一个有效闭合手势
- **THEN** 系统将该 Lasso 栅格化一次并按当前 Mode 编辑 active Image Empty 的 Alpha
#### Scenario: Brush edits Alpha
- **WHEN** 用户在 Mask 中选择 Brush 并完成一次点击或拖动
- **THEN** 系统将连续圆形笔画栅格化一次并按当前 Mode 编辑 active Image Empty 的 Alpha
### Requirement: Mask provides three Alpha modes
Mask SHALL 提供 `SET`、`ADD`、`SUBTRACT` 三种互斥 Mode,并 SHALL 在 Tool Settings 中使用 Set、Add、Subtract 图标展开显示。设当前 Alpha 为 `A`、本次 SelectionMask 权重为 `M`,系统 MUST 分别计算 `A × M`、`max(A, M)`、`A × (1 - M)`,同时保持 RGB 不变。
#### Scenario: Set keeps only current content inside the gesture
- **WHEN** 用户以 Set 应用一次 Mask 手势
- **THEN** 手势外 Alpha 变为零,手势内 Alpha 等于当前 Alpha 与 SelectionMask 权重的乘积
#### Scenario: Add extends current Alpha
- **WHEN** 用户以 Add 应用一次 Mask 手势
- **THEN** 每个像素 Alpha 等于当前 Alpha 与 SelectionMask 权重的较大值
#### Scenario: Subtract removes current Alpha
- **WHEN** 用户以 Subtract 应用一次 Mask 手势
- **THEN** 每个像素 Alpha 等于当前 Alpha 与 SelectionMask 补集的乘积
#### Scenario: Fractional edge coverage is preserved
- **WHEN** SelectionMask 边缘包含 `0` 与 `1` 之间的权重
- **THEN** 系统使用该权重执行对应公式而不是先转为布尔值
### Requirement: Mask settings persist per scene
系统 SHALL 在 Scene 中保存 Mask 的 Gesture、Mode 和 Radius。Radius SHALL 是正整数 Region 屏幕像素,默认 25 px,并 MUST 仅在 Gesture 为 Brush 时显示和生效;每次手势开始后 MUST 冻结本次使用的设置。
#### Scenario: Mask settings are reused
- **WHEN** 用户设置 Brush、Add 和一个 Radius 后完成一次操作并再次使用 Mask
- **THEN** Tool Settings 保持 Brush、Add 和该 Radius
#### Scenario: Lasso hides Radius
- **WHEN** Mask Gesture 为 Lasso
- **THEN** Tool Settings 不显示 Radius 且 Lasso 栅格化不读取 Radius
### Requirement: Brush produces continuous screen-space coverage
Brush SHALL 以当前 Region 中的屏幕像素显示 Radius 圆环,并 MUST 让单击产生圆形覆盖、拖动产生带圆角端点的连续覆盖。快速移动时相邻事件点之间 MUST NOT 留下未覆盖间隙,图片范围外的笔画 MUST 被裁到图片画布。
#### Scenario: Single click stamps a circle
- **WHEN** 用户以 Brush 在图片上单击而不拖动
- **THEN** 系统以点击位置和当前 Radius 生成一个圆形 SelectionMask
#### Scenario: Sparse motion events remain connected
- **WHEN** 一次 Brush 拖动的相邻采样点距离大于 Radius
- **THEN** 两点之间仍由连续笔画覆盖
#### Scenario: Viewport scale does not change displayed Radius
- **WHEN** 用户改变 Viewport 缩放后以同一 Radius 使用 Brush
- **THEN** 笔刷圆环在 Region 中继续使用相同屏幕像素半径
### Requirement: Mask preserves the rectangular image canvas
Mask SHALL 创建与当前图片相同宽高的 packed Image,并 SHALL 保持 active Image Empty 的对象身份、matrix、display size 和 image offset。Mask MUST NOT 按 Alpha 紧裁切或传入 placement bounds,且 SHALL 允许产生全透明结果。
#### Scenario: Lasso keeps the canvas
- **WHEN** Lasso 手势只覆盖图片的一小部分
- **THEN** 结果图片尺寸和 Image Empty 矩形画框与操作前完全相同
#### Scenario: Brush keeps the canvas
- **WHEN** Brush 手势只覆盖图片的一小部分
- **THEN** 结果图片尺寸和 Image Empty 矩形画框与操作前完全相同
#### Scenario: Entire Alpha can be removed
- **WHEN** Subtract 覆盖当前全部非零 Alpha
- **THEN** 操作成功并产生相同尺寸的全透明结果
#### Scenario: Shared source remains unchanged
- **WHEN** active Image Empty 与其他对象共享同一个源 Image
- **THEN** active 对象改为引用新的编辑结果,其他对象继续引用未修改的源 Image
### Requirement: Mask and Rectify replace the active image
Mask 与 Rectify SHALL 直接替换 active Image Empty 承载的 Image,并 MUST NOT 提供 Keep Original 属性、Operator 参数或复制结果对象的路径。Frame SHALL 继续让 active Image Empty 承载合成结果。
#### Scenario: Mask replaces the active result
- **WHEN** 用户完成一次 Mask 操作
- **THEN** active Image Empty 本身承载结果且系统不创建其副本
#### Scenario: Rectify replaces the active result
- **WHEN** 用户完成一次 Rectify 操作
- **THEN** active Image Empty 本身承载结果且系统不创建其副本
### Requirement: Rectify performs four-point perspective correction
Rectify SHALL 接受四个图片点,规范化为有效凸 Quad,并通过 Homography 生成目标宽高比的矩形图片。系统 SHALL 保留现有预览、输出分辨率限制、可见 Alpha trim 和结果 placement 语义,同时 SHALL 在类名、Tool/Operator ID、标签、状态、warning 与 Undo 中统一使用 Rectify 术语。
#### Scenario: Four points create a rectified image
- **WHEN** 用户为 still Image Empty 提交四个构成有效凸 Quad 的点
- **THEN** 系统生成透视校正后的矩形图片并替换 active Image Empty 的 Image
#### Scenario: Rectify retains output limits
- **WHEN** 四点区域对应的原始输出超过长边或总像素限制
- **THEN** 系统按既有 Rectify 输出限制等比约束结果
### Requirement: Refine Selection is absent from Mask and Cutout
Mask 与 Cutout MUST NOT 声明、显示、保存或传递 Refine Selection。Server MUST NOT 注册 `refine-image-selection` Job,Cutout Job MUST NOT 接收或执行 `refine_selection` 与 `refine_model`;BEN2 Remove Background 与 Debug 能力 SHALL 保持可用。
#### Scenario: Mask has no AI refinement
- **WHEN** 用户查看 Mask Tool Settings 或执行 Mask
- **THEN** 不存在 Refine Selection 选项、AI readiness 门控或 BEN2 Job
#### Scenario: Cutout runs without refinement parameters
- **WHEN** 用户创建本地、Depth 或 Normal Cutout
- **THEN** Blender 和 Server 请求中不包含 Refine Selection 参数,最终 Shape 仍由本地几何 SelectionMask 约束
#### Scenario: BEN2 remains available elsewhere
- **WHEN** 用户运行 Remove Background 或 BEN2 Debug
- **THEN** 系统继续使用现有 BEN2 模型与缓存完成请求

View File

@@ -0,0 +1,42 @@
## 1. Image Edit 模块与工具注册
- [x] 1.1 确认 `standardize-cutout-z-axis` 已完成并以其最终 Cutout 主链、测试和文档作为实施基线
- [x] 1.2 将 `operators/crop_tool/` 迁移为 `operators/image_edit_tool/`,建立 Frame、Mask、Rectify 内容边界并删除 Polyline 文件与导出
- [x] 1.3 将类名、Operator ID、Tool ID、激活入口和状态文字改为 Image Edit、Mask、Rectify 当前术语,不保留旧 Crop 注册或兼容层
- [x] 1.4 更新扩展注册与逆序注销,补充测试验证 Frame、Mask、Rectify、Cutout 的顺序及旧工具完全缺席
## 2. Mask 设置与 Alpha 合成
- [x] 2.1 增加持久化的 Mask Gesture、Mode 和 Radius Scene Property,默认使用 Lasso、Set、25 px,并删除旧 Lasso/Polyline Refine、Invert 和 Keep Original 属性
- [x] 2.2 实现统一 Alpha 合成函数,按 Set `A × M`、Add `max(A, M)`、Subtract `A × (1 - M)` 修改 Alpha并原样保留 RGB
- [x] 2.3 用硬边、分数边缘、连续重复编辑、Add 恢复透明区域和全透明结果测试固定三种 Alpha 公式
- [x] 2.4 实现单一 Mask WorkSpaceTool 的 Tool Settings,以 Lasso/Brush 切换 Gesture、以三个展开图标切换 Mode,并仅在 Brush 下显示 Radius
## 3. Lasso 与 Brush 手势
- [x] 3.1 将公共 Crop/Polyline modal 拆为无 Crop 术语的图片手势基础能力,让 Mask Lasso 与 Cutout Lasso 只复用路径采样和图片平面投影
- [x] 3.2 为 Mask Lasso 接入统一 SelectionMask 与 Alpha 合成入口,删除 Refine Job、Invert、紧裁切和空 Alpha 拒绝分支
- [x] 3.3 实现屏幕像素 Radius 的 Brush 圆环、单击圆形印记和连续拖动胶囊覆盖,并将图片外笔画裁到当前画布
- [x] 3.4 补充 Brush 单击、稀疏移动连续性、倾斜/缩放图片投影、画布边界、Radius 显示和设置冻结测试
- [x] 3.5 让 Lasso 与 Brush 创建同尺寸 packed Image 并直接替换 active Image Empty,测试对象身份、matrix、display size、offset、共享源图片和单步 Undo 保持正确
## 4. Rectify 与结果替换
- [x] 4.1 将 Perspective 文件、类、ID、标签、状态、warning、Undo 和测试统一改为 Rectify,同时保留四点 Quad、预览、宽高比、Homography 和输出限制
- [x] 4.2 删除 Rectify 的 Keep Original 设置与 Operator 参数,使结果始终由 active Image Empty 承载
- [x] 4.3 保留 Rectify 的可见 Alpha trim 与 placement 行为,并用既有像素、交互和对象对齐测试验证改名未改变结果
- [x] 4.4 删除不再有调用方的结果复制 helper 和测试,让 Mask、Rectify 直接复用公共图片替换入口
## 5. 删除 Refine Selection
- [x] 5.1 删除 Cutout Tool Settings、Pie 传值、Operator Property、AI readiness 和请求中的 Refine Selection,仅以 Depth 或 Normal 判定是否提交 Job
- [x] 5.2 简化 Cutout Blender 响应和 Server Job,使本地、Depth、Normal 分支使用原始 Selection bounds 输入并继续由本地 SelectionMask 约束最终 Shape
- [x] 5.3 删除 `refine-image-selection` 注册、专用 Job、Server selection 封装、SelectionMask sidecar 序列化和无调用方的清理代码
- [x] 5.4 更新 Server、Cutout、打包和模型测试,验证 Refine 参数与文件缺席,同时 Remove Background、BEN2 Debug、模型缓存和 BEN2 包内容保持可用
## 6. 文档与完整验证
- [x] 6.1 更新 README、用户索引、架构、公共模块、Operator 索引及 Image Edit、Cutout、Runtime、Output 文档,只正面描述 Frame、Mask、Rectify 当前实现
- [x] 6.2 搜索并清除图片编辑业务中遗留的 Crop Lasso、Crop Polyline、Crop Perspective、Refine Selection、Invert、Keep Original 名称与旧 ID
- [x] 6.3 运行 Selection、Blender Addon、Cutout、Server Runtime、Packaging 定向测试并修正失败
- [x] 6.4 运行 `uv run pytest` 完整验证,不构建文档、节点资产或发布产物