Clarify overlay comparison visualization
This commit is contained in:
56
app.py
56
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)
|
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:
|
||||||
|
|||||||
Reference in New Issue
Block a user