Fix CT orientation handling in viewer and affine
This commit is contained in:
34
app.py
34
app.py
@@ -195,10 +195,16 @@ def normalize_slice(image: np.ndarray, p_low: float = 1.0, p_high: float = 99.0)
|
||||
return np.clip((image - low) / (high - low), 0.0, 1.0)
|
||||
|
||||
|
||||
def orient_for_display(image: np.ndarray) -> np.ndarray:
|
||||
"""统一显示方向,让三视图在网页中更接近医学浏览器观感。"""
|
||||
def orient_for_display(image: np.ndarray, plane: str) -> np.ndarray:
|
||||
"""按平面统一显示方向。
|
||||
|
||||
return np.rot90(image)
|
||||
数组轴约定为 (X, Y, Z),其中 Z 轴在当前患者1 DICOM 中从头侧到足侧
|
||||
递增。冠状面和矢状面应让 Z 轴作为屏幕竖直方向,避免头脚倒置。
|
||||
"""
|
||||
|
||||
if image.ndim == 3 and image.shape[-1] in (3, 4):
|
||||
return np.swapaxes(image, 0, 1)
|
||||
return image.T
|
||||
|
||||
|
||||
def get_slice(volume_xyz: np.ndarray, plane: str, index: int) -> np.ndarray:
|
||||
@@ -267,18 +273,18 @@ def format_mm(value: float) -> str:
|
||||
return f"{value:.2f}"
|
||||
|
||||
|
||||
def render_image(image: np.ndarray, caption: str, cmap: str = "gray") -> None:
|
||||
def render_image(image: np.ndarray, caption: str, cmap: str = "gray", plane: str = "Axial") -> None:
|
||||
fig, ax = plt.subplots(figsize=(4.8, 4.8), dpi=120)
|
||||
ax.imshow(orient_for_display(image), cmap=cmap, interpolation="nearest")
|
||||
ax.imshow(orient_for_display(image, plane), cmap=cmap, interpolation="nearest")
|
||||
ax.set_title(caption, fontsize=10)
|
||||
ax.axis("off")
|
||||
st.pyplot(fig, width="stretch")
|
||||
plt.close(fig)
|
||||
|
||||
|
||||
def render_rgb(image: np.ndarray, caption: str) -> None:
|
||||
def render_rgb(image: np.ndarray, caption: str, plane: str = "Axial") -> None:
|
||||
fig, ax = plt.subplots(figsize=(4.8, 4.8), dpi=120)
|
||||
ax.imshow(orient_for_display(image), interpolation="nearest")
|
||||
ax.imshow(orient_for_display(image, plane), interpolation="nearest")
|
||||
ax.set_title(caption, fontsize=10)
|
||||
ax.axis("off")
|
||||
st.pyplot(fig, width="stretch")
|
||||
@@ -290,14 +296,15 @@ def render_heatmap_overlay(
|
||||
mag_slice: np.ndarray,
|
||||
alpha: float,
|
||||
caption: str,
|
||||
plane: str,
|
||||
vmin: float,
|
||||
vmax: float,
|
||||
show_base: bool,
|
||||
) -> None:
|
||||
fig, ax = plt.subplots(figsize=(4.8, 4.8), dpi=120)
|
||||
if show_base:
|
||||
ax.imshow(orient_for_display(normalize_slice(base_slice)), cmap="gray", interpolation="nearest")
|
||||
heat = orient_for_display(mag_slice)
|
||||
ax.imshow(orient_for_display(normalize_slice(base_slice), plane), cmap="gray", interpolation="nearest")
|
||||
heat = orient_for_display(mag_slice, plane)
|
||||
im = ax.imshow(
|
||||
heat,
|
||||
cmap="turbo",
|
||||
@@ -321,7 +328,7 @@ def render_three_views(volume_xyz: np.ndarray, title: str) -> None:
|
||||
axis = plane_axis(plane)
|
||||
with col:
|
||||
index = st.slider(f"{cn_name}", 0, volume_xyz.shape[axis] - 1, volume_xyz.shape[axis] // 2, key=f"{title}-{plane}")
|
||||
render_image(get_slice(volume_xyz, plane, index), f"{cn_name} #{index}")
|
||||
render_image(get_slice(volume_xyz, plane, index), f"{cn_name} #{index}", plane=plane)
|
||||
|
||||
|
||||
def render_three_way_views(fixed_xyz: np.ndarray, moving_xyz: np.ndarray, warped_xyz: np.ndarray | None) -> None:
|
||||
@@ -351,7 +358,7 @@ def render_three_way_views(fixed_xyz: np.ndarray, moving_xyz: np.ndarray, warped
|
||||
if volume is None:
|
||||
st.info("待生成")
|
||||
else:
|
||||
render_image(get_slice(volume, plane, index), f"{label} #{index}")
|
||||
render_image(get_slice(volume, plane, index), f"{label} #{index}", plane=plane)
|
||||
|
||||
|
||||
def render_overlay_views(fixed_xyz: np.ndarray, warped_xyz: np.ndarray) -> None:
|
||||
@@ -369,9 +376,9 @@ def render_overlay_views(fixed_xyz: np.ndarray, warped_xyz: np.ndarray) -> None:
|
||||
fixed_slice = get_slice(fixed_xyz, plane, index)
|
||||
warped_slice = get_slice(warped_xyz, plane, index)
|
||||
if mode == "棋盘格":
|
||||
render_image(checkerboard(fixed_slice, warped_slice, tile=tile), f"{cn_name} 棋盘格 #{index}")
|
||||
render_image(checkerboard(fixed_slice, warped_slice, tile=tile), f"{cn_name} 棋盘格 #{index}", plane=plane)
|
||||
else:
|
||||
render_rgb(alpha_overlay(fixed_slice, warped_slice, alpha=alpha), f"{cn_name} 融合 #{index}")
|
||||
render_rgb(alpha_overlay(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:
|
||||
@@ -410,6 +417,7 @@ def render_ddf_views(fixed_xyz: np.ndarray, ddf_xyz: np.ndarray) -> None:
|
||||
get_slice(fixed_xyz, plane, index),
|
||||
get_slice(mag, plane, index),
|
||||
alpha=alpha,
|
||||
plane=plane,
|
||||
vmin=vmin,
|
||||
vmax=vmax,
|
||||
show_base=show_base,
|
||||
|
||||
Reference in New Issue
Block a user