From a2d909c288330d67cd8e8ef3d783c0087a7b7f8d Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 3 Jun 2026 11:34:06 +0800 Subject: [PATCH] Clarify overlay comparison visualization --- app.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/app.py b/app.py index d34c902..6a59b0b 100644 --- a/app.py +++ b/app.py @@ -229,16 +229,29 @@ def checkerboard(fixed_slice: np.ndarray, warped_slice: np.ndarray, tile: int = return np.where(mask, fixed_norm, warped_norm) -def alpha_overlay(fixed_slice: np.ndarray, warped_slice: np.ndarray, alpha: float = 0.45) -> np.ndarray: +def gray_fusion(fixed_slice: np.ndarray, warped_slice: np.ndarray, alpha: float = 0.5) -> np.ndarray: fixed_norm = normalize_slice(fixed_slice) warped_norm = normalize_slice(warped_slice) + return np.clip(fixed_norm * (1.0 - alpha) + warped_norm * alpha, 0.0, 1.0) + + +def color_difference_overlay(fixed_slice: np.ndarray, warped_slice: np.ndarray, alpha: float = 0.65) -> np.ndarray: + fixed_norm = normalize_slice(fixed_slice) + warped_norm = normalize_slice(warped_slice) + base = fixed_norm * (1.0 - alpha) + warped_norm * alpha rgb = np.zeros((*fixed_norm.shape, 3), dtype=np.float32) - rgb[..., 0] = warped_norm - rgb[..., 1] = fixed_norm * (1.0 - alpha) + warped_norm * alpha - rgb[..., 2] = fixed_norm + rgb[..., 0] = np.maximum(base, warped_norm * alpha) + rgb[..., 1] = base + rgb[..., 2] = np.maximum(base, fixed_norm * alpha) return np.clip(rgb, 0.0, 1.0) +def signed_difference(fixed_slice: np.ndarray, warped_slice: np.ndarray) -> np.ndarray: + fixed_norm = normalize_slice(fixed_slice) + warped_norm = normalize_slice(warped_slice) + return warped_norm - fixed_norm + + def ddf_magnitude(ddf_xyz: np.ndarray) -> np.ndarray: if ddf_xyz.ndim != 4 or ddf_xyz.shape[-1] != 3: raise ValueError("DDF 应为 (X, Y, Z, 3)。") @@ -291,6 +304,20 @@ def render_rgb(image: np.ndarray, caption: str, plane: str = "Axial") -> None: plt.close(fig) +def render_signed_difference(image: np.ndarray, caption: str, plane: str = "Axial") -> None: + fig, ax = plt.subplots(figsize=(4.8, 4.8), dpi=120) + diff = orient_for_display(image, plane) + finite = diff[np.isfinite(diff)] + vmax = float(np.percentile(np.abs(finite), 99.0)) if finite.size else 1.0 + vmax = max(vmax, 1e-6) + im = ax.imshow(diff, cmap="coolwarm", vmin=-vmax, vmax=vmax, interpolation="nearest") + ax.set_title(caption, fontsize=10) + ax.axis("off") + fig.colorbar(im, ax=ax, fraction=0.046, pad=0.02) + st.pyplot(fig, width="stretch") + plt.close(fig) + + def render_heatmap_overlay( base_slice: np.ndarray, mag_slice: np.ndarray, @@ -363,9 +390,20 @@ def render_three_way_views(fixed_xyz: np.ndarray, moving_xyz: np.ndarray, warped def render_overlay_views(fixed_xyz: np.ndarray, warped_xyz: np.ndarray) -> None: fixed_xyz, warped_xyz = crop_to_common_shape(fixed_xyz, warped_xyz) - mode = st.radio("对比模式", ["Alpha 融合", "棋盘格"], horizontal=True) - alpha = st.slider("透明度", 0.0, 1.0, 0.45, 0.05) - tile = st.slider("棋盘格尺寸", 8, 64, 24, 4) + mode = st.radio("对比模式", ["灰度融合", "红蓝差异", "差异热图", "棋盘格"], horizontal=True) + if mode in {"灰度融合", "红蓝差异"}: + alpha = st.slider("配准后图像权重", 0.0, 1.0, 0.5, 0.05) + else: + alpha = 0.5 + if mode == "棋盘格": + tile = st.slider("棋盘格尺寸", 8, 64, 24, 4) + else: + tile = 24 + + if mode == "红蓝差异": + st.caption("红/金色偏向配准后图像,蓝色偏向固定图像;白灰区域表示两者灰度接近。") + elif mode == "差异热图": + st.caption("差异热图显示配准后图像减固定图像的灰度差,色条以当前切片的 99% 绝对差自动缩放。") columns = st.columns(3) planes = [("Axial", "轴状面"), ("Coronal", "冠状面"), ("Sagittal", "矢状面")] @@ -377,8 +415,12 @@ def render_overlay_views(fixed_xyz: np.ndarray, warped_xyz: np.ndarray) -> None: warped_slice = get_slice(warped_xyz, plane, index) if mode == "棋盘格": render_image(checkerboard(fixed_slice, warped_slice, tile=tile), f"{cn_name} 棋盘格 #{index}", plane=plane) + elif mode == "红蓝差异": + render_rgb(color_difference_overlay(fixed_slice, warped_slice, alpha=alpha), f"{cn_name} 红蓝差异 #{index}", plane=plane) + elif mode == "差异热图": + render_signed_difference(signed_difference(fixed_slice, warped_slice), f"{cn_name} 差异热图 #{index}", plane=plane) else: - render_rgb(alpha_overlay(fixed_slice, warped_slice, alpha=alpha), f"{cn_name} 融合 #{index}", plane=plane) + render_image(gray_fusion(fixed_slice, warped_slice, alpha=alpha), f"{cn_name} 灰度融合 #{index}", plane=plane) def render_ddf_views(fixed_xyz: np.ndarray, ddf_xyz: np.ndarray) -> None: