Files
TrueGrowth/docs/AI_IMAGE_MASK_EDIT_LESSONS.md

93 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI 图片蒙版编辑经验
更新日期2026-05-17
## 背景
GPT Image 的局部编辑不是把蒙版笔迹合成到参考图里,而是通过 `/images/edits` 的独立 `mask` multipart 字段表达编辑区域。画布 UI 可以把蒙版显示成半透明笔迹,但提交给模型时必须按 OpenAI 语义输出:透明区域表示需要编辑,非透明区域表示保留。
这次问题的核心不是 UI 是否能看到蒙版而是从画布选择、AI 输入框、任务创建、重新生成、同步/异步适配器到最终 FormData 的每一层都要保留 `maskImage`
## 问题表现
- 蒙版笔迹被当作普通画笔合成进参考图,最终请求没有独立 `mask` 参数。
- AI 输入框预览能显示参考图,但看不出该图携带蒙版,容易误判是否会局部编辑。
- 重新生成只回填 prompt 和参考图,漏掉原任务的 `maskImage`
- 异步图片链路只透传 `referenceImages`,未把 `maskImage` 带到 provider 表单。
- edit 请求与 generation 请求的默认参数不一致,`response_format` 没有统一默认到 `url`
## 修复思路
- 把“蒙版画笔”作为 Freehand 的一种 shape不新增独立实体大小、形状、快捷键和画笔体验沿用现有工具。
- 选择单张普通图片时扫描与图片相交的蒙版笔迹,按图片显示矩形裁剪,并输出与原图自然尺寸一致的 mask 图片。
- `processSelectedContentForAI` 返回 `maskImage`,并确保蒙版笔迹不再进入普通 `graphicsImage` 合成。
- AI 输入框创建图片编辑任务时使用 `referenceImages: [原图]``generationMode: "image_edit"``maskImage`,预览中同时展示原图和蒙版。
- 任务预填、重新生成、storage、executor、同步图片适配器和异步图片适配器都透传 `maskImage`
- GPT Image generation/edit 都默认写入 `response_format: "url"`edit FormData 必须同时包含 `image[]``mask`
## 代码落点
- `ai-mask-brush.ts`:蒙版笔迹发现、裁剪和导出。
- `selection-utils.ts`:选择内容时识别单图蒙版,避免把蒙版合成进普通图形。
- `AIInputBar.tsx` / `SelectedContentPreview.tsx`AI 输入框提交与蒙版预览。
- `image-task-prefill.ts` / `task-utils.ts`:重新生成、任务回填和 mask 元数据保留。
- `gpt-image-adapter.ts`GPT Image 请求体和 edit FormData 的默认 `response_format``mask` 字段。
- `async-image-api-service.ts` / `media-api` / `media-executor`:异步图片任务透传并提交 `maskImage`
- `popup-toolbar.tsx` / `freehand-panel.tsx` / `with-hotkey.ts`:蒙版反选入口、工具栏入口和快捷键。
## 经验规则
- 蒙版是任务参数,不是参考图的一部分;任何合成预览都不能替代最终请求里的独立 `mask`
- `maskImage` 必须和 `referenceImages` 一样进入任务协议、缓存解析、重新生成和异步 provider 表单。
- UI 层的 60% 透明度只是可视化效果;提交给 OpenAI 时要重新生成 alpha 语义正确的 PNG。
- 预览要展示“原图 + 蒙版”这对媒体,避免用户看见参考图却不知道是否带 mask。
- 本地缓存 URL、素材库 URL 等虚拟路径在提交 FormData 前必须解析成真实图片数据或可上传值。
- 新增图片参数时至少覆盖选择解析、AI 输入框工作流、任务预填、同步 adapter、异步 adapter、重新生成。
## 验证
- `pnpm --dir packages/drawnix exec tsc --noEmit --pretty false`
- `pnpm --dir packages/drawnix exec vitest run src/services/__tests__/async-image-api-service.test.ts src/services/__tests__/default-image-adapter.test.ts src/services/__tests__/media-api-routing.test.ts src/services/__tests__/media-executor.test.ts`
- 建议补充抽查:`ai-mask-brush.test.ts``selection-utils.test.ts``image-task-prefill.test.ts``gpt-image-adapter.test.ts``workflow-converter.test.ts`
## 九、蒙版预览缓存不进入素材库
这次还补了一条更底层的经验:**预览可以缓存,不能默认入库**。蒙版画笔导出的 PNG 只是编辑过程中的中间态,不应被素材库当成用户素材展示。
### 问题表现
- 蒙版预览图会出现在素材库网格里,和真正的图片素材混在一起。
- 这些预览图并不是用户主动保存的资产,却会参与素材库排序、去重和浏览。
- 从产品语义上看,这会把“编辑辅助资源”误当成“可管理素材”。
### 根因
- `exportImageMaskFromBrushes()` 会把预览 PNG 写入统一缓存。
- `AssetContext` 加载素材时会扫统一缓存,并把可见缓存合并进素材库。
- 之前只排除了 `video-frame` 这类内部缓存,没覆盖蒙版预览这一类新中间态。
### 修复思路
- 不改蒙版生成逻辑,保留预览缓存能力。
- 在素材库侧新增内部缓存排除规则。
-`ai-mask-brush``ai-mask-reference-resize` 归为内部缓存,不参与素材库展示。
### 架构变更
- 统一缓存继续承担“预览/中间态/可复用资源”的底层存储。
- 素材库不再把统一缓存全量当成业务素材全集,而是先做“可见性过滤”。
- 资产边界被拆成两层:
- 用户素材上传、AI 成果、可管理资源
- 内部缓存:蒙版预览、参考图缩放、视频帧等编辑辅助资源
### 经验规则
- 任何编辑辅助图都应先问一句:它是“展示给用户看的结果”,还是“给系统内部流程用的中间态”。
- 如果是中间态,允许缓存,但不要直接进入素材库。
- 以后新增内部缓存 source 时,优先补到排除名单,而不是改素材库主流程。
### 关联代码
- [asset-utils.ts](</Users/ljq/code/tuziapi/tuzi-api/aitu/packages/drawnix/src/utils/asset-utils.ts>)
- [AssetContext.tsx](</Users/ljq/code/tuziapi/tuzi-api/aitu/packages/drawnix/src/contexts/AssetContext.tsx>)