2025-12-11 13:41:07 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 使用示例
|
|
|
|
|
|
def run_yolo_with_chinese():
|
|
|
|
|
|
# 初始化中文渲染器
|
|
|
|
|
|
renderer = ChineseTextRenderer()
|
|
|
|
|
|
|
|
|
|
|
|
# 模拟YOLO检测结果
|
|
|
|
|
|
# 注意:这里假设你已经有检测结果
|
|
|
|
|
|
detections = [
|
|
|
|
|
|
{"bbox": [100, 100, 200, 300], "conf": 0.95, "class": "person"},
|
|
|
|
|
|
{"bbox": [300, 150, 450, 350], "conf": 0.88, "class": "car"},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# 中英文类别映射
|
|
|
|
|
|
class_map = {
|
|
|
|
|
|
"person": "人",
|
|
|
|
|
|
"car": "汽车",
|
|
|
|
|
|
"bicycle": "自行车",
|
|
|
|
|
|
"dog": "狗",
|
|
|
|
|
|
"cat": "猫",
|
|
|
|
|
|
"chair": "椅子",
|
|
|
|
|
|
"bottle": "瓶子"
|
2025-11-26 13:55:04 +08:00
|
|
|
|
}
|
2025-12-11 13:41:07 +08:00
|
|
|
|
|
|
|
|
|
|
# 读取图像或创建默认图像
|
|
|
|
|
|
img_path = "test.jpg"
|
|
|
|
|
|
if os.path.exists(img_path):
|
|
|
|
|
|
img = cv2.imread(img_path)
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 用户选择图像文件
|
|
|
|
|
|
import tkinter as tk
|
|
|
|
|
|
from tkinter import filedialog
|
|
|
|
|
|
|
|
|
|
|
|
root = tk.Tk()
|
|
|
|
|
|
root.withdraw()
|
|
|
|
|
|
img_path = filedialog.askopenfilename(
|
|
|
|
|
|
title="选择图像文件",
|
|
|
|
|
|
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.tiff")]
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if img_path:
|
|
|
|
|
|
img = cv2.imread(img_path)
|
|
|
|
|
|
else:
|
|
|
|
|
|
print("未选择文件,创建测试图像")
|
|
|
|
|
|
img = None
|
|
|
|
|
|
|
|
|
|
|
|
# 确保图像有效
|
|
|
|
|
|
img = ensure_image_valid(img)
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制检测结果
|
|
|
|
|
|
for det in detections:
|
|
|
|
|
|
x1, y1, x2, y2 = det["bbox"]
|
|
|
|
|
|
conf = det["conf"]
|
|
|
|
|
|
cls_en = det["class"]
|
|
|
|
|
|
cls_cn = class_map.get(cls_en, cls_en)
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制边界框
|
|
|
|
|
|
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制中文标签
|
|
|
|
|
|
label = f"{cls_cn}: {conf:.2f}"
|
|
|
|
|
|
img = renderer.put_text(img, label, (x1, max(y1 - 20, 10)),
|
|
|
|
|
|
font_size=15, color=(0, 255, 0))
|
|
|
|
|
|
|
|
|
|
|
|
# 添加标题
|
|
|
|
|
|
img = renderer.put_text(img, "YOLO检测结果", (10, 30),
|
|
|
|
|
|
font_size=25, color=(255, 255, 0))
|
|
|
|
|
|
|
|
|
|
|
|
# 显示结果
|
|
|
|
|
|
cv2.imshow("YOLO Detection with Chinese", img)
|
|
|
|
|
|
cv2.waitKey(0)
|
|
|
|
|
|
cv2.destroyAllWindows()
|
|
|
|
|
|
|
|
|
|
|
|
# 保存结果
|
|
|
|
|
|
output_path = "detection_result.jpg"
|
|
|
|
|
|
cv2.imwrite(output_path, img)
|
|
|
|
|
|
print(f"结果已保存: {output_path}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
run_yolo_with_chinese()
|