删除抠图上海自有 OSS 死分支并清理审查发现的死代码
代码审查发现 4 项问题,本次一并处理: - 抠图输入"上海地域自有 OSS 上传"分支整体删除:我们没有上海地域 OSS,region marker 判定永远为假,该分支任何环境都不会触发,且其 matting-input/ 中间对象无清理会造成存储泄漏(若将来启用)。抠图 输入统一走 VIAPI 官方临时桶(1 天自动过期,天然无泄漏)。 - platform-matting:segment_image_to_transparent_png 去掉恒为 None 的 accessible_url 参数,简化提交分支;修正"生产建议用上海自有 OSS"的误导注释。 - editor_green_screen:删除不可达的 #00FF00 绿幕特判分支(legacy 绿幕色产生点已删、调色板无此色)。 - segment_smoke example:默认参数里的个人路径改为用法提示 + 退出。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,15 @@
|
||||
//! 阿里云通用抠图在 api-server 侧的适配层。
|
||||
//!
|
||||
//! 输入 URL 策略:自有 OSS 在上海地域时直接上传草稿区并签名读 URL(抠图服务只认
|
||||
//! 上海地域 OSS);其他地域交给 platform-matting 走 VIAPI 官方临时桶。
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
//! 输入 URL 策略:抠图服务只认上海地域 OSS URL,而我们没有上海地域自有 OSS,
|
||||
//! 统一由 platform-matting 上传 VIAPI 官方临时桶(1 天自动过期,无需清理)。
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use platform_oss::{
|
||||
LegacyAssetPrefix, OssObjectAccess, OssPutObjectRequest, OssSignedGetObjectUrlRequest,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
http_error::AppError, openai_image_generation::DownloadedOpenAiImage, state::AppState,
|
||||
};
|
||||
|
||||
const MATTING_INPUT_PATH_SEGMENT: &str = "matting-input";
|
||||
const MATTING_INPUT_READ_EXPIRE_SECONDS: u64 = 10 * 60;
|
||||
const ALIYUN_MATTING_OSS_REGION_MARKER: &str = "oss-cn-shanghai";
|
||||
|
||||
/// 图片字节 → 阿里云通用抠图 → 原尺寸透明 PNG。
|
||||
/// 未配置抠图客户端时返回错误,由调用方决定是否降级本地算法。
|
||||
pub(crate) async fn segment_image_with_aliyun_matting(
|
||||
@@ -33,11 +24,10 @@ pub(crate) async fn segment_image_with_aliyun_matting(
|
||||
}))
|
||||
})?;
|
||||
|
||||
let accessible_url = prepare_shanghai_oss_input_url(state, image, log_label).await;
|
||||
let file_name = format!("{log_label}.png");
|
||||
let started_at = std::time::Instant::now();
|
||||
let output_bytes = matting_client
|
||||
.segment_image_to_transparent_png(image.bytes.as_slice(), &file_name, accessible_url)
|
||||
.segment_image_to_transparent_png(image.bytes.as_slice(), &file_name)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
@@ -58,67 +48,3 @@ pub(crate) async fn segment_image_with_aliyun_matting(
|
||||
extension: "png".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 自有 OSS 在上海地域时,把待抠图图片传到草稿区并返回签名读 URL;
|
||||
/// 其他情况返回 None(platform-matting 会转走官方临时桶)。
|
||||
/// 该步骤失败不阻断抠图,只是退化为临时桶通道。
|
||||
async fn prepare_shanghai_oss_input_url(
|
||||
state: &AppState,
|
||||
image: &DownloadedOpenAiImage,
|
||||
log_label: &str,
|
||||
) -> Option<String> {
|
||||
let oss_client = state.oss_client()?;
|
||||
let endpoint = state.config.oss_endpoint.as_deref().unwrap_or("");
|
||||
if !endpoint.contains(ALIYUN_MATTING_OSS_REGION_MARKER) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let http_client = reqwest::Client::new();
|
||||
let file_name = format!(
|
||||
"{log_label}-{}.{}",
|
||||
shared_kernel::new_uuid_simple_string(),
|
||||
image.extension
|
||||
);
|
||||
let put_result = oss_client
|
||||
.put_object(
|
||||
&http_client,
|
||||
OssPutObjectRequest {
|
||||
prefix: LegacyAssetPrefix::CharacterDrafts,
|
||||
path_segments: vec![MATTING_INPUT_PATH_SEGMENT.to_string()],
|
||||
file_name,
|
||||
content_type: Some(image.mime_type.clone()),
|
||||
access: OssObjectAccess::Private,
|
||||
metadata: BTreeMap::new(),
|
||||
body: image.bytes.clone(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
let put_result = match put_result {
|
||||
Ok(result) => result,
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
provider = "aliyun-matting",
|
||||
log_label,
|
||||
error = %error,
|
||||
"抠图输入图上传自有 OSS 失败,回退官方临时桶"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
match oss_client.sign_get_object_url(OssSignedGetObjectUrlRequest {
|
||||
object_key: put_result.object_key,
|
||||
expire_seconds: Some(MATTING_INPUT_READ_EXPIRE_SECONDS),
|
||||
}) {
|
||||
Ok(signed) => Some(signed.signed_url),
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
provider = "aliyun-matting",
|
||||
log_label,
|
||||
error = %error,
|
||||
"抠图输入图签名读 URL 失败,回退官方临时桶"
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,9 +141,6 @@ pub(crate) const EDITOR_GREEN_SCREEN_CHARACTER_GUARDRAILS: &str =
|
||||
"角色主体及其服饰、道具的颜色必须与背景色明显区分,不得带与背景色相同或相近的描边、投影或反光";
|
||||
|
||||
fn editor_screen_background_color_prompt(color: EditorScreenBackgroundColor) -> String {
|
||||
if color.hex == "#00FF00" {
|
||||
return "单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕".to_string();
|
||||
}
|
||||
format!(
|
||||
"单一纯色背景 {} {} / RGB({},{},{})",
|
||||
color.label, color.hex, color.red, color.green, color.blue
|
||||
|
||||
@@ -33,7 +33,8 @@ async fn main() {
|
||||
load_env_files();
|
||||
|
||||
let input_path = std::env::args().nth(1).unwrap_or_else(|| {
|
||||
r"C:\Users\lingh\Downloads\gpt-image-2-elf-archer-2048x2048.png".to_string()
|
||||
eprintln!("用法:cargo run -p platform-matting --example segment_smoke -- <图片路径>");
|
||||
std::process::exit(1);
|
||||
});
|
||||
let input_bytes = std::fs::read(&input_path)
|
||||
.unwrap_or_else(|error| panic!("读取测试图片失败({input_path}):{error}"));
|
||||
|
||||
@@ -18,8 +18,8 @@ pub const DEFAULT_IMAGESEG_ENDPOINT: &str = "imageseg.cn-shanghai.aliyuncs.com";
|
||||
const IMAGESEG_API_VERSION: &str = "2019-12-30";
|
||||
const SEGMENT_COMMON_IMAGE_ACTION: &str = "SegmentCommonImage";
|
||||
|
||||
// VIAPI 官方临时上传通道:非上海地域 OSS / 本地文件先传到官方临时桶(1 天过期,
|
||||
// 全用户共享 QPS,生产建议直接用上海地域自有 OSS)。
|
||||
// VIAPI 官方临时上传通道:抠图输入统一先传官方临时桶(1 天自动过期,无需清理;
|
||||
// 全用户共享 QPS)。抠图服务只认上海地域 OSS URL,而我们没有上海地域自有 OSS。
|
||||
// 文档:https://help.aliyun.com/document_detail/155645.html
|
||||
const VIAPI_UTILS_ENDPOINT: &str = "viapiutils.cn-shanghai.aliyuncs.com";
|
||||
const VIAPI_UTILS_VERSION: &str = "2020-04-01";
|
||||
@@ -282,15 +282,13 @@ impl MattingClient {
|
||||
|
||||
/// 图片字节 → 通用抠图 → 原尺寸透明 PNG 字节。
|
||||
///
|
||||
/// - `accessible_url`:该字节对应的、抠图服务可访问的 URL(上海地域 OSS 签名 URL)。
|
||||
/// 为 None 或图片超分辨率上限需要缩图时,自动改走官方临时桶上传。
|
||||
/// - 输入统一上传 VIAPI 官方临时桶(1 天自动过期,无需清理)后作为 ImageURL 送抠。
|
||||
/// - 分辨率守卫:任一边 >= 2000 时先等比缩小送抠,抠完只取结果 alpha 上采样回贴
|
||||
/// 原图 RGB,保证输出与输入同尺寸、画质无损。
|
||||
pub async fn segment_image_to_transparent_png(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
file_name: &str,
|
||||
accessible_url: Option<String>,
|
||||
) -> Result<Vec<u8>, MattingError> {
|
||||
let source = image::load_from_memory(bytes).map_err(|error| {
|
||||
MattingError::InvalidRequest(format!("解析待抠图图片失败:{error}"))
|
||||
@@ -299,28 +297,19 @@ impl MattingClient {
|
||||
let (source_width, source_height) = source_rgba.dimensions();
|
||||
|
||||
let needs_resize = source_width > MAX_INPUT_EDGE || source_height > MAX_INPUT_EDGE;
|
||||
let (submit_bytes, submit_url) = if needs_resize {
|
||||
let upload_bytes = if needs_resize {
|
||||
let resized = source.resize(
|
||||
MAX_INPUT_EDGE,
|
||||
MAX_INPUT_EDGE,
|
||||
image::imageops::FilterType::CatmullRom,
|
||||
);
|
||||
let resized_bytes = encode_rgba_png(&resized.to_rgba8())?;
|
||||
(Some(resized_bytes), None)
|
||||
encode_rgba_png(&resized.to_rgba8())?
|
||||
} else {
|
||||
(None, accessible_url)
|
||||
};
|
||||
|
||||
let image_url = match submit_url {
|
||||
Some(url) => url,
|
||||
None => {
|
||||
let upload_bytes = submit_bytes
|
||||
.clone()
|
||||
.unwrap_or_else(|| bytes.to_vec());
|
||||
self.upload_temp_image(upload_bytes, file_name, "image/png")
|
||||
.await?
|
||||
}
|
||||
bytes.to_vec()
|
||||
};
|
||||
let image_url = self
|
||||
.upload_temp_image(upload_bytes, file_name, "image/png")
|
||||
.await?;
|
||||
|
||||
let result = self
|
||||
.segment_common_image(SegmentCommonImageRequest {
|
||||
@@ -385,7 +374,7 @@ impl MattingClient {
|
||||
}
|
||||
|
||||
/// 把本地图片字节上传到 VIAPI 官方临时桶,返回可直接作为 ImageURL 的公网地址。
|
||||
/// 适用于本地文件或非上海地域 OSS 的开发/调试场景;生产建议用上海地域自有 OSS。
|
||||
/// 抠图输入的统一上传通道(我们没有上海地域自有 OSS,临时桶 1 天自动过期无需清理)。
|
||||
pub async fn upload_temp_image(
|
||||
&self,
|
||||
bytes: Vec<u8>,
|
||||
|
||||
Reference in New Issue
Block a user