解决 putText 中文乱码问题

2024-12-11

最近写的一个 DEMO 中,需要把 [[OpenCV]] 处理后的视频流直接通过 [[imshow]] 显示,并且要在左上角显示对应的结果给客户演示。测试时发现用 [[putText]] 加入中文会导致乱码。

putText 乱码

看了官方文档发现使用 putText 中文乱码问题是因为在 OpenCV 中使用的是 Hershey 字体,这类字体是不支持中文的。

1
import cv2
2
3
def main(rtsp_url:str):
4
# 连接 RTSP 视频流
5
cap = cv2.VideoCapture(rtsp_url)
6
7
while True:
8
ret, frame = cap.read()
9
if not ret:
10
print("无法读取视频流")
11
break
12
13
# 左上角添加中文
14
cv2.putText(frame,
15
"演示 Demo",
16
(30, 50), # 位置坐标
17
cv2.FONT_HERSHEY_SIMPLEX,# 字体
18
1, # 字体大小
19
(0, 255, 0), # 颜色 (BGR)
20
2) # 线条粗细
21
22
# 显示结果
23
cv2.imshow("Demo Preview", frame)
24
25
# 按 'q' 退出
26
if cv2.waitKey(1) & 0xFF == ord('q'):
27
break
28
29
# 释放资源
30
cap.release()
31
cv2.destroyAllWindows()
32
33
if __name__ == "__main__":
34
rtsp_url = "rtsp://192.168.1.100:554/live"
35
main(rtsp_url)

Pillow 介入解决

尝试使用 [[pillow]] 库中 [[Image]] 模块解决:

1
import numpy as np
2
from PIL import Image, ImageDraw, ImageFont
3
4
def add_chinese_text(
5
img: np.ndarray,
6
text: str,
7
position: tuple,
8
font_path: str,
9
font_size: int = 32,
10
text_color: str = "black",
11
) -> np.ndarray:
12
13
# 先把 OpenCV 图像转换为 PIL 图像
14
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
15
16
# 创建 PIL 绘图对象,并设置字体和大小,然后绘制文字
17
draw = ImageDraw.Draw(img_pil)
18
font = ImageFont.truetype(font_path, font_size)
19
draw.text(position, text, font=font, fill=text_color)
20
21
# 把 PIL 图像转换回 OpenCV 格式
22
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

然后把最初代码中的 cv2.putText 替换为 add_chinese_text 即可:

1
# 添加中文文字
2
frame = add_chinese_text(frame,
3
"演示 Demo",
4
font_path="STHeiti Light.ttc",
5
position=(30, 50),
6
font_size=32,
7
text_color="blue")

需要注意的是 font_path 传递的字体在对应系统中必须存在。比如 [[macOS]] 中可以使用 STHeiti Light.ttc 字体,在 [[Ubuntu]] 可以使用 NotoSansCJK-Regular.ttc 字体(也支持传递完整的路径和相对路径)。

到此,中文应该可以正常显示了,下面是完整代码:

1
import cv2
2
import numpy as np
3
from PIL import Image, ImageDraw, ImageFont
4
5
def add_chinese_text(
6
img: np.ndarray,
7
text: str,
8
position: tuple,
9
font_path: str,
10
font_size: int = 32,
11
text_color: str = "black",
12
) -> np.ndarray:
13
14
# 先把 OpenCV 图像转换为 PIL 图像
15
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
16
17
# 创建 PIL 绘图对象,并设置字体和大小,然后绘制文字
18
draw = ImageDraw.Draw(img_pil)
19
font = ImageFont.truetype(font_path, font_size)
20
draw.text(position, text, font=font, fill=text_color)
21
22
# 把 PIL 图像转换回 OpenCV 格式
23
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
24
25
def main(rtsp_url:str):
26
# 连接 RTSP 视频流
27
cap = cv2.VideoCapture(rtsp_url)
28
29
while True:
30
ret, frame = cap.read()
31
if not ret:
32
print("无法读取视频流")
33
break
34
35
# 左上角添加中文
36
frame = add_chinese_text(frame,
37
"演示 Demo",
38
font_path="STHeiti Light.ttc",
39
position=(30, 50),
40
font_size=32,
41
text_color="blue")
42
# 显示结果
43
cv2.imshow("Demo Preview", frame)
44
45
# 按 'q' 退出
46
if cv2.waitKey(1) & 0xFF == ord('q'):
47
break
48
49
# 释放资源
50
cap.release()
51
cv2.destroyAllWindows()
52
53
if __name__ == "__main__":
54
rtsp_url = "rtsp://192.168.1.100:554/live"
55
main(rtsp_url)

最后

如果文字是固定不变的,可以先用 Image.new 创建一个空白图片,然后通过 [[ImageDraw]] 把文字绘制好,这样只需要把图片叠加到 frame 上即可,不需要每一帧都重复操作。

参考