解决 putText 中文乱码问题

2024-12-11

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

putText 乱码

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

import cv2
def main(rtsp_url:str):
# 连接 RTSP 视频流
cap = cv2.VideoCapture(rtsp_url)
while True:
ret, frame = cap.read()
if not ret:
print("无法读取视频流")
break
# 左上角添加中文
cv2.putText(frame,
"演示 Demo",
(30, 50), # 位置坐标
cv2.FONT_HERSHEY_SIMPLEX,# 字体
1, # 字体大小
(0, 255, 0), # 颜色 (BGR)
2) # 线条粗细
# 显示结果
cv2.imshow("Demo Preview", frame)
# 按 'q' 退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
rtsp_url = "rtsp://192.168.1.100:554/live"
main(rtsp_url)

Pillow 介入解决

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

import numpy as np
from PIL import Image, ImageDraw, ImageFont
def add_chinese_text(
img: np.ndarray,
text: str,
position: tuple,
font_path: str,
font_size: int = 32,
text_color: str = "black",
) -> np.ndarray:
# 先把 OpenCV 图像转换为 PIL 图像
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 创建 PIL 绘图对象,并设置字体和大小,然后绘制文字
draw = ImageDraw.Draw(img_pil)
font = ImageFont.truetype(font_path, font_size)
draw.text(position, text, font=font, fill=text_color)
# 把 PIL 图像转换回 OpenCV 格式
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

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

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

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

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

import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
def add_chinese_text(
img: np.ndarray,
text: str,
position: tuple,
font_path: str,
font_size: int = 32,
text_color: str = "black",
) -> np.ndarray:
# 先把 OpenCV 图像转换为 PIL 图像
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 创建 PIL 绘图对象,并设置字体和大小,然后绘制文字
draw = ImageDraw.Draw(img_pil)
font = ImageFont.truetype(font_path, font_size)
draw.text(position, text, font=font, fill=text_color)
# 把 PIL 图像转换回 OpenCV 格式
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
def main(rtsp_url:str):
# 连接 RTSP 视频流
cap = cv2.VideoCapture(rtsp_url)
while True:
ret, frame = cap.read()
if not ret:
print("无法读取视频流")
break
# 左上角添加中文
frame = add_chinese_text(frame,
"演示 Demo",
font_path="STHeiti Light.ttc",
position=(30, 50),
font_size=32,
text_color="blue")
# 显示结果
cv2.imshow("Demo Preview", frame)
# 按 'q' 退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
rtsp_url = "rtsp://192.168.1.100:554/live"
main(rtsp_url)

最后

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

参考