核心概念:字体文件
无论使用哪个库,字体的核心都是一个字体文件,常见的字体文件格式有:
- .ttf (TrueType Font): 最通用,Windows, macOS, Linux 都支持。
- .otf (OpenType Font): .ttf 的增强版,支持更多特性。
- .woff (Web Open Font Format): 主要用于网页。
在 Python 中,你需要告诉库去哪里找到这些字体文件。
数据可视化 (Matplotlib / Seaborn)
这是最常见的字体标注场景,用于修改图表中的标题、标签、图例等文本的字体。
查找和加载系统字体
你需要知道你的系统上有哪些字体,以及它们的完整路径。
方法 A:使用 Matplotlib 的 font_manager (推荐)
这是最可靠的方法,因为它会自动扫描系统字体路径。
import matplotlib.font_manager as fm
# 列出所有可用的字体
# for font in fm.findSystemFonts():
# print(font)
# 或者更清晰地列出字体名称
available_fonts = sorted([f.name for f in fm.fontManager.ttflist])
print("部分可用字体:")
for font in available_fonts[:10]: # 只打印前10个,避免输出过长
print(font)
# 查找特定字体,'SimHei' (黑体)
# font_path = fm.findfont('SimHei')
# print(f"黑体路径: {font_path}")
方法 B:手动指定字体路径
如果你有一个下载好的字体文件(my_font.ttf),你可以直接指定它的路径。
import matplotlib.pyplot as plt
import matplotlib as mpl
# 假设你的字体文件和脚本在同一目录下
font_path = 'my_font.ttf'
# 检查字体是否存在
if not os.path.exists(font_path):
print(f"错误:字体文件 {font_path} 不存在!")
else:
# 加载字体
my_font = mpl.font_manager.FontProperties(fname=font_path)
# 使用字体
plt.plot([1, 2, 3], [4, 5, 1])
plt.title("自定义字体标题", fontproperties=my_font)
plt.xlabel("X轴", fontproperties=my_font)
plt.ylabel("Y轴", fontproperties=my_font)
plt.show()
全局设置字体
如果你想让整个图表都使用某种字体,可以设置全局默认字体。
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# --- Windows 用户常用中文字体 ---
# 'SimHei' - 黑体
# 'Microsoft YaHei' - 微软雅黑
# 'KaiTi' - 楷体
# 'FangSong' - 仿宋
# --- macOS 用户常用中文字体 ---
# 'PingFang SC' - 苹方
# 'STHeiti' - 华文黑体
# 'STKaiti' - 华文楷体
# --- Linux 用户常用中文字体 ---
# 'WenQuanYi Zen Hei' - 文泉驿微米黑
# 'Noto Sans CJK SC' - 思源黑体
# 设置全局字体
# plt.rcParams['font.family'] = 'SimHei'
# plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定一个字体列表,Matplotlib 会按顺序尝试
# 一个更健壮的设置方式,处理字体找不到的情况
# 先尝试设置一个字体列表
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] # DejaVu Sans 是 Matplotlib 默认的,防止前两者都找不到
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 现在所有文本都会使用这个字体
plt.plot([1, 2, 3], [4, 5, 1])"全局设置字体标题")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.grid(True)
plt.show()
在 Seaborn 中使用
Seaborn 底层使用 Matplotlib,所以方法完全一样。
import seaborn as sns
import matplotlib.pyplot as plt
# 设置全局字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 使用 Seaborn 绘制图表
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax.set_title("一周消费金额分布", fontsize=16)
ax.set_xlabel("星期", fontsize=12)
ax.set_ylabel("总消费", fontsize=12)
plt.show()
图像处理 (Pillow / PIL)
使用 Pillow 库可以在图片上直接绘制文本,非常适合制作海报、水印等。
from PIL import Image, ImageDraw, ImageFont
import os
# 1. 准备一张图片
# 如果没有图片,可以创建一个空白背景
try:
img = Image.open("background.jpg")
except FileNotFoundError:
print("未找到 background.jpg,将创建一个空白图片。")
img = Image.new('RGB', (800, 400), color = (255, 255, 255))
# 2. 创建一个可以在图片上绘制的对象
draw = ImageDraw.Draw(img)
# 3. 加载字体
# 同样,需要字体文件的路径
try:
# 尝试加载一个较大的字体用于标题font = ImageFont.truetype("simhei.ttf", 60)
# 尝试加载一个较小的字体用于副标题
subtitle_font = ImageFont.truetype("simhei.ttf", 30)
except IOError:
print("错误:未找到 simhei.ttf 字体文件,将使用默认字体。")font = ImageFont.load_default()
subtitle_font = ImageFont.load_default()
# 4. 绘制文本
# textbbox 方法可以获取文本的边界框,从而精确计算位置text = "Python 字体标注"text = "Using Pillow"
# 获取文本尺寸bbox = draw.textbbox((0, 0), title_text, font=title_font)bbox = draw.textbbox((0, 0), subtitle_text, font=subtitle_font)
width = title_bbox[2] - title_bbox[0]height = title_bbox[3] - title_bbox[1]
width = subtitle_bbox[2] - subtitle_bbox[0]height = subtitle_bbox[3] - subtitle_bbox[1]
# 计算居中位置
img_width, img_height = img.size= (img_width - title_width) / 2= 50
= (img_width - subtitle_width) / 2= y_title + title_height + 20
# 绘制文本,可以指定颜色、对齐方式等
draw.text((x_title, y_title), title_text, font=title_font, fill="black", anchor="mm") # anchor="mm" 表示以坐标点为中心
draw.text((x_subtitle, y_subtitle), subtitle_text, font=subtitle_font, fill="gray", anchor="mm")
# 5. 保存或显示图片
img.save("annotated_image.png")
img.show()
OpenCV
OpenCV 主要用于计算机视觉,但它也提供了在图像上绘制文本的功能,它的 putText 函数使用起来比较直接,但需要指定字体的基线索引。
重要提示:OpenCV 不支持直接加载 .ttf 或 .otf 字体文件,它使用一种内置的、基于位图的字体,你可以通过一个变通的方法来实现:
- 使用 Pillow 在一张透明背景的图片上绘制好文本。
- 将 Pillow 图像转换为 NumPy 数组。
- 使用 OpenCV 将这个数组作为“水印”图像叠加到目标图像上。
这里我们主要介绍 OpenCV 自身的 putText 方法,因为它速度更快,适合在视频流等实时场景中使用。
import cv2
import numpy as np
import os
# 1. 创建一个空白图像
# OpenCV 使用 BGR 格式
image = np.zeros((500, 800, 3), dtype=np.uint8)
image[:] = (255, 255, 255) # 设置白色背景
# 2. 定义文本和位置
text = "OpenCV Text Annotation"
org = (50, 250) # 文本左下角的坐标
# 3. 定义字体、缩放、颜色、粗细等
# cv2.FONT_HERSHEY_SIMPLEX 是一种常见的无衬线字体
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 2
color = (0, 0, 255) # BGR 格式的红色
thickness = 3
line_type = cv2.LINE_AA # 抗锯齿线,看起来更平滑
# 4. 绘制文本
cv2.putText(image, text, org, font, font_scale, color, thickness, line_type)
# 5. 显示和保存
cv2.imshow("OpenCV Text", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("opencv_text.png", image)
常见问题与解决方案
-
中文显示为方块或乱码
- 原因:Python / Matplotlib / Pillow 找不到支持中文的字体文件。
- 解决方案:
- 确保你安装了中文字体(如 Windows 自带的
SimHei,Microsoft YaHei)。 - 使用
matplotlib.font_manager.findfont()确认系统能否找到你的中文字体。 - 在代码中显式地指定字体路径或字体名称,并确保路径正确。
- 确保你安装了中文字体(如 Windows 自带的
-
findfont: Font family not found错误- 原因:
plt.rcParams['font.sans-serif']中列出的字体在你的系统上不存在。 - 解决方案:
- 使用
matplotlib.font_manager.fontManager.ttflist查看系统所有可用字体。 - 从列表中选择一个你确定存在的字体名称。
- 或者,直接下载一个
.ttf字体文件,并在代码中通过FontProperties(fname='path/to/font.ttf')指定其完整路径。
- 使用
- 原因:
-
如何获取更多字体?
- Windows: 在控制面板的 "字体" 设置中,可以下载和安装
.ttf或.otf文件。 - macOS: 在 "字体册" 应用中管理。
- Linux: 使用包管理器安装,例如在 Ubuntu 上:
sudo apt-get install fonts-wqy-microhei(文泉驿微米黑)。 - 在线资源:
- Google Fonts: 提供大量免费开源字体,可下载
.ttf文件。 - 思源字体: Adobe 和 Google 合作开发的开源中日韩字体,质量非常高。
- Google Fonts: 提供大量免费开源字体,可下载
- Windows: 在控制面板的 "字体" 设置中,可以下载和安装
| 库/场景 | 核心方法 | 字体加载方式 | 适用场景 |
|---|---|---|---|
| Matplotlib | plt.rcParams / fontproperties |
font_manager.findfont() 或指定 .ttf 路径 |
数据图表、科学计算可视化 |
| Pillow | ImageDraw.text() |
ImageFont.truetype() |
静态图像处理、海报制作、水印 |
| OpenCV | cv2.putText() |
不支持外部字体,使用内置位图字体 | 实时视频流、图像标注、性能要求高的场景 |
对于绝大多数 Python Matplotlib 的 font_manager 和 Pillow 的 ImageFont.truetype() 是处理字体标注最常用和最强大的工具。
