Add specifications for two-times upscale and unified depth plane features

- Introduced the two-times upscale specification detailing requirements for unified output, fixed model presets, arbitrary dimensions, and consistent job behavior.
- Added scenarios for existing models, repeated upscale, and handling of alpha preservation.
- Created the unified depth plane specification outlining geometry modes, stable interface, and asset evaluation.
- Defined scenarios for mode switching, relief and camera preservation, and validation of delivered assets.
This commit is contained in:
2026-09-17 17:19:05 +08:00
parent 2811c7bf3a
commit 076a8c90f1
524 changed files with 6514 additions and 3 deletions

View File

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

View File

@@ -0,0 +1,99 @@
## Context
用户已确认 Empty 的 Alpha Blending 有效,并要求继续采用 Premultiplied。当前 `create_image_edit_result()` 设置 STRAIGHT,Job Color 加载设置 CHANNEL_PACKED,Plane 复制和 Cutout 裁剪图片继承源模式。材质 Color 与 Alpha 已分别连接,材质使用 DITHERED。
本次采用 Blender `PREMUL` 解释模式作为用户指定的默认策略。它涉及读写和采样解释,并非纯粹的显示开关;业务像素依然需要保留独立 RGB,包括 Alpha 为零时的颜色。
## Goals / Non-Goals
**Goals:**
- AnyImage 新建及编辑结果 Empty 开启连续 Alpha 混合。
- 编辑结果与 Mesh Color 图片统一默认 PREMUL。
- 保留 Mask Add 的颜色恢复、Frame 底层 RGB 和现有 Alpha 计算。
- 保持共享源保护、撤销和失败恢复语义。
- Frame 投射边界同时平滑 RGB 与 Alpha,保持内部细节及既有输出尺寸。
**Non-Goals:**
交付范围为图片呈现默认值、必要的数据读写保护及 Frame 像素边界采样。Depth、Normal、节点资产、Mesh 几何和 Server 算法沿用既有约定;普通用户图片在未参与操作时保持原设置。
## Decisions
### 1. 先验证模式切换,再调整公共入口
先用真实 Blender 构造包含非零隐藏 RGB 和 Alpha 0、0.25、0.5、1 的小图片,对 GENERATED byte buffer、浮点源及加载的 RGBA PNG 验证:模式切换前后读取、Pack、PNG 字节、临时 blend 保存重开和再次编辑。
PREMUL 由用户明确要求,因此本次不是把最终持久化 RGB 数值统一乘 Alpha。业务编辑与导出仍需得到和现有算法一致的 RGB/Alpha。具体设置顺序由真实 Blender 测试决定;如需在入口短暂切换解释模式以保留像素,须保证恢复最终 PREMUL,且仅发生在当前操作拥有的结果 Image 上。
用户自行验证后明确要求直接落地 PREMUL。模式设置在操作拥有的 Color 结果上,不增加双图片存储。业务读取 PREMUL 图片时临时使用 STRAIGHT 解释取得原始 RGB,并立即恢复最终 PREMUL;这只改变读取解释,不修改 Image 像素。完整浮点重载、序列逐帧和实际视口验证单独列为未完成验收。
### 2. Empty 混合在成功提交时设置
`replace_empty_image()` 成功提交结果时开启 `use_empty_image_alpha`;Frame 自定义恢复分支同步检查该设置。原 Image、混合开关和显示范围均纳入失败恢复。取消手势不修改源对象。
剪贴板创建 Image Empty 时直接开启同一开关。`color[3]`、显示深度、显示方向和 image_user 设置继续保留各自业务含义。
### 3. 图片模式在 Color 产物边界统一
```mermaid
flowchart TD
Local[Mask / Frame / Rectify] --> Create[公共编辑结果创建]
Jobs[Remove Background / Upscale 等图片编辑 Job] --> Load[公共图片结果加载]
Create --> Mode[结果 Image 默认 PREMUL]
Load --> Mode
Mode --> Replace[替换 Empty 并开启 Alpha Blending]
Plane[Plane 复制 / Cutout 裁剪 / AI Color] --> Material[公共材质 Color 入口]
Material --> Color[Color Image 默认 PREMUL]
Clipboard[新建 Image Empty] --> Blend[开启 Alpha Blending]
```
公共 Color 创建、加载和材质配置复用 `common/image.py` 中职责明确的模式设置入口。避免在每个 Operator 重复赋值,或继续保留 STRAIGHT/CHANNEL_PACKED 的竞争性最终设置。只读像素计算仍可在临时数组中预乘;Rectify 保留原采样,Frame 按下节扩展几何边界采样,各样本沿用已确认的图层合成公式。
普通图片通过共享编辑 Job 加载入口落地时也采用 PREMUL,包括 Upscale;视频和序列沿用现有时间映射并验证多个帧的读取。剪贴板仅创建 Reference 时,本次只保证对象 Alpha Blending,图片模式在后续编辑或 Mesh Color 入口配置。
### 4. Mesh 配置的是 Image,而非节点独立模式
`create_image_material()` 为 Color 图片统一设置 PREMUL。Plane 使用既有复制图片,Cutout 使用既有裁剪结果,AI 分支使用新加载的 Color Image;不得通过修改原共享源图片间接影响未参与对象。若某个入口直接复用源图片,先沿用创建独立 Color 产物的规则再配置。
Normal 和 Depth 继续保留独立通道及 Non-Color 语义。保留 Color/Alpha 连线、Linear 插值和 DITHERED;材质构建后的颜色空间设置也纳入像素保留测试。
### 5. Frame 边界使用 4×4 子像素覆盖采样
输出像素覆盖的屏幕区域与源投射边界或深度层级交界相交时,在像素内使用均匀 4×4 网格的中心点采样。边界判定基于像素覆盖区域与几何边界,不能仅以像素中心命中作为条件;透视跨观察平面的情况复用现有有效求交规则。覆盖和顺序稳定的内部像素保持原中心双线性采样,以保留对齐图片的内部细节。
每个子样本独立计算有效覆盖和深度排序,再执行当前底层 RGB 打底、前景按 Alpha 叠加及独立 Alpha 合成。完全没有源覆盖的样本 Alpha 为零;最终 Alpha 为 16 个子样本 Alpha 的算术平均。RGB 为有几何覆盖样本的合成 RGB 等权平均,权重与源 Alpha 无关。没有任何覆盖时保持透明黑色,避免 Mask Add 显露不存在的源外颜色。
```mermaid
flowchart TD
Pixel[输出像素覆盖区域] --> Boundary{覆盖或层级边界}
Boundary -- 否 --> Center[原像素中心采样]
Boundary -- 是 --> Grid[4×4 子像素网格]
Grid --> Layers[逐样本有效求交与深度排序]
Layers --> Composite[逐样本底层 RGB 与前景合成]
Composite --> Resolve[Alpha 按全部样本平均<br/>RGB 按有覆盖样本平均]
Center --> Output[完整 RGBA Canvas]
Resolve --> Output
Output --> Edge[清除完全无覆盖像素并交付结果]
```
两图重叠边界处,每个样本都有底图时,RGB 自然按前图几何覆盖比例混入底图;恢复 Alpha 后仍保留该过渡。单图外缘的无源样本只降低覆盖 Alpha,不以黑色压暗有效 RGB。源投射之外更远处没有可恢复内容,继续保持透明黑色。
按现有行块处理,可分批累积子样本,避免分配宽高各放大四倍的整图缓冲。4×4 是内部固定采样策略,不增加用户选项;评估耗时和峰值内存,边界优化必须与参考逐样本结果一致。
## Risks / Trade-offs
- [PREMUL 解释与当前像素数值约定不同] → 先验证实际 Blender byte/float、PNG 读写和重复编辑,记录差异而非只断言枚举值。
- [Alpha Blending 出现层间排序现象] → 验证 Empty 重叠与场景遮挡,保留对象深度策略,并记录已知显示限制。
- [改变 Image 模式影响共享使用者] → 配置操作拥有的结果或 Color 副本,测试未参与共享对象的模式和像素。
- [白色已混入 Frame RGB] → 保留用户确认的 RGB 合成规则;PREMUL 策略不承诺消除所有来源的白边。
- [边界判定漏掉细窄投射] → 覆盖测试包含中心未命中但子样本命中的倾斜条带及跨观察平面投射。
- [抗锯齿增加耗时或使内部变糊] → 只对几何边界启用多样本,按块累积,测试内部对齐细节与典型最大分辨率耗时/内存。
## Migration Plan
先通过真实 Blender 像素保留验证,再实现公共默认值与集成测试。现有场景不做加载时批量修改,后续编辑成功或生成 Mesh Color 时应用默认值。只运行测试与 OpenSpec 校验,不构建产物或同步修改产品文档。
## Open Questions
业务默认值已确定;PREMUL 与现有像素读写的匹配方式以第一组 Blender 验证作为实施门槛。

