首先,网上有好几个显示中文标签的教程了,我为什么还要写呢??哼,很显然,是觉得他们实现的不够完美嘛~

YOLOv5在标签显示上,是花了点心思的,标签字体的大小,会根据图片尺寸进行调整,背景颜色条是为了避免图片中的内容干扰标签的展示,网上有的教程直接把字体大小定死了,背景颜色条也去除了,根本就不合理嘛!完全算不上实现。

因为OpenCV不支持中文,所以原代码中用来计算字体尺寸的方法cv2.getTextSize()对中文字符是不能正确计算宽度的。因此按网上的另一教程,虽然可以显示中文,但标签的背景颜色条宽度是不对的,会长出一截,如图:


我改进的思路是:保留代码原有的自适应尺寸操作,虽然cv2.getTextSize()对中文字符的宽度计算是错误的,但高度是对的,使用这个高度作为字体的size,然后用支持中文字符的PIL里的方法来计算字符宽度,这样就能得到宽度合适的背景颜色条啦~

在这里插入图片描述
代码比较简单,就不装biu啦,以YOLOv5-3.1版本为例,步骤如下

1、找个中文字体如MSYH.TTC放在YOLOv5文件夹下。

2、train.py文件

with open(opt.data) as f:

改为

with open(opt.data, encoding='UTF-8') as f:

3、test.py文件

with open(data) as f:

改为

with open(data, encoding='UTF-8') as f:

4、utils/general.py文件,导包

from PIL import Image, ImageDraw, ImageFont

修改plot_one_box 函数,if label之后的代码改为

    if label:
        tf = max(tl - 1, 1)  # font thickness
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        font_size = t_size[1]
        font = ImageFont.truetype('MSYH.TTC', font_size)
        t_size = font.getsize(label)
        c2 = c1[0] + t_size[0], c1[1] - t_size[1]
        cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA)  # filled
        img_PIL = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(img_PIL)
        draw.text((c1[0], c2[1] - 2), label, fill=(255, 255, 255), font=font)

        return cv2.cvtColor(np.array(img_PIL), cv2.COLOR_RGB2BGR)

其中font路径改为自己的字体路径。

5、utils/general.py文件,plot_images函数中

plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl)

改为

mosaic = plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl)

6、detect.py文件

plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3)

改为

im0 = plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3)

搞定, 这样无论是训练、验证还是推理过程,都可以是中文显示了。

这才是完美实现嘛,哼~


5.0版本更新

针对增加的混淆矩阵等评估图中的中文显示异常问题的解决方案

utils/metrics.py文件

开头添加代码

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

下列代码

sn.set(font_scale=1.0 if self.nc < 50 else 0.8)  # for label size

改为

 sn.set(font='SimHei', font_scale=1.0 if self.nc < 50 else 0.8)  # for label size

6.0版本更新

6.0版本项目改动较多,官方添加了画中文标签的方法,给我们省了点事儿,但在评估图中的中文还是需要自己改。

1、utils/general.py文件

with open(data, errors='ignore') as f:

改为

with open(data, encoding='UTF-8', errors='ignore') as f:

2、utils/metrics.py文件

sn.set(font_scale=1.0 if self.nc < 50 else 0.8)  # for label size

改为

sn.set(font='SimHei', font_scale=1.0 if self.nc < 50 else 0.8)  # for label size

3、utils/plots.py文件

开头添加代码

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

最后,6.0版本实现的画中文标签的方法是准备两种字体(默认是Arial.ttf和Arial.Unicode.ttf),根据用户需要选择使用英文字体还是中文字体,毕竟画中文的成本比较高,如果是英文标签就没必要浪费时间了。

我们只需要下载这2种字体到指定位置(默认 C:\Users\用户\AppData\Roaming\Ultralytics),然后在Annotator类的__init__()方法中,把参数example改成任意中文就好了。

当然这里的两种字体可以自己选,只要修改下列两行中的参数font(分别对应英文字体和中文字体):

def __init__(self, im, line_width=None, font_size=None, font='Arial.ttf', pil=False, example='abc'):
self.font = check_font(font='Arial.Unicode.ttf' if is_chinese(example) else font,=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12))

7.0版本更新

这个版本有点麻烦,评估图画图的代码改了不少,导致按照正常步骤还是会出现中文不显示的情况。
先进行以下步骤:

1、utils/general.py文件,yaml_load方法中:

with open(file, errors='ignore') as f:

改为

with open(file, encoding='UTF-8', errors='ignore') as f:

2、utils/plots.py文件

开头添加代码

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

完成这两步后,精确率召回率等图还会出现随机中文不显示的问题。我测试了半天,发现是因为画图被改成了多线程,具体多线程为什么会导致这个问题不清楚。
解决方案:
utils/metrics.py文件,删除plot_pr_curve方法和plot_mc_curve方法上方的多线程装饰器:

@threaded

最后,如果按6.0版本的步骤2,混淆矩阵confusion_matrix的图还是显示不了中文,这和seaborn画图有关,不知道官方为什么要改动这里的代码,新代码画的图跟之前基本没有区别,就是多了个标题…我没找到简单的办法解决这个问题,如果非要混淆矩阵显示中文,建议把ConfusionMatrix类中的plot方法完全替换成6.0版本的代码,再进行6.0版本的步骤2即可。


最后补充Ultralytics YOLOv8,已自动支持中文标签框的绘制,只有评估图会乱码,在调用前添加代码即可:

from matplotlib import pyplot as plt
import seaborn as sn
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
sn.set(font='SimHei')
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