Fix Chinese font rendering in Streamlit charts
This commit is contained in:
54
app.py
54
app.py
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user