拆分大文件
This commit is contained in:
@@ -104,11 +104,11 @@ pub async fn generate_character_visual(
|
||||
text_output: Some(prompt.clone()),
|
||||
structured_payload_json: Some(
|
||||
json!({
|
||||
"characterId": character_id,
|
||||
"sourceMode": payload.source_mode,
|
||||
"size": size,
|
||||
"referenceImageCount": payload.reference_image_data_urls.len(),
|
||||
})
|
||||
"characterId": character_id,
|
||||
"sourceMode": payload.source_mode,
|
||||
"size": size,
|
||||
"referenceImageCount": payload.reference_image_data_urls.len(),
|
||||
})
|
||||
.to_string(),
|
||||
),
|
||||
warning_messages: Vec::new(),
|
||||
@@ -832,12 +832,9 @@ async fn resolve_reference_image_as_data_url(
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.unwrap_or("image/png")
|
||||
.to_string();
|
||||
let body = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|error| {
|
||||
map_dashscope_request_error(format!("读取角色主形象参考图内容失败:{error}"))
|
||||
})?;
|
||||
let body = response.bytes().await.map_err(|error| {
|
||||
map_dashscope_request_error(format!("读取角色主形象参考图内容失败:{error}"))
|
||||
})?;
|
||||
if !status.is_success() {
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
@@ -911,9 +908,7 @@ async fn create_character_visual_generation(
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| {
|
||||
map_dashscope_request_error(format!("创建角色主形象任务失败:{error}"))
|
||||
})?;
|
||||
.map_err(|error| map_dashscope_request_error(format!("创建角色主形象任务失败:{error}")))?;
|
||||
let response_status = response.status();
|
||||
let response_text = response.text().await.map_err(|error| {
|
||||
map_dashscope_request_error(format!("读取角色主形象任务响应失败:{error}"))
|
||||
@@ -963,19 +958,23 @@ async fn create_character_visual_generation(
|
||||
if task_status == "SUCCEEDED" {
|
||||
let image_urls = extract_image_urls(&poll_json.payload);
|
||||
if image_urls.is_empty() {
|
||||
return Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(
|
||||
json!({
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": "角色主形象生成成功,但没有返回可下载图片。",
|
||||
}),
|
||||
));
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
let mut images = Vec::with_capacity(image_urls.len());
|
||||
for image_url in image_urls {
|
||||
images.push(
|
||||
download_generated_image(http_client, image_url.as_str(), "下载角色主形象候选图失败。")
|
||||
.await?,
|
||||
download_generated_image(
|
||||
http_client,
|
||||
image_url.as_str(),
|
||||
"下载角色主形象候选图失败。",
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -992,13 +991,18 @@ async fn create_character_visual_generation(
|
||||
));
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(CHARACTER_VISUAL_TASK_POLL_INTERVAL_MS)).await;
|
||||
sleep(Duration::from_millis(
|
||||
CHARACTER_VISUAL_TASK_POLL_INTERVAL_MS,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": "角色主形象任务执行超时,请稍后重试。",
|
||||
})))
|
||||
Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": "角色主形象任务执行超时,请稍后重试。",
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
async fn download_generated_image(
|
||||
@@ -1023,11 +1027,13 @@ async fn download_generated_image(
|
||||
.await
|
||||
.map_err(|error| map_dashscope_request_error(format!("{fallback_message}:{error}")))?;
|
||||
if !status.is_success() {
|
||||
return Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": fallback_message,
|
||||
"status": status.as_u16(),
|
||||
})));
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": fallback_message,
|
||||
"status": status.as_u16(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
let normalized_mime_type = normalize_downloaded_image_mime_type(content_type.as_str());
|
||||
@@ -1244,7 +1250,10 @@ fn map_character_visual_oss_error(error: platform_oss::OssError) -> AppError {
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_json_payload(raw_text: &str, fallback_message: &str) -> Result<ParsedJsonPayload, AppError> {
|
||||
fn parse_json_payload(
|
||||
raw_text: &str,
|
||||
fallback_message: &str,
|
||||
) -> Result<ParsedJsonPayload, AppError> {
|
||||
serde_json::from_str::<Value>(raw_text)
|
||||
.map(|payload| ParsedJsonPayload { payload })
|
||||
.map_err(|error| {
|
||||
@@ -1541,11 +1550,7 @@ fn collect_foreground_neighbor_color(
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn remove_background_from_rgba(
|
||||
pixels: &mut [u8],
|
||||
width: usize,
|
||||
height: usize,
|
||||
) -> bool {
|
||||
pub(crate) fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -> bool {
|
||||
const SOFT_EDGE_ALPHA_THRESHOLD: u8 = 224;
|
||||
const FOREGROUND_NEIGHBOR_ALPHA_THRESHOLD: u8 = 96;
|
||||
|
||||
@@ -1574,26 +1579,24 @@ pub(crate) fn remove_background_from_rgba(
|
||||
|
||||
green_scores[pixel_index] = green_score;
|
||||
white_scores[pixel_index] = white_score;
|
||||
background_hints[pixel_index] =
|
||||
green_score.max(white_score).max(transparency_hint);
|
||||
background_hints[pixel_index] = green_score.max(white_score).max(transparency_hint);
|
||||
}
|
||||
|
||||
let try_seed_background =
|
||||
|pixel_index: usize, background_mask: &mut [u8], queue: &mut Vec<usize>| {
|
||||
if background_mask[pixel_index] != 0 {
|
||||
return;
|
||||
}
|
||||
let offset = pixel_index * 4;
|
||||
let alpha = pixels[offset + 3];
|
||||
let strong_candidate = alpha < 40
|
||||
|| green_scores[pixel_index] > 0.12
|
||||
|| white_scores[pixel_index] > 0.32;
|
||||
if !strong_candidate {
|
||||
return;
|
||||
}
|
||||
background_mask[pixel_index] = 1;
|
||||
queue.push(pixel_index);
|
||||
};
|
||||
if background_mask[pixel_index] != 0 {
|
||||
return;
|
||||
}
|
||||
let offset = pixel_index * 4;
|
||||
let alpha = pixels[offset + 3];
|
||||
let strong_candidate =
|
||||
alpha < 40 || green_scores[pixel_index] > 0.12 || white_scores[pixel_index] > 0.32;
|
||||
if !strong_candidate {
|
||||
return;
|
||||
}
|
||||
background_mask[pixel_index] = 1;
|
||||
queue.push(pixel_index);
|
||||
};
|
||||
|
||||
for x in 0..width {
|
||||
try_seed_background(x, &mut background_mask, &mut queue);
|
||||
@@ -1612,9 +1615,21 @@ pub(crate) fn remove_background_from_rgba(
|
||||
let y = pixel_index / width;
|
||||
let neighbor_indexes = [
|
||||
if x > 0 { Some(pixel_index - 1) } else { None },
|
||||
if x + 1 < width { Some(pixel_index + 1) } else { None },
|
||||
if y > 0 { Some(pixel_index - width) } else { None },
|
||||
if y + 1 < height { Some(pixel_index + width) } else { None },
|
||||
if x + 1 < width {
|
||||
Some(pixel_index + 1)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if y > 0 {
|
||||
Some(pixel_index - width)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if y + 1 < height {
|
||||
Some(pixel_index + width)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
];
|
||||
|
||||
for next_pixel_index in neighbor_indexes.into_iter().flatten() {
|
||||
@@ -1628,9 +1643,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
let next_hint = background_hints[next_pixel_index];
|
||||
let reachable_soft_edge = next_hint > 0.08
|
||||
&& next_alpha < SOFT_EDGE_ALPHA_THRESHOLD
|
||||
&& (next_green_score > 0.04
|
||||
|| next_white_score > 0.08
|
||||
|| next_alpha < 180);
|
||||
&& (next_green_score > 0.04 || next_white_score > 0.08 || next_alpha < 180);
|
||||
|
||||
if next_alpha < 40
|
||||
|| next_green_score > 0.12
|
||||
@@ -1678,8 +1691,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
}
|
||||
|
||||
if adjacent_background_count >= 2
|
||||
|| (adjacent_background_count >= 1 && hint > 0.18)
|
||||
if adjacent_background_count >= 2 || (adjacent_background_count >= 1 && hint > 0.18)
|
||||
{
|
||||
expanded_mask[pixel_index] = 1;
|
||||
}
|
||||
@@ -1712,10 +1724,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
let next_x = x as i32 + offset_x;
|
||||
let next_y = y as i32 + offset_y;
|
||||
if next_x < 0
|
||||
|| next_x >= width as i32
|
||||
|| next_y < 0
|
||||
|| next_y >= height as i32
|
||||
if next_x < 0 || next_x >= width as i32 || next_y < 0 || next_y >= height as i32
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1770,10 +1779,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
let next_x = x as i32 + offset_x;
|
||||
let next_y = y as i32 + offset_y;
|
||||
if next_x < 0
|
||||
|| next_x >= width as i32
|
||||
|| next_y < 0
|
||||
|| next_y >= height as i32
|
||||
if next_x < 0 || next_x >= width as i32 || next_y < 0 || next_y >= height as i32
|
||||
{
|
||||
touches_transparent_edge = true;
|
||||
continue;
|
||||
@@ -1795,7 +1801,11 @@ pub(crate) fn remove_background_from_rgba(
|
||||
let white_score = white_scores[pixel_index];
|
||||
let contamination = green_score
|
||||
.max(white_score)
|
||||
.max(if background_mask[pixel_index] != 0 { 0.35 } else { 0.0 })
|
||||
.max(if background_mask[pixel_index] != 0 {
|
||||
0.35
|
||||
} else {
|
||||
0.0
|
||||
})
|
||||
.max(if alpha < 220 {
|
||||
((220 - alpha) as f32 / 220.0) * 0.25
|
||||
} else {
|
||||
@@ -1818,7 +1828,8 @@ pub(crate) fn remove_background_from_rgba(
|
||||
&background_mask,
|
||||
&background_hints,
|
||||
);
|
||||
let blend = clamp01(contamination.max(if touches_transparent_edge { 0.22 } else { 0.0 }));
|
||||
let blend =
|
||||
clamp01(contamination.max(if touches_transparent_edge { 0.22 } else { 0.0 }));
|
||||
|
||||
if let Some((sample_red, sample_green, sample_blue)) = sample {
|
||||
red = lerp(red, sample_red as f32, blend);
|
||||
@@ -1835,7 +1846,8 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
} else {
|
||||
if green_score > 0.04 {
|
||||
green = green.max(red.max(blue))
|
||||
green = green
|
||||
.max(red.max(blue))
|
||||
.max((green - (green - red.max(blue)) * 0.78).round());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user