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:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-04
|
||||
51
openspec/changes/preserve-frame-bottom-layer-rgb/design.md
Normal file
51
openspec/changes/preserve-frame-bottom-layer-rgb/design.md
Normal file
@@ -0,0 +1,51 @@
|
||||
## Context
|
||||
|
||||
Frame 使用逐像素有效投影和稳定深度顺序。结果 RGB 需要保存前景到最底层原色的完整渐变,以便 Mask Add 恢复 Alpha 后仍能自然显示。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 最底层原始 RGB 打底,前景按自身 Alpha 混合。
|
||||
- Alpha 独立沿用现有 source-over 与数值判定。
|
||||
- 复用当前投影、排序、分块采样和 Packed Image 生命周期。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
交付范围为 Frame 的 RGB 合成及测试;Mask、Rectify 与输出 Canvas 延续既有规则。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 逐像素确定底层
|
||||
|
||||
复用 frame_source_coordinates 有效性和现有 order,第一个有效样本为底层。相同深度 active 在最上层,其余源沿用名称顺序。正交负深度、透视有效前方交点和局部覆盖按现有规则处理。
|
||||
|
||||
### 2. RGB 与 Alpha 独立累积
|
||||
|
||||
底层 RGB 使用未乘 Alpha 的双线性采样初始化。每个更近的前景使用预乘后插值的 P 与 A,执行 C = P + C × (1 - A)。底层自身不再叠加一次。
|
||||
|
||||
Alpha 从零开始,对包含底层的每一层执行 A_out = A_layer + A_out × (1 - A_layer)。最终 RGB 不除以该 Alpha。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Input[源 RGBA 与有效投影] --> Order[逐像素从远到近排序]
|
||||
Order --> Base[底层原始 RGB 初始化 C]
|
||||
Order --> Alpha[所有层按 source-over 累积 Alpha]
|
||||
Base --> Foreground[前景预乘样本叠加到 C]
|
||||
Foreground --> Result[独立的 RGB 与 Alpha]
|
||||
Alpha --> Result
|
||||
Result --> Pack[边缘扩色与 Packed Image]
|
||||
```
|
||||
|
||||
单源只有底层,保留其原始 RGB 采样。多源中前景的透明黑色邻居通过预乘采样排除;半透明前景持续混入底图白色,而不是等 Alpha 为零才切换颜色。
|
||||
|
||||
### 3. 源外扩色使用最终颜色
|
||||
|
||||
将合成颜色乘以最终 Alpha 交给现有 straight_rgba 处理 Alpha 数值判定与一圈扩色,再在全部有效覆盖位置写回合成颜色。这样源外扩色来自新的合成 RGB,源内透明色也得以保留。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [底层重复叠加导致变暗] → RGB 从第二层开始叠加,Alpha 从第一层开始。
|
||||
- [透明前景的黑背景污染插值] → 前景坚持预乘后插值,测试黑色透明邻居与白色底图。
|
||||
- [恢复 Alpha 前的半透明显示也会改变] → RGB 已融合底色,Alpha 仍单独控制可见性;这是本次确认的颜色语义。
|
||||
- [已合并图片无法找回分层信息] → 用户须用原始分层图片重新执行 Frame。
|
||||
28
openspec/changes/preserve-frame-bottom-layer-rgb/proposal.md
Normal file
28
openspec/changes/preserve-frame-bottom-layer-rgb/proposal.md
Normal file
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
Mask 保留了被剔除区域的 RGB,但后续 Frame 的预乘 Alpha 处理会丢失这些颜色,使用户再通过 Mask Add 恢复 Alpha 时无法找回图片内容。Frame 需要在透明结果中保留最底层图片的 RGB,同时保持前景渐变和既有 Alpha 合成。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 对每个输出像素,以现有逐像素深度顺序中最底层的有效图片样本作为隐藏 RGB 来源。
|
||||
- 以底层原始 RGB 打底,前景按自身 Alpha 连续混入底色,覆盖完全透明与半透明区域。
|
||||
- RGB 与 Alpha 分开合成:前景使用预乘采样叠加到底色,Alpha 独立沿用原有 source-over;恢复 Alpha 后边缘保持渐变。
|
||||
- 图片投射外的透明 Canvas 与现有边缘扩色继续保留;扩色仅填充没有有效源覆盖的透明区域。
|
||||
- 补充单图 Mask → Frame → Mask Add、多图透明叠放、半透明混合和有效投影边界测试。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `frame-transparent-rgb`: 定义 Frame 透明像素的底层 RGB 来源、保留规则及其与 Alpha 合成、边缘扩色的关系。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/anyimage/operators/image_edit_tool/frame.py`:保留原始颜色采样入口,复用当前深度排序和投影有效性,补全透明结果 RGB。
|
||||
- `src/anyimage/common/image.py`:复用公共双线性采样与 RGBA 转换能力,保持其他调用方的现有结果。
|
||||
- `tests/test_blender_addon.py`、`tests/test_cutout_objects.py`:像素计算及 Packed Image 编辑链路验证。
|
||||
- 仅涉及 Blender 本地像素处理与测试;实施后运行测试,不构建文档或扩展包。
|
||||
@@ -0,0 +1,91 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Frame selects the bottom valid source for hidden RGB
|
||||
|
||||
Frame SHALL 在每个输出像素复用当前有效投影和从远到近的稳定深度顺序,以第一个几何有效样本作为底层 RGB 来源。底层判定 SHALL 独立于 Alpha,并 SHALL 沿用相同深度时 active 位于最上层、其他对象按名称稳定排序的规则。
|
||||
|
||||
#### Scenario: Transparent foreground overlaps a transparent background
|
||||
- **WHEN** 蓝色底层和红色前景覆盖同一像素,且两者 Alpha 都为零
|
||||
- **THEN** 输出 RGB SHALL 为底层蓝色
|
||||
- **AND** 输出 Alpha SHALL 为零
|
||||
|
||||
#### Scenario: The farthest image does not cover a pixel
|
||||
- **WHEN** 最远图片在某像素没有有效投影,而其他图片具有有效投影
|
||||
- **THEN** 该像素的底层来源 SHALL 为实际覆盖它的最远有效图片
|
||||
|
||||
#### Scenario: Transparent images share the same depth
|
||||
- **WHEN** active 与另一透明图片在相同深度覆盖同一像素
|
||||
- **THEN** 底层 RGB SHALL 来自非 active 图片
|
||||
- **AND** 多个非 active 同深度图片 SHALL 沿用现有名称排序确定底层
|
||||
|
||||
#### Scenario: Source planes cross
|
||||
- **WHEN** 两张透明图片的平面相交,并在不同像素交换前后关系
|
||||
- **THEN** 各像素的隐藏 RGB SHALL 分别来自该像素处最底层的有效图片
|
||||
|
||||
#### Scenario: Projection validity depends on the view mode
|
||||
- **WHEN** 一个候选底层在透视视图中位于观察方向后方,或无法取得有限且位于源边界内的交点
|
||||
- **THEN** 该样本 SHALL 不作为底层来源
|
||||
- **AND** 正交视图中有限且位于源边界内的非正深度交点 SHALL 继续具有底层资格
|
||||
|
||||
### Requirement: Frame preserves bottom RGB in transparent output
|
||||
|
||||
Frame SHALL 在最终 straight RGBA 转换后 Alpha 为零且具有有效源覆盖的像素中,保存底层源未乘 Alpha 的 RGB 双线性采样结果。采样 SHALL 使用现有 Frame 投影坐标和边界约定;系统 SHALL 保持当前 Alpha 可见性数值判定与最终 Alpha 结果。
|
||||
|
||||
#### Scenario: A masked single image is framed without resampling displacement
|
||||
- **WHEN** 单张图片经 Mask 剔除 Alpha 后,以原尺寸和像素中心对齐的映射执行 Frame
|
||||
- **THEN** 透明区域的 RGB SHALL 与源图片对应像素一致
|
||||
- **AND** Alpha SHALL 保持剔除后的结果
|
||||
|
||||
#### Scenario: Transparent source colors require interpolation
|
||||
- **WHEN** Frame 缩放或透视映射在底层完全透明区域取得非整数像素采样位置
|
||||
- **THEN** RGB SHALL 为底层原始 RGB 的双线性插值结果
|
||||
- **AND** Alpha SHALL 保持零
|
||||
|
||||
#### Scenario: Mask Add follows a packed Frame result
|
||||
- **WHEN** 用户对 Mask 剔除后经 Frame 生成的单图 Packed Image 执行 Mask Add
|
||||
- **THEN** 提升 Alpha 的区域 SHALL 显示 Frame 保留的底层颜色
|
||||
- **AND** 该颜色 SHALL 在 Packed Image 像素中实际存在
|
||||
|
||||
### Requirement: Frame blends foreground RGB over bottom color with independent alpha
|
||||
|
||||
Frame SHALL 以最底层有效源的原始 RGB 双线性样本初始化颜色,随后从远到近对每个前景计算 C = P_front + C × (1 - A_front),其中 P_front 为预乘后插值的 RGB。Alpha SHALL 从零开始对包括底层在内的全部有效层独立执行 source-over,并沿用现有数值判定。最终 RGB SHALL 不除以合成 Alpha。
|
||||
|
||||
#### Scenario: Both layers are partially transparent
|
||||
- **WHEN** 蓝色底层 Alpha 为 0.5,红色前景 Alpha 为 0.25
|
||||
- **THEN** 输出 Alpha SHALL 为 0.625
|
||||
- **AND** straight RGB SHALL 为红色 0.25、绿色 0、蓝色 0.75
|
||||
|
||||
#### Scenario: A visible foreground overlaps a transparent bottom layer
|
||||
- **WHEN** 蓝色底层 Alpha 为零,红色前景 Alpha 为 0.25
|
||||
- **THEN** 输出 SHALL 为 RGB (0.25, 0, 0.75) 和 Alpha 0.25
|
||||
- **AND** 恢复 Alpha 后 SHALL 保留相同 RGB
|
||||
|
||||
#### Scenario: Hidden neighbor colors meet a visible sampling edge
|
||||
- **WHEN** 前景插值邻域同时含有可见红色像素和完全透明绿色像素
|
||||
- **THEN** 前景采样 SHALL 沿用预乘插值结果并按采样 Alpha 混入底色
|
||||
- **AND** 隐藏绿色 SHALL 不污染可见红色
|
||||
|
||||
#### Scenario: Restoring alpha reveals a continuous transition into white
|
||||
|
||||
- **WHEN** 前景原图的黑色背景被 Alpha 隐藏,前景边缘 Alpha 逐渐下降,且底层原始 RGB 为白色、Alpha 为零
|
||||
- **THEN** RGB SHALL 随前景 Alpha 连续过渡到白色
|
||||
- **AND** Packed Image 经 Mask Add 恢复 Alpha 后 SHALL 保留该过渡
|
||||
|
||||
### Requirement: Source coverage takes priority over edge color extension
|
||||
|
||||
Frame SHALL 保持完整 Canvas 和源外透明边缘扩色。存在有效源覆盖且最终透明的像素 SHALL 使用底层 RGB;没有有效源覆盖的像素 SHALL 保持 Alpha 零,并沿用紧邻可见内容的一圈 RGB 扩色,其余区域保持透明黑色。
|
||||
|
||||
#### Scenario: Transparent source pixels border visible content
|
||||
- **WHEN** 源内部透明像素紧邻可见像素,且隐藏 RGB 与相邻可见颜色不同
|
||||
- **THEN** 源内部透明像素 SHALL 保留底层原始 RGB
|
||||
- **AND** 该 RGB SHALL 不被边缘扩色覆盖
|
||||
|
||||
#### Scenario: The frame extends beyond all source images
|
||||
- **WHEN** Frame 包含所有源投影之外的区域
|
||||
- **THEN** 外部像素 Alpha SHALL 为零
|
||||
- **AND** 一圈扩色之外的无覆盖像素 SHALL 为透明黑色
|
||||
- **AND** Canvas 尺寸 SHALL 保持原有 Frame 规则
|
||||
|
||||
#### Scenario: A visible edge crosses a processing chunk boundary
|
||||
- **WHEN** 可见边缘跨越 Frame 行块边界
|
||||
- **THEN** 透明边缘颜色 SHALL 与使用同一规则处理完整画布的结果一致
|
||||
16
openspec/changes/preserve-frame-bottom-layer-rgb/tasks.md
Normal file
16
openspec/changes/preserve-frame-bottom-layer-rgb/tasks.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## 1. RGB 合成修正
|
||||
|
||||
- [x] 1.1 复核有效投影、最底层选择和公共采样入口,保留已有几何行为。
|
||||
- [x] 1.2 以底层原始 RGB 初始化,前景逐层按 Alpha 叠加;Alpha 独立沿用原公式,源外扩色使用最终颜色。
|
||||
|
||||
## 2. 验证
|
||||
|
||||
- [x] 2.1 更新半透明预期,补充三层合成、白色底图上的前景渐变、预乘插值和单源原始 RGB 测试。
|
||||
- [x] 2.2 验证实际 Packed Image 在 Mask Add 后保留前景到白色的连续渐变。
|
||||
- [x] 2.3 运行定向与全量测试,校验 OpenSpec;不改产品文档,不构建产物。
|
||||
|
||||
## 验证结果
|
||||
|
||||
- 定向测试:51 passed;全量 uv run pytest:440 passed。
|
||||
- 实际 Packed PNG 与 Mask Add 结果均验证了前景到白色的连续 RGB 渐变,Alpha 合成保持独立。
|
||||
- 未做 Blender 视口人工验收,未构建产物。
|
||||
Reference in New Issue
Block a user