Files
CADx-4/tests/test_ceus_contrast_side.py
iomgaa 1de88a5d0a fix(ceus): 降低长视频内存峰值,快照开跑即写,build 核对 CEUS 运行与交付包
- 内存:prepare_segmentation 改为流式解码,只留分屏和配准的抽样帧,绿框逐帧在两半上找;
  分割那一半由 SegmentationInput.target_frames() 到用时再解码,segment_video 边读边写 jpg;
  MedSAM2 init_state 的读帧换成直接在存放设备上建张量(不改 MedSAM2 源码,结果逐位相同);
  传播结果写进预分配数组。帧数最多的一段视频常驻内存峰值 11.2 GiB → 2.3 GiB,分辨率最大的
  一段原代码被 OOM 杀掉、现为 4.2 GiB;两段的掩码和全部产物与改前逐字节相同,33 段视频的
  第 1–2 步结果与改前一致,29 例配准与 review.csv 一致。
- 快照:ceus_quant 开跑时写 snapshot/(git、ceus.yaml、review.csv、uv.lock、segment.json),
  配置和核对表从快照副本读;收尾由新函数 runs.finish_snapshot 只新建 finished.json。
  build 与 ceus_align_review 同样改用 finish_snapshot 记结束时间。
- build:summary.json 加 video_sha256;装 CEUS 结果时核对快照里的 delivery_dir、逐例核对
  视频 SHA-256 和推注时间,不同就整次停下;新字段做类型检查,不进 tic.json。

来源:主会话任务(问题 1–3 及补充的 delivery_dir 比对)。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:01:44 -04:00

109 lines
4.1 KiB
Python
Raw Permalink 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.
"""造影画面在哪一半:两半各自选提示帧,恰好一半有才定(任务书 02 第一阶段第 2 条)。
合成 400×200 的分屏视频,分屏线在 x=200;绿框是 120×100 的实心框(外接框面积 12000,
大于 config/ceus.yaml 的 min_area 10000),画在顶部文字带之下。预期值按画框位置手算。
"""
import cv2
import numpy as np
import pytest
from cadx.ceus import (
CeusCaseError,
CeusConfig,
ContrastSideError,
ViewRect,
locate_contrast_side,
map_source_mask_to_target,
split_views,
)
from cadx.ceus._geometry import Frame
_WIDTH, _HEIGHT, _SPLIT = 400, 200, 200
_GREEN = (0, 255, 0)
def _frames(green_boxes_x1: list[int]) -> list[Frame]:
"""10 帧分屏画面;每个 x1 画一个整帧坐标 [x1, x1+119] × [60, 159] 的绿框。"""
frame = np.zeros((_HEIGHT, _WIDTH, 3), dtype=np.uint8)
frame[:, :_SPLIT] = 120
frame[:, _SPLIT + 1 :] = 90
for x1 in green_boxes_x1:
cv2.rectangle(frame, (x1, 60), (x1 + 119, 159), _GREEN, thickness=-1)
return [frame.copy() for _ in range(10)]
def test_green_box_in_left_half(ceus_config: CeusConfig) -> None:
frames = _frames([40])
_, left, right = split_views(frames, ceus_config.views)
assert (left, right) == (ViewRect(0, 0, 200, 200), ViewRect(200, 0, 400, 200))
side = locate_contrast_side(
frames, left, right, ceus_config.prompt_source, ceus_config.keyframes
)
assert side.side == "left"
assert side.contrast_view == left
assert side.target_view == right
# 10 帧绿框不变:只有第一帧是提示帧。
assert [p.frame_index for p in side.prompt_frames] == [0]
x1, y1, _, _ = side.prompt_frames[0].bbox_xyxy
assert abs(x1 - 40) <= 3 and abs(y1 - 60) <= 3
def test_green_box_in_right_half(ceus_config: CeusConfig) -> None:
frames = _frames([250])
_, left, right = split_views(frames, ceus_config.views)
side = locate_contrast_side(
frames, left, right, ceus_config.prompt_source, ceus_config.keyframes
)
assert side.side == "right"
assert side.contrast_view == right
assert side.target_view == left
# 整帧 x=250 在右半局部是 250 − 200 = 50。
prompt = side.prompt_frames[0]
assert abs(prompt.bbox_xyxy[0] - 50) <= 3
# 挪到左半(灰阶那一半),两半扇形对齐、配准平移为 (0, 0):局部 x 仍是 50。
mapped = map_source_mask_to_target(prompt.mask, (0, 0), side.target_view)
xs = np.where(mapped)[1]
assert abs(int(xs.min()) - 50) <= 3
assert abs(int(xs.max()) - 169) <= 3
def test_frames_are_iterated_once(ceus_config: CeusConfig) -> None:
"""帧只遍历一遍:传逐帧交出的生成器,结果与传列表相同。"""
frames = _frames([250])
_, left, right = split_views(frames, ceus_config.views)
from_list = locate_contrast_side(
frames, left, right, ceus_config.prompt_source, ceus_config.keyframes
)
from_generator = locate_contrast_side(
(frame for frame in frames), left, right, ceus_config.prompt_source, ceus_config.keyframes
)
assert from_generator.side == from_list.side == "right"
assert [p.frame_index for p in from_generator.prompt_frames] == [0]
assert np.array_equal(from_generator.prompt_frames[0].mask, from_list.prompt_frames[0].mask)
def test_green_box_in_both_halves_fails_case(ceus_config: CeusConfig) -> None:
frames = _frames([40, 250])
_, left, right = split_views(frames, ceus_config.views)
with pytest.raises(ContrastSideError, match="两半都找到绿框") as raised:
locate_contrast_side(frames, left, right, ceus_config.prompt_source, ceus_config.keyframes)
assert isinstance(raised.value, CeusCaseError)
assert raised.value.step == 2
def test_no_green_box_fails_case(ceus_config: CeusConfig) -> None:
frames = _frames([])
_, left, right = split_views(frames, ceus_config.views)
with pytest.raises(ContrastSideError, match="两半都没有找到绿框") as raised:
locate_contrast_side(frames, left, right, ceus_config.prompt_source, ceus_config.keyframes)
assert raised.value.step == 2