时间:2026年2月
标签:YOLOv11, OpenCV, Pillow, 中文渲染
环境:Windows / Python 3.9 / PySide6
1. 需求背景与技术痛点
在完成 YOLOv11-Snake 模型的训练后,系统进入了部署与 UI 对接阶段。原始数据集采用数字编码(0-7)作为标签,导致在推理阶段存在两个严重的工程化阻碍:
-
语义缺失 :系统输出的检测结果仅为
0,1等数字 ID,用户(非算法开发人员)无法直观判断病害类型(如"掉块"或"裂纹")。 -
渲染乱码 :OpenCV 的原生绘图函数
cv2.putText仅支持 ASCII 字符。尝试在视频帧上绘制中文标签时,字符显示为???或乱码方框,严重影响系统的可用性。
为了解决上述问题,我实施了从模型元数据修改 到渲染层重写的完整方案。
2. 解决方案一:模型元数据注入
思路:
YOLO 模型文件 (.pt) 内部存储了一个 names 字典。与其在应用程序代码中维护一份"数字-中文"的映射表(Hard-coding),不如直接修改模型文件的元数据。这样模型本身即携带语义信息,具备更好的迁移性。
实施步骤:
编写了一个独立的工具脚本,加载训练好的权重,覆写其 names 属性并重保存。
Python
from ultralytics import YOLO
# 1. 加载原始模型
model = YOLO('best.pt')
# 2. 注入中文映射 (根据数据集定义)
model.model.names = {
0: '掉块', 1: '暗斑(擦伤)', 2: '轨道小凹陷', 3: '横向大裂缝',
4: '局部凹陷', 5: '横向巨大凹陷', 6: '剥离裂纹', 7: '波磨'
}
# 3. 序列化保存
model.save('best_chinese.pt')
局限性:
此方法解决了推理结果的语义问题(result.names 返回中文),UI 的表格列表可以直接显示中文。但它无法解决 OpenCV 在图片上画不出中文的问题。
3. 解决方案二:渲染引擎替换
思路:
鉴于 OpenCV 在中文支持上的先天缺陷,决定引入 Pillow (PIL) 库接管文字绘制层。采用"混合渲染"策略:OpenCV 负责高效的图像读取与矩形框绘制,Pillow 负责高质量的字体渲染。
技术流分析:
-
色彩空间转换 :OpenCV 默认使用
BGR格式,而 Pillow 使用RGB。必须在转换前后进行cv2.cvtColor操作,否则会出现颜色反转(如蓝色变红色)。 -
字体加载 :利用
ImageFont.truetype加载系统字体(如微软雅黑msyh.ttc),彻底解决乱码。 -
性能权衡:虽然 PIL 转换会有轻微的 CPU 开销,但在单帧处理中(<5ms)完全可以接受,不会造成肉眼可见的卡顿。
代码实现逻辑:
在 ImageProcessor 类中重构了 process_frame 方法:
Python
# 核心渲染管线
def draw_chinese_labels(self, frame, detections):
# 1. BGR 转 RGB (OpenCV -> PIL)
cv2_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(cv2_img)
draw = ImageDraw.Draw(pil_img)
for box in detections:
# 获取中文标签与坐标
label = self.class_map[box['cls_id']]
color = self.colors[box['cls_id']]
x1, y1, x2, y2 = box['xyxy']
# 2. 绘制矩形框 (PIL 绘制空心矩形)
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
# 3. 绘制文字背景条 (确保高对比度)
text_str = f"{label} {box['conf']:.2f}"
bbox = draw.textbbox((x1, y1), text_str, font=self.font)
draw.rectangle(bbox, fill=color)
# 4. 绘制中文字符
draw.text((x1, y1), text_str, font=self.font, fill=(255, 255, 255))
# 5. RGB 转 BGR (PIL -> OpenCV)
return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
4. 实施细节与防错机制
在落地过程中,针对环境差异做了以下健壮性处理:
-
字体回退机制:
并不是所有部署机器都有微软雅黑。代码中增加了
try-except块,优先加载msyh.ttc,失败则尝试simhei.ttf,再次失败则回退到 PIL 默认字体(并打印警告),防止因缺少字体文件导致程序崩溃。 -
色彩管理:
定义了一个包含 8 种高辨识度颜色的
self.colors列表。每个病害类别通过cls_id % 8映射到固定颜色。例如,"掉块"始终显示为红色,"裂纹"始终为绿色,便于用户形成肌肉记忆。 -
数据流统一:
确保 UI 线程接收到的
detections列表中的class字段也经过了映射。这样右侧的QTableWidget列表和左侧的视频画面保持了严格的语义一致性。
5. 最终效果
经过上述改造,系统达到了交付标准:
-
可视化:视频画面中准确显示了"横向大裂缝 0.85"等中文标注,字体清晰抗锯齿。
-
交互性:右侧列表与画面标注一一对应,消除了用户查阅"数字代码表"的操作成本。
-
兼容性:即使在未修改模型元数据的情况下,依靠运行时的字典映射也能正常工作。
此次重构打通了从底层推理到上层展示的"语义鸿沟",是系统从实验原型向工程化应用转变的关键一步。