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:
@@ -0,0 +1,57 @@
|
||||
"""测试用合成 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()
|
||||
Reference in New Issue
Block a user