Files
CADx-4/tests/fake_ceus_video.py
T
iomgaa 00ebbffb28 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>
2026-10-16 06:47:27 -04:00

58 lines
2.6 KiB
Python
Raw 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.
"""测试用合成 CEUS 视频:400×200、10 fps、40 秒的分屏造影视频(test_ceus_pipeline、test_ceus_quant、
test_ceus_align_review 共用)。
两半各有一块位置相同、带纹理、随时间轻微起伏的扇形区(`fake_split_scene`,配准平移 (0, 0))。
造影画面在右半:病灶是整帧 `LESION` 方块,亮度按 Lognormal 团注曲线 `BOLUS` 变化,内部有一块一直
发黑的囊 `CYST`;`green_box` 为真时画医生的绿框(病灶的空心描边)。
"""
from pathlib import Path
import cv2
import numpy as np
from cadx.ceus import LognormalParameters, evaluate_lognormal
from fake_split_scene import make_scene, scene_gray, to_bgr
WIDTH, HEIGHT, SPLIT = 400, 200, 200
FPS, FRAMES = 10.0, 400
LESION = (250, 60, 369, 159) # 整帧坐标 x1, y1, x2, y2(含端点)
CYST = (290, 95, 320, 125)
BOLUS = LognormalParameters(area=1300.0, mu=2.4, sigma=0.45, arrival_offset_s=4.0, baseline=40.0)
def write_video(path: Path, *, green_box: bool) -> None:
rng = np.random.default_rng(3)
intensity = evaluate_lognormal(np.arange(FRAMES) / FPS, BOLUS)
# 两半是同一幅带纹理的扇形,平移 (0, 0):配准应估出 (0, 0),绿框原位挪到左半。
scene = make_scene(
height=HEIGHT,
half_width=SPLIT,
fan_polygon=[(30, 22), (170, 22), (197, 192), (3, 192)],
offset=(0, 0),
interior_shift=None,
texture_std=20.0,
# 造影那一半调暗(扇形内平均约 32,与改动前的 30 相当):mp4 压缩把绿框描边的颜色渗到
# 相邻像素,背景亮时渗出的像素更容易落进绿框的 HSV 范围,绿框会被认大。
right_gain=0.15,
right_bias=0.0,
seed=7,
)
x1, y1, x2, y2 = LESION
# 病灶里保留组织纹理(减去它在病灶方块内的均值),病灶平均亮度仍等于团注曲线。
detail = scene.right[y1 : y2 + 1, x1 - SPLIT : x2 - SPLIT + 1]
detail = detail - detail.mean()
writer = cv2.VideoWriter(str(path), cv2.VideoWriter.fourcc(*"mp4v"), FPS, (WIDTH, HEIGHT))
for index in range(FRAMES):
gray = scene_gray(scene, index, rng)
gray[:, SPLIT] = 0 # 分屏缝
lesion = intensity[index] + detail + rng.normal(0.0, 6.0, detail.shape)
gray[y1 : y2 + 1, x1 : x2 + 1] = lesion
cx1, cy1, cx2, cy2 = CYST
gray[cy1 : cy2 + 1, cx1 : cx2 + 1] = 10
frame = np.ascontiguousarray(to_bgr(gray))
if green_box:
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), thickness=3)
writer.write(frame)
writer.release()