112 lines
3.7 KiB
Python
112 lines
3.7 KiB
Python
import cv2
|
||
import numpy as np
|
||
from PIL import Image, ImageDraw, ImageFont
|
||
import os
|
||
|
||
|
||
class ChineseTextRenderer:
|
||
def __init__(self, font_path=None):
|
||
self.font_cache = {}
|
||
self.font_path = font_path
|
||
|
||
def put_text(self, img, text, position, font_size=20, color=(0, 255, 0)):
|
||
"""安全的中文文本绘制函数"""
|
||
# 输入验证
|
||
if img is None:
|
||
raise ValueError("输入图像不能为None")
|
||
|
||
if not isinstance(img, np.ndarray):
|
||
raise TypeError(f"输入图像必须是numpy数组,实际是{type(img)}")
|
||
|
||
if len(img.shape) != 3 or img.shape[2] != 3:
|
||
# 尝试转换
|
||
if len(img.shape) == 2:
|
||
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
|
||
elif len(img.shape) == 3 and img.shape[2] == 4:
|
||
img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
|
||
else:
|
||
raise ValueError(f"不支持的图像格式,shape={img.shape}")
|
||
|
||
try:
|
||
# BGR转RGB
|
||
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
||
pil_img = Image.fromarray(img_rgb)
|
||
draw = ImageDraw.Draw(pil_img)
|
||
|
||
# 获取字体
|
||
font = self._get_font(font_size)
|
||
|
||
# 颜色转换:BGR到RGB
|
||
rgb_color = color[::-1]
|
||
|
||
# 绘制文本
|
||
draw.text(position, text, font=font, fill=rgb_color)
|
||
|
||
# RGB转BGR
|
||
return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
|
||
|
||
except Exception as e:
|
||
print(f"中文渲染失败: {e},使用英文后备")
|
||
# 后备方案:使用OpenCV绘制英文
|
||
cv2.putText(img, text, position, cv2.FONT_HERSHEY_SIMPLEX,
|
||
font_size / 30, color, 2)
|
||
return img
|
||
|
||
def _get_font(self, size):
|
||
"""获取字体对象,带缓存"""
|
||
if size in self.font_cache:
|
||
return self.font_cache[size]
|
||
|
||
# 查找字体文件
|
||
font_paths = []
|
||
if self.font_path and os.path.exists(self.font_path):
|
||
font_paths.append(self.font_path)
|
||
|
||
# 添加常用字体路径
|
||
font_paths.extend([
|
||
"simhei.ttf",
|
||
"msyh.ttc",
|
||
"C:/Windows/Fonts/simhei.ttf",
|
||
"C:/Windows/Fonts/msyh.ttc",
|
||
"C:/Windows/Fonts/Deng.ttf",
|
||
"C:/Windows/Fonts/simsun.ttc",
|
||
])
|
||
|
||
# 遍历所有可能的字体路径
|
||
font = None
|
||
for path in font_paths:
|
||
if os.path.exists(path):
|
||
try:
|
||
font = ImageFont.truetype(path, size, encoding="utf-8")
|
||
print(f"加载字体: {path}")
|
||
break
|
||
except Exception as e:
|
||
continue
|
||
|
||
# 如果找不到字体,尝试创建默认字体
|
||
if font is None:
|
||
try:
|
||
font = ImageFont.load_default()
|
||
# 调整默认字体大小
|
||
font = ImageFont.truetype("arial.ttf", size)
|
||
except:
|
||
# 最后手段:使用PIL的默认字体
|
||
font = ImageFont.load_default()
|
||
|
||
self.font_cache[size] = font
|
||
return font
|
||
|
||
|
||
def ensure_image_valid(img, default_size=(640, 480)):
|
||
"""确保图像有效,如果无效则创建默认图像"""
|
||
if img is None or not isinstance(img, np.ndarray) or img.size == 0:
|
||
print("创建默认图像...")
|
||
img = np.zeros((default_size[0], default_size[1], 3), dtype=np.uint8)
|
||
img.fill(50)
|
||
# 添加一些参考线
|
||
h, w = img.shape[:2]
|
||
cv2.line(img, (0, 0), (w, h), (0, 255, 0), 1)
|
||
cv2.line(img, (w, 0), (0, h), (0, 255, 0), 1)
|
||
cv2.circle(img, (w // 2, h // 2), min(w, h) // 4, (255, 0, 0), 2)
|
||
|
||
return img |