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:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-10
|
||||
@@ -0,0 +1,72 @@
|
||||
## Context
|
||||
|
||||
Remove Background 通过 ImageEditTarget 准备输入并提交结果。普通文件按路径校验,packed 图片导出 PNG;后端 BEN2 接收 Pillow RGB,预测 mask 后转换成 8-bit RGBA,Job 写入 PNG。源像素、结果载入和图片替换都涉及 HDR 精度边界。
|
||||
|
||||
BEN2 网络输出是 alpha,识别本身可复用既有模型。HDR 支持的关键是分离识别预览与原始 RGB,并让浮点信息贯穿主线程合成、EXR 编码和结果替换。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
本次交付为 HDR 静态图去背景:保留源图的场景线性色彩、有效动态范围和尺寸,以模型预测更新透明度。支持 Image Empty 和材质 Image Texture 两种已有入口,以及 Blender 可读取的外部、packed、generated 浮点静态图片。普通媒体沿用既有处理方式;HDR 动画在提交前明确提示首版支持范围。Upscale 的 HDR 适配作为独立能力评估。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 在 Blender 侧捕获源图和准备预览
|
||||
|
||||
以 `Image.is_float` 及真实图片来源判断 HDR 路径,扩展名仅用于来源识别与提示。所有可读浮点静态图片走该路径,包括数值暂时处于 0–1 内的图片。准备时读取当前像素,包含尚未保存的编辑;为本次任务保存独立的 RGBA 数值快照、尺寸、色彩空间和 alpha 解释信息。
|
||||
|
||||
Blender 数据仅在主线程准备与提交阶段访问,Job 参数只包含普通字典中的路径、字符串和数值。后端始终读取 PNG 识别预览,因此公共媒体格式校验仍校验实际提交文件。源图与临时文件的所有权由准备阶段和 Job cleanup 明确交接。
|
||||
|
||||
### 2. 用固定映射生成可识别的普通 RGB
|
||||
|
||||
识别预览从场景线性、非预乘 RGB 生成。Blender 的浮点缓冲已经采用场景线性颜色与预乘 alpha;预览先解关联,再采用按亮度的 Reinhard 映射,以非负亮度 Y 计算 `RGB / (1 + Y)`,执行线性 Rec.709 到 sRGB 编码并限制到可编码范围。负值只在预览中处理;源浮点数值保持原样。全黑图保持有限值。预览按源 alpha 合成到黑底,符合现有 BEN2 输入语义。
|
||||
|
||||
映射固定且不读取用户当前 View Transform、Look、Exposure,避免切换显示设置导致 mask 变化。源颜色解码由 Blender 完成。数据图片与 CHANNEL_PACKED 输入在准备时给出明确错误。
|
||||
|
||||
该映射是首版确定性基线。用实际高动态范围图片验证高光边缘、暗部与半透明物体;若识别不足,在本变更内调整并固定预览算法和对应行为测试。
|
||||
|
||||
### 3. 让 BEN2 推理核心返回数值 alpha
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[主线程捕获 HDR RGBA 快照] --> B[固定色调映射生成 PNG 预览]
|
||||
B --> C[BEN2 推理]
|
||||
C --> D[浮点 alpha 归一化并还原源尺寸]
|
||||
D --> E[Job 写入 alpha.npy]
|
||||
A --> F[主线程合成原始 RGB 与新 alpha]
|
||||
E --> F
|
||||
F --> G[写入 32-bit float RGBA EXR]
|
||||
G --> H[验证并提交到原图片目标]
|
||||
```
|
||||
|
||||
将现有推理核心重构为返回原尺寸 float32 alpha 数组。保留既有 mask 归一化语义,缩放过程使用浮点数据并在最后限制到 0–1,检查尺寸与有限值。普通 RGBA 消费者在自身产物边界完成量化与合成,清理旧核心入口并更新真实调用者。
|
||||
|
||||
Remove Background Job 增加明确的产物模式,例如 `output_kind: "alpha"`,HDR 静态图产出 `alpha.npy` 并通过结果字段声明。读取使用 `allow_pickle=False`,验证形状、范围和有限值。现有普通图片与动画消费者继续取得对应的 RGBA 结果。
|
||||
|
||||
### 4. 主线程合成和 EXR 持久化
|
||||
|
||||
在非预乘语义下计算 `A_out = A_source × A_predicted`,保留原始 RGB。Blender 原生浮点缓冲使用预乘 RGB,因此实现直接将 RGBA 乘以预测 alpha,保持解关联后的颜色;解码 STRAIGHT EXR 后同样采用该缓冲语义。零 alpha 像素的不可恢复颜色置零,NONE 模式按不透明源处理。测试分别比较 straight RGB 或重新合成后的可见颜色。
|
||||
|
||||
创建 `float_buffer=True` 的结果图片,使用独立临时场景指定 32-bit float RGBA EXR、ZIP 压缩,通过 `save_render` 写出场景线性值。结果采用 Linear Rec.709 与 PREMUL,保存后载入并核对数值,再打包和提交;临时场景及文件在对应阶段结束后释放。
|
||||
|
||||
复用 ImageEditTarget 和 common 内的提交、共享引用隔离、图片内容备份与恢复。HDR 的 Empty 提交同时保护材质节点等其他实际使用者。独占源图包含未保存像素或为生成图时,将其当前浮点内容转为内嵌 EXR 并建立撤销基线,保留 Image 身份;一次撤销恢复原始内容,重做恢复处理结果。提交失败时恢复操作前的源状态。先验证候选结果再提交,其他共享使用者保持原状。
|
||||
|
||||
### 5. 保持异步编辑事务完整
|
||||
|
||||
使用提交时的源快照进行合成,完成前复核原目标及图片绑定。纹理节点保存独立的 `anyimage_identity`,提交时与捕获值核对,避免 Blender 删除重建节点后复用 RNA 地址造成误认。增加对捕获后源像素或色彩解释被修改的冲突检查,发现冲突则提示重新执行并保留当前编辑。
|
||||
|
||||
临时预览由输入 cleanup 释放,alpha 由 Job 目录管理,候选 EXR 完成载入和打包后释放临时副本。取消、推理失败、合成失败、提交失败均释放快照与本次临时数据。成功结果支持一次撤销、重做以及保存重开。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 固定预览映射可能压低极暗部的可辨识度:用代表性 HDR 图片进行真实 BEN2 验收。
|
||||
- Blender 像素、alpha 模式与 EXR 编码之间存在转换:以已知数值的真实 Blender 保存重开测试约束实现。
|
||||
- 源快照增加内存占用:仅保留合成和冲突检测所需的数据,所有终止路径释放;大型输入准备失败时提供可读错误。
|
||||
- 工作区相关公共入口正在修改:实施前核对现有变更,复用最新公共校验和事务接口。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
先完成数值 alpha 核心与现有消费者回归,再实现 HDR 准备、合成和事务提交。验证普通媒体行为及 HDR round-trip 后完成能力交付。Job 协议由同版本插件与后端一起更新。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无需要用户决策的前置问题。Blender 像素空间、PREMUL round-trip 与固定预览的识别质量已通过数值测试及实际 BEN2 推理确认。
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
BEN2 通过普通 RGB 图像预测前景透明度。当前去背景流程用 Pillow 合成并保存 PNG,无法保留 HDR 图像的浮点颜色。将识别预览与原始颜色分离,可以让用户对 HDR 图片去背景,同时保留用于渲染的亮度和色彩。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 为 Remove Background 增加 HDR 静态图处理,覆盖 Blender 已加载的 EXR、Radiance HDR、已打包及生成的浮点图片。
|
||||
- 从提交时的浮点像素生成经过固定色调映射的 RGB 识别预览,原始颜色留在 Blender 侧。
|
||||
- BEN2 提供浮点 alpha 结果;主线程将其与源 alpha 相乘,并与原始颜色合成。
|
||||
- 结果使用 32-bit float RGBA EXR 持久化,支持保存重开、共享图片隔离、撤销和失败恢复。
|
||||
- 对 HDR 动画输入在准备阶段给出明确提示;普通图片与现有视频、序列沿用当前行为。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `hdr-background-removal`: HDR 静态图片的识别预览、alpha 预测、浮点结果及生命周期。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无已发布能力规格需要修改;与现有 ImageEditTarget 的目标提交语义衔接。
|
||||
|
||||
## Impact
|
||||
|
||||
- Blender:`common/image.py`、`common/image_target.py`、`operators/remove_background.py`;复杂的 HDR 图像处理可独立放入 `common/hdr_image.py`。
|
||||
- 后端:`server/models/onnx_ben2.py`、`server/models/ben2.py`、`server/jobs/remove_background.py`,以及对应测试。
|
||||
- 输入仍为普通预览 PNG,新增 Job 的 alpha 产物模式,复用 NumPy 保存数值结果。BEN2 权重与运行依赖保持现有配置。
|
||||
- 与进行中的 `unify-ai-input-validation` 及图片目标编辑变更共同使用实施时的真实调用链。
|
||||
@@ -0,0 +1,89 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: HDR still image preparation
|
||||
|
||||
系统 SHALL 为可读的浮点静态图片提供 Remove Background,覆盖 Image Empty 与材质 Image Texture 的外部、packed 和 generated 图片。输入 SHALL 采用任务开始时的当前像素快照。
|
||||
|
||||
#### Scenario: An HDR still is submitted
|
||||
- **WHEN** 用户对可读的 EXR、HDR 或其他浮点静态图片执行 Remove Background
|
||||
- **THEN** 系统捕获当前 RGBA、尺寸与颜色解释,并提交普通 RGB 识别预览
|
||||
|
||||
#### Scenario: Unsaved pixels are present
|
||||
- **WHEN** 源浮点图包含未保存的像素编辑
|
||||
- **THEN** 识别预览与最终 RGB 均使用当前编辑后的快照
|
||||
|
||||
#### Scenario: An HDR animation is submitted
|
||||
- **WHEN** 用户对 HDR 序列或浮点动画执行 Remove Background
|
||||
- **THEN** 系统在创建推理任务前提示本次 HDR 功能支持静态图,并保持源图状态
|
||||
|
||||
### Requirement: Deterministic recognition preview
|
||||
|
||||
系统 SHALL 通过确定性的曝光范围压缩及色彩编码生成 BEN2 的普通 RGB 预览,原始 RGB SHALL 独立保留。预览 SHALL 正确解释源 alpha,且不受用户显示变换设置影响。
|
||||
|
||||
#### Scenario: Highlights exceed one
|
||||
- **WHEN** 源图包含多个大于 1 的不同亮度值
|
||||
- **THEN** 预览先进行色调映射再编码,原始快照保留这些数值
|
||||
|
||||
#### Scenario: Display settings change
|
||||
- **WHEN** 相同源像素分别在不同 View Transform、Look 或 Exposure 下提交
|
||||
- **THEN** 识别预览一致
|
||||
|
||||
#### Scenario: The image is black or contains negative values
|
||||
- **WHEN** 可读源像素为全黑或包含有限负颜色值
|
||||
- **THEN** 预览数值有限,源 RGB 不因预览映射改变
|
||||
|
||||
### Requirement: Float alpha result
|
||||
|
||||
HDR 去背景 Job SHALL 返回原始尺寸的 float32 alpha 数组,数值有限且位于 0–1。系统 SHALL 在使用前验证产物。
|
||||
|
||||
#### Scenario: BEN2 predicts a soft edge
|
||||
- **WHEN** 模型预测连续透明度边缘
|
||||
- **THEN** alpha 经过浮点缩放返回,合成前不量化为 8-bit
|
||||
|
||||
#### Scenario: Alpha is invalid
|
||||
- **WHEN** alpha 文件损坏、尺寸不符或包含非有限及越界数值
|
||||
- **THEN** 系统报告结果错误,保持源图片与目标绑定
|
||||
|
||||
### Requirement: HDR color and transparency persistence
|
||||
|
||||
系统 SHALL 保留源 RGB 的场景线性色彩及动态范围,并以源 alpha 与预测 alpha 的乘积作为结果透明度。结果 SHALL 为同尺寸浮点图像并采用 32-bit float RGBA EXR 持久化,颜色误差 SHALL 限于浮点运算与必要颜色转换的容差。
|
||||
|
||||
#### Scenario: Straight alpha is present
|
||||
- **WHEN** 源图为 straight alpha 且存在大于 1 的 RGB 和部分透明像素
|
||||
- **THEN** 结果 RGB 保留,alpha 等于源 alpha 乘预测 alpha
|
||||
|
||||
#### Scenario: Premultiplied alpha is present
|
||||
- **WHEN** 源图采用 PREMUL 且包含部分透明及完全透明像素
|
||||
- **THEN** 系统按 alpha 关联语义合成,边缘颜色正确且结果没有 NaN 或 Inf
|
||||
|
||||
#### Scenario: The project is saved and reopened
|
||||
- **WHEN** 用户保存含处理结果的 blend 文件后重新打开
|
||||
- **THEN** 结果尺寸、HDR 数值、颜色解释和透明度保持一致
|
||||
|
||||
### Requirement: Transactional target update
|
||||
|
||||
系统 SHALL 复用既有图片目标提交规则,保护共享使用者并支持撤销、重做与错误恢复。Blender 数据访问 SHALL 发生于主线程,任务通信 SHALL 使用可序列化普通参数。
|
||||
|
||||
#### Scenario: Another owner shares the image
|
||||
- **WHEN** 源图片存在其他实际使用者
|
||||
- **THEN** 仅发起操作的目标取得独立 HDR 结果,其他使用者仍引用原图
|
||||
|
||||
#### Scenario: The source changes during inference
|
||||
- **WHEN** 原目标失效、改绑图片或源像素与颜色解释发生修改
|
||||
- **THEN** 系统拒绝覆盖当前状态,提示重新执行并清理本次临时资源
|
||||
|
||||
#### Scenario: The operation is cancelled or fails
|
||||
- **WHEN** 准备、推理、合成或提交取消或失败
|
||||
- **THEN** 系统保持或恢复源图及目标绑定,释放本次快照和临时数据
|
||||
|
||||
#### Scenario: The user undoes and redoes
|
||||
- **WHEN** 用户撤销并重做一次成功的 HDR 去背景
|
||||
- **THEN** 原始和处理后的浮点内容及绑定正确恢复,重做无需重新推理
|
||||
|
||||
### Requirement: Existing media behavior
|
||||
|
||||
系统 SHALL 保留普通图片、视频和普通序列的现有去背景行为,所有 BEN2 消费者 SHALL 适配统一的数值 alpha 核心。
|
||||
|
||||
#### Scenario: An existing regular image or animation is processed
|
||||
- **WHEN** 用户提交现有支持的普通图片或动画
|
||||
- **THEN** 系统按既有帧选择、透明度合成和输出规则完成操作
|
||||
@@ -0,0 +1,31 @@
|
||||
## 1. 验证浮点图片基础行为
|
||||
|
||||
- [x] 1.1 核对最新输入校验、BEN2 消费者与图片提交调用链,保留工作区已有修改。
|
||||
- [x] 1.2 在真实 Blender 中验证已知 HDR 数值、straight/PREMUL alpha、色彩空间及 32-bit EXR 保存载入行为,确定可用颜色转换与数值容差。
|
||||
- [x] 1.3 检查图片内容备份、原位替换、打包及撤销对 float buffer 的支持,定位需要修改的公共边界。
|
||||
|
||||
## 2. 分离 BEN2 alpha 核心
|
||||
|
||||
- [x] 2.1 将推理核心改为返回原尺寸 float32 alpha,保留归一化行为,使用浮点缩放并校验有限值。
|
||||
- [x] 2.2 更新所有真实 BEN2 消费者及相关测试,在各自输出边界合成 RGBA,移除旧核心入口。
|
||||
- [x] 2.3 为 Remove Background Job 增加静态 alpha 产物模式、结果字段及安全的 NumPy 读写校验,覆盖错误与取消路径。
|
||||
|
||||
## 3. 准备 HDR 识别输入
|
||||
|
||||
- [x] 3.1 在主线程识别浮点静态图并捕获当前 RGBA 快照,覆盖外部、packed、generated 和未保存像素。
|
||||
- [x] 3.2 实现固定色调映射及 sRGB 预览编码,覆盖 alpha、负值、全黑及显示设置独立性。
|
||||
- [x] 3.3 接入 Remove Background 的准备与普通参数提交,明确拒绝 HDR 动画并验证临时输入清理。
|
||||
|
||||
## 4. 合成与提交 HDR 结果
|
||||
|
||||
- [x] 4.1 校验 float alpha 并按 straight/PREMUL 语义合成,保留源 HDR RGB 和尺寸。
|
||||
- [x] 4.2 创建浮点结果并写入无损 32-bit RGBA EXR,完成载入、打包及临时文件释放。
|
||||
- [x] 4.3 复用并补全 ImageEditTarget 的浮点提交与恢复行为,覆盖共享隔离、独占 Image 身份、源修改冲突、撤销和重做。
|
||||
- [x] 4.4 验证取消、失败、目标失效各路径的资源释放和源状态恢复。
|
||||
|
||||
## 5. 验收
|
||||
|
||||
- [x] 5.1 更新并运行相关后端、common 和 Operator 测试,覆盖浮点 alpha 精度与现有普通媒体行为。
|
||||
- [x] 5.2 用已知 RGB 大于 1、有限负值、软 alpha 的图片进行真实 Blender 两种入口及保存重开验证;记录数值误差。
|
||||
- [x] 5.3 用代表性 HDR 实图运行 BEN2,检查高光、暗部、半透明边缘与黑底预览效果,必要时调整固定映射并补齐验证。
|
||||
- [x] 5.4 运行全量 `uv run pytest` 并确认正常退出,检查最终差异及旧入口引用,记录验证结果。
|
||||
@@ -0,0 +1,32 @@
|
||||
## 验证环境
|
||||
|
||||
2026-09-10,Windows,项目 uv 环境,Blender Python 4.5.3 LTS,BEN2 本地 `models/BEN2-ONNX/onnx/model_fp16.onnx`,ONNX Runtime CPU。
|
||||
|
||||
## 浮点与编辑事务
|
||||
|
||||
- 真实 Blender 验证外部 EXR、packed、generated、未保存像素,覆盖 Image Empty 与材质 Image Texture,以及共享和独占图片。
|
||||
- RGBA 样本包含大于 1 的颜色、有限负值、零 alpha 与部分透明像素;32-bit ZIP EXR 保存、打包、载入和 blend 保存重开均通过数值检查。
|
||||
- 单独验证 STRAIGHT EXR 解码后的预乘缓冲语义,处理后解关联颜色在 `rtol=1e-6` 内保持一致。
|
||||
- 已验证 Radiance `.hdr` 实际文件读取与识别预览生成,源浮点像素保持不变。
|
||||
- 原生 Operator 的撤销、重做、取消、提交异常和资源释放通过测试;未保存的独占浮点源图采用内嵌 EXR 撤销基线。
|
||||
- 纹理节点删除重建测试覆盖独立身份字段,防止 RNA 地址复用造成异步目标误认。
|
||||
|
||||
## 实际 BEN2 推理
|
||||
|
||||
Cycles 渲染 320×320 金属 Suzanne,16 samples,两盏面光源,原始 RGB 峰值约 112.432。用渲染 alpha 作为轮廓参考,构造三档曝光的灰底 HDR 图,以及保留原始软 alpha 的透明图。四组均执行真实 BEN2 Job、浮点 alpha 读回、Blender 合成及 EXR 持久化。
|
||||
|
||||
| 输入 | 原始 RGB 峰值 | 输出预乘 RGB 峰值 | 轮廓 IoU |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| 正常亮度 | 112.4321 | 112.4235 | 0.9822 |
|
||||
| 16 倍亮度 | 1798.9137 | 1796.3881 | 0.9874 |
|
||||
| 0.05 倍亮度 | 5.6216 | 5.6176 | 0.9757 |
|
||||
| 原始透明度 | 112.4321 | 112.4292 | 0.9656 |
|
||||
|
||||
IoU 使用预测 alpha 与渲染 alpha 的 0.5 阈值轮廓计算。四组 EXR 读回值相对于 `source_rgba × predicted_alpha` 的最大绝对误差均为 0。已目视检查高亮预览、低亮度预览与预测轮廓。该结果验证当前渲染样例的识别效果;不同实图的抠图质量取决于模型预测。
|
||||
|
||||
## 自动检查
|
||||
|
||||
- OpenSpec 严格校验通过。
|
||||
- BEN2 的旧 `infer` 入口及旧节点使用者检查名称已清理,相关调用者和测试已更新。
|
||||
- 最终 `uv run pytest -q`:1074 passed、2 xfailed、52 subtests passed,175.71 秒,退出码 0。两项 xfail 为已有的浮点图片显示色彩空间适配测试。
|
||||
- `git diff --check` 通过。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-12
|
||||
@@ -0,0 +1,49 @@
|
||||
## Context
|
||||
|
||||
Cutout 的 `SelectCutoutSelection` 已继承 `common.viewport.ImageGesture`,目前固定为 Lasso 并通过拖动键位启动。Mask 的 Polyline 已由该共用类实现。Cutout 完成选区后打开 Shape 饼菜单,Depth Solid 经异步生成后使用 `O Image Depth Cutout` 创建对象。
|
||||
|
||||
节点构建内部的 Uniform Thickness 输入在保存前显示名改为 Thickness;节点组同时存在 Balloon 使用的 Thickness 输入,初始化必须区分 socket 标识。
|
||||
|
||||
## Goals
|
||||
|
||||
支持 Cutout 逐点剪出及 Uniform 初始化;统一 Mask 的 Extend 命名和 Set 默认值。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 共用手势与独立设置
|
||||
|
||||
在 `properties.py` 定义 Cutout 的 Lasso / Polyline 属性,由 Scene 保存,默认 Lasso。Cutout 在启动时读取手势,沿用 `ImageGesture` 的逐点输入、实时预览、闭合提示和取消清理。使用共用按下启动键位支持首点输入,并验证 Lasso 拖动完成以及空白处源对象保持行为。共用逻辑留在 `common`,Cutout 负责自己的状态文本和选区提交。
|
||||
|
||||
### 创建时应用 Uniform
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[读取 Cutout Gesture] --> B[ImageGesture 收集并确认选区]
|
||||
B --> C[Shape 饼菜单携带本次 Gesture 快照]
|
||||
C --> D[CutoutSelectionToShape 保存 Gesture]
|
||||
D --> E[生成所需图像并在主线程创建对象]
|
||||
E --> F{Polyline 且 Depth Solid}
|
||||
F -->|是| G[设置新修改器 Mode 为 Uniform]
|
||||
F -->|否| H[按 Shape 初始化修改器]
|
||||
```
|
||||
|
||||
手势作为普通字符串从选择 Operator 显式传到饼菜单、转换 Operator 和对象创建链路,异步响应使用该快照,避免用户中途切换 Scene 设置影响结果。服务器生成协议继续处理深度和法线产物;修改器初始化由主线程完成。Uniform 自动设置针对本次新建的 Depth Solid 对象,之后用户可手动修改 Mode。
|
||||
|
||||
### 节点厚度默认值
|
||||
|
||||
将构建内部 Uniform Thickness 默认值设为 0.5,并验证最终资产对应 socket 的默认值和新修改器实际值。检查现有通用 Thickness 赋值只初始化其目标输入,避免同名 socket 覆盖 Uniform 默认值。按现有资产修订机制更新 Depth Solid 版本并运行 `uv run node-group build`。
|
||||
|
||||
### Mask 模式
|
||||
|
||||
统一为 `SET` / Set、`EXTEND` / Extend、`SUBTRACT` / Subtract,保留枚举数值 0、1、2。Scene 和 Operator 均通过共用属性默认 Set。Extend 保持 `max(alpha, mask)`,Set 保持 `alpha * mask`,Subtract 保持 `alpha * (1-mask)`;状态文本和测试同步命名。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 按下启动可能改变 Cutout 的源对象选择时机 → 覆盖 Lasso、Polyline、空白点击和取消行为。
|
||||
- 异步生成期间手势被切换 → 在提交时保存本次手势,并覆盖设置改变后的响应。
|
||||
- 同名 Thickness socket 容易导致写入错误 → 验证具体 socket identifier、资产默认值及实际修改器值。
|
||||
- 旧脚本传入 `ADD` 将失效 → 调用方统一使用 `EXTEND`,保持原枚举数值且不增加别名。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
实现代码与测试后重建节点资产,执行相关测试及全量测试。回退时将本次代码、测试和资产作为同一变更回退。
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
Cutout 目前只能自由绘制 Lasso,沿直线轮廓剪出时需要逐点定义边界。复用 Mask 的 Polyline 交互,并为这种剪出方式设置 Uniform 厚度,可减少手动调整;Mask 使用 Extend 和默认 Set,使名称与常用操作一致。
|
||||
|
||||
## What Changes
|
||||
|
||||
- Cutout 增加 Gesture 选项 Lasso / Polyline,默认 Lasso,复用共用逐点交互和选区提交链路。
|
||||
- Polyline 剪出创建使用 `O Image Depth Cutout` 的 Depth Solid 对象时,自动设置修改器 Mode 为 Uniform。
|
||||
- `O Image Depth Cutout` 的 Uniform Thickness 接口默认值改为 0.5(资产中显示为 Thickness)。
|
||||
- **BREAKING** Mask 的 `ADD` / Add 统一重命名为 `EXTEND` / Extend,保留扩展 Alpha 的运算语义;默认模式从 Subtract 改为 Set。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `cutout-polyline`: Cutout 手势选择、共用 Polyline 交互,以及创建结果的 Uniform 初始化。
|
||||
- `mask-mode-options`: Mask 的 Set / Extend / Subtract 名称、运算和默认值。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。当前 `openspec/specs` 尚无已归档的对应规范。
|
||||
|
||||
## Impact
|
||||
|
||||
- `properties.py`、`common/viewport.py`、`operators/cutout_tool`、`operators/mask_tool.py` 和相关注册、交互、对象创建测试。
|
||||
- `tools/nodes/groups/image_depth_cutout.py`、节点资产 `O_AnyImage.blend` 及节点测试;实现阶段需运行节点构建与验证。
|
||||
- 无新增依赖。已有 change 中的 Mask、Polyline 和 Cutout 提案作为实现背景,本 change 定义本次行为。
|
||||
@@ -0,0 +1,57 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Cutout offers persistent gesture selection
|
||||
|
||||
Cutout SHALL 提供 Lasso 和 Polyline 手势,默认 Lasso,并保存到 Scene 的独立 Cutout 设置。
|
||||
|
||||
#### Scenario: User selects Polyline
|
||||
- **WHEN** 用户将 Cutout Gesture 切换为 Polyline 并开始剪出
|
||||
- **THEN** 工具以首次左键按下的位置为起点,随后通过点击添加顶点
|
||||
|
||||
#### Scenario: User returns to Cutout
|
||||
- **WHEN** 用户选择 Polyline 后切换到其他工具再返回
|
||||
- **THEN** Cutout 继续使用 Polyline,Mask Gesture 保持自己的设置
|
||||
|
||||
#### Scenario: Default Lasso interaction
|
||||
- **WHEN** 新 Scene 中用户拖动 Cutout 并释放左键
|
||||
- **THEN** 工具以 Lasso 收集轮廓并在有效选区完成后打开 Shape 饼菜单
|
||||
|
||||
### Requirement: Cutout reuses shared Polyline interaction
|
||||
|
||||
Cutout MUST 复用 Mask 所用的共用 Polyline 手势,提供轮廓预览、起点闭合提示、左键添加顶点、Backspace 删除末点、点击起点或双击或 Enter 完成,以及 RMB、Esc 和切换工具取消。
|
||||
|
||||
#### Scenario: User completes a polygon
|
||||
- **WHEN** 至少三个顶点后,用户点击起点闭合范围内、双击或按 Enter / Numpad Enter
|
||||
- **THEN** 工具提交已确认的闭合 SelectionPath 并打开 Shape 饼菜单,闭合点击和双击第二击不添加重复顶点
|
||||
|
||||
#### Scenario: Too few points
|
||||
- **WHEN** 少于三个顶点时用户尝试完成
|
||||
- **THEN** 工具继续收集顶点,不打开 Shape 菜单
|
||||
|
||||
#### Scenario: User edits or cancels a polygon
|
||||
- **WHEN** 用户按 Backspace,或通过 RMB、Esc、切换工具取消
|
||||
- **THEN** Backspace 删除末点并至少保留起点;取消清理预览且不创建结果对象
|
||||
|
||||
#### Scenario: Empty click preserves the source
|
||||
- **WHEN** 用户在空白处启动 Cutout 手势
|
||||
- **THEN** 工具保持现有源对象选择规则,不因空白点击清空源对象
|
||||
|
||||
### Requirement: Polyline initializes Depth Solid with Uniform mode
|
||||
|
||||
通过 Polyline 创建 Depth Solid 时,系统 MUST 将新对象的 `O Image Depth Cutout` 修改器 Mode 初始化为 Uniform。手势 MUST 使用本次提交快照。
|
||||
|
||||
#### Scenario: Polyline depth result completes asynchronously
|
||||
- **WHEN** 用户提交 Polyline 并选择 Depth Solid,在生成结束前将 Cutout Gesture 改为 Lasso
|
||||
- **THEN** 本次结果的 Mode 仍为 Uniform,用户可在创建后手动调整
|
||||
|
||||
#### Scenario: Lasso or another shape is used
|
||||
- **WHEN** 用户通过 Lasso 创建 Depth Solid,或通过 Polyline 创建 Flat、Solid、Depth Balloon
|
||||
- **THEN** 对象沿用该 Shape 的现有 Mode 初始化规则
|
||||
|
||||
### Requirement: Uniform thickness defaults to half a unit
|
||||
|
||||
`O Image Depth Cutout` 的 Uniform Thickness socket MUST 默认 0.5,保存资产中对应显示为 Thickness 的 socket 和新建修改器 MUST 使用该值。
|
||||
|
||||
#### Scenario: Node asset is built and loaded
|
||||
- **WHEN** 完成节点资产构建并从资产加载节点组创建 Polyline Depth Solid
|
||||
- **THEN** Mode 为 Uniform,Uniform 厚度值为 0.5,通用 Thickness 初始化不会覆盖该值
|
||||
@@ -0,0 +1,25 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Mask exposes Set Extend and Subtract
|
||||
|
||||
Mask MUST 按顺序提供 `SET` / Set、`EXTEND` / Extend、`SUBTRACT` / Subtract,枚举数值分别为 0、1、2;界面和状态文本 MUST 使用 Extend。
|
||||
|
||||
#### Scenario: User extends Alpha
|
||||
- **WHEN** 用户以 Extend 提交值为 mask 的选区到现有 alpha
|
||||
- **THEN** 结果 Alpha 为 `max(alpha, mask)`,RGB 保持不变
|
||||
|
||||
#### Scenario: User uses Set or Subtract
|
||||
- **WHEN** 用户使用 Set 或 Subtract
|
||||
- **THEN** 结果 Alpha 分别为 `alpha * mask` 或 `alpha * (1-mask)`,RGB 保持不变
|
||||
|
||||
### Requirement: Mask defaults to Set
|
||||
|
||||
Scene 的 Mask Mode 和 Mask Operator 的模式属性 MUST 默认 Set,并通过同一属性定义保持一致。
|
||||
|
||||
#### Scenario: New scene and operator defaults
|
||||
- **WHEN** 创建新 Scene 或未指定模式的 Mask Operator
|
||||
- **THEN** 模式默认值为 `SET`
|
||||
|
||||
#### Scenario: User chooses another mode
|
||||
- **WHEN** 用户主动设置 Extend 或 Subtract 后启动 Mask
|
||||
- **THEN** 本次操作使用用户保存的模式
|
||||
@@ -0,0 +1,21 @@
|
||||
## 1. Cutout 手势
|
||||
|
||||
- [x] 1.1 在 properties.py 添加默认 Lasso 的 Cutout Gesture 属性并接入 Scene、Operator 和工具设置,检查注册与逆序注销测试。
|
||||
- [x] 1.2 将 Cutout 接入共用 Polyline 启动、预览与完成逻辑,更新状态文本;覆盖逐点、闭合、双击、Enter、Backspace、取消、Lasso 回归和空白处源对象保持行为。
|
||||
|
||||
## 2. Uniform 初始化与资产
|
||||
|
||||
- [x] 2.1 将本次 Gesture 快照从选区提交经 Shape 菜单传入转换 Operator 和对象创建链路;覆盖异步期间切换设置的行为。
|
||||
- [x] 2.2 为 Polyline 创建的 Depth Solid 设置 Mode 为 Uniform,验证 Lasso 与其他 Shape 的初始化行为。
|
||||
- [x] 2.3 将节点内部 Uniform Thickness 默认值改为 0.5,检查同名 Thickness socket 的赋值目标并更新资产修订号;补充资产默认值与实际修改器值测试。
|
||||
- [x] 2.4 运行 `uv run node-group build`,更新并验证 O_AnyImage.blend,确认源码与保存资产的 Capture Attribute 数量为零。
|
||||
|
||||
## 3. Mask 模式
|
||||
|
||||
- [x] 3.1 将 Mask 的 ADD / Add 统一改为 EXTEND / Extend,保持枚举数值与 Alpha 运算语义,将共用模式属性默认值改为 SET。
|
||||
- [x] 3.2 更新模式、状态文本、设置持久化和注册测试,验证三种模式的 Alpha 结果及 RGB 保持行为。
|
||||
|
||||
## 4. 验证
|
||||
|
||||
- [x] 4.1 运行相关测试及 `uv run pytest`,确认正常退出。
|
||||
- [x] 4.2 检查运行时代码和测试中的旧 Mask 模式引用及最终差异,确认源码、测试和节点资产一致。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-09
|
||||
@@ -0,0 +1,44 @@
|
||||
## Context
|
||||
|
||||
`build_material_layer_group()` 共用构建 O Image Layer 和 O Image Depth Layer。普通层直接传出输入 Alpha,深度层使用前景阈值。`create_image_material()` 已集中创建材质,并通过 `configured_material_view_adaptation()` 配置颜色图片。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
目标是将参考图的 Alpha 调整内置到 O Image Layer,支持连续强度调节,并在创建时读取现有偏好。变更范围为普通图像材质层;深度层继续使用其前景阈值协议。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 曲线与接口
|
||||
|
||||
O Image Layer 的默认折叠面板 `Options` 依次包含 `Alpha Fix`、`Normal Scale`、`Object Space`。Alpha Fix 类型为 NodeSocketFloat,subtype 为 FACTOR,范围 0–1,默认 0。直接连接 ShaderNodeFloatCurve 的 Factor,复用节点自身的混合语义:`output = (1 - factor) * alpha + factor * curve(alpha)`。
|
||||
|
||||
曲线使用四个点:起点 `(0, 0)`、低端控制点 `(0.8, 0)`、上方控制点 `(0.925, 0.925)` 和终点 `(1, 1)`。上方控制点为参考图估计值。采用自动钳制手柄、水平延伸及 0–1 的裁剪与采样区间。低端控制点之后快速上升,完整调整时输入 0.85 输出约 0.1593、输入 0.9 输出约 0.774;低端附近存在 Blender 曲线查表插值误差,测试容差为 0.0003。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[组输入 Alpha] --> C[Float Curve Value]
|
||||
B[组输入 Alpha Fix] --> D[Float Curve Factor]
|
||||
C --> E[曲线调整并按 Factor 混合]
|
||||
D --> E
|
||||
E --> F[组输出 Alpha]
|
||||
F --> G[Principled Alpha 或 Shadeless 混合系数]
|
||||
```
|
||||
|
||||
Alpha 曲线放在独立且就近的支线,保持曲线可见、默认节点标题和未使用输出隐藏;Color、Roughness 与 Normal 保持各自连接。
|
||||
|
||||
### 创建时应用偏好
|
||||
|
||||
`create_image_material()` 在普通层节点绑定后读取现有公共偏好函数,将节点实例的 `Alpha Fix` 设为 1 或 0。判断依据就是偏好值,因此开启偏好但颜色空间回退到 sRGB 时仍设为 1。手动拖入节点组时采用资产默认值 0。
|
||||
|
||||
用户随后可独立调整每个材质实例;偏好切换只影响后续创建。普通着色与 Shadeless 共用组输出 Alpha。Color 图片的颜色空间和 Alpha 模式继续由现有配置入口处理。
|
||||
|
||||
### 验证与交付
|
||||
|
||||
修改构建脚本、资产验证及材质测试;通过测试期间构建的节点组检查接口、连接与曲线采样。节点资产文档按节点组专项规范同步描述接口。用户已授权更新节点资产,O_AnyImage.blend 中的 O Image Layer 与运行时代码配套交付;在 Blender 节点编辑器确认实际尺寸、节点间距和连接,并运行全量测试及资产验证。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 截图未展示所有精确坐标与手柄设置 → 用上述初值实现,检查曲线采样,并对照实际节点编辑器及透明边缘效果验收。
|
||||
- 曲线会削弱中低 Alpha → 默认手动实例强度为 0,创建时按用户偏好启用,保留每个实例的强度控制。
|
||||
- 工作区已有材质与节点资产修改 → 在当前实现上追加变更,保留已有修改并检查最终差异。
|
||||
- 旧资产缺少输入 → 验证资产接口与运行时代码一致,正式发布配套资产。
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
使用 inverse 类颜色空间时,用户观察到 Alpha 无法正确遮盖图片边缘,手动添加 Float Curve 后可改善。将这项调整放入 O Image Layer,让材质创建时自动应用,并允许用户调节强度。
|
||||
|
||||
## What Changes
|
||||
|
||||
- O Image Layer 内新增 Alpha Float Curve,依据参考图设置曲线。
|
||||
- 默认折叠的 `Options` 面板依次包含 `Alpha Fix`、`Normal Scale`、`Object Space`;Alpha Fix 连接 Float Curve 的 Factor,范围 0–1,默认 0。
|
||||
- 创建使用 O Image Layer 的材质时,读取 `Adapt to Scene View Transform`:开启设为 1,关闭设为 0。
|
||||
- 验证曲线、接口、材质创建和着色输出行为。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `image-layer-alpha-adjustment`: 材质层 Alpha 曲线调整、强度输入与创建时偏好联动。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
- `tools/node_assets/material_layers.py`:节点组接口、曲线与布局。
|
||||
- `src/anyimage/common/material.py`:创建节点实例时设置调整强度。
|
||||
- `tools/node_assets/asset_validation.py` 及材质相关测试:接口、连接、曲线求值和偏好联动验证。
|
||||
- 发布节点资产 `src/anyimage/assets/O_AnyImage.blend` 需要包含新接口;本轮仅生成提案。
|
||||
@@ -0,0 +1,56 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Image layer exposes alpha adjustment strength
|
||||
|
||||
O Image Layer SHALL 在默认折叠的 `Options` 面板中依次提供 `Alpha Fix`、`Normal Scale`、`Object Space`。Alpha Fix SHALL 为浮点输入,范围 0–1,subtype 为 FACTOR,默认 0,并以该值控制内部 Alpha Float Curve 的 Factor。
|
||||
|
||||
#### Scenario: Options are initially collapsed
|
||||
- **WHEN** 创建 O Image Layer 节点实例
|
||||
- **THEN** Options 面板 SHALL 默认折叠,包含 Alpha Fix、Normal Scale、Object Space
|
||||
- **AND** Color、Alpha、Normal、Bump Scale SHALL 位于面板外
|
||||
|
||||
#### Scenario: Adjustment is disabled
|
||||
- **WHEN** Alpha Fix 为 0
|
||||
- **THEN** 组输出 Alpha SHALL 等于输入 Alpha
|
||||
|
||||
#### Scenario: Adjustment is fully applied
|
||||
- **WHEN** Alpha Fix 为 1
|
||||
- **THEN** 组输出 Alpha SHALL 等于内部 Float Curve 对输入 Alpha 的求值
|
||||
|
||||
#### Scenario: Adjustment is partially applied
|
||||
- **WHEN** Alpha Fix 为 0.5
|
||||
- **THEN** 输出 SHALL 等于原始 Alpha 与完整曲线结果的平均值
|
||||
|
||||
### Requirement: Alpha curve follows the reference profile
|
||||
|
||||
Alpha 曲线 SHALL 使用四个点:起点 `(0, 0)`、低端控制点 `(0.8, 0)`、靠近右上方的平滑控制点和终点 `(1, 1)`,形成参考图中的快速上升轮廓。对输入范围 0–1,完整调整后的输出 SHALL 保持在 0–1,保持单调不减,并保持完全透明与完全不透明端值。
|
||||
|
||||
#### Scenario: Low alpha is suppressed
|
||||
- **WHEN** 输入 Alpha 在 0–0.8 且调整强度为 1
|
||||
- **THEN** 输出 Alpha SHALL 为 0,允许浮点求值误差
|
||||
|
||||
#### Scenario: Opaque alpha is preserved
|
||||
- **WHEN** 输入 Alpha 为 1
|
||||
- **THEN** 任意调整强度下输出 Alpha SHALL 为 1
|
||||
|
||||
### Requirement: Material creation initializes adjustment from preferences
|
||||
|
||||
创建使用 O Image Layer 的材质时,系统 SHALL 通过现有偏好读取入口,将 Alpha Fix 初始化为 `Adapt to Scene View Transform` 开启时的 1、关闭时的 0。
|
||||
|
||||
#### Scenario: Adaptation is enabled
|
||||
- **WHEN** 开启适配后创建普通着色或 Shadeless 材质
|
||||
- **THEN** O Image Layer 实例的 Alpha Fix SHALL 为 1
|
||||
- **AND** 对应着色器 SHALL 使用该组调整后的 Alpha 输出
|
||||
|
||||
#### Scenario: Adaptation is disabled
|
||||
- **WHEN** 关闭适配后创建材质
|
||||
- **THEN** O Image Layer 实例的 Alpha Fix SHALL 为 0
|
||||
|
||||
#### Scenario: Color space falls back
|
||||
- **WHEN** 适配开启且颜色空间解析回退到 sRGB
|
||||
- **THEN** 新建 O Image Layer 实例的 Alpha Fix SHALL 仍为 1
|
||||
|
||||
#### Scenario: Existing material is manually adjusted
|
||||
- **WHEN** 用户修改现有材质的 Alpha Fix 后切换偏好
|
||||
- **THEN** 已有实例的调整值 SHALL 保留
|
||||
- **AND** 后续新建材质 SHALL 使用当前偏好初始化
|
||||
@@ -0,0 +1,18 @@
|
||||
## 1. Alpha 曲线与接口
|
||||
|
||||
- [x] 1.1 在 `material_layers.py` 的 O Image Layer 分支加入 Alpha Fix 输入及 Float Curve,按设计设置坐标、手柄、裁剪和连接。
|
||||
- [x] 1.2 调整节点布局及未使用输出隐藏,在实际 Blender 节点编辑器检查总览与曲线局部。
|
||||
- [x] 1.3 更新资产验证及对应测试,检查 socket 顺序、默认值、连接和曲线采样;覆盖强度 0、0.5、1、低 Alpha、端点、单调性与范围。
|
||||
|
||||
## 2. 材质创建联动
|
||||
|
||||
- [x] 2.1 在 `create_image_material()` 中复用偏好读取入口,为 O Image Layer 实例设置 Alpha Fix。
|
||||
- [x] 2.2 补充材质行为测试,覆盖偏好开关、sRGB 回退、普通着色、Shadeless 及现有材质手动值保留,验证 O Image Depth Layer 的既有 Alpha 行为。
|
||||
|
||||
## 3. 验收
|
||||
|
||||
- [x] 3.1 对照参考图验证曲线形状,使用透明边缘图片比较强度 0、0.5、1 的效果,记录估计控制点的最终取值。
|
||||
- [x] 3.2 按节点组专项规范同步 `docs/internals/node-assets.md` 的新输入与曲线说明。
|
||||
- [x] 3.3 按用户授权更新 O Image Layer 节点资产,运行全量测试并确认正常退出,检查最终差异和接口引用;验证 Blender 节点资产,不构建文档或扩展发布包。
|
||||
|
||||
验证结果:766 passed、2 xfailed、50 subtests passed,退出码 0;Blender 4.5.10 资产验证与 OpenSpec 严格校验通过。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-03
|
||||
@@ -0,0 +1,84 @@
|
||||
## Context
|
||||
|
||||
Mask 的 Lasso、Brush 与 Polyline 共用 `ImageGesture`。第一版 Brush 实时预览在 Modal 期间创建同尺寸 Generated Image,并以最多 30 FPS 更新 Pixel Buffer;4K 实测表明 `image.update()` 仍可能上传完整纹理,频繁更新会造成明显卡顿,红色填充 Overlay 也会随着 Flush 时机出现或消失。
|
||||
|
||||
因此 Brush 回到与 Lasso、Polyline 一致的事务边界:手势期间只保留简化后的屏幕轨迹并绘制几何 Overlay,不读取、创建或修改 Image;鼠标释放后才投影和栅格化完整笔画,创建一个最终 Image 并替换 active Image Empty。
|
||||
|
||||
Keep Original 与 Refine Selection 已无运行时代码,但测试中仍有旧字段缺席断言和一个多余参数。`SelectionPath.invert` 也已无生产调用方,却继续扩大 JSON 协议并让栅格器保留完整画布反选分支。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Brush 拖动期间显示与 Lasso、Polyline 相同的虚线和 Mode 填充,不更新 active Image Empty。
|
||||
- Overlay 与释放提交复用同一组 Brush Primitive 和 union 语义,保证预览范围就是最终 Selection。
|
||||
- 增加与原 Crop Polyline 手势一致、但使用当前 Mask 术语和 Alpha 主链的 Polyline。
|
||||
- 将 Mask 默认 Mode 改为 Subtract。
|
||||
- 删除 Keep Original、Refine Selection、Selection Invert 的剩余状态和历史测试噪音。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Lasso 与 Polyline 在路径闭合前只显示几何 Overlay,不实时修改 Image Alpha。
|
||||
- 不编辑共享源 Image 的 Pixel Buffer,不改变 Frame、Rectify、Cutout Shape 或 Server 行为。
|
||||
- 不在 Modal 拖动阶段创建预览 Image、读取源 RGBA 或执行图片级计算。
|
||||
- 不增加 Pillow、OpenCV 或 GPU Compute 依赖。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Brush 拖动阶段只维护屏幕轨迹
|
||||
|
||||
Brush 确认开始编辑后只保存屏幕坐标。鼠标移动使用与 Lasso 相同的最小距离和近共线简化,避免静止事件或直线移动积累冗余点。Modal 期间不调用 `image_rgba()`、`bpy.data.images.new()`、Pixel Buffer 写入、`image.update()` 或 `pack()`,active Image Empty 始终引用原 Image。
|
||||
|
||||
RMB、Esc、工具切换和异常只移除 Draw Handler 并结束 Operator,不需要临时 Image 恢复或数据块清理。这样拖动性能只与 Viewport Overlay 和简化后的屏幕轨迹有关,不随图片分辨率变化。
|
||||
|
||||
### 2. Overlay 与提交共享 Brush Primitive Union
|
||||
|
||||
简化后的中心轨迹生成一组稳定 Primitive:每个采样点对应一个圆,每对相邻点对应一个等宽连接条。Viewport 在 screen space 对这些 Primitive 做 union;鼠标释放后把同一组 Primitive 投影到图片,逐个栅格化并以逐像素 `max` 合入单个覆盖画布。转向、折返与交错都由圆和连接条的并集定义,不再从中心线偏移出一个可能改变 winding 的自交多边形,因此不会产生 miter 尖角或未填充三角。
|
||||
|
||||
Selection 完成后读取一次源 RGBA,并根据操作开始 Alpha `A0` 与 Mask `M` 合成:
|
||||
|
||||
```text
|
||||
Set A' = A0 × M
|
||||
Add A' = max(A0, M)
|
||||
Subtract A' = A0 × (1 - M)
|
||||
```
|
||||
|
||||
`apply_alpha_mask()` 使用同一纯 Alpha 合成函数;释放阶段只保留一个累计覆盖画布,不保存逐 Primitive Mask 列表。
|
||||
|
||||
不选择在每次刷新时重新栅格化完整轨迹,因为成本随轨迹长度持续增加;也不保存每个 Primitive 的独立 Mask,因为内存会随鼠标事件数增长。
|
||||
|
||||
### 3. Brush 使用与 Lasso、Polyline 相同的 Overlay
|
||||
|
||||
Viewport Overlay 在每个有效移动事件后,把全部 Brush Primitive 在局部屏幕 bounds 内以 2 px scanline bands 做区间 union,再由合并 bands 生成填充三角形,并从相邻 bands 的边界差异连接出闭合外轮廓。内部重叠边和 band 接缝不会进入虚线,急转与交错区域保持连续填充。该过程只处理 Viewport 坐标,不读取或栅格化源 Image。
|
||||
|
||||
Subtract 使用红色半透明填充,Set 与 Add 使用灰色半透明填充;虚线保持相同样式。Overlay 不随任何 Image 状态变化,并让用户在释放前看清即将作用的准确区域。
|
||||
|
||||
### 4. Polyline 是 Mask 的第三种 Gesture
|
||||
|
||||
`mask_gesture_property()` 增加 `POLYLINE`,单一 Mask WorkspaceTool 和 `EditImageAlpha` Operator 保持不变。`ImageGesture` 恢复无 Crop 术语的 Polyline 状态机:首次点击建立起点,后续左键提交顶点,鼠标移动预览下一条边;至少三个点后,点击起点 8 px 范围或按 Enter 完成,Backspace 删除最后一点,RMB/Esc 取消。
|
||||
|
||||
完成后把已提交顶点转换为一个 `SelectionPath`,与 Lasso 一样栅格化并进入当前 Mode 的 Alpha 合成。Radius 只属于 Brush;Polyline Tool Settings 只显示 Gesture 与 Mode。
|
||||
|
||||
### 5. SelectionPath 只保存 Points
|
||||
|
||||
删除 `SelectionPath.invert`、JSON 中的 `invert`、完整画布反选 bounds 和 `1 - mask` 分支。Cutout 的 Lasso 与双击整图都已经提供正向 Path,不需要迁移运行时状态。JSON 继续用于 Cutout Operator 间传值,但只包含 `points`。
|
||||
|
||||
删除重复验证 Keep Original、Refine Selection 和旧 Crop 字段缺席的历史断言;仍通过当前 Scene Property、Operator annotation、Job request 和打包文件的正向精确集合测试固定公开边界。测试夹具中的多余 `keep_original=False` 同步删除。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [高分辨率图片会让释放后的提交耗时可感知] → 将图片级投影、栅格化和结果创建集中到一次释放事件,避免拖动期间反复上传纹理。
|
||||
- [长而复杂的笔画会保留较多屏幕点] → 使用与 Lasso 相同的距离与近共线简化,只保留表达轨迹形状所需的点。
|
||||
- [Set 模式会让笔画外 Alpha 归零] → 保持既有 Set 公式;默认 Subtract 仍覆盖最常用的擦除行为。
|
||||
- [复杂笔画包含大量重叠 Primitive] → screen-space scanline 先合并每个 band 的区间,再生成一次填充和外轮廓;图片空间只在释放时使用单个累计覆盖画布。
|
||||
- [Polyline PRESS 事件与对象点选冲突] → 沿用 `resolve_image_edit_click()`,首次点击另一对象只切换 active,再次点击当前 Image 才开始手势。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
先完成已实现的 `replace-crop-with-mask-and-rectify` 作为基线。实现时先精简 SelectionPath,再加入 Polyline 状态机,随后实现 Brush 虚线 Overlay 与释放栅格化,最后删除失去调用方的 Helper 和历史测试。Scene 中未显式保存 Mode 的文件自动使用新的 Subtract 默认值;已经保存的 Mode 值保持不变。
|
||||
|
||||
回滚时可恢复本 change 前的 Mask 实现;它不迁移持久文件、Server 数据或 Blender Object 结构。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无。4K 实测决定拖动阶段不再更新 Image;后续实测进一步要求 Overlay 与提交共享圆角 footprint,并恢复 Mode 填充。
|
||||
@@ -0,0 +1,26 @@
|
||||
## Why
|
||||
|
||||
Mask Brush 的实时 Image 预览在 4K 图片上会频繁上传大纹理,造成明显卡顿;红色填充 Overlay 还会随 Image 刷新时机出现或消失。Brush 更适合像 Lasso、Polyline 一样在手势期间只显示稳定的几何反馈,释放后再提交 Alpha 修改。与此同时,已删除的 Keep Original、Refine Selection 和 Invert 仍有测试或通用数据分支残留,Brush 还会为整条轨迹积累大量临时 Mask,适合在扩展 Gesture 时一并收紧。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**:Mask Mode 默认值从 Set 改为 Subtract,使 Mask 默认表现为擦除 Alpha。
|
||||
- Brush 按住拖动期间不读取或修改 Image Pixel Buffer;Overlay 与释放提交复用同一组圆形印记、连接条和 union 规则,急转与交错区域融合且虚线只保留融合后的外轮廓。显示沿用 Lasso、Polyline 的 Mode 填充:Subtract 为红色,Set/Add 为灰色。
|
||||
- Mask Gesture 增加 Polyline,沿用逐点单击、移动预览、点击起点或 Enter 完成、Backspace 撤回一点的交互,并复用 Set、Add、Subtract Alpha 合成。
|
||||
- 删除无生产调用方的 `SelectionPath.invert`、全画布反选栅格分支及旧字段序列化,清理只验证 Keep Original、Refine Selection 缺席的历史测试和测试参数。
|
||||
- 删除由中心线偏移生成单一自交外框的方案,避免急转时 winding 产生三角孔洞;改为直接合并 Brush 圆和连接条。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `mask-editing`: 定义 Mask 的 Lasso、Brush、Polyline Gesture,Subtract 默认模式,Brush 虚线预览、释放提交与取消语义,以及无旧编辑状态的精简 Selection 数据边界。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
- 影响 `properties.py` 的 Mask 默认设置,以及 `operators/image_edit_tool/mask.py` 的释放提交与 Brush Selection 主链。
|
||||
- 影响 `common/viewport.py` 的 Gesture 状态机、Polyline 与 Brush Overlay,以及 `common/selection.py` 的 SelectionPath 表示。
|
||||
- 影响 Mask、Selection、Cutout、注册、交互与打包测试,并同步 Image Edit、Cutout 和 Common 内部文档。
|
||||
- 不增加第三方依赖,不改变 Frame、Rectify、Cutout Shape、Remove Background 或 Server Job 协议。
|
||||
@@ -0,0 +1,94 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Mask exposes three gestures with Subtract by default
|
||||
系统 SHALL 在单一 Mask Tool 中提供 Lasso、Brush 和 Polyline Gesture,并 SHALL 提供 Set、Add、Subtract Mode。新 Scene 的 Mask Mode MUST 默认为 Subtract;已有 Scene 中已保存的 Mode MUST 保持其值。Radius MUST 只在 Brush Gesture 下显示和生效。
|
||||
|
||||
#### Scenario: New scene uses Subtract
|
||||
- **WHEN** 新 Scene 尚未保存 Mask Mode
|
||||
- **THEN** Mask Tool Settings 与新启动的 Mask Operator 使用 Subtract
|
||||
|
||||
#### Scenario: Gesture settings remain unified
|
||||
- **WHEN** 用户在 Lasso、Brush 与 Polyline 之间切换
|
||||
- **THEN** 系统仍使用同一个 Mask Tool、Mode 设置和 Alpha 合成入口,并只在 Brush 下显示 Radius
|
||||
|
||||
### Requirement: Brush defers Image work until release
|
||||
Brush MUST 在手势完成前保持 active Image Empty 的 Image 不变。拖动期间 MUST NOT 读取源 RGBA、创建临时 Image、栅格化图片级 Mask、写入 Pixel Buffer、调用 `image.update()` 或 Pack;鼠标释放后 SHALL 一次性计算并提交结果。
|
||||
|
||||
#### Scenario: Dragging leaves the Image untouched
|
||||
- **WHEN** 用户按住左键移动 Brush 且手势尚未完成
|
||||
- **THEN** active Image Empty 继续显示并引用操作开始时的 Image,只通过 Viewport Overlay 呈现待提交范围
|
||||
|
||||
#### Scenario: Shared source remains unchanged during the gesture
|
||||
- **WHEN** active Image Empty 与另一个对象共享源 Image 并开始 Brush
|
||||
- **THEN** 两个对象都继续引用且显示原始 Image,直到 active Image Empty 在释放后接收新结果
|
||||
|
||||
#### Scenario: Stationary hold performs no Image work
|
||||
- **WHEN** 用户按住 Brush 且鼠标没有产生有效位移
|
||||
- **THEN** 系统不执行 Image 更新或图片级计算
|
||||
|
||||
### Requirement: Brush completion and cancellation are transactional
|
||||
Brush 完成时 MUST 创建并 Pack 一个最终结果 Image,并 SHALL 通过公共替换入口让 active Image Empty 承载结果。取消、工具切换或错误 MUST 保持源 Image 不变且不产生完成的编辑;成功完成 MUST 保持对象 identity、matrix、display size、offset、共享源引用和单步 Undo 语义。
|
||||
|
||||
#### Scenario: Completing a Brush persists one result
|
||||
- **WHEN** 用户释放左键完成 Brush
|
||||
- **THEN** 系统计算完整覆盖、创建并 Pack 一个同尺寸结果,active Image Empty 的画框和对象身份保持不变
|
||||
|
||||
#### Scenario: Cancelling restores the source
|
||||
- **WHEN** 用户在 Brush 期间按 RMB 或 Esc
|
||||
- **THEN** active Image Empty 仍引用操作前 Image,且没有临时 Image 或完成的 Undo 编辑
|
||||
|
||||
#### Scenario: Tool switch cleans the preview
|
||||
- **WHEN** Brush 运行期间 active WorkspaceTool 发生变化
|
||||
- **THEN** 系统只结束 Overlay 与 Modal,Image 保持不变
|
||||
|
||||
### Requirement: Brush uses one continuous primitive union
|
||||
Brush MUST 从简化后的屏幕轨迹生成圆形采样点和等宽连接条。Overlay 与释放后的 Selection MUST 使用同一组 Primitive 和 union 语义;急转、折返、自交与重叠区域 MUST 保持连续,MUST NOT 出现超出 Radius 的尖角或未填充三角。图片空间 union MUST 以逐像素最大值累计到单个覆盖画布,不得保存逐 Primitive Mask 列表。
|
||||
|
||||
#### Scenario: Sparse events leave no gap
|
||||
- **WHEN** 两个连续 Brush 采样点之间的距离大于 Radius
|
||||
- **THEN** 两点之间的胶囊完整覆盖连线且不出现空隙
|
||||
|
||||
#### Scenario: Turning does not create a horn or hole
|
||||
- **WHEN** Brush 中心轨迹发生方向变化
|
||||
- **THEN** 圆形采样点与连接条融合,Overlay 与最终 Selection 都不出现 miter 尖角或未填充三角
|
||||
|
||||
#### Scenario: Preview and result share the footprint
|
||||
- **WHEN** 用户释放 Brush
|
||||
- **THEN** 系统投影并栅格化拖动时显示的同一组 Brush Primitive,并使用相同的 union 规则
|
||||
|
||||
### Requirement: Brush matches the Lasso and Polyline overlay
|
||||
Brush SHALL 在每个有效鼠标事件后绘制整次未提交笔画的 footprint。Overlay MUST 复用 Lasso、Polyline 的虚线边界、non-zero winding 三角化和半透明填充;自交区域 MUST 融合为一个填充区域,虚线 MUST 只绘制融合后的外轮廓而不得保留内部交错边。Subtract MUST 使用红色填充,Set 与 Add MUST 使用灰色填充。
|
||||
|
||||
#### Scenario: Overlay persists until release
|
||||
- **WHEN** 用户继续拖动 Brush 且尚未释放左键
|
||||
- **THEN** Viewport Overlay 以当前 Mode 的填充和虚线边界显示从起点到当前点的准确 footprint,Image 本身保持不变
|
||||
|
||||
#### Scenario: Crossing strokes merge visually
|
||||
- **WHEN** Brush footprint 与自身交错或重叠
|
||||
- **THEN** 交错部分显示为连续的一整块填充,虚线只围绕融合区域的外边界
|
||||
|
||||
### Requirement: Polyline edits Alpha through the Mask pipeline
|
||||
Polyline SHALL 以逐点单击构建闭合 Selection:鼠标移动 MUST 预览下一条边;至少三个点后点击起点 8 px 范围或按 Enter MUST 完成;Backspace MUST 删除最后一个已提交点;RMB 或 Esc MUST 取消。完成的 Polyline MUST 栅格化为一个 SelectionMask,并 MUST 以当前 Set、Add 或 Subtract Mode 修改当前 Alpha,保持 RGB、图片尺寸和 Image Empty 画框不变。
|
||||
|
||||
#### Scenario: Clicking the start closes Polyline
|
||||
- **WHEN** 用户已提交至少三个点并在起点 8 px 范围内单击
|
||||
- **THEN** 系统闭合路径并应用当前 Mask Mode
|
||||
|
||||
#### Scenario: Enter confirms Polyline
|
||||
- **WHEN** 用户已提交至少三个点并按 Enter 或 Numpad Enter
|
||||
- **THEN** 系统闭合路径并应用当前 Mask Mode
|
||||
|
||||
#### Scenario: Backspace removes one point
|
||||
- **WHEN** Polyline 已有两个以上已提交点且用户按 Backspace
|
||||
- **THEN** 系统删除最后一点并继续预览未完成路径
|
||||
|
||||
### Requirement: Image selections carry only positive path geometry
|
||||
`SelectionPath` SHALL 只保存并序列化 Points。Mask、Cutout、Scene Property、Operator 参数和 Server Job 请求 MUST NOT 声明或传递 Invert、Keep Original、Refine Selection 或 Refine Model 状态;Selection 栅格化 SHALL 只建立带抗锯齿 Padding 的局部 Path Bounds。
|
||||
|
||||
#### Scenario: Cutout serializes a path
|
||||
- **WHEN** Cutout 把 Lasso SelectionPath 传给 Shape Operator
|
||||
- **THEN** JSON 只包含 `points`,恢复后得到相同的正向路径
|
||||
|
||||
#### Scenario: Public state contains current concepts
|
||||
- **WHEN** 系统枚举 Scene Property、Mask/Cutout Operator annotation 和 Cutout Job 请求
|
||||
- **THEN** 只出现当前 Gesture、Mode、Radius、Shape、Depth 与 Normal 业务字段
|
||||
@@ -0,0 +1,35 @@
|
||||
## 1. Mask 设置与 Selection 精简
|
||||
|
||||
- [x] 1.1 将 Mask Mode 默认值改为 Subtract,在 Gesture 中增加 Polyline,并更新 Scene Property、Operator 与 Tool Settings 测试
|
||||
- [x] 1.2 删除 `SelectionPath.invert`、JSON `invert` 字段、完整画布反选 bounds 和补集栅格化分支,使 Cutout 序列化只传递 points
|
||||
- [x] 1.3 清理测试中遗留的 Keep Original、Refine Selection 参数及重复历史缺席断言,以当前 Property、Operator、Job 和打包内容的精确集合固定公开边界
|
||||
- [x] 1.4 更新 Selection 与 Cutout 测试,验证局部抗锯齿 bounds、JSON 往返、整图 Cutout 和普通 Lasso 在精简后保持正确
|
||||
|
||||
## 2. Polyline Gesture
|
||||
|
||||
- [x] 2.1 在无 Crop 术语的 `ImageGesture` 中实现 Polyline 点提交、移动预览、8 px 起点闭合、Enter 完成、Backspace 回退和取消状态机
|
||||
- [x] 2.2 为 Polyline 绘制闭合预览、虚线轮廓和起点指示,并更新 Mask 状态文字与 Radius 显示条件
|
||||
- [x] 2.3 将完成的 Polyline 接入 Lasso 共用的 SelectionMask 与 Alpha 合成入口,验证 Set、Add、Subtract、画布保持和对象点选语义
|
||||
- [x] 2.4 补充 Polyline Modal、Overlay、闭合方式、无效路径、取消和工具切换测试
|
||||
|
||||
## 3. Brush Footprint 与 Alpha
|
||||
|
||||
- [x] 3.1 将 Alpha 公式整理为完整 Selection 共用的纯函数,固定 Set、Add、Subtract 结果
|
||||
- [x] 3.2 从简化后的屏幕轨迹生成圆形采样点和等宽连接条,并在 screen space 与图片空间分别执行同语义 union
|
||||
- [x] 3.3 删除逐 Primitive Mask 列表和失去调用方的 `merge_selection_masks()`,图片空间只保留一个累计覆盖画布
|
||||
- [x] 3.4 补充单击、稀疏移动、急转折返、自交、倾斜/缩放投影与图片边界测试
|
||||
|
||||
## 4. Brush 虚线预览与延迟提交
|
||||
|
||||
- [x] 4.1 删除临时 Image、dirty RGBA、30 FPS Flush 与实时 Pixel Buffer 更新,使 Brush 拖动阶段不读取或修改 Image
|
||||
- [x] 4.2 将 Brush Primitive 在 screen-space scanline bands 中融合,消除内部交错边、急转尖角和未填充三角,并复用 Lasso、Polyline 的填充配色
|
||||
- [x] 4.3 让 Overlay 与鼠标释放后的 Selection 栅格化复用同一组 Primitive 与 union 语义,再通过公共 Alpha 与 Image 替换入口一次性提交
|
||||
- [x] 4.4 取消、异常与工具切换只结束 Overlay,不创建临时 Image 或恢复分支,并保持对象 identity、transform、画框和单步 Undo
|
||||
- [x] 4.5 更新 Brush 急转折返、Primitive Union、融合外轮廓、Overlay 配色、释放提交和真实 Blender Image 生命周期测试
|
||||
|
||||
## 5. 文档与验证
|
||||
|
||||
- [x] 5.1 更新 README 与 Image Edit、Common、Cutout 内部文档,说明三种 Gesture、Subtract 默认值、Brush Mode 填充、Primitive Union、释放提交和只含 points 的 SelectionPath
|
||||
- [x] 5.2 搜索并清除运行时与文档中的 Keep Original、Refine Selection、Selection Invert、旧 Brush 全轨迹合并 Helper 和历史 Crop Polyline 名称
|
||||
- [x] 5.3 运行 Selection、Blender Addon、Cutout、Image Edit 与 Packaging 定向测试并修正失败
|
||||
- [x] 5.4 运行 `uv run pytest` 和 OpenSpec strict validation,不构建文档、节点资产或发布产物
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-10
|
||||
@@ -0,0 +1,63 @@
|
||||
## Context
|
||||
|
||||
项目现有几何推理使用 ONNX Runtime,Windows 使用 DirectML,其他平台使用默认 Runtime。MoGe 3 的稀疏细化已在项目外改写为标准 ONNX 索引、聚合和矩阵运算,完整三次细化已实际运行。
|
||||
|
||||
实现使用上游提交 `74fbce054ebed49800de42d0ad0e83495065719a`,骨干与细化分别导出为 ONNX 图。骨干支持动态输入和 token 数,细化支持动态点数。在 RTX 5070 Ti 上,1200–3600 tokens 的完整动态推理约需 4.86–17.11 秒,平均深度相对误差约 0.002%,二值 Mask 完全一致。参考比较关闭 FlexGEMM 独立的 TF32 开关。Windows DirectML 与 Linux CPU 均已通过真实 Job 验证,详细结果见 [验收记录](validation.md)。
|
||||
|
||||
实验脚本与日志位于项目外 `C:/Users/icrdr/AppData/Local/Temp/anyimage-moge3-trial/`。正式转换位于 `tools/models/moge3_export.py`,模型准备和运行使用项目已有的资产机制。
|
||||
|
||||
## Goals
|
||||
|
||||
以最小改动将完整 MoGe 3 接入 MoGe 2 的现有工程流程,沿用输入准备、参数、下载、会话管理和几何产物契约。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 动态尺寸与分辨率等级作为接入门槛
|
||||
|
||||
正式模型接收现有输入准备流程产生的图片尺寸及宽高比,沿用现有 resolution_level 到 token 数的控制方式,输出尺寸对应分析图片。256×192 / 1200 tokens 是原型测试配置,正式推理需通过动态形状及可变 token 数验证,同一组模型资产在任务间接受不同配置。
|
||||
|
||||
沿用已验证的两个图边界:骨干输出初始坐标、编码特征、法线、Mask 和尺度;细化图输出 log-depth 增量。MoGe 3 模块内部使用 NumPy 生成体素邻域并执行三轮调度,学习网络计算全部由 ONNX Runtime 执行,几何恢复复用现有后处理能力。逐层对比用于验证动态化后的数值一致性。
|
||||
|
||||
正式 Runtime 使用标准 ONNX 图和现有执行提供程序,完整执行三次细化。原型中主要网络计算由 DirectML 执行,部分索引与计数节点由 ONNX Runtime CPU 执行;正式验收继续记录实际执行路径。
|
||||
|
||||
### 最小工程改动
|
||||
|
||||
沿用现有图像准备、最大分析尺寸、分辨率等级、设备选择、Job 参数和产物。新模型特有的图集合、索引生成及迭代封装在 `models/onnx_moge3.py`;已有推理调用点只增加必要的模型分派。
|
||||
|
||||
复用现有模型下载与会话生命周期,通用编排名称仅在职责确实扩展时局部调整并一次更新真实调用者。Operator、Blender 响应和节点资产继续消费现有契约。正式运行依赖保持现有 ONNX 和后处理体系,离线转换依赖独立管理。
|
||||
|
||||
### 精度和性能验证
|
||||
|
||||
比较同权重、相同输入与分辨率等级的原始三次细化结果和 ONNX 结果。先用 FP32 验证语义,再评估 FP16。实验需在导出前声明验收容差,记录有效区域深度与点图误差、法线角误差、内参偏差、Mask 一致性及每次细化误差,不得根据结果事后放宽。
|
||||
|
||||
样本包含室内、细小物体、室外、不同宽高比及全景透视视图;覆盖低/高分辨率等级、自动 FOV 与固定 90 度 FOV。记录转换耗时、ONNX 文件大小、会话启动、热推理耗时及峰值内存/显存。Windows 验证 CPU 和 DirectML 的完整执行情况及实际算子回退;其他平台需分别验证,不沿用 PyTorch 的 CUDA 平台限制。
|
||||
|
||||
### 验证后的正式接入
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[已验证的双 ONNX 图原型] --> B[动态尺寸及分辨率等级验证]
|
||||
B --> C{精度 性能 提供程序通过}
|
||||
C -->|通过| D[生成并校验模型资产]
|
||||
D --> E[登记模型与下载]
|
||||
E --> F[统一 MoGe ONNX 推理入口]
|
||||
F --> G[深度平面 裁切 全景]
|
||||
C -->|未通过| H[记录阻碍与实验结果]
|
||||
```
|
||||
|
||||
转换工具放在 `tools/models`,依赖固定版本或上游提交;最终登记所有图文件及 external data 的大小和哈希,完整下载后标记就绪。镜像发布后才登记对应来源。生产推理只读取已准备的 ONNX 资产。
|
||||
|
||||
新增模型沿用现有几何模型枚举,保留已有模型键和默认值;仅调整识别新模型所必需的元数据与筛选。输出使用现有 CPU NumPy 数组和相机内参约定。全景传入 90 度 FOV,保持 Mask 与源有效性、Alpha 合成以及径向距离融合契约。
|
||||
|
||||
缓存按模型、设备和图集合识别,连续帧及视图复用会话,切换模型和关闭时释放资源。取消与失败沿用现有 Job 事务。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 动态体素分箱可能放大浮点数值差异 → 对相同 FP32 配置按预定容差比较。
|
||||
- 高分辨率等级增加计算量与内存 → 已记录 CPU/DirectML 性能及峰值占用,继续沿用现有分辨率等级控制。
|
||||
- 多图执行会增加 CPU/GPU 数据搬运 → 测量完整流水线耗时并据此决定图边界。
|
||||
- 提供程序算子覆盖不同 → Windows DirectML 与 Linux CPU 已验收,macOS 仍需实机验证。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
先基于已完成原型验证动态尺寸、分辨率等级、精度与性能,再完成转换工具和模型登记。沿用现有设置与默认模型,只增加新模型选择及必要分派。实施更新相关测试,检查依赖与打包内容一致性。
|
||||
@@ -0,0 +1,23 @@
|
||||
## Why
|
||||
|
||||
MoGe-3 ViT-L 的完整三次细化已在项目外通过 CPU ONNX Runtime 与 DirectML 原型验证。正式接入应沿用 MoGe 2 的工程流程,以最小改动增加模型选项,并完成动态输入和分辨率等级验证。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 将已跑通的双 ONNX 图原型扩展到现有输入尺寸处理、宽高比和分辨率等级,建立可复现导出与精度比较流程。
|
||||
- 验证通过后新增 `MOGE3_VITL` 模型选项、ONNX 资产下载和就绪检查,保留已有模型及默认值。
|
||||
- 深度平面、裁切与全景统一按模型分派,推理统一采用现有 ONNX Runtime,复用几何产物。
|
||||
- 两个 ONNX 图、体素邻域生成与三次细化封装在 MoGe 3 模型内部;复用现有 Operator、节点资产、Job 参数和用户操作流程。
|
||||
- 以完整细化、数值一致性、执行提供程序兼容性与可用性能作为正式接入门槛。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `moge3-model`: 完整 MoGe 3 的 ONNX 推理、模型选择、资产准备与几何工作流集成。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
改动集中在离线转换工具、模型登记、MoGe 3 ONNX 模块和必要的推理分派;模型枚举、下载与会话管理沿用既有机制。PyTorch、MoGe、FlexGEMM 与 Triton 仅用于隔离的离线转换及参考验证环境;正式 Server 沿用 ONNX Runtime 与现有后处理依赖。正式接入以动态尺寸、分辨率等级及完整流程验收通过为前提。
|
||||
@@ -0,0 +1,73 @@
|
||||
## Purpose
|
||||
|
||||
让用户通过现有 ONNX Runtime 体系使用完整的 MoGe-3 ViT-L 几何预测,使模型准备、选择和执行在深度平面、裁切与全景中具有一致行为,并通过完整细化及数值验证保证结果可信。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Execute complete MoGe 3 with ONNX
|
||||
|
||||
正式推理 SHALL 通过 ONNX Runtime 执行 MoGe 3 的学习网络计算,并完成三次细化;运行环境 SHALL 无需 PyTorch、FlexGEMM 或 Triton。
|
||||
|
||||
#### Scenario: Production inference
|
||||
- **WHEN** 在仅安装声明的 ONNX 运行依赖的 Server 中执行 MoGe 3
|
||||
- **THEN** 完成包含三次细化的预测,不依赖外部 Torch 环境或服务
|
||||
|
||||
### Requirement: Validate conversion before availability
|
||||
|
||||
系统 SHALL 在完整流程的动态尺寸、分辨率等级、精度、性能和执行提供程序验证通过后才将新模型登记为正式可用模型。
|
||||
|
||||
#### Scenario: Incomplete conversion
|
||||
- **WHEN** 只有关闭细化的模型可以运行,或完整模型未通过预定数值验收
|
||||
- **THEN** 保留验证结果且不将其作为完整 MoGe 3 发布
|
||||
|
||||
### Requirement: Select and prepare model
|
||||
|
||||
系统 SHALL 提供 MOGE3_VITL 选项,保留已有模型键和默认值,下载并校验推理所需的全部 ONNX 资产。
|
||||
|
||||
#### Scenario: Download success
|
||||
- **WHEN** 所需图文件及外部权重全部下载并通过完整性校验
|
||||
- **THEN** 标记模型就绪,深度平面、裁切与全景使用用户所选模型
|
||||
|
||||
#### Scenario: Interrupted download
|
||||
- **WHEN** 下载失败、取消或文件不完整
|
||||
- **THEN** 不标记就绪,并允许重新准备
|
||||
|
||||
### Requirement: Preserve geometry contract
|
||||
|
||||
MoGe 3 SHALL 按所选分辨率等级提供与分析图尺寸对应的深度、法线、有效性、点图和相机内参,并遵循既有源 Alpha 处理契约。
|
||||
|
||||
#### Scenario: Image inference
|
||||
- **WHEN** 有效图片完成推理
|
||||
- **THEN** 有效区域深度为有限正值,产物可供既有几何工作流应用
|
||||
|
||||
#### Scenario: Panorama inference
|
||||
- **WHEN** 全景任务选择 MoGe 3
|
||||
- **THEN** 透视视图使用已知 90 度水平 FOV,模型 Mask 与源有效性共同约束融合及 Alpha 输出
|
||||
|
||||
### Requirement: Reuse existing input and workflow controls
|
||||
|
||||
MoGe 3 SHALL 沿用 MoGe 2 的输入准备、最大分析尺寸、分辨率等级及设备选择流程。同一组已下载模型资产 SHALL 接受现有流程产生的不同图片尺寸、宽高比和分辨率等级,输出尺寸对应分析图片。
|
||||
|
||||
#### Scenario: Vary image dimensions
|
||||
- **WHEN** 用户连续提交现有输入流程支持的不同尺寸和宽高比图片
|
||||
- **THEN** 使用同一组模型资产完成预测,遵循现有分析尺寸限制及输出尺寸契约
|
||||
|
||||
#### Scenario: Change resolution level
|
||||
- **WHEN** 用户修改现有 MoGe Resolution Level 后再次执行任务
|
||||
- **THEN** 新任务采用对应 token 数并执行三次细化,复用已下载模型资产
|
||||
|
||||
#### Scenario: Switch to MoGe 3 in an existing workflow
|
||||
- **WHEN** 用户在深度平面、裁切或全景工作流中将模型改为 MoGe 3
|
||||
- **THEN** 沿用原有任务参数、操作步骤与几何产物,模型内部完成多图及细化调度
|
||||
|
||||
### Requirement: Manage supported sessions
|
||||
|
||||
系统 SHALL 按已验证的 ONNX 设备能力执行,复用连续帧或视图的会话,在模型切换及关闭时释放资源,对不支持的执行方式和推理失败给出明确反馈。
|
||||
|
||||
#### Scenario: Model switching
|
||||
- **WHEN** 用户在 MoGe 2 与 MoGe 3 之间切换
|
||||
- **THEN** 执行新选择并释放旧模型资源
|
||||
|
||||
#### Scenario: Unsupported execution or cancellation
|
||||
- **WHEN** 执行设备不支持、资源不足或任务取消
|
||||
- **THEN** 给出明确状态,不应用部分结果,并允许后续重新运行
|
||||
20
openspec/changes/archive/2026-09-17-add-moge3-model/tasks.md
Normal file
20
openspec/changes/archive/2026-09-17-add-moge3-model/tasks.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## 1. 原型与动态配置验收
|
||||
|
||||
- [x] 1.1 在项目外将完整 MoGe 3 拆为骨干与细化 ONNX,使用固定 256×192 / 1200 tokens 在 CPU 和 DirectML 上运行三张图、三次细化,保存参考比较及耗时记录。
|
||||
- [x] 1.2 将骨干扩展为动态输入尺寸和可变 token 数,用同一组 ONNX 资产连续运行不同宽高比、分析尺寸与分辨率等级,验证输出尺寸遵循现有契约。
|
||||
- [x] 1.3 定位原型中较大像素误差,确定后续验收容差,在 CPU 与 DirectML 验证动态配置及 90 度 FOV,记录精度、实际执行路径、耗时与内存峰值。
|
||||
- [x] 1.4 汇总动态配置、精度、性能及平台支持矩阵;完整验收通过后进入第 2 阶段,未通过项保留具体实验结论。
|
||||
|
||||
## 2. 模型资产与推理
|
||||
|
||||
- [x] 2.1 将已验证的转换方案整理至 tools/models,固定离线依赖,重新导出并通过相同精度验收以验证可复现性。
|
||||
- [x] 2.2 登记 MOGE3_VITL 的全部 ONNX 图及外部数据文件,验证大小和哈希,测试下载成功、失败、取消与就绪判断。
|
||||
- [x] 2.3 将双图、邻域生成和三次细化封装在 MoGe 3 ONNX 模块,仅增加必要推理分派;测试现有参数、输出尺寸、内参、有效性及分辨率等级传递。
|
||||
- [x] 2.4 更新会话缓存和释放,测试模型交替、连续帧复用与失败后恢复,确认正式环境不需 Torch 或 Triton。
|
||||
|
||||
## 3. 工作流接入与验收
|
||||
|
||||
- [x] 3.1 在现有模型登记与枚举中加入新选项,测试旧模型键和默认值保持、新模型选择及设备错误提示,核对继续使用既有 Job 参数。
|
||||
- [x] 3.2 在深度平面、裁切与全景的必要调用点接入分派,复用现有 Operator 和节点资产,运行产物、90 度 FOV、Alpha、取消与融合回归测试。
|
||||
- [x] 3.3 在正式 ONNX Runtime 环境运行完整工作流并记录结果,在支持矩阵中的其他平台完成对应验收。(Windows 与 Linux 已通过,macOS 见 validation.md。)
|
||||
- [x] 3.4 检查依赖、Manifest 和打包测试的一致性,运行相关测试与全量 pytest,确认正常退出及最终差异范围。
|
||||
@@ -0,0 +1,51 @@
|
||||
# MoGe 3 ONNX 验收
|
||||
|
||||
2026-09-10。新增 `MOGE3_VITL`,复用 MoGe 2 的输入准备、0–9 分辨率等级、下载、会话缓存和几何产物。骨干支持动态图片尺寸及 token 数,细化图支持动态点数,正式推理执行三次细化。
|
||||
|
||||
## 资产
|
||||
|
||||
转换源码:`tools/models/moge3_export.py`。上游提交为 `74fbce054ebed49800de42d0ad0e83495065719a`,权重 revision 为 `184008f877d7ad1ad4c2cd2182a9bd1f63d0e5be`。固定 Torch、ONNX、NumPy 和上游提交后重复导出,两次文件哈希一致。
|
||||
|
||||
| 文件 | 字节数 | SHA-256 |
|
||||
|---|---:|---|
|
||||
| backbone.onnx | 1324190318 | f21246c6921373c8c79ecdb8c78818a79bd2506c2fb88f4d4cdec21b92f725e3 |
|
||||
| refiner.onnx | 157729628 | 67ad7297aba18f8ef7939e8f700a5b25c41a19ddb68656bc2cd08b54c248fe2a |
|
||||
|
||||
权重内嵌于两个 ONNX 文件。镜像已发布到 `models.omoolab.xyz/moge-3-vitl-onnx/`,使用项目 `ModelManager.download` 下载到新目录,耗时 136.1 秒,大小、哈希及 `ready()` 校验通过。
|
||||
|
||||
## 精度与动态配置
|
||||
|
||||
参考为原始 MoGe 3、相同权重和三次细化。上游 FlexGEMM 的 `SPCONV_ALLOW_TF32` 独立于 Torch TF32 设置;关闭该选项后进行 FP32 比较。验收脚本预设平均深度相对误差小于 0.1%、99 分位小于 1%、二值 Mask 一致率大于 99.9%。
|
||||
|
||||
同一组动态图依次执行以下配置;耗时包含骨干、邻域生成、三次细化和后处理,不包含会话创建。
|
||||
|
||||
| 图片宽×高 | tokens | 水平 FOV | Windows CPU | DirectML | DirectML 平均深度相对误差 | 99 分位 |
|
||||
|---|---:|---|---:|---:|---:|---:|
|
||||
| 384×256 | 1200 | 自动 | 10.65 s | 4.86 s | 0.00216% | 0.02068% |
|
||||
| 257×385 | 2400 | 90° | 22.92 s | 9.67 s | 0.00228% | 0.01823% |
|
||||
| 320×320 | 3600 | 自动 | 36.41 s | 17.11 s | 0.00167% | 0.01609% |
|
||||
|
||||
三项均通过 CPU 和 DirectML 容差,二值 Mask 一致率为 100%。正式模块使用现有 MoGe 2 后处理,等级 0/9 的平均点图相对误差约 0.00217%/0.00173%,平均法线角误差约 0.006°,归一化内参最大绝对偏差小于 0.000002。2400 tokens 是图层测试值;公开等级继续按原公式映射至 1200–3600 tokens。
|
||||
|
||||
## 工作流与平台
|
||||
|
||||
| 平台 / Runtime | 验收结果 |
|
||||
|---|---|
|
||||
| Windows / DirectML 1.24.4 / RTX 5070 Ti | 动态精度、深度、裁切、12 视图全景通过;纯 ONNX 环境 |
|
||||
| Windows / CPU | 动态精度通过;原型 CPU Runtime 为 1.29.0 |
|
||||
| Linux x64 / CPU 1.24.4 / Python 3.12 | 使用项目声明的正式依赖,深度、裁切、12 视图全景通过 |
|
||||
| macOS / CPU、CoreML | 当前没有可用设备,待实机验收 |
|
||||
|
||||
真实 Job 调用仅替换进度/取消传输上下文,图片准备、模型管理、推理、几何处理与文件写入均使用项目代码。Windows 深度、裁切、全景分别约 9.81、5.45、67.13 秒;Linux 分别约 19.19、9.10、112.10 秒。深度/裁切生成 EXR、元数据及法线,全景完成 12 个 90° 视图推理和径向融合并生成 EXR 与元数据。
|
||||
|
||||
DirectML 的主要网络计算在 GPU 执行,部分 Gather、Unsqueeze、Concat、Less、Cast、ReduceSum 由 ONNX Runtime CPU 提供程序执行。正式环境依赖保持原样,未引入 Torch、FlexGEMM 或 Triton。
|
||||
|
||||
等级 0/9 连续运行时,Windows 进程峰值工作集约 1483 MiB;显卡整体占用由 6370 MiB 上升至最高 15716 MiB(每 0.5 秒采样,包含桌面及其他进程,不能视为模型独占显存)。Linux 三个 Job 进程峰值 RSS 约 4664 MiB。高等级的内存占用明显高于低等级。
|
||||
|
||||
## 回归与复现记录
|
||||
|
||||
全量 pytest:1143 passed、52 subtests passed,187.79 秒,正常退出。随后新增会话加载失败恢复测试,MoGe 3 专项 7 passed。覆盖下载完整性、缓存复用/切换/恢复、三次细化、尺寸、参数及现有 Alpha、FOV、取消、全景融合和打包内容契约。OpenSpec 严格校验通过。
|
||||
|
||||
实验与运行日志保存在 `C:/Users/icrdr/AppData/Local/Temp/anyimage-moge3-trial/`:`dynamic_reference.py`、`dynamic_validate.py`、`production_validate.py`、`job_validate.py`、`linux_jobs.py`、`download_validate.py`、`memory_validate.py`,及对应 JSON/日志。Linux 使用只读项目挂载的 Python 3.12 容器;Windows 全量测试使用按项目锁文件创建的独立测试环境。
|
||||
|
||||
任务 3.3 保留未完成状态,待补充 macOS 实机验收。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-09
|
||||
@@ -0,0 +1,86 @@
|
||||
## Context
|
||||
|
||||
独立原型验证了 12 个透视视图的 MoGe2 推理、距离融合和 Quad Sphere。正式转换沿用 Image Empty 的异步转换流程。用户明确选择通过现有 Remove Background 预先处理背景,转换消费当前图片和 Alpha。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
本次范围是静态 2:1 等距柱状全景的菜单转换、正式节点资产和 Split Threshold。沿用原型的 Subdivide、Depth Scale 和单张深度图片约定。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 入口与对象生命周期
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Image Empty 右键 AnyImage] --> B[Convert to Panorama]
|
||||
B --> C[检查静态图和 2 比 1 尺寸]
|
||||
C --> D[读取当前 RGBA 和 Preferences]
|
||||
D --> E[提交全景 Job]
|
||||
E --> F[加载并打包颜色和深度]
|
||||
F --> G[创建全景对象和节点修改器]
|
||||
G --> H[替换源 Empty 并记录撤销]
|
||||
```
|
||||
|
||||
新增 `operators/convert_to_panorama/`,`operators.py` 定义 `ConvertToPanorama` 与 `GeneratePanorama`,`object.py` 创建结果,`__init__.py` 导出注册清单。公开入口为 `anyimage.convert_to_panorama`,Job 为 `generate-panorama-geometry`。复用 `common` 的输入暂存、材质、深度加载、修改器赋值和对象收尾函数。
|
||||
|
||||
模型、推理级别和分析尺寸从 Preferences 读取,Job 参数为普通字典。Blender 数据只在主线程读取输入和处理响应。新对象中心对应原 Empty 原点,继承其世界变换;局部 +Z 为全景上方,U=0 朝向 +X,U 增加沿 +Y 转动。生成成功后替换源对象并激活结果;失败、取消或源对象已失效时清理本次临时资源。图片显示偏移只属于平面预览,球面方向由完整图片 UV 定义。
|
||||
|
||||
### 后端与数据协议
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[当前 RGBA 全景] --> B[12 个重叠 90 度视图]
|
||||
B --> C[MoGe2 与已知 FOV 的几何恢复]
|
||||
C --> D[有效视图的径向距离]
|
||||
D --> E[横向周期的对数梯度融合]
|
||||
E --> F[尺度锚定与有限距离场]
|
||||
A --> G[源 Alpha 对齐到深度尺寸]
|
||||
F --> H[depth.exr]
|
||||
G --> H
|
||||
```
|
||||
|
||||
`server/jobs/panorama.py` 负责任务调度,`server/geometry/panorama.py` 负责投影与融合。将已知 FOV 接入已有 ONNX 后处理和模型会话生命周期,普通图片继续使用焦距估计。保留原分辨率颜色;视图分析尺寸遵循输入上限,首版目标视图为 512×512,融合网格最长边上限 512 且保持 2:1。Subdivide 独立控制最终网格密度。
|
||||
|
||||
源 Alpha 对齐、黑底模型输入及可见区域排除采用原型已验证的流程。全透明视图跳过推理,部分视图无效时继续融合其余有效观测;全局无有效几何时报错。逐视图及融合前后检查取消,释放模型资源。
|
||||
|
||||
| 字段 | 文件 | 内容 |
|
||||
| --- | --- | --- |
|
||||
| 颜色文件 | `image.0001.png` | 当前 RGBA,保留源 Alpha |
|
||||
| `depth` | `depth.exr` | float32 RGB 重复保存径向距离,A 为融合 Mask × 源 Alpha |
|
||||
| `depth_metadata` | `depth.json` | `projection: equirectangular`、`image_size`、`reference_distance` |
|
||||
|
||||
`reference_distance` 为满足 Alpha 阈值的有限正径向距离中位数,用于 Split Threshold 的尺度归一化。全景元数据按投影类型单独读取;复用现有 EXR 写入及 Non-Color、CHANNEL_PACKED、Pack 配置。颜色由材质采样,几何节点只有一个深度图片输入。
|
||||
|
||||
### Quad Sphere 与 Split Threshold
|
||||
|
||||
新增资产 `O Image Panorama`。Quad Sphere 与球面校正子组的构建定义归入本仓库节点构建模块,参考已检查的 `O Quad Sphere`,运行时从 `O_AnyImage.blend` 加载。面数为 `6 × 4^Subdivide`,默认 Subdivide=7;Depth Scale 默认 1,范围 0–1;Validity Threshold 默认 0.9;Split Threshold 默认 0,范围 0–1。深度图片、材质和参考距离作为数据输入。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[可调细分 Quad Sphere] --> B[固定单位射线与球面 UV]
|
||||
B --> C[按深度 EXR Alpha 剔除无效面]
|
||||
C --> D[相邻面径向距离变化判断]
|
||||
D --> E[Split Edges 与条带面清理]
|
||||
E --> F[分离面角沿原射线取所属面距离]
|
||||
F --> G[Depth Scale 插值到重建表面]
|
||||
G --> H[材质与临时属性清理]
|
||||
```
|
||||
|
||||
提取并复用 `tools/nodes/common/depth_surface.py` 中的相邻面采样、内边判断、Split Edges、面角归属和条带面清理。共用逻辑消费明确的深度字段、基础面中心和归一化比例;Depth Plane 的调用同步适配,保持已有求值结果。
|
||||
|
||||
对全景,令相邻面距离为 rA/rB,单位球面上的面中心为 cA/cB,参考距离为 R。沿用现有 2.5 系数,以 `abs(rA-rB) / R × Split Threshold > 2.5 × distance(cA,cB)` 选择有两个邻面的内边。阈值为 0 时直接保留通过有效性剔除的原网格;阈值增大时选择更多断层边。
|
||||
|
||||
选中断层附近的面角使用其所属面的距离,未选中的顶点保持原采样;所有点沿原始球面射线定位。最终半径为 `1 + Depth Scale × (目标距离 - 1)`。条带面清理遵循当前 Depth Plane 的同一拓扑快照规则。
|
||||
|
||||
全景面中心采样由面中心方向重新计算 UV,横向循环采样;材质面角 UV 独立处理经度分支和极点。这样左右接缝与普通内边参与同样的判断。分裂后保留原射线及面角 UV,末尾清理专用临时属性。
|
||||
|
||||
### 验证与交付
|
||||
|
||||
正式模块从原型迁移有效算法及测试,运行时依赖正式模块与打包资产。此次实现运行相关单元测试、完整回归测试和独立 Blender 求值检查。节点资产更新走现有离线资产流程;扩展包及文档构建属于另行发布操作。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 径向距离与平面 Z 深度的比较尺度不同 → 用参考距离归一化,并测试相同形状整体缩放后的分裂选择一致。
|
||||
- 全景接缝和极点的 UV 有多种等价表示 → 用方向生成采样坐标,并验证接缝两侧的等价深度断层。
|
||||
- 共享分裂代码近期增加了条带清理 → 保留现有 Depth Plane 回归结果,并覆盖全景分裂后孤立条带、Alpha 边缘和连续斜面。
|
||||
- 天空及遮挡导致预测缺失 → 以当前源图 Alpha 和融合 Mask 确定有效几何,允许通过现有 Remove Background 预先处理。
|
||||
@@ -0,0 +1,25 @@
|
||||
## Why
|
||||
|
||||
全景原型已验证 Quad Sphere、径向深度和单张深度 EXR 的可行性。现在需要把生成流程接入 Image Empty 的常用入口,并通过 Depth Plane 的 Split Threshold 机制处理前后景连接处的拉伸。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 在 Image Empty 的 AnyImage 右键菜单增加 `Convert to Panorama`,通过后台 Job 将静态 2:1 全景转换成可编辑的全景表面。
|
||||
- 将原型的多视图 MoGe2 推理、距离融合和深度 Alpha 导出接入正式 Server。
|
||||
- 新增 `O Image Panorama` 节点资产,提供 Quad Sphere 细分、Depth Scale、Validity Threshold 和 Split Threshold。
|
||||
- 复用 Depth Plane 的分边、面角深度重定位和条带面清理机制,按全景径向距离适配判断。
|
||||
- 延续单深度图片输入:`depth.exr` 的 Alpha 在导出时合成模型 Mask 与当前源图 Alpha。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `panorama-conversion`: Image Empty 菜单、异步转换、对象替换与撤销。
|
||||
- `panorama-geometry-generation`: 全景多视图推理、融合与文件产物。
|
||||
- `panorama-surface`: Quad Sphere、球面 UV、深度有效性剔除与 Split Threshold。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
涉及 `menu.py`、类型注册、新增 `operators/convert_to_panorama/`、Server Job 与全景几何模块、共享深度表面节点构建函数,以及对应测试。生产节点组由资产构建流程写入 `O_AnyImage.blend`,运行时按名称加载。使用现有 MoGe2、ONNX Runtime、NumPy 与 SciPy 环境。
|
||||
@@ -0,0 +1,33 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Image Empty panorama conversion entry
|
||||
|
||||
系统 SHALL 在 Image Empty 的 AnyImage 右键菜单展示 `Convert to Panorama`,调用 `anyimage.convert_to_panorama`。
|
||||
|
||||
#### Scenario: Select panorama conversion
|
||||
- **WHEN** 用户选中静态 2:1 Image Empty 并选择 Convert to Panorama
|
||||
- **THEN** 提交当前图片及 Alpha 的全景生成任务,使用 Preferences 中的模型、推理级别和分析尺寸
|
||||
|
||||
#### Scenario: Unsupported image
|
||||
- **WHEN** 输入是动画、视频或非 2:1 图片
|
||||
- **THEN** 提示输入限制并保留源对象
|
||||
|
||||
### Requirement: Consume current image alpha
|
||||
|
||||
转换 SHALL 直接使用当前源图及 Alpha。背景处理由用户通过现有 Remove Background 完成。
|
||||
|
||||
#### Scenario: Already removed background
|
||||
- **WHEN** 源 Empty 的天空已透明
|
||||
- **THEN** 当前 Alpha 进入全景任务,转换过程中只运行几何模型
|
||||
|
||||
### Requirement: Complete conversion transaction
|
||||
|
||||
系统 SHALL 在主线程加载结果、打包贴图并创建节点对象,成功后替换源 Empty,并提供完整撤销。
|
||||
|
||||
#### Scenario: Successful conversion
|
||||
- **WHEN** Job 返回有效产物
|
||||
- **THEN** 创建使用 O Image Panorama 的对象,继承源 Empty 世界变换并以其原点为拍摄中心,激活结果并支持撤销恢复源对象
|
||||
|
||||
#### Scenario: Cancelled or failed conversion
|
||||
- **WHEN** 任务失败、取消或响应时源对象已失效
|
||||
- **THEN** 清理本任务暂存及未使用数据块,不应用部分转换结果
|
||||
@@ -0,0 +1,33 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Generate panoramic radial distance
|
||||
|
||||
Server SHALL 从当前 RGBA 全景生成 12 个重叠的 90 度透视视图,使用已知 FOV 恢复 MoGe2 几何并融合为横向周期的径向距离场。
|
||||
|
||||
#### Scenario: Continuous panorama
|
||||
- **WHEN** 输入包含跨越 U=0/1 的连续表面
|
||||
- **THEN** 两侧使用同一周期求解,产出有限正距离且无额外拼接断层
|
||||
|
||||
#### Scenario: Invalid views and cancellation
|
||||
- **WHEN** 部分视图全透明或模型无有效预测
|
||||
- **THEN** 利用其余有效观测完成融合,全局无有效几何时返回明确错误,并在视图与融合阶段响应取消
|
||||
|
||||
### Requirement: Export combined depth alpha
|
||||
|
||||
系统 SHALL 返回颜色文件、`depth.exr` 与 `depth.json`。深度 RGB 保存径向距离,Alpha 在导出时一次合成融合 Mask × 源 Alpha;元数据 SHALL 标明等距柱状投影、深度图尺寸和参考距离。
|
||||
|
||||
#### Scenario: Transparent background
|
||||
- **WHEN** 源图某区域 Alpha 为零
|
||||
- **THEN** 该区域深度 EXR Alpha 为零,颜色文件保留源 Alpha
|
||||
|
||||
#### Scenario: Fractional alpha
|
||||
- **WHEN** 有效模型 Mask 为 1 且源 Alpha 为 0.5
|
||||
- **THEN** 深度导出 Alpha 为 0.5,几何端直接消费该值
|
||||
|
||||
### Requirement: Use managed production runtime
|
||||
|
||||
全景生成 SHALL 使用现有模型会话、Preferences 配置和 Job 生命周期;Job 参数可序列化,Server 与 Blender 数据保持进程边界。
|
||||
|
||||
#### Scenario: Configured inference
|
||||
- **WHEN** 用户修改模型或推理分辨率级别后发起转换
|
||||
- **THEN** 新任务采用配置值,最终网格细分仍由独立 Subdivide 参数控制
|
||||
@@ -0,0 +1,41 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Editable Quad Sphere surface
|
||||
|
||||
系统 SHALL 提供资产 `O Image Panorama`,使用全四边面 Quad Sphere,并提供 Subdivide、Depth Scale、Validity Threshold 和 Split Threshold。几何节点 SHALL 只有一个深度图片输入。
|
||||
|
||||
#### Scenario: Sphere and depth endpoints
|
||||
- **WHEN** 所有样本有效且 Split Threshold 为 0
|
||||
- **THEN** Subdivide=n 产生 6×4^n 个四边面,Depth Scale=0 为单位球,Depth Scale=1 为预测径向距离表面
|
||||
|
||||
#### Scenario: Validity culling
|
||||
- **WHEN** 深度 EXR Alpha 低于 Validity Threshold
|
||||
- **THEN** 按 Depth Plane 的点域布尔字段与面域删除方式剔除对应区域
|
||||
|
||||
### Requirement: Depth Plane split semantics
|
||||
|
||||
全景 SHALL 复用 Depth Plane 的相邻面深度差判断、内边分裂、面角深度重定位和条带面清理机制,并使用参考距离归一化的径向距离适配球面。
|
||||
|
||||
#### Scenario: Disabled splitting
|
||||
- **WHEN** Split Threshold 为 0
|
||||
- **THEN** 分裂与条带清理完全旁路,有效性剔除保持生效
|
||||
|
||||
#### Scenario: Foreground depth discontinuity
|
||||
- **WHEN** 相邻有效面的归一化距离跳变达到分裂条件
|
||||
- **THEN** 分离选中的内边,断层附近面角沿原球面射线使用所属面的距离,未选中顶点保持原始深度采样
|
||||
|
||||
#### Scenario: Threshold and scale consistency
|
||||
- **WHEN** 增大 Split Threshold 或对所有距离与参考距离同时乘以相同正比例
|
||||
- **THEN** 增大阈值不会减少被选中的断层边,等比例距离变化不会改变断层边选择
|
||||
|
||||
#### Scenario: Seam and strip cleanup
|
||||
- **WHEN** 深度断层跨越全景经度接缝或分裂产生孤立条带
|
||||
- **THEN** 经度接缝采用周期采样,面角 UV 保持正确,条带面按 Depth Plane 的同一拓扑快照规则清理
|
||||
|
||||
### Requirement: Packaged node assets
|
||||
|
||||
生产节点组及 Quad Sphere 子组 SHALL 包含在 `O_AnyImage.blend` 中,通过现有加载入口使用。
|
||||
|
||||
#### Scenario: Reopen converted object
|
||||
- **WHEN** 在新 Blender 进程重新打开转换后的文件
|
||||
- **THEN** 无需访问原型目录或外部参考库即可求值,贴图、细分、Depth Scale 和 Split Threshold 均正常工作
|
||||
@@ -0,0 +1,24 @@
|
||||
## 1. 全景后端
|
||||
|
||||
- [x] 1.1 将原型的投影、周期距离融合和参考距离计算迁移到正式 Server 模块,保留解析几何测试
|
||||
- [x] 1.2 为现有 MoGe2 ONNX 推理增加已知 FOV 的几何恢复,复用模型会话并验证普通图片回归
|
||||
- [x] 1.3 实现 generate-panorama-geometry Job、尺寸和有效性检查、取消与清理,导出当前颜色、深度及全景元数据
|
||||
- [x] 1.4 验证源 Alpha 与 Mask 仅在深度导出合成一次,覆盖透明视图、全局无有效数据和跨经度接缝
|
||||
|
||||
## 2. 全景节点资产
|
||||
|
||||
- [x] 2.1 在本仓库实现 Quad Sphere 及校正子组的资产构建,创建 O Image Panorama 的细分、球面 UV 和 Depth Scale
|
||||
- [x] 2.2 复用深度 EXR 加载与 Alpha 剔除,保持几何节点单深度图片输入
|
||||
- [x] 2.3 提取共享 Split Threshold 机制并同步适配现有调用,将径向距离判断、面角重定位和条带清理接入全景
|
||||
- [x] 2.4 覆盖阈值旁路、断层强度、尺度不变性、连续斜面、经度接缝、极点及条带清理,并运行现有深度表面回归
|
||||
|
||||
## 3. 菜单与对象转换
|
||||
|
||||
- [x] 3.1 实现 ConvertToPanorama 与 GeneratePanorama,接入 Preferences、输入暂存、Job 参数与注册清单
|
||||
- [x] 3.2 在 Image Empty 的 AnyImage 菜单添加 Convert to Panorama,验证静态 2:1 输入限制及当前 Alpha 传递
|
||||
- [x] 3.3 实现主线程结果加载、材质、全景对象和修改器,覆盖源变换继承、替换、撤销及失败清理
|
||||
|
||||
## 4. 集成验证
|
||||
|
||||
- [x] 4.1 通过既有离线节点资产流程更新 O_AnyImage.blend,验证新进程加载和打包后独立求值
|
||||
- [x] 4.2 使用已去天空的真实全景与解析样本检查正式菜单流程和 Split Threshold,运行完整测试并检查最终差异
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-08
|
||||
@@ -0,0 +1,74 @@
|
||||
## Context
|
||||
|
||||
当前 `onnx_upscale.py` 用 256 像素 tile 执行三个 4× 模型,`models/upscale.py` 负责输入尺寸限制和 alpha 回填,`ModelManager` 缓存单个 Upscale Session。Operator 的倍率和提示均为 4×。
|
||||
|
||||
本地实验位于 `build/upscale-explore/pisa-research/`。已验证固定 512×512 的 PiSA-SR FP16 ONNX 全流程在 DirectML 上出图,0.7 预设对应像素强度 1.0、语义强度 0.7、空提示词和 seed 42。两套 UNet 常驻时,512 输出约 1.57–2.37 秒;默认强度 1.0 的单 UNet 速度不代表 0.7。分块和跨设备部署尚待验证。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** 四个选项统一 2× 输出;复现已选定的 PiSA-SR 0.7 观感;通过已有 ONNX 运行时服务图片和逐帧任务。
|
||||
|
||||
**Non-Goals:** 本次范围限定为固定预设与既有 Upscale 操作,强度滑杆、提示词编辑和视频时序增强属于独立能力。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 最终倍率与模型原生倍率分别定义
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[读取并限制输入尺寸] --> B{模型}
|
||||
B -->|现有三个模型| C[原生 4× 分块推理]
|
||||
C --> D[Lanczos 缩至输入宽高的 2 倍]
|
||||
B -->|PiSA-SR| E[Bicubic 放大至 2×]
|
||||
E --> F[VAE 编码与固定噪声采样]
|
||||
F --> G[像素与完整 UNet 预测]
|
||||
G --> H[按语义强度 0.7 组合并解码]
|
||||
H --> I[AdaIN 颜色校正与裁切]
|
||||
D --> J[按最终尺寸回填原始 alpha]
|
||||
I --> J
|
||||
J --> K[保存结果并替换图片]
|
||||
```
|
||||
|
||||
公共结果倍率为 2,旧 ONNX 图的 shape 校验仍为原生 4。先实现完整 4× 拼接后一次 Lanczos 缩小,作为明确的像素基线;避免先逐 tile 缩小引入新的滤波接缝。最终尺寸相对于 `limit_image()` 之后的输入,沿用 Operator 对输入长边的既有限制。
|
||||
|
||||
保留三个模型的 key、原生名称和默认选择,说明文字明确最终输出 2×。新增 key `PISA_SR`、显示名 `PiSA-SR`,说明固定细节强度 0.7;生产和 Debug 复用相同输出入口。
|
||||
|
||||
### 2. 固定 0.7 的定义与实验一致
|
||||
|
||||
使用同一采样 latent 计算 `pixel + 0.7 * (full - pixel)`,再按已验证的公式解码。空提示词 embedding 与 timestep 固定在资产中,seed 固定为 42;采样算法、精度和噪声布局通过测试锁定。保留实验中的 Bicubic 输入插值、VAE scaling、输出量化和 AdaIN 颜色校正。
|
||||
|
||||
该强度是在预测结果上插值。将 LoRA 权重乘以 0.7 或插值两套 UNet 权重,不能视为等价的单 UNet 优化;任何这样的优化必须另行通过效果对照。
|
||||
|
||||
### 3. PiSA-SR 使用独立适配器与多文件资产
|
||||
|
||||
新增 `server/models/onnx_pisa_sr.py` 承担 VAE、UNet、采样及分块。`ModelManager` 按模型类型创建单 Session 或 PiSA-SR 管线,并统一缓存 key、释放和状态展示。Session 创建和运行复用 `onnx_runtime.py`,补齐 DirectML 所需的 sequential execution 与禁用 memory pattern 配置。
|
||||
|
||||
资产以 encoder、decoder、pixel UNet、full UNet 为明确文件集合,通过既有 catalog 下载与完整性校验。实验包约 3.63 GB,发布清单必须使用最终导出文件的尺寸和 SHA256。导出工具固定 PiSA 源码 commit、基础模型 revision、LoRA 校验值和导出环境;训练依赖仅用于离线工具。实验目录仅作为参考,不作为运行时路径。
|
||||
|
||||
### 4. 分阶段执行限制模型常驻
|
||||
|
||||
优先采用按阶段处理全部 tile 的调度:编码并把 latent 存入主机内存,运行像素 UNet 保存预测,释放该 Session 后运行完整 UNet并组合,最后解码。GPU 上同一时刻只常驻一套 UNet;切换在整图阶段边界发生。大图中间数组可落入任务临时目录,结束或取消后清理。
|
||||
|
||||
此方案以降低显存峰值为优先,代价是阶段加载成本;实施时同时测冷启动、连续图片和多帧耗时。任务结束按实际 Session 最后使用点释放临时推理内存,资源错误使整个 PiSA 管线缓存失效,Clear Models 释放全部组件。
|
||||
|
||||
### 5. 固定尺寸 ONNX 通过重叠分块覆盖任意输入
|
||||
|
||||
以已验证的 512×512 输出 tile 为初始部署尺寸,采用重叠区域加权融合,边缘填充后裁回精确的 2× 尺寸。噪声按整图 latent 坐标生成并切片,重叠区域共享噪声;AdaIN 在拼接完成后按整图执行。单 tile 情况保持实验基线,多 tile 的重叠宽度通过接缝对照确定并固化为内部常量。
|
||||
|
||||
alpha 从受尺寸限制的源图直接 Lanczos 放大到最终 2×,独立于生成模型;透明 RGB 的处理沿用现有输入语义,并加入半透明边缘目视验收。多帧逐帧处理并复用固定噪声规则,支持取消;生成纹理的时序稳定性列入验收观察。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [分块改变生成纹理或形成接缝] → 对两张实验样图及跨 tile 的纹理、渐变、透明边缘做完整图与局部对照;接缝未通过时继续调整分块方案。
|
||||
- [分阶段 Session 加载拖慢连续任务] → 记录端到端冷暖耗时、峰值显存和主机内存;不将单 UNet 默认模式的数字作为 0.7 性能承诺。
|
||||
- [FP16 图在 CPU/CoreML 上兼容性未知] → 实测项目支持的设备路径;需要时导出匹配的精度资产,并在 catalog 中表达准确支持范围。不可运行的设备返回明确原因。
|
||||
- [新细节改变原始图案或引起逐帧闪动] → 保留已有模型选择,验收脸部、文字、纹理和短序列样例,界面说明生成型细节补充。
|
||||
- [多 GB 权重分发与依赖来源] → 发布前核对基础模型和 PiSA 权重的分发条款,登记固定来源及最终文件校验信息。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
先准备与验证 PiSA 资产及分块管线,再接入 catalog、模型管理器和界面,统一最终 2× 结果并运行测试。现有用户保存的模型 key 继续有效,后续执行采用 2×;已有图片产物无需迁移。回退时同步回退模型选项和推理入口,已下载资产可由已有缓存管理处理。
|
||||
|
||||
## Open Questions
|
||||
|
||||
实施实验需确定分块重叠宽度、阶段加载的可接受耗时,以及 CPU/CoreML 的实际精度与支持范围。这些验证不改变 0.7 和最终 2× 的产品约定。
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
现有 Upscale 每次把宽高放大 4 倍,尺寸增长过快。PiSA-SR 在已测试的 0.7 细节强度下能够补充更丰富的纹理,适合作为新的生成型选项;所有模型统一每次输出 2×,方便逐步放大。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新增 `PiSA-SR` 模型选项,固定 `lambda_pix=1.0`、`lambda_sem=0.7`,输出宽高各为输入的 2 倍。
|
||||
- **BREAKING**:现有 General WDN x4v3、SAFMN Real x4、Real-ESRGAN x4plus 仍使用已有 4× 权重,完成推理后以 Lanczos 缩小至一半,最终输出 2×。
|
||||
- Upscale 的尺寸计算、界面说明、生产与调试任务统一采用最终 2× 语义;保留现有默认模型。
|
||||
- PiSA-SR 通过 ONNX Runtime 接入模型下载、设备选择、缓存、取消和资源释放流程,支持任意合法输入尺寸与透明度保留。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `two-times-upscale`: 四个模型的统一 2× 输出、PiSA-SR 固定细节预设及模型资源生命周期。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无已发布的对应 capability。
|
||||
|
||||
## Impact
|
||||
|
||||
- `server/model_catalog.py`、模型下载与导出工具:增加 PiSA-SR 多文件模型资产及校验信息。
|
||||
- `server/models/`、`server/model_manager.py`:PiSA-SR 推理、分块及多 Session 生命周期;旧模型结果缩小。
|
||||
- `operators/upscale.py`、模型选择与相关界面:模型选项和最终倍率。
|
||||
- 生产、调试、透明图片和多帧处理相关测试。运行时沿用 ONNX Runtime;PyTorch 等仅用于离线导出与数值验证。
|
||||
@@ -0,0 +1,74 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Unified two-times output
|
||||
|
||||
系统 SHALL 对全部 Upscale 模型返回处理输入宽高各 2 倍的结果,并将该倍率用于界面说明和尺寸计算;处理输入为应用已有最大输入尺寸限制后的图像。
|
||||
|
||||
#### Scenario: Existing models produce two-times images
|
||||
- **WHEN** 任一现有模型处理 W×H 输入
|
||||
- **THEN** 系统先得到原生 4W×4H 结果,再使用 Lanczos 缩小到 2W×2H
|
||||
- **AND** 使用已有 4× 权重
|
||||
|
||||
#### Scenario: Repeated upscale
|
||||
- **WHEN** 用户对一次 2× 结果再次执行 Upscale 且输入尺寸仍满足限制
|
||||
- **THEN** 新结果宽高各为本次输入的 2 倍,即最初输入的 4 倍
|
||||
|
||||
### Requirement: Fixed PiSA-SR preset
|
||||
|
||||
系统 SHALL 提供 `PISA_SR` 模型选项,使用固定像素强度 1.0、语义强度 0.7、空提示词和 seed 42,通过 ONNX Runtime 生成 2× 结果。现有默认模型 SHALL 保持有效。
|
||||
|
||||
#### Scenario: Selected preset is executed
|
||||
- **WHEN** 用户选择 PiSA-SR 并执行 Upscale
|
||||
- **THEN** 模型对同一 latent 的预测按 `pixel + 0.7 * (full - pixel)` 组合
|
||||
- **AND** 输入插值、采样、解码与颜色校正遵循已验证的实验基线
|
||||
|
||||
#### Scenario: Repeatable inference
|
||||
- **WHEN** 相同图像在相同运行环境重复执行 PiSA-SR
|
||||
- **THEN** 系统使用相同噪声和处理规则,结果在该设备的数值容差内一致
|
||||
|
||||
### Requirement: Arbitrary dimensions and alpha preservation
|
||||
|
||||
系统 SHALL 支持合法输入范围内的小图、非方形图、奇数尺寸和跨 tile 图像,输出精确 2× 尺寸;有 alpha 的生产输入 SHALL 保留由源 alpha 插值而来的透明度。
|
||||
|
||||
#### Scenario: Tiled PiSA-SR image
|
||||
- **WHEN** 输入需要多个 PiSA-SR tile
|
||||
- **THEN** 系统融合重叠区域并裁去填充,输出无多余边缘的 2W×2H 图像
|
||||
- **AND** 连续纹理与渐变测试图的 tile 边界不出现可辨识拼接线
|
||||
|
||||
#### Scenario: Transparent input
|
||||
- **WHEN** 输入包含全透明和半透明像素
|
||||
- **THEN** 输出 alpha 等于源 alpha 直接 Lanczos 放大到最终尺寸的结果
|
||||
- **AND** RGB 生成过程不生成或覆盖 alpha
|
||||
|
||||
### Requirement: Model readiness and resource lifecycle
|
||||
|
||||
系统 SHALL 对 PiSA-SR 全部必需资产完成下载及校验后才判定就绪,按所选设备创建管线;其模型切换、取消、资源错误和显式清理 SHALL 覆盖全部组件。
|
||||
|
||||
#### Scenario: Incomplete model package
|
||||
- **WHEN** PiSA-SR 任一必需文件缺失或校验失败
|
||||
- **THEN** 系统提示准备模型并阻止使用不完整管线进行推理
|
||||
|
||||
#### Scenario: Bounded UNet residency
|
||||
- **WHEN** PiSA-SR 执行像素与完整预测阶段
|
||||
- **THEN** GPU 同时常驻的 UNet 至多一套,阶段间中间数据被保留以完成 0.7 组合
|
||||
|
||||
#### Scenario: Cancel or resource failure
|
||||
- **WHEN** 推理被取消或发生资源错误
|
||||
- **THEN** 系统停止后续阶段并清理任务中间文件
|
||||
- **AND** 资源错误释放整个 PiSA-SR 管线并报告可理解的失败原因
|
||||
|
||||
#### Scenario: Unsupported device
|
||||
- **WHEN** 所选设备无法执行已分发的 PiSA-SR 资产
|
||||
- **THEN** 系统报告明确的设备支持原因或执行已验证并明确呈现的回退路径
|
||||
|
||||
### Requirement: Consistent job behavior
|
||||
|
||||
系统 SHALL 在生产与调试任务采用同一模型预设和最终倍率,并对多帧输入逐帧应用该约定。
|
||||
|
||||
#### Scenario: Multiple frames
|
||||
- **WHEN** Upscale 处理多帧输入
|
||||
- **THEN** 每帧输出均为其处理输入的 2× 尺寸,帧顺序正确且阶段之间可以取消
|
||||
|
||||
#### Scenario: Debug matches production
|
||||
- **WHEN** 相同 RGB 输入、模型和设备分别通过生产与 Debug 入口处理
|
||||
- **THEN** 两者的 RGB 处理规则和最终尺寸一致
|
||||
@@ -0,0 +1,26 @@
|
||||
## 1. PiSA-SR 资产与数值基线
|
||||
|
||||
- [x] 1.1 将实验的固定来源、revision、LoRA 校验信息与离线依赖纳入正式导出工具,核对分发条款并实现四个 ONNX 组件的可复现导出。
|
||||
- [x] 1.2 固化 0.7 预测组合、seed 42 采样、空提示词、插值、量化和 AdaIN 规则;对两张样图验证 PyTorch 与 ONNX 0.7 的最终 RGB 平均绝对误差不超过 1 个 8-bit 灰度级,并目视确认细节。
|
||||
- [x] 1.3 验证 DirectML、CUDA 及 CPU/CoreML 的实际支持路径,确定精度资产与设备支持信息,记录不可用路径的明确处理方式。
|
||||
|
||||
## 2. 分块与资源管理
|
||||
|
||||
- [x] 2.1 实现 PiSA-SR 独立 ONNX 适配器,复用共享运行时,补齐 DirectML Session 配置并覆盖其他模型配置回归。
|
||||
- [x] 2.2 实现编码、像素预测、完整预测、解码的分阶段调度,GPU 至多常驻一套 UNet;覆盖阶段切换、中间数据保存及临时文件清理。
|
||||
- [x] 2.3 实现固定尺寸重叠分块、共享坐标噪声、加权拼接与最终裁切;用单 tile 基线、奇数尺寸、小图、渐变和跨边界纹理确定重叠参数并确认无可辨识接缝。
|
||||
- [x] 2.4 接入模型管理器的管线缓存、切换、Clear Models、取消与 OOM 释放;验证完整任务各组件的最后使用点和多帧行为。
|
||||
- [x] 2.5 实测 0.7 的 512、1024 和多 tile 输出,记录加载时间、端到端冷暖耗时、连续帧耗时及峰值显存/主机内存,确认阶段调度消除双 UNet 常驻。
|
||||
|
||||
## 3. 模型选项与统一倍率
|
||||
|
||||
- [x] 3.1 将最终 PiSA-SR 文件及 SHA256、尺寸、来源加入 catalog 和模型同步工具,覆盖多文件下载、损坏校验与就绪判定。
|
||||
- [x] 3.2 将三个旧模型的完整原生 4× 输出以 Lanczos 缩至 2×,保留原生 shape 校验;测试结果与先完整 4× 再缩半的基线一致。
|
||||
- [x] 3.3 在公共 Upscale 入口按模型分派,统一输入限制及最终 alpha 回填,覆盖生产与 Debug 两种路径。
|
||||
- [x] 3.4 新增 PiSA-SR 选项和 0.7 说明,更新 Operator 最终倍率、尺寸计算及所有相关 4× 行为提示,保留现有模型 key 与默认选择。
|
||||
|
||||
## 4. 验收
|
||||
|
||||
- [x] 4.1 覆盖四模型最终尺寸、连续两次 2×、输入限制、透明度、帧顺序、取消与资源错误的必要回归测试。
|
||||
- [x] 4.2 在 Blender 中验证模型选择、准备模型、图片替换与撤销,对样图、文字、半透明边缘和短序列目视检查细节、接缝及闪动。
|
||||
- [x] 4.3 运行相关测试及 `uv run pytest`;依赖或 Manifest 如有变更,核对锁文件与打包测试。记录实际设备验证范围及未解决问题。
|
||||
@@ -0,0 +1,32 @@
|
||||
## 实施结果
|
||||
|
||||
四个 Upscale 选项统一输出 2×。PiSA-SR 固定像素强度 1.0、语义强度 0.7;旧模型完成原生 4× 拼接后使用 Lanczos 缩半。默认模型与现有 key 保持有效。
|
||||
|
||||
PiSA-SR 使用 512 输出 tile、128 像素重叠。固定 seed 42 的 64×64 latent 噪声作为 noise.npy 分发,按整图坐标周期索引,重叠区域使用相同噪声。中间 latent 与像素预测存入任务临时目录,GPU 同时只加载一个组件;最终颜色校正在整图拼接后执行。
|
||||
|
||||
正式导出器从固定源码、基础权重和 LoRA 重新生成的四个 ONNX 文件与实验图逐文件 SHA256 相同。发布资产另外写入许可和修改元数据,并随包提供 Apache 2.0、CreativeML Open RAIL++-M 与 NOTICE。最终 8 个文件已上传 R2;远端大小核对零差异,应用实际下载函数完成 noise.npy 的下载与 SHA256 校验。
|
||||
|
||||
## 验证
|
||||
|
||||
- `uv run --no-sync pytest -q`:680 passed、2 xfailed、60 subtests passed。
|
||||
- 两张 0.7 样图,DirectML 与 PyTorch 最终成品平均绝对差异分别为 0.693、0.596 个 8-bit 灰度级;CPU 为 0.257、0.569。
|
||||
- DirectML 测试覆盖 1024×1024、1114×1518、1026×514 和 2×6 输出;完整主体、渐变的目视检查未见明显拼接线。
|
||||
- 三帧轻微平移、文字和半透明边缘通过生产入口验证;alpha 与源 alpha 直接 Lanczos 放大完全一致。细纹理随帧有变化,文字出现生成型模型的笔画变化。
|
||||
- Blender 4.5.3 实际 Python 接口验证四个模型枚举、Operator 注册、结果尺寸与图片替换;真实 Undo 测试恢复原图尺寸和像素。窗口菜单未通过鼠标操作,菜单与请求参数由接口测试覆盖。
|
||||
|
||||
## 速度与设备
|
||||
|
||||
RTX 5070 Ti、其他 GPU 应用同时运行。计时包含阶段加载、采样、拼接和颜色校正;不含模型下载。
|
||||
|
||||
| DirectML 输出 | 完整耗时 |
|
||||
| --- | --- |
|
||||
| 512 首次 | 5.24 秒 |
|
||||
| 512 重复 | 4.67 秒 |
|
||||
| 1024 | 5.55 秒 |
|
||||
| 三帧逐帧 | 每帧 4.79–5.05 秒 |
|
||||
|
||||
采样进程 RSS 峰值约 437 MiB;显卡总占用从约 7985 MiB 到峰值 11930 MiB,包含其他应用,不能视作本进程独占显存。阶段加载约占单张 512 耗时的大部分。
|
||||
|
||||
DirectML 和 CPU 完整出图。独立 CUDA ONNX Runtime 1.24.4 配合 torch 2.7.1 的 cuDNN 9.7.1 在本机 VAE 卷积上报 `No valid engine configs for ConvFwd`;该组合未通过 CUDA 验收,运行错误明确提示改选 DirectML 或 CPU,关闭 Session 的运行中隐式回退。CoreML 无可用测试设备,当前 PiSA-SR 明确提示选择 CPU。Windows 的既有生产路径使用 DirectML。
|
||||
|
||||
实验日志、输出图片和各阶段计时在 `build/upscale-explore/pisa-research/`。项目运行时依赖与 Manifest 未修改。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-08
|
||||
@@ -0,0 +1,78 @@
|
||||
## Context
|
||||
|
||||
普通 Plane 的 `create_mesh_plane_mesh()` 创建中心四边形和完整 UV,`O Mesh Plane` 负责矩形细分。现有 Depth Plane 使用固定底面的浮雕置换;Depth Cutout 的曲面分支使用相机 XYZ 投影。整图 Job 已通过通用 Artifact 层执行一次 MoGe-2 推理,该层支持 VECTOR Depth。
|
||||
|
||||
Cutout 的双击入口集中在 `SelectCutoutSelection.invoke()`、`full_image` Property 和 WorkspaceTool keymap。工作区中的 Cutout Shape 重组仍在进行,本提案以当前文件中的实际实现为基线。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
本次目标是建立独立的整图 Depth Surface 转换,并让 Cutout 通过套索提交区域。Depth Surface 的用户控制固定为 Subdivide、Thickness、Depth Scale、Reference Depth、Normal Smooth。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 入口与命名
|
||||
|
||||
采用 `ConvertToDepthSurface`、`anyimage.convert_to_depth_surface`、界面文字 **Convert to Depth Surface** 和资产名 `O Image Depth Surface`,表达输出是相机投影曲面。菜单中的顺序为 Plane、Depth Plane、Depth Surface,现有 Depth Plane 继续承担固定底面浮雕。
|
||||
|
||||
Cutout 直接使用共享拖动 keymap,删除双击整图绑定、`full_image` 声明和完整矩形 Selection 构造代码;`invoke()` 仅记录 Pie Event 后启动共享手势。清理因此失去用途的 import 和测试夹具。
|
||||
|
||||
### 整图生成主链
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Image[Image Empty] --> Convert[Convert to Depth Surface]
|
||||
Convert --> Check[静态图片与 AI 环境检查]
|
||||
Check --> Job[整图 Depth Job:VECTOR 模式]
|
||||
Job --> Prediction[一次 MoGe-2 推理]
|
||||
Prediction --> Files[完整 Color、vector-depth.exr、depth.json、tangent-normal.png]
|
||||
Files --> Load[主线程加载并 Pack 深度与法线]
|
||||
Load --> Mesh[共用矩形 Mesh、中心矩阵与材质]
|
||||
Mesh --> Modifier[加载 O Image Depth Surface]
|
||||
Modifier --> Result[替换源 Empty 并创建一个 Undo 步骤]
|
||||
```
|
||||
|
||||
在现有整图 Job 上增加明确的深度产物选择,Depth Plane 使用 Z,Depth Surface 使用 VECTOR;两者均从一次预测生成 Tangent Normal。Blender 侧将转换类型随请求保存至响应阶段,据此选择节点组和结果标识。复用 `common` 的图片加载、深度尺度、材质与对象替换函数;Server 只引用自身目录内的共享 Artifact 层。
|
||||
|
||||
### 基础网格与节点接口
|
||||
|
||||
Depth Surface 使用 `create_mesh_plane_mesh()` 和 `build_mesh_plane_matrix()`,保持整张图片的矩形边界、中心原点、UV 和材质槽。节点内部调用 `O Mesh Plane`,以 Thickness 0 生成基础曲面;同一 Subdivide 下,它与普通 Plane 的零厚度网格具有相同顶点、面连接与 UV。Alpha 参与材质显示,模型 Validity 用于数据有效性处理,几何始终覆盖完整矩形。
|
||||
|
||||
| 输入 | 默认值 | 语义 |
|
||||
| --- | --- | --- |
|
||||
| Subdivide | 6,范围 0–10 | 沿用 O Mesh Plane 的细分 |
|
||||
| Thickness | 0,最小 0 | 均匀曲面壳厚度,DISTANCE |
|
||||
| Depth Scale | 1,最小 0 | 平面到相机曲面的缩放,可大于 1 |
|
||||
| Normal Smooth | 50,范围 0–50 | 厚度方向的法线平滑次数,放在 Options |
|
||||
| Reference Depth | Metadata reference_depth × Uniform Scale | 局部深度基准,DISTANCE,放在 Options |
|
||||
|
||||
Geometry 为结构输入;Depth Image 与 Uniform Scale 为隐藏数据输入。所有修改器输入使用 SINGLE。
|
||||
|
||||
### 投影与厚度
|
||||
|
||||
沿用 Depth Cutout 的 VECTOR Depth 坐标约定:将采样相机 XYZ 乘以 Uniform Scale,翻转 Y 得到局部 XY,局部 Z 为 `(Reference Depth − scaled_camera_z) × Depth Scale`。局部 XY 从基础矩形位置按 Depth Scale 插值到缩放后的相机 XY;0 为原平面,1 为完整投影,大于 1 为外推。Reference Depth 使用与采样相同的局部单位。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Base[O Mesh Plane 零厚度矩形细分] --> Projection[相机 XY 插值与相对 Z 深度]
|
||||
Texture[VECTOR Depth × Uniform Scale] --> Projection
|
||||
Projection --> Thickness{Thickness}
|
||||
Thickness -- 0 --> Surface[单层曲面]
|
||||
Thickness -- 大于 0 --> Shell[沿平滑法线向内生成均匀壳并连接外边界]
|
||||
Surface --> Output[保留 UV 与材质,清理临时属性]
|
||||
Shell --> Output
|
||||
```
|
||||
|
||||
提取现有曲面分支中可复用的投影、均匀厚度节点构建函数,按业务职责集中在共用构建模块;新组只组合自身需要的步骤。零厚度保持单层矩形拓扑,正厚度保留投影正面、沿内侧扩展背面并连接四周。厚度采用正面 POINT 法线,按 Normal Smooth 迭代次数进行邻域平滑后归一化,使对应正背面顶点之间的距离等于 Thickness。
|
||||
|
||||
### 验证边界
|
||||
|
||||
以合成相机 XYZ 纹理验证投影和坐标方向,用相同 Subdivide 的普通 Plane 对照基础拓扑与 UV;使用非方图、偏移 Image Empty、旋转缩放矩阵验证定位。检查五项可见参数、隐藏数据输入、SINGLE、材质槽和临时属性清理。
|
||||
|
||||
节点布局按实际 Blender 绘制尺寸检查总览和局部,几何主链连续、计算支线就近接入。实现阶段按项目总则只运行测试;资产构建、资产视觉验收和节点资产文档同步作为后续发布准备,当前提案不执行构建。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 相机纹理的 Y 方向或中心对齐错误会使结果镜像、偏移 → 使用带非中心主点的合成相机数据验证,并保持与现有 Depth Cutout 相同的投影公式。
|
||||
- 深度不连续处的矩形面会被拉伸 → 将连续矩形拓扑作为明确输出约定,用阶跃深度验证连接关系稳定。
|
||||
- 新增类型和共享 Job 分支可能影响现有转换 → 检查注册与逆序注销、Z/VECTOR 产物选择、单次推理及 Undo 回归。
|
||||
- 工作区同时调整 Cutout 节点 → 实施前核对当前构建函数和 Shape 设置,在现有修改基础上提取共用步骤。
|
||||
@@ -0,0 +1,30 @@
|
||||
## Why
|
||||
|
||||
整图相机投影曲面需要与普通 Plane 一样稳定的矩形基础网格,以及少量直接的深度控制。将它作为独立转换入口,也让 Cutout 的交互集中在用户圈选区域。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**:完全删除 Cutout 双击整图生成入口,包括快捷键、`full_image` 属性和整图 Selection 构造分支。
|
||||
- 新增 **Convert to Depth Surface**,与 Convert to Plane、Convert to Depth Plane 并列,用完整图片生成相机投影深度曲面。
|
||||
- 新增 `O Image Depth Surface`:基础网格复用普通 Plane 的矩形、UV、中心原点和 `O Mesh Plane` 细分,只公开 `Subdivide`、`Thickness`、`Depth Scale`、`Reference Depth` 四项用户参数。
|
||||
- 相机投影沿用 Depth Cutout 的坐标与深度缩放语义;厚度采用均匀曲面壳,默认零厚度。
|
||||
- 整图转换复用现有 MoGe-2 单次推理、材质与对象替换流程。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `projected-depth-surface`: 整图 Depth Surface 转换、矩形基础网格、相机投影和精简节点接口。
|
||||
- `cutout-lasso-entry`: Cutout 通过有效套索进入 Shape 选择,彻底移除双击整图入口。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。当前 `openspec/specs/` 尚无已归档规范;本次独立规定新增及调整后的行为。
|
||||
|
||||
## Impact
|
||||
|
||||
- `operators/cutout_tool/main.py` 及其交互测试。
|
||||
- `operators/convert_to_plane/`、`menu.py`、`__init__.py` 的转换入口、结果创建与类型注册。
|
||||
- `common/depth.py` 等共用深度数据处理;按需扩展整图 Job 的产物选择,保持后端模块边界。
|
||||
- `tools/node_assets/` 的构建和验证、新节点组及节点测试;实施阶段按项目约束执行测试,节点资产构建和产物更新单独安排。
|
||||
- 无新增第三方依赖;实现以当前工作区为基线,衔接正在进行的 Cutout Shape 调整。
|
||||
@@ -0,0 +1,23 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Cutout starts shape generation from a lasso
|
||||
Cutout SHALL 在用户提交有效套索后打开 Shape Pie Menu,并将圈选路径传给 Shape 生成入口。
|
||||
|
||||
#### Scenario: Submit a lasso
|
||||
- **WHEN** 用户在 active Image Empty 上完成有效 Cutout 套索
|
||||
- **THEN** 系统打开当前可用 Shape 菜单并传递该套索路径
|
||||
|
||||
### Requirement: Full-image double-click entry is removed
|
||||
系统 MUST 删除 Cutout 的双击整图 keymap、`full_image` Property、整图 Selection 构造分支及专属调用代码。双击 MUST NOT 打开整图 Shape 菜单或提交整图生成任务。
|
||||
|
||||
#### Scenario: Double-click an image
|
||||
- **WHEN** Cutout 已激活,用户在 Image Empty 上双击且未形成有效套索
|
||||
- **THEN** 不打开 Shape 菜单、不创建对象、不提交生成 Job
|
||||
|
||||
#### Scenario: Double-click viewport space
|
||||
- **WHEN** active Image Empty 存在,用户在图片外空白区域双击
|
||||
- **THEN** 不启动整图 Cutout,选择处理沿用共享图片手势规则
|
||||
|
||||
#### Scenario: Inspect registration
|
||||
- **WHEN** 检查 Cutout 的 Operator Property 和 WorkspaceTool keymap
|
||||
- **THEN** 不存在 `full_image` 或用于整图生成的 `DOUBLE_CLICK` 绑定
|
||||
@@ -0,0 +1,85 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Full-image depth surface has a conversion entry
|
||||
系统 SHALL 提供 `anyimage.convert_to_depth_surface`,界面显示 Convert to Depth Surface,与 Plane、Depth Plane 并列;该入口 SHALL 从当前静态 Image Empty 创建完整图片的相机投影曲面。
|
||||
|
||||
#### Scenario: Convert a still image
|
||||
- **WHEN** 当前 Image Empty 是静态图片且 AI 环境可用
|
||||
- **THEN** 执行一次完整图片转换,成功后结果沿用源对象名称并替换源 Empty
|
||||
- **AND** 一次 Undo 恢复转换前状态
|
||||
|
||||
#### Scenario: Reject unsupported media
|
||||
- **WHEN** 输入为多帧 Movie 或 Sequence
|
||||
- **THEN** 提示仅支持静态图片,不提交推理任务
|
||||
|
||||
#### Scenario: AI setup is required
|
||||
- **WHEN** AI 环境未就绪
|
||||
- **THEN** 沿用现有 AI 设置流程,源 Empty 保持可用
|
||||
|
||||
### Requirement: Base geometry matches Plane conversion
|
||||
Depth Surface SHALL 使用普通 Convert to Plane 的基础 Mesh 创建、中心矩阵、UV 和 O Mesh Plane 细分。相同 Subdivide 下投影前零厚度几何 MUST 与普通 Plane 的零厚度几何一致。整图矩形拓扑 SHALL 覆盖透明区域及深度跳变区域。
|
||||
|
||||
#### Scenario: Compare planar meshes
|
||||
- **WHEN** 对同一图片以相同 Subdivide 创建普通 Plane 和 Thickness 0、Depth Scale 0 的 Depth Surface
|
||||
- **THEN** 两者顶点位置、面连接、UV 和材质索引一致
|
||||
|
||||
#### Scenario: Preserve placement
|
||||
- **WHEN** 源 Image Empty 具有非中心显示偏移以及平移、旋转、缩放
|
||||
- **THEN** Depth Scale 0、Thickness 0 的结果四角与源显示矩形的世界空间四角一致
|
||||
- **AND** 对象原点位于显示矩形中心
|
||||
|
||||
#### Scenario: Preserve rectangular coverage
|
||||
- **WHEN** 图片包含透明孔洞或纹理深度跳变
|
||||
- **THEN** 基础网格仍为完整矩形细分,Alpha 由材质表达
|
||||
|
||||
### Requirement: Surface controls include normal smoothing
|
||||
`O Image Depth Surface` SHALL 只公开 Subdivide、Thickness、Depth Scale、Reference Depth 以及 Normal Smooth 五项用户控制。默认值 SHALL 分别为 6、0、1 和 Metadata reference_depth 乘以 Uniform Scale。Geometry SHALL 为结构输入,Depth Image 与 Uniform Scale SHALL 为隐藏数据输入。输入 MUST 使用 SINGLE;Thickness、Reference Depth MUST 使用 DISTANCE。
|
||||
|
||||
#### Scenario: Inspect a new modifier
|
||||
- **WHEN** 创建 Depth Surface 修改器
|
||||
- **THEN** 用户看到规定的五项控制,Reference Depth 与 Normal Smooth 位于 Options;Normal Smooth 默认 50,范围 0–50
|
||||
- **AND** 不出现 Cutout 的 Mode、Split、Balloon、Inflation、轮廓或轴向控制
|
||||
|
||||
### Requirement: Geometry follows camera projection
|
||||
Depth Surface SHALL 按现有 Depth Cutout 的相机 XYZ 坐标约定投影。采样 XYZ 乘以 Uniform Scale 后,XY SHALL 以 Depth Scale 从基础平面插值至相机 X、负相机 Y;Z SHALL 为 `(Reference Depth − scaled_camera_z) × Depth Scale`。
|
||||
|
||||
#### Scenario: Full projection
|
||||
- **WHEN** Thickness 为 0、Depth Scale 为 1,输入合成相机 XYZ 纹理
|
||||
- **THEN** 每个顶点的位置符合相机投影公式,包括 Y 方向与深度基准
|
||||
|
||||
#### Scenario: Adjust depth strength
|
||||
- **WHEN** Depth Scale 分别为 0、0.5、1 和大于 1 的值
|
||||
- **THEN** XY 与 Z 按同一缩放语义连续变化,0 恢复基础平面,大于 1 正常外推
|
||||
|
||||
#### Scenario: Adjust reference depth
|
||||
- **WHEN** Reference Depth 增加局部距离 d,Depth Scale 为 s
|
||||
- **THEN** 曲面 Z 增加 d × s,投影 XY 保持不变
|
||||
|
||||
### Requirement: Thickness forms a uniform surface shell
|
||||
Thickness 为 0 时系统 SHALL 输出单层曲面;Thickness 大于 0 时 SHALL 保留投影正面,按正面 POINT 法线按 Normal Smooth 进行邻域平滑并归一化后的方向向内生成背面并连接矩形边界。
|
||||
|
||||
#### Scenario: Zero thickness
|
||||
- **WHEN** Thickness 为 0
|
||||
- **THEN** 仅存在一层曲面,无重复背面或退化侧壁
|
||||
|
||||
#### Scenario: Positive thickness
|
||||
- **WHEN** 对平坦深度输入设置正 Thickness
|
||||
- **THEN** 对应正背面顶点之间的距离等于 Thickness,边界闭合且法线朝向一致
|
||||
|
||||
### Requirement: One prediction produces complete image artifacts
|
||||
整图 Depth Surface SHALL 通过共用 Artifact 层执行一次 MoGe-2 推理,生成 VECTOR Depth、Metadata 与 Tangent Normal,并保留完整 Color。Blender SHALL 在主线程加载、Pack 并设置图片色彩空间,将 Normal 用于材质,将 VECTOR Depth 用于几何。
|
||||
|
||||
#### Scenario: Complete a generation job
|
||||
- **WHEN** 整图 Depth Surface Job 成功
|
||||
- **THEN** 只执行一次推理,并返回 `vector_depth`、`depth_metadata`、`tangent_normal` 及完整颜色产物
|
||||
|
||||
#### Scenario: Preserve existing depth plane generation
|
||||
- **WHEN** 用户执行现有 Convert to Depth Plane
|
||||
- **THEN** 继续生成 Z Depth 与 Tangent Normal,并创建固定底面浮雕
|
||||
|
||||
### Requirement: Node assets preserve geometry data
|
||||
新节点组 SHALL 从 `assets/O_AnyImage.blend` 按名称加载。节点求值 SHALL 保留 UV 与第 0 个材质槽,并清理自身临时属性;构建验证 SHALL 检查接口、实际几何与 Blender 中的节点布局。
|
||||
|
||||
#### Scenario: Evaluate the asset
|
||||
- **WHEN** 加载并求值 `O Image Depth Surface`
|
||||
- **THEN** 结果保留 UV、用户属性和材质,不残留自身 `o_depth_*` 临时属性
|
||||
@@ -0,0 +1,31 @@
|
||||
## 1. 删除 Cutout 双击整图入口
|
||||
|
||||
- [x] 1.1 删除 Cutout 的 DOUBLE_CLICK 整图绑定、full_image Property、整图 Selection 分支及失去用途的 import,保留共享套索交互。
|
||||
- [x] 1.2 调整交互和注册测试,验证图片内外双击均不打开 Shape 菜单或提交任务,有效套索继续生成 Shape。
|
||||
|
||||
## 2. 新增整图 Depth Surface 转换
|
||||
|
||||
- [x] 2.1 增加 ConvertToDepthSurface、菜单入口和正逆序注册,验证静态图片、AI 环境及忙碌状态检查。
|
||||
- [x] 2.2 扩展共用整图 Job 的 Z/VECTOR 产物选择及 Blender 响应分派,保持普通 Depth Plane 的 Z 路径;验证 VECTOR Depth 和 Tangent Normal 来自一次推理。
|
||||
- [x] 2.3 复用 common 的图片、深度、材质及对象收尾函数创建 Depth Surface;共用 Plane 的基础 Mesh 和中心矩阵,初始化四项参数及隐藏深度数据。
|
||||
- [x] 2.4 验证完整颜色、材质槽、深度与法线 Pack、源对象替换、失败清理和单步 Undo。
|
||||
|
||||
## 3. 构建定义与几何验证
|
||||
|
||||
- [x] 3.1 按业务职责提取共用相机投影和均匀厚度构建步骤,新增 O Image Depth Surface 构建定义,并接入节点组构建和验证索引。
|
||||
- [x] 3.2 以 O Mesh Plane 的零厚度输出生成完整矩形基础网格,设置 Subdivide、Thickness、Depth Scale、Reference Depth 以及隐藏数据接口,检查 SINGLE 与 socket subtype。
|
||||
- [x] 3.3 增加合成纹理几何测试:同级 Plane 拓扑与 UV 对照、Depth Scale 0/0.5/1/>1、Reference Depth、相机 Y 方向和非中心主点。
|
||||
- [x] 3.4 增加零厚度单层、正厚度闭合、透明区域完整矩形、深度跳变连接、材质和临时属性清理验证。
|
||||
- [x] 3.5 编排新节点组布局与验证入口,覆盖未用输出隐藏、主链及支线间距;记录发布准备时所需的实际绘制尺寸视觉验收项目。
|
||||
|
||||
## 4. 回归与交付检查
|
||||
|
||||
- [x] 4.1 运行相关转换、交互、注册、Server Job 和节点构建定义测试,修复回归;检查现有 Depth Plane 和圈选 Cutout 的行为。
|
||||
- [x] 4.2 运行 uv run pytest,记录测试结果及需要 Blender 或已构建新资产才能完成的验收项。按项目总则,代码实施只运行测试,资产构建、二进制产物和节点资产文档同步留给发布准备。
|
||||
|
||||
|
||||
发布准备验收:构建节点资产后,在 Blender 节点编辑器按实际绘制尺寸检查新组总览、投影支线、厚度分支及旁路;检查修改器四项参数的实际显示,并对已发布资产运行几何验证。本次不生成资产或文档产物。
|
||||
|
||||
测试记录:全量测试两次均为 723 passed、2 xfailed、62 subtests passed;显式调用 pytest.main 返回 0。两次完整进程均在 pytest 返回后以退出码 1 结束,退出阶段异常尚未定位。新曲面几何、接口、对象定位及失败清理测试全部通过。OpenSpec strict 校验通过。
|
||||
|
||||
资产更新记录:已按用户要求运行 build_node --skip-tests,Blender 4.5.10 LTS 构建及三项资产验证均成功。更新后资产与转换回归 53 passed;构建定义和磁盘发布资产使用相同几何用例验证,31 passed,命令退出码均为 0。已同步节点资产说明。Computer Use 应用授权超时,实际节点编辑器视觉验收仍待完成。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-14
|
||||
@@ -0,0 +1,77 @@
|
||||
## Context
|
||||
|
||||
`O Image Depth Balloon` 当前以原始 Cutout 网格作为对称平面上的轮廓,通过 `o_balloon` 和深度差构建两侧。新需求已经用用户提供的小车图完成独立实验:正面投射后截留正 Z 区域,两侧围绕固定 XY 平面镜像,并以直壁连接对应外边界。
|
||||
|
||||
实验文件位于 `C:/Users/icrdr/AppData/Local/Temp/anyimage-car-symmetry/`:`car-depth-balloon-fast-modes.blend`、`fast_balloon.py`、`performance-results.json`、`mode-validation.json`。这些是可复核的实验材料,正式实现和测试自行提供所需资源。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** 双模式、手势预设、完整对称网格、独立且可复用的节点构建、统一命名。
|
||||
|
||||
**Non-Goals:** 本变更不包含旧资产兼容层、AI 模型调整或自动推断最佳车身对称方向。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 名称与手势
|
||||
|
||||
| 对象 | 名称 |
|
||||
|---|---|
|
||||
| 功能与菜单 | `Depth Symmetry` |
|
||||
| 节点组 | `O Image Depth Symmetry` |
|
||||
| 构建模块与入口 | `image_depth_symmetry.py` / `build_image_depth_symmetry_group()` |
|
||||
| Shape 标识 | `DEPTH_SYMMETRY` |
|
||||
| Mode 顺序 | `Balloon`、`Projected` |
|
||||
|
||||
`Balloon` 描述原有鼓起方式;`Projected` 描述新模式的投射轮廓。功能名称表达两者共同的对称职责,避免用其中一个模式命名整个节点组。
|
||||
|
||||
| 手势 | Depth Cutout(现有) | Depth Symmetry |
|
||||
|---|---|---|
|
||||
| Lasso | Balloon | Balloon |
|
||||
| Polyline | Shell | Projected |
|
||||
|
||||
在 Cutout 对象创建阶段按 gesture 设置 Mode。预设只影响初值,用户仍可直接切换节点 Mode。裸节点默认 Balloon,保持原有入口的默认行为。
|
||||
|
||||
### 直接构建 Projected 正面
|
||||
|
||||
复用 `_position_field` 读取 Depth Image 的相机 XYZ,乘以 Uniform Scale,将 Y 转为现有 Cutout 坐标约定,以 `Reference Depth − metric depth` 构建 Z。Depth Scale 沿用已验证的平面坐标与相机坐标插值,并缩放深度差。
|
||||
|
||||
将 Depth Direction 对应的方向对齐到局部 Z 轴,输出成形坐标中的正面。Reference Depth 和 Depth Direction 共同改变位移与投射后的轮廓;最终镜像面固定在成形坐标 `Z=0`,不把镜像结果转回倾斜平面。既有 Depth Axis 作为统一的最终坐标变换保留。
|
||||
|
||||
复用公共边界平滑,随后在 POINT 域判断负 Z,并在 FACE 域判断是否包含越界顶点。删除包含越界顶点的整面及其失去面的边线、孤点。仅保留正 Z 面,不产生压扁在对称平面上的外围裙边。平面附近使用与几何尺度相适应的小容差处理零厚度退化。
|
||||
|
||||
采用整面删除,因为它只依赖现有网格与字段求值;相较精确裁切,边界具有网格分辨率级别的近似。新分支不使用 Mesh Boolean,也不嵌套 Depth Plane、Depth Cutout 节点组。
|
||||
|
||||
### 镜像与直壁
|
||||
|
||||
先完成正面和平滑,再镜像到负 Z,避免封闭后的全网格平滑破坏直壁。以边的面邻接数识别正面外边界,将每个边界点 `(x,y,z)` 与 `(x,y,-z)` 连接;生成的侧壁沿 Z 轴延伸,其法线垂直于 Z 轴。保留正背面平滑着色,侧壁使用平面着色。
|
||||
|
||||
合并对应顶点,正确处理对称平面上的零厚度边。Double Sided 打开时生成完整封闭网格;关闭时沿用已验证原型的正面预览行为。Balloon 分支原有 Double Sided、Thickness、平滑等语义保持不变。Thickness 继续控制 Balloon 轮廓;Projected 的形体由 Depth Scale、Reference Depth、Depth Direction 控制,不引入未经验证的厚度推断。
|
||||
|
||||
### 复用与模式分派
|
||||
|
||||
复用公共节点接口、深度采样、岛清理、边界平滑、普通平滑和属性函数。节点内部保留直接构建的两条几何分支,通过 Mode 选择;共享最终输出和坐标约定,不通过嵌套节点组包装旧实现。
|
||||
|
||||
Projected 需要局部辅助函数时放在该业务模块;只有确有其他消费者的函数才进入 common。UV 和现有命名属性继续随拓扑传递,新增临时属性需要明确生命周期;源码与保存资产中 Capture Attribute 数量均为零。
|
||||
|
||||
### 性能与验证
|
||||
|
||||
同一小车输入、相同参数与 3 次边界平滑,预热 4 次后连续修改 Reference Depth,记录 24 次依赖图求值的中位数。外部实验的双模式 Projected 分支结果如下:
|
||||
|
||||
| 输入顶点 | 布尔原型 | 双模式直接构建 | 提速 |
|
||||
|---|---:|---:|---:|
|
||||
| 27,860 | 174.9 ms | 30.9 ms | 约 5.7 倍 |
|
||||
| 165,771 | 960.7 ms | 135.6 ms | 约 7.1 倍 |
|
||||
|
||||
这些数值是当前机器的对比证据,不作为跨机器测试断言。正式验证保留同输入、同平滑、同参数的对照,并分别记录 Reference Depth 与 Depth Direction 调整耗时。几何测试覆盖封闭性、顶点流形性、面绕序、XY 镜像、直壁法线、越界删除、空结果及模式切换;Balloon 分支与变更前的基准几何比较。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 整面删除使切口随网格密度离散变化 → 使用已有采样密度控制,比较多个密度与连续参数变化,明确其为近似边界。
|
||||
- 深度噪声可能形成小岛或点接触 → 复用岛清理,测试连通性与顶点流形性,退化处删除无效拓扑。
|
||||
- 在补壁后平滑可能令侧壁弯曲 → Projected 的平滑在正面阶段完成,再镜像和补壁。
|
||||
- 只在中等网格测量可能掩盖扩展成本 → 同时验证普通与加密输入,记录求值而非渲染耗时。
|
||||
- 资产与代码名称失配 → 同步重命名 Shape 标识、模块、校准函数、构建/校验清单和界面文本,构建保存资产后检查旧引用。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
实现时整体替换旧名称并更新相关代码、测试和节点资产,不保留旧路径或别名。通过 `uv run node-group build` 构建并验证 `O_AnyImage.blend`,检查新组存在、旧组不再由构建产出。已保存在用户工程中的旧节点组不执行自动扫描或重写。
|
||||
@@ -0,0 +1,29 @@
|
||||
## Why
|
||||
|
||||
现有 Depth Balloon 始终向原 Cutout 剪影收拢。用户需要另一种对称形体:轮廓随深度投射变化,两侧围绕固定 XY 平面对称,并通过直壁补成完整网格。小车独立实验已验证效果;直接构建版本在保留相同边界平滑时,比布尔原型约快 5.7–7.1 倍。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 将功能命名为 **Depth Symmetry**,节点组命名为 `O Image Depth Symmetry`,提供 `Balloon / Projected` 两种模式。
|
||||
- `Balloon` 保持原有行为;`Projected` 直接采样深度、调整投射方向、删除越界面、镜像并补直壁。
|
||||
- Lasso 默认使用 `Balloon`,Polyline 默认使用 `Projected`;创建后仍可通过 Mode 切换。与现有 Depth Cutout 的 `Lasso → Balloon / Polyline → Shell` 对应。
|
||||
- 正面保持在局部 `Z≥0`;开启 Double Sided 后,两侧关于局部 `Z=0` 对称,外边界沿 Z 轴连接,形成封闭网格。
|
||||
- 复用公共采样、清理、平滑和节点接口构建函数,新模式独立构建,不使用布尔或依赖 Depth Plane、Depth Cutout 节点组。
|
||||
- **BREAKING**:将节点资产名、Shape 标识 `DEPTH_BALLOON`、构建模块及相关代码名称统一为 Depth Symmetry,不保留旧名转发或兼容资产。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `image-depth-symmetry`: 双模式深度对称节点、直壁封闭、手势预设、命名和性能约束。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。当前 `openspec/specs` 尚无可修改的已归档规格。
|
||||
|
||||
## Impact
|
||||
|
||||
- `tools/nodes/groups/image_depth_balloon.py`、相关公共构建函数、节点组构建与校验入口。
|
||||
- `src/anyimage/operators/cutout_tool` 的 Shape 菜单、手势分派、深度校准与对象创建,以及相关界面文本。
|
||||
- `src/anyimage/assets/O_AnyImage.blend` 和对应节点、对象创建、资产清单测试。
|
||||
- 实现时运行相关测试与 `uv run node-group build`,更新并验证源码及资产;不新增依赖。
|
||||
@@ -0,0 +1,74 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Unified depth symmetry identity
|
||||
系统 SHALL 使用菜单名称 `Depth Symmetry`、节点组名称 `O Image Depth Symmetry` 和 Shape 标识 `DEPTH_SYMMETRY`,节点 Mode SHALL 按 `Balloon / Projected` 顺序提供选择。
|
||||
|
||||
#### Scenario: Load the node asset
|
||||
- **WHEN** 用户创建 Depth Symmetry 对象
|
||||
- **THEN** 系统从节点资产加载 `O Image Depth Symmetry`,Mode 包含 Balloon 和 Projected
|
||||
|
||||
### Requirement: Gesture selects the initial mode
|
||||
系统 SHALL 为 Lasso 创建结果选择 Balloon,为 Polyline 创建结果选择 Projected,并允许用户随后手动切换 Mode。
|
||||
|
||||
#### Scenario: Lasso preset
|
||||
- **WHEN** 用户通过 Lasso 选择区域并创建 Depth Symmetry
|
||||
- **THEN** Mode 初始值为 Balloon
|
||||
|
||||
#### Scenario: Polyline preset
|
||||
- **WHEN** 用户通过 Polyline 选择区域并创建 Depth Symmetry
|
||||
- **THEN** Mode 初始值为 Projected,Depth Cutout 自身的 Polyline → Shell 关系保持不变
|
||||
|
||||
#### Scenario: Switch the generated object's mode
|
||||
- **WHEN** 用户修改已创建对象的 Mode
|
||||
- **THEN** 同一份输入网格和深度数据按所选模式重新求值,不受创建手势锁定
|
||||
|
||||
### Requirement: Preserve balloon behavior
|
||||
Balloon 模式 SHALL 保持原 Depth Balloon 的几何与参数行为,在对称平面向原 Cutout 剪影收拢。
|
||||
|
||||
#### Scenario: Compare the original mode
|
||||
- **WHEN** 同一输入、深度图和原有参数分别进入旧基准与新节点的 Balloon 模式
|
||||
- **THEN** 输出拓扑和位置在浮点容差内一致,包括 Double Sided、Thickness、平滑及 Depth Axis 的行为
|
||||
|
||||
### Requirement: Projected positive surface
|
||||
Projected 模式 SHALL 独立读取深度,使用 Reference Depth 和 Depth Direction 调整正面的置换与投射方向。成形坐标中的正面 MUST 保持在 `Z≥0`,越界面 SHALL 被删除。
|
||||
|
||||
#### Scenario: Adjust both depth controls
|
||||
- **WHEN** 用户修改 Reference Depth 或 Depth Direction
|
||||
- **THEN** 正面形体与投射轮廓重新求值,镜像平面保持在成形坐标的 `Z=0`
|
||||
|
||||
#### Scenario: A face crosses the plane
|
||||
- **WHEN** 正面某个面含有负 Z 顶点
|
||||
- **THEN** 该面及失去面支撑的边线、孤点被删除,剩余结果不包含越界正面或外围压平裙边
|
||||
|
||||
#### Scenario: Everything is outside
|
||||
- **WHEN** 参数使全部输入面越界
|
||||
- **THEN** 输出为空几何,求值正常结束
|
||||
|
||||
### Requirement: Close the projected symmetric volume with straight walls
|
||||
Projected 开启 Double Sided 时 SHALL 关于成形坐标 `Z=0` 镜像,并沿 Z 轴直线连接对应外边界,生成封闭网格。正面平滑 SHALL 在镜像补壁之前完成。
|
||||
|
||||
#### Scenario: Complete a car volume
|
||||
- **WHEN** 合法正面存在外边界且 Double Sided 打开
|
||||
- **THEN** 输出网格没有开放边、异常面邻接或顶点非流形,面绕序一致;每个 `(x,y,z)` 存在镜像对应点 `(x,y,-z)`
|
||||
|
||||
#### Scenario: Check straight walls after smoothing
|
||||
- **WHEN** 用户开启 Projected 平滑后生成双面网格
|
||||
- **THEN** 外边界仍沿 Z 轴直线连接,侧壁法线与 Z 轴垂直,平滑不使侧壁弯曲
|
||||
|
||||
#### Scenario: Preview only the front
|
||||
- **WHEN** 用户关闭 Projected 的 Double Sided
|
||||
- **THEN** 仅输出非负 Z 正面,用于检查投射和裁去区域
|
||||
|
||||
### Requirement: Direct node construction and shared builders
|
||||
Projected 分支 MUST 直接使用基础节点与公共构建函数,不得使用 Mesh Boolean、Capture Attribute 或依赖 Depth Plane、Depth Cutout 节点组。插件运行时 SHALL 从资产加载节点组。
|
||||
|
||||
#### Scenario: Inspect the built asset
|
||||
- **WHEN** 构建与检查 `O Image Depth Symmetry`
|
||||
- **THEN** 新模式不存在布尔、Capture Attribute 或上述节点组依赖,Depth Symmetry 源码与资产接口一致
|
||||
|
||||
### Requirement: Verify performance on equivalent inputs
|
||||
实现 SHALL 在同一机器、同输入、同平滑和同参数序列下,对比直接构建版本与已验证的布尔原型,分别记录 Reference Depth 和 Depth Direction 更新耗时。
|
||||
|
||||
#### Scenario: Compare ordinary and dense meshes
|
||||
- **WHEN** 对普通与加密网格执行预热后的重复求值
|
||||
- **THEN** 报告两种更新的中位数、输入规模与测试条件,并确认正式双模式 Projected 分支的求值收益,而非以固定毫秒值作为跨机器测试门槛
|
||||
@@ -0,0 +1,29 @@
|
||||
## 1. 统一功能命名
|
||||
|
||||
- [x] 1.1 将构建模块和入口重命名为 `image_depth_symmetry.py` / `build_image_depth_symmetry_group()`,更新构建、校验与测试引用。
|
||||
- [x] 1.2 将菜单、Shape 标识、节点资产名称和深度校准相关名称统一为 Depth Symmetry,清除旧名称的业务引用。
|
||||
|
||||
## 2. 双模式节点
|
||||
|
||||
- [x] 2.1 保留 Balloon 分支与参数语义,新增顺序为 `Balloon / Projected` 的 Mode;复用公共接口、清理、属性及输出坐标处理。
|
||||
- [x] 2.2 直接构建 Projected 深度正面,使 Depth Direction 与 Reference Depth 控制投射,保留固定 XY 对称平面。
|
||||
- [x] 2.3 复用边界平滑与正面平滑,删除含越界点的整面并清理无面支撑拓扑,处理零厚度与空结果。
|
||||
- [x] 2.4 构建正面镜像与直壁补全,处理接缝合并、绕序、平滑着色和 Double Sided 正面预览。
|
||||
- [x] 2.5 整理节点布局,确保直接构建、无布尔、无 Capture Attribute、无复杂几何节点组依赖。
|
||||
|
||||
## 3. 手势预设
|
||||
|
||||
- [x] 3.1 在 Cutout 对象创建阶段设置 `Lasso → Balloon`、`Polyline → Projected`,保留现有 Depth Cutout 的 Balloon / Shell 预设。
|
||||
- [x] 3.2 验证两类手势的默认值、创建后 Mode 切换、深度校准输入及节点资产加载名称。
|
||||
|
||||
## 4. 几何与性能验证
|
||||
|
||||
- [x] 4.1 补充可独立运行的几何测试,覆盖非负正面、双面镜像、封闭性、顶点流形性、面绕序、直壁法线、平滑、空结果和 Depth Axis。
|
||||
- [x] 4.2 将新 Balloon 分支与变更前基准比较,验证原参数行为及模式切换不污染状态。
|
||||
- [x] 4.3 使用同输入、同边界平滑的普通与加密网格,分别测量 Reference Depth 和 Depth Direction 更新,记录预热后中位数及条件。
|
||||
- [x] 4.4 用已确认的小车案例检查视角一致的效果对比,记录整面删除在切口处的分辨率取舍。
|
||||
|
||||
## 5. 资产交付
|
||||
|
||||
- [x] 5.1 更新资产清单和相关测试,运行 `uv run node-group build`,保存并验证 `src/anyimage/assets/O_AnyImage.blend`。
|
||||
- [x] 5.2 运行相关对象创建与节点测试,检查源码、资产中的旧名称及最终差异,确认工作正常退出;不构建文档或打包扩展。
|
||||
@@ -0,0 +1,28 @@
|
||||
# 实现验证
|
||||
|
||||
2026-09-14,本机 Windows;资产由 Blender 4.5.10 LTS 构建和重载校验,几何测试及性能测量使用 bpy 4.5.3。
|
||||
|
||||
## 几何
|
||||
|
||||
- 源码构建和保存资产均验证正面越界删除、完整镜像、边与顶点流形性、面绕序、直壁法线、平滑、空输出和 Depth Axis。
|
||||
- 测试几何尺度为 0.001、1、100,包含倾斜方向和正面平滑。
|
||||
- Lasso / Polyline 默认值、模式往返切换与深度校准输入通过对象创建测试。
|
||||
- 小车 Balloon 分支在默认、单面、零厚度、平滑、坐标轴及方向变化共 5 组参数下,与变更前基准的顶点和面完全一致,最大坐标差为 0。
|
||||
- 同相机、同参数的小车渲染与已确认布尔原型一致。基准顶点到新网格最近顶点距离中位数为 0,P95 为 6.66e-8,最大值为 0.0361;这是顶点距离比较,不是曲面误差界。整面删除的切口精度受输入网格密度影响。
|
||||
|
||||
## 性能
|
||||
|
||||
同一小车网格及深度图,Boundary Smooth=3、Smooth=0、Depth Scale=1、Uniform Scale=1、Double Sided=true。Reference Depth=5.5786533,Depth Direction=(-0.6635724, -0.2783087, 0.6944177)。每次只修改所列参数:参考深度或方向 X 分量增加 `0.002*sin(i*0.71)`;预热 4 次,记录随后 24 次依赖图更新耗时中位数。加密输入通过一次 Simple Subdivision 生成,保留 UV。
|
||||
|
||||
| 输入顶点 | 修改参数 | 布尔原型 | 正式 Projected | 提速 |
|
||||
|---:|---|---:|---:|---:|
|
||||
| 27,860 | Reference Depth | 170.4 ms | 32.8 ms | 5.2 倍 |
|
||||
| 27,860 | Depth Direction | 173.1 ms | 32.6 ms | 5.3 倍 |
|
||||
| 165,771 | Reference Depth | 948.3 ms | 147.0 ms | 6.5 倍 |
|
||||
| 165,771 | Depth Direction | 987.2 ms | 151.5 ms | 6.5 倍 |
|
||||
|
||||
本机复核材料在 `C:/Users/icrdr/AppData/Local/Temp/anyimage-car-symmetry/`:`validate_production.py`、`production-validation.json`、`production-baseline.png`、`production-result.png`、`car-depth-symmetry-production.blend`。性能数字为本机对照证据。
|
||||
|
||||
## 交付检查
|
||||
|
||||
`uv run node-group build` 正常退出,10 个保存节点组通过 Blender 重载校验,附带全量测试 1064 项全部通过(188.83 秒)。源码、测试和构建清单中旧名称引用为零;资产清单验证新组存在且旧组不存在。`openspec validate add-projected-depth-symmetry` 通过。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-09
|
||||
@@ -0,0 +1,55 @@
|
||||
## Context
|
||||
|
||||
现有菜单挂载于对象右键菜单,两个 AI 操作通过 Empty 取得 Image 和播放设置。`common/image.py` 已提供输入准备、结果打包、Image 内容快照和恢复;现有共享判定专用于 Empty。动机见 proposal.md。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
本设计围绕材质 Shader Editor 的 Image Texture 节点完成图片输入、异步目标定位和原位提交;Empty 的现有行为作为回归基线。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 菜单按编辑器和当前节点确定目标
|
||||
|
||||
在 `NODE_MT_context_menu` 注册回调,以材质 Shader Editor、当前编辑树、活动 Image Texture 节点及有效 Image 为显示条件。支持进入材质节点组和固定材质后的编辑上下文,节点编辑器中的无效目标直接判为不可用。新增 `AnyImageTextureNodeMenu`,包含两个图片操作及现有 AI 设置入口;Upscale 标签使用目标图片尺寸。注册与逆序注销对称。
|
||||
|
||||
### 在 common 中集中处理图片编辑目标
|
||||
|
||||
新增职责明确的 `common/image_target.py`,统一取得源 Image、ImageUser、目标校验与结果提交分派。复用两个现有 Operator 和服务端 Job;将结果加载中对 `source_object.data` 的依赖改为显式 Image 参数,并同步修改真实调用点。Empty 特有的显示对齐和共享规则仍由现有提交函数负责,节点提交复用 Image 内容事务函数。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[节点右键选择操作] --> B[解析并捕获节点和源 Image]
|
||||
B --> C[复用输入准备与 AI Job]
|
||||
C --> D[主线程验证原目标仍有效]
|
||||
D --> E[加载并打包处理结果]
|
||||
E --> F{源图有其他实际使用者}
|
||||
F -->|有| G[当前节点绑定独立结果]
|
||||
F -->|无| H[保留 Image 身份并更新内容]
|
||||
G --> I[完成撤销记录和临时资源清理]
|
||||
H --> I
|
||||
```
|
||||
|
||||
### 捕获提交时的节点身份
|
||||
|
||||
在主线程捕获编辑树、节点与源 Image 的运行期身份,以及节点 ImageUser 播放参数。响应在主线程使用捕获目标,校验节点仍属于原树且仍绑定源图;名称只用于提示,防止删除后同名重建误命中。运行期引用仅保存在本地任务状态,Job 请求使用路径、帧参数等普通字典数据。切换活动节点不改变目标;目标删除、源绑定变化或变为只读时报告错误并清理结果。
|
||||
|
||||
### 原位替换保留节点结构并隔离共享图片
|
||||
|
||||
节点独占 Image 时复用内容快照与恢复,保留 Image 身份、名称和 Fake User;存在其他实际 Image 使用者时,仅当前节点绑定新结果,保留原图。共享判定覆盖其他节点树、Empty 和其他数据使用者,忽略 Fake User、编辑器显示和本次临时持有;在响应提交时计算。该规则仅用于节点目标。
|
||||
|
||||
保持节点位置、插值、投影、扩展方式和连线。结果继承源色彩空间并使用现有 alpha 配置;去背景输出透明图片,材质的 Alpha 连线和渲染设置由用户已有材质决定。动画输入沿用现有处理帧数约定,起始帧和播放设置从节点 ImageUser 读取,结果序列以同样时间位置播放。
|
||||
|
||||
### 失败恢复和撤销采用完整提交边界
|
||||
|
||||
节点提交覆盖 Image 内容、绑定和 ImageUser 设置的快照、恢复与清理。沿用两个操作现有撤销入口,验证异步成功仅产生一个有效图片编辑撤销步骤,重做恢复结果且无需再次推理。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [节点组被多个材质实例使用] → 编辑共享节点树内的节点会作用于所有该组实例,遵循 Blender 节点树共享语义;图片隔离以节点身份为边界。
|
||||
- [共享判定遗漏其他 Image 使用者] → 结合实际数据引用校验,测试跨材质、跨节点组、Empty 及 Fake User 情况。
|
||||
- [异步完成时上下文已变化] → 使用捕获的运行期身份并在提交前验证;覆盖改名、删除和重新绑定图片。
|
||||
- [Image 内容原位修改的撤销与打包行为] → 使用真实 Blender 测试验证撤销重做、保存重开、动画和失败恢复。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
先集中目标解析与图片结果加载,再接入菜单和节点提交,最后完成注册、回归及 Blender 验证。无需数据迁移;回退代码后,已有处理结果继续作为普通 Image 使用。
|
||||
@@ -0,0 +1,24 @@
|
||||
## Why
|
||||
|
||||
材质的 Image Texture 节点目前无法直接使用 AnyImage 去背景和放大。用户需要在节点右键菜单中完成处理,并像 Empty Image 一样在当前位置使用结果。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 在材质 Shader Editor 的有效 Image Texture 节点右键菜单增加 AnyImage 菜单,提供 Remove Background 和 Upscale。
|
||||
- 复用现有 AI 环境准备、模型配置、输入限制、任务进度和图片处理流程。
|
||||
- 处理成功后原位更新目标节点图片,保留节点、连线及设置;单独使用的图片保留 Image 身份,共享图片仅替换当前节点的绑定。
|
||||
- 为异步目标校验、失败恢复、撤销重做和菜单注册增加覆盖。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `texture-node-image-actions`: 材质 Image Texture 节点的 AI 图片操作入口及原位提交语义。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
涉及 `src/anyimage/menu.py`、扩展注册入口、Remove Background / Upscale Operator、`common` 图片输入与提交逻辑及对应测试。服务端沿用现有 Job 协议,依赖和节点资产保持现状。
|
||||
@@ -0,0 +1,85 @@
|
||||
## Purpose
|
||||
|
||||
为材质 Shader Editor 中的 Image Texture 节点提供直接去背景与放大的操作入口,让用户在现有节点位置使用处理结果,并明确共享图片、异步完成和撤销恢复的可见行为。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Material image texture context actions
|
||||
|
||||
系统 SHALL 在材质 Shader Editor 中为具有有效图片的活动 Image Texture 节点提供 AnyImage 右键菜单,包含 Remove Background 和 Upscale;一次操作 SHALL 处理当前节点。
|
||||
|
||||
#### Scenario: An image texture is active
|
||||
- **WHEN** 用户在材质编辑上下文中对有图片的活动 Image Texture 节点打开右键菜单
|
||||
- **THEN** 菜单提供两项操作,Upscale 显示预期 2× 输出尺寸
|
||||
|
||||
#### Scenario: The target is unavailable
|
||||
- **WHEN** 当前上下文为 World、Compositor、Geometry Nodes,或活动节点不是 Image Texture、没有图片、不可编辑
|
||||
- **THEN** 系统不提供可执行的节点图片操作,且不使用活动 Empty 作为替代目标
|
||||
|
||||
#### Scenario: A material or node group is being edited
|
||||
- **WHEN** 用户固定材质或进入其节点组并对有效 Image Texture 发起操作
|
||||
- **THEN** 系统处理该编辑树内的目标节点图片
|
||||
|
||||
### Requirement: Existing AI processing settings apply
|
||||
|
||||
系统 SHALL 沿用 Empty Image 操作的 AI 环境准备、模型选择、任务状态和输入限制;Upscale SHALL 使用当前模型输出 2× 结果。
|
||||
|
||||
#### Scenario: Environment preparation is needed
|
||||
- **WHEN** 用户点击操作且 AI 环境或模型尚未准备好
|
||||
- **THEN** 系统进入现有准备流程
|
||||
|
||||
#### Scenario: Upscale is unavailable
|
||||
- **WHEN** 服务忙碌或图片长边达到当前最大 AI 输入尺寸
|
||||
- **THEN** Upscale 不可执行;服务忙碌时 Remove Background 也不可执行
|
||||
|
||||
#### Scenario: An animated texture is processed
|
||||
- **WHEN** 目标图片为现有流程支持的视频或序列
|
||||
- **THEN** 系统依据节点播放设置和现有处理帧数约定取得输入,结果序列保留对应播放时间位置
|
||||
|
||||
### Requirement: Results replace the image at the target node
|
||||
|
||||
系统 SHALL 在成功后保留目标节点、位置、连线和纹理设置,并在该节点使用结果图片。源图独占时 SHALL 保留 Image 身份、名称与 Fake User;源图有其他实际使用者时 SHALL 仅替换当前节点图片绑定并保持其他使用者和原图不变。结果 SHALL 继承源色彩空间、正确保存透明信息并支持保存重开。
|
||||
|
||||
#### Scenario: A sole image is processed
|
||||
- **WHEN** 目标节点独占源图并完成去背景或放大
|
||||
- **THEN** 原 Image 内容及尺寸更新为结果,身份和名称保留,临时结果被释放
|
||||
|
||||
#### Scenario: Other users share the image
|
||||
- **WHEN** 另一纹理节点或 Empty 等数据使用者仍引用源图
|
||||
- **THEN** 当前节点使用独立处理结果,其他使用者继续使用未改变的原图
|
||||
|
||||
#### Scenario: Only a fake user is present
|
||||
- **WHEN** 源图除当前节点外只有 Fake User 或编辑器显示引用
|
||||
- **THEN** 系统保留源 Image 身份并更新内容
|
||||
|
||||
#### Scenario: Background removal succeeds
|
||||
- **WHEN** 用户对节点执行 Remove Background 成功
|
||||
- **THEN** 图片带有处理后的透明信息,材质原有连线和渲染设置保留
|
||||
|
||||
### Requirement: Asynchronous completion keeps the original target
|
||||
|
||||
系统 SHALL 将结果提交到发起任务时的节点,并在提交前验证原节点和图片绑定仍有效。
|
||||
|
||||
#### Scenario: Active selection changes
|
||||
- **WHEN** 处理期间用户切换编辑器、材质或活动节点
|
||||
- **THEN** 结果仍提交到原节点
|
||||
|
||||
#### Scenario: The target is renamed
|
||||
- **WHEN** 原节点或所属材质在任务期间改名且绑定仍有效
|
||||
- **THEN** 结果提交到同一原节点
|
||||
|
||||
#### Scenario: The original target is invalid
|
||||
- **WHEN** 原节点被删除、同名重建、变为只读或改绑图片
|
||||
- **THEN** 系统报告目标失效,保留现有节点和图片状态,并清理本次结果
|
||||
|
||||
### Requirement: Edits support failure recovery and undo
|
||||
|
||||
系统 SHALL 在失败或取消时保持或恢复原图片内容、节点绑定和播放设置,并清理临时资源。成功操作 SHALL 支持一次撤销还原及重做恢复。
|
||||
|
||||
#### Scenario: Commit fails
|
||||
- **WHEN** 写入图片内容或绑定、配置播放设置期间发生错误
|
||||
- **THEN** 节点绑定与原图完整恢复,其他使用者保持不变
|
||||
|
||||
#### Scenario: The user undoes and redoes an edit
|
||||
- **WHEN** 用户撤销一次成功操作,然后重做
|
||||
- **THEN** 撤销恢复原图、尺寸、绑定和播放设置,重做恢复处理结果且无需再次推理
|
||||
@@ -0,0 +1,20 @@
|
||||
## 1. 图片编辑目标
|
||||
|
||||
- [x] 1.1 在 common 集中解析 Empty 与材质纹理节点目标,捕获源 Image、编辑树身份和播放参数;测试材质固定、节点组编辑、无效上下文和节点改名。
|
||||
- [x] 1.2 将图片结果加载改为显式 Image 输入并更新真实调用点;通过现有 Empty 图片处理测试,检查旧调用签名无残留。
|
||||
|
||||
## 2. 原位提交与任务接入
|
||||
|
||||
- [x] 2.1 实现节点图片事务提交,复用内容快照、恢复与打包;测试独占 Image 身份、Fake User、共享节点/Empty 隔离、连线设置保留及失败回滚。
|
||||
- [x] 2.2 接入现有 Remove Background 和 Upscale 的目标解析、输入准备和响应;测试模型配置、2× 尺寸、输入限制、动画帧参数及任务清理。
|
||||
- [x] 2.3 在异步响应中校验原树、节点及源绑定;测试切换选择后仍命中原目标,删除、同名重建和改绑图片时安全退出。
|
||||
|
||||
## 3. 右键菜单
|
||||
|
||||
- [x] 3.1 添加纹理节点 AnyImage 菜单,复用两项操作及 AI 设置入口,显示目标图片的 Upscale 尺寸;测试可见条件、服务忙碌及环境准备入口。
|
||||
- [x] 3.2 接入节点菜单回调与菜单类型注册、逆序注销;通过注册测试验证重复启用与禁用没有遗留回调。
|
||||
|
||||
## 4. 综合验证
|
||||
|
||||
- [x] 4.1 使用真实 Blender 测试验证两项异步操作的撤销重做、图片打包保存重开、共享节点组实例与动画播放,确认透明结果及原节点结构正确。
|
||||
- [x] 4.2 运行 `uv run pytest` 全量测试并确认正常退出,检查最终差异与旧引用;保留既有未提交修改,不构建文档或产物。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-14
|
||||
@@ -0,0 +1,45 @@
|
||||
## Context
|
||||
|
||||
Convert to Panorama currently validates `width == height * 2` before it exports the source color image. The shared color-input helper already accepts pixel bounds and uses the resulting file for both material color and AI analysis, so the crop can be applied once at the Blender boundary without changing the server job or duplicating image processing.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Derive a deterministic, centered, exact 2:1 integer-pixel crop from any usable static source image.
|
||||
- Use the same cropped pixels for AI analysis and the generated panorama material.
|
||||
- Warn users whenever the prepared input omits source pixels.
|
||||
- Keep exact 2:1 inputs unchanged.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Padding, stretching, resampling, or content-aware crop positioning.
|
||||
- Repairing an image that is not actually an equirectangular panorama.
|
||||
- Changing server inference, panorama fusion, output metadata, or node groups.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Compute the largest centered exact 2:1 pixel rectangle
|
||||
|
||||
Let `crop_height = min(source_height, source_width // 2)` and `crop_width = crop_height * 2`. Center these dimensions independently within the source using integer offsets. This retains the maximum possible source area, handles both wide and tall images, and removes one unavoidable column from odd-width inputs. Integer floor placement gives deterministic bounds when the discarded pixels cannot be split equally.
|
||||
|
||||
Padding would preserve all pixels but introduce artificial panorama regions. Resizing would distort the projection. Both are less faithful than an explicit crop.
|
||||
|
||||
### Select bounds before preparing the shared color input
|
||||
|
||||
Add a small reusable image-bound calculation in the appropriate `common` image module, then pass those bounds to `prepare_material_color_input`. `material_analysis_input` will continue to derive its input from that exported color file. This guarantees that inference and the material see identical framing while retaining existing HDR, color-space, alpha, temporary-file, and cleanup behavior.
|
||||
|
||||
Cropping only the analysis preview was rejected because the generated material and inferred geometry would then use different angular content.
|
||||
|
||||
### Warn at the public operator boundary
|
||||
|
||||
`ConvertToPanorama` will emit one Blender `WARNING` report after determining that the crop differs from the source. The message will state original and cropped pixel dimensions. Exact 2:1 sources will not report a warning. Animated images and inputs for which the computed crop has zero width or height remain errors.
|
||||
|
||||
The warning belongs at the public operator boundary because that is where source dimensions and user feedback are both available; it does not need to become part of the job protocol.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Centered cropping can remove important edge content] → Make the crop explicit through a warning with dimensions; interactive crop selection remains outside this change.
|
||||
- [Odd discarded pixel counts make perfect geometric centering impossible] → Use stable integer floor offsets and test the exact bounds.
|
||||
- [Very narrow images cannot yield a positive 2:1 integer crop] → Keep a clear validation error for widths below two pixels or otherwise invalid dimensions.
|
||||
- [Warning timing could imply success before setup or submission later fails] → Report the warning as part of input preparation, while preserving normal error reporting for subsequent failures.
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
Convert to Panorama currently rejects any image whose pixel dimensions are not exactly 2:1, so even a nearly compliant panorama cannot be processed. Panorama conversion should accept arbitrary static image aspect ratios by preparing a valid 2:1 input automatically while making the loss of edge content visible to the user.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Accept static panorama source images regardless of their original aspect ratio.
|
||||
- Center-crop the source to the largest integer-pixel 2:1 region before preparing both the AI analysis input and panorama color material.
|
||||
- Report a warning when any rows or columns are removed, including the original and cropped dimensions.
|
||||
- Preserve the existing behavior without a warning when the source dimensions are already exactly 2:1.
|
||||
- Continue rejecting animated or unusably small images that cannot produce a positive 2:1 crop.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `panorama-input-cropping`: Defines validation, centered 2:1 crop selection, prepared panorama inputs, and user warning behavior.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
None.
|
||||
|
||||
## Impact
|
||||
|
||||
- Affects the Blender-side Convert to Panorama operator and shared material color input preparation used by its job submission.
|
||||
- Adds focused operator and image-preparation tests for wide, tall, odd-width, already-2:1, animated, and unusably small inputs.
|
||||
- Does not change the panorama job protocol, server geometry pipeline, model dependencies, or node assets.
|
||||
@@ -0,0 +1,42 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Panorama input uses an exact centered 2:1 crop
|
||||
The system SHALL prepare panorama conversion from the largest centered integer-pixel rectangle whose width is exactly twice its height. The prepared color material and AI analysis input MUST use the same cropped pixels without stretching, padding, or aspect-ratio resampling.
|
||||
|
||||
#### Scenario: Source is wider than 2:1
|
||||
- **WHEN** a user converts a usable static image whose width is greater than twice its height
|
||||
- **THEN** the system crops columns equally where possible from the left and right and prepares an exact 2:1 input at the source height
|
||||
|
||||
#### Scenario: Source is narrower than 2:1
|
||||
- **WHEN** a user converts a usable static image whose width is less than twice its height
|
||||
- **THEN** the system crops rows equally where possible from the top and bottom and prepares the largest exact 2:1 input at or below the source width
|
||||
|
||||
#### Scenario: Source width is odd
|
||||
- **WHEN** a usable static source has an odd width that cannot be part of an exact integer-pixel 2:1 rectangle
|
||||
- **THEN** the system omits the unmatched column as part of its deterministic centered crop
|
||||
|
||||
#### Scenario: Source is already 2:1
|
||||
- **WHEN** a user converts a usable static image whose width is exactly twice its height
|
||||
- **THEN** the system prepares the full source image without removing pixels
|
||||
|
||||
### Requirement: Panorama crop is disclosed to the user
|
||||
The system SHALL issue one warning whenever panorama input preparation removes source pixels, and the warning MUST identify both the original and cropped pixel dimensions.
|
||||
|
||||
#### Scenario: Conversion requires cropping
|
||||
- **WHEN** the computed 2:1 bounds differ from the source bounds
|
||||
- **THEN** the system reports a warning containing the source width and height and the cropped width and height
|
||||
|
||||
#### Scenario: Conversion does not require cropping
|
||||
- **WHEN** the source bounds already form an exact 2:1 image
|
||||
- **THEN** the system does not report a crop warning
|
||||
|
||||
### Requirement: Unsupported panorama sources remain rejected
|
||||
The system MUST reject animated images and images too small to produce a positive integer-pixel 2:1 crop before submitting panorama generation.
|
||||
|
||||
#### Scenario: Source is animated
|
||||
- **WHEN** a user attempts to convert an animated image
|
||||
- **THEN** the system reports an error and does not submit panorama generation
|
||||
|
||||
#### Scenario: Source cannot produce a positive crop
|
||||
- **WHEN** a source image cannot provide at least a 2-pixel-wide by 1-pixel-high crop
|
||||
- **THEN** the system reports an error and does not submit panorama generation
|
||||
@@ -0,0 +1,15 @@
|
||||
## 1. Crop Bounds
|
||||
|
||||
- [x] 1.1 Add a common helper that returns the largest deterministic centered exact 2:1 integer-pixel bounds and rejects dimensions that cannot produce a positive crop
|
||||
- [x] 1.2 Add focused tests for exact, wide, tall, odd-width, uneven-margin, and unusably small dimension cases
|
||||
|
||||
## 2. Panorama Conversion
|
||||
|
||||
- [x] 2.1 Update Convert to Panorama to keep animated-image validation, compute the crop bounds, and pass them into the shared material color input preparation
|
||||
- [x] 2.2 Report one crop warning with original and cropped dimensions only when pixels are removed
|
||||
- [x] 2.3 Update panorama operator tests to verify arbitrary aspect ratios submit cropped color and analysis inputs, exact 2:1 input stays unchanged, and unsupported sources remain rejected
|
||||
|
||||
## 3. Verification
|
||||
|
||||
- [x] 3.1 Run the related common image and panorama conversion test suites and confirm they pass
|
||||
- [x] 3.2 Inspect the final diff and search the panorama conversion path to confirm the strict 2:1 rejection and stale expectations are removed
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-09
|
||||
@@ -0,0 +1,64 @@
|
||||
## Context
|
||||
|
||||
两个节点组共用 `split_depth_surface`,在变形前读取相邻面深度并拆边,随后固定相机坐标、投影和加厚。实验直接复制原资产,只插入单次 Delete Geometry;关闭实验开关时求值的顶点与面与原组完全一致。
|
||||
|
||||
实验四边网格结果:山景从 80,449 面删去 21,306 面,蜘蛛图 Depth Cutout 从 37,206 面删去 2,249 面。蜘蛛图三角网格的 74,412 面全部保留。两个实验均为 MoGe-2 Small、level 0、Split Threshold 0.5;面数取决于实验网格,正式测试验证拓扑性质。实验深度采用无损 EXR 打包,保存重开结果一致。
|
||||
|
||||
## Goals
|
||||
|
||||
在两个 Depth 节点组中共用确定性的单次条带删面逻辑,将 Split 控制放在主输入区 Smooth 前,并保留剩余表面的 UV、独立切口身份和加厚连接。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 在拆边分支内执行一次删除
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[当前基础几何及已有预处理] --> B{Split Threshold 大于 0}
|
||||
B -->|是| C[原有面深度采样与 Split Edges]
|
||||
C --> D[四边面按顺序读取四条边的 Face Count]
|
||||
D --> E[任一组对边均为边界边]
|
||||
E --> F[Delete Geometry:Face / All,单次删除]
|
||||
F --> G[固定剩余顶点的相机坐标]
|
||||
B -->|否| G
|
||||
G --> H[深度投影]
|
||||
H --> I[现有厚度与 Smooth 流程]
|
||||
```
|
||||
|
||||
共用实现放在 `tools/nodes/common/depth_surface.py`,将删除节点接在 Split Edges 输出与现有启用 Switch 的 True 输入之间。删除选择在该时刻的完整拆后几何上求值一次。
|
||||
|
||||
### 2. 按面角顺序识别边界对边
|
||||
|
||||
在 FACE 域用 Index → Corners of Face 获取当前面的一个起始角和面角数量。通过 Offset Corner in Face 的偏移 0、1、2、3,分别用 Edges of Corner 的 Next Edge Index 取得同方向的四条边;Evaluate at Index 在 EDGE 域读取 Mesh Edge Neighbors 的 Face Count。
|
||||
|
||||
令 `b_i = (FaceCount(edge_i) == 1)`,选择条件为:
|
||||
|
||||
```text
|
||||
corner_count == 4 AND ((b0 AND b2) OR (b1 AND b3))
|
||||
```
|
||||
|
||||
该结果与起始角和面绕序无关。一张面宽条带、三条边界边的条带末端、孤立四边面被选中;具有一条边界边或两条相邻边界边的普通面保留。边界计数严格等于 1,零邻面和非流形边不作为边界边参与匹配。
|
||||
|
||||
### 3. 正式接口只有 Split 控制
|
||||
|
||||
条带清理随 Split 正值分支固定启用。两个节点组仅有一个 `Split Threshold`,其 parent 为主输入区,顺序紧邻 `Smooth` 之前。维持 `FACTOR`、范围 0–1、默认 0 及现有阈值映射。
|
||||
|
||||
移动接口时检查构建顺序、局部 Group Input、修改器赋值与测试中的标识符或顺序依赖。组名保持正式名称;正式接口不引入实验开关。节点使用默认名称、局部连线和未使用输出隐藏规则。
|
||||
|
||||
### 4. 按输入拓扑验收
|
||||
|
||||
四边面按上述条件处理;三角面和其他多边面通过面角数量门控保留。Depth Cutout 的当前三角化输入仍可使用原 Split,清理阶段自然不选面。保持真实建网流程,不为匹配清理规则增加三角面合并或重拓扑。
|
||||
|
||||
### 5. 更新覆盖约束并验证厚度
|
||||
|
||||
将现有“Split 保留所有面”的测试改为“保留所有未命中条带条件的面”,并保留独立的纯拆边与零值旁路检查。厚度依据删面后几何建立边界;正厚度测试检查剩余区域闭合、切口区域独立和属性生命周期。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 规则识别一张面宽的拓扑结构;真实窄结构、已被原 Split 过度拆分的远山也可能命中。不能据此承诺语义上的前景或远山完整保留。
|
||||
- 两张面宽的条带及三角条带不受本规则处理,剩余边界仍受原网格分辨率限制。
|
||||
- 单次求值避免清理不断向完整区域推进。首次删除后新暴露的候选面留待后续用户改变输入时重新求值,不能在同一次求值中反复删除。
|
||||
|
||||
## Delivery
|
||||
|
||||
本提案完成后实施源码与测试,保留工作区其他修改,执行全量测试并确认正常退出。依照项目规范,此实施任务不构建文档或产物。正式节点资产重建发布需要单独安排;源码实施完成不代表用户安装的节点资产已更新。
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
Depth Plane 与 Depth Cutout 的 Split Edges 会在深度过渡区域产生一张面宽的条带和孤立四边面。拆边后检查四边面的两组对边,可以识别这些结构,同时保留只有相邻两条边为边界的普通表面角落。独立节点组实验已验证该判定及单次删除行为。
|
||||
|
||||
## What Changes
|
||||
|
||||
- `O Image Depth Plane` 与 `O Image Depth Cutout` 在启用 Split 的分支中,拆边后默认执行一次条带删面。
|
||||
- 仅选择至少一组对边均为边界边的四边面;边界边的 Face Count 必须等于 1。
|
||||
- 正式接口不增加实验中的 `Remove Strip Faces` 输入。`Split Threshold = 0` 时旁路拆边和条带删面。
|
||||
- 两组的 `Split Threshold` 从 `Options` 移到主输入区,紧邻并位于 `Smooth` 前;名称、默认值、范围和阈值映射保持现状。
|
||||
- 共用节点构建逻辑,并更新针对面数保留、接口顺序和厚度连接的行为测试。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `depth-surface-strip-cleanup`:拆边后默认进行单次四边面对边界边筛选删面,以及两个节点组的 Split 输入位置。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。当前 `openspec/specs/` 为空;本提案依据当前源码及实验结果定义增量行为。
|
||||
|
||||
## Impact
|
||||
|
||||
涉及 `tools/nodes/common/depth_surface.py`、两个 Depth 节点组构建模块和对应测试。运行时继续加载正式命名的节点资产。实现阶段只修改源码与测试并执行测试;正式 `.blend` 的重建发布另行安排。
|
||||
|
||||
三角面及其他非四边面保持原行为。当前 Cutout 建网流程产生三角面,因此本次规则对这类输入不会删面;四边网格输入的实验效果不能作为三角网格的验收结果。条带判定依据拓扑,已被拆成条带的真实物体细节也可能被删除。
|
||||
@@ -0,0 +1,69 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 拆边后默认执行单次条带清理
|
||||
|
||||
`O Image Depth Plane` 与 `O Image Depth Cutout` SHALL 在 Split 正值分支内、Split Edges 之后且投影和厚度生成之前,通过几何节点执行一次 Face / All 删除。两个节点组 SHALL 共用该逻辑,正式接口 SHALL NOT 暴露 `Remove Strip Faces` 开关。
|
||||
|
||||
#### Scenario: 开启 Split
|
||||
- **WHEN** Split Threshold 大于 0
|
||||
- **THEN** 系统先按当前规则拆边,再根据拆后拓扑执行一次条带删面
|
||||
|
||||
#### Scenario: 关闭 Split
|
||||
- **WHEN** Split Threshold 为 0
|
||||
- **THEN** 系统旁路拆边及新增条带删面,保持当前零值分支的几何与投影行为
|
||||
|
||||
#### Scenario: 单次删除
|
||||
- **WHEN** 首次删除使另一张面新出现一组边界对边
|
||||
- **THEN** 该面不会因新边界在本次求值中被继续删除
|
||||
|
||||
### Requirement: 四边面边界对边判定
|
||||
|
||||
系统 SHALL 仅选择四边面,并按面角顺序取得四条边。若至少一组对边的 Face Count 均等于 1,该面 SHALL 被选中;其余面 SHALL 被保留。
|
||||
|
||||
#### Scenario: 条带及孤立四边面
|
||||
- **WHEN** 四边面具有两条相对的边界边,或具有三条、四条边界边
|
||||
- **THEN** 该面被删除
|
||||
|
||||
#### Scenario: 普通边缘及角落
|
||||
- **WHEN** 四边面没有边界边、只有一条边界边,或仅有两条相邻边界边
|
||||
- **THEN** 该面保留
|
||||
|
||||
#### Scenario: 面角起点与绕序
|
||||
- **WHEN** 同一四边面的面角起点改变或绕序反转
|
||||
- **THEN** 选择结果不变
|
||||
|
||||
#### Scenario: 非四边面
|
||||
- **WHEN** 输入面为三角面或其他非四边面
|
||||
- **THEN** 新增清理规则不删除该面
|
||||
|
||||
#### Scenario: 非流形边
|
||||
- **WHEN** 一条边的 Face Count 不等于 1
|
||||
- **THEN** 该边不能作为匹配条件中的边界边
|
||||
|
||||
### Requirement: 清理结果接入原投影和厚度流程
|
||||
|
||||
系统 SHALL 在剩余几何上固定相机坐标并执行现有投影及加厚,保留 UV 和用户属性,保持切口区域独立。清理选择 SHALL 只依据拆后拓扑,不依赖 Thickness 或后续 Smooth。
|
||||
|
||||
#### Scenario: 剩余区域加厚
|
||||
- **WHEN** 清理后的定向正确的流形片状区域设置正厚度
|
||||
- **THEN** 每个保留区域的正背面与侧壁保持完整连接,区域之间保持独立
|
||||
|
||||
#### Scenario: 改变显示形状参数
|
||||
- **WHEN** 基础几何、深度输入与 Split Threshold 不变,仅修改 Thickness 或 Smooth
|
||||
- **THEN** 被条带清理选中的基础面集合不变
|
||||
|
||||
#### Scenario: 属性与空结果
|
||||
- **WHEN** 清理保留部分面或删除全部面
|
||||
- **THEN** 求值正常完成且坐标有限;有剩余几何时 UV 与用户属性可用,临时属性依当前流程清理
|
||||
|
||||
### Requirement: Split Threshold 位于 Smooth 前
|
||||
|
||||
两个节点组 SHALL 将唯一的 `Split Threshold` 输入放在主输入区,紧邻并位于 `Smooth` 之前。该输入 SHALL 保持原名称、FACTOR 类型、0–1 范围、默认 0 和当前阈值映射。
|
||||
|
||||
#### Scenario: 检查接口
|
||||
- **WHEN** 检查任一节点组或其修改器输入
|
||||
- **THEN** Split Threshold 位于 Smooth 前且不属于 Options,Options 内没有重复输入,接口没有 Remove Strip Faces
|
||||
|
||||
#### Scenario: 参数读取
|
||||
- **WHEN** Operator 为修改器赋值或用户调整 Split Threshold
|
||||
- **THEN** 新位置的输入正常控制原拆边强度及默认条带清理
|
||||
@@ -0,0 +1,16 @@
|
||||
## 1. 共用条带判定
|
||||
|
||||
- [x] 1.1 在 `tools/nodes/common/depth_surface.py` 实现四边面对边界边判定,在 Split Edges 后、启用分支 Switch 前接入单次 Face / All 删除;正式逻辑固定启用,无额外开关。
|
||||
- [x] 1.2 验证孤立四边面、一面宽条带、普通边缘与角落、面角起点和绕序、三角面及其他多边面、非流形边;加入首次删除后出现新候选面的用例以确认仅执行一遍。
|
||||
|
||||
## 2. 两组接口与行为接入
|
||||
|
||||
- [x] 2.1 将 `O Image Depth Plane` 和 `O Image Depth Cutout` 的 Split Threshold 移到主输入区 Smooth 紧前,保留输入语义,整理局部节点布局。
|
||||
- [x] 2.2 更新接口顺序、Options 归属、唯一输入、无实验开关及相关修改器赋值测试。
|
||||
- [x] 2.3 更新原有面数保留断言,分别验证两组的四边面清理、三角面保留和 Split 为零时原行为;确认原梯度选择与比例映射不变。
|
||||
|
||||
## 3. 几何与集成验证
|
||||
|
||||
- [x] 3.1 验证剩余表面的 UV、用户属性、临时属性清理、空结果、正厚度闭合以及 Smooth 后区域独立;确认厚度与 Smooth 不改变基础删面集合。
|
||||
- [x] 3.2 使用真实 Blender 求值验证两组节点;分别记录四边网格与当前 Cutout 三角网格的结果,不能用四边实验替代正式三角输入验收。
|
||||
- [x] 3.3 运行 `uv run pytest` 全量测试并确认正常退出,检查最终差异和旧接口断言,保留其他已有修改;不构建文档或产物,并在交付说明中明确正式节点资产尚待另行重建发布。验证结果:940 passed、2 xfailed,退出码 0。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-11
|
||||
@@ -0,0 +1,21 @@
|
||||
## Context
|
||||
|
||||
排列器以几何主链、取值支线及共享引用组织节点。循环目前额外计算支线宽度并预留两端间距。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
目标是让循环执行步骤紧凑可读,并保持前置几何处理的横向先后关系。
|
||||
|
||||
## Decisions
|
||||
|
||||
- 预览层从完整区域成员中筛选几何执行主链,取值计算复用支线排列流程。
|
||||
- 横向列依赖保留实际几何来源,局部引用端口保持连接顺序。
|
||||
- 使用真实 Depth Cutout 和小型循环验证布局、连接及几何求值。
|
||||
- 简单计算链在有限高度内纵向分段,复杂支线按消费者形成独立 Frame。
|
||||
- 支线按实际消费输入与连通依赖分组,计算流程保持连续展开。
|
||||
- 几何引用线同时处理向前与回向连接,水平通道统一避让节点、支线及已有通道。
|
||||
- 引用线的高差通过有横向展开的斜向曲线过渡。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
支线引用循环数据时可能需要回向连线;通过分离主链通道和支线区域保证可读性,并验证几何连接语义保持一致。
|
||||
@@ -0,0 +1,22 @@
|
||||
## Why
|
||||
|
||||
Repeat 为取值支线预留宽度,导致单个 Set Position 的循环被拉长,前置循环的处理顺序也难以辨认。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 按循环执行主链排列 Repeat 两端,取值支线独立排列。
|
||||
- 几何处理节点按真实上游依赖确定先后位置。
|
||||
- 更新排列测试、节点资产和交互预览。
|
||||
- 将长计算链紧凑排列,为复杂支线添加 Frame,并分离几何引用线通道。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `compact-repeat-arrangement`: 紧凑呈现循环执行主链并保持几何处理顺序。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
涉及 `tools/nodes/arrangement`、`tools/nodes/preview`、相关测试和 `O_AnyImage.blend`。
|
||||
@@ -0,0 +1,64 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Compact repeat execution path
|
||||
|
||||
排列器 SHALL 按循环执行的几何步骤安排 Repeat 两端,取值支线独立排列。
|
||||
|
||||
#### Scenario: Set Position loop with long value calculations
|
||||
- **WHEN** 循环只执行 Set Position,输入值来自长计算链
|
||||
- **THEN** Repeat 两端保持围绕 Set Position 的紧凑排列,预览执行区域按几何主链确定
|
||||
|
||||
### Requirement: Geometry processing order
|
||||
|
||||
排列器 SHALL 将前置循环安排在后续几何处理之前。
|
||||
|
||||
#### Scenario: Depth Cutout boundary smoothing
|
||||
- **WHEN** 排列 O Image Depth Cutout 的前置边界平滑循环
|
||||
- **THEN** 循环位于其后续几何消费者左侧,保持原有连接与求值结果
|
||||
|
||||
### Requirement: Readable field branches and geometry references
|
||||
|
||||
排列器 SHALL 将取值支线紧凑安排在消费者附近,复杂支线使用互不重叠的 Frame,并分离几何引用的水平通道。
|
||||
|
||||
#### Scenario: Long calculation chain
|
||||
- **WHEN** 消费者使用较长的简单计算链
|
||||
- **THEN** 计算链使用紧凑折叠及分段纵向布局
|
||||
- **AND** 连续依赖按计算方向展开,独立计算按实际消费输入分组
|
||||
|
||||
#### Scenario: Complex depth cutout branches
|
||||
- **WHEN** 排列 O Image Depth Cutout
|
||||
- **THEN** 复杂计算支线分别成框,几何引用线保持独立的水平通道,并通过斜向曲线过渡,原有几何求值保持一致
|
||||
|
||||
#### Scenario: Geometry trunk alongside its consumer frame
|
||||
- **WHEN** 几何主干向 Frame 内的计算节点供值
|
||||
- **THEN** 主干与完整 Frame 边界保持间距,通过斜向分支接入框内消费者
|
||||
|
||||
#### Scenario: Long branch near its geometry consumer
|
||||
- **WHEN** 较大支线需要占用来源和消费者之间的横向空间
|
||||
- **THEN** 主线在消费者之前预留支线及斜向出口的空间,连续主线保持直接向前连接
|
||||
|
||||
#### Scenario: Independent calculation before a geometry consumer
|
||||
- **WHEN** 支线独立于上游几何结果
|
||||
- **THEN** 支线可向主线起点之前展开,主线按实际出口位置保持紧凑
|
||||
|
||||
#### Scenario: Return connection and neighboring reference tracks
|
||||
- **WHEN** 几何结果连接左侧消费者
|
||||
- **THEN** 连线直接向消费者方向过渡,各水平通道按最终范围保留间距
|
||||
|
||||
#### Scenario: Geometry fanout without overlapping wires
|
||||
- **WHEN** 同一几何结果供多个节点消费
|
||||
- **THEN** 近处消费者直接接入,公共水平通道按从左到右分发,每段公共连线只绘制一次
|
||||
- **AND** 水平连线保持向前,短 reroute 间距合并,近乎平行的曲线与主线保持间距
|
||||
|
||||
#### Scenario: Shared results outside calculation branches
|
||||
- **WHEN** 几何或其他类型的结果供多个支线消费
|
||||
- **THEN** 共享结果沿支线外围的公共管道分发,在消费者附近斜向接入
|
||||
- **AND** 原有主线连接保持执行顺序,公共管道与节点、Frame 及其他管道保留间距
|
||||
|
||||
#### Scenario: Geometry transits an unrelated calculation
|
||||
- **WHEN** 几何连接沿途经过其他支线
|
||||
- **THEN** 整段曲线绕开不消费该结果的 Frame,仅允许进入自身来源或消费者所属的 Frame
|
||||
|
||||
#### Scenario: Peripheral producers feed their shared pipes forward
|
||||
- **WHEN** 支线计算的结果通过公共管道供多个消费者使用
|
||||
- **THEN** 共享计算支线与管道一同位于消费者外围,管道入口在结果输出节点右侧
|
||||
@@ -0,0 +1,46 @@
|
||||
## 1. 排列
|
||||
|
||||
- [x] 1.1 分离循环执行主链与取值支线,修正前置循环位置
|
||||
- [x] 1.2 更新预览区域和回归测试
|
||||
|
||||
## 2. 验证
|
||||
|
||||
- [x] 2.1 运行节点资产构建和全量测试
|
||||
- [x] 2.2 生成并检查交互预览
|
||||
|
||||
## 3. 支线与引用
|
||||
|
||||
- [x] 3.1 缩短支线,支持简单计算链纵向排列并靠近消费者
|
||||
- [x] 3.2 为复杂支线生成独立 Frame,并同步预览与坐标处理
|
||||
- [x] 3.3 统一几何引用线避让,分离水平通道和回向连线
|
||||
- [x] 3.4 更新回归测试、构建资产并完成全量及视觉验证
|
||||
|
||||
## 4. 主干与 Frame 间距
|
||||
|
||||
- [x] 4.1 为几何主干保留整个 Frame 的边界间距,覆盖相连消费者的 Frame
|
||||
- [x] 4.2 验证回归用例、重建资产并更新预览
|
||||
|
||||
## 5. 支线消费位置
|
||||
|
||||
- [x] 5.1 按较大支线及斜向出口所需空间延长主线,按出口距离选择支线位置
|
||||
- [x] 5.2 保留直接向前的主线连接,减少几何引用的外围折返
|
||||
- [x] 5.3 验证连接、求值与布局,重建资产并更新预览
|
||||
|
||||
## 6. 收紧主线与分离回向连接
|
||||
|
||||
- [x] 6.1 去掉回向连接在来源右侧的多余折返点
|
||||
- [x] 6.2 按实际出口估算主线间距,独立计算可在起点之前展开
|
||||
- [x] 6.3 按最终线段范围分配水平通道并扩大不同来源的间距
|
||||
- [x] 6.4 总线按从左到右分发,近处消费者直接接入,分离近乎平行的曲线并检查同源绿线重复覆盖
|
||||
- [x] 6.5 重建资产、检查截图区域并完成全量验证
|
||||
|
||||
## 7. 共享结果外围管道
|
||||
|
||||
- [x] 7.1 合并几何与其他类型的共享引用布线,共用结果沿支线外围就近分发
|
||||
- [x] 7.2 绿线绕开不消费该结果的 Frame,并验证实际曲线范围
|
||||
- [x] 7.3 验证主线保留、外围间距与连接语义,重建资产并检查预览
|
||||
|
||||
## 8. 共享来源与管道入口
|
||||
|
||||
- [x] 8.1 将共享结果的计算支线安排在消费者外围,管道从输出右侧开始
|
||||
- [x] 8.2 验证入口方向、支线位置及求值,重建资产并更新预览
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-14
|
||||
@@ -0,0 +1,77 @@
|
||||
## Context
|
||||
|
||||
现有 Cutout 负责图像深度投影与 Balloon / Shell 成形;现有 Depth Symmetry 同时承担投影及对称。桌面独立实验已将后者的 Projected 后半段取出,接在原 Cutout 后面。
|
||||
|
||||
`C:/Users/icrdr/Desktop/anyimage/post-cutout-symmetry-20260914/comparison.json` 记录了两张图片、两种模式、补面开关的零厚度对比:八组顶点位置误差均为 0,面数量及带方向拓扑一致。非零厚度对比也验证了镜像和封闭直壁。该证据对应实验参数;本次精简平滑接口后还需验证匹配预处理条件下的一致性。
|
||||
|
||||
Shell 的 `build_normal_weight` 使用 `smoothstep(Shell Thickness / Reference Depth)` 混合法线。实验恐龙的 0.075 厚度与约 5.505 参考深度只留下约 0.055% 的平滑权重,解释了薄壳调节不明显。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
目标是两个入口共用 Cutout 成形,独立对称节点负责后续镜像与补面,并让薄壳法线平滑有效。本轮实现范围为节点资产、创建流程及对应验证;Cutout 继续使用 Balloon / Shell,深度推理流程保持现有实现。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 对称作为成形后的节点
|
||||
|
||||
使用资产名称 `O Image Cutout Symmetry`,将其改为几何处理节点。Depth Solid 使用 Cutout;Depth Symmetry 使用以下顺序:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[原 Depth Symmetry 入口] --> B[O Image Depth Cutout:Thickness = 0]
|
||||
B --> C[O Image Cutout Symmetry]
|
||||
```
|
||||
|
||||
先完成 Balloon 或 Shell,再将结果整体交给对称节点。这与已验证实验一致,避免在 Cutout 内组合成形和镜像的模式分支。旧 Symmetry 的 Balloon 分支由上游 Cutout 的 Balloon 承担。
|
||||
|
||||
对称沿用原 Projected 的方向对齐、Z 向偏移、负侧面移除、镜像翻面及对应边界连接顺序。Shell 的内外表面都属于输入结果,分别保留其边界连接关系。负侧面移除沿用原面选择语义。
|
||||
|
||||
### 2. 简化接口与坐标约定
|
||||
|
||||
| 输入 | 行为 |
|
||||
| --- | --- |
|
||||
| Geometry | 接收 Cutout 已成形的几何 |
|
||||
| Symmetry Direction | 复用原 Depth Direction 的方向对齐语义,默认 (0, 0, 1) |
|
||||
| Depth Offset | 方向调整后沿对称轴偏移,默认 0 |
|
||||
| Fill Sides | 连接前后对应边界,默认开启 |
|
||||
| Fill Smooth | 仅控制新增侧壁的几何平滑次数,默认 2 |
|
||||
|
||||
始终保留镜像后的两侧。移除 Mode、图像及深度标定输入、Double Sided、Depth Axis、Smooth、Boundary Smooth、Smooth Weight、Uniform Scale。对称坐标系沿用原 Projected 的局部 XY 平面和 Z 轴;创建流程将上游 Cutout 初始化为与之匹配的 -Z 输出。接缝容差由输入几何尺度获得。
|
||||
|
||||
### 3. 补面平滑约束在新增侧壁内部
|
||||
|
||||
Fill Smooth 为 0 时直接使用原直壁补面。开启时,在对应前后边界间生成含中间环的侧壁条带,对中间顶点进行受限平滑,固定与原几何相接的两端;保持成对顶点关于对称平面对称。条带采样采用内部固定策略,不新增采样参数。
|
||||
|
||||
这使侧壁平滑具备可移动的内部顶点,并保持输入保留下来的面形状。只有改变着色法线不足以满足几何平滑要求。Fill Sides 关闭时跳过条带及平滑。复用 common 中的字段与平滑函数;不用 Capture Attribute,临时属性仅在确有跨几何保存需求时使用并清理。
|
||||
|
||||
### 4. 入口分配参数与零厚度基线
|
||||
|
||||
Cutout 节点资产的 Balloon 与 Shell 两种 Thickness 默认值均为 0,手动添加节点时也使用零厚度初态。Depth Solid 创建时按 Thickness 的 subtype 分别设置 Balloon 为 1、Shell 为 0.2;两个输入均初始化,切换模式时使用各自的创建值。手势对应模式沿用 Cutout 的 Balloon / Shell 选择约定。
|
||||
|
||||
Depth Symmetry 入口创建两个依次求值的 Geometry Nodes 修改器。深度图、参考深度和尺度交给 Cutout,原标定得到的方向交给 Symmetry。Cutout 使用两种 Thickness 的零默认值,切换模式也保持零厚度初态。
|
||||
|
||||
Depth Solid 与 Depth Symmetry 均请求并读取 `object_normal` 结果;对象创建时为深度 Cutout 材质指定 OBJECT,开启 `O Image Layer` 的 Object Space。
|
||||
|
||||
Depth Symmetry 创建时保留 Cutout 的参数默认值,包括 Depth Split、Boundary Smooth、Smooth 与 Depth Axis;仅沿用通用的手势模式选择、清理阈值和图像标定输入。下述一致性测试所需的关闭分割/平滑条件只在测试中显式设置。
|
||||
|
||||
一致性验收使用同一输入网格、深度、参考值、尺度、方向和偏移;新链路关闭额外 Cutout 分割/平滑,旧 Projected 的 Smooth 与 Boundary Smooth 也设为 0,新 Fill Smooth 为 0。分别验证 Balloon/Shell 零厚度与 Fill Sides 开关。额外开启上游平滑后的结果由该上游处理决定。
|
||||
|
||||
### 5. Shell 使用完整的平滑厚度方向
|
||||
|
||||
Shell 使用在原正面点域求值、按 Normal Smooth 模糊并归一化的法线作为厚度偏移方向,不再以厚度与参考深度之比衰减。厚度仅控制偏移距离;零厚度输出保持原正面。Balloon 保留现有厚度相关权重。
|
||||
|
||||
Normal Smooth 平滑的是壳体挤出方向,原始深度正面的几何平滑仍由 Cutout 的 Smooth 控制。测试覆盖实际较薄厚度及较大参考深度,避免只测试厚壳而遗漏衰减问题。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 侧壁平滑可能收缩狭窄凹口或在复杂轮廓附近自交 → 固定接缝、保持镜像配对,使用现有两张样例检查小幅平滑效果;不将全局无自交作为任意参数的保证。
|
||||
- 精简接口与旧图文件中的内嵌组不同 → 创建流程统一使用新资产接口,不自动重写用户已有对象;构建前保存基线测试资料,按新的参数分配更新节点测试。
|
||||
- 同名资产及待归档旧提案可能描述不同职责 → 本变更作为后续修订;实现与归档时协调 `add-projected-depth-symmetry` 的最终规格,防止旧的单节点要求覆盖此设计。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
先保留旧 Projected 的小型基线数据,再替换构建逻辑、接入双修改器入口并修正 Shell 法线。运行相关测试和 `uv run node-group build`,同步源码与资产。出现回归时成套恢复此次构建逻辑、入口参数和资产。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无阻塞产品决策。侧壁条带的内部采样数量在实现时以现有样例验证,不增加公开控制项。
|
||||
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
Depth Cutout 已负责深度置换及 Balloon / Shell 成形,对称应当作为后续几何操作复用这些结果。独立实验已验证零厚度 Cutout 接对称节点可以复现原 Depth Symmetry 的 Projected 构成方式,同时发现薄 Shell 的 Normal Smooth 被厚度权重压低,需要修正。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 保留 Depth Solid、Depth Symmetry 两个入口;Depth Symmetry 创建串联的 `O Image Depth Cutout`(两个模式的 Thickness 均为 0)和 `O Image Cutout Symmetry`。
|
||||
- Cutout 节点资产中 Balloon / Shell 两种 Thickness 的默认值均为 0;Depth Solid 创建时分别设置为 1 和 0.2,Depth Symmetry 使用零厚度默认值。
|
||||
- **BREAKING**:`O Image Cutout Symmetry` 改为接收已成形几何的对称节点,提取原 Projected 的方向对齐、偏移、负侧面移除、镜像及边界直壁补面逻辑;移除图像置换和 Mode。
|
||||
- 对称节点提供 Geometry、Symmetry Direction、Depth Offset、Fill Sides、Fill Smooth;始终生成镜像,移除 Double Sided、Depth Axis、全局 Smooth、Boundary Smooth 及其配套输入。
|
||||
- Fill Smooth 只平滑生成的侧壁,默认 2;Cutout 保持 Balloon / Shell 两种模式及其自身平滑控制。
|
||||
- 两种深度 Cutout 入口均使用 Object Space 法线贴图,并开启材质 `O Image Layer` 的 Object Space。
|
||||
- Shell 的 Normal Smooth 独立于厚度与参考深度的比例,实际控制壳体厚度偏移方向的平滑。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `post-cutout-symmetry`: 对已成形几何执行方向对齐、镜像、补面和补面平滑,定义原入口的节点串联及零厚度一致性。
|
||||
- `shell-normal-smoothing`: 定义薄 Shell 的法线平滑作用及零厚度行为。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。当前主规格目录为空;本变更接续 `add-projected-depth-symmetry` 中的节点实现。
|
||||
|
||||
## Impact
|
||||
|
||||
影响 `tools/nodes/groups/image_cutout_symmetry.py`、`image_depth_cutout.py`、相关共用节点函数、Cutout 对象创建与参数初始化、相关测试及 `src/anyimage/assets/O_AnyImage.blend`。不增加依赖。既有 `.blend` 内嵌节点不会自动迁移,后续从资产加载使用新接口。
|
||||
@@ -0,0 +1,74 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 原入口串联 Cutout 与对称节点
|
||||
|
||||
系统 SHALL 保留 Depth Solid 与 Depth Symmetry 入口。Depth Symmetry SHALL 按顺序创建 `O Image Depth Cutout` 和 `O Image Cutout Symmetry` 修改器,将 Cutout 两种 Thickness 输入均初始化为 0,并分别将深度标定参数与对称方向交给对应节点。
|
||||
|
||||
#### Scenario: 创建默认对称对象
|
||||
- **WHEN** 用户通过原 Depth Symmetry 入口完成轮廓创建
|
||||
- **THEN** 对象先执行零厚度 Cutout,再执行对称;Cutout 保留节点默认参数,包括 Depth Split、Boundary Smooth、Smooth 与 Depth Axis,仅沿用通用的手势模式选择、清理阈值和图像标定输入
|
||||
- **AND** 切换 Balloon / Shell 后厚度仍为 0
|
||||
|
||||
### Requirement: 节点厚度默认值与入口初始化
|
||||
|
||||
`O Image Depth Cutout` 节点资产的 Balloon / Shell 两种 Thickness 默认值 SHALL 均为 0。Depth Solid 创建时 SHALL 按输入 subtype 分别设置 Balloon Thickness 为 1、Shell Thickness 为 0.2,其他创建默认设置保持不变;Depth Symmetry SHALL 使用两种厚度的零默认值。
|
||||
|
||||
#### Scenario: 手动添加 Cutout 节点
|
||||
- **WHEN** 用户从资产添加未经入口覆写的 Cutout 节点
|
||||
- **THEN** Balloon / Shell 两种 Thickness 均为 0
|
||||
|
||||
#### Scenario: 创建 Depth Solid
|
||||
- **WHEN** 用户通过 Depth Solid 入口创建对象
|
||||
- **THEN** Balloon Thickness 为 1,Shell Thickness 为 0.2,两个输入均完成初始化
|
||||
- **AND** 切换模式时使用对应厚度值,边界平滑等其他默认设置保持不变
|
||||
|
||||
### Requirement: 成形后镜像与补面
|
||||
|
||||
对称节点 SHALL 接收上游完整几何,沿用原 Projected 的方向对齐、偏移、负侧面移除、镜像翻面与对应边界直壁连接语义。Fill Sides SHALL 控制是否添加连接侧壁。
|
||||
|
||||
#### Scenario: 非零厚度的两种成形结果
|
||||
- **WHEN** Balloon 或 Shell 的非零厚度输出连接至对称节点
|
||||
- **THEN** 节点对已成形结果执行对称;Shell 的壳体内外表面一同参与
|
||||
- **AND** Fill Sides 关闭时不添加连接壁,开启时连接对应边界且保持面方向一致
|
||||
|
||||
### Requirement: 精简对称控制
|
||||
|
||||
对称节点 SHALL 仅公开 Geometry、Symmetry Direction、Depth Offset、Fill Sides、Fill Smooth 输入及 Geometry 输出,始终生成镜像两侧。默认方向 SHALL 为 (0, 0, 1),偏移 SHALL 为 0,Fill Smooth SHALL 为 2,Fill Sides SHALL 开启。
|
||||
|
||||
#### Scenario: 默认补面平滑
|
||||
- **WHEN** 从资产加载新的对称节点
|
||||
- **THEN** Fill Smooth 默认为 2
|
||||
|
||||
#### Scenario: 调整对称方向
|
||||
- **WHEN** 用户调整 Symmetry Direction 和 Depth Offset
|
||||
- **THEN** 几何使用原方向对齐及轴向偏移语义更新,对称平面位于调整后的局部 XY 平面
|
||||
- **AND** 界面不提供 Double Sided、Depth Axis、全局 Smooth 或 Boundary Smooth
|
||||
|
||||
### Requirement: 深度 Cutout 使用 Object Space 法线
|
||||
|
||||
Depth Solid 与 Depth Symmetry SHALL 使用 Object Space 法线贴图,并在创建材质时开启 `O Image Layer` 的 Object Space。
|
||||
|
||||
#### Scenario: 创建深度 Cutout 材质
|
||||
- **WHEN** 用户通过任一深度 Cutout 入口生成对象
|
||||
- **THEN** 生成流程请求并读取 object_normal,材质的 Object Space 为开启状态
|
||||
|
||||
### Requirement: 仅平滑新增侧壁
|
||||
|
||||
Fill Smooth SHALL 控制新增侧壁的几何平滑,固定与保留输入几何相接的端点并保持镜像对称;数值为 0 SHALL 使用原直壁构成。
|
||||
|
||||
#### Scenario: 平滑连接壁
|
||||
- **WHEN** Fill Sides 开启且 Fill Smooth 大于 0
|
||||
- **THEN** 有曲率的轮廓所生成侧壁内部顶点可被平滑移动,原保留表面及接缝端点不受该平滑移动
|
||||
- **AND** 对应连接保持封闭,镜像对称关系保持成立
|
||||
|
||||
#### Scenario: 禁用补面
|
||||
- **WHEN** Fill Sides 关闭而 Fill Smooth 改变
|
||||
- **THEN** 输出几何保持一致
|
||||
|
||||
### Requirement: 零厚度匹配原 Projected
|
||||
|
||||
在相同输入、标定与方向偏移、相同预处理条件下,Cutout 零厚度接对称节点 SHALL 与原 Depth Symmetry 的 Projected 在顶点位置、面连接及朝向上等价。
|
||||
|
||||
#### Scenario: 两种模式与补面开关的一致性
|
||||
- **WHEN** 分别选择 Balloon / Shell 且两个厚度均为 0,Cutout 分割与平滑关闭、Depth Axis 为 -Z,旧 Projected 的 Smooth 与 Boundary Smooth 为 0,新 Fill Smooth 为 0
|
||||
- **THEN** 对 Fill Sides 开关分别比较,新旧结果的顶点位置在浮点容差内一致,面数、连接及朝向一致
|
||||
@@ -0,0 +1,19 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 薄壳法线平滑不受相对厚度衰减
|
||||
|
||||
Shell 的 Normal Smooth SHALL 控制原正面点域法线的模糊次数,归一化后的结果用于壳体厚度偏移方向。系统 SHALL 不使用 Shell Thickness / Reference Depth 衰减该平滑;厚度控制偏移距离,Balloon 保留现有权重行为。
|
||||
|
||||
#### Scenario: 小厚度下调节平滑
|
||||
- **WHEN** 非平面且法线有起伏的正面以小厚度生成 Shell,用户增大 Normal Smooth
|
||||
- **THEN** 壳体的归一化偏移方向发生相应平滑变化,且不会因厚度与参考深度的比值接近零而退回原始法线
|
||||
- **AND** 保持前表面相同而改变厚度时,平滑方向保持一致、偏移距离随厚度变化
|
||||
|
||||
### Requirement: 法线平滑保留正面与零厚度行为
|
||||
|
||||
Shell 的 Normal Smooth SHALL 仅通过厚度偏移作用于壳体,保留原深度正面;零厚度 SHALL 不因该参数改变几何。
|
||||
|
||||
#### Scenario: 零厚度与平面
|
||||
- **WHEN** Shell 厚度为 0 时改变 Normal Smooth
|
||||
- **THEN** 顶点位置与面连接保持一致
|
||||
- **AND** 对常法线平面使用非零厚度时,改变 Normal Smooth 也不改变壳体形状
|
||||
@@ -0,0 +1,27 @@
|
||||
## 1. 对称节点
|
||||
|
||||
- [x] 1.1 保存原 Projected 的小型确定性基线数据,覆盖关闭额外平滑后的顶点位置、面连接和朝向。
|
||||
- [x] 1.2 将 `O Image Cutout Symmetry` 改为几何处理节点,保留方向对齐、偏移、负侧面移除、镜像和直壁补面,按设计精简接口并处理几何尺度容差。
|
||||
- [x] 1.3 实现 Fill Smooth:生成侧壁内部采样,固定接缝并保持镜像配对;关闭补面时跳过,平滑为 0 时使用原直壁。
|
||||
|
||||
## 2. 入口与 Shell
|
||||
|
||||
- [x] 2.1 将 Cutout 资产中两种 Thickness 默认值设为 0;Depth Solid 创建时按 subtype 分别设置 Balloon 为 1、Shell 为 0.2。更新 Depth Symmetry 创建为 Cutout → Symmetry 两个修改器,保留 Cutout 节点默认参数,沿用通用的手势模式选择、清理阈值与图像标定,为对称节点初始化方向。
|
||||
- [x] 2.2 修正 Shell 厚度方向使用完整平滑法线,解除相对厚度衰减,保留原正面、零厚度行为和 Balloon 权重逻辑。
|
||||
|
||||
## 3. 验证与资产
|
||||
|
||||
- [x] 3.1 更新相关入口及接口测试,验证资产两种厚度默认值为 0、Depth Solid 创建值为 1 / 0.2、Depth Symmetry 使用零厚度且切换模式正确;验证两种模式零厚度、Fill Sides 开关与原 Projected 基线一致。
|
||||
- [x] 3.2 验证 Balloon/Shell 非零厚度的镜像与补面,以及 Fill Smooth 的侧壁位移、接缝固定、封闭性和镜像关系;使用已有两张去背景图片复核外观。
|
||||
- [x] 3.3 增加薄 Shell 法线方向平滑的有效覆盖,同时验证零厚度、常法线平面及 Balloon 行为。
|
||||
- [x] 3.4 运行相关测试和 `uv run node-group build`,验证源码与保存资产无 Capture Attribute、临时属性正确清理,检查最终差异;不打包扩展。
|
||||
|
||||
## 验证记录
|
||||
|
||||
- 后续调整:Fill Smooth 默认值改为 2;两个深度入口均请求 Object 法线并开启材质 Object Space。资产重建及独立校验通过,66 项对应测试通过。
|
||||
- 81 项相关测试通过,覆盖入口、节点定义与资产、零厚度基线、补面平滑、薄 Shell、材质属性与布局。
|
||||
- `uv run node-group build` 完成资产生成与独立进程校验;随后的全量测试发现 5 个需要同步的新接口/行为断言及 1 个既有 Depth Plane 边界平滑失败。前 5 个已修正并通过相关复测。
|
||||
- 既有失败为 `test_validity_cut_smoothing_preserves_original_outline_and_interior`;在内存中恢复 HEAD 版本的两份节点构建模块后仍能复现,本变更未修改该用例或 Depth Plane 平滑逻辑。
|
||||
- 最终 `uv run node-group build --skip-tests` 正常退出,10 个保存节点组均通过校验;`git diff --check` 通过。
|
||||
- 两张已去背景的原样例分别检查 Balloon=1、Shell=0.2 和 Fill Smooth=0/3:八组均无边界边或非流形边,镜像位置误差为 0;已检查渲染。结果位于 `C:/Users/icrdr/Desktop/anyimage/post-cutout-symmetry-20260914/applied/`。
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-05
|
||||
@@ -0,0 +1,61 @@
|
||||
## Context
|
||||
|
||||
`common/material.py` 通过 `scene.view_settings.view_transform` 选择颜色空间,随后调用 `configure_color_alpha()` 固定设置 PREMUL。当前映射包含 ACES 1.3、ACES 2.0、AgX、Filmic 和 Khronos PBR Neutral;Standard 及未匹配视图返回 sRGB。剪贴板 Mesh 材质使用独立构建入口。
|
||||
|
||||
`common/image.py` 的 Alpha 配置服务于普通编辑结果。此次策略集中作用于材质 Color Image,并复用已有像素读取和恢复机制。
|
||||
|
||||
## Goals
|
||||
|
||||
- 提供一个全局着色偏好,作为材质颜色贴图适配状态的唯一控制来源。
|
||||
- 保证实际 inverse 空间搭配 STRAIGHT,实际 sRGB 搭配 PREMUL。
|
||||
- 保留 Color 的业务 RGB、Alpha、透明区域 RGB 和共享源图片。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 偏好与生效时间
|
||||
|
||||
在 `AnyImagePreferences` 添加布尔属性 `adapt_material_to_view_transform`,UI 名称为 `Adapt to Scene View Transform`,放入独立 Shading 分组。默认值采用 `False`,让用户主动启用适配;这是本提案的默认选择。读取入口放在 `preferences.py`,通过 `addon_preferences()` 获取;偏好不可用时采用相同默认值。
|
||||
|
||||
新建材质时读取当前偏好,已创建的材质 Color Image 保留原配置。切换偏好只决定后续创建的材质贴图策略。创建操作、源图片状态和场景 View Transform 都只能参与贴图配置,不能改写适配开关。View Transform 用于开启适配时选择对应颜色空间,回退到 sRGB 后偏好仍保持开启。
|
||||
|
||||
### 颜色空间与 Alpha 一起决策
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[新建材质 Color] --> B{Preferences 开启视图适配}
|
||||
B -->|是| C[按当前场景选择颜色空间]
|
||||
B -->|否| D[设置 sRGB]
|
||||
C --> E{对应 inverse 空间可用}
|
||||
E -->|是| F[设置 inverse 空间]
|
||||
E -->|否| D
|
||||
F --> G[设置 STRAIGHT]
|
||||
D --> H[设置 PREMUL]
|
||||
G --> I[绑定材质颜色贴图]
|
||||
H --> I
|
||||
```
|
||||
|
||||
沿用 `VIEW_TRANSFORM_COLOR_SPACES` 中的准确名称。Standard、未匹配的 View Transform、缺失 scene 以及不支持的 inverse 空间统一使用 sRGB + PREMUL;这属于颜色空间解析结果,适配开关仍由 Preferences 保持。Alpha 模式以实际成功应用的颜色空间为准。如果连 sRGB 也无法设置,配置明确报错并保留该贴图之前的有效配置。
|
||||
|
||||
### 公共配置边界
|
||||
|
||||
在 `common/material.py` 集中配置材质 Color 的颜色空间和 Alpha。公共材质与剪贴板 Mesh 材质创建入口调用同一配置函数,在 Blender 主线程处理,颜色空间映射采用创建时当前场景的 View Transform。审查 Plane、Depth Plane、Cutout 调用链,保证加载、复制或 Pack 步骤保持偏好决定的配置。
|
||||
|
||||
材质专属配置函数负责设置 STRAIGHT 或 PREMUL;普通图片编辑继续使用现有 `configure_color_alpha()`。复用 `common` 中的像素读取函数处理数据保留。
|
||||
|
||||
颜色空间和 Alpha 都属于 Image 数据块属性。配置作用于本次新建材质专用的 Color Image;当源图还被 Empty、普通图片编辑或已有材质引用时,先复制并仅绑定本次新建材质的 Color 节点。复用 `image_pixels()` 读取业务像素,对 generated、dirty、packed 和文件来源验证两项设置先后变化后的内容保留。
|
||||
|
||||
### 与已有 Alpha 变更的协调
|
||||
|
||||
活动变更 `enable-image-alpha-blending-and-premul` 中 `Mesh Color images use PREMUL independently of data textures` 的材质规则由本变更细化为颜色空间配对策略。实施时调整对应材质断言,归档时保证最终主规格采用此规则。普通编辑结果默认 PREMUL、Empty Alpha Blending、Depth 与 Normal 数据解释继续按各自规格执行。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- inverse + STRAIGHT 的边缘优势来自用户观察,需使用实际半透明边缘在 Blender 中复核;属性断言只能验证策略落地。
|
||||
- Image 颜色空间或 Alpha 切换可能改变像素读取解释,需覆盖 Pack、保存重载以及重新作为编辑源的行为。
|
||||
- 新建材质使用共享源图片时,需要隔离本次 Color 配置,保障已有材质及图片的其他用途。
|
||||
|
||||
## Validation
|
||||
|
||||
测试偏好默认值、UI、注册与逆序注销;覆盖各视图映射、关闭状态、Standard 和 inverse 不可用的回退,并断言创建及回退前后偏好值保持一致。对 Plane、Depth Plane、Cutout、剪贴板材质验证切换偏好后已有 Color 保持原值、新建 Color 使用当前设置及共享图片隔离。回归验证 Empty、普通编辑结果、Depth、Normal 和非目标材质保持原值。对 byte、float、generated、dirty、packed 材质图片验证业务 RGB/Alpha 保留及保存重载;在实际 Blender 中比较半透明边缘并记录结果。
|
||||
|
||||
实施完成后运行 `uv run pytest`。本次实施仅修改实现和测试。
|
||||
@@ -0,0 +1,29 @@
|
||||
## Why
|
||||
|
||||
用户实测材质颜色贴图在 inverse 类颜色空间下使用 STRAIGHT 边缘更好,在 sRGB 下使用 PREMUL 效果合适。当前材质入口自动适配场景 View Transform 并统一设置 PREMUL,需要提供着色偏好,让用户选择适配方式并配套正确的 Alpha 模式。
|
||||
|
||||
## What Changes
|
||||
|
||||
- Preferences 新增 Shading 分组和 Adapt to Scene View Transform(适配场景视图变换)开关,默认关闭。
|
||||
- 开启时根据当前场景选择对应 inverse 颜色空间,并设置 STRAIGHT。
|
||||
- 关闭时使用 sRGB + PREMUL;开启适配但实际回退到 sRGB 时也使用 PREMUL。
|
||||
- Preferences 是适配开关的唯一控制来源;新建材质读取当前设置,已创建的材质贴图保留原配置。
|
||||
- Plane、Depth Plane、Cutout 和剪贴板 Mesh 材质的 Color Image 统一应用该策略,保留业务像素和源图片独立性。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `material-view-adaptation`: 材质视图适配偏好、颜色空间与 Alpha 配对、应用时机和像素保留。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。当前主规格目录为空;与活动变更 `enable-image-alpha-blending-and-premul` 的 Mesh Color 默认值重叠部分,在实施和归档时按本变更的配对策略协调。
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/anyimage/preferences.py`:偏好属性、统一读取入口和 Shading UI。
|
||||
- `src/anyimage/common/material.py`:颜色空间选择与材质 Color 配置。
|
||||
- `src/anyimage/common/image.py`:复用现有业务像素读取能力;材质专属策略由 `common/material.py` 承担。
|
||||
- `src/anyimage/operators/clipboard_image/actions.py`:接入公共材质 Color 配置。
|
||||
- Blender 偏好注册、材质配置、Alpha 数据保留和转换入口测试。
|
||||
@@ -0,0 +1,108 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Preferences expose material view adaptation
|
||||
|
||||
AnyImage SHALL 在 Preferences 的 Shading 分组提供 `Adapt to Scene View Transform` 布尔选项,默认关闭。扩展设置及读取入口 SHALL 位于 `preferences.py`,并通过 `addon_preferences()` 读取。
|
||||
|
||||
#### Scenario: Preferences use the default
|
||||
|
||||
- **WHEN** 用户尚未设置该选项或读取不到扩展偏好
|
||||
- **THEN** 材质颜色贴图 SHALL 使用关闭适配的行为
|
||||
|
||||
### Requirement: Material color space determines alpha presentation
|
||||
|
||||
开启适配时,AnyImage SHALL 按当前场景 View Transform 选择既有映射中的 inverse 颜色空间,并使用 STRAIGHT。关闭适配时 SHALL 使用 sRGB + PREMUL。实际回退为 sRGB 时 SHALL 使用 PREMUL,并保持 Preferences 中的适配开关原值。
|
||||
|
||||
#### Scenario: A supported view is adapted
|
||||
|
||||
- **WHEN** 适配开启,当前场景为 AgX,且 `AgX Base sRGB` 可用
|
||||
- **THEN** 材质 Color Image SHALL 使用 `AgX Base sRGB` + STRAIGHT
|
||||
|
||||
#### Scenario: Other mapped views are adapted
|
||||
|
||||
- **WHEN** 适配开启,场景使用 ACES 1.3、ACES 2.0、Filmic 或 Khronos PBR Neutral,且对应空间可用
|
||||
- **THEN** Color Image SHALL 分别使用 `ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB` 或 `Khronos PBR Neutral sRGB`
|
||||
- **AND** Alpha 模式 SHALL 为 STRAIGHT
|
||||
|
||||
#### Scenario: Adaptation is disabled
|
||||
|
||||
- **WHEN** 适配关闭,创建任意受支持入口的材质
|
||||
- **THEN** Color Image SHALL 使用 sRGB + PREMUL
|
||||
|
||||
#### Scenario: The scene resolves to sRGB
|
||||
|
||||
- **WHEN** 适配开启,但场景为 Standard、视图未匹配、场景不可用或 inverse 空间不被支持
|
||||
- **THEN** Color Image SHALL 使用 sRGB + PREMUL
|
||||
|
||||
#### Scenario: The fallback color space is unavailable
|
||||
|
||||
- **WHEN** 所需颜色空间及 sRGB 均无法应用
|
||||
- **THEN** 配置 SHALL 明确报错并保留贴图之前的有效配置
|
||||
- **AND** Preferences 中的适配开关 SHALL 保持原值
|
||||
|
||||
### Requirement: Material entry points apply the same policy
|
||||
|
||||
AnyImage SHALL 在 Plane、Depth Plane、Cutout 和剪贴板 Mesh 材质创建时统一配置 Color Image。最终模式 SHALL 在后续绑定及交付步骤中保留。Depth 与 Normal SHALL 保留各自的数据解释约定。
|
||||
|
||||
#### Scenario: A material is created from an edited color result
|
||||
|
||||
- **WHEN** 一个 PREMUL 编辑结果进入开启适配的 inverse 材质
|
||||
- **THEN** 最终材质 Color Image SHALL 为 STRAIGHT
|
||||
- **AND** 材质交付后 SHALL 保留该模式
|
||||
|
||||
#### Scenario: Shadeless material is created
|
||||
|
||||
- **WHEN** 用户通过支持 Shadeless 的入口创建材质
|
||||
- **THEN** Color Image SHALL 应用相同颜色空间与 Alpha 配对策略
|
||||
|
||||
### Requirement: Configuration preserves business pixels and source ownership
|
||||
|
||||
材质 Color 配置 SHALL 保留原有业务 RGB、Alpha 和完全透明区域 RGB,并在 Pack、保存重载和再次编辑后保持可读取。配置 SHALL 作用于材质专用 Color Image 或独立 Color 副本。
|
||||
|
||||
#### Scenario: The source image has other users
|
||||
|
||||
- **WHEN** 源 Image 同时被其他对象引用
|
||||
- **THEN** 配置 SHALL 作用于独立 Color 结果
|
||||
- **AND** 原 Image 的颜色空间、Alpha 模式和业务像素 SHALL 保持原值
|
||||
|
||||
#### Scenario: A color result is packed and reopened
|
||||
|
||||
- **WHEN** byte、float、generated、dirty 或 packed 来源的 Color 完成配置并保存重载
|
||||
- **THEN** 业务 RGB/Alpha SHALL 在既有编码精度内保留
|
||||
- **AND** 结果 SHALL 保持所选颜色空间和 Alpha 模式
|
||||
|
||||
### Requirement: Preferences exclusively control material adaptation
|
||||
|
||||
Preferences SHALL 是适配开关的唯一控制来源。新建材质 SHALL 读取当前偏好;已创建的材质颜色贴图 SHALL 保留原配置。创建操作和颜色空间解析 SHALL 保持偏好值。
|
||||
|
||||
#### Scenario: The preference changes
|
||||
|
||||
- **WHEN** 用户在 Preferences 开启或关闭适配
|
||||
- **THEN** AnyImage 已创建的材质颜色贴图 SHALL 保持原颜色空间、Alpha 模式和像素
|
||||
- **AND** 后续新建材质 SHALL 使用新的偏好设置
|
||||
|
||||
#### Scenario: A new material shares its source with an existing material
|
||||
|
||||
- **WHEN** 切换偏好后,使用已被现有材质引用的源 Image 创建新材质
|
||||
- **THEN** 新材质 SHALL 使用独立 Color Image 并应用当前偏好
|
||||
- **AND** 已有材质的贴图及其配置 SHALL 保持原值
|
||||
|
||||
#### Scenario: A material is created or an inverse space is unavailable
|
||||
|
||||
- **WHEN** 创建材质或颜色空间解析发生回退
|
||||
- **THEN** Preferences 中的适配开关 SHALL 保持操作前的值
|
||||
|
||||
### Requirement: Adaptation affects only material color textures
|
||||
|
||||
本选项 SHALL 仅配置 AnyImage 材质的 Color 贴图。Empty、普通图片编辑结果、Depth、Normal 和非目标材质 SHALL 保持原有行为与数据。
|
||||
|
||||
#### Scenario: A material color image is shared with an Empty
|
||||
|
||||
- **WHEN** 新建材质的源 Color Image 同时被 Empty 引用
|
||||
- **THEN** 系统 SHALL 为材质 Color 创建独立副本并重新绑定目标节点
|
||||
- **AND** Empty 引用的原 Image 的颜色空间、Alpha 模式与业务像素 SHALL 保持原值
|
||||
|
||||
#### Scenario: Ordinary image editing runs with adaptation enabled
|
||||
|
||||
- **WHEN** 开启适配后执行 Mask、Frame、Rectify 或图片编辑 Job
|
||||
- **THEN** 编辑结果 SHALL 沿用各自已有颜色空间与 Alpha 规则
|
||||
@@ -0,0 +1,32 @@
|
||||
## 1. 着色偏好
|
||||
|
||||
- [x] 1.1 在 `preferences.py` 增加默认关闭的 `adapt_material_to_view_transform`、统一读取入口和 Shading 分组。
|
||||
- [x] 1.2 补充偏好默认值、读取回退、UI 展示测试,并检查 `__init__.py` 注册和逆序注销。
|
||||
|
||||
## 2. 材质配置
|
||||
|
||||
- [x] 2.1 在 `common/material.py` 集中颜色空间与 Alpha 配对:inverse + STRAIGHT,sRGB + PREMUL;实现映射、回退和设置失败处理。
|
||||
- [x] 2.2 在材质专属配置入口设置 Alpha,复用现有业务像素读取能力,验证颜色空间与 Alpha 连续切换的数据完整性。
|
||||
- [x] 2.3 接入公共材质与剪贴板 Mesh 入口,审查 Plane、Depth Plane、Cutout 的独立 Color 所有权及最终交付模式。
|
||||
- [x] 2.4 确保新建材质使用独立 Color 图片,隔离共享源对已有材质及其他用途的影响。
|
||||
|
||||
## 3. 验证
|
||||
|
||||
- [x] 3.1 覆盖开关两种状态、全部既有视图映射、Standard、场景缺失、inverse 不可用及 sRGB 设置失败。
|
||||
- [x] 3.2 覆盖各材质入口及 Shadeless,验证切换偏好后已有材质 Color 保持原值,新建材质读取当前设置,创建和回退均保持偏好值。
|
||||
- [x] 3.3 更新现有材质 PREMUL 断言,验证 byte、float、generated、dirty、packed Color 的 RGB/Alpha、透明 RGB、Pack、保存重载和再次编辑。
|
||||
- [x] 3.4 在实际 Blender 中用半透明边缘复核 inverse + STRAIGHT 与 sRGB + PREMUL 的效果,并记录验证结果。
|
||||
- [x] 3.5 运行 `uv run pytest`,修复相关回归。
|
||||
- [x] 3.6 验证仅材质 Color 受影响:共享源、Empty、Mask/Frame/Rectify、图片编辑 Job、Depth、Normal 和非目标材质保持原有行为与数据。
|
||||
|
||||
## 4. 规格收敛
|
||||
|
||||
- [x] 4.1 归档时协调 `enable-image-alpha-blending-and-premul` 的 Mesh Color 需求,使最终主规格统一采用本变更的颜色空间与 Alpha 配对。
|
||||
|
||||
## 验证记录
|
||||
|
||||
2026-09-05:`uv run --no-sync pytest -q --tb=short`:562 passed、2 xfailed、56 subtests passed。`--no-sync` 使用本轮已同步的项目环境。OpenSpec strict 校验通过。
|
||||
|
||||
Blender 4.5.3 LTS / Cycles CPU、AgX、16 samples:渲染 64×64 橙色圆形贴图的渐变 Alpha 边缘,192×192 输出。sRGB + PREMUL 和 AgX Base sRGB + STRAIGHT 均正常显示连续半透明边缘;后者中心色更接近输入橙色。该检查确认两种策略可用。
|
||||
|
||||
3.3 部分完成:byte、generated byte、dirty、文件和 packed 图片的业务像素及保存重载通过;原生浮点图片在保存重载后的 RGB 不满足原值断言,两种设置均可复现,已用严格 xfail 保留检查。即时配置和共享源保留通过。此项尚未解决,因此保留未完成状态。4.1 留待归档阶段处理。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-01
|
||||
@@ -0,0 +1,68 @@
|
||||
## Context
|
||||
|
||||
AnyImage 的 ONNX 推理把设备值 `mps` 映射到 `CoreMLExecutionProvider`,但该名称通常指 PyTorch Metal backend,与实际实现不一致。Blender 端还通过 CPU brand string 将 Apple 加速限制为 M4 及以上,而 macOS 扩展本身只发布 `macos-arm64`,支持范围已经限定为 Apple Silicon。
|
||||
|
||||
共享 Session 工厂目前为所有 CoreML 模型强制传入 `ModelFormat=MLProgram`。MoGe-2 的 ONNX 图具有动态空间尺寸、动态 `num_tokens`、控制流和大量 shape 运算,CoreML 在 MacBook M4 上会在 Session 创建阶段因执行计划构建失败;加入该配置前,同一 Provider 使用默认模型格式可以运行。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 让设备名称准确表达 CoreML Execution Provider。
|
||||
- 在全部受支持的 macOS arm64 设备上提供 CoreML。
|
||||
- 恢复 MoGe-2 在 MacBook M4 CoreML 模式下的 Session 创建与推理。
|
||||
- 保持共享 ONNX Session 工厂简单,并保留 CPU fallback。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 引入 PyTorch 或实现 PyTorch MPS backend。
|
||||
- 保证所有 CoreML 算子只在 GPU 上执行。
|
||||
- 支持 macOS x64、iOS 或新的模型格式。
|
||||
- 保留 `mps` 任务值、配置值或转发映射。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 使用 `coreml` 作为唯一 Apple 加速设备标识
|
||||
|
||||
Blender Enum、Job 参数、运行时设备清单和 Provider 选择表统一使用小写 `coreml`,界面显示 `CoreML`。旧 `mps` 标识直接删除,因为它描述的是不同的推理 API;增加别名会继续扩大协议边界并违反项目不保留兼容层的约束。
|
||||
|
||||
### 2. 按平台能力判断 CoreML,不按芯片代际判断
|
||||
|
||||
macOS 且机器架构为 `arm64` 或 `aarch64` 时提供 `coreml` 和 `cpu`,其他 Unix 平台只提供 `cpu`,Windows 维持 DirectML 与 CPU。删除 CPU brand string 正则、`sysctl` 子进程和 M4 代际测试。这样支持范围与 `macos-arm64` 扩展包以及 ONNX Runtime macOS arm64 wheel 一致。
|
||||
|
||||
保留 M4 门槛的替代方案被放弃,因为它不是 Provider 能力判断,只会让 M1–M3 在具备 CoreML 时被迫使用 CPU。
|
||||
|
||||
### 3. 默认 CoreML 配置与 MoGe-2 静态分区配置分离
|
||||
|
||||
共享 Session 工厂默认向 ONNX Runtime 传递普通的 `CoreMLExecutionProvider` 名称,不再为所有模型强制 `MLProgram`。它同时接受可选的模型级 CoreML options;只有调用方明确提供时才把 CoreML Provider 配置为 tuple。CUDA 的独立 arena 配置保持不变。
|
||||
|
||||
MoGe-2 传入 `RequireStaticInputShapes=1`,使 CoreML EP 只接管输入 shape 已静态确定的分区,动态空间尺寸、动态 `num_tokens` 和相关控制流留给 CPU fallback。BEN2、DA3 与 Upscale 不携带该选项,继续使用普通 CoreML 配置。
|
||||
|
||||
该决定取代“所有模型都使用普通 CoreML 配置”的原方案:M2 真机证明 NeuralNetwork 格式虽然能创建 MoGe-2 Session,但动态 CoreML 分区仍会在执行时失败。也不选择 `CPUAndGPU`,因为故障来自动态图兼容性,而不是 Neural Engine 选择;不选择整体强制 CPU,因为静态限制仍允许安全分区使用 CoreML。
|
||||
|
||||
### 4. macOS 界面默认 CPU,CoreML Session 保持 CPU fallback
|
||||
|
||||
macOS arm64 的设备枚举把 CPU 放在 CoreML 之前,使新安装和未保存设备选择时默认使用 CPU。CoreML 的性能取决于模型和设备,用户需要时可手动选择,不根据芯片代际自动启用。
|
||||
|
||||
显式选择 `coreml` 时仍以 `CoreMLExecutionProvider` 为首选并显式附带 `CPUExecutionProvider`;共享 Session 工厂的自动 Provider 顺序也保持不变。这样 MoGe-2 被静态限制排除的动态节点具有确定的 CPU fallback;明确选择 CPU 时则只请求 CPU Provider。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [CoreML 默认配置在未来 ONNX Runtime 版本中可能变化] → 固定 ONNX Runtime 版本,并以 Session 参数测试保证 AnyImage 不再主动强制 `MLProgram`。
|
||||
- [MoGe-2 静态分区限制可能减少 CoreML 加速比例] → 接受 CPU fallback 以换取正确性,并用真机 smoke test 验证;不把 CoreML 设备描述为纯 GPU。
|
||||
- [M1–M3 未覆盖真实设备集成测试] → 单元测试覆盖平台枚举,并在可用 Apple Silicon 设备上执行 MoGe-2 smoke test;真实失败仍可选择 CPU。
|
||||
- [旧 Blender 配置保存了 `MPS`] → 这是明确的 breaking rename;不解析旧值,用户在新版中选择 `CoreML` 或 `CPU`。
|
||||
- [CoreML 不保证纯 GPU] → 界面和文档只承诺 CoreML,不再使用 Metal/MPS 或纯 GPU 表述。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 同步替换设备协议与界面枚举中的 `mps` 为 `coreml`,删除芯片代际检测。
|
||||
2. 移除共享的强制 CoreML options,让普通模型使用默认配置,并让 MoGe-2 单独传入 `RequireStaticInputShapes=1`。
|
||||
3. 更新测试和内部文档,再运行相关单元测试。
|
||||
4. 在可用的 M2 或 M4 设备选择 CoreML,分别执行 MoGe-2、BEN2 和 Upscale smoke test;MoGe-2 必须完成 Session 创建和一次推理。
|
||||
|
||||
若真实设备验证失败,回滚本变更并使用 CPU;不重新引入 `mps` 兼容层。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无。
|
||||
@@ -0,0 +1,30 @@
|
||||
## Why
|
||||
|
||||
macOS 设备当前以 `mps` 表示实际的 ONNX Runtime CoreML Execution Provider,并人为限制为 M4 及以上;同时强制使用 `MLProgram` 会使具有动态输入和控制流的 MoGe-2 在 MacBook M4 上创建模型时触发 `Error in building plan`。需要让设备名称、支持范围和实际推理后端一致,并恢复 MoGe-2 可用性。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**:将公开任务参数、运行时设备清单和界面枚举中的设备标识由 `mps` 改为 `coreml`,不保留旧标识兼容层。
|
||||
- macOS arm64 构建对所有 Apple Silicon 设备提供 CoreML 与 CPU 选项,移除 Apple 芯片代际解析和 M4 最低代际限制。
|
||||
- Apple Silicon Mac 默认使用 CPU,CoreML 保留为用户手动选择的可选设备。
|
||||
- 通用 CoreML Session 使用 ONNX Runtime 的兼容默认模型格式,不再强制把所有模型转换为 `MLProgram`。
|
||||
- MoGe-2 为 CoreML 单独启用静态输入分区限制,只将静态 shape 分区交给 CoreML,并把其余节点交给 CPU Execution Provider。
|
||||
- 显式保留 CoreML 之后的 CPU fallback,并验证 MoGe-2、BEN2 与 Upscale 的设备选择不回归。
|
||||
- 同步更新设备相关测试和内部文档,使 `coreml` 明确表示 CoreML 调度的 Apple CPU、GPU 与 Neural Engine,而不是 PyTorch MPS backend。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `coreml-inference`: 规定 macOS arm64 的 CoreML 设备标识、支持范围、Provider 选择和 MoGe-2 兼容行为。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
- 影响 Blender 设备枚举、Job 参数、运行时 Manifest 和 ONNX Runtime Session 创建。
|
||||
- 影响 `properties.py`、`server/models/onnx_runtime.py`、`server/models/onnx_moge2.py` 及对应测试与内部文档。
|
||||
- `mps` 设备值会被删除;尚未完成或外部构造的旧任务参数必须改用 `coreml`。
|
||||
- 不新增依赖,不改变模型文件、模型输出协议或 Blender Operator 标识符。
|
||||
@@ -0,0 +1,69 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: CoreML 使用唯一设备标识
|
||||
系统 SHALL 在界面、任务参数、运行时设备清单和 ONNX Provider 选择中使用 `coreml` 表示 Apple CoreML Execution Provider,并 SHALL NOT 接受或生成 `mps` 设备标识。
|
||||
|
||||
#### Scenario: macOS 用户查看推理设备
|
||||
- **WHEN** 用户在受支持的 macOS arm64 设备上查看推理设备枚举
|
||||
- **THEN** 系统显示 `CoreML` 和 `CPU`,且不显示 `MPS`
|
||||
|
||||
#### Scenario: CoreML 任务参数
|
||||
- **WHEN** 用户选择 CoreML 并创建 AI 任务
|
||||
- **THEN** 系统提交的设备参数为 `coreml`
|
||||
|
||||
#### Scenario: 旧设备标识不再受支持
|
||||
- **WHEN** Provider 选择入口接收到 `mps`
|
||||
- **THEN** 系统拒绝该值,而不将其转换或转发为 CoreML
|
||||
|
||||
### Requirement: Apple Silicon 支持范围按平台确定
|
||||
系统 SHALL 在 macOS 的 `arm64` 或 `aarch64` 架构上提供 CoreML,不得根据 Apple 芯片代际限制该设备。
|
||||
|
||||
#### Scenario: M1 至 M3 设备
|
||||
- **WHEN** 系统运行于 M1、M2 或 M3 的 macOS arm64 环境
|
||||
- **THEN** 可用设备包含 `coreml` 和 `cpu`
|
||||
|
||||
#### Scenario: M4 或更新设备
|
||||
- **WHEN** 系统运行于 M4 或更新的 macOS arm64 环境
|
||||
- **THEN** 可用设备包含 `coreml` 和 `cpu`
|
||||
|
||||
#### Scenario: Apple Silicon 默认设备
|
||||
- **WHEN** macOS arm64 同时提供 CPU 与 CoreML
|
||||
- **THEN** 设备枚举默认选择 CPU,且用户仍可手动选择 CoreML
|
||||
|
||||
#### Scenario: 非 Apple Silicon 平台
|
||||
- **WHEN** 系统不运行于 macOS arm64 或 aarch64 环境
|
||||
- **THEN** 系统不因 CoreML 规则新增 `coreml` 设备
|
||||
|
||||
### Requirement: CoreML Session 使用模型兼容配置
|
||||
系统 SHALL 以 `CoreMLExecutionProvider` 创建 CoreML Session,并 SHALL NOT 强制设置 `ModelFormat=MLProgram`。普通模型 SHALL 使用默认 CoreML 配置;MoGe-2 SHALL 只允许 CoreML 接管静态输入 shape 分区;CoreML 不支持或未接管的执行 SHALL 由 CPU fallback 处理。
|
||||
|
||||
#### Scenario: 显式选择 CoreML
|
||||
- **WHEN** 任务请求设备 `coreml` 且 ONNX Runtime 提供 CoreML Execution Provider
|
||||
- **THEN** Session 以 CoreML 为首选 Provider、以 CPU 为后备 Provider,且创建参数不包含强制的 CoreML `MLProgram` 配置
|
||||
|
||||
#### Scenario: MoGe-2 限制 CoreML 动态分区
|
||||
- **WHEN** 系统以 CoreML 创建 MoGe-2 Session
|
||||
- **THEN** CoreML Provider options 包含 `RequireStaticInputShapes=1`,且不包含 `ModelFormat=MLProgram`
|
||||
|
||||
#### Scenario: 其他模型不继承 MoGe-2 限制
|
||||
- **WHEN** 系统以 CoreML 创建 BEN2、DA3 或 Upscale Session
|
||||
- **THEN** CoreML Provider 不包含 MoGe-2 的静态输入限制
|
||||
|
||||
#### Scenario: 自动选择 CoreML
|
||||
- **WHEN** 任务请求自动设备且 CoreML 与 CPU Provider 都可用
|
||||
- **THEN** CoreML 的优先级高于 CPU,并保留 CPU fallback
|
||||
|
||||
#### Scenario: CoreML 不可用
|
||||
- **WHEN** 任务请求 CoreML 但 ONNX Runtime 只提供 CPU Provider
|
||||
- **THEN** 系统使用 CPU Provider
|
||||
|
||||
### Requirement: MoGe-2 可在 CoreML 模式推理
|
||||
系统 MUST 支持 MoGe-2 ONNX 模型在兼容 Apple Silicon CoreML 环境中创建 Session 并完成推理,不得因 AnyImage 强制选择 MLProgram 而在创建模型时失败。
|
||||
|
||||
#### Scenario: Apple Silicon 执行 MoGe-2
|
||||
- **WHEN** M2 或 M4 用户选择 CoreML 并运行 MoGe-2 功能
|
||||
- **THEN** 系统成功创建 CoreML Session 并完成至少一帧 MoGe-2 推理
|
||||
|
||||
#### Scenario: 其他 AI 模型继续使用 CoreML
|
||||
- **WHEN** 用户选择 CoreML 并运行 BEN2 或 Upscale
|
||||
- **THEN** 对应模型继续完成 Session 创建和推理
|
||||
@@ -0,0 +1,18 @@
|
||||
## 1. 统一 CoreML 设备协议
|
||||
|
||||
- [x] 1.1 将 Blender 设备标签、Enum 值、任务参数和 ONNX Provider 映射中的 `mps` 全部替换为 `coreml`,删除旧值的兼容路径。
|
||||
- [x] 1.2 删除 Apple 芯片品牌解析、`sysctl` 调用和 M4 代际限制,按 macOS arm64/aarch64 平台提供 `coreml` 与 `cpu`。
|
||||
- [x] 1.3 调整设备枚举测试,覆盖 M1–M4 所在的 macOS arm64、非 Apple Silicon 平台、Manifest 设备清单和旧 `mps` 值拒绝行为。
|
||||
- [x] 1.4 在全部 Apple Silicon Mac 上默认选择 CPU,并保留 CoreML 手动选项。
|
||||
|
||||
## 2. 恢复 MoGe-2 CoreML 兼容性
|
||||
|
||||
- [x] 2.1 让共享 Session 工厂默认使用普通 `CoreMLExecutionProvider`,同时支持可选模型级 CoreML options;显式 CoreML 选择保留 CPU fallback,CUDA arena 配置不变。
|
||||
- [x] 2.2 更新共享 ONNX Runtime 测试,验证默认配置、可选 CoreML options、显式/自动 CPU fallback 以及 `mps` 拒绝行为。
|
||||
- [x] 2.3 让 MoGe-2 CoreML Session 传入 `RequireStaticInputShapes=1` 且不强制 `MLProgram`,更新测试并保持动态图片与 `num_tokens` 输入不变。
|
||||
|
||||
## 3. 文档与验证
|
||||
|
||||
- [x] 3.1 更新用户设备说明和 `docs/internals/runtime.md`,统一使用 CoreML,并说明它由 Apple CPU、GPU 与 Neural Engine 调度而非 PyTorch MPS。
|
||||
- [x] 3.2 运行 `uv run pytest tests/test_blender_addon.py tests/test_model_onnx_runtime.py tests/test_model_moge2_onnx.py tests/test_docs.py`。
|
||||
- [x] 3.3 在可用的 M2 或 M4 设备选择 CoreML,分别完成 MoGe-2、BEN2 和 Upscale 的单次 smoke test,确认 MoGe-2 不再出现 CoreML 计划构建或预测执行错误。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-04
|
||||
@@ -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 验证作为实施门槛。
|
||||
@@ -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 单元与集成测试。沿用现有依赖、节点资产和编辑算法;实施阶段不同步修改产品文档,不构建产物。
|
||||
@@ -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 倍高的输出副本
|
||||
@@ -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 保持原样
|
||||
@@ -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 视口、多图排序与完整序列验收尚未执行。
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-05
|
||||
@@ -0,0 +1,69 @@
|
||||
## Context
|
||||
|
||||
Cutout 的矩形 RGBA 经黑底合成后进入 MoGe,Vector Depth 保存相机 XYZ,节点按 UV 线性采样。边缘 Alpha 可用于保留毛发,但其深度可能属于背景。
|
||||
|
||||
本次实验使用 BEN2、MoGe-2 ViT-S Level 5,固定猫(585×643)和人物(234×222)的同一份预测。Alpha ≥ 0.95、内缩 2 px、最近点外延、σ=0.8 px 局部平滑明显减少拖尾。替换深度的可见像素分别约 4.4% 与 9.6%;可靠内部保持原值。8 px 内缩替换人物约 27% 的可见区域,形状变化明显。
|
||||
|
||||
实验采用独立投影网格。完整 Blender 的线性采样、平滑、厚度、拉伸剔除及 Normal Map 组合需要在实施阶段验收。实验记录位于 `C:/Users/icrdr/.codex/visualizations/2026/09/05/01a071bc-9eae-7f91-8791-816aa621fd8a/`,其中 `depth_experiment.py` 保存推理入口,`analyze_depth.py` 保存对比算法,`metrics.json` 保存结果;该目录是本次本地证据,自动测试使用可重建的合成数据。
|
||||
|
||||
## Goals
|
||||
|
||||
保留 Alpha 0.1 的可见轮廓,以可靠内部深度补齐边缘,并保持相机投影与 Cutout 产物单位一致。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 深度修复入口
|
||||
|
||||
在 `server/geometry/edge_depth.py` 集中处理普通数组和 GeometryFrame。`generate_moge2_artifacts()` 增加内部显式选项,由 Cutout Job 在生成深度时开启。Depth Surface 与 Depth Balloon 分别输出修复后的 Vector Depth、Z Depth,并使用对应 Metadata。Normal Writer 继续消费原始预测;普通 Depth Plane 和仅生成 Normal 的路径维持现有入口行为。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Cutout RGBA] --> B[现有黑底合成与 MoGe 推理]
|
||||
B --> C[原始 GeometryFrame]
|
||||
A --> D[对齐到深度图的连续 Alpha]
|
||||
C --> E[构建可靠内部]
|
||||
D --> E
|
||||
E --> F[分量内深度外延与局部平滑]
|
||||
F --> G[按当前像素射线重建 XYZ]
|
||||
G --> H[输出 Vector/Z Depth 与 Metadata]
|
||||
C --> I[现有 Normal Writer]
|
||||
H --> J[现有 Cutout 修改器]
|
||||
```
|
||||
|
||||
### 可见域与可靠域
|
||||
|
||||
- 连续 Alpha 使用与现有 `_alpha_mask()` 相同的最近邻尺寸对齐;可见域为 Alpha ≥ 0.1。
|
||||
- 可靠候选为 Alpha ≥ 0.95、模型 Validity > 0、深度有限且为正。
|
||||
- 在深度图输出像素单位下,保留到候选域外欧氏距离大于 2 px 的可靠核心;图片矩形边界按边缘延续处理,避免把贴边主体当作透明边缘内缩。
|
||||
- 全不透明源图直接使用原始 Frame。所有参数先固定在服务端实现中。
|
||||
|
||||
### 外延与回退
|
||||
|
||||
可见域按 8 连通分量处理,每个分量只使用自身的可靠核心作为供体。没有内缩核心时使用该分量内未内缩的可靠候选;连候选也没有时保留该分量的原始预测。这样细小或半透明分量具有确定的结果。
|
||||
|
||||
对有供体的分量,以最近可靠像素的标量深度补齐非核心区域。透明背景在可见域外扩 4 px 内接收最近可修复分量的延伸值,覆盖平滑核与线性采样所需的邻域;其他可见分量的值由自身规则决定。局部计算边界须保留滤波所需的 padding。
|
||||
|
||||
外延结果用 σ=0.8 px、半径 3 px 的 Gaussian 核平滑,仅写回补齐区域;可靠核心逐值保留。分量内平滑使用本分量的外延场,避免滤波读入其他分量深度。未处理区域保留原值。
|
||||
|
||||
### 投影与产物一致性
|
||||
|
||||
使用 Frame 的像素单位内参和当前像素中心,由修复后的 Z 重建相机 X、Y。复用服务端现有深度反投影函数时,须显式处理其归一化内参约定。核心位置保留原始 XYZ,防止不必要的浮点变化。
|
||||
|
||||
修复后的 Frame 保留原模型 Validity;修复不会把模型有效性改写为预测置信度。Metadata 使用修复后的 Depth、原模型 Validity 和现有参考 Alpha Mask 计算,确保参考深度与导出贴图一致。颜色 Alpha、UV 与 Selection Mask 继续由现有路径管理。
|
||||
|
||||
### 数值依赖
|
||||
|
||||
复用 SciPy 的距离变换、连通分量和 Gaussian 滤波。实施时选择同时支持开发 Python 3.11、服务端 Python 3.12 及其 NumPy 约束的版本,并同步运行环境声明、开发依赖与锁文件。处理按分量局部范围执行,验证 2048 长边输入的时间和峰值内存。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 高 Alpha 不等同于正确深度 → 保留 2 px 内缩,并用真实深度场检查残余污染。
|
||||
- 最近供体在同一分量内可能跨越前后表面 → 保持小内缩、仅局部平滑,并验收耳朵、衣领和手部交界。
|
||||
- 半透明孤立分量没有可靠供体时仍可能后缩 → 保留原始预测,测试回退的有限值和确定性。
|
||||
- 固定像素宽度在不同输入尺寸下覆盖比例不同 → 以深度图像素明确单位,验证两张原图及缩放输入;进一步调整参数须有新增证据。
|
||||
- 原始 Normal Map 与修复几何在边缘可能不一致 → 分别验收 Normal 开关下的边缘着色,记录残余问题。
|
||||
- 边缘跳变减小只证明拉伸改善,并不等同于真实三维深度准确 → 同时核对可见域、正面投影和侧视外观。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
新任务在模型产物导出阶段应用修复,已有 Blender 对象通过重新生成 Cutout 获得结果。实施完成后运行相关测试和完整 pytest,再对现有节点资产执行两张实图验收。
|
||||
@@ -0,0 +1,25 @@
|
||||
## Why
|
||||
|
||||
Cutout 用 Alpha 0.1 保留毛发时,边缘可能读取背景深度,形成严重后缩和长条拉伸。猫与人物的实图实验已验证:从可靠内部外延深度,可以保留原 Alpha 轮廓并明显减少拖尾。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 为 Cutout 的 Depth Surface 与 Depth Balloon 增加导出前的边缘深度修复。
|
||||
- 以 Alpha ≥ 0.95 且模型深度有效的内部区域为依据,内缩 2 个深度图像素后向边缘外延,并仅对补齐区域轻微平滑。
|
||||
- 保留颜色 Alpha、0.1 裁切阈值和可靠内部深度;按当前像素的相机射线重建 XYZ。
|
||||
- 为细小分量、无可靠深度和透明区采样增加确定的处理规则,并补充自动测试与 Blender 实图验收。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `cutout-edge-depth`: Cutout 的可靠深度区域、边缘外延、投影保持和产物一致性。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
- `server/geometry/`:增加深度修复模块,接入推理产物编排。
|
||||
- `server/jobs/cutout.py`:为 Cutout 深度产物启用修复。
|
||||
- `runtime.py`、`pyproject.toml`、`uv.lock`:加入 SciPy,复用距离变换与局部滤波,并检查环境及打包测试。
|
||||
- 服务端几何与任务测试,以及现有 Blender Cutout 修改器的实图验证。
|
||||
@@ -0,0 +1,65 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Reliable interior supplies edge depth
|
||||
|
||||
系统 SHALL 为包含透明内容的 Cutout 深度产物,从 Alpha ≥ 0.95、Validity > 0 且深度有限为正的候选域中,选择欧氏内缩 2 个深度图像素后的可靠核心。可见域 SHALL 继续使用 Alpha ≥ 0.1,核心深度 SHALL 逐值保留。
|
||||
|
||||
#### Scenario: Hair samples contain background depth
|
||||
- **WHEN** 半透明毛发仍属于可见域,但其预测深度接近背景,所属分量存在可靠核心
|
||||
- **THEN** 系统以该分量最近可靠核心的深度补齐毛发区域,并保留原颜色 Alpha 和裁切轮廓
|
||||
|
||||
#### Scenario: Subject reaches the image rectangle boundary
|
||||
- **WHEN** 不透明主体延伸到图片矩形边界
|
||||
- **THEN** 系统按边缘延续计算内缩,不将图片外部自动视为透明背景
|
||||
|
||||
### Requirement: Extension has deterministic component fallbacks
|
||||
|
||||
系统 SHALL 按可见域的 8 连通分量选择供体。缺少内缩核心时 SHALL 使用本分量未内缩的可靠候选;缺少可靠候选时 SHALL 保留该分量原始预测。
|
||||
|
||||
#### Scenario: Narrow component has no eroded core
|
||||
- **WHEN** 细小分量的可靠候选在 2 px 内缩后消失
|
||||
- **THEN** 系统使用该分量未内缩候选完成外延
|
||||
|
||||
#### Scenario: Translucent component has no reliable candidates
|
||||
- **WHEN** 独立分量完全由低 Alpha 或模型无效像素组成
|
||||
- **THEN** 系统保留该分量预测,不从另一可见分量复制深度
|
||||
|
||||
### Requirement: Filtered sampling uses extended depth
|
||||
|
||||
系统 SHALL 向可修复分量相邻透明区外扩 4 px 的范围延伸深度,并以 σ=0.8 px、半径 3 px 的 Gaussian 核仅平滑补齐区域。核心及其他未处理区域 SHALL 保留原值。
|
||||
|
||||
#### Scenario: Texture sampling straddles the silhouette
|
||||
- **WHEN** 线性采样邻域跨越可见边缘和邻近透明背景
|
||||
- **THEN** 透明侧提供外延深度,采样不会重新混入该邻域原来的背景深度
|
||||
|
||||
#### Scenario: Adjacent components have different depths
|
||||
- **WHEN** 两个可见分量分别位于不同深度
|
||||
- **THEN** 各分量的供体与平滑输入保持分量隔离
|
||||
|
||||
### Requirement: Corrected geometry preserves camera projection
|
||||
|
||||
系统 SHALL 根据修复后的 Z 和当前像素中心的相机射线重建 XYZ,使修复前后的相机投影保持一致,并保留核心原始 XYZ。
|
||||
|
||||
#### Scenario: Edge pixel inherits an interior depth
|
||||
- **WHEN** 边缘像素采用另一像素的可靠 Z
|
||||
- **THEN** 重建 XYZ 仍投影到边缘像素自身位置,而不是供体位置;数值测试的归一化投影误差 SHALL 小于 1e-6
|
||||
|
||||
### Requirement: Cutout artifacts share corrected depth
|
||||
|
||||
系统 SHALL 为 Depth Surface 与 Depth Balloon 的 Vector/Z Depth 和 Metadata 使用同一修复后深度。模型 Validity SHALL 保留其原始语义,Normal Writer SHALL 继续使用原始预测。
|
||||
|
||||
#### Scenario: Cutout exports vector depth and metadata
|
||||
- **WHEN** Cutout 请求深度产物
|
||||
- **THEN** Vector Depth 的 Z 与 Metadata 参考深度来自同一修复后深度场,所有输出坐标有限
|
||||
|
||||
#### Scenario: Existing artifact paths do not require correction
|
||||
- **WHEN** 输入完全不透明,或请求来自普通 Depth Plane,或 Cutout 只生成 Normal
|
||||
- **THEN** 系统使用对应的原始产物路径与预测行为
|
||||
|
||||
### Requirement: Edge correction is verified in the full Cutout pipeline
|
||||
|
||||
验收 SHALL 使用猫与人物原图依次执行 Remove Background 和 Cutout,固定同一 Alpha 与模型预测比较修复前后结果,并检查 Depth Surface、Depth Balloon 及 Normal 开关组合。
|
||||
|
||||
#### Scenario: Side-view regression comparison
|
||||
- **WHEN** 两张实图完成完整 Blender 修改器求值
|
||||
- **THEN** 主要边缘长条后缩明显减少,原 Alpha 可见域保持一致,可靠核心深度逐值不变,并记录残余台阶、着色或剔除问题
|
||||
@@ -0,0 +1,28 @@
|
||||
## 1. 数值依赖与深度修复
|
||||
|
||||
- [x] 1.1 确定兼容开发和服务端 Python、NumPy 的 SciPy 版本,同步 `runtime.py`、`pyproject.toml` 和 `uv.lock`,检查 Manifest 与环境、打包测试约束。
|
||||
- [x] 1.2 在 `server/geometry/edge_depth.py` 实现连续 Alpha 对齐、可靠候选、2 px 欧氏内缩及图片边缘延续,补充阈值和贴边主体测试。
|
||||
- [x] 1.3 实现按可见域 8 连通分量选取最近供体,以及无内缩核心、无候选的回退规则,测试细小分量与不同深度的相邻分量。
|
||||
- [x] 1.4 实现透明区 4 px 外扩和 σ=0.8 px、半径 3 px 的局部平滑,测试核心逐值不变、邻域采样与跨分量隔离。
|
||||
|
||||
## 2. 产物接入
|
||||
|
||||
- [x] 2.1 按 Frame 内参和当前像素射线重建修复区域 XYZ;复用现有服务端反投影能力,测试非方形图片、像素中心、有限值及小于 1e-6 的归一化投影误差。
|
||||
- [x] 2.2 为 `generate_moge2_artifacts()` 增加内部修复选项,由 Cutout Job 在深度请求中启用;Vector/Z Depth 与 Metadata 共用修复后 Frame,保留模型 Validity 和原始 Normal Writer 输入。
|
||||
- [x] 2.3 补充任务回归测试,覆盖 Depth Surface、Depth Balloon、完全不透明图片、普通 Depth Plane 和仅 Normal 请求的产物行为。
|
||||
|
||||
## 3. 验证
|
||||
|
||||
- [x] 3.1 运行数值、服务端任务、运行环境和打包相关测试,再运行 `uv run pytest`。
|
||||
- [x] 3.2 用可重建的 2048 长边输入检查处理时间、峰值内存和分量数量对开销的影响,确认局部范围处理可用。
|
||||
- [x] 3.3 使用两张用户原图重新执行 BEN2 与 MoGe,固定同一 Alpha 和预测,对比修复前后边缘跳变、可见域及正面投影;补充缩放输入检查,记录结果。
|
||||
- [x] 3.4 使用现有 Blender 节点资产验收两张图的 Depth Surface、Depth Balloon 和 Normal 开关组合,检查线性采样、平滑、厚度与拉伸剔除后的侧视拖尾及残余台阶、着色问题。
|
||||
|
||||
## 验证记录
|
||||
|
||||
- `uv run pytest -q`:524 passed,56 subtests passed。
|
||||
- SciPy 1.15.3 支持 Python ≥ 3.10、NumPy ≥ 1.23.5 且 < 2.5;已在开发和服务端环境执行。
|
||||
- 2048×2048、1/64/1024 个分量:0.445/0.399/0.394 秒;独立进程峰值工作集约 453/343/367 MiB(处理前约 229 MiB)。
|
||||
- 猫与人物原图及半尺寸图完成重新推理,正面投影误差均小于 3×10⁻⁸,可靠核心逐值保持。猫原图边缘跳变 P95 从约 14.86% 降至 0.10%,人物从约 2.98% 降至 0.35%(以各自参考深度归一化)。
|
||||
- Blender 4.5.3 现有资产完成 16 组渲染,并对平滑 2 次、厚度 0.02、Stretch Limit 0.05 组合额外求值;所有网格坐标有限。Depth Surface 主要拖尾消除,猫最大网格边长从 2.266 降至 0.107,人物从 1.311 降至 0.662。Depth Balloon 轮廓整体稳定,人物手部原有拉伸仍可见;原始 Normal 的边缘着色仍有局部差异。
|
||||
- 本地验证脚本、预测、指标及渲染位于 `C:/Users/icrdr/.codex/visualizations/2026/09/05/01a071bc-9eae-7f91-8791-816aa621fd8a/verification/`;脚本 `verify_edge_depth.py`、`verify_blender_depth.py` 位于其父目录。
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user