Fix Chinese font rendering in Streamlit charts

This commit is contained in:
admin
2026-06-03 09:49:45 +08:00
parent 37faa967e1
commit a1e1d71f66

54
app.py
View File

@@ -10,7 +10,9 @@ import json
from pathlib import Path from pathlib import Path
from typing import Dict, List, Sequence, Tuple from typing import Dict, List, Sequence, Tuple
import matplotlib as mpl
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib import font_manager
import numpy as np import numpy as np
import streamlit as st import streamlit as st
@@ -73,6 +75,46 @@ CUSTOM_CSS = """
""" """
def configure_matplotlib_cjk_font() -> None:
"""配置 Matplotlib 中文字体。
Streamlit 自身用浏览器字体渲染中文通常没有问题Matplotlib 图像会走
后端字体管理,如果默认 DejaVu Sans 不含中文字形,就会显示成方块。
"""
candidate_paths = [
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc",
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
]
candidate_names = [
"Noto Sans CJK SC",
"Noto Sans CJK JP",
"Droid Sans Fallback",
"WenQuanYi Micro Hei",
"Source Han Sans SC",
"SimHei",
]
discovered_names: List[str] = []
for path_str in candidate_paths:
path = Path(path_str)
if not path.exists():
continue
try:
font_manager.fontManager.addfont(str(path))
discovered_names.append(font_manager.FontProperties(fname=str(path)).get_name())
except Exception:
continue
mpl.rcParams["font.family"] = "sans-serif"
mpl.rcParams["font.sans-serif"] = [*discovered_names, *candidate_names, "DejaVu Sans"]
mpl.rcParams["axes.unicode_minus"] = False
configure_matplotlib_cjk_font()
def _require_nibabel(): def _require_nibabel():
try: try:
import nibabel as nib # type: ignore import nibabel as nib # type: ignore
@@ -199,7 +241,7 @@ def render_image(image: np.ndarray, caption: str, cmap: str = "gray") -> None:
ax.imshow(orient_for_display(image), cmap=cmap, interpolation="nearest") ax.imshow(orient_for_display(image), cmap=cmap, interpolation="nearest")
ax.set_title(caption, fontsize=10) ax.set_title(caption, fontsize=10)
ax.axis("off") ax.axis("off")
st.pyplot(fig, use_container_width=True) st.pyplot(fig, width="stretch")
plt.close(fig) plt.close(fig)
@@ -208,7 +250,7 @@ def render_rgb(image: np.ndarray, caption: str) -> None:
ax.imshow(orient_for_display(image), interpolation="nearest") ax.imshow(orient_for_display(image), interpolation="nearest")
ax.set_title(caption, fontsize=10) ax.set_title(caption, fontsize=10)
ax.axis("off") ax.axis("off")
st.pyplot(fig, use_container_width=True) st.pyplot(fig, width="stretch")
plt.close(fig) plt.close(fig)
@@ -220,7 +262,7 @@ def render_heatmap_overlay(base_slice: np.ndarray, mag_slice: np.ndarray, alpha:
ax.set_title(caption, fontsize=10) ax.set_title(caption, fontsize=10)
ax.axis("off") ax.axis("off")
fig.colorbar(im, ax=ax, fraction=0.046, pad=0.02) fig.colorbar(im, ax=ax, fraction=0.046, pad=0.02)
st.pyplot(fig, use_container_width=True) st.pyplot(fig, width="stretch")
plt.close(fig) plt.close(fig)
@@ -321,7 +363,7 @@ def render_metric_charts(fixed_xyz: np.ndarray, moving_xyz: np.ndarray, warped_x
axes[1].set_xlabel("Slice") axes[1].set_xlabel("Slice")
axes[1].grid(alpha=0.22) axes[1].grid(alpha=0.22)
axes[1].legend(frameon=False) axes[1].legend(frameon=False)
st.pyplot(fig, use_container_width=True) st.pyplot(fig, width="stretch")
plt.close(fig) plt.close(fig)
if ddf_xyz is not None: if ddf_xyz is not None:
@@ -332,7 +374,7 @@ def render_metric_charts(fixed_xyz: np.ndarray, moving_xyz: np.ndarray, warped_x
ax.set_xlabel("mm") ax.set_xlabel("mm")
ax.set_ylabel("Voxel count") ax.set_ylabel("Voxel count")
ax.grid(axis="y", alpha=0.2) ax.grid(axis="y", alpha=0.2)
st.pyplot(fig2, use_container_width=True) st.pyplot(fig2, width="stretch")
plt.close(fig2) plt.close(fig2)
@@ -370,7 +412,7 @@ def main() -> None:
out_dir = st.text_input("输出目录", value=str(INFERENCE_DIR)) out_dir = st.text_input("输出目录", value=str(INFERENCE_DIR))
display_max_voxels = st.slider("Web显示体素上限", 2_000_000, 30_000_000, 14_000_000, 1_000_000) display_max_voxels = st.slider("Web显示体素上限", 2_000_000, 30_000_000, 14_000_000, 1_000_000)
start = st.button("开始推理", type="primary", use_container_width=True) start = st.button("开始推理", type="primary", width="stretch")
if start: if start:
with st.spinner("推理运行中"): with st.spinner("推理运行中"):
try: try: