feat(ceus): 两半坐标换算改为每段视频配准,加目视核对脚本

- 第 1 步分屏后在两半扇形区内用边缘图归一化互相关估 (dx, dy),再用逐帧多块模板匹配的中位数独立核对;
  两者各分量之差超过门槛(15 像素)这一例第 1 步失败。删掉「两个视图左上角相减」的实现与测试。
- 门槛依据:34 例中走完第 1 步的 29 例,差值中位 2、95 分位 6.2、最大 13;参数与门槛在同一批视频上调出。
- scripts/ceus_align_review.py:只做第 1 步,出扇形区棋盘图与边缘叠加图,混入错开 15–30 像素的
  阴性对照、随机编号;计分时任一对照未被判「没对齐」即作废。
- ceus_quant 读 review.csv:不通过的病例第 1 步失败;表里没有这段视频或平移对不上即停。
- 配准与紫圈颜色参数进分割缓存键。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 06:47:27 -04:00
parent c582fe426f
commit 00ebbffb28
20 changed files with 1500 additions and 277 deletions
+24 -24
View File
@@ -1,5 +1,5 @@
"""polygons 的单元测试(照搬旧项目 test_ceus_polygons.py;坐标换算改为两个视图矩形,
去掉旧的有效区矩形和映射锚点参数)。
"""polygons 的单元测试(照搬旧项目 test_ceus_polygons.py;坐标换算改为配准得到的平移 `offset`,
去掉旧的有效区矩形和映射锚点参数。旧用例里两个视图左上角相同,平移取 (0, 0),断言不变)。
旧标题:B6 polygons.py 的单元测试:mask→平滑多边形 + 位置约束 + 绿框先验融合。
@@ -104,7 +104,7 @@ def test_masks_to_smoothed_polygons_all_empty_raises() -> None:
def test_position_constraint_pulls_center_to_keyframe_track_without_area_change() -> None:
"""strength=1.0 把每帧质心拉到 keyframe 中心插值轨迹,且整体平移不改面积。"""
target_eff = ViewRect(0, 0, 200, 200)
source_view = ViewRect(0, 0, 200, 200)
offset = (0, 0)
# 两个 keyframe:source mask 中心分别在 (40, 100) 与 (160, 100)
keyframes = [
@@ -118,7 +118,7 @@ def test_position_constraint_pulls_center_to_keyframe_track_without_area_change(
corrected = apply_prompt_frame_position_constraints(
polygons,
keyframes,
source_view,
offset,
target_eff,
_CONFIG.constraints.keyframe_position, # enabled、center_interpolation、strength=1.0
)
@@ -138,14 +138,14 @@ def test_position_constraint_pulls_center_to_keyframe_track_without_area_change(
def test_position_constraint_disabled_returns_input() -> None:
"""enabled=False 时原样返回输入(门关)。"""
target_eff = ViewRect(0, 0, 100, 100)
source_view = ViewRect(0, 0, 100, 100)
offset = (0, 0)
polygons = [_circle_polygon((50.0, 50.0), 10.0, 64)]
keyframes = [{"frame_index": 0, "mask": _disk_mask(100, 100, (50, 50), 10)}]
out = apply_prompt_frame_position_constraints(
polygons,
keyframes,
source_view,
offset,
target_eff,
_CONFIG.constraints.keyframe_position.model_copy(update={"enabled": False}),
)
@@ -156,14 +156,14 @@ def test_position_constraint_disabled_returns_input() -> None:
def test_fusion_gate_closed_returns_pure_medsam() -> None:
"""roi_shape.source != 'prompt_prior' → 直接返回纯 medsam(门关,findings §3.7)。"""
target_eff = ViewRect(0, 0, 100, 100)
source_view = ViewRect(0, 0, 100, 100)
offset = (0, 0)
medsam = [_circle_polygon((50.0, 50.0), 10.0, 64)]
keyframes = [{"frame_index": 0, "mask": _disk_mask(100, 100, (50, 50), 10)}]
out = fuse_prompt_prior_with_medsam(
medsam,
keyframes,
source_view,
offset,
target_eff,
_CONFIG.roi_shape.model_copy(update={"source": "medsam2"}),
_CONFIG.smoothing,
@@ -175,7 +175,7 @@ def test_fusion_gate_closed_returns_pure_medsam() -> None:
def test_fusion_keyframe_strength_one_yields_prior_shape() -> None:
"""keyframe 帧 strength=1.0:融合结果几何上贴近绿框先验(逐顶点混合 s=1)。"""
target_eff = ViewRect(0, 0, 200, 200)
source_view = ViewRect(0, 0, 200, 200)
offset = (0, 0)
# 绿框先验中心 (100, 100) 半径 30;medsam 偏移到 (130, 130) 半径 30
keyframes = [{"frame_index": 0, "mask": _disk_mask(200, 200, (100, 100), 30)}]
@@ -184,7 +184,7 @@ def test_fusion_keyframe_strength_one_yields_prior_shape() -> None:
fused = fuse_prompt_prior_with_medsam(
medsam,
keyframes,
source_view,
offset,
target_eff,
_CONFIG.roi_shape.model_copy(update={"keyframe_shape_strength": 1.0}),
_CONFIG.smoothing,
@@ -200,7 +200,7 @@ def test_fusion_keyframe_strength_one_yields_prior_shape() -> None:
def test_fusion_area_clamped_to_prior_band() -> None:
"""融合面积超过 max_area_ratio_to_prompt 时按 sqrt 绕先验质心缩放回界(findings §3.7)。"""
target_eff = ViewRect(0, 0, 400, 400)
source_view = ViewRect(0, 0, 400, 400)
offset = (0, 0)
# 关键帧只在 frame 0;frame 1 为传播帧(非 keyframe),用 propagation_shape_strength=0
# 让 medsam 形状主导,从而触发面积收紧。先验半径 30,medsam 半径 80(面积远超 1.25×)。
@@ -222,7 +222,7 @@ def test_fusion_area_clamped_to_prior_band() -> None:
fused = fuse_prompt_prior_with_medsam(
medsam,
keyframes,
source_view,
offset,
target_eff,
shape_cfg,
_CONFIG.smoothing,
@@ -246,7 +246,7 @@ def test_fusion_propagation_frame_linear_blend_without_tightening() -> None:
使逐顶点线性混合的几何结果可被精确预测(绕开起点对齐 / 收紧的扰动)。
"""
target_eff = ViewRect(0, 0, 400, 400)
source_view = ViewRect(0, 0, 400, 400)
offset = (0, 0)
# 单关键帧(frame 0),frame 1 为传播帧;先验半径 50,medsam 半径 60(面积比≈1.44,
# 但 max_ratio 放宽到 2.0 → 不触发收紧),中心同在 (200, 200)
@@ -267,7 +267,7 @@ def test_fusion_propagation_frame_linear_blend_without_tightening() -> None:
fused = fuse_prompt_prior_with_medsam(
medsam,
keyframes,
source_view,
offset,
target_eff,
shape_cfg,
_CONFIG.smoothing,
@@ -297,7 +297,7 @@ def test_fusion_propagation_frame_linear_blend_without_tightening() -> None:
def test_fusion_lower_bound_tightening_expands_area() -> None:
"""下界收紧:medsam 面积 < min_ratio × prior 面积 → 被放大到 ≈ min_ratio × prior。"""
target_eff = ViewRect(0, 0, 400, 400)
source_view = ViewRect(0, 0, 400, 400)
offset = (0, 0)
# 先验半径 60;medsam 传播帧半径 20(面积比≈0.11 < min_ratio 0.70 → 触发下界放大)
keyframes = [{"frame_index": 0, "mask": _disk_mask(400, 400, (200, 200), 60)}]
@@ -316,7 +316,7 @@ def test_fusion_lower_bound_tightening_expands_area() -> None:
fused = fuse_prompt_prior_with_medsam(
medsam,
keyframes,
source_view,
offset,
target_eff,
shape_cfg,
_CONFIG.smoothing,
@@ -336,7 +336,7 @@ def test_fusion_start_point_misalignment_does_not_distort_shape() -> None:
若不先对齐起点,逐顶点混合会把不同角度的点平均出来导致半径塌缩;align 后应保形。
"""
target_eff = ViewRect(0, 0, 400, 400)
source_view = ViewRect(0, 0, 400, 400)
offset = (0, 0)
radius = 50.0
keyframes = [{"frame_index": 0, "mask": _disk_mask(400, 400, (200, 200), int(radius))}]
@@ -357,7 +357,7 @@ def test_fusion_start_point_misalignment_does_not_distort_shape() -> None:
fused = fuse_prompt_prior_with_medsam(
medsam,
keyframes,
source_view,
offset,
target_eff,
shape_cfg,
_CONFIG.smoothing,
@@ -374,7 +374,7 @@ def test_fusion_start_point_misalignment_does_not_distort_shape() -> None:
def test_fusion_propagation_strength_clipped_to_unit_interval() -> None:
"""传播权重越界(1.5 / −0.5)经函数内部 np.clip → 行为等价 1.0 / 0.0。"""
target_eff = ViewRect(0, 0, 400, 400)
source_view = ViewRect(0, 0, 400, 400)
offset = (0, 0)
keyframes = [{"frame_index": 0, "mask": _disk_mask(400, 400, (200, 200), 40)}]
medsam = [
@@ -392,7 +392,7 @@ def test_fusion_propagation_strength_clipped_to_unit_interval() -> None:
return fuse_prompt_prior_with_medsam(
medsam,
keyframes,
source_view,
offset,
target_eff,
shape_cfg,
_CONFIG.smoothing,
@@ -465,7 +465,7 @@ def test_temporal_smoothing_no_vertex_crossing_distortion() -> None:
def test_position_constraint_invalid_mode_raises() -> None:
"""mode != 'center_interpolation' → RuntimeError(当前仅支持该模式,纵深防御)。"""
target_eff = ViewRect(0, 0, 200, 200)
source_view = ViewRect(0, 0, 200, 200)
offset = (0, 0)
polygons = [_circle_polygon((100.0, 100.0), 20.0, 64)]
keyframes = [{"frame_index": 0, "mask": _disk_mask(200, 200, (100, 100), 20)}]
@@ -473,7 +473,7 @@ def test_position_constraint_invalid_mode_raises() -> None:
apply_prompt_frame_position_constraints(
polygons,
keyframes,
source_view,
offset,
target_eff,
_RawConstraintCfg(mode="affine"),
)
@@ -482,7 +482,7 @@ def test_position_constraint_invalid_mode_raises() -> None:
def test_position_constraint_strength_clipped_to_unit_interval() -> None:
"""strength 越界(1.5 / −0.5)经函数内部 np.clip → 行为等价 1.0 / 0.0。"""
target_eff = ViewRect(0, 0, 200, 200)
source_view = ViewRect(0, 0, 200, 200)
offset = (0, 0)
keyframes = [
{"frame_index": 0, "mask": _disk_mask(200, 200, (40, 100), 15)},
{"frame_index": 2, "mask": _disk_mask(200, 200, (160, 100), 15)},
@@ -493,7 +493,7 @@ def test_position_constraint_strength_clipped_to_unit_interval() -> None:
corrected = apply_prompt_frame_position_constraints(
[poly.copy() for poly in base_polys],
keyframes,
source_view,
offset,
target_eff,
_RawConstraintCfg(strength=strength),
)