Files
T
lhk229 0c04bbbea3 内存优化 (#91)
BGfilter服务调用改为传url,减少内存占用

Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/91
Reviewed-by: 段舒康 <kdletters@qq.com>
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-07-18 21:17:35 +08:00

167 lines
5.7 KiB
Rust
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.
//! 通用抠图冒烟验证:本地图片 → AuthorizeFileUpload 临时对象 → SegmentCommonImage → 下载结果。
//!
//! 运行(在 server-rs 目录下):
//! cargo run -p platform-matting --example segment_smoke -- "C:\path\to\input.png"
//!
//! 依赖仓库根目录 .env / .env.local / .env.secrets.local 中的 AK/SK
//!`GENARRATIVE_ALIYUN_MATTING_*` 或 `ALIBABA_CLOUD_ACCESS_KEY_*`);
//! 可选 `GENARRATIVE_ALIYUN_MATTING_ENDPOINT`。临时 OSS bucket/endpoint 由
//! AuthorizeFileUpload 动态下发,不依赖自有 OSS 配置。
use std::path::{Path, PathBuf};
use platform_matting::{
DEFAULT_IMAGESEG_ENDPOINT, MattingClient, MattingConfig, SegmentCommonImageRequest,
SegmentReturnForm,
};
fn load_env_files() {
// example 的工作目录通常是 server-rs,.env 都在仓库根目录。
for candidate in [
".env",
".env.local",
".env.secrets.local",
"../.env",
"../.env.local",
"../.env.secrets.local",
] {
if Path::new(candidate).exists() {
let _ = dotenvy::from_path_override(candidate);
}
}
}
#[tokio::main]
async fn main() {
load_env_files();
let input_path = std::env::args().nth(1).unwrap_or_else(|| {
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}"));
println!(
"[1/5] 已读取测试图片:{input_path}{} 字节)",
input_bytes.len()
);
// SegmentCommonImage 要求分辨率低于 2000x2000,超限先等比缩小。
const MAX_EDGE: u32 = 1999;
let decoded = image::load_from_memory(&input_bytes).expect("测试图片应可解码");
let input_bytes = if decoded.width() > MAX_EDGE || decoded.height() > MAX_EDGE {
let resized = decoded.resize(MAX_EDGE, MAX_EDGE, image::imageops::FilterType::CatmullRom);
let mut buffer = std::io::Cursor::new(Vec::new());
resized
.write_to(&mut buffer, image::ImageFormat::Png)
.expect("缩放后的图片应可编码为 PNG");
println!(
" 分辨率 {}x{} 超限,已缩放到 {}x{}",
decoded.width(),
decoded.height(),
resized.width(),
resized.height()
);
buffer.into_inner()
} else {
input_bytes
};
let http_client = reqwest::Client::new();
// --- 调用通用抠图 ---
// key 优先级与 api-server 配置保持一致:抠图专用 → 官方 SDK 标准命名。
let (matting_key_id, matting_key_secret) = [
(
"GENARRATIVE_ALIYUN_MATTING_ACCESS_KEY_ID",
"GENARRATIVE_ALIYUN_MATTING_ACCESS_KEY_SECRET",
),
(
"ALIBABA_CLOUD_ACCESS_KEY_ID",
"ALIBABA_CLOUD_ACCESS_KEY_SECRET",
),
]
.iter()
.find_map(|(id_name, secret_name)| {
let id = std::env::var(id_name).ok()?;
let secret = std::env::var(secret_name).ok()?;
if id.trim().is_empty() || secret.trim().is_empty() {
return None;
}
println!(" 使用 {id_name} 调用抠图服务");
Some((id, secret))
})
.expect("未找到可用的抠图 AccessKey 环境变量");
let matting_config = MattingConfig::new(
std::env::var("GENARRATIVE_ALIYUN_MATTING_ENDPOINT")
.unwrap_or_else(|_| DEFAULT_IMAGESEG_ENDPOINT.to_string()),
matting_key_id,
matting_key_secret,
)
.expect("抠图配置应有效");
let matting_client = MattingClient::new(matting_config).expect("抠图客户端应可构建");
// 非上海地域输入按新版官方 SDK 的 AdvanceRequest 口径申请单对象 Policy 后上传。
let temp_url = matting_client
.upload_temp_image(input_bytes, "segment-input.png", "image/png")
.await
.expect("上传 AuthorizeFileUpload 临时对象应成功");
println!("[2/5] 已上传 AuthorizeFileUpload 临时对象");
println!("[3/5] 输入图 URL host{}", host_of(&temp_url));
let result = matting_client
.segment_common_image(SegmentCommonImageRequest {
image_url: temp_url,
return_form: Some(SegmentReturnForm::Crop),
})
.await;
let result = match result {
Ok(result) => result,
Err(error) => {
eprintln!("[4/5] 通用抠图调用失败:{error}");
std::process::exit(1);
}
};
println!(
"[4/5] 通用抠图成功,RequestId={}",
result.request_id.as_deref().unwrap_or("unknown")
);
// --- 下载结果 ---
let output_bytes = http_client
.get(&result.image_url)
.send()
.await
.expect("下载抠图结果应成功")
.error_for_status()
.expect("抠图结果 URL 应返回 200")
.bytes()
.await
.expect("读取抠图结果字节应成功");
let output_path = build_output_path(&input_path);
std::fs::write(&output_path, &output_bytes).expect("写出抠图结果应成功");
println!(
"[5/5] 抠图结果已保存:{}{} 字节)",
output_path.display(),
output_bytes.len()
);
}
fn build_output_path(input_path: &str) -> PathBuf {
let input = Path::new(input_path);
let stem = input
.file_stem()
.and_then(|value| value.to_str())
.unwrap_or("segment-output");
input.with_file_name(format!("{stem}-matting.png"))
}
fn host_of(url: &str) -> String {
url.split("//")
.nth(1)
.and_then(|rest| rest.split('/').next())
.unwrap_or("unknown")
.to_string()
}