调整切分区域边界扩展上限

将像素边界每边调整上限常量设为百分之百

同步更新受影响的区域归一化测试

技术方案仅引用上限常量,不重复写死数值
This commit is contained in:
2026-09-10 18:46:39 +08:00
parent 74f726e65d
commit 69a0804c03
2 changed files with 12 additions and 11 deletions
@@ -1,10 +1,11 @@
use super::model::BindingArea;
use image::RgbaImage;
/// Each edge may move by at most half of the area dimension returned by the
/// visual model. Keep this policy explicit so changing it is an intentional
/// workflow decision rather than a scattered numeric literal.
pub(crate) const MAX_BINDING_AREA_EDGE_ADJUSTMENT_PERCENT: u32 = 50;
/// Each edge may move by at most this percentage of the corresponding area
/// dimension returned by the visual model. Keep this policy explicit so
/// changing it is an intentional workflow decision rather than a scattered
/// numeric literal.
pub(crate) const MAX_BINDING_AREA_EDGE_ADJUSTMENT_PERCENT: u32 = 100;
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct NormalizedBindingArea {
@@ -339,7 +340,7 @@ mod tests {
fn adjusts_each_edge_independently() {
let image = image_with_rect(32, 32, 10, 11, 16, 18);
let result = normalize_binding_area(&image, area(10, 12, 10, 3)).unwrap();
assert_eq!(result.area, area(10, 11, 6, 5));
assert_eq!(result.area, area(10, 11, 6, 7));
}
#[test]
@@ -361,10 +362,10 @@ mod tests {
}
#[test]
fn caps_each_edge_at_half_of_the_original_dimension() {
fn caps_each_edge_at_original_dimension() {
let image = image_with_rect(64, 64, 0, 0, 64, 64);
let result = normalize_binding_area(&image, area(16, 16, 8, 8)).unwrap();
assert_eq!(result.area, area(12, 12, 16, 16));
assert_eq!(result.area, area(8, 8, 24, 24));
assert!(result.clamped);
}
@@ -385,11 +386,11 @@ mod tests {
}
#[test]
fn one_pixel_area_stays_nonzero_when_adjustment_limit_is_zero() {
fn one_pixel_area_expands_with_configured_adjustment_limit() {
let image = image_with_rect(8, 8, 2, 2, 5, 5);
let result = normalize_binding_area(&image, area(3, 3, 1, 1)).unwrap();
assert_eq!(result.area, area(3, 3, 1, 1));
assert!(result.clamped);
assert_eq!(result.area, area(2, 2, 3, 3));
assert!(!result.clamped);
}
#[test]
@@ -51,7 +51,7 @@ Rust 在接收并校验工具结果后,才把这两个枚举变体映射为正
`spawn_blocking` 任务;image-edit 与 visual binding 网络请求仍运行在 async future 中。
- 视觉 binding 输入源图与处理图,必须为当前 batch 每个节点恰好返回一次 `Ok``NeedRework`
- `Ok` 返回 `NodeId + BindingArea`Rust 仅校验 NodeId、区域边界和非零尺寸,不检查与原节点框的偏差,也不要求区域不重叠。
- cut 前会对视觉模型返回的 `BindingArea` 做本地像素边界归一化。处理图是透明 PNG,有效像素定义为 `alpha > 0`。四条边以模型 area 为起点,每条边根据首次扫描结果固定方向:边上无有效像素则只向内收缩,边上有有效像素则只向外扩展;四边每轮从同一矩形快照同时逐像素推进,直到达到“内侧有像素、外侧无像素”的分界、图像边界或每条边相对原始 area 的 `50%` 位移上限。该上限由模块级常量定义。方向固定用于避免稀疏像素造成边界来回振荡;没有理想分界时使用受限范围内的最终 area,不重新请求视觉模型,也不转 problematic。全透明处理图不走特殊错误分支,仍沿同一规则得到最终 area 后裁切。归一化只影响本地 cut,不改写原始 `BindingDecision`、sidecar 或 DTO;日志记录原始 area、最终 area、是否变更,以及仍需移动时是否受到 `50%` 上限、图像边界或非零尺寸约束。性能优化列 TODO。
- cut 前会对视觉模型返回的 `BindingArea` 做本地像素边界归一化。处理图是透明 PNG,有效像素定义为 `alpha > 0`。四条边以模型 area 为起点,每条边根据首次扫描结果固定方向:边上无有效像素则只向内收缩,边上有有效像素则只向外扩展;四边每轮从同一矩形快照同时逐像素推进,直到达到“内侧有像素、外侧无像素”的分界、图像边界或每条边相对原始 area 的位移上限。该上限由模块级常量 `MAX_BINDING_AREA_EDGE_ADJUSTMENT_PERCENT` 定义。方向固定用于避免稀疏像素造成边界来回振荡;没有理想分界时使用受限范围内的最终 area,不重新请求视觉模型,也不转 problematic。全透明处理图不走特殊错误分支,仍沿同一规则得到最终 area 后裁切。归一化只影响本地 cut,不改写原始 `BindingDecision`、sidecar 或 DTO;日志记录原始 area、最终 area、是否变更,以及仍需移动时是否受到该常量上限、图像边界或非零尺寸约束。性能优化列 TODO。
- `NeedRework` 携带短问题描述(最多 512 个 Unicode 字符);通过校验后按产生顺序追加到目标 `SeparationNode.note.rework_notes`,下一次该节点进入 image-edit 时全部意见会注入提取 prompt。结构化工具调用失败时使用可复用 repair harness,把错误反馈给模型并额外请求一次;image-edit 不使用该 harness。
- 达到返工上限时仍先保留最后一条视觉模型意见,再把节点追加到 problematic;网络、IO、裁切等基础设施错误不写入节点意见。
- 达到模块级重做常量后,节点移入 problematic;不中断整条工作流,最终统一通知用户。image-edit、图像写入或裁切失败保留当前 state 并返回错误,不自动把整批标记为 problematic。