View File

@@ -0,0 +1,34 @@
## Why
用户已验证 Empty 开启 Alpha Blending 能改善渐变显示,并确认希望 AnyImage 处理后的图片及 Mesh Color 图片默认使用 Premultiplied。目前本地编辑、Job 结果与材质图片采用不同 Alpha 模式,需要统一这些交付入口并验证重复编辑和保存行为。
Frame 当前按像素中心判断图片投射覆盖,倾斜边缘存在阶梯;需要在 RGB 和 Alpha 中共同保留子像素覆盖信息,使 Mask Add 恢复后的图层边界也能平滑过渡。
## What Changes
- AnyImage 新建或成功替换图片的 Image Empty 默认开启 `use_empty_image_alpha`,保留原有对象不透明度和深度设置。
- Mask、Frame、Rectify 及 Remove Background 等公共图片编辑结果默认设置 `Image.alpha_mode = 'PREMUL'`。
- Plane、Depth Plane 和 Cutout 的 Color 图片默认使用 `PREMUL`;共享普通 Color 结果加载入口的图片采用同一约定。
- 将用户要求落实为 Blender Alpha 解释模式策略,保留既有 RGB/Alpha 像素编辑算法与透明 RGB;不额外永久执行 RGB 乘 Alpha。
- 验证 Mask Add、重复 Frame、实际 Packed Image、保存重载及 Mesh 材质采样,避免模式切换改变可恢复的颜色数据。
- Frame 几何边界采用 4×4 子像素覆盖采样,逐样本完成深度与图层合成,再汇总 RGB/Alpha;内部区域保留现有中心采样,输出尺寸不变。
## Capabilities
### New Capabilities
- `image-alpha-presentation`: AnyImage 图片与 Image Empty 的 Alpha 展示默认值、Color 材质图片策略及数据保留约束。
- `frame-edge-antialiasing`: Frame 投射轮廓与图层边界抗锯齿,包含恢复 Alpha 后的 RGB 过渡和源外颜色保护。
### Modified Capabilities
无。
## Impact
- `src/anyimage/common/image.py`:公共图片创建、加载、替换入口和像素保留验证。
- `src/anyimage/operators/image_edit_tool/frame.py`:边界子像素覆盖、逐样本排序合成及分块汇总。
- `src/anyimage/common/material.py`:Mesh Color 图片进入材质时的统一 Alpha 模式。
- `src/anyimage/operators/clipboard_image/actions.py`:新建 Image Empty 的 Alpha Blending。
- `src/anyimage/operators/convert_to_plane/object.py`、`src/anyimage/operators/cutout_tool/object.py`:复制或裁剪创建的 Color 图片。
- 相应 Blender 单元与集成测试。沿用现有依赖、节点资产和编辑算法;实施阶段不同步修改产品文档,不构建产物。

