diff --git a/docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md b/docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md index 5e4a3465b..184972293 100644 --- a/docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md +++ b/docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md @@ -65,7 +65,7 @@ 角色设定:<用户输入的角色设定> ``` -- 角色图生成完成后,编辑器后端必须先把带自动决策纯色背景的源图写入 OSS,再调用共享 BgFilter 服务透明化:multipart 字段包含 `file`、`screen_color=`、`seg_model=`、`background_mode=flat` 和 `cross_check=on`,用户路径默认并只提交 `seg_model=birefnet`;`flat` 明确表示单一纯色背景抠图模式,`birefnet` 是 BgFilter 管线内部后端。首次请求失败后立即重试 `1` 次,第二次仍失败进入“阿里云通用抠图 → 本地键色”降级链。角色图 prompt 按 `screenColor` 写入颜色名称、hex 和 RGB。该流程不再调用 RPG / 资产工坊的角色主图专用 `character_visual_assets` 后处理,也不复用手动去背景的 `background_mode=complex` 路径。透明背景处理正常成功时,输出透明背景 PNG,随后写入 OSS 私有对象并确认 `asset_object`;接口回包返回透明 PNG Data URL 及 `objectKey` / `assetObjectId`。三段透明背景处理最终仍失败、但 provider 原图已经持久化时,任务以 `completed + warning` 收口,只把 provider 原图作为唯一主图放入画布,`generatedLayerId` 指向原图,不创建不存在的透明处理图;通用 `warning.code/reason` 携带完整降级原因。该收口只捕获透明背景处理本身的最终失败;phase 上报、provider 原图持久化、透明处理图持久化和 `canvasCompletion` 写回错误仍正常传播,不能被原图降级吞掉。前端创建图层和画板资源记录时必须保存最终回包对应的媒体引用。 +- 角色图生成完成后,编辑器后端必须先把带自动决策纯色背景的源图写入私有 OSS,再签发 600 秒 GET URL 调用共享 BgFilter 服务透明化;api-server 不重新下载原图,multipart 字段包含 `image_url`、`screen_color=`、`seg_model=`、`background_mode=flat` 和 `cross_check=on`,不包含 `file`,用户路径默认并只提交 `seg_model=birefnet`;`flat` 明确表示单一纯色背景抠图模式,`birefnet` 是 BgFilter 管线内部后端。首次请求失败后立即重试 `1` 次,第二次仍失败进入“阿里云通用抠图 → 本地键色”降级链。角色图 prompt 按 `screenColor` 写入颜色名称、hex 和 RGB。该流程不再调用 RPG / 资产工坊的角色主图专用 `character_visual_assets` 后处理,也不复用手动去背景的 `background_mode=complex` 路径。透明背景处理正常成功时,输出透明背景 PNG,随后写入 OSS 私有对象并确认 `asset_object`;接口回包返回透明 PNG Data URL 及 `objectKey` / `assetObjectId`。三段透明背景处理最终仍失败、但 provider 原图已经持久化时,任务以 `completed + warning` 收口,只把 provider 原图作为唯一主图放入画布,`generatedLayerId` 指向原图,不创建不存在的透明处理图;通用 `warning.code/reason` 携带完整降级原因。该收口只捕获透明背景处理本身的最终失败;phase 上报、provider 原图持久化、透明处理图持久化和 `canvasCompletion` 写回错误仍正常传播,不能被原图降级吞掉。前端创建图层和画板资源记录时必须保存最终回包对应的媒体引用。 - 对 `assetKind: "character"` 的角色图层执行 `重绘` 时,前端仍使用原图作为参考图,但请求 `kind` 必须传 `character`,让后端继续套用上述角色提示词限定、角色图后处理和角色资产持久化;透明背景正常成功与最终失败保留 provider 原图的收口规则和角色新生成一致。普通图片图层重绘仍保持 `kind: "quick-edit"`。 ## 生成规范参考图 diff --git a/server-rs/crates/api-server/src/aliyun_matting.rs b/server-rs/crates/api-server/src/aliyun_matting.rs index 9f4776ae9..c506e0cb4 100644 --- a/server-rs/crates/api-server/src/aliyun_matting.rs +++ b/server-rs/crates/api-server/src/aliyun_matting.rs @@ -112,7 +112,6 @@ mod tests { let body = &tail[..end]; assert!(body.contains("segment_image_url_to_transparent_png")); - assert!(!body.contains("segment_image_to_transparent_png(image.bytes")); } #[test] diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index 94b912a63..0289d3540 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -9669,7 +9669,9 @@ mod tests { &[ "ensure_editor_background_removal_source_is_not_data_url", "caller.report_processing_phase(state).await?", - "parse_editor_reference_image", + "resolve_editor_reference_object_key", + "ensure_editor_reference_object_key_owned", + "request_editor_background_removal_image_with_retry", ], ); } diff --git a/server-rs/crates/platform-matting/src/lib.rs b/server-rs/crates/platform-matting/src/lib.rs index 7885ee039..8e28404f3 100644 --- a/server-rs/crates/platform-matting/src/lib.rs +++ b/server-rs/crates/platform-matting/src/lib.rs @@ -381,58 +381,6 @@ impl MattingClient { }) } - /// 图片字节 → 通用抠图 → 原尺寸透明 PNG 字节。 - /// - /// - 输入统一经 AuthorizeFileUpload 单对象 Policy 上传临时 OSS 后作为 ImageURL 送抠。 - /// - 分辨率守卫:任一边 >= 2000 时先等比缩小送抠,抠完只取结果 alpha 上采样回贴 - /// 原图 RGB,保证输出与输入同尺寸、画质无损。 - pub async fn segment_image_to_transparent_png( - &self, - bytes: &[u8], - file_name: &str, - ) -> Result, MattingError> { - let source = decode_source_image(bytes)?; - let source_rgba = source.to_rgba8(); - let (source_width, source_height) = source_rgba.dimensions(); - - // 阿里云要求每条边 > 32px;过小的图直接拒绝,让调用方走本地兜底,省一次注定 4xx 的往返。 - if source_width <= MIN_INPUT_EDGE || source_height <= MIN_INPUT_EDGE { - return Err(MattingError::InvalidRequest(format!( - "待抠图图片尺寸 {source_width}x{source_height} 过小,阿里云通用抠图要求每条边大于 {MIN_INPUT_EDGE} 像素" - ))); - } - - // 归一化成阿里云 SegmentCommonImage 接受的输入:统一 8 位 RGBA PNG(消除 MIME 名不副实 - // 与不支持位深/格式的问题)、最长边 ≤1999、体积 ≤3MB。返回实际上传尺寸,用于判断结果 - // 是否需要 alpha 上采样回原尺寸。 - let (upload_bytes, upload_dims) = normalize_matting_input_png(&source)?; - let downscaled = upload_dims != (source_width, source_height); - let image_url = self - .upload_temp_image(upload_bytes, file_name, "image/png") - .await?; - let result_image = self - .segment_uploaded_input_to_rgba(image_url, (source_width, source_height), upload_dims) - .await?; - - if !downscaled { - if result_image.dimensions() != (source_width, source_height) { - return Err(MattingError::upstream_response_error( - format!( - "抠图结果尺寸 {}x{} 与输入 {}x{} 不一致", - result_image.width(), - result_image.height(), - source_width, - source_height - ), - None, - )); - } - return encode_rgba_png(&result_image); - } - - compose_source_with_result_alpha(source_rgba, result_image) - } - /// 私有 OSS 签名 URL → 下载 → AuthorizeFileUpload 临时对象 → 通用抠图 → 透明 PNG。 /// /// 源图下载缓冲和解码图只活到临时对象上传完成;若输入发生过降尺寸,结果返回后再单独下载