引言
在视频流分析中,我们经常需要在每一帧上绘制检测框和对应的中文标签。然而,直接使用 OpenCV 绘制中文存在一定的局限性,因为 OpenCV 的 cv2.putText 不支持中文字符。通常我们会选择结合 PIL 来完成中文绘制,但这会涉及到 OpenCV 图像和 PIL 图像的频繁转换,尤其是在处理高分辨率图像时,这种转换非常耗时,可能导致性能瓶颈。
为了解决这个问题,我将介绍一种 Python高效中文字符绘制方法:通过预渲染中文标签为纹理并缓存起来,再将纹理粘贴到图像上。这种方法避免了每帧都实时渲染中文字符,大幅提升了性能,非常适合实时视频流分析场景。
核心思路
- 预渲染中文文本:使用 PIL 将中文文本渲染成带透明背景的纹理图像,并将纹理缓存到字典中,避免重复渲染相同的文本。
- 纹理粘贴:将预渲染的纹理直接粘贴到 OpenCV 图像中,利用透明度实现无缝融合。
- 批量绘制:通过缓存机制和纹理粘贴优化整个绘制过程。
代码实现
import cv2
from PIL import Image, ImageDraw, ImageFont
import numpy as np
class DetectionDrawer:
def __init__(self, font_path: str, font_size: int):
"""
初始化绘制类,加载中文字体并创建缓存。
:param font_path: 字体文件路径(支持中文的 TTF 文件)。
:param font_size: 字体大小。
"""
self.font = ImageFont.truetype(font_path, font_size)
self.text_texture_cache = {} # 用于缓存已渲染的中文标签纹理
def create_text_texture(self, text: str, color: tuple):
"""
创建中文文本的纹理并缓存。
:param text: 要渲染的文本内容。
:param color: 文本颜色 (R, G, B)。
:return: 预渲染的文本纹理(NumPy 数组)。
"""
if text in self.text_texture_cache:
return self.text_texture_cache[text]
# 计算文本尺寸
bbox = self.font.getbbox(text)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
# 创建透明背景的图像
text_img = Image.new("RGBA", (text_width, text_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(text_img)
draw.text((0, 0), text, font=self.font, fill=color + (255,)) # 带透明度
# 转换为 NumPy 格式并缓存
text_texture = np.array(text_img)
self.text_texture_cache[text] = text_texture
return text_texture
def paste_text_texture(self, img: np.ndarray, position: tuple, text_texture: np.ndarray):
"""
将预渲染的文本纹理粘贴到 OpenCV 图像上。
:param img: OpenCV 图像。
:param position: 粘贴位置 (x, y)。
:param text_texture: 文本纹理(带透明通道的 NumPy 数组)。
:return: 添加文本后的图像。
"""
x, y = position
h, w, _ = text_texture.shape
h_img, w_img, _ = img.shape
# 限制粘贴区域,避免超出图像边界
w = min(w, w_img - x)
h = min(h, h_img - y)
if w <= 0 or h <= 0: # 如果超出边界,直接返回
return img
alpha_channel = text_texture[:h, :w, 3] / 255.0 # 透明度通道
rgb_texture = text_texture[:h, :w, :3] # RGB 通道
# 粘贴纹理
img[y:y + h, x:x + w, :] = (
(1 - alpha_channel[:, :, None]) * img[y:y + h, x:x + w, :]
+ alpha_channel[:, :, None] * rgb_texture
)
return img
def draw_detections(self, frame: np.ndarray, detections: list):
"""
批量绘制检测框和中文标签。
:param frame: 输入帧(OpenCV 格式)。
:param detections: 检测信息 [(bbox, label_text, color)]。
bbox 格式为 (x1, y1, x2, y2)。
:return: 绘制后的帧。
"""
for bbox, label_text, color in detections:
x1, y1, x2, y2 = bbox
# 绘制检测框
cv2.rectangle(frame, (x1, y1), (x2, y2), color, thickness=2)
# 创建文本纹理
text_texture = self.create_text_texture(label_text, color[::-1]) # BGR -> RGB
# 计算文本位置
text_h, text_w, _ = text_texture.shape
text_x, text_y = x1, y1 - text_h if y1 > text_h else y1 + 2
# 粘贴文本纹理
frame = self.paste_text_texture(frame, (text_x, text_y), text_texture)
return frame
使用方法
-
加载字体文件:确保提供一个支持中文字符的字体文件(如 simhei.ttf 或其他 TTF 文件)。
-
实例化类:
drawer = DetectionDrawer(font_path="simhei.ttf", font_size=20)
-
调用绘制方法:
# 示例检测框和标签 detections = [ [(50, 50, 200, 200), "类别1", (0, 255, 0)], [(300, 300, 450, 450), "类别2", (255, 0, 0)] ] # 绘制检测框和中文标签 frame = cv2.imread("example.jpg") frame_with_detections = drawer.draw_detections(frame, detections)
优势分析
- 性能优化:
- 文本纹理的缓存机制避免重复渲染,大幅减少 PIL 的调用次数。
- 结合 OpenCV 和 PIL 的长处,既能高效处理图像,又能支持中文绘制。
- 易用性:
- 支持多种字体、颜色和大小的中文标签渲染。
- 使用简单,可直接集成到检测或跟踪系统中。
- 适用场景:
- 实时视频流分析。
- 各类目标检测任务(如安全监控、自动驾驶等)。
总结
通过将中文标签预渲染为纹理并缓存起来,我们成功实现了一种高效的中文绘制方法,特别适合实时视频流分析场景。这种方法不仅提升了性能,还简化了代码逻辑,非常值得在实际项目中推广使用。