View File

@@ -0,0 +1,81 @@
## ADDED Requirements
### Requirement: Frame samples geometric boundary coverage
Frame SHALL 在输出像素与源投射轮廓或前后层级交界相交时使用固定 4×4 均匀子像素中心样本,分别判断有效覆盖及深度顺序。每个样本 SHALL 沿用现有底层 RGB 打底、前景预乘采样与独立 Alpha 合成公式。
#### Scenario: An opaque tilted edge covers half a pixel
- **WHEN** 单张不透明图片仅覆盖该像素的 8 个子样本,其余 8 个没有源覆盖
- **THEN** 输出 Alpha SHALL 为 0.5
- **AND** 该像素 SHALL 不因中心点位于边缘某一侧而变为全有或全无
#### Scenario: The center misses a narrow projection
- **WHEN** 像素中心未命中投射,但 4×4 网格存在有效命中
- **THEN** 有效子样本 SHALL 参与边界合成
#### Scenario: Depth order changes inside a pixel
- **WHEN** 两个源平面在像素覆盖区域内交换前后关系
- **THEN** 每个子样本 SHALL 使用自己的深度顺序
- **AND** 同深度 SHALL 沿用 active 置顶及现有稳定顺序
### Requirement: Frame resolves RGB and alpha with independent coverage weights
边界像素 Alpha SHALL 为全部 16 个子样本 Alpha 的算术平均,无源样本贡献零。RGB SHALL 为具有几何源覆盖的子样本合成 RGB 的等权平均,SHALL NOT 以源 Alpha 作为 RGB 平均权重。全无覆盖像素 SHALL 保持透明黑色,不向 Frame Canvas 外扩展 RGB。
#### Scenario: Mask Add reveals the canvas outside every source
- **WHEN** Frame 范围大于所有源投射,随后 Mask Add 将完全无覆盖区域的 Alpha 恢复为 1
- **THEN** 该区域 RGB SHALL 保持黑色
- **AND** 源边缘颜色 SHALL NOT 形成一像素外框
#### Scenario: A half-transparent foreground partially covers a transparent bottom image
- **WHEN** 透明蓝色底图覆盖全部 16 个样本,Alpha 为 0.5 的红色前图覆盖其中 8 个
- **THEN** 输出 RGB SHALL 为 (0.25, 0, 0.75)
- **AND** 输出 Alpha SHALL 为 0.25
#### Scenario: A white outer edge meets empty canvas
- **WHEN** 不透明白色源覆盖 8 个子样本,其余没有源覆盖
- **THEN** 输出 RGB SHALL 保持 (1, 1, 1),Alpha SHALL 为 0.5
- **AND** 无源区域的黑色 SHALL 不混入白色边缘
#### Scenario: Transparent layers meet along an oblique boundary
- **WHEN** 子样本合成结果 Alpha 均为零,但各自底层 RGB 不同
- **THEN** 输出 SHALL 保留这些有效 RGB 的覆盖平均
- **AND** Alpha SHALL 保持零
### Requirement: Restoring alpha preserves antialiased layer boundaries
Frame SHALL 将边界颜色过渡存入结果 RGB,使实际 Packed Image 经 Mask Add 后仍保留图层间的抗锯齿过渡。
#### Scenario: Mask Add reveals a tilted foreground over a background
- **WHEN** 倾斜图片与黑色或白色底图合成,经 Pack 后执行 Mask Add 恢复 Alpha
- **THEN** 边界 RGB SHALL 保留子像素覆盖混合的中间颜色
- **AND** Mask Add SHALL 不将边缘恢复为单点覆盖产生的两色跳变
### Requirement: Frame keeps interior detail and bounded output processing
Frame SHALL 对覆盖与层级稳定的内部像素保留现有中心双线性采样。结果 Canvas、输出尺寸限制和对象放置 SHALL 保持原约定;子样本处理 SHALL 按块执行并保持跨块结果一致。
#### Scenario: Pixel-aligned interior contains fine detail
- **WHEN** 原尺寸对齐的图片内部包含交替明暗像素,且该区域没有几何层级边界
- **THEN** 内部 RGB 与 Alpha SHALL 保持现有采样结果
#### Scenario: A boundary crosses processing chunks
- **WHEN** 相同斜边使用不同的行块尺寸执行 Frame
- **THEN** 输出 SHALL 在浮点误差范围内一致
- **AND** 边界 SHALL 不出现行块接缝
#### Scenario: Frame uses the maximum output resolution
- **WHEN** 输出达到配置的最大分辨率
- **THEN** 抗锯齿 SHALL 保持原输出尺寸
- **AND** 系统 SHALL 不为全部源分配完整 4 倍宽、4 倍高的输出副本

