增强分离标记框的可视辨识度
为分离标记框增加两条绿色角到角交叉线。 新增 MARKER_LINE_WIDTH 常量统一配置标记线宽,并确保绘制结果裁剪在矩形内。 补充标记框像素级回归测试并同步更新自动分离专题文档。
This commit is contained in:
@@ -3,6 +3,8 @@ use base64::Engine as _;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const MARKER_LINE_WIDTH: u32 = 2;
|
||||
|
||||
pub async fn build_marked_image(
|
||||
source_url: String,
|
||||
nodes: Vec<SeparationNode>,
|
||||
@@ -120,12 +122,144 @@ fn draw_frame(
|
||||
return;
|
||||
};
|
||||
let green = image::Rgba([0, 255, 0, 255]);
|
||||
for xx in x0..=x1 {
|
||||
image.put_pixel(xx, y0, green);
|
||||
image.put_pixel(xx, y1, green);
|
||||
}
|
||||
for yy in y0..=y1 {
|
||||
image.put_pixel(x0, yy, green);
|
||||
image.put_pixel(x1, yy, green);
|
||||
draw_line(
|
||||
image,
|
||||
(x0, y0),
|
||||
(x1, y0),
|
||||
green,
|
||||
MARKER_LINE_WIDTH,
|
||||
(x0, y0, x1, y1),
|
||||
);
|
||||
draw_line(
|
||||
image,
|
||||
(x0, y1),
|
||||
(x1, y1),
|
||||
green,
|
||||
MARKER_LINE_WIDTH,
|
||||
(x0, y0, x1, y1),
|
||||
);
|
||||
draw_line(
|
||||
image,
|
||||
(x0, y0),
|
||||
(x0, y1),
|
||||
green,
|
||||
MARKER_LINE_WIDTH,
|
||||
(x0, y0, x1, y1),
|
||||
);
|
||||
draw_line(
|
||||
image,
|
||||
(x1, y0),
|
||||
(x1, y1),
|
||||
green,
|
||||
MARKER_LINE_WIDTH,
|
||||
(x0, y0, x1, y1),
|
||||
);
|
||||
draw_line(
|
||||
image,
|
||||
(x0, y0),
|
||||
(x1, y1),
|
||||
green,
|
||||
MARKER_LINE_WIDTH,
|
||||
(x0, y0, x1, y1),
|
||||
);
|
||||
draw_line(
|
||||
image,
|
||||
(x1, y0),
|
||||
(x0, y1),
|
||||
green,
|
||||
MARKER_LINE_WIDTH,
|
||||
(x0, y0, x1, y1),
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_line(
|
||||
image: &mut image::RgbaImage,
|
||||
start: (u32, u32),
|
||||
end: (u32, u32),
|
||||
color: image::Rgba<u8>,
|
||||
line_width: u32,
|
||||
bounds: (u32, u32, u32, u32),
|
||||
) {
|
||||
let mut x = start.0 as i64;
|
||||
let mut y = start.1 as i64;
|
||||
let target_x = end.0 as i64;
|
||||
let target_y = end.1 as i64;
|
||||
let dx = (target_x - x).abs();
|
||||
let sx = if x < target_x { 1 } else { -1 };
|
||||
let dy = -(target_y - y).abs();
|
||||
let sy = if y < target_y { 1 } else { -1 };
|
||||
let mut error = dx + dy;
|
||||
|
||||
loop {
|
||||
draw_brush(image, x, y, color, line_width, bounds);
|
||||
if x == target_x && y == target_y {
|
||||
break;
|
||||
}
|
||||
let twice_error = error * 2;
|
||||
if twice_error >= dy {
|
||||
error += dy;
|
||||
x += sx;
|
||||
}
|
||||
if twice_error <= dx {
|
||||
error += dx;
|
||||
y += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_brush(
|
||||
image: &mut image::RgbaImage,
|
||||
x: i64,
|
||||
y: i64,
|
||||
color: image::Rgba<u8>,
|
||||
line_width: u32,
|
||||
bounds: (u32, u32, u32, u32),
|
||||
) {
|
||||
let (x0, y0, x1, y1) = bounds;
|
||||
let line_width = line_width.max(1) as i64;
|
||||
let before = (line_width - 1) / 2;
|
||||
let after = line_width / 2;
|
||||
let min_x = (x - before).max(x0 as i64);
|
||||
let max_x = (x + after).min(x1 as i64);
|
||||
let min_y = (y - before).max(y0 as i64);
|
||||
let max_y = (y + after).min(y1 as i64);
|
||||
for yy in min_y..=max_y {
|
||||
for xx in min_x..=max_x {
|
||||
image.put_pixel(xx as u32, yy as u32, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn draw_frame_adds_green_cross_corner_lines() {
|
||||
let mut image = image::RgbaImage::from_pixel(8, 6, image::Rgba([1, 2, 3, 255]));
|
||||
draw_frame(&mut image, 1, 1, 5, 3, 8, 6);
|
||||
let green = image::Rgba([0, 255, 0, 255]);
|
||||
|
||||
for &(x, y) in &[(1, 1), (5, 1), (1, 3), (5, 3), (3, 2)] {
|
||||
assert_eq!(*image.get_pixel(x, y), green, "pixel ({x}, {y})");
|
||||
}
|
||||
assert_eq!(*image.get_pixel(3, 1), green);
|
||||
assert_eq!(*image.get_pixel(3, 3), green);
|
||||
assert_eq!(*image.get_pixel(2, 2), green);
|
||||
assert_eq!(*image.get_pixel(4, 2), green);
|
||||
assert_eq!(*image.get_pixel(0, 0), image::Rgba([1, 2, 3, 255]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draw_frame_keeps_cross_inside_clipped_rect() {
|
||||
let mut image = image::RgbaImage::from_pixel(4, 4, image::Rgba([1, 2, 3, 255]));
|
||||
draw_frame(&mut image, 2, 2, 4, 4, 4, 4);
|
||||
let green = image::Rgba([0, 255, 0, 255]);
|
||||
for y in 2..4 {
|
||||
for x in 2..4 {
|
||||
assert_eq!(*image.get_pixel(x, y), green);
|
||||
}
|
||||
}
|
||||
assert_eq!(*image.get_pixel(1, 1), image::Rgba([1, 2, 3, 255]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ const SHARED_SEPARATION_REQ: &str = r#"
|
||||
MUST hard edges; preserve no glow/blur beyond the exact visible shape.
|
||||
NEVER keep its parent's background with it.
|
||||
|
||||
UI elements that needs to extract has been marked with GREEN line frames (only for mark purpose, NEVER wrap a frame in your extraction).
|
||||
UI elements that needs to extract has been marked with GREEN line frames box with crossline inside. (only for mark purpose, NEVER wrap a frame in your extraction).
|
||||
On some UI elements, there is some PURPLE filled area, they were removed UI elements, reconstruct the background under where they were.
|
||||
|
||||
MUST extract exactly these marked UI elements area.
|
||||
"#;
|
||||
pub(super) fn gen_extract_prompt(separation_notes: Vec<SeparationNote>) -> String {
|
||||
let extract_system_prompt = format!(
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
## 图片编辑与视觉绑定
|
||||
|
||||
- image-edit 使用源 UI design 图片及由 Rust 生成的绿色标记/紫色重建输入。处理父节点时,紫色填充其 children 的矩形区域(包括已 problematic 的 children),再在父节点自身外围绘制绿色框;绿色框覆盖在紫色之上。叶节点只绘制绿色框,不填充自身。
|
||||
- image-edit 使用源 UI design 图片及由 Rust 生成的绿色标记/紫色重建输入。处理父节点时,紫色填充其 children 的矩形区域(包括已 problematic 的 children),再在父节点自身外围绘制绿色框和角到角的绿色交叉线;绿色标记覆盖在紫色之上。叶节点只绘制绿色框和角到角的绿色交叉线,不填充自身。
|
||||
- 请求尺寸始终使用源 UI design 尺寸;Raw GPT Image 2 API 保证返回相同尺寸,客户端不额外做尺寸拒绝检查。
|
||||
- 标记图构建、处理图解码/写入和 cut 裁切属于本地 CPU/文件操作,放入独立的
|
||||
`spawn_blocking` 任务;image-edit 与 visual binding 网络请求仍运行在 async future 中。
|
||||
|
||||
Reference in New Issue
Block a user