最近写的一个 DEMO 中,需要把 [[OpenCV]] 处理后的视频流直接通过 [[imshow]] 显示,并且要在左上角显示对应的结果给客户演示。测试时发现用 [[putText]] 加入中文会导致乱码。
putText 乱码
看了官方文档发现使用 putText
中文乱码问题是因为在 OpenCV 中使用的是 Hershey 字体,这类字体是不支持中文的。
1import cv22
3def 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 break12
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 break28
29 # 释放资源30 cap.release()31 cv2.destroyAllWindows()32
33if __name__ == "__main__":34 rtsp_url = "rtsp://192.168.1.100:554/live"35 main(rtsp_url)
Pillow 介入解决
尝试使用 [[pillow]] 库中 [[Image]] 模块解决:
1import numpy as np2from PIL import Image, ImageDraw, ImageFont3
4def 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# 添加中文文字2frame = 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
字体(也支持传递完整的路径和相对路径)。
到此,中文应该可以正常显示了,下面是完整代码:
1import cv22import numpy as np3from PIL import Image, ImageDraw, ImageFont4
5def 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
25def 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 break34
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 break48
49 # 释放资源50 cap.release()51 cv2.destroyAllWindows()52
53if __name__ == "__main__":54 rtsp_url = "rtsp://192.168.1.100:554/live"55 main(rtsp_url)
最后
如果文字是固定不变的,可以先用 Image.new
创建一个空白图片,然后通过 [[ImageDraw]] 把文字绘制好,这样只需要把图片叠加到 frame 上即可,不需要每一帧都重复操作。