View File

@@ -0,0 +1,94 @@
## ADDED Requirements
### Requirement: Image Empty results enable alpha blending
AnyImage SHALL 为新建的 Reference Image Empty 及成功提交图片编辑结果的 Image Empty 设置 `use_empty_image_alpha = True`,并保留对象原有整体不透明度与深度设置。
#### Scenario: An alpha editing operation succeeds
- **WHEN** Mask、Frame、Rectify 或 Remove Background 成功替换 Image Empty 图片
- **THEN** 承载结果的 Empty SHALL 开启 Alpha Blending
- **AND** 对象 `color[3]` 与 `empty_image_depth` SHALL 保持原值
#### Scenario: A reference image is pasted
- **WHEN** AnyImage 新建剪贴板 Reference Image Empty
- **THEN** 对象 SHALL 默认开启 Alpha Blending
#### Scenario: Editing is cancelled or fails
- **WHEN** 用户取消手势或结果提交失败
- **THEN** 原 Image 和 Empty Alpha Blending 状态 SHALL 保持或恢复到操作前
### Requirement: Edited color images default to PREMUL
AnyImage SHALL 将本地图片编辑结果及公共图片编辑 Job 加载结果的最终 `Image.alpha_mode` 设置为 `PREMUL`。该默认值 SHALL 在成功交付后生效,SHALL NOT 依赖源图片原模式。
#### Scenario: Local alpha editing creates a packed result
- **WHEN** Mask、Frame 或 Rectify 从 STRAIGHT、CHANNEL_PACKED 或 PREMUL 源创建结果
- **THEN** 结果 Image SHALL 使用 PREMUL 并保留现有 Pack 规则
#### Scenario: A background removal result is loaded
- **WHEN** Remove Background 或共享图片编辑结果加载入口的 Upscale 返回 RGBA 图片
- **THEN** 加载结果 SHALL 使用 PREMUL
- **AND** 序列结果 SHALL 保留原有帧映射、播放范围及逐帧像素内容
### Requirement: Alpha presentation preserves editable pixel content
Alpha 呈现默认值 SHALL 保留既有业务编辑算法产出的 RGB 与 Alpha 数值,包括完全透明 RGB,并 SHALL 保证 Pack、保存重载和重复编辑后仍可读取与恢复这些内容。系统 SHALL NOT 为设置 PREMUL 而永久清除透明 RGB 或重复乘入 Alpha。
#### Scenario: Mask Add restores hidden color after reload
- **WHEN** 图片经过 Mask Subtract、结果 Pack、blend 保存重开,再执行 Mask Add
- **THEN** 恢复区域 SHALL 保留原隐藏 RGB,误差仅限既有编码精度
- **AND** 结果 SHALL 继续使用 PREMUL
#### Scenario: A PREMUL result is framed again
- **WHEN** PREMUL 结果以像素对齐映射再次执行 Frame
- **THEN** RGB 和 Alpha SHALL 符合 Frame 图层合成与边界覆盖采样规则
- **AND** 模式设置 SHALL 不导致额外 Alpha 相乘或颜色衰减
#### Scenario: Foreground alpha is restored over a white bottom image
- **WHEN** 当前 Frame 合成包含透明白色底图及 Alpha 渐变前景,随后恢复结果 Alpha
- **THEN** RGB SHALL 保留前景到白色的连续混合
- **AND** Alpha SHALL 沿用当前独立合成与编辑公式
#### Scenario: Packed pixels and exported PNG are inspected
- **WHEN** 检查 byte buffer 编辑结果、浮点源派生结果及加载的 PNG 结果
- **THEN** 操作前后、Pack 和导出重载后的业务 RGB/Alpha SHALL 在约定编码精度内一致
### Requirement: Mesh Color images use PREMUL independently of data textures
AnyImage 为 Plane、Depth Plane 及 Cutout 创建的材质 SHALL 将 Color Image 配置为 PREMUL,并保持独立 Color/Alpha 连线及当前材质透明策略。Depth 和 Normal Image SHALL 保留各自数据通道、Alpha 模式与颜色空间约定。
#### Scenario: A local mesh uses copied or cropped color
- **WHEN** Plane 使用源图片副本或 Cutout 使用裁剪 Color 图片构建材质
- **THEN** Color Image SHALL 为 PREMUL
- **AND** 构建过程 SHALL 保留既有 Color 像素及颜色空间策略
#### Scenario: AI geometry uses generated textures
- **WHEN** Depth Plane 或 Cutout 加载 AI Color、Depth 和 Normal 结果
- **THEN** Color Image SHALL 为 PREMUL
- **AND** Depth 与 Normal SHALL 保留原有数据解释方式
#### Scenario: Source images have other users
- **WHEN** 被编辑或转换的源 Image 同时被未参与的对象引用
- **THEN** 模式设置 SHALL 作用于结果 Image 或独立 Color 副本
- **AND** 未参与对象引用的源 Image 模式与像素 SHALL 保持不变
### Requirement: Defaults are applied at operation boundaries
AnyImage SHALL 在新建或成功交付范围内应用本策略,SHALL NOT 在注册、加载场景或普通重绘时批量修改已有图片。
#### Scenario: An existing scene is opened
- **WHEN** 用户打开含有已有 Image Empty 和材质的场景
- **THEN** 未参与 AnyImage 新操作的图片及对象设置 SHALL 保持原样

