Clarify overlay comparison visualization

This commit is contained in:
admin
2026-06-03 11:34:06 +08:00
parent 3e8ddc3468
commit a2d909c288

56
app.py
View File

@@ -229,16 +229,29 @@ def checkerboard(fixed_slice: np.ndarray, warped_slice: np.ndarray, tile: int =
return np.where(mask, fixed_norm, warped_norm) 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) fixed_norm = normalize_slice(fixed_slice)
warped_norm = normalize_slice(warped_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 = np.zeros((*fixed_norm.shape, 3), dtype=np.float32)
rgb[..., 0] = warped_norm rgb[..., 0] = np.maximum(base, warped_norm * alpha)
rgb[..., 1] = fixed_norm * (1.0 - alpha) + warped_norm * alpha rgb[..., 1] = base
rgb[..., 2] = fixed_norm rgb[..., 2] = np.maximum(base, fixed_norm * alpha)
return np.clip(rgb, 0.0, 1.0) 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: def ddf_magnitude(ddf_xyz: np.ndarray) -> np.ndarray:
if ddf_xyz.ndim != 4 or ddf_xyz.shape[-1] != 3: if ddf_xyz.ndim != 4 or ddf_xyz.shape[-1] != 3:
raise ValueError("DDF 应为 (X, Y, Z, 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) 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( def render_heatmap_overlay(
base_slice: np.ndarray, base_slice: np.ndarray,
mag_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: 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) fixed_xyz, warped_xyz = crop_to_common_shape(fixed_xyz, warped_xyz)
mode = st.radio("对比模式", ["Alpha 融合", "棋盘格"], horizontal=True) mode = st.radio("对比模式", ["灰度融合", "红蓝差异", "差异热图", "棋盘格"], horizontal=True)
alpha = st.slider("透明度", 0.0, 1.0, 0.45, 0.05) 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) tile = st.slider("棋盘格尺寸", 8, 64, 24, 4)
else:
tile = 24
if mode == "红蓝差异":
st.caption("红/金色偏向配准后图像,蓝色偏向固定图像;白灰区域表示两者灰度接近。")
elif mode == "差异热图":
st.caption("差异热图显示配准后图像减固定图像的灰度差,色条以当前切片的 99% 绝对差自动缩放。")
columns = st.columns(3) columns = st.columns(3)
planes = [("Axial", "轴状面"), ("Coronal", "冠状面"), ("Sagittal", "矢状面")] 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) warped_slice = get_slice(warped_xyz, plane, index)
if mode == "棋盘格": if mode == "棋盘格":
render_image(checkerboard(fixed_slice, warped_slice, tile=tile), f"{cn_name} 棋盘格 #{index}", plane=plane) 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: 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: def render_ddf_views(fixed_xyz: np.ndarray, ddf_xyz: np.ndarray) -> None: