Yolov/mapping_cn.py

194 lines
5.9 KiB
Python
Raw Normal View History

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()