View File

@@ -0,0 +1,30 @@
## 1. Blender 数据保留验证
用户确认直接实施 PREMUL。byte GENERATED 与加载 PNG 的 Blender 4.5.3 重载解释差异保留在 `tests/test_image_premul_retention.py`;原始 Packed PNG 字节保持。公共业务像素入口在 PREMUL 下临时按 STRAIGHT 读取原始 RGB 并立即恢复模式,避免 Mask 将 Blender 展开的 RGB 写回。已验证 byte/float 独立 Color 产物与 Mask → Frame → Pack → reload → Mask Add;完整浮点保存重载和序列逐帧验收仍待完成。
- [x] 1.1 使用真实 Blender 为 byte/float 与 RGBA PNG 建立 PREMUL 模式切换、读取、Pack、导出和 blend 保存重开测试,包含隐藏 RGB 与连续 Alpha;记录适用的设置顺序。
- [x] 1.2 验证 PREMUL 结果再次 Mask Add、Frame 和序列逐帧读取仍符合原像素算法;业务读取须绕过 Blender 的 PREMUL RGB 展开并恢复最终模式。
## 2. 公共默认值
- [x] 2.1 在 common 中统一 Color Image 的 PREMUL 设置与必要的像素保留操作,接入本地编辑和公共图片 Job 结果入口。
- [x] 2.2 在成功替换和新建 Reference Image Empty 时开启 use_empty_image_alpha,补全失败恢复,保留不透明度、深度与时间映射。
- [x] 2.3 在材质 Color 入口及 Plane/Cutout Color 创建路径应用 PREMUL,保护共享源 Image,并保持 Depth/Normal 数据贴图原设置。
## 3. Frame 边缘抗锯齿
- [x] 3.1 增加覆盖及层级边界判定与 4×4 子像素参考采样,复用有效求交和逐样本排序;稳定内部继续中心采样。
- [x] 3.2 按全部子样本平均 Alpha,按有效覆盖样本平均 RGB,保留底层颜色规则并清除完全无覆盖的源外 RGB;采用分块累积。
- [x] 3.3 补充倾斜轮廓、中心漏采细条、交叉平面、透明图层、半覆盖数值、源外无黑边及内部细节测试。
- [x] 3.4 验证实际 Frame → Pack → Mask Add 的黑/白底图边缘 RGB,检查行块一致性并记录典型最大分辨率的耗时和峰值内存。
## 4. 集成与验收
- [x] 4.1 覆盖 Mask、Frame、Rectify、Remove Background 及共享加载入口的最终模式、取消/失败恢复和撤销边界测试。
- [x] 4.2 覆盖 Plane、Depth Plane、Cutout 的 Color Image、材质连线、颜色空间切换和共享源保护测试。
- [x] 4.3 使用黑/白背景与渐变边缘检查 Empty Alpha Blending、PREMUL 及 Mesh 显示,并记录多图排序或尚未完成的视口验收限制。
- [x] 4.4 运行定向测试、完整 uv run pytest 与 OpenSpec 严格校验;不修改产品文档,不构建节点、文档或扩展包。
最终自动测试:460 passed,56 subtests passed;OpenSpec strict 校验通过。当前 10/13 项完成,1.1、1.2 和 4.3 的剩余验收如上所述,不作为用户已确认的默认值落地阻塞。
验收记录:两层小纹理源、默认 2048×2048 输出约 7.916 秒,tracemalloc 峰值 359.2 MiB;8192×2048(16,777,216 像素上限)约 32.481 秒、1436.6 MiB。该数值为 Python/NumPy 跟踪内存,不是进程 RSS;未为源分配整图 4×4 放大副本。行块 1/3/256 结果一致。实际 Blender 视口、多图排序与完整序列验收尚未